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 }
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: TLCLHandle; override;
procedure EndMessageProcess(context: TLCLHandle); 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();

View File

@ -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;

View File

@ -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;