Cocoa/Form: #40606 improved, Merge branch 'cocoa/form'

This commit is contained in:
rich2014 2023-11-18 02:34:05 +08:00
commit 575e58d312
3 changed files with 94 additions and 85 deletions

View File

@ -53,25 +53,12 @@ type
{ TAppDelegate } { 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) TAppDelegate = objcclass(NSObject, NSApplicationDelegateProtocol)
public public
orderArray : PWinLevelOrderArray;
orderArrayCount : Integer;
procedure application_openFiles(sender: NSApplication; filenames: NSArray); procedure application_openFiles(sender: NSApplication; filenames: NSArray);
procedure applicationDidHide(notification: NSNotification); procedure applicationDidHide(notification: NSNotification);
procedure applicationDidUnhide(notification: NSNotification); procedure applicationDidUnhide(notification: NSNotification);
procedure applicationDidBecomeActive(notification: NSNotification); procedure applicationDidBecomeActive(notification: NSNotification);
procedure applicationWillResignActive(notification: NSNotification);
procedure applicationDidResignActive(notification: NSNotification); procedure applicationDidResignActive(notification: NSNotification);
procedure applicationDidChangeScreenParameters(notification: NSNotification); procedure applicationDidChangeScreenParameters(notification: NSNotification);
procedure applicationWillFinishLaunching(notification: NSNotification); procedure applicationWillFinishLaunching(notification: NSNotification);
@ -206,6 +193,10 @@ type
procedure AppBringToFront; override; procedure AppBringToFront; override;
procedure AppSetIcon(const Small, Big: HICON); override; procedure AppSetIcon(const Small, Big: HICON); override;
procedure AppSetTitle(const ATitle: string); 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: TLCLHandle; override; function BeginMessageProcess: TLCLHandle; override;
procedure EndMessageProcess(context: TLCLHandle); override; procedure EndMessageProcess(context: TLCLHandle); override;
@ -897,19 +888,13 @@ end;
function TCocoaWidgetSet.StartModal(awin: NSWindow; hasMenu: Boolean): Boolean; function TCocoaWidgetSet.StartModal(awin: NSWindow; hasMenu: Boolean): Boolean;
var var
sess : NSModalSession; sess : NSModalSession;
lvl : NSInteger;
begin begin
Result := false; Result := false;
if not Assigned(awin) then Exit; if not Assigned(awin) then Exit;
lvl := awin.level;
sess := NSApplication(NSApp).beginModalSessionForWindow(awin); sess := NSApplication(NSApp).beginModalSessionForWindow(awin);
if not Assigned(sess) then Exit; 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 not Assigned(Modals) then Modals := TList.Create;
MenuTrackCancelAll(); MenuTrackCancelAll();

View File

@ -319,6 +319,20 @@ begin
// There is no way to change the dock title // There is no way to change the dock title
end; 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; function TCocoaWidgetSet.GetLCLCapability(ACapability: TLCLCapability): PtrUInt;
begin begin
case ACapability of case ACapability of
@ -601,77 +615,93 @@ end;
procedure TAppDelegate.applicationDidBecomeActive(notification: NSNotification); procedure TAppDelegate.applicationDidBecomeActive(notification: NSNotification);
var var
i : integer; windows: NSArray;
window: NSWindow;
form: TObject;
style: TFormStyle;
i: Integer;
begin begin
// Cocoa changes level and order of windows to it's liking windows := NSApp.orderedWindows;
// (it happens between Will- and DidBecomeActive) for i:= windows.count-1 downto 0 do begin
// for example Model windows becoming level 8, window:= NSWindow( windows.objectAtIndex(i) );
// even if LCL set them to level 0 before. if not window.isVisible then
// 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
continue; continue;
if orderArray^[i].lvl=NSNormalWindowLevel then form:= window.lclGetTarget;
if not (form is TCustomForm) then
continue; continue;
orderArray^[i].win.setLevel( orderArray^[i].lvl ); if csDesigning in TCustomForm(form).ComponentState then
orderArray^[i].win.orderFrontRegardless; continue;
end; style:= TCustomForm(form).FormStyle;
orderArrayCount := 0; if style in fsAllNonSystemStayOnTop then
if orderArray <> nil then window.setLevel( NSFloatingWindowLevel );
begin
Freemem(orderArray);
orderArray := nil;
end; end;
Application.IntfAppActivate; Application.IntfAppActivate;
end; end;
procedure TAppDelegate.applicationWillResignActive(notification: NSNotification procedure TAppDelegate.applicationDidResignActive(notification: NSNotification);
);
var var
baseWindowNumber: NSInteger; lastWindowNumber: NSInteger;
topWindowNumber: NSInteger;
windows: NSArray; windows: NSArray;
window: NSWindow; window: NSWindow;
form: TObject; form: TObject;
info: PWinLevelOrder; style: TFormStyle;
state: TFormState;
i: Integer;
begin begin
windows := NSApp.orderedWindows; // no window in this space
if NSWindow.windowNumbersWithOptions(0).count = 0 then
Exit;
baseWindowNumber:= 0; windows:= NSApp.orderedWindows;
// 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 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 for window in windows do begin
if window.level=NSNormalWindowLevel then begin if not window.isVisible then
baseWindowNumber:= window.windowNumber; continue;
if window.level <> NSNormalWindowLevel then
continue;
topWindowNumber:= window.windowNumber;
break; break;
end; end;
end;
orderArrayCount:= windows.count; // bring up modal form
orderArray:= GetMem(orderArrayCount * sizeof(TWinLevelOrder)); for i:=windows.count-1 downto 0 do begin
window:= NSWindow( windows.objectAtIndex(i) );
info:= orderArray^; if not window.isVisible then
for window in windows do begin continue;
info^.win := window;
info^.lvl := window.level;
info^.ord := window.orderedIndex;
info^.vis := window.isVisible;
inc( info );
end;
for window in windows do begin
form:= window.lclGetTarget; form:= window.lclGetTarget;
if form is TCustomForm then begin if not (form is TCustomForm) then
if TCustomForm(form).FormStyle=fsStayOnTop then begin continue;
window.setLevel( NSNormalWindowLevel ); state:= TCustomForm(form).FormState;
window.orderWindow_relativeTo( NSWindowAbove, baseWindowNumber ); if fsModal in state then begin
window.orderWindow_relativeTo( NSWindowAbove, topWindowNumber );
topWindowNumber:= window.windowNumber;
end; end;
end; end;
end;
end;
procedure TAppDelegate.applicationDidResignActive(notification: NSNotification);
begin
Application.IntfAppDeactivate; Application.IntfAppDeactivate;
Application.DoBeforeMouseMessage(nil); Application.DoBeforeMouseMessage(nil);
end; end;

View File

@ -725,25 +725,14 @@ end;
procedure TCocoaWindow.DoWindowDidBecomeKey(); procedure TCocoaWindow.DoWindowDidBecomeKey();
begin begin
if CocoaWidgetSet.isModalSession then if Assigned(NSApp.keyWindow) then
self.orderFront(nil); NSApp.keyWindow.orderFrontRegardless;
CursorHelper.SetCursorOnActive(); CursorHelper.SetCursorOnActive();
end; end;
procedure TCocoaWindow.windowDidBecomeKey(notification: NSNotification); procedure TCocoaWindow.windowDidBecomeKey(notification: NSNotification);
begin 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 if Assigned(callback) then
callback.Activate; callback.Activate;
@ -787,6 +776,8 @@ begin
// MacOS does 10.7 fullscreen switch with an animation (that's about 1 second long) // 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 // if during that animation there's another call toggleFullScreen() is made
// then macOS produces an output "not in fullscreen state" and ignores the call. // then macOS produces an output "not in fullscreen state" and ignores the call.
self.setLevel( NSNormalWindowLevel );
end; end;
procedure TCocoaWindow.windowDidEnterFullScreen(notification: NSNotification); procedure TCocoaWindow.windowDidEnterFullScreen(notification: NSNotification);
@ -800,6 +791,9 @@ begin
if orderOutAfterFS then begin if orderOutAfterFS then begin
self.orderOut(nil); self.orderOut(nil);
orderOutAfterFS := false; orderOutAfterFS := false;
end else begin
if self.level<>self.keepWinLevel then
self.setLevel(self.keepWinLevel);
end; end;
end; end;