I believe I understand what's going on.
First, can you confirm that when the text field is actually being edited, that the focus ring encompasses the whole table cell? It's not just tight around the actual current text, right?
If it encompasses the whole table cell, then that confirms that the constraints are stretching it to fill the table cell rather than the text field hugging its content. In other words, I'm trying to confirm that the "Not Editable" arrow in your image actually does point to a part of the text field. I expect it does.
So, with that out of the way, the problem is how NSTableView
manages clicks and whether or not they set the clicked-on view as the first responder. From Table View Programming Guide for Mac: Enabling Row Selection and User Actions – Specifying How Subviews Should Respond to Events, we learn that the table view implements special logic in an override of validateProposedFirstResponder(_:forEvent:)
:
The default NSTableView
implementation of
validateProposedFirstResponder:forEvent:
uses the following logic:
Return YES
for all proposed first responder views unless they are instances or subclasses of NSControl
.
Determine whether the proposed first responder is an NSControl
instance or subclass.
If the control is an NSButton
object, return YES
.
If the control is not an NSButton
, call the control’s hitTestForEvent:inRect:ofView:
to see whether the hit area is
trackable (that is, NSCellHitTrackableArea
) or is an editable text
area (that is, NSCellHitEditableTextArea
), and return the appropriate
value. Note that if a text area is hit, NSTableView
also delays the
first responder action.
I have implemented a custom subclass of NSTextFieldCell
. The only thing it does is override hitTestForEvent(_:inRect:ofView:)
to call through to super, log the result, and return it. I then set the text field in a table cell view to use that custom class for its cell. From that, I learned that clicking in the empty area of the text field results in .None
. Clicking on actual text results in .ContentArea | .EditableTextArea
.
The first result does not cause NSTableView
's implementation of validateProposedFirstResponder(_:forEvent:)
to allow the proposed first responder to actually be made first responder. The latter result does.
So, you could implement your own subclass of NSTextFieldCell
which overrides hitTestForEvent(_:inRect:ofView:)
. In your override, you'd call through to super. If the result is .None
, you'd change it to .ContentArea | .EditableTextArea
before returning it. Then, use that custom cell class for the text fields in your table.
Alternatively you could try to tackle this by using a custom subclass of NSTableView
that overrides validateProposedFirstResponder(_:forEvent:)
. The problem is that it's not straightforward to reimplement the logic of that method except which hit-test codes it responds to.