View Issue Details

IDProjectCategoryView StatusLast Update
0036049LazarusWidgetsetpublic2019-09-07 10:04
ReporterDavid JenkinsAssigned To 
PrioritynormalSeverityminorReproducibilityalways
Status newResolutionopen 
Product Version2.0.4Product Build 
Target VersionFixed in Version 
Summary0036049: Mapping between LCL Coordinates and Cocoa non-flipped coordinates not always correct
DescriptionMy system has two monitors of different size and pitch. I noticed that with our app, pop up windows didn't always display as expected depending on which screen the app was located on. So I went through and reviewed all of the code that maps back and forth between LCL coordinates (origin at top) and Cocoa (origin at bottom). The attached patch are fixes for what I found (and fixes the problems I was seeing with multiple screens).

Things I found:
1) Cocoa screen layout is always referenced with an origin set by NSScreens.screens.objectAtIndex(0). NSScreens.mainscreen is the current screen that has focus. Whenever mapping between LCL screen coords and Cocoa screen coords NSScreen0 should be used and not NSScreen.mainscreen.

I added a NSScreenZeroHeight() function to minimize the text needed to get Screen0

2) LCLViewExtension.lclframe and LCLViewExtension.lclSetFrame were not consistent in checking for flipped. lclframe only checked if Assigned(superview) but did not check for superview.isflipped before using superview.frame. This is different from lclsetFrame that checks for superview.isflipped.

3) LCLViewExtension.lclRelativePos() wasn't flipping coords but results as used are expected to be LCL Coordinates.

4) It was difficult stepping through some code to figure out which coordinate systems they were mapping to or from: LCL screen, Cocoa screen, LCL view, LCL Cocoa, etc. So some of patch is just adding comments to make it more explicit and also expanding some code steps to make it more explicit.

4)
TagsNo tags attached.
Fixed in Revision
LazTarget
WidgetsetCocoa
Attached Files
  • CocoaToLCLCoords.patch (11,421 bytes)
    Index: lcl/interfaces/cocoa/cocoaprivate.pas
    ===================================================================
    --- lcl/interfaces/cocoa/cocoaprivate.pas	(revision 61813)
    +++ lcl/interfaces/cocoa/cocoaprivate.pas	(working copy)
    @@ -1023,7 +1023,7 @@
     procedure LCLViewExtension.lclRelativePos(var Left, Top: Integer);
     begin
       Left := Round(frame.origin.x);
    -  Top := Round(frame.origin.y);
    +  Top := Round(superview.bounds.size.height - frame.origin.y);
     end;
     
     procedure LCLViewExtension.lclLocalToScreen(var X, Y:Integer);
    @@ -1031,19 +1031,21 @@
       P: NSPoint;
     
     begin
    -  // 1. convert to window base
    +  // Convert from View-lcl to View-cocoa
       P.x := X;
       if isFlipped then
    -    p.y := Y
    +    P.y := Y
       else
    -    P.y := frame.size.height-y;   // convert to Cocoa system
    +    P.y := frame.size.height - Y;
     
    -  P := convertPoint_ToView(P, nil);
    +  // Convert from View-cocoa to Window-cocoa
    +  P := convertPoint_toView(P, nil);
     
    -  X := Round(P.X);
    -  Y := Round(window.frame.size.height-P.Y); // convert to LCL system
    +  // Convert from Window-cocoa to Window-lcl
    +  X := Round(P.x);
    +  Y := Round(window.frame.size.height - P.y);
     
    -  // 2. convert window to screen
    +  // Use window function to convert fomr Window-lcl to Screen-lcl
       window.lclLocalToScreen(X, Y);
     end;
     
    @@ -1051,19 +1053,22 @@
     var
       P: NSPoint;
     begin
    -  // 1. convert from screen to window
    +  // use window function to onvert from Screen-lcl to Window-lcl
    +  window.lclScreenToLocal(X,Y);
     
    -  window.lclScreenToLocal(X, Y);
    +  // Convert from Window-lcl to Window-cocoa
       P.x := X;
    -  P.y := Round(window.frame.size.height-Y); // convert to Cocoa system
    +  P.y := window.frame.size.height - Y;
     
    -  // 2. convert from window to local
    -  P := convertPoint_FromView(P, nil);
    +  // Convert from Window-cocoa to View-cocoa
    +  P := convertPoint_fromView(P, nil);
    +
    +  // Convert from View-cocoa to View-lcl
       X := Round(P.x);
       if isFlipped then
         Y := Round(p.y)
       else
    -    Y := Round(frame.size.height-P.y);   // convert to Cocoa system
    +    Y := Round(frame.size.height - P.y);
     end;
     
     function LCLViewExtension.lclParent:id;
    @@ -1076,7 +1081,7 @@
       v: NSView;
     begin
       v := superview;
    -  if Assigned(v) then
    +  if Assigned(v) and not v.isFlipped then
         NSToLCLRect(frame, v.frame.size.height, Result)
       else
         Result := NSRectToRect(frame);
    Index: lcl/interfaces/cocoa/cocoautils.pas
    ===================================================================
    --- lcl/interfaces/cocoa/cocoautils.pas	(revision 61813)
    +++ lcl/interfaces/cocoa/cocoautils.pas	(working copy)
    @@ -42,6 +42,8 @@
     procedure NSToLCLRect(const ns: NSRect; ParentHeight: Single; out lcl: TRect);
     procedure LCLToNSRect(const lcl: TRect; ParentHeight: Single; out ns: NSRect);
     
    +function NSScreenZeroHeight: CGFloat;
    +
     function CreateParamsToNSRect(const params: TCreateParams): NSRect;
     
     function NSStringUtf8(s: PChar): NSString;
    @@ -573,6 +575,11 @@
       ns.size.height:=lcl.Bottom-lcl.Top;
     end;
     
    +function NSScreenZeroHeight: CGFloat;
    +begin
    +  Result := NSScreen(NSScreen.screens.objectAtIndex(0)).frame.size.height;
    +end;
    +
     function CreateParamsToNSRect(const params: TCreateParams): NSRect;
     begin
       with params do Result:=GetNSRect(X,Y,Width,Height);
    Index: lcl/interfaces/cocoa/cocoawinapi.inc
    ===================================================================
    --- lcl/interfaces/cocoa/cocoawinapi.inc	(revision 61813)
    +++ lcl/interfaces/cocoa/cocoawinapi.inc	(working copy)
    @@ -1008,7 +1008,7 @@
         SPI_GETWORKAREA:
         begin
           NSToLCLRect(NSScreen.mainScreen.visibleFrame
    -        , NSScreen.mainScreen.frame.size.height
    +        , NSScreenZeroHeight
             , TRect(pvParam^));
         end;
       else
    @@ -1106,7 +1106,7 @@
       begin
         lpPoint.x := Round(x);
         // cocoa returns cursor with inverted y coordinate
    -    lpPoint.y := Round(NSScreen.mainScreen.frame.size.height-y);
    +    lpPoint.y := Round(NSScreenZeroHeight - y);
       end;
       //debugln('GetCursorPos='+DbgS(lpPoint));
       Result := True;
    @@ -1114,7 +1114,7 @@
     
     function TCocoaWidgetSet.GetMonitorInfo(hMonitor: HMONITOR; lpmi: PMonitorInfo): Boolean;
     var
    -  ScreenID: NSScreen;
    +  Screen0, ScreenID: NSScreen;
       idx : NSUInteger;
     begin
       Result := (lpmi <> nil) and (lpmi^.cbSize >= SizeOf(TMonitorInfo));
    @@ -1123,9 +1123,10 @@
       Result := (idx < NSScreen.screens.count);
       if not Result then Exit;
     
    +  Screen0 := NSScreen.screens.objectAtIndex(0);
       ScreenID := NSScreen(NSScreen.screens.objectAtIndex(idx));
    -  lpmi^.rcMonitor := NSRectToRect(ScreenID.frame);
    -  NSToLCLRect(ScreenID.visibleFrame, ScreenID.frame.size.height, lpmi^.rcWork);
    +  NSToLCLRect(ScreenID.frame, Screen0.frame.size.height, lpmi^.rcMonitor);
    +  NSToLCLRect(ScreenID.visibleFrame, Screen0.frame.size.height, lpmi^.rcWork);
       // according to the documentation the primary (0,0 coord screen)
       // is always and index 0
       if idx = 0 then
    @@ -1490,7 +1491,7 @@
       begin
         window:=windows.objectAtIndex(win);
         p.x:=Point.X;
    -    p.y:=window.screen.frame.size.height-Point.Y;
    +    p.y:=NSScreenZeroHeight - Point.Y;
         winnr:=NSWindow.windowNumberAtPoint_belowWindowWithWindowNumber(p,0);
         windowbelowpoint:=NSWindow(NSApp.windowWithWindowNumber(winnr));
         if windowbelowpoint=window then
    Index: lcl/interfaces/cocoa/cocoawindows.pas
    ===================================================================
    --- lcl/interfaces/cocoa/cocoawindows.pas	(revision 61813)
    +++ lcl/interfaces/cocoa/cocoawindows.pas	(working copy)
    @@ -231,8 +231,6 @@
         function stringValue: NSString; message 'stringValue';
       end;
     
    -procedure NSScreenGetRect(sc: NSScreen; out r: TRect);
    -procedure NSScreenGetRect(sc: NSScreen; mainScreenHeight: double; out r: TRect);
     
     implementation
     
    @@ -437,7 +435,7 @@
       begin
         //Window bounds should return "client rect" in screen coordinates
         if Assigned(window.screen) then
    -      NSToLCLRect(window.frame, window.screen.frame.size.height, wfrm)
    +      NSToLCLRect(window.frame, NSScreenZeroHeight, wfrm)
         else
           wfrm := NSRectToRect(frame);
         OffsetRect(Result, -Result.Left+wfrm.Left, -Result.Top+wfrm.Top);
    @@ -1143,37 +1141,57 @@
     var
        f: NSRect;
     begin
    -  if Assigned(screen) then
    -  begin
    -    f:=frame;
    -    Left := Round(f.origin.x);
    -    Top := Round(screen.frame.size.height - f.size.height - f.origin.y);
    -    //debugln('Top:'+dbgs(Top));
    -  end;
    +  if not Assigned(screen) then
    +    exit;
    +
    +  // Get Frame which will be in Screen-cocoa
    +  f:=frame;
    +
    +  // Convert from Screen-cocoa to Screen-lcl
    +  Left := Round(f.origin.x);
    +  Top := Round(NSScreenZeroHeight - f.origin.y);
     end;
     
     procedure LCLWindowExtension.lclLocalToScreen(var X, Y:Integer);
     var
       f: NSRect;
    +  p: NSPoint;
     begin
    -  if Assigned(screen) then
    -  begin
    -    f := frame;
    -    inc(X, Round(f.origin.x));
    -    inc(Y, Round(screen.frame.size.height - f.size.height - f.origin.y));
    -  end;
    +  if not Assigned(screen) then
    +    exit;
    +
    +  // Convert Window-lcl to Window-cocoa
    +  p.x := X;
    +  p.y := frame.size.height - Y;
    +
    +  // Convert Window-cocoa to Screen-cocoa
    +  p.x := p.x + frame.origin.x;
    +  p.y := p.y + frame.origin.y;
    +
    +  // Convert Screen-cocoa to Screen-lcl
    +  X := Round(p.x);
    +  Y := Round(NSScreenZeroHeight - p.y);
     end;
     
     procedure LCLWindowExtension.lclScreenToLocal(var X, Y: Integer);
     var
       f: NSRect;
    +  p: NSPoint;
     begin
    -  if Assigned(screen) then
    -  begin
    -    f := frame;
    -    dec(X, Round(f.origin.x));
    -    dec(Y, Round(screen.frame.size.height - f.size.height - f.origin.y));
    -  end;
    +  if not Assigned(screen) then 
    +    exit;
    +
    +  // Convert Screen-lcl to Screen-cocoa
    +  p.x := X;
    +  p.y := NSScreenZeroHeight - Y;
    +
    +  // Convert Screen-cocoa to Window-cocoa
    +  p.x := p.x - frame.origin.x;
    +  p.y := p.y - frame.origin.y;
    +
    +  // Convert Window-cocoa to Window-lcl
    +  X := Round(p.x);
    +  Y := Round(frame.size.height - p.y);
     end;
     
     function LCLWindowExtension.lclFrame: TRect;
    @@ -1183,7 +1201,7 @@
       else
       begin
         if Assigned(screen) then
    -      NSToLCLRect(frame, screen.frame.size.height, Result)
    +      NSToLCLRect(frame, NSScreenZeroHeight, Result)
         else
           Result := NSRectToRect(frame);
       end;
    @@ -1202,48 +1220,6 @@
       Point.y := contentView.bounds.size.height - Point.y;
     end;
     
    -procedure NSScreenGetRect(sc: NSScreen; mainScreenHeight: double; out r: TRect);
    -var
    -  fr : NSRect;
    -begin
    -  fr := sc.frame;
    -  r := Bounds(
    -    Round(fr.origin.x),
    -    Round(fr.origin.y - fr.size.height + mainScreenHeight),
    -    Round(fr.size.width), Round(fr.size.height)
    -  );
    -end;
    -
    -procedure NSScreenGetRect(sc: NSScreen; out r: TRect);
    -begin
    -  NSScreenGetRect(sc, NSScreen.mainScreen.frame.size.height, r);
    -end;
    -
    -function GetScreenForPoint(x,y: Integer): NSScreen;
    -var
    -  scarr : NSArray;
    -  sc    : NSScreen;
    -  r     : TRect;
    -  h     : double;
    -  p     : TPoint;
    -  i     : Integer;
    -begin
    -  p.x := x;
    -  p.y := y;
    -  scarr := NSScreen.screens;
    -  h := NSScreen.mainScreen.frame.size.height;
    -  sc := NSScreen(scarr.objectAtIndex(0));
    -  for i:=0 to scarr.count-1 do begin
    -    sc:=NSScreen(scarr.objectAtIndex(i));
    -    NSScreenGetRect(sc, h, r);
    -    if Types.PtInRect(r, p) then begin
    -      Result := sc;
    -      Exit;
    -    end;
    -  end;
    -  Result := NSScreen.mainScreen;
    -end;
    -
     procedure LCLWindowExtension.lclSetFrame(const r: TRect);
     var
       ns : NSRect;
    @@ -1251,12 +1227,7 @@
       sc : NSScreen;
       srect : NSRect;
     begin
    -  sc := GetScreenForPoint(r.Left, r.Top);
    -  srect := sc.frame;
    -
    -  LCLToNSRect(r, srect.size.height, ns);
    -
    -  // add topbar height
    +  LCLToNSRect(r, NSScreenZeroHeight, ns);
       h:=lclGetTopBarHeight;
       ns.size.height:=ns.size.height+h;
       ns.origin.y:=ns.origin.y-h;
    Index: lcl/interfaces/cocoa/cocoawscommon.pas
    ===================================================================
    --- lcl/interfaces/cocoa/cocoawscommon.pas	(revision 61813)
    +++ lcl/interfaces/cocoa/cocoawscommon.pas	(working copy)
    @@ -401,12 +401,17 @@
       f: NSRect;
       lWindow: NSWindow;
     begin
    +  // Convert from Window-cocoa to Screen-lcl
       lWindow := NSWindow(GetNSObjectWindow(Owner));
       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;
    +    // Convert from Window-cocoa to Screen-cocoa
    +    Point.x := Point.x + f.origin.x;
    +    Point.y := Point.y + f.origin.y;
    +
    +    // Convert from Screen-cocoa to Screen-lcl
    +    Point.y := NSScreenZeroHeight - Point.y;
       end;
     end;
     
    @@ -498,8 +503,8 @@
       FillChar(MsgContext, SizeOf(MsgContext), #0);
       MsgContext.Msg := LM_CONTEXTMENU;
       MsgContext.hWnd := HWND(HandleFrame);
    -  MousePos := Event.locationInWindow;
    -  ScreenMousePos(MousePos);
    +  MousePos := Event.locationInWindow; // Window-cocoa Coordinates
    +  ScreenMousePos(MousePos);           // Convert to Screen-lcl
       MsgContext.XPos := Round(MousePos.X);
       MsgContext.YPos := Round(MousePos.Y);
       Rcp := Owner;
    Index: lcl/interfaces/cocoa/cocoawsmenus.pas
    ===================================================================
    --- lcl/interfaces/cocoa/cocoawsmenus.pas	(revision 61813)
    +++ lcl/interfaces/cocoa/cocoawsmenus.pas	(working copy)
    @@ -807,7 +807,7 @@
     function LCLCoordsToCocoa(AControl: TControl; X, Y: Integer): NSPoint;
     begin
       Result.x := X;
    -  Result.y := NSScreen.mainScreen.frame.size.height - Y;
    +  Result.y := NSScreenZeroHeight - Y;
       if AControl <> nil then Result.y := Result.y - AControl.Height;
     end;
     
    
    CocoaToLCLCoords.patch (11,421 bytes)

Activities

David Jenkins

2019-09-05 22:48

reporter  

CocoaToLCLCoords.patch (11,421 bytes)
Index: lcl/interfaces/cocoa/cocoaprivate.pas
===================================================================
--- lcl/interfaces/cocoa/cocoaprivate.pas	(revision 61813)
+++ lcl/interfaces/cocoa/cocoaprivate.pas	(working copy)
@@ -1023,7 +1023,7 @@
 procedure LCLViewExtension.lclRelativePos(var Left, Top: Integer);
 begin
   Left := Round(frame.origin.x);
-  Top := Round(frame.origin.y);
+  Top := Round(superview.bounds.size.height - frame.origin.y);
 end;
 
 procedure LCLViewExtension.lclLocalToScreen(var X, Y:Integer);
@@ -1031,19 +1031,21 @@
   P: NSPoint;
 
 begin
-  // 1. convert to window base
+  // Convert from View-lcl to View-cocoa
   P.x := X;
   if isFlipped then
-    p.y := Y
+    P.y := Y
   else
-    P.y := frame.size.height-y;   // convert to Cocoa system
+    P.y := frame.size.height - Y;
 
-  P := convertPoint_ToView(P, nil);
+  // Convert from View-cocoa to Window-cocoa
+  P := convertPoint_toView(P, nil);
 
-  X := Round(P.X);
-  Y := Round(window.frame.size.height-P.Y); // convert to LCL system
+  // Convert from Window-cocoa to Window-lcl
+  X := Round(P.x);
+  Y := Round(window.frame.size.height - P.y);
 
-  // 2. convert window to screen
+  // Use window function to convert fomr Window-lcl to Screen-lcl
   window.lclLocalToScreen(X, Y);
 end;
 
@@ -1051,19 +1053,22 @@
 var
   P: NSPoint;
 begin
-  // 1. convert from screen to window
+  // use window function to onvert from Screen-lcl to Window-lcl
+  window.lclScreenToLocal(X,Y);
 
-  window.lclScreenToLocal(X, Y);
+  // Convert from Window-lcl to Window-cocoa
   P.x := X;
-  P.y := Round(window.frame.size.height-Y); // convert to Cocoa system
+  P.y := window.frame.size.height - Y;
 
-  // 2. convert from window to local
-  P := convertPoint_FromView(P, nil);
+  // Convert from Window-cocoa to View-cocoa
+  P := convertPoint_fromView(P, nil);
+
+  // Convert from View-cocoa to View-lcl
   X := Round(P.x);
   if isFlipped then
     Y := Round(p.y)
   else
-    Y := Round(frame.size.height-P.y);   // convert to Cocoa system
+    Y := Round(frame.size.height - P.y);
 end;
 
 function LCLViewExtension.lclParent:id;
@@ -1076,7 +1081,7 @@
   v: NSView;
 begin
   v := superview;
-  if Assigned(v) then
+  if Assigned(v) and not v.isFlipped then
     NSToLCLRect(frame, v.frame.size.height, Result)
   else
     Result := NSRectToRect(frame);
Index: lcl/interfaces/cocoa/cocoautils.pas
===================================================================
--- lcl/interfaces/cocoa/cocoautils.pas	(revision 61813)
+++ lcl/interfaces/cocoa/cocoautils.pas	(working copy)
@@ -42,6 +42,8 @@
 procedure NSToLCLRect(const ns: NSRect; ParentHeight: Single; out lcl: TRect);
 procedure LCLToNSRect(const lcl: TRect; ParentHeight: Single; out ns: NSRect);
 
+function NSScreenZeroHeight: CGFloat;
+
 function CreateParamsToNSRect(const params: TCreateParams): NSRect;
 
 function NSStringUtf8(s: PChar): NSString;
@@ -573,6 +575,11 @@
   ns.size.height:=lcl.Bottom-lcl.Top;
 end;
 
+function NSScreenZeroHeight: CGFloat;
+begin
+  Result := NSScreen(NSScreen.screens.objectAtIndex(0)).frame.size.height;
+end;
+
 function CreateParamsToNSRect(const params: TCreateParams): NSRect;
 begin
   with params do Result:=GetNSRect(X,Y,Width,Height);
Index: lcl/interfaces/cocoa/cocoawinapi.inc
===================================================================
--- lcl/interfaces/cocoa/cocoawinapi.inc	(revision 61813)
+++ lcl/interfaces/cocoa/cocoawinapi.inc	(working copy)
@@ -1008,7 +1008,7 @@
     SPI_GETWORKAREA:
     begin
       NSToLCLRect(NSScreen.mainScreen.visibleFrame
-        , NSScreen.mainScreen.frame.size.height
+        , NSScreenZeroHeight
         , TRect(pvParam^));
     end;
   else
@@ -1106,7 +1106,7 @@
   begin
     lpPoint.x := Round(x);
     // cocoa returns cursor with inverted y coordinate
-    lpPoint.y := Round(NSScreen.mainScreen.frame.size.height-y);
+    lpPoint.y := Round(NSScreenZeroHeight - y);
   end;
   //debugln('GetCursorPos='+DbgS(lpPoint));
   Result := True;
@@ -1114,7 +1114,7 @@
 
 function TCocoaWidgetSet.GetMonitorInfo(hMonitor: HMONITOR; lpmi: PMonitorInfo): Boolean;
 var
-  ScreenID: NSScreen;
+  Screen0, ScreenID: NSScreen;
   idx : NSUInteger;
 begin
   Result := (lpmi <> nil) and (lpmi^.cbSize >= SizeOf(TMonitorInfo));
@@ -1123,9 +1123,10 @@
   Result := (idx < NSScreen.screens.count);
   if not Result then Exit;
 
+  Screen0 := NSScreen.screens.objectAtIndex(0);
   ScreenID := NSScreen(NSScreen.screens.objectAtIndex(idx));
-  lpmi^.rcMonitor := NSRectToRect(ScreenID.frame);
-  NSToLCLRect(ScreenID.visibleFrame, ScreenID.frame.size.height, lpmi^.rcWork);
+  NSToLCLRect(ScreenID.frame, Screen0.frame.size.height, lpmi^.rcMonitor);
+  NSToLCLRect(ScreenID.visibleFrame, Screen0.frame.size.height, lpmi^.rcWork);
   // according to the documentation the primary (0,0 coord screen)
   // is always and index 0
   if idx = 0 then
@@ -1490,7 +1491,7 @@
   begin
     window:=windows.objectAtIndex(win);
     p.x:=Point.X;
-    p.y:=window.screen.frame.size.height-Point.Y;
+    p.y:=NSScreenZeroHeight - Point.Y;
     winnr:=NSWindow.windowNumberAtPoint_belowWindowWithWindowNumber(p,0);
     windowbelowpoint:=NSWindow(NSApp.windowWithWindowNumber(winnr));
     if windowbelowpoint=window then
Index: lcl/interfaces/cocoa/cocoawindows.pas
===================================================================
--- lcl/interfaces/cocoa/cocoawindows.pas	(revision 61813)
+++ lcl/interfaces/cocoa/cocoawindows.pas	(working copy)
@@ -231,8 +231,6 @@
     function stringValue: NSString; message 'stringValue';
   end;
 
-procedure NSScreenGetRect(sc: NSScreen; out r: TRect);
-procedure NSScreenGetRect(sc: NSScreen; mainScreenHeight: double; out r: TRect);
 
 implementation
 
@@ -437,7 +435,7 @@
   begin
     //Window bounds should return "client rect" in screen coordinates
     if Assigned(window.screen) then
-      NSToLCLRect(window.frame, window.screen.frame.size.height, wfrm)
+      NSToLCLRect(window.frame, NSScreenZeroHeight, wfrm)
     else
       wfrm := NSRectToRect(frame);
     OffsetRect(Result, -Result.Left+wfrm.Left, -Result.Top+wfrm.Top);
@@ -1143,37 +1141,57 @@
 var
    f: NSRect;
 begin
-  if Assigned(screen) then
-  begin
-    f:=frame;
-    Left := Round(f.origin.x);
-    Top := Round(screen.frame.size.height - f.size.height - f.origin.y);
-    //debugln('Top:'+dbgs(Top));
-  end;
+  if not Assigned(screen) then
+    exit;
+
+  // Get Frame which will be in Screen-cocoa
+  f:=frame;
+
+  // Convert from Screen-cocoa to Screen-lcl
+  Left := Round(f.origin.x);
+  Top := Round(NSScreenZeroHeight - f.origin.y);
 end;
 
 procedure LCLWindowExtension.lclLocalToScreen(var X, Y:Integer);
 var
   f: NSRect;
+  p: NSPoint;
 begin
-  if Assigned(screen) then
-  begin
-    f := frame;
-    inc(X, Round(f.origin.x));
-    inc(Y, Round(screen.frame.size.height - f.size.height - f.origin.y));
-  end;
+  if not Assigned(screen) then
+    exit;
+
+  // Convert Window-lcl to Window-cocoa
+  p.x := X;
+  p.y := frame.size.height - Y;
+
+  // Convert Window-cocoa to Screen-cocoa
+  p.x := p.x + frame.origin.x;
+  p.y := p.y + frame.origin.y;
+
+  // Convert Screen-cocoa to Screen-lcl
+  X := Round(p.x);
+  Y := Round(NSScreenZeroHeight - p.y);
 end;
 
 procedure LCLWindowExtension.lclScreenToLocal(var X, Y: Integer);
 var
   f: NSRect;
+  p: NSPoint;
 begin
-  if Assigned(screen) then
-  begin
-    f := frame;
-    dec(X, Round(f.origin.x));
-    dec(Y, Round(screen.frame.size.height - f.size.height - f.origin.y));
-  end;
+  if not Assigned(screen) then 
+    exit;
+
+  // Convert Screen-lcl to Screen-cocoa
+  p.x := X;
+  p.y := NSScreenZeroHeight - Y;
+
+  // Convert Screen-cocoa to Window-cocoa
+  p.x := p.x - frame.origin.x;
+  p.y := p.y - frame.origin.y;
+
+  // Convert Window-cocoa to Window-lcl
+  X := Round(p.x);
+  Y := Round(frame.size.height - p.y);
 end;
 
 function LCLWindowExtension.lclFrame: TRect;
@@ -1183,7 +1201,7 @@
   else
   begin
     if Assigned(screen) then
-      NSToLCLRect(frame, screen.frame.size.height, Result)
+      NSToLCLRect(frame, NSScreenZeroHeight, Result)
     else
       Result := NSRectToRect(frame);
   end;
@@ -1202,48 +1220,6 @@
   Point.y := contentView.bounds.size.height - Point.y;
 end;
 
-procedure NSScreenGetRect(sc: NSScreen; mainScreenHeight: double; out r: TRect);
-var
-  fr : NSRect;
-begin
-  fr := sc.frame;
-  r := Bounds(
-    Round(fr.origin.x),
-    Round(fr.origin.y - fr.size.height + mainScreenHeight),
-    Round(fr.size.width), Round(fr.size.height)
-  );
-end;
-
-procedure NSScreenGetRect(sc: NSScreen; out r: TRect);
-begin
-  NSScreenGetRect(sc, NSScreen.mainScreen.frame.size.height, r);
-end;
-
-function GetScreenForPoint(x,y: Integer): NSScreen;
-var
-  scarr : NSArray;
-  sc    : NSScreen;
-  r     : TRect;
-  h     : double;
-  p     : TPoint;
-  i     : Integer;
-begin
-  p.x := x;
-  p.y := y;
-  scarr := NSScreen.screens;
-  h := NSScreen.mainScreen.frame.size.height;
-  sc := NSScreen(scarr.objectAtIndex(0));
-  for i:=0 to scarr.count-1 do begin
-    sc:=NSScreen(scarr.objectAtIndex(i));
-    NSScreenGetRect(sc, h, r);
-    if Types.PtInRect(r, p) then begin
-      Result := sc;
-      Exit;
-    end;
-  end;
-  Result := NSScreen.mainScreen;
-end;
-
 procedure LCLWindowExtension.lclSetFrame(const r: TRect);
 var
   ns : NSRect;
@@ -1251,12 +1227,7 @@
   sc : NSScreen;
   srect : NSRect;
 begin
-  sc := GetScreenForPoint(r.Left, r.Top);
-  srect := sc.frame;
-
-  LCLToNSRect(r, srect.size.height, ns);
-
-  // add topbar height
+  LCLToNSRect(r, NSScreenZeroHeight, ns);
   h:=lclGetTopBarHeight;
   ns.size.height:=ns.size.height+h;
   ns.origin.y:=ns.origin.y-h;
Index: lcl/interfaces/cocoa/cocoawscommon.pas
===================================================================
--- lcl/interfaces/cocoa/cocoawscommon.pas	(revision 61813)
+++ lcl/interfaces/cocoa/cocoawscommon.pas	(working copy)
@@ -401,12 +401,17 @@
   f: NSRect;
   lWindow: NSWindow;
 begin
+  // Convert from Window-cocoa to Screen-lcl
   lWindow := NSWindow(GetNSObjectWindow(Owner));
   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;
+    // Convert from Window-cocoa to Screen-cocoa
+    Point.x := Point.x + f.origin.x;
+    Point.y := Point.y + f.origin.y;
+
+    // Convert from Screen-cocoa to Screen-lcl
+    Point.y := NSScreenZeroHeight - Point.y;
   end;
 end;
 
@@ -498,8 +503,8 @@
   FillChar(MsgContext, SizeOf(MsgContext), #0);
   MsgContext.Msg := LM_CONTEXTMENU;
   MsgContext.hWnd := HWND(HandleFrame);
-  MousePos := Event.locationInWindow;
-  ScreenMousePos(MousePos);
+  MousePos := Event.locationInWindow; // Window-cocoa Coordinates
+  ScreenMousePos(MousePos);           // Convert to Screen-lcl
   MsgContext.XPos := Round(MousePos.X);
   MsgContext.YPos := Round(MousePos.Y);
   Rcp := Owner;
Index: lcl/interfaces/cocoa/cocoawsmenus.pas
===================================================================
--- lcl/interfaces/cocoa/cocoawsmenus.pas	(revision 61813)
+++ lcl/interfaces/cocoa/cocoawsmenus.pas	(working copy)
@@ -807,7 +807,7 @@
 function LCLCoordsToCocoa(AControl: TControl; X, Y: Integer): NSPoint;
 begin
   Result.x := X;
-  Result.y := NSScreen.mainScreen.frame.size.height - Y;
+  Result.y := NSScreenZeroHeight - Y;
   if AControl <> nil then Result.y := Result.y - AControl.Height;
 end;
 
CocoaToLCLCoords.patch (11,421 bytes)

Issue History

Date Modified Username Field Change
2019-09-05 22:48 David Jenkins New Issue
2019-09-05 22:48 David Jenkins File Added: CocoaToLCLCoords.patch