diff --git a/lcl/interfaces/cocoa/cocoaint.pas b/lcl/interfaces/cocoa/cocoaint.pas index 71fc1b11f7..f9e5051fb3 100644 --- a/lcl/interfaces/cocoa/cocoaint.pas +++ b/lcl/interfaces/cocoa/cocoaint.pas @@ -53,25 +53,12 @@ type { TAppDelegate } - TWinLevelOrder = record - win : NSWindow; - lvl : NSInteger; - ord : NSinteger; - vis : Boolean; - end; - PWinLevelOrder = ^TWinLevelOrder; - TWinLevelOrderArray = array [Word] of TWinLevelOrder; - PWinLevelOrderArray = ^TWinLevelOrderArray; - TAppDelegate = objcclass(NSObject, NSApplicationDelegateProtocol) public - orderArray : PWinLevelOrderArray; - orderArrayCount : Integer; procedure application_openFiles(sender: NSApplication; filenames: NSArray); procedure applicationDidHide(notification: NSNotification); procedure applicationDidUnhide(notification: NSNotification); procedure applicationDidBecomeActive(notification: NSNotification); - procedure applicationWillResignActive(notification: NSNotification); procedure applicationDidResignActive(notification: NSNotification); procedure applicationDidChangeScreenParameters(notification: NSNotification); procedure applicationWillFinishLaunching(notification: NSNotification); @@ -206,6 +193,10 @@ type procedure AppBringToFront; override; procedure AppSetIcon(const Small, Big: HICON); override; procedure AppSetTitle(const ATitle: string); override; + function AppRemoveStayOnTopFlags(const ASystemTopAlso: Boolean=False + ): Boolean; override; + function AppRestoreStayOnTopFlags(const ASystemTopAlso: Boolean=False + ): Boolean; override; function BeginMessageProcess: THandle; override; procedure EndMessageProcess(context: THandle); override; @@ -897,19 +888,13 @@ 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; MenuTrackCancelAll(); diff --git a/lcl/interfaces/cocoa/cocoaobject.inc b/lcl/interfaces/cocoa/cocoaobject.inc index 4c38e7d704..d2270aecd6 100644 --- a/lcl/interfaces/cocoa/cocoaobject.inc +++ b/lcl/interfaces/cocoa/cocoaobject.inc @@ -319,6 +319,20 @@ begin // There is no way to change the dock title end; +// NSModalPanelWindowLevel has higher priority than NSFloatingWindowLevel +// on Cocoa, so nothing needs to be done +function TCocoaWidgetSet.AppRemoveStayOnTopFlags(const ASystemTopAlso: Boolean + ): Boolean; +begin + Result:= true; +end; + +function TCocoaWidgetSet.AppRestoreStayOnTopFlags(const ASystemTopAlso: Boolean + ): Boolean; +begin + Result:= true; +end; + function TCocoaWidgetSet.GetLCLCapability(ACapability: TLCLCapability): PtrUInt; begin case ACapability of @@ -601,77 +615,93 @@ end; procedure TAppDelegate.applicationDidBecomeActive(notification: NSNotification); var - i : integer; + windows: NSArray; + window: NSWindow; + form: TObject; + style: TFormStyle; + i: Integer; begin - // Cocoa changes level and order of windows to it's liking - // (it happens between Will- and DidBecomeActive) - // for example Model windows becoming level 8, - // even if LCL set them to level 0 before. - // As a result the OrderedIndex also goes messed up. - // It's being restored here - for i := orderArrayCount-1 downto 0 do - begin - if not orderArray^[i].vis then + windows := NSApp.orderedWindows; + for i:= windows.count-1 downto 0 do begin + window:= NSWindow( windows.objectAtIndex(i) ); + if not window.isVisible then continue; - if orderArray^[i].lvl=NSNormalWindowLevel then + form:= window.lclGetTarget; + if not (form is TCustomForm) then continue; - orderArray^[i].win.setLevel( orderArray^[i].lvl ); - orderArray^[i].win.orderFrontRegardless; - end; - orderArrayCount := 0; - if orderArray <> nil then - begin - Freemem(orderArray); - orderArray := nil; + if csDesigning in TCustomForm(form).ComponentState then + continue; + style:= TCustomForm(form).FormStyle; + if style in fsAllNonSystemStayOnTop then + window.setLevel( NSFloatingWindowLevel ); end; Application.IntfAppActivate; end; -procedure TAppDelegate.applicationWillResignActive(notification: NSNotification - ); +procedure TAppDelegate.applicationDidResignActive(notification: NSNotification); var - baseWindowNumber: NSInteger; + lastWindowNumber: NSInteger; + topWindowNumber: NSInteger; windows: NSArray; window: NSWindow; form: TObject; - info: PWinLevelOrder; + style: TFormStyle; + state: TFormState; + i: Integer; begin - windows := NSApp.orderedWindows; + // no window in this space + if NSWindow.windowNumbersWithOptions(0).count = 0 then + Exit; - baseWindowNumber:= 0; - for window in windows do begin - if window.level=NSNormalWindowLevel then begin - baseWindowNumber:= window.windowNumber; - break; - end; - end; + windows:= NSApp.orderedWindows; - orderArrayCount:= windows.count; - orderArray:= GetMem(orderArrayCount * sizeof(TWinLevelOrder)); - - info:= orderArray^; - for window in windows do begin - info^.win := window; - info^.lvl := window.level; - info^.ord := window.orderedIndex; - info^.vis := window.isVisible; - inc( info ); - end; - - for window in windows do begin + // reset fsStayOnTop form + lastWindowNumber:= 0; + for i:=windows.count-1 downto 0 do begin + window:= NSWindow( windows.objectAtIndex(i) ); + if not window.isVisible then + continue; form:= window.lclGetTarget; - if form is TCustomForm then begin - if TCustomForm(form).FormStyle=fsStayOnTop then begin - window.setLevel( NSNormalWindowLevel ); - window.orderWindow_relativeTo( NSWindowAbove, baseWindowNumber ); - end; + if not (form is TCustomForm) then + continue; + if csDesigning in TCustomForm(form).ComponentState then + continue; + style:= TCustomForm(form).FormStyle; + if style in fsAllNonSystemStayOnTop then begin + window.setLevel( NSNormalWindowLevel ); + if lastWindowNumber<>0 then + window.orderWindow_relativeTo( NSWindowAbove, lastWindowNumber ); + end; + lastWindowNumber:= window.windowNumber; + end; + + // find top window of NSNormalWindowLevel + topWindowNumber:= 0; + for window in windows do begin + if not window.isVisible then + continue; + if window.level <> NSNormalWindowLevel then + continue; + topWindowNumber:= window.windowNumber; + break; + end; + + // bring up modal form + for i:=windows.count-1 downto 0 do begin + window:= NSWindow( windows.objectAtIndex(i) ); + if not window.isVisible then + continue; + form:= window.lclGetTarget; + if not (form is TCustomForm) then + continue; + state:= TCustomForm(form).FormState; + if fsModal in state then begin + window.orderWindow_relativeTo( NSWindowAbove, topWindowNumber ); + topWindowNumber:= window.windowNumber; end; end; -end; -procedure TAppDelegate.applicationDidResignActive(notification: NSNotification); -begin Application.IntfAppDeactivate; Application.DoBeforeMouseMessage(nil); end; diff --git a/lcl/interfaces/cocoa/cocoawindows.pas b/lcl/interfaces/cocoa/cocoawindows.pas index 993f2ac88e..5817787dec 100644 --- a/lcl/interfaces/cocoa/cocoawindows.pas +++ b/lcl/interfaces/cocoa/cocoawindows.pas @@ -725,25 +725,14 @@ end; procedure TCocoaWindow.DoWindowDidBecomeKey(); begin - if CocoaWidgetSet.isModalSession then - self.orderFront(nil); + if Assigned(NSApp.keyWindow) then + NSApp.keyWindow.orderFrontRegardless; + CursorHelper.SetCursorOnActive(); 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; @@ -787,6 +776,8 @@ begin // MacOS does 10.7 fullscreen switch with an animation (that's about 1 second long) // if during that animation there's another call toggleFullScreen() is made // then macOS produces an output "not in fullscreen state" and ignores the call. + + self.setLevel( NSNormalWindowLevel ); end; procedure TCocoaWindow.windowDidEnterFullScreen(notification: NSNotification); @@ -800,6 +791,9 @@ begin if orderOutAfterFS then begin self.orderOut(nil); orderOutAfterFS := false; + end else begin + if self.level<>self.keepWinLevel then + self.setLevel(self.keepWinLevel); end; end;