diff --git a/lcl/interfaces/cocoa/cocoaint.pas b/lcl/interfaces/cocoa/cocoaint.pas index 9c567d1ba5..fa97fb09f1 100644 --- a/lcl/interfaces/cocoa/cocoaint.pas +++ b/lcl/interfaces/cocoa/cocoaint.pas @@ -517,21 +517,47 @@ var ev : NSEvent; p : NSPoint; wfr : NSRect; + windowNumbers : NSArray; + windowNumber : NSNumber; begin kw := app.keyWindow; + p := theEvent.mouseLocation; + + if Assigned(kw) then + begin + wfr := kw.contentRectForFrameRect(kw.frame); + // if mouse outside of ClientFrame of keyWindow, + // Cursor should be forced to default. + // see also: https://gitlab.com/freepascal.org/lazarus/lazarus/-/issues/40515 + if not NSPointInRect(p, wfr) then + begin + if Screen.Cursor=crDefault then + CursorHelper.ForceSetDefaultCursor + else + CursorHelper.SetScreenCursor; + end; + end; // mouse move was consumed by the focused window - if Assigned(kw) and NSPointInRect( theEvent.mouseLocation, kw.frame) then - Exit; + if Assigned(kw) and NSPointInRect(p, kw.frame) then + exit; - for w in app.windows do + // windowNumbersWithOptions() shoulde be used here. + // because windowNumbersWithOptions() return windowsNumber of visible windows + // from front to back, and NSAPP.windows return all windows not ordered. + windowNumbers := NSWindow.windowNumbersWithOptions(0); + for windowNumber in windowNumbers do begin - if w = kw then Continue; - if not w.isVisible then Continue; - // todo: check for enabled windows? modal windows? + w := app.windowWithWindowNumber(windowNumber.integerValue); + if not Assigned(w) then + continue; wfr := w.frame; - if not NSPointInRect( theEvent.mouseLocation, wfr) then Continue; + if not NSPointInRect( theEvent.mouseLocation, wfr) then + continue; + + if not w.isKindOfClass(TCocoaWindow) then + break; p := theEvent.mouseLocation; p.x := p.x - w.frame.origin.x; @@ -548,6 +574,7 @@ begin theEvent.pressure ); w.sendEvent(ev); + break; end; end; diff --git a/lcl/interfaces/cocoa/cocoawinapi.inc b/lcl/interfaces/cocoa/cocoawinapi.inc index bb5b65ca62..4f866adcf8 100644 --- a/lcl/interfaces/cocoa/cocoawinapi.inc +++ b/lcl/interfaces/cocoa/cocoawinapi.inc @@ -2526,8 +2526,10 @@ end; function TCocoaWidgetSet.SetCursor(ACursor: HCURSOR): HCURSOR; begin - if ACursor = 0 then ACursor:= Screen.Cursors[crDefault]; - TCocoaCursor(ACursor).SetCursor; + if (ACursor=0) or (ACursor=Screen.Cursors[crDefault]) then + CursorHelper.SetCursorAtMousePos + else + CursorHelper.SetNewCursor( TCocoaCursor(ACursor) ); Result := 0; end; diff --git a/lcl/interfaces/cocoa/cocoawindows.pas b/lcl/interfaces/cocoa/cocoawindows.pas index 03a8fadcc6..2524746932 100644 --- a/lcl/interfaces/cocoa/cocoawindows.pas +++ b/lcl/interfaces/cocoa/cocoawindows.pas @@ -1019,15 +1019,15 @@ begin end; // return proper focused responder by kind of class of NSResponder -function getProperFocusedResponder( const aResponder : NSResponder ): NSResponder; +function getProperFocusedResponder( const win : NSWindow; const aResponder : NSResponder ): NSResponder; begin if aResponder<>nil then Result := aResponder else - Result:= NSApp.keyWindow; + Result:= win; - if Result.isKindOfClass(NSWindow) then - Result:= TCocoaWindowContent(NSWindow(Result).contentView).documentView; + if Result.isKindOfClass(TCocoaWindow) then + Result:= TCocoaWindowContent(TCocoaWindow(Result).contentView).documentView; end; // return responder callback by kind of class of NSResponder @@ -1080,7 +1080,7 @@ begin inc( makeFirstResponderCount ); try lastResponder := self.firstResponder; - newResponder := getProperFocusedResponder( aResponder ); + newResponder := getProperFocusedResponder( self, aResponder ); if lastResponder = newResponder then exit; // do toggle Focused Control diff --git a/lcl/interfaces/cocoa/cocoawscommon.pas b/lcl/interfaces/cocoa/cocoawscommon.pas index ad0df68c6c..5a758b2290 100644 --- a/lcl/interfaces/cocoa/cocoawscommon.pas +++ b/lcl/interfaces/cocoa/cocoawscommon.pas @@ -24,11 +24,12 @@ type _lastCursor: NSCursor; public procedure SetNewCursor( newCursor:TCocoaCursor ); + procedure ForceSetDefaultCursor; + procedure SetCursorOnActive; + procedure SetScreenCursor; + procedure SetScreenCursorWhenNotDefault; public - class procedure SetCursorOnActive; class procedure SetCursorAtMousePos; - class procedure SetScreenCursor; - class procedure SetScreenCursorWhenNotDefault; end; { TLCLCommonCallback } @@ -380,9 +381,18 @@ begin end; end; -class procedure TCursorHelper.SetCursorOnActive; +procedure TCursorHelper.ForceSetDefaultCursor; +var + newCursor: TCocoaCursor; begin - CursorHelper._lastCursor:= nil; + newCursor:= TCocoaCursor(Screen.Cursors[crDefault]); + newCursor.SetCursor; + _lastCursor:= newCursor.Cursor; +end; + +procedure TCursorHelper.SetCursorOnActive; +begin + _lastCursor:= NSCursor.arrowCursor; if Screen.Cursor<>crDefault then SetScreenCursor else @@ -411,15 +421,16 @@ begin rect.origin, 0, 0, window.windowNumber, nil, 0, 0, 0); - window.lclGetCallback.MouseMove(event); + NSApp.postEvent_atStart(event, true); end; -class procedure TCursorHelper.SetScreenCursor; +procedure TCursorHelper.SetScreenCursor; begin + _lastCursor:= nil; TCocoaCursor(Screen.Cursors[Screen.Cursor]).SetCursor; end; -class procedure TCursorHelper.SetScreenCursorWhenNotDefault; +procedure TCursorHelper.SetScreenCursorWhenNotDefault; begin if Screen.Cursor<>crDefault then SetScreenCursor; @@ -1887,6 +1898,7 @@ class procedure TCocoaWSWinControl.SetCursor(const AWinControl: TWinControl; const ACursor: HCursor); var control: TControl; + topParent: TControl; begin //debugln('SetCursor '+AWinControl.name+' '+dbgs(ACursor)); @@ -1894,6 +1906,19 @@ begin if Screen.Cursor<>crDefault then exit; + // control cursor only should be set when mouse in the keyWindow. + // for the MacOS system automatic restore cursor feature(also an issue), + // it is more appropriate to set the default cursor when the mouse is out + // of the keyWindow, which has been set in TLCLCommonCallback.MouseMove(). + // the keyWindow here refers to the Client Frame, excluding TitleBar. + // see also: https://gitlab.com/freepascal.org/lazarus/lazarus/-/issues/40515 + topParent:= AWinControl.GetTopParent; + if topParent is TCustomForm then + begin + if NSView(TCustomForm(topParent).handle).window <> NSApp.keyWindow then + exit; + end; + // control cursor only need be set when mouse in AWinControl. // suppose there is a Button, which is to set a Cursor of a ListBox. // without the code here, it will be set to the Cursor of the ListBox diff --git a/lcl/interfaces/cocoa/cocoawsforms.pas b/lcl/interfaces/cocoa/cocoawsforms.pas index ac39e32399..9694b7783f 100644 --- a/lcl/interfaces/cocoa/cocoawsforms.pas +++ b/lcl/interfaces/cocoa/cocoawsforms.pas @@ -793,6 +793,8 @@ begin // 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 begin