cocoa: improve tracking of menu changes when modal dialogs are switching. Merged menu routines from coocaojbect.inc into cocoaint.pas

git-svn-id: trunk@59131 -
This commit is contained in:
dmitry 2018-09-22 17:11:40 +00:00
parent 7b1d87b432
commit be159e85b6
4 changed files with 127 additions and 84 deletions

View File

@ -81,7 +81,16 @@ type
TModalSession = class(TObject)
window : NSWindow;
sess : NSModalSession;
constructor Create(awin: NSWindow; asess: NSModalSession);
// recording menu state for the modality stack
// there's no limitation for a modal window to have its own menu
// if it override the mainMenu, we still need the information
// to restore the previous state of the mainmenu
prevMenuEnabled: Boolean;
cocoaMenu : NSMenu;
lclMenu : TMenu;
constructor Create(awin: NSWindow; asess: NSModalSession;
APrevMenuEnabled: Boolean;
amainmenu: NSMenu; ALCL: TMenu);
end;
{ TCocoaWidgetSet }
@ -126,10 +135,17 @@ type
procedure SendCheckSynchronizeMessage;
procedure OnWakeMainThread(Sender: TObject);
procedure DoSetMainMenu(AMenu: NSMenu; ALCLMenu: TMenu);
public
// modal session
CurModalForm: NSWindow;
Modals : TList;
MainMenuEnabled: Boolean; // the latest main menu status
PrevMenu : NSMenu;
PrevLCLMenu : TMenu;
LCLMenu: TMenu;
PrevMenuEnabled: Boolean; // previous mainmenu status
constructor Create; override;
destructor Destroy; override;
@ -159,10 +175,9 @@ type
procedure FreeSysColorBrushes;
procedure SetMainMenu(const AMenu: HMENU; const ALCLMenu: TMenu);
function StartModal(awin: NSWindow): Boolean;
function StartModal(awin: NSWindow; hasMenu: Boolean): Boolean;
procedure EndModal(awin: NSWindow);
{todo:}
function DCGetPixel(CanvasHandle: HDC; X, Y: integer): TGraphicsColor; override;
procedure DCSetPixel(CanvasHandle: HDC; X, Y: integer; AColor: TGraphicsColor); override;
@ -359,11 +374,15 @@ end;
{ TModalSession }
constructor TModalSession.Create(awin: NSWindow; asess: NSModalSession);
constructor TModalSession.Create(awin: NSWindow; asess: NSModalSession;
APrevMenuEnabled: Boolean; amainmenu: NSMenu; ALCL: TMenu);
begin
inherited Create;
window := awin;
sess := asess;
prevMenuEnabled := APrevMenuEnabled;
cocoaMenu := amainmenu;
lclMenu := alcl;
end;
{ TCocoaApplication }
@ -498,6 +517,105 @@ begin
if Assigned(MainPool) then MainPool.release;
end;
procedure TCocoaWidgetSet.DoSetMainMenu(AMenu: NSMenu; ALCLMenu: TMenu);
var
i: Integer;
lCurItem: TMenuItem;
lMenuObj: NSObject;
lNSMenu: NSMenu absolute AMenu;
begin
PrevMenu := NSApplication(NSApp).mainMenu;
PrevLCLMenu := LCLMenu;
NSApp.setMainMenu(lNSMenu);
LCLMenu := ALCLMenu;
if (ALCLMenu = nil) or not ALCLMenu.HandleAllocated then Exit;
// Find the Apple menu, if the user provided any by setting the Caption to
// Some older docs say we should use setAppleMenu to obtain the Services/Hide/Quit items,
// but its now private and in 10.10 it doesn't seam to do anything
// NSApp.setAppleMenu(NSMenu(lMenuObj));
for i := 0 to ALCLMenu.Items.Count-1 do
begin
lCurItem := ALCLMenu.Items.Items[i];
if not lNSMenu.isKindOfClass_(TCocoaMenu) then Break;
if not lCurItem.HandleAllocated then Continue;
lMenuObj := NSObject(lCurItem.Handle);
if not lMenuObj.isKindOfClass_(TCocoaMenuItem) then Continue;
if TCocoaMenuItem(lMenuObj).isValidAppleMenu() then
begin
TCocoaMenu(lNSMenu).overrideAppleMenu(TCocoaMenuItem(lMenuObj));
Break;
end;
end;
end;
procedure TCocoaWidgetSet.SetMainMenu(const AMenu: HMENU; const ALCLMenu: TMenu);
begin
if AMenu<>0 then
begin
DoSetMainMenu(NSMenu(AMenu), LCLMenu);
PrevMenuEnabled := MainMenuEnabled;
MainMenuEnabled := true;
ToggleAppMenu(true);
//if not Assigned(ACustomForm.Menu) then ToggleAppMenu(false);
// for modal windows work around bug, but doesn't work :(
{$ifdef COCOA_USE_NATIVE_MODAL}
{if CurModalForm <> nil then
for i := 0 to lNSMenu.numberOfItems()-1 do
begin
lNSMenu.itemAtIndex(i).setTarget(TCocoaWSCustomForm.GetWindowFromHandle(CurModalForm));
end;}
{$endif}
end;
end;
function TCocoaWidgetSet.StartModal(awin: NSWindow; hasMenu: Boolean): Boolean;
var
sess : NSModalSession;
begin
Result := false;
if not Assigned(awin) then Exit;
sess := NSApplication(NSApp).beginModalSessionForWindow(awin);
if not Assigned(sess) then Exit;
if not Assigned(Modals) then Modals := TList.Create;
// cannot use MainMenuEnabled to record the status, because "MainMenuEnabled"
// has been changed in SetMainMenu for the menu of ModalWindow
Modals.Add( TModalSession.Create(awin, sess, PrevMenuEnabled, PrevMenu, PrevLCLMenu));
if not hasMenu then begin
MainMenuEnabled := false;
ToggleAppMenu(false); // modal menu doesn't have a window, disabling it
end;
Result := true;
end;
procedure TCocoaWidgetSet.EndModal(awin: NSWindow);
var
ms : TModalSession;
begin
if not Assigned(Modals) or (Modals.Count = 0) then Exit;
ms := TModalSession(Modals[Modals.Count-1]);
if (ms.window <> awin) then Exit;
NSApplication(NSApp).endModalSession(ms.sess);
// restoring the menu status that was before the modality
DoSetMainMenu(ms.cocoaMenu, ms.lclMenu);
PrevMenuEnabled := MainMenuEnabled;
MainMenuEnabled := ms.prevMenuEnabled;
ToggleAppMenu(ms.prevMenuEnabled); // modal menu doesn't have a window, disabling it
ms.Free;
Modals.Delete(Modals.Count-1);
end;
initialization
// {$I Cocoaimages.lrs}

View File

@ -26,7 +26,7 @@
Params: ACursor - Cursor type
Returns: Cursor object in Cocoa for the specified cursor type
------------------------------------------------------------------------------}
function TCocoaWidgetSet.CreateStandardCursor(ACursor: SmallInt): HCursor;
function TCocoaWidgetSet.CreateStandardCursor(ACursor: SmallInt): hCursor;
begin
case ACursor of
crArrow,
@ -294,13 +294,9 @@ end; {TCocoaWidgetSet.PromptUser}
Shows modal dialog with the specified caption, message and buttons and prompts
user to push one.
------------------------------------------------------------------------------}
function TCocoaWidgetSet.PromptUser(const DialogCaption : string;
const DialogMessage : string;
DialogType : LongInt;
Buttons : PLongInt;
ButtonCount : LongInt;
DefaultIndex : LongInt;
EscapeResult : LongInt) : LongInt;
function TCocoaWidgetSet.PromptUser(const DialogCaption, DialogMessage: String;
DialogType: longint; Buttons: PLongint; ButtonCount, DefaultIndex,
EscapeResult: Longint): Longint;
begin
Result := CocoaPromptUser(DialogCaption, DialogMessage, DialogType, Buttons, ButtonCount,
DefaultIndex, EscapeResult);

View File

@ -478,74 +478,6 @@ begin
DeleteAndNilObject(FSysColorBrushes[i]);
end;
procedure TCocoaWidgetSet.SetMainMenu(const AMenu: HMENU; const ALCLMenu: TMenu);
var
i: Integer;
lCurItem: TMenuItem;
lMenuObj: NSObject;
lNSMenu: NSMenu absolute AMenu;
begin
if AMenu<>0 then
begin
NSApp.setMainMenu(lNSMenu);
if (ALCLMenu = nil) or not ALCLMenu.HandleAllocated then Exit;
// Find the Apple menu, if the user provided any by setting the Caption to 
// Some older docs say we should use setAppleMenu to obtain the Services/Hide/Quit items,
// but its now private and in 10.10 it doesn't seam to do anything
// NSApp.setAppleMenu(NSMenu(lMenuObj));
for i := 0 to ALCLMenu.Items.Count-1 do
begin
lCurItem := ALCLMenu.Items.Items[i];
if not lNSMenu.isKindOfClass_(TCocoaMenu) then Break;
if not lCurItem.HandleAllocated then Continue;
lMenuObj := NSObject(lCurItem.Handle);
if not lMenuObj.isKindOfClass_(TCocoaMenuItem) then Continue;
if TCocoaMenuItem(lMenuObj).isValidAppleMenu() then
begin
TCocoaMenu(lNSMenu).overrideAppleMenu(TCocoaMenuItem(lMenuObj));
Break;
end;
end;
// for modal windows work around bug, but doesn't work :(
{$ifdef COCOA_USE_NATIVE_MODAL}
{if CurModalForm <> nil then
for i := 0 to lNSMenu.numberOfItems()-1 do
begin
lNSMenu.itemAtIndex(i).setTarget(TCocoaWSCustomForm.GetWindowFromHandle(CurModalForm));
end;}
{$endif}
end;
end;
function TCocoaWidgetSet.StartModal(awin: NSWindow): Boolean;
var
sess : NSModalSession;
begin
Result := false;
if not Assigned(awin) then Exit;
sess := NSApplication(NSApp).beginModalSessionForWindow(awin);
if not Assigned(sess) then Exit;
if not Assigned(Modals) then Modals := TList.Create;
Modals.Add( TModalSession.Create(awin, sess));
Result := true;
end;
procedure TCocoaWidgetSet.EndModal(awin: NSWindow);
var
ms : TModalSession;
begin
if not Assigned(Modals) or (Modals.Count = 0) then Exit;
ms := TModalSession(Modals[Modals.Count-1]);
if (ms.window <> awin) then Exit;
NSApplication(NSApp).endModalSession(ms.sess);
ms.Free;
Modals.Delete(Modals.Count-1);
end;
{------------------------------------------------------------------------------
Method: TCocoaWidgetSet.GetAppHandle
Returns: Returns NSApp object, created via NSApplication.sharedApplication

View File

@ -312,7 +312,6 @@ begin
end
else
debugln('Warning: Menu does not have a valid handle.');
ToggleAppMenu(true);
end
else
CocoaWidgetSet.SetMainMenu(0, nil);
@ -759,14 +758,12 @@ begin
// At this point of time, we simply force enabling of the new modal form
// (which is happening in LCL code, but at the wrong time)
NSObject(ACustomForm.Handle).lclSetEnabled(true);
if not Assigned(ACustomForm.Menu) then ToggleAppMenu(false);
// Another possible implementation is using a session, but this requires
// disabling the other windows ourselves
win := TCocoaWSCustomForm.GetWindowFromHandle(ACustomForm);
if win = nil then Exit;
CocoaWidgetSet.StartModal(NSView(ACustomForm.Handle).window);
CocoaWidgetSet.StartModal(NSView(ACustomForm.Handle).window, Assigned(ACustomForm.Menu));
// Another possible implementation is using runModalForWindow
{$ifdef COCOA_USE_NATIVE_MODAL}