From 27fd42670e5c2bc3aad56b6c510f086b3f63ca0c Mon Sep 17 00:00:00 2001 From: rich2014 Date: Sat, 28 Oct 2023 19:23:39 +0800 Subject: [PATCH 1/4] Cocoa: new utils for Multi Display added --- lcl/interfaces/cocoa/cocoautils.pas | 90 +++++++++++++++++++++-------- 1 file changed, 67 insertions(+), 23 deletions(-) diff --git a/lcl/interfaces/cocoa/cocoautils.pas b/lcl/interfaces/cocoa/cocoautils.pas index 1e3b1a051a..824417dfbc 100644 --- a/lcl/interfaces/cocoa/cocoautils.pas +++ b/lcl/interfaces/cocoa/cocoautils.pas @@ -42,15 +42,21 @@ 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 IndexToHMonitor(i: NSUInteger): HMonitor; -function HMonitorToIndex(h: HMonitor): NSUInteger; -function getScreenFromHMonitor(h: HMonitor): NSScreen; +function ScreenPointFromLCLToNS(const lclPoint: TPoint): NSPoint; +function ScreenPointFromNSToLCL(const cocoaPoint: NSPoint): TPoint; +function ScreenRectFromLCLToNS(const lclRect: TRect): NSRect; +function ScreenRectFromNSToLCL(const cocoaRect: NSRect): TRect; function NSPrimaryScreen: NSScreen; function NSPrimaryScreenFrame: NSRect; function NSGlobalScreenFrame: NSRect; function NSGlobalScreenLCLFrame: NSRect; function NSGlobalScreenHeight: CGFloat; +function NSGlobalScreenBottom: CGFloat; + +function IndexToHMonitor(i: NSUInteger): HMonitor; +function HMonitorToIndex(h: HMonitor): NSUInteger; +function getScreenFromHMonitor(h: HMonitor): NSScreen; function CreateParamsToNSRect(const params: TCreateParams): NSRect; @@ -759,34 +765,35 @@ begin ns.size.height:=lcl.Bottom-lcl.Top; 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; +function ScreenPointFromLCLToNS(const lclPoint: TPoint): NSPoint; begin - if i = NSIntegerMax then Result := 0 - else Result := i + 1; + Result.x:= lclPoint.x; + Result.y:= NSGlobalScreenBottom - lclPoint.y; end; -function HMonitorToIndex(h: HMonitor): NSUInteger; +function ScreenPointFromNSToLCL(const cocoaPoint: NSPoint): TPoint; begin - if h = 0 then Result := NSIntegerMax - else Result := NSUInteger(h)-1; + Result.x:= Round( cocoaPoint.x ); + Result.y:= Round( NSGlobalScreenBottom - cocoaPoint.y ); end; -function getScreenFromHMonitor(h: HMonitor): NSScreen; +function ScreenRectFromLCLToNS(const lclRect: TRect): NSRect; +begin + Result.origin.x:= lclRect.left; + Result.origin.y:= NSGlobalScreenBottom - lclRect.bottom; + Result.size.width:= lclRect.Right - lclRect.Left; + Result.size.height:= lclRect.Bottom - lclRect.Top; +end; + +function ScreenRectFromNSToLCL(const cocoaRect: NSRect): TRect; var - index: NSUInteger; + bottom: CGFloat; begin - Result:= nil; - index:= HMonitorToIndex( h ); - if index>=NSScreen.screens.count then - Exit; - Result:= NSScreen( NSScreen.screens.objectAtIndex(index) ); + bottom:= NSGlobalScreenBottom; + Result.Left:= Round( cocoaRect.origin.x ); + Result.Top:= Round( bottom - NSMaxY(cocoaRect) ); + Result.Right:= Round( cocoaRect.origin.x + cocoaRect.size.width ); + Result.Bottom:= Round( bottom - cocoaRect.origin.y ); end; // primary display @@ -827,6 +834,43 @@ begin Result:= NSGlobalScreenFrame.size.height; end; +// the bottom of global full virtual display +// for cocoa window/screen to lcl coordinate +function NSGlobalScreenBottom: CGFloat; +begin + Result:= NSMaxY( NSGlobalScreenFrame ); +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 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; + function CreateParamsToNSRect(const params: TCreateParams): NSRect; begin with params do Result:=GetNSRect(X,Y,Width,Height); From e3a53a2a42577e73d9fcc19d537c70313691cabd Mon Sep 17 00:00:00 2001 From: rich2014 Date: Sat, 28 Oct 2023 19:26:56 +0800 Subject: [PATCH 2/4] Cocoa: new utils for Multi Display applied --- lcl/interfaces/cocoa/cocoaprivate.pas | 2 +- lcl/interfaces/cocoa/cocoawinapi.inc | 19 ++++++------------- lcl/interfaces/cocoa/cocoawindows.pas | 18 +++++++++--------- lcl/interfaces/cocoa/cocoawscommon.pas | 4 ++-- lcl/interfaces/cocoa/cocoawsmenus.pas | 2 +- 5 files changed, 19 insertions(+), 26 deletions(-) diff --git a/lcl/interfaces/cocoa/cocoaprivate.pas b/lcl/interfaces/cocoa/cocoaprivate.pas index 881acae889..6320a33f84 100644 --- a/lcl/interfaces/cocoa/cocoaprivate.pas +++ b/lcl/interfaces/cocoa/cocoaprivate.pas @@ -1144,7 +1144,7 @@ begin params.isFirstCall:= not hasMarkedText(); rect:= imeHandler.IMEGetTextBound( params ); - LCLToNSRect( rect, NSGlobalScreenHeight, Result ); + LCLToNSRect( rect, NSGlobalScreenBottom, Result ); end; procedure TCocoaFullControlEdit.unmarkText; diff --git a/lcl/interfaces/cocoa/cocoawinapi.inc b/lcl/interfaces/cocoa/cocoawinapi.inc index 04f70edd99..01a11e832a 100644 --- a/lcl/interfaces/cocoa/cocoawinapi.inc +++ b/lcl/interfaces/cocoa/cocoawinapi.inc @@ -1074,7 +1074,7 @@ begin contentView := TCocoaWindowContent(Handle); if (not contentView.isembedded) and Assigned(contentView.window) then - NSToLCLRect(contentView.window.frame, NSGlobalScreenHeight, ARect) + ARect := ScreenRectFromNSToLCL( contentView.window.frame ) else ARect := contentView.lclFrame; Result := 1; @@ -1138,19 +1138,13 @@ end; function TCocoaWidgetSet.GetCursorPos(var lpPoint: TPoint ): Boolean; begin - with NSEvent.mouseLocation do - begin - lpPoint.x := Round(x); - // cocoa returns cursor with inverted y coordinate - lpPoint.y := Round(NSGlobalScreenHeight-y); - end; + lpPoint:= ScreenPointFromNSToLCL( NSEvent.mouseLocation ); //debugln('GetCursorPos='+DbgS(lpPoint)); Result := True; end; function TCocoaWidgetSet.GetMonitorInfo(hMonitor: HMONITOR; lpmi: PMonitorInfo): Boolean; var - globalScreenHeight: CGFloat; ScreenID: NSScreen; begin Result := (lpmi <> nil) and (lpmi^.cbSize >= SizeOf(TMonitorInfo)); @@ -1159,9 +1153,8 @@ begin Result := Assigned(ScreenID); if not Result then Exit; - globalScreenHeight := NSGlobalScreenHeight; - NSToLCLRect(ScreenID.frame, globalScreenHeight, lpmi^.rcMonitor); - NSToLCLRect(ScreenID.visibleFrame, globalScreenHeight, lpmi^.rcWork); + lpmi^.rcMonitor:= ScreenRectFromNSToLCL( ScreenID.frame ); + lpmi^.rcWork:= ScreenRectFromNSToLCL( ScreenID.visibleFrame ); // according to the documentation the primary (0,0 coord screen) // is always and index 0 if HMonitorToIndex(hMonitor) = 0 then @@ -1477,7 +1470,7 @@ begin Exit; p.x:=Point.X; - p.y:=NSGlobalScreenHeight-Point.Y; + p.y:=NSGlobalScreenBottom-Point.Y; window := GetCocoaWindowAtPos(p); if Assigned(window) then Result:= HWND(window.contentView); @@ -1876,7 +1869,7 @@ var i: Integer; begin Result:= 0; - point:= LCLToNSPoint( ptScreenCoords, NSGlobalScreenHeight ); + point:= ScreenPointFromLCLToNS( ptScreenCoords ); if point.y>=1 then // NSPointInRect is (upper,left) inside point.y:= point.y-1; // (lower,right) outside diff --git a/lcl/interfaces/cocoa/cocoawindows.pas b/lcl/interfaces/cocoa/cocoawindows.pas index 8ad080b81d..dd1c18fbc1 100644 --- a/lcl/interfaces/cocoa/cocoawindows.pas +++ b/lcl/interfaces/cocoa/cocoawindows.pas @@ -446,7 +446,7 @@ begin if not isembedded then begin //Window bounds should return "client rect" in screen coordinates - NSToLCLRect(window.frame, NSGlobalScreenHeight, wfrm); + wfrm := ScreenRectFromNSToLCL(window.frame); Types.OffsetRect(Result, -Result.Left+wfrm.Left, -Result.Top+wfrm.Top); end; end; @@ -1250,7 +1250,7 @@ var begin f := frame; inc(X, Round(f.origin.x)); - inc(Y, Round(NSGlobalScreenHeight - NSMaxY(f))); + inc(Y, Round(NSGlobalScreenBottom - NSMaxY(f))); end; procedure LCLWindowExtension.lclScreenToLocal(var X, Y: Integer); @@ -1259,15 +1259,15 @@ var begin f := frame; dec(X, Round(f.origin.x)); - dec(Y, Round(NSGlobalScreenHeight - NSMaxY(f))); + dec(Y, Round(NSGlobalScreenBottom - NSMaxY(f))); end; function LCLWindowExtension.lclFrame: TRect; begin if Assigned(contentView) then - Result:=contentView.lclFrame + Result:= contentView.lclFrame else - NSToLCLRect(frame, NSGlobalScreenHeight, Result); + Result:= ScreenRectFromNSToLCL( frame ); end; function LCLWindowExtension.lclGetTopBarHeight:integer; @@ -1288,12 +1288,12 @@ var ns : NSRect; h : integer; begin - LCLToNSRect(r, NSGlobalScreenHeight, ns); + ns:= ScreenRectFromLCLToNS( r ); // add topbar height - h:=lclGetTopBarHeight; - ns.size.height:=ns.size.height+h; - ns.origin.y:=ns.origin.y-h; + h:= lclGetTopBarHeight; + ns.size.height:= ns.size.height + h; + ns.origin.y:= ns.origin.y - h; {$ifdef BOOLFIX} setFrame_display_(ns, Ord(isVisible)); {$else} diff --git a/lcl/interfaces/cocoa/cocoawscommon.pas b/lcl/interfaces/cocoa/cocoawscommon.pas index e8bf47905e..978e15265c 100644 --- a/lcl/interfaces/cocoa/cocoawscommon.pas +++ b/lcl/interfaces/cocoa/cocoawscommon.pas @@ -536,8 +536,8 @@ begin if lWindow <> nil then begin f := lWindow.frame; - Point.x := Point.x+f.origin.x; - Point.y := lWindow.screen.frame.size.height- f.origin.y - Point.y; + Point.x := Point.x + f.origin.x; + Point.y := NSGlobalScreenBottom - ( Point.y + f.origin.y ); end; end; diff --git a/lcl/interfaces/cocoa/cocoawsmenus.pas b/lcl/interfaces/cocoa/cocoawsmenus.pas index f4a4a489fd..830716b2e2 100644 --- a/lcl/interfaces/cocoa/cocoawsmenus.pas +++ b/lcl/interfaces/cocoa/cocoawsmenus.pas @@ -963,7 +963,7 @@ begin point:= TPoint.Create( x, y ); screen:= getScreenFromHMonitor( CocoaWidgetSet.MonitorFromPoint(point, MONITOR_DEFAULTTONULL) ); - mouseY:= NSGlobalScreenHeight - y; + mouseY:= NSGlobalScreenBottom - y; if Assigned(screen) then begin menuY:= screen.visibleFrame.origin.y + menu.size.height + 1; menuY:= max( mouseY, MenuY ); From 408f836d14aed45874bef88a5513eaaebbaaf6c7 Mon Sep 17 00:00:00 2001 From: rich2014 Date: Sat, 28 Oct 2023 19:29:51 +0800 Subject: [PATCH 3/4] Cocoa: TCocoaWidgetSet.GetWindowRelativePosition() removed TWidgetSet.GetWindowRelativePosition() works fine --- lcl/interfaces/cocoa/cocoaprivate.pas | 22 ---------------------- lcl/interfaces/cocoa/cocoawinapi.inc | 7 ------- lcl/interfaces/cocoa/cocoawinapih.inc | 1 - lcl/interfaces/cocoa/cocoawindows.pas | 20 -------------------- 4 files changed, 50 deletions(-) diff --git a/lcl/interfaces/cocoa/cocoaprivate.pas b/lcl/interfaces/cocoa/cocoaprivate.pas index 6320a33f84..2c4c3af7b7 100644 --- a/lcl/interfaces/cocoa/cocoaprivate.pas +++ b/lcl/interfaces/cocoa/cocoaprivate.pas @@ -138,7 +138,6 @@ type // Returns the position of the view or window, in the immediate // parent (view or screen), relative to its client coordinates system // Left and Top are always returned in LCL coordinate system. - procedure lclRelativePos(var Left, Top: Integer); message 'lclRelativePos::'; procedure lclLocalToScreen(var X, Y: Integer); message 'lclLocalToScreen::'; procedure lclScreenToLocal(var X, Y: Integer); message 'lclScreenToLocal::'; function lclParent: id; message 'lclParent'; @@ -177,7 +176,6 @@ type procedure lclInvalidateRect(const r: TRect); message 'lclInvalidateRect:'; reintroduce; procedure lclInvalidate; message 'lclInvalidate'; reintroduce; procedure lclUpdate; message 'lclUpdate'; reintroduce; - procedure lclRelativePos(var Left, Top: Integer); message 'lclRelativePos::'; reintroduce; procedure lclLocalToScreen(var X, Y: Integer); message 'lclLocalToScreen::'; reintroduce; procedure lclScreenToLocal(var X, Y: Integer); message 'lclScreenToLocal::'; reintroduce; function lclParent: id; message 'lclParent'; reintroduce; @@ -1216,10 +1214,6 @@ procedure LCLObjectExtension.lclUpdate; begin end; -procedure LCLObjectExtension.lclRelativePos(var Left,Top: Integer); -begin -end; - procedure LCLObjectExtension.lclLocalToScreen(var X,Y: Integer); begin end; @@ -1481,22 +1475,6 @@ begin //display; end; -procedure LCLViewExtension.lclRelativePos(var Left, Top: Integer); -var - sv : NSView; - fr : NSRect; -begin - Left := Round(frame.origin.x); - sv := superview; - if Assigned(sv) and (not sv.isFlipped) then - begin - fr := frame; - Top := Round(sv.frame.size.height - fr.origin.y - fr.size.height); - end - else - Top := Round(frame.origin.y); -end; - procedure LCLViewExtension.lclLocalToScreen(var X, Y:Integer); var P: NSPoint; diff --git a/lcl/interfaces/cocoa/cocoawinapi.inc b/lcl/interfaces/cocoa/cocoawinapi.inc index 01a11e832a..4d52b37bdb 100644 --- a/lcl/interfaces/cocoa/cocoawinapi.inc +++ b/lcl/interfaces/cocoa/cocoawinapi.inc @@ -1326,13 +1326,6 @@ begin Result:=0; end; -function TCocoaWidgetSet.GetWindowRelativePosition(Handle: hwnd; var Left, Top: Integer): boolean; -begin - Result := Handle <> 0; - if Result then - 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 diff --git a/lcl/interfaces/cocoa/cocoawinapih.inc b/lcl/interfaces/cocoa/cocoawinapih.inc index d1338903eb..a732e36f08 100644 --- a/lcl/interfaces/cocoa/cocoawinapih.inc +++ b/lcl/interfaces/cocoa/cocoawinapih.inc @@ -122,7 +122,6 @@ function GetViewPortOrgEx(DC: HDC; P: PPoint): Integer; override; {function GetWindowLong(Handle : hwnd; int: Integer): PtrInt; override;} function GetWindowOrgEx(dc : hdc; P : PPoint): Integer; override; function GetWindowRect(Handle: hwnd; var ARect: TRect): Integer; override; -function GetWindowRelativePosition(Handle: hwnd; var Left, Top: Integer): boolean; override; function GetWindowSize(Handle: hwnd; var Width, Height: Integer): boolean; override; function HideCaret(Handle: HWND): Boolean; override; diff --git a/lcl/interfaces/cocoa/cocoawindows.pas b/lcl/interfaces/cocoa/cocoawindows.pas index dd1c18fbc1..993f2ac88e 100644 --- a/lcl/interfaces/cocoa/cocoawindows.pas +++ b/lcl/interfaces/cocoa/cocoawindows.pas @@ -47,7 +47,6 @@ type procedure lclInvalidateRect(const r: TRect); message 'lclInvalidateRect:'; reintroduce; procedure lclInvalidate; message 'lclInvalidate'; reintroduce; procedure lclUpdate; message 'lclUpdate'; reintroduce; - procedure lclRelativePos(var Left, Top: Integer); message 'lclRelativePos::'; reintroduce; procedure lclLocalToScreen(var X, Y: Integer); message 'lclLocalToScreen::'; reintroduce; procedure lclScreenToLocal(var X, Y: Integer); message 'lclScreenToLocal::'; reintroduce; function lclFrame: TRect; message 'lclFrame'; reintroduce; @@ -222,7 +221,6 @@ type function lclOwnWindow: NSWindow; message 'lclOwnWindow'; procedure lclSetFrame(const r: TRect); override; function lclFrame: TRect; override; - procedure lclRelativePos(var Left, Top: Integer); override; procedure viewDidMoveToSuperview; override; procedure viewDidMoveToWindow; override; procedure viewWillMoveToWindow(newWindow: CocoaAll.NSWindow); override; @@ -451,14 +449,6 @@ begin end; end; -procedure TCocoaWindowContent.lclRelativePos(var Left, Top: Integer); -begin - if isembedded then - inherited lclRelativePos(Left, Top) - else - window.lclRelativePos(Left, Top); -end; - procedure TCocoaWindowContent.viewDidMoveToSuperview; begin inherited viewDidMoveToSuperview; @@ -1234,16 +1224,6 @@ begin contentView.lclUpdate; end; -procedure LCLWindowExtension.lclRelativePos(var Left, Top: Integer); -var - f: NSRect; -begin - 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; From a58d6acc3d518d11fdc96943dba4370f4dd069c3 Mon Sep 17 00:00:00 2001 From: rich2014 Date: Sat, 28 Oct 2023 19:31:49 +0800 Subject: [PATCH 4/4] Cocoa: cleanup in CocoaUtils --- lcl/interfaces/cocoa/cocoautils.pas | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lcl/interfaces/cocoa/cocoautils.pas b/lcl/interfaces/cocoa/cocoautils.pas index 824417dfbc..826d706f3f 100644 --- a/lcl/interfaces/cocoa/cocoautils.pas +++ b/lcl/interfaces/cocoa/cocoautils.pas @@ -51,7 +51,6 @@ function NSPrimaryScreen: NSScreen; function NSPrimaryScreenFrame: NSRect; function NSGlobalScreenFrame: NSRect; function NSGlobalScreenLCLFrame: NSRect; -function NSGlobalScreenHeight: CGFloat; function NSGlobalScreenBottom: CGFloat; function IndexToHMonitor(i: NSUInteger): HMonitor; @@ -828,12 +827,6 @@ begin 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; - // the bottom of global full virtual display // for cocoa window/screen to lcl coordinate function NSGlobalScreenBottom: CGFloat;