View Issue Details

IDProjectCategoryView StatusLast Update
0025362LazarusWidgetsetpublic2013-12-09 17:34
ReporteraccSoneAssigned ToPaul Ishenin 
PrioritynormalSeverityminorReproducibilityalways
Status resolvedResolutionfixed 
Platformdarwin x86_64OSOSXOS Version10.6.8
Product Version1.3 (SVN)Product Build 
Target VersionFixed in Version1.4 
Summary0025362: Cocoa Widgetset hangs in endless loop once TEdit becomes focus
DescriptionSetFocus triggers BecomesFirstResponder Message that triggeres SetFocus again:

See stack:

#0 SETFOCUS(0x1028e90c0, 4327572864) at cocoawinapi.inc:1700
0000001 SETFOCUS(4327572864) at winapi.inc:926
0000002 WNDPROC(0x102841410, {MSG = 7, UNUSEDMSG = 0, WPARAM = 0, LPARAM = 0, RESULT = 0}) at customform.inc:1396
0000003 DELIVERMESSAGE(0x102841410, 0) at lclmessageglue.pas:112
0000004 SENDSIMPLEMESSAGE(0x102841410, 7) at lclmessageglue.pas:143
0000005 LCLSENDSETFOCUSMSG(0x102841410) at lclmessageglue.pas:193
0000006 BECOMEFIRSTRESPONDER(0x10288a040) at cocoa/cocoawscommon.pas:776
0000007 \"-[TCocoaPanel becomeFirstResponder]\"((^TCocoaPanel) 0x102d2fbb0, 0x7fff89605ad8) at cocoa/cocoaprivate.pp:492
0000008 -[NSWindow makeFirstResponder:] at :0
0000009 -[NSWindow endEditingFor:] at :0
0000010 -[NSView removeFromSuperview] at :0
0000011 -[_NSKeyboardFocusClipView removeFromSuperview] at :0
0000012 -[NSWindow endEditingFor:] at :0
0000013 -[NSView removeFromSuperview] at :0
0000014 -[_NSKeyboardFocusClipView removeFromSuperview] at :0
0000015 -[NSCell endEditing:] at :0
0000016 -[NSTextField textDidEndEditing:] at :0
0000017 _nsnote_callback at :0
0000018 __CFXNotificationPost at :0
0000019 _CFXNotificationPostNotification at :0
0000020 -[NSNotificationCenter postNotificationName:object:userInfo:] at :0
0000021 -[NSTextView(NSSharing) resignFirstResponder] at :0
0000022 -[NSWindow makeFirstResponder:] at :0
0000023 SETFOCUS(0x1028e90c0, 4327572864) at cocoawinapi.inc:1700
0000024 SETFOCUS(4327572864) at winapi.inc:926
0000025 UPDATESHOWING(0x102841410) at customform.inc:2703
0000026 DOALLAUTOSIZE(0x102841410) at wincontrol.inc:3547
0000027 ENABLEAUTOSIZING(0x102841410) at control.inc:5429
0000028 SETVISIBLE(0x102841410, true) at control.inc:4269
0000029 SETVISIBLE(0x102841410, true) at customform.inc:486
0000030 SHOW(0x102841410) at customform.inc:2183
0000031 RUN(0x1028403b0) at application.inc:1377
0000032 PASCALMAIN at project1.lpr:19
0000033 SYSTEM_FPC_SYSTEMMAIN$LONGINT$PPCHAR$PPCHAR at :0
Steps To ReproducePut a TEdit and a TButton on a TForm and compile it with Cocoa Widgetset. As soon as TEdit gots the focus program hangs.
TagsNo tags attached.
Fixed in Revision43525,43526
LazTarget-
WidgetsetCocoa
Attached Files
  • fix_0025362.patch (2,197 bytes)
    Index: /Developer/lazarus_trunc/lcl/interfaces/cocoa/cocoaprivate.pp
    ===================================================================
    --- /Developer/lazarus_trunc/lcl/interfaces/cocoa/cocoaprivate.pp	(revision 43444)
    +++ /Developer/lazarus_trunc/lcl/interfaces/cocoa/cocoaprivate.pp	(working copy)
    @@ -243,6 +243,7 @@
       { TCocoaPanel }
     
       TCocoaPanel = objcclass(NSPanel, NSWindowDelegateProtocol)
    +
       protected
         function windowShouldClose(sender : id): LongBool; message 'windowShouldClose:';
         procedure windowWillClose(notification: NSNotification); message 'windowWillClose:';
    @@ -251,6 +252,7 @@
         procedure windowDidResize(notification: NSNotification); message 'windowDidResize:';
         procedure windowDidMove(notification: NSNotification); message 'windowDidMove:';
       public
    +    FocusSender:TCocoaPanel;      // for detecting Focus loops
         callback: IWindowCallback;
         function acceptsFirstResponder: Boolean; override;
         function canBecomeKeyWindow: Boolean; override;
    @@ -488,8 +490,13 @@
     function TCocoaPanel.becomeFirstResponder: Boolean;
     begin
       Result := inherited becomeFirstResponder;
    +  if TCocoaPanel(FocusSender)<>self then
    +  begin
       if Assigned(callback) then
         callback.BecomeFirstResponder;
    +  end else
    +      FocusSender:=nil;
    +
     end;
     
     function TCocoaPanel.resignFirstResponder: Boolean;
    
    Index: /Developer/lazarus_trunc/lcl/interfaces/cocoa/cocoawinapi.inc
    ===================================================================
    --- /Developer/lazarus_trunc/lcl/interfaces/cocoa/cocoawinapi.inc	(revision 43471)
    +++ /Developer/lazarus_trunc/lcl/interfaces/cocoa/cocoawinapi.inc	(working copy)
    
    +
     function TCocoaWidgetSet.SetFocus(Handle: HWND): HWND;
     var
       Obj: NSObject;
    +  Res:NSResponder;
     begin
    +  Result:=GetFocus;
    +
       if Handle <> 0 then
       begin
    +
         if Result = Handle then
           Exit;
         Obj := NSObject(Handle);
    @@ -1696,6 +1701,7 @@
         else
         if Obj.isKindOfClass(NSView) then
         begin
    +      TCocoaPanel(NSView(Obj).window).FocusSender:=TCocoaPanel(NSView(Obj).window);   // anti focus loop detection
           NSView(Obj).window.makeKeyWindow;
           NSView(Obj).window.makeFirstResponder(NSView(Obj));
         end;
    
    fix_0025362.patch (2,197 bytes)
  • focusset.zip (2,055 bytes)

Activities

accSone

2013-11-24 11:55

developer   ~0071531

Fix is not very nice because it tries to detect the loop by transferring the calling TCocoaPanel reference. But it works. Additional I fixed the missing SetFocus result.

accSone

2013-11-24 11:56

developer  

fix_0025362.patch (2,197 bytes)
Index: /Developer/lazarus_trunc/lcl/interfaces/cocoa/cocoaprivate.pp
===================================================================
--- /Developer/lazarus_trunc/lcl/interfaces/cocoa/cocoaprivate.pp	(revision 43444)
+++ /Developer/lazarus_trunc/lcl/interfaces/cocoa/cocoaprivate.pp	(working copy)
@@ -243,6 +243,7 @@
   { TCocoaPanel }
 
   TCocoaPanel = objcclass(NSPanel, NSWindowDelegateProtocol)
+
   protected
     function windowShouldClose(sender : id): LongBool; message 'windowShouldClose:';
     procedure windowWillClose(notification: NSNotification); message 'windowWillClose:';
@@ -251,6 +252,7 @@
     procedure windowDidResize(notification: NSNotification); message 'windowDidResize:';
     procedure windowDidMove(notification: NSNotification); message 'windowDidMove:';
   public
+    FocusSender:TCocoaPanel;      // for detecting Focus loops
     callback: IWindowCallback;
     function acceptsFirstResponder: Boolean; override;
     function canBecomeKeyWindow: Boolean; override;
@@ -488,8 +490,13 @@
 function TCocoaPanel.becomeFirstResponder: Boolean;
 begin
   Result := inherited becomeFirstResponder;
+  if TCocoaPanel(FocusSender)<>self then
+  begin
   if Assigned(callback) then
     callback.BecomeFirstResponder;
+  end else
+      FocusSender:=nil;
+
 end;
 
 function TCocoaPanel.resignFirstResponder: Boolean;

Index: /Developer/lazarus_trunc/lcl/interfaces/cocoa/cocoawinapi.inc
===================================================================
--- /Developer/lazarus_trunc/lcl/interfaces/cocoa/cocoawinapi.inc	(revision 43471)
+++ /Developer/lazarus_trunc/lcl/interfaces/cocoa/cocoawinapi.inc	(working copy)

+
 function TCocoaWidgetSet.SetFocus(Handle: HWND): HWND;
 var
   Obj: NSObject;
+  Res:NSResponder;
 begin
+  Result:=GetFocus;
+
   if Handle <> 0 then
   begin
+
     if Result = Handle then
       Exit;
     Obj := NSObject(Handle);
@@ -1696,6 +1701,7 @@
     else
     if Obj.isKindOfClass(NSView) then
     begin
+      TCocoaPanel(NSView(Obj).window).FocusSender:=TCocoaPanel(NSView(Obj).window);   // anti focus loop detection
       NSView(Obj).window.makeKeyWindow;
       NSView(Obj).window.makeFirstResponder(NSView(Obj));
     end;
fix_0025362.patch (2,197 bytes)

Dmitry Boyarintsev

2013-11-24 16:30

developer   ~0071548

Last edited: 2013-11-24 16:32

View 2 revisions

This looks odd. It appears that AppKit itself caused the recursion. It ended with calling makeFirstResponder: as a reaction on the call of makeFirstResponder:

I do recall the similar refocus detection made in Carbon, however it was introduced for other purposes as well. Plus it was made on the global level, rather than window level only.

I'm wondering if the similar issue can be caused by switching focus within controls (2 TEdits on a TPanel)

Dmitry Boyarintsev

2013-11-28 06:39

developer   ~0071665

a code is needed on CocoaPanel/CocoaWindow becomeFirstResponder handler.
If there is any control on the form that can be focused, the method itself should return False.

Paul Ishenin

2013-11-28 12:57

manager   ~0071676

What if I want to focus the form itself, not a child control? On win32 it is possible.

Paul Ishenin

2013-12-09 02:33

manager   ~0071827

Since now a window handle is not really a windows handle but a handle of window content then window itself should not notify LCL about the focus change.

Not sure that it is 100% right but it works for this case. The following things needs to be tested:
- is it still possible to move the focus to a Form itself
- does LCL is notified when focus moves between Forms. E.g. I mouse click on another form which has no child controls

Dmitry Boyarintsev

2013-12-09 17:34

developer   ~0071838

In Win32 the focus would be set to the form, only if there are no focusable children

Dmitry Boyarintsev

2013-12-09 17:34

developer  

focusset.zip (2,055 bytes)

Issue History

Date Modified Username Field Change
2013-11-24 10:31 accSone New Issue
2013-11-24 11:55 accSone Note Added: 0071531
2013-11-24 11:56 accSone File Added: fix_0025362.patch
2013-11-24 12:31 Paul Ishenin Assigned To => Paul Ishenin
2013-11-24 12:31 Paul Ishenin Status new => assigned
2013-11-24 16:30 Dmitry Boyarintsev Note Added: 0071548
2013-11-24 16:32 Dmitry Boyarintsev Note Edited: 0071548 View Revisions
2013-11-28 06:39 Dmitry Boyarintsev Note Added: 0071665
2013-11-28 12:57 Paul Ishenin Note Added: 0071676
2013-12-09 01:29 Paul Ishenin Fixed in Revision => 43525
2013-12-09 01:29 Paul Ishenin LazTarget => -
2013-12-09 02:33 Paul Ishenin Fixed in Revision 43525 => 43525,43526
2013-12-09 02:33 Paul Ishenin Note Added: 0071827
2013-12-09 02:33 Paul Ishenin Status assigned => resolved
2013-12-09 02:33 Paul Ishenin Fixed in Version => 1.4
2013-12-09 02:33 Paul Ishenin Resolution open => fixed
2013-12-09 17:34 Dmitry Boyarintsev Note Added: 0071838
2013-12-09 17:34 Dmitry Boyarintsev File Added: focusset.zip