From b3b559273cef15c31d68028a285138dd9bb68620 Mon Sep 17 00:00:00 2001 From: dmitry Date: Mon, 17 Sep 2018 01:33:50 +0000 Subject: [PATCH] cocoa: reduce the amount of focus (responders) related code. Process the focus handling in NSWindow only (rather than each control individually). Update tab switch notification to report tab change prior to focus switch git-svn-id: trunk@59039 - --- lcl/interfaces/cocoa/cocoabuttons.pas | 16 --- lcl/interfaces/cocoa/cocoadatepicker.pas | 16 --- lcl/interfaces/cocoa/cocoaprivate.pas | 69 ++-------- lcl/interfaces/cocoa/cocoascrollers.pas | 32 ----- lcl/interfaces/cocoa/cocoatabcontrols.pas | 25 ++-- lcl/interfaces/cocoa/cocoatables.pas | 14 -- lcl/interfaces/cocoa/cocoatextedits.pas | 150 +--------------------- lcl/interfaces/cocoa/cocoawindows.pas | 34 +++++ 8 files changed, 62 insertions(+), 294 deletions(-) diff --git a/lcl/interfaces/cocoa/cocoabuttons.pas b/lcl/interfaces/cocoa/cocoabuttons.pas index ddf8fa2063..2001c3b3dc 100644 --- a/lcl/interfaces/cocoa/cocoabuttons.pas +++ b/lcl/interfaces/cocoa/cocoabuttons.pas @@ -69,8 +69,6 @@ type procedure dealloc; override; function initWithFrame(frameRect: NSRect): id; override; function acceptsFirstResponder: Boolean; override; - function becomeFirstResponder: Boolean; override; - function resignFirstResponder: Boolean; override; procedure drawRect(dirtyRect: NSRect); override; function lclGetCallback: ICommonCallback; override; procedure lclClearCallback; override; @@ -236,20 +234,6 @@ begin Result := True; end; -function TCocoaButton.becomeFirstResponder: Boolean; -begin - Result := inherited becomeFirstResponder; - if Assigned(callback) then - callback.BecomeFirstResponder; -end; - -function TCocoaButton.resignFirstResponder: Boolean; -begin - Result := inherited resignFirstResponder; - if Assigned(callback) then - callback.ResignFirstResponder; -end; - procedure TCocoaButton.drawRect(dirtyRect: NSRect); var ctx: NSGraphicsContext; begin diff --git a/lcl/interfaces/cocoa/cocoadatepicker.pas b/lcl/interfaces/cocoa/cocoadatepicker.pas index 46586d1315..46d8de65fb 100755 --- a/lcl/interfaces/cocoa/cocoadatepicker.pas +++ b/lcl/interfaces/cocoa/cocoadatepicker.pas @@ -30,8 +30,6 @@ type procedure mouseMoved(event: NSEvent); override; function acceptsFirstResponder: Boolean; override; - function becomeFirstResponder: Boolean; override; - function resignFirstResponder: Boolean; override; procedure setFrame(aframe: NSRect); override; end; @@ -73,20 +71,6 @@ begin Result := True; end; -function TCocoaDatePicker.becomeFirstResponder: Boolean; -begin - Result := inherited becomeFirstResponder; - if Assigned(callback) then - callback.BecomeFirstResponder; -end; - -function TCocoaDatePicker.resignFirstResponder: Boolean; -begin - Result := inherited resignFirstResponder; - if Assigned(callback) then - callback.ResignFirstResponder; -end; - function TCocoaDatePicker.lclGetCallback: ICommonCallback; begin Result := callback; diff --git a/lcl/interfaces/cocoa/cocoaprivate.pas b/lcl/interfaces/cocoa/cocoaprivate.pas index eac35f78fa..2fffab6609 100644 --- a/lcl/interfaces/cocoa/cocoaprivate.pas +++ b/lcl/interfaces/cocoa/cocoaprivate.pas @@ -186,8 +186,6 @@ type auxMouseByParent: Boolean; procedure dealloc; override; function acceptsFirstResponder: Boolean; override; - function becomeFirstResponder: Boolean; override; - function resignFirstResponder: Boolean; override; procedure drawRect(dirtyRect: NSRect); override; function lclGetCallback: ICommonCallback; override; procedure lclClearCallback; override; @@ -254,8 +252,6 @@ type public callback: ICommonCallback; function acceptsFirstResponder: Boolean; override; - function becomeFirstResponder: Boolean; override; - function resignFirstResponder: Boolean; override; function lclGetCallback: ICommonCallback; override; procedure lclClearCallback; override; procedure resetCursorRects; override; @@ -275,8 +271,6 @@ type TCocoaProgressIndicator = objcclass(NSProgressIndicator) callback: ICommonCallback; function acceptsFirstResponder: Boolean; override; - function becomeFirstResponder: Boolean; override; - function resignFirstResponder: Boolean; override; function lclGetCallback: ICommonCallback; override; procedure lclClearCallback; override; procedure resetCursorRects; override; @@ -318,8 +312,6 @@ type procedure drawRect(dirtyRect: NSRect); override; function acceptsFirstResponder: Boolean; override; - function becomeFirstResponder: Boolean; override; - function resignFirstResponder: Boolean; override; function lclGetCallback: ICommonCallback; override; procedure lclClearCallback; override; procedure resetCursorRects; override; @@ -368,10 +360,19 @@ var // todo: this should be a threadvar TrackedControl : NSObject = nil; +function isCallbackForSameObject(cb1, cb2: ICommonCallback): Boolean; + implementation uses CocoaInt; +function isCallbackForSameObject(cb1, cb2: ICommonCallback): Boolean; +begin + Result := Assigned(cb1) and Assigned(cb2); + if Result then + Result := (cb1 = cb2) or (cb1.GetTarget = cb2.GetTarget); +end; + {$I mackeycodes.inc} procedure SetViewDefaults(AView: NSView); @@ -453,20 +454,6 @@ begin Result := True; end; -function TCocoaGroupBox.becomeFirstResponder: Boolean; -begin - Result := inherited becomeFirstResponder; - if Assigned(callback) then - callback.BecomeFirstResponder; -end; - -function TCocoaGroupBox.resignFirstResponder: Boolean; -begin - Result := inherited resignFirstResponder; - if Assigned(callback) then - callback.ResignFirstResponder; -end; - function TCocoaGroupBox.lclGetCallback: ICommonCallback; begin Result := callback; @@ -523,20 +510,6 @@ begin Result := True; end; -function TCocoaCustomControl.becomeFirstResponder: Boolean; -begin - Result := inherited becomeFirstResponder; - if Assigned(callback) then - callback.BecomeFirstResponder; -end; - -function TCocoaCustomControl.resignFirstResponder: Boolean; -begin - Result := inherited resignFirstResponder; - if Assigned(callback) then - callback.ResignFirstResponder; -end; - function TCocoaCustomControl.acceptsFirstMouse(event: NSEvent): Boolean; begin // By default, a mouse-down event in a window that isn’t the key window @@ -1178,18 +1151,6 @@ begin Result:=True; end; -function TCocoaProgressIndicator.becomeFirstResponder: Boolean; -begin - Result := inherited becomeFirstResponder; - callback.BecomeFirstResponder; -end; - -function TCocoaProgressIndicator.resignFirstResponder: Boolean; -begin - Result := inherited resignFirstResponder; - callback.ResignFirstResponder; -end; - function TCocoaProgressIndicator.lclGetCallback: ICommonCallback; begin Result:=callback; @@ -1361,18 +1322,6 @@ begin Result := True; end; -function TCocoaSlider.becomeFirstResponder: Boolean; -begin - Result := inherited becomeFirstResponder; - callback.BecomeFirstResponder; -end; - -function TCocoaSlider.resignFirstResponder: Boolean; -begin - Result := inherited resignFirstResponder; - callback.ResignFirstResponder; -end; - function TCocoaSlider.lclGetCallback: ICommonCallback; begin Result:=callback; diff --git a/lcl/interfaces/cocoa/cocoascrollers.pas b/lcl/interfaces/cocoa/cocoascrollers.pas index a85489d747..2da1e059f2 100644 --- a/lcl/interfaces/cocoa/cocoascrollers.pas +++ b/lcl/interfaces/cocoa/cocoascrollers.pas @@ -44,8 +44,6 @@ type procedure dealloc; override; procedure setFrame(aframe: NSRect); override; function acceptsFirstResponder: Boolean; override; - function becomeFirstResponder: Boolean; override; - function resignFirstResponder: Boolean; override; function lclGetCallback: ICommonCallback; override; procedure lclClearCallback; override; procedure resetCursorRects; override; @@ -114,8 +112,6 @@ type procedure actionScrolling(sender: NSObject); message 'actionScrolling:'; function IsHorizontal: Boolean; message 'IsHorizontal'; function acceptsFirstResponder: Boolean; override; - function becomeFirstResponder: Boolean; override; - function resignFirstResponder: Boolean; override; function lclGetCallback: ICommonCallback; override; procedure lclClearCallback; override; procedure resetCursorRects; override; @@ -746,20 +742,6 @@ begin Result := True; end; -function TCocoaScrollView.becomeFirstResponder: Boolean; -begin - Result := inherited becomeFirstResponder; - if Assigned(callback) then - callback.BecomeFirstResponder; -end; - -function TCocoaScrollView.resignFirstResponder: Boolean; -begin - Result := inherited resignFirstResponder; - if Assigned(callback) then - callback.ResignFirstResponder; -end; - function TCocoaScrollView.lclGetCallback: ICommonCallback; begin Result := callback; @@ -946,20 +928,6 @@ begin Result := True; end; -function TCocoaScrollBar.becomeFirstResponder: Boolean; -begin - Result := inherited becomeFirstResponder; - if Assigned(callback) then - callback.BecomeFirstResponder; -end; - -function TCocoaScrollBar.resignFirstResponder: Boolean; -begin - Result := inherited resignFirstResponder; - if Assigned(callback) then - callback.ResignFirstResponder; -end; - function TCocoaScrollBar.lclGetCallback: ICommonCallback; begin Result := callback; diff --git a/lcl/interfaces/cocoa/cocoatabcontrols.pas b/lcl/interfaces/cocoa/cocoatabcontrols.pas index 5366cfcf7e..07ba977d3c 100644 --- a/lcl/interfaces/cocoa/cocoatabcontrols.pas +++ b/lcl/interfaces/cocoa/cocoatabcontrols.pas @@ -479,22 +479,27 @@ procedure TCocoaTabControl.tabView_willSelectTabViewItem(tabView: NSTabView; tabViewItem: NSTabViewItem); begin if Assigned(callback) then + begin callback.willSelectTabViewItem( IndexOfTab( self, tabViewItem) ); + + // This must be called, prior to notification about focus (firstResponder) change + // Focus changing goes as following: + // First page becomes visible + // Then focus is switching to the control of the page + // In Cocoa world, first "willSelect" runs, then "firstResponder" changes, then "didSelect" is fired + callback.didSelectTabViewItem( IndexOfTab( self, tabViewItem) ); + end; end; procedure TCocoaTabControl.tabView_didSelectTabViewItem(tabView: NSTabView; tabViewItem: NSTabViewItem); -//var - //i: Integer; - //lTabView, lCurSubview: NSView; - //lLCLControl: TWinControl; - //lBounds: TRect; - //lCurCallback: ICommonCallback; begin - if Assigned(callback) then - begin - callback.didSelectTabViewItem( IndexOfTab( self, tabViewItem) ); - end; + //it's called together with "willSelect" + + //if Assigned(callback) then + //begin + //callback.didSelectTabViewItem( IndexOfTab( self, tabViewItem) ); + //end; // The recent clean up, drove the workaround below unnecessary // (at least the problem is not observed) diff --git a/lcl/interfaces/cocoa/cocoatables.pas b/lcl/interfaces/cocoa/cocoatables.pas index d9b5fb4bd3..ced168d5da 100644 --- a/lcl/interfaces/cocoa/cocoatables.pas +++ b/lcl/interfaces/cocoa/cocoatables.pas @@ -77,8 +77,6 @@ type smallimages : NSMutableDictionary; function acceptsFirstResponder: Boolean; override; - function becomeFirstResponder: Boolean; override; - function resignFirstResponder: Boolean; override; function lclGetCallback: ICommonCallback; override; procedure lclClearCallback; override; @@ -420,18 +418,6 @@ begin Result := True; end; -function TCocoaTableListView.becomeFirstResponder: Boolean; -begin - Result := inherited becomeFirstResponder; - callback.BecomeFirstResponder; -end; - -function TCocoaTableListView.resignFirstResponder: Boolean; -begin - Result := inherited resignFirstResponder; - callback.ResignFirstResponder; -end; - function TCocoaTableListView.lclGetCallback: ICommonCallback; begin Result := callback; diff --git a/lcl/interfaces/cocoa/cocoatextedits.pas b/lcl/interfaces/cocoa/cocoatextedits.pas index 8376047e74..07950f48d1 100644 --- a/lcl/interfaces/cocoa/cocoatextedits.pas +++ b/lcl/interfaces/cocoa/cocoatextedits.pas @@ -59,9 +59,6 @@ type TCocoaTextField = objcclass(NSTextField) callback: ICommonCallback; function acceptsFirstResponder: Boolean; override; - function becomeFirstResponder: Boolean; override; - function RealResignFirstResponder: Boolean; message 'RealResignFirstResponder'; - function resignFirstResponder: Boolean; override; function lclGetCallback: ICommonCallback; override; procedure lclClearCallback; override; procedure resetCursorRects; override; @@ -86,9 +83,6 @@ type public callback: ICommonCallback; function acceptsFirstResponder: Boolean; override; - function becomeFirstResponder: Boolean; override; - function RealResignFirstResponder: Boolean; message 'RealResignFirstResponder'; - function resignFirstResponder: Boolean; override; procedure resetCursorRects; override; // key //procedure keyDown(event: NSEvent); override; -> keyDown doesn't work in NSTextField @@ -115,8 +109,6 @@ type supressTextChangeEvent: Integer; // if above zero, then don't send text change event function acceptsFirstResponder: Boolean; override; - function becomeFirstResponder: Boolean; override; - function resignFirstResponder: Boolean; override; function lclGetCallback: ICommonCallback; override; procedure lclClearCallback; override; procedure resetCursorRects; override; @@ -150,7 +142,7 @@ type TCocoaFieldEditor = objcclass(NSTextView) public - function resignFirstResponder: Boolean; override; + function lclGetCallback: ICommonCallback; override; // keyboard procedure keyDown(event: NSEvent); override; // mouse @@ -228,8 +220,6 @@ type list: TCocoaComboBoxList; resultNS: NSString; //use to return values to combo function acceptsFirstResponder: Boolean; override; - function becomeFirstResponder: Boolean; override; - function resignFirstResponder: Boolean; override; procedure textDidChange(notification: NSNotification); override; procedure textDidEndEditing(notification: NSNotification); override; // NSComboBoxDataSourceProtocol @@ -287,8 +277,6 @@ type isOwnerDrawn: Boolean; isOwnerMeasure: Boolean; function acceptsFirstResponder: Boolean; override; - function becomeFirstResponder: Boolean; override; - function resignFirstResponder: Boolean; override; procedure dealloc; override; function lclGetCallback: ICommonCallback; override; procedure lclClearCallback; override; @@ -327,8 +315,6 @@ type procedure StepperChanged(sender: NSObject); message 'StepperChanged:'; // lcl function acceptsFirstResponder: Boolean; override; - function becomeFirstResponder: Boolean; override; - function resignFirstResponder: Boolean; override; function lclGetCallback: ICommonCallback; override; procedure lclClearCallback; override; // NSViewFix @@ -352,9 +338,6 @@ type procedure controlTextDidChange(obj: NSNotification); override; // lcl function acceptsFirstResponder: Boolean; override; - function becomeFirstResponder: Boolean; override; - function RealResignFirstResponder: Boolean; message 'RealResignFirstResponder'; - function resignFirstResponder: Boolean; override; function lclGetCallback: ICommonCallback; override; procedure lclClearCallback; override; procedure resetCursorRects; override; @@ -493,24 +476,10 @@ begin Result := NSView(v); end; -function TCocoaFieldEditor.resignFirstResponder: Boolean; -var - v : NSObject; +function TCocoaFieldEditor.lclGetCallback: ICommonCallback; begin - v := GetEditBox(Self); - //DebugLn('[TCocoaFieldEditor.resignFirstResponder]'); - if (v <> nil) then - begin - if v.isKindOfClass_(TCocoaTextField) then - begin - TCocoaTextField(v).RealResignFirstResponder(); - end - else if v.isKindOfClass_(TCocoaSecureTextField) then - begin - TCocoaSecureTextField(v).RealResignFirstResponder(); - end; - end; - Result := inherited resignFirstResponder; + if Assigned(delegate) then Result := NSObject(delegate).lclGetCallback + else Result := nil; end; procedure TCocoaFieldEditor.keyDown(event: NSEvent); @@ -655,30 +624,6 @@ begin Result := True; end; -function TCocoaTextField.becomeFirstResponder: Boolean; -begin - Result := inherited becomeFirstResponder; - callback.BecomeFirstResponder; -end; - -function TCocoaTextField.RealResignFirstResponder: Boolean; -begin - callback.ResignFirstResponder; - Result := True; -end; - -// Do not propagate this event to the LCL, -// because Cocoa NSTextField loses focus as soon as it receives it -// and the shared editor gets focus instead. -// see NSWindow.fieldEditor:forObject: -// See http://www.cocoabuilder.com/archive/cocoa/103607-resignfirstresponder-called-immediately.html -// See http://stackoverflow.com/questions/3192905/nstextfield-not-noticing-lost-focus-when-pressing-tab -function TCocoaTextField.resignFirstResponder: Boolean; -begin - //DebugLn('[TCocoaTextField.resignFirstResponder]'); - Result := inherited resignFirstResponder; -end; - function TCocoaTextField.lclGetCallback: ICommonCallback; begin Result := callback; @@ -821,18 +766,6 @@ begin Result := True; end; -function TCocoaTextView.becomeFirstResponder: Boolean; -begin - Result := inherited becomeFirstResponder; - callback.BecomeFirstResponder; -end; - -function TCocoaTextView.resignFirstResponder: Boolean; -begin - Result := inherited resignFirstResponder; - callback.ResignFirstResponder; -end; - function TCocoaTextView.lclGetCallback: ICommonCallback; begin Result := callback; @@ -943,24 +876,6 @@ begin Result := True; end; -function TCocoaSecureTextField.becomeFirstResponder: Boolean; -begin - Result := inherited becomeFirstResponder; - callback.BecomeFirstResponder; -end; - -function TCocoaSecureTextField.RealResignFirstResponder: Boolean; -begin - callback.ResignFirstResponder; - Result := True; -end; - -function TCocoaSecureTextField.resignFirstResponder: Boolean; -begin - //DebugLn('[TCocoaTextField.resignFirstResponder]'); - Result := inherited resignFirstResponder; -end; - procedure TCocoaSecureTextField.resetCursorRects; begin if not callback.resetCursorRects then @@ -1144,18 +1059,6 @@ begin Result := True; end; -function TCocoaComboBox.becomeFirstResponder: Boolean; -begin - Result := inherited becomeFirstResponder; - callback.BecomeFirstResponder; -end; - -function TCocoaComboBox.resignFirstResponder: Boolean; -begin - Result := inherited resignFirstResponder; - callback.ResignFirstResponder; -end; - procedure TCocoaComboBox.textDidChange(notification: NSNotification); var TheEvent: NSEvent; @@ -1352,18 +1255,6 @@ begin Result := True; end; -function TCocoaReadOnlyComboBox.becomeFirstResponder: Boolean; -begin - Result := inherited becomeFirstResponder; - callback.BecomeFirstResponder; -end; - -function TCocoaReadOnlyComboBox.resignFirstResponder: Boolean; -begin - Result := inherited resignFirstResponder; - callback.ResignFirstResponder; -end; - procedure TCocoaReadOnlyComboBox.dealloc; begin if Assigned(list) then @@ -1649,20 +1540,6 @@ begin Result := True; end; -function TCocoaSpinEdit.becomeFirstResponder: Boolean; -begin - Result := Edit.becomeFirstResponder; - if Assigned(callback) then - callback.BecomeFirstResponder; -end; - -function TCocoaSpinEdit.resignFirstResponder: Boolean; -begin - Result := inherited resignFirstResponder; - if Assigned(callback) then - callback.ResignFirstResponder; -end; - function TCocoaSpinEdit.lclGetCallback: ICommonCallback; begin Result := callback; @@ -1822,25 +1699,6 @@ begin Result := True; end; -function TCocoaSpinEdit.becomeFirstResponder: Boolean; -begin - Result := inherited becomeFirstResponder; - callback.BecomeFirstResponder; -end; - -function TCocoaSpinEdit.RealResignFirstResponder: Boolean; -begin - callback.ResignFirstResponder; - Result := True; -end; - -// See TCocoaTextField.resignFirstResponder as to why this is done here -function TCocoaSpinEdit.resignFirstResponder: Boolean; -begin - //DebugLn('[TCocoaTextField.resignFirstResponder]'); - Result := inherited resignFirstResponder; -end; - function TCocoaSpinEdit.lclGetCallback: ICommonCallback; begin Result := callback; diff --git a/lcl/interfaces/cocoa/cocoawindows.pas b/lcl/interfaces/cocoa/cocoawindows.pas index 9c73e93182..29aa3c3af1 100644 --- a/lcl/interfaces/cocoa/cocoawindows.pas +++ b/lcl/interfaces/cocoa/cocoawindows.pas @@ -121,6 +121,10 @@ type isInFullScreen: Boolean; orderOutAfterFS : Boolean; fsview: TCocoaWindowContent; + + responderSwitch: Integer; + respInitCb : ICommonCallback; + function windowShouldClose(sender : id): LongBool; message 'windowShouldClose:'; procedure windowWillClose(notification: NSNotification); message 'windowWillClose:'; function windowWillReturnFieldEditor_toObject(sender: NSWindow; client: id): id; message 'windowWillReturnFieldEditor:toObject:'; @@ -160,6 +164,8 @@ type // NSDraggingDestinationCategory function draggingEntered(sender: NSDraggingInfoProtocol): NSDragOperation; override; function performDragOperation(sender: NSDraggingInfoProtocol): Boolean; override; + // windows + function makeFirstResponder(r: NSResponder): Boolean; override; // menu support procedure lclItemSelected(sender: id); message 'lclItemSelected:'; @@ -204,6 +210,7 @@ type procedure didAddSubview(aview: NSView); override; end; + implementation { TCocoaDesignOverlay } @@ -917,6 +924,33 @@ begin Result := True; end; +function TCocoaWindow.makeFirstResponder(r: NSResponder): Boolean; +var + cbnew: ICommonCallback; +begin + if (responderSwitch = 0) then + respInitCb := firstResponder.lclGetCallback; + + // makeFirstResponder calls can be recursive! + // the resulting NSResponder can be the same object (i.e. fieldEditor) + // yet, the callback should be the different anyway + + inc(responderSwitch); + Result:=inherited makeFirstResponder(r); + dec(responderSwitch); + + if (responderSwitch = 0) then + begin + cbnew := firstResponder.lclGetCallback; + + if not isCallbackForSameObject(respInitCb, cbnew) then + begin + if Assigned(respInitCb) then respInitCb.ResignFirstResponder; + if Assigned(cbnew) then cbnew.BecomeFirstResponder; + end; + end; +end; + procedure TCocoaWindow.lclItemSelected(sender: id); begin