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) TModalSession = class(TObject)
window : NSWindow; window : NSWindow;
sess : NSModalSession; 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; end;
{ TCocoaWidgetSet } { TCocoaWidgetSet }
@ -126,10 +135,17 @@ type
procedure SendCheckSynchronizeMessage; procedure SendCheckSynchronizeMessage;
procedure OnWakeMainThread(Sender: TObject); procedure OnWakeMainThread(Sender: TObject);
procedure DoSetMainMenu(AMenu: NSMenu; ALCLMenu: TMenu);
public public
// modal session // modal session
CurModalForm: NSWindow; CurModalForm: NSWindow;
Modals : TList; Modals : TList;
MainMenuEnabled: Boolean; // the latest main menu status
PrevMenu : NSMenu;
PrevLCLMenu : TMenu;
LCLMenu: TMenu;
PrevMenuEnabled: Boolean; // previous mainmenu status
constructor Create; override; constructor Create; override;
destructor Destroy; override; destructor Destroy; override;
@ -159,10 +175,9 @@ type
procedure FreeSysColorBrushes; procedure FreeSysColorBrushes;
procedure SetMainMenu(const AMenu: HMENU; const ALCLMenu: TMenu); procedure SetMainMenu(const AMenu: HMENU; const ALCLMenu: TMenu);
function StartModal(awin: NSWindow): Boolean; function StartModal(awin: NSWindow; hasMenu: Boolean): Boolean;
procedure EndModal(awin: NSWindow); procedure EndModal(awin: NSWindow);
{todo:} {todo:}
function DCGetPixel(CanvasHandle: HDC; X, Y: integer): TGraphicsColor; override; function DCGetPixel(CanvasHandle: HDC; X, Y: integer): TGraphicsColor; override;
procedure DCSetPixel(CanvasHandle: HDC; X, Y: integer; AColor: TGraphicsColor); override; procedure DCSetPixel(CanvasHandle: HDC; X, Y: integer; AColor: TGraphicsColor); override;
@ -359,11 +374,15 @@ end;
{ TModalSession } { TModalSession }
constructor TModalSession.Create(awin: NSWindow; asess: NSModalSession); constructor TModalSession.Create(awin: NSWindow; asess: NSModalSession;
APrevMenuEnabled: Boolean; amainmenu: NSMenu; ALCL: TMenu);
begin begin
inherited Create; inherited Create;
window := awin; window := awin;
sess := asess; sess := asess;
prevMenuEnabled := APrevMenuEnabled;
cocoaMenu := amainmenu;
lclMenu := alcl;
end; end;
{ TCocoaApplication } { TCocoaApplication }
@ -498,6 +517,105 @@ begin
if Assigned(MainPool) then MainPool.release; if Assigned(MainPool) then MainPool.release;
end; 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 initialization
// {$I Cocoaimages.lrs} // {$I Cocoaimages.lrs}

View File

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

View File

@ -478,74 +478,6 @@ begin
DeleteAndNilObject(FSysColorBrushes[i]); DeleteAndNilObject(FSysColorBrushes[i]);
end; 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 Method: TCocoaWidgetSet.GetAppHandle
Returns: Returns NSApp object, created via NSApplication.sharedApplication Returns: Returns NSApp object, created via NSApplication.sharedApplication

View File

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