diff --git a/lcl/interfaces/cocoa/cocoaint.pas b/lcl/interfaces/cocoa/cocoaint.pas index 97001d8ec4..37a9eb22ff 100644 --- a/lcl/interfaces/cocoa/cocoaint.pas +++ b/lcl/interfaces/cocoa/cocoaint.pas @@ -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} diff --git a/lcl/interfaces/cocoa/cocoalclintf.inc b/lcl/interfaces/cocoa/cocoalclintf.inc index 9adc110d61..a76f0ecaac 100644 --- a/lcl/interfaces/cocoa/cocoalclintf.inc +++ b/lcl/interfaces/cocoa/cocoalclintf.inc @@ -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); diff --git a/lcl/interfaces/cocoa/cocoaobject.inc b/lcl/interfaces/cocoa/cocoaobject.inc index 3cccca21aa..23c8a04b2e 100644 --- a/lcl/interfaces/cocoa/cocoaobject.inc +++ b/lcl/interfaces/cocoa/cocoaobject.inc @@ -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 diff --git a/lcl/interfaces/cocoa/cocoawsforms.pas b/lcl/interfaces/cocoa/cocoawsforms.pas index 7b365e4102..ce77eea660 100644 --- a/lcl/interfaces/cocoa/cocoawsforms.pas +++ b/lcl/interfaces/cocoa/cocoawsforms.pas @@ -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}