From a4503bccc006c750fc0861a98984a4fe89a5fc90 Mon Sep 17 00:00:00 2001 From: dmitry Date: Sat, 29 Sep 2018 21:33:29 +0000 Subject: [PATCH] cocoa: additional control over cocoa window levels git-svn-id: trunk@59185 - --- lcl/interfaces/cocoa/cocoaint.pas | 13 ++++++++++ lcl/interfaces/cocoa/cocoawindows.pas | 13 ++++++++++ lcl/interfaces/cocoa/cocoawsforms.pas | 37 +++++++++++++++++++-------- 3 files changed, 52 insertions(+), 11 deletions(-) diff --git a/lcl/interfaces/cocoa/cocoaint.pas b/lcl/interfaces/cocoa/cocoaint.pas index 0fb5ed7c12..5a1eee617a 100644 --- a/lcl/interfaces/cocoa/cocoaint.pas +++ b/lcl/interfaces/cocoa/cocoaint.pas @@ -177,6 +177,7 @@ type procedure SetMainMenu(const AMenu: HMENU; const ALCLMenu: TMenu); function StartModal(awin: NSWindow; hasMenu: Boolean): Boolean; procedure EndModal(awin: NSWindow); + function isModalSession: Boolean; {todo:} function DCGetPixel(CanvasHandle: HDC; X, Y: integer): TGraphicsColor; override; @@ -580,11 +581,19 @@ end; function TCocoaWidgetSet.StartModal(awin: NSWindow; hasMenu: Boolean): Boolean; var sess : NSModalSession; + lvl : NSInteger; begin Result := false; if not Assigned(awin) then Exit; + + lvl := awin.level; + sess := NSApplication(NSApp).beginModalSessionForWindow(awin); if not Assigned(sess) then Exit; + + // beginModalSession "configures" the modality and potentially is changing window level + awin.setLevel(lvl); + if not Assigned(Modals) then Modals := TList.Create; // If a modal menu has it's menu, then SetMainMenu has already been called @@ -619,6 +628,10 @@ begin Modals.Delete(Modals.Count-1); end; +function TCocoaWidgetSet.isModalSession: Boolean; +begin + Result := Assigned(Modals) and (Modals.Count > 0); +end; initialization // {$I Cocoaimages.lrs} diff --git a/lcl/interfaces/cocoa/cocoawindows.pas b/lcl/interfaces/cocoa/cocoawindows.pas index c76b7a57b3..c1ce41b9fd 100644 --- a/lcl/interfaces/cocoa/cocoawindows.pas +++ b/lcl/interfaces/cocoa/cocoawindows.pas @@ -138,6 +138,7 @@ type procedure windowDidExitFullScreen(notification: NSNotification); message 'windowDidExitFullScreen:'; public callback: IWindowCallback; + keepWinLevel : NSInteger; //LCLForm: TCustomForm; procedure dealloc; override; function acceptsFirstResponder: Boolean; override; @@ -758,6 +759,18 @@ end; procedure TCocoaWindow.windowDidBecomeKey(notification: NSNotification); begin + // forcing to keep the level as all other LCL windows + // Modal windows tend to "restore" their elevated level + // And that doesn't work for modal windows that are "Showing" other windows + + // Another approach is to set elevated levels for windows, shown during modal session + // That requires to revoke the elevated level from windows on closing a window session + // This might be the way to go, if FormStyle (such as fsStayOnTop) would come + // in conflict with modality + if level <> keepWinLevel then begin + setLevel(keepWinLevel); + end; + if Assigned(callback) then callback.Activate; end; diff --git a/lcl/interfaces/cocoa/cocoawsforms.pas b/lcl/interfaces/cocoa/cocoawsforms.pas index ce77eea660..3dbab16d10 100644 --- a/lcl/interfaces/cocoa/cocoawsforms.pas +++ b/lcl/interfaces/cocoa/cocoawsforms.pas @@ -170,6 +170,7 @@ type procedure ArrangeTabOrder(const AWinControl: TWinControl); function HWNDToForm(AFormHandle: HWND): TCustomForm; +procedure WindowSetFormStyle(win: NSWindow; AFormStyle: TFormStyle); implementation @@ -210,6 +211,25 @@ begin Result := AForm.BorderStyle; end; +procedure WindowSetFormStyle(win: NSWindow; AFormStyle: TFormStyle); +var + lvl : NSInteger; +begin + if not (AFormStyle in [fsNormal, fsMDIChild, fsMDIForm]) then + begin + lvl := FormStyleToWindowLevel[AFormStyle]; + win.setHidesOnDeactivate(FormStyleToHideOnDeactivate[AFormStyle]); + end + else + begin + lvl := 0; + win.setHidesOnDeactivate(false); + end; + win.setLevel(lvl); + if win.isKindOfClass(TCocoaWindow) then + TCocoaWindow(win).keepWinLevel := lvl; +end; + { TCocoaWSHintWindow } class function TCocoaWSHintWindow.CreateHandle(const AWinControl: TWinControl; @@ -323,6 +343,9 @@ begin ACustForm.SetFocusedControl(ACustForm.ActiveControl); IsActivating:=False; + + if CocoaWidgetSet.isModalSession then + NSView(ACustForm.Handle).window.orderFront(nil); end; end; @@ -580,12 +603,8 @@ begin UpdateWindowIcons(win, GetDesigningBorderStyle(Form), Form.BorderIcons); // For safety, it is better to not apply any setLevel & similar if the form is just a standard style // see issue http://bugs.freepascal.org/view.php?id=28473 - if not (Form.FormStyle in [fsNormal, fsMDIChild, fsMDIForm]) - and not (csDesigning in AWinControl.ComponentState) then - begin - win.setLevel(FormStyleToWindowLevel[Form.FormStyle]); - win.setHidesOnDeactivate(FormStyleToHideOnDeactivate[Form.FormStyle]); - end; + if not (csDesigning in AWinControl.ComponentState) then + WindowSetFormStyle(win, Form.FormStyle); win.enableCursorRects; TCocoaWindow(win).callback := cb; @@ -831,11 +850,7 @@ begin if AForm.HandleAllocated and not (csDesigning in AForm.ComponentState) then begin win := TCocoaWindowContent(AForm.Handle).lclOwnWindow; - if Assigned(win) then - begin - win.setLevel(FormStyleToWindowLevel[AFormStyle]); - win.setHidesOnDeactivate(FormStyleToHideOnDeactivate[AFormStyle]); - end; + WindowSetFormStyle(win, AFormStyle); end; end;