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 -
This commit is contained in:
dmitry 2018-09-17 01:33:50 +00:00
parent f2ecffd6b1
commit b3b559273c
8 changed files with 62 additions and 294 deletions

View File

@ -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

View File

@ -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;

View File

@ -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 isnt 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;

View File

@ -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;

View File

@ -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)

View File

@ -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;

View File

@ -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;

View File

@ -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