Cocoa/Display: Merge branch 'cocoa/display', fix and improve Display related

This commit is contained in:
rich2014 2023-10-21 21:53:12 +08:00
parent eb2b425869
commit 1d09fd6361
7 changed files with 155 additions and 166 deletions

View File

@ -1144,11 +1144,7 @@ begin
params.isFirstCall:= not hasMarkedText();
rect:= imeHandler.IMEGetTextBound( params );
if isFlipped then
Result:= RectToNSRect( rect )
else
LCLToNSRect( rect, NSScreen.mainScreen.frame.size.height, Result );
LCLToNSRect( rect, NSGlobalScreenHeight, Result );
end;
procedure TCocoaFullControlEdit.unmarkText;

View File

@ -42,7 +42,14 @@ function NSRectToRect(const NS: NSRect): TRect;
procedure NSToLCLRect(const ns: NSRect; ParentHeight: Single; out lcl: TRect);
procedure LCLToNSRect(const lcl: TRect; ParentHeight: Single; out ns: NSRect);
function NSPrimaryScreenHeight: CGFloat;
function IndexToHMonitor(i: NSUInteger): HMonitor;
function HMonitorToIndex(h: HMonitor): NSUInteger;
function getScreenFromHMonitor(h: HMonitor): NSScreen;
function NSPrimaryScreen: NSScreen;
function NSPrimaryScreenFrame: NSRect;
function NSGlobalScreenFrame: NSRect;
function NSGlobalScreenLCLFrame: NSRect;
function NSGlobalScreenHeight: CGFloat;
function CreateParamsToNSRect(const params: TCreateParams): NSRect;
@ -752,14 +759,50 @@ begin
ns.size.height:=lcl.Bottom-lcl.Top;
end;
// the height of primary display
function NSPrimaryScreenHeight: CGFloat;
// According to the documentation of NSScreen.screen It's recommended
// not to cache NSScreen objects stored in the array. As those might change.
// However, according to the same documentation, the objects can change
// only with a notificatio sent out. BUT while using a macincloud (remote desktop)
// services, it was identified that NSScreen object CAN change without any notification.
// So, instead of passing NSScreen as HMonitor, only INDEX+1 in NSScreen.screen
// is used.
function IndexToHMonitor(i: NSUInteger): HMonitor;
begin
Result := NSScreen(NSScreen.screens.objectAtIndex(0)).frame.size.height;
if i = NSIntegerMax then Result := 0
else Result := i + 1;
end;
// the height of global full virtual display
function NSGlobalScreenHeight: CGFloat;
function HMonitorToIndex(h: HMonitor): NSUInteger;
begin
if h = 0 then Result := NSIntegerMax
else Result := NSUInteger(h)-1;
end;
function getScreenFromHMonitor(h: HMonitor): NSScreen;
var
index: NSUInteger;
begin
Result:= nil;
index:= HMonitorToIndex( h );
if index>=NSScreen.screens.count then
Exit;
Result:= NSScreen( NSScreen.screens.objectAtIndex(index) );
end;
// primary display
function NSPrimaryScreen: NSScreen;
begin
Result := NSScreen(NSScreen.screens.objectAtIndex(0));
end;
// the frame of primary display
function NSPrimaryScreenFrame: NSRect;
begin
Result := NSPrimaryScreen.frame;
end;
// the frame of global full virtual display, in LCL coordinate (left,bottom)
function NSGlobalScreenFrame: NSRect;
var
globalFrame: NSRect;
screen: NSScreen;
@ -768,7 +811,20 @@ begin
for screen in NSScreen.screens do begin
globalFrame:= NSUnionRect( globalFrame, screen.frame );
end;
Result:= globalFrame.size.height;
Result:= globalFrame;
end;
// the frame of global full virtual display, in LCL coordinate (left,top)
function NSGlobalScreenLCLFrame: NSRect;
begin
Result:= NSGlobalScreenFrame;
Result.origin.y:= NSPrimaryScreenFrame.size.height - NSMaxY(Result);
end;
// the height of global full virtual display
function NSGlobalScreenHeight: CGFloat;
begin
Result:= NSGlobalScreenFrame.size.height;
end;
function CreateParamsToNSRect(const params: TCreateParams): NSRect;

View File

@ -740,25 +740,6 @@ begin
end;
end;
// According to the documentation of NSScreen.screen It's recommended
// not to cache NSScreen objects stored in the array. As those might change.
// However, according to the same documentation, the objects can change
// only with a notificatio sent out. BUT while using a macincloud (remote desktop)
// services, it was identified that NSScreen object CAN change without any notification.
// So, instead of passing NSScreen as HMonitor, only INDEX+1 in NSScreen.screen
// is used.
function IndexToHMonitor(i: NSUInteger): HMonitor;
begin
if i = NSIntegerMax then Result := 0
else Result := i + 1;
end;
function HMonitorToIndex(h: HMonitor): NSUInteger;
begin
if h = 0 then Result := NSIntegerMax
else Result := NSUInteger(h)-1;
end;
function TCocoaWidgetSet.EnumDisplayMonitors(hdc: HDC; lprcClip: PRect;
lpfnEnum: MonitorEnumProc; dwData: LPARAM): LongBool;
var
@ -973,9 +954,6 @@ var
lCocoaWin: TCocoaWindow = nil;
lWinContent: TCocoaWindowContent = nil;
disableFS : Boolean;
const
NSFullScreenWindowMask = 1 shl 14;
begin
Result:=true;
{$ifdef VerboseCocoaWinAPI}
@ -1065,9 +1043,9 @@ begin
SPI_GETWHEELSCROLLLINES: PDword(pvPAram)^ := 1;
SPI_GETWORKAREA:
begin
NSToLCLRect(NSScreen(NSScreen.screens.objectAtIndex(0)).visibleFrame
, NSPrimaryScreenHeight
, TRect(pvParam^));
NSToLCLRect( NSPrimaryScreen.visibleFrame,
NSPrimaryScreenFrame.size.height,
TRect(pvParam^) );
end;
else
Result := False;
@ -1085,21 +1063,21 @@ end;
------------------------------------------------------------------------------}
function TCocoaWidgetSet.GetWindowRect(Handle: hwnd; var ARect: TRect): Integer;
var
dx, dy: Integer;
contentView: TCocoaWindowContent;
begin
if Handle <> 0 then
begin
ARect := NSObject(Handle).lclFrame;
if not NSObject(Handle).isKindOfClass_(NSWindow) then
begin
dx := 0;
dy := 0;
NSObject(Handle).lclLocalToScreen(dx, dy);
MoveRect(ARect, dx, dy);
end;
Result := 1;
end else
Result := 0;
Result := 0;
if Handle=0 then
exit;
if not NSObject(Handle).isKindOfClass(TCocoaWindowContent) then
exit;
contentView := TCocoaWindowContent(Handle);
if (not contentView.isembedded) and Assigned(contentView.window) then
NSToLCLRect(contentView.window.frame, NSGlobalScreenHeight, ARect)
else
ARect := contentView.lclFrame;
Result := 1;
end;
function TCocoaWidgetSet.IsWindowEnabled(Handle: HWND): boolean;
@ -1174,21 +1152,19 @@ function TCocoaWidgetSet.GetMonitorInfo(hMonitor: HMONITOR; lpmi: PMonitorInfo):
var
globalScreenHeight: CGFloat;
ScreenID: NSScreen;
idx : NSUInteger;
begin
Result := (lpmi <> nil) and (lpmi^.cbSize >= SizeOf(TMonitorInfo));
if not Result then Exit;
idx := HMonitorToIndex(hMonitor);
Result := (idx < NSScreen.screens.count);
ScreenID := getScreenFromHMonitor( hMonitor );
Result := Assigned(ScreenID);
if not Result then Exit;
globalScreenHeight := NSGlobalScreenHeight;
ScreenID := NSScreen(NSScreen.screens.objectAtIndex(idx));
NSToLCLRect(ScreenID.frame, globalScreenHeight, lpmi^.rcMonitor);
NSToLCLRect(ScreenID.visibleFrame, globalScreenHeight, lpmi^.rcWork);
// according to the documentation the primary (0,0 coord screen)
// is always and index 0
if idx = 0 then
if HMonitorToIndex(hMonitor) = 0 then
lpmi^.dwFlags := MONITORINFOF_PRIMARY
else
lpmi^.dwFlags := 0;
@ -1364,6 +1340,12 @@ begin
NSObject(handle).lclRelativePos(Left, Top);
end;
// 1. not only for Window, but also for other controls
// 2. for a Window, according to this function specification, Width and Height
// should be returned. but ClientWidth and ClientHeight were returned
// actually before.
// 3. after the LCL FORM specification determined, corresponding modifications
// need to be made.
function TCocoaWidgetSet.GetWindowSize(Handle: hwnd; var Width, Height: Integer): boolean;
var
r: TRect;
@ -1887,6 +1869,29 @@ begin
Result := CocoaRegionTypeToWin32Map[TCocoaRegion(RGN).GetType];
end;
function TCocoaWidgetSet.MonitorFromPoint(ptScreenCoords: TPoint; dwFlags: DWord): HMONITOR;
var
point: NSPoint;
screen: NSScreen;
i: Integer;
begin
Result:= 0;
point:= LCLToNSPoint( ptScreenCoords, NSGlobalScreenHeight );
if point.y>=1 then // NSPointInRect is (upper,left) inside
point.y:= point.y-1; // (lower,right) outside
for i := 0 to NSScreen.screens.count - 1 do begin
screen:= NSScreen( NSScreen.screens.objectAtIndex(i) );
if NSPointInRect(point, screen.frame) then begin
Result:= IndexToHMonitor( i );
Exit;
end;
end;
if dwFlags<>MONITOR_DEFAULTTONULL then
Result:= IndexToHMonitor( 0 );
end;
function TCocoaWidgetSet.MoveToEx(DC: HDC; X, Y: Integer; OldPoint: PPoint): Boolean;
var
ctx: TCocoaContext;
@ -2614,13 +2619,19 @@ begin
SM_CYVSCROLL:
Result := Round(NSScroller.scrollerWidthForControlSize(NSRegularControlSize));
SM_CXSCREEN,
SM_CXVIRTUALSCREEN,
SM_CXFULLSCREEN: Result := Round(NSScreen.mainScreen.frame.size.width);
SM_CXFULLSCREEN:
Result := Round(NSPrimaryScreenFrame.size.width);
SM_CYSCREEN,
SM_CYVIRTUALSCREEN,
SM_CYFULLSCREEN: Result := Round(NSScreen.mainScreen.frame.size.height);
SM_XVIRTUALSCREEN: Result := Round(NSScreen.mainScreen.frame.origin.x);
SM_YVIRTUALSCREEN: Result := Round(NSScreen.mainScreen.frame.origin.y);
SM_CYFULLSCREEN:
Result := Round(NSPrimaryScreenFrame.size.height);
SM_CXVIRTUALSCREEN:
Result := Round(NSGlobalScreenLCLFrame.size.width);
SM_CYVIRTUALSCREEN:
Result := Round(NSGlobalScreenLCLFrame.size.height);
SM_XVIRTUALSCREEN:
Result := Round(NSGlobalScreenLCLFrame.origin.x);
SM_YVIRTUALSCREEN:
Result := Round(NSGlobalScreenLCLFrame.origin.y);
SM_CXSMICON,
SM_CYSMICON:
Result := 16;

View File

@ -140,6 +140,8 @@ function LPtoDP(DC: HDC; var Points; Count: Integer): BOOL; override;
function OffsetRgn(RGN: HRGN; nXOffset, nYOffset: Integer): Integer; override;
function MonitorFromPoint(ptScreenCoords: TPoint; dwFlags: DWord): HMONITOR; override;
{function MessageBox(hWnd: HWND; lpText, lpCaption: PChar; uType: Cardinal): integer; override;}
function MoveToEx(DC: HDC; X, Y: Integer; OldPoint: PPoint): Boolean; override;

View File

@ -218,9 +218,7 @@ type
preventKeyOnShow: Boolean;
ownwin: NSWindow;
fswin: NSWindow; // window that was used as a content prior to switching to old-school fullscreen
popup_parent: HWND; // if not 0, indicates that we should set the popup parent
function performKeyEquivalent(event: NSEvent): LCLObjCBoolean; override;
procedure resolvePopupParent(); message 'resolvePopupParent';
function lclOwnWindow: NSWindow; message 'lclOwnWindow';
procedure lclSetFrame(const r: TRect); override;
function lclFrame: TRect; override;
@ -424,37 +422,6 @@ begin
Result := inherited performKeyEquivalent(event);
end;
procedure TCocoaWindowContent.resolvePopupParent();
var
lWindow: NSWindow;
isfront: Boolean;
begin
lWindow := nil;
if (popup_parent <> 0) then
begin
if (NSObject(popup_parent).isKindOfClass(TCocoaWindowContent)) then
begin
if (not TCocoaWindowContent(popup_parent).isembedded) then
lWindow := NSWindow(TCocoaWindowContent(popup_parent).window);
end
else
begin
lWindow := NSWindow(popup_parent);
end;
end;
if lWindow <> nil then
begin
isfront:=NSApplication(NSApp).mainWindow=self.window;
lWindow.addChildWindow_ordered(Self.window, NSWindowAbove);
// adding a window as a child, would bring the "child" form to the bottom
// of Zorder. need to restore the order.
if isfront then self.window.makeKeyAndOrderFront(nil);
end;
popup_parent := 0;
end;
function TCocoaWindowContent.lclOwnWindow: NSWindow;
begin
if not isembedded then
@ -479,10 +446,7 @@ begin
if not isembedded then
begin
//Window bounds should return "client rect" in screen coordinates
if Assigned(window.screen) then
NSToLCLRect(window.frame, NSGlobalScreenHeight, wfrm)
else
wfrm := NSRectToRect(frame);
NSToLCLRect(window.frame, NSGlobalScreenHeight, wfrm);
Types.OffsetRect(Result, -Result.Left+wfrm.Left, -Result.Top+wfrm.Top);
end;
end;
@ -1272,39 +1236,30 @@ end;
procedure LCLWindowExtension.lclRelativePos(var Left, Top: Integer);
var
f: NSRect;
f: NSRect;
begin
if Assigned(screen) then
begin
f:=frame;
Left := Round(f.origin.x);
Top := Round(NSGlobalScreenHeight - f.size.height - f.origin.y);
//debugln('Top:'+dbgs(Top));
end;
f:=frame;
Left := Round(f.origin.x);
Top := Round(NSGlobalScreenHeight - NSMaxY(f));
//debugln('Top:'+dbgs(Top));
end;
procedure LCLWindowExtension.lclLocalToScreen(var X, Y:Integer);
var
f: NSRect;
begin
if Assigned(screen) then
begin
f := frame;
inc(X, Round(f.origin.x));
inc(Y, Round(NSGlobalScreenHeight - f.size.height - f.origin.y));
end;
f := frame;
inc(X, Round(f.origin.x));
inc(Y, Round(NSGlobalScreenHeight - NSMaxY(f)));
end;
procedure LCLWindowExtension.lclScreenToLocal(var X, Y: Integer);
var
f: NSRect;
begin
if Assigned(screen) then
begin
f := frame;
dec(X, Round(f.origin.x));
dec(Y, Round(NSGlobalScreenHeight - f.size.height - f.origin.y));
end;
f := frame;
dec(X, Round(f.origin.x));
dec(Y, Round(NSGlobalScreenHeight - NSMaxY(f)));
end;
function LCLWindowExtension.lclFrame: TRect;
@ -1312,12 +1267,7 @@ begin
if Assigned(contentView) then
Result:=contentView.lclFrame
else
begin
if Assigned(screen) then
NSToLCLRect(frame, NSGlobalScreenHeight, Result)
else
Result := NSRectToRect(frame);
end;
NSToLCLRect(frame, NSGlobalScreenHeight, Result);
end;
function LCLWindowExtension.lclGetTopBarHeight:integer;

View File

@ -789,11 +789,6 @@ begin
cnt.wincallback := TCocoaWindow(win).callback;
win.setContentView(cnt);
// Don't call addChildWindow_ordered here because this function can cause
// events to arrive for this window, creating a second call to TCocoaWSCustomForm.CreateHandle
// while the first didn't finish yet, instead delay the call
cnt.popup_parent := AParams.WndParent;
win.makeFirstResponder(doc);
end
else
@ -945,11 +940,6 @@ begin
if (not fullscreen) and (lWinContent.window.isKindOfClass(TCocoaWindow)) then
fullscreen := TCocoaWindow(lWinContent.window).lclIsFullScreen;
// A window opening in full screen doesn't like to be added as someones popup
// Thus resolvePopupParent should only be used for non full-screens forms
//if (lWinContent <> nil) and (not fullscreen) then
//lWinContent.resolvePopupParent();
CocoaWidgetSet.CurModalForm := lWinContent.lclOwnWindow;
// LCL initialization code would cause the custom form to be disabled
// (due to the fact, ShowModal() has not been called yet, and a previous form

View File

@ -946,44 +946,28 @@ end;
class procedure TCocoaWSPopupMenu.Popup(const APopupMenu: TPopupMenu; const X,
Y: Integer);
var
menu : TCocoaMenu;
view : NSView;
w : NSWindow;
px, py: Integer;
menuY: Integer;
menu: TCocoaMenu;
point: TPoint;
screen: NSScreen;
mouseY: CGFloat;
menuY: CGFloat;
begin
if (not Assigned(APopupMenu)) or (APopupMenu.Handle=0) then
exit;
px := x;
py := y;
menu := TCocoaMenu(APopupMenu.Handle);
view := nil;
w :=NSApp.keyWindow;
// in macOS it's possible to "rightclick" without focusing a window
// so let's try to find the window
if not Assigned(w) then
w := GetCocoaWindowAtPos( NSMakePoint(px, Round(NSGlobalScreenHeight) - py) );
point:= TPoint.Create( x, y );
screen:= getScreenFromHMonitor( CocoaWidgetSet.MonitorFromPoint(point, MONITOR_DEFAULTTONULL) );
if Assigned(w) then
begin
view := w.contentView;
if Assigned(view) then
begin
// LCL Screen coordinate
menuY := Round(NSGlobalScreenHeight - w.screen.visibleFrame.origin.y - menu.size.height) - 1;
py := min(py, menuY);
view.lclScreenToLocal(px, py);
// have to flip again, because popUpMenuPositioningItem expects point
// to be in View coordinates and it does respect Flipped flag
if not view.isFlipped then
py := Round(view.frame.size.height) - py;
end;
end
else
py := Round(NSGlobalScreenHeight) - py;
mouseY:= NSGlobalScreenHeight - y;
if Assigned(screen) then begin
menuY:= screen.visibleFrame.origin.y + menu.size.height + 1;
menuY:= max( mouseY, MenuY );
end else begin
menuY:= mouseY;
end;
menu.popUpMenuPositioningItem_atLocation_inView(nil, NSMakePoint(px, py), view);
menu.popUpMenuPositioningItem_atLocation_inView(nil, NSMakePoint(x,menuY), nil);
APopupMenu.Close; // notify LCL popup menu
end;