Discussion:
Calling commitEditing text field loses focus
Christiaan Hofman
2011-12-20 13:14:30 UTC
Permalink
I have a text field whose value is bound through an NSObjectController. Before performing certain actions I call -commitEditing on that object controller to make sure I have the current value from the text field. What I notice is that if there are any uncommitted changes to the text, the text field loses focus due to this call. This is certainly not what I want, I just want to commit the edit. I also don't recall this to happen in the past, but I am not sure for how long this has been going on. Also, the focus is not lost when the text had not changed since the last commit. To me this sounds like a bug. Can anyone confirm this is a bug, or tell me why this would make sense? Or tell me whether there is some kind of setting to change this behavior?

thanks,
Christiaan
Kyle Sluder
2011-12-20 20:50:01 UTC
Permalink
Post by Christiaan Hofman
I have a text field whose value is bound through an NSObjectController. Before performing certain actions I call -commitEditing on that object controller to make sure I have the current value from the text field. What I notice is that if there are any uncommitted changes to the text, the text field loses focus due to this call. This is certainly not what I want, I just want to commit the edit. I also don't recall this to happen in the past, but I am not sure for how long this has been going on. Also, the focus is not lost when the text had not changed since the last commit. To me this sounds like a bug. Can anyone confirm this is a bug, or tell me why this would make sense? Or tell me whether there is some kind of setting to change this behavior?
Try using -commitEditingAndReturnError: (new in 10.7) instead?

--Kyle Sluder
Christiaan Hofman
2011-12-20 21:41:47 UTC
Permalink
Post by Kyle Sluder
Post by Christiaan Hofman
I have a text field whose value is bound through an NSObjectController. Before performing certain actions I call -commitEditing on that object controller to make sure I have the current value from the text field. What I notice is that if there are any uncommitted changes to the text, the text field loses focus due to this call. This is certainly not what I want, I just want to commit the edit. I also don't recall this to happen in the past, but I am not sure for how long this has been going on. Also, the focus is not lost when the text had not changed since the last commit. To me this sounds like a bug. Can anyone confirm this is a bug, or tell me why this would make sense? Or tell me whether there is some kind of setting to change this behavior?
Try using -commitEditingAndReturnError: (new in 10.7) instead?
--Kyle Sluder
Not relevant, I support 10.5. Moreover, why would or should returning an error change this behavior?

Christiaan
Kyle Sluder
2011-12-20 23:49:06 UTC
Permalink
Post by Christiaan Hofman
Post by Kyle Sluder
Try using -commitEditingAndReturnError: (new in 10.7) instead?
--Kyle Sluder
Not relevant, I support 10.5. Moreover, why would or should returning an error change this behavior?
It's not "returning an error" that would change the behavior. That
method was added to support autosaving in Lion, which you probably
wouldn't want to disturb the responder chain at random times even if
it was successful. Now that that method exists, it could be possible
that the behavior of NSTextField's -commitEditing was changed to give
up first responder. That's why I suggested you try it.

--Kyle Sluder
Christiaan Hofman
2011-12-21 00:09:38 UTC
Permalink
Post by Kyle Sluder
Post by Christiaan Hofman
Post by Kyle Sluder
Try using -commitEditingAndReturnError: (new in 10.7) instead?
--Kyle Sluder
Not relevant, I support 10.5. Moreover, why would or should returning an error change this behavior?
It's not "returning an error" that would change the behavior. That
method was added to support autosaving in Lion, which you probably
wouldn't want to disturb the responder chain at random times even if
it was successful. Now that that method exists, it could be possible
that the behavior of NSTextField's -commitEditing was changed to give
up first responder. That's why I suggested you try it.
--Kyle Sluder
The fact that you may want this for autosaving does not mean you may not want this for other commits. So this would be no explanation. Moreover, it now also behaves this way in 10.6.8 where the new method is not available. I see no reason why generically committing should stop an edit.

Christiaan
Kyle Sluder
2011-12-21 00:33:27 UTC
Permalink
Post by Christiaan Hofman
The fact that you may want this for autosaving does not mean you may not want this for other commits. So this would be no explanation.
I offered a possible explanation for what might have happened, made
under the assumption that you correctly identified this as a recent
change in behavior. The validity of the explanation and the
desirability of its implications are separate issues. Please don't
jump down my throat simply because you don't like the consequences
posed by a theory I posited in good faith.
Post by Christiaan Hofman
Moreover, it now also behaves this way in 10.6.8 where the new method is not available. I see no reason why generically committing should stop an edit.
_This_ would be the reason that my explanation is invalid.

I agree that I would not expect -commitEditing to lose focus. I
_would_ expect it to select all the text in the text field, as if the
user had pressed the Return key.

--Kyle Sluder
Jim Correia
2011-12-21 01:45:53 UTC
Permalink
Post by Christiaan Hofman
I have a text field whose value is bound through an NSObjectController. Before performing certain actions I call -commitEditing on that object controller to make sure I have the current value from the text field. What I notice is that if there are any uncommitted changes to the text, the text field loses focus due to this call. This is certainly not what I want, I just want to commit the edit. I also don't recall this to happen in the past, but I am not sure for how long this has been going on. Also, the focus is not lost when the text had not changed since the last commit. To me this sounds like a bug. Can anyone confirm this is a bug, or tell me why this would make sense? Or tell me whether there is some kind of setting to change this behavior?
This how it has worked since NSController was introduced.

It is less than ideal for a number of reasons. (You need to commit editing before saving, but that really shouldn’t change keyboard focus, nor should it change the user’s selection in many situations.)

If you want to solve this problem specifically, you can manually push the value through to the bound object when the field is editing. (Make sure you also take care of the value transformer case. Yes, this feels a little bit like you’ve given up most of the advantages of having bindings.)

A more general solution would be to write something like adding

- (void)commitEditingAndRestoreFirstResponderMumbleMumble;

to NSController. It’s a bit tedious to get this implementation right, since you have to handle NSTextView, plus field editors for the various types of things which might use them, scrolling selection ranges back into view, etc. This will take longer, but then you’ll have the problem solved centrally, once and for all.

—Jim
Christiaan Hofman
2011-12-21 11:15:29 UTC
Permalink
Post by Jim Correia
Post by Christiaan Hofman
I have a text field whose value is bound through an NSObjectController. Before performing certain actions I call -commitEditing on that object controller to make sure I have the current value from the text field. What I notice is that if there are any uncommitted changes to the text, the text field loses focus due to this call. This is certainly not what I want, I just want to commit the edit. I also don't recall this to happen in the past, but I am not sure for how long this has been going on. Also, the focus is not lost when the text had not changed since the last commit. To me this sounds like a bug. Can anyone confirm this is a bug, or tell me why this would make sense? Or tell me whether there is some kind of setting to change this behavior?
This how it has worked since NSController was introduced.
I am pretty sure that long ago when I tested these things out it did remain first responder. I distinctly remember being frustrated that it's so hard to manually (i.e. without bindings) commit without losing first responder the way bindings can do.
Post by Jim Correia
It is less than ideal for a number of reasons. (You need to commit editing before saving, but that really shouldn’t change keyboard focus, nor should it change the user’s selection in many situations.)
Sorry, can't think of any. If it can push through an edit on every key stroke without losing focus, why can't it do that at a point when I ask it to?
Post by Jim Correia
If you want to solve this problem specifically, you can manually push the value through to the bound object when the field is editing. (Make sure you also take care of the value transformer case. Yes, this feels a little bit like you’ve given up most of the advantages of having bindings.)
Not just a little. That basically makes bindings meaningless, it comes to manual updating the old way with an extra registration layer added.
Post by Jim Correia
A more general solution would be to write something like adding
- (void)commitEditingAndRestoreFirstResponderMumbleMumble;
to NSController. It’s a bit tedious to get this implementation right, since you have to handle NSTextView, plus field editors for the various types of things which might use them, scrolling selection ranges back into view, etc. This will take longer, but then you’ll have the problem solved centrally, once and for all.
—Jim
Well, and not to say that all the information I need about editors is not publicly available (all I can find is a private ivar _editors). So this seems impossible.

Christiaan
Jim Correia
2011-12-21 16:08:58 UTC
Permalink
Post by Christiaan Hofman
This is how it has worked since NSController was introduced.
I am pretty sure that long ago when I tested these things out it did remain first responder. I distinctly remember being frustrated that it's so hard to manually (i.e. without bindings) commit without losing first responder the way bindings can do.
I’ve been using bindings since 10.4, and this is always how -commitEditing has worked. (I wouldn’t have gone through the trouble to write -commitEditingAndRestoreFirstResponderMumbleMumble otherwise.)
Post by Christiaan Hofman
It is less than ideal for a number of reasons. (You need to commit editing before saving, but that really shouldn’t change keyboard focus, nor should it change the user’s selection in many situations.)
Sorry, can't think of any. If it can push through an edit on every key stroke without losing focus, why can't it do that at a point when I ask it to?
Depending on the user interface element, it may also be appropriate to end editing on save, and select the entire context of the field, for example. (This isn’t giving up first responder, but it isn’t preserving the insertion point either.)
Post by Christiaan Hofman
If you want to solve this problem specifically, you can manually push the value through to the bound object when the field is editing. (Make sure you also take care of the value transformer case. Yes, this feels a little bit like you’ve given up most of the advantages of having bindings.)
Not just a little. That basically makes bindings meaningless, it comes to manual updating the old way with an extra registration layer added.
It’s good for dramatic effect, but calling bindings useless because it gets this aspect wrong is kind of harsh.
Post by Christiaan Hofman
A more general solution would be to write something like adding
- (void)commitEditingAndRestoreFirstResponderMumbleMumble;
to NSController. It’s a bit tedious to get this implementation right, since you have to handle NSTextView, plus field editors for the various types of things which might use them, scrolling selection ranges back into view, etc. This will take longer, but then you’ll have the problem solved centrally, once and for all.
Well, and not to say that all the information I need about editors is not publicly available (all I can find is a private ivar _editors). So this seems impossible.
This is not impossible, because I’ve written and shipped code which does this. Unfortunately, this isn’t code I can share here.

You don’t need access to the bindings’ private parts. Step back and consider the problem you are trying to solve. You don’t want to “loose” the first responder. You can look at the first responder in the windows associated with your controller, and remember it (as well as a bunch of other details—the underlying control if it is a field editor, visible range, selection ranges, etc.) and restore it after committing editing. A bit tedious, but not impossible. (You’ll end up with a fairly generic solution and won’t have to think about the problem again once the edge cases are worked out.)

—Jim
Christiaan Hofman
2011-12-21 16:28:09 UTC
Permalink
Post by Jim Correia
Post by Christiaan Hofman
This is how it has worked since NSController was introduced.
I am pretty sure that long ago when I tested these things out it did remain first responder. I distinctly remember being frustrated that it's so hard to manually (i.e. without bindings) commit without losing first responder the way bindings can do.
I’ve been using bindings since 10.4, and this is always how -commitEditing has worked. (I wouldn’t have gone through the trouble to write -commitEditingAndRestoreFirstResponderMumbleMumble otherwise.)
Post by Christiaan Hofman
It is less than ideal for a number of reasons. (You need to commit editing before saving, but that really shouldn’t change keyboard focus, nor should it change the user’s selection in many situations.)
Sorry, can't think of any. If it can push through an edit on every key stroke without losing focus, why can't it do that at a point when I ask it to?
Depending on the user interface element, it may also be appropriate to end editing on save, and select the entire context of the field, for example. (This isn’t giving up first responder, but it isn’t preserving the insertion point either.)
Post by Christiaan Hofman
If you want to solve this problem specifically, you can manually push the value through to the bound object when the field is editing. (Make sure you also take care of the value transformer case. Yes, this feels a little bit like you’ve given up most of the advantages of having bindings.)
Not just a little. That basically makes bindings meaningless, it comes to manual updating the old way with an extra registration layer added.
It’s good for dramatic effect, but calling bindings useless because it gets this aspect wrong is kind of harsh.
I am saying that if you implement manual updating on top of binding you can just as well forget about binding because it does not add anything anymore. In this situation. I am not saying that binding would not have other uses still.
Post by Jim Correia
Post by Christiaan Hofman
A more general solution would be to write something like adding
- (void)commitEditingAndRestoreFirstResponderMumbleMumble;
to NSController. It’s a bit tedious to get this implementation right, since you have to handle NSTextView, plus field editors for the various types of things which might use them, scrolling selection ranges back into view, etc. This will take longer, but then you’ll have the problem solved centrally, once and for all.
Well, and not to say that all the information I need about editors is not publicly available (all I can find is a private ivar _editors). So this seems impossible.
This is not impossible, because I’ve written and shipped code which does this. Unfortunately, this isn’t code I can share here.
You don’t need access to the bindings’ private parts. Step back and consider the problem you are trying to solve. You don’t want to “loose” the first responder. You can look at the first responder in the windows associated with your controller, and remember it (as well as a bunch of other details—the underlying control if it is a field editor, visible range, selection ranges, etc.) and restore it after committing editing. A bit tedious, but not impossible. (You’ll end up with a fairly generic solution and won’t have to think about the problem again once the edge cases are worked out.)
—Jim
This makes no sense to me. An NSController does not even have *any* properties, let alone a window. It's not an interface element. And NSObjectController only knows (publicly) about things it binds to (its content or selection), but what is needed is the objects that bind *to* it. There's no API for that. So I really fail to see how you could have done this without using SPI.

And I know the tedious update part, I already do that for non-binding situations.

Christiaan
Jim Correia
2011-12-21 16:32:52 UTC
Permalink
Post by Christiaan Hofman
This makes no sense to me. An NSController does not even have *any* properties, let alone a window. It's not an interface element. And NSObjectController only knows (publicly) about things it binds to (its content or selection), but what is needed is the objects that bind *to* it. There's no API for that. So I really fail to see how you could have done this without using SPI.
You’ve got two options here:

You know where you use the controller, so you can associate one or more windows with your controller. (It doesn’t have any built-in properties to track this association—you’ll have to add this.)

Or, you can just snapshot the first responder and state for every window, commit editing, and restore it in those windows where it has changed.

For my purposes, the latter performed perfectly acceptably (having hundreds of windows open isn’t a typical use case), so that was the solution that I wrote.

Jim
Christiaan Hofman
2011-12-21 16:50:49 UTC
Permalink
Post by Jim Correia
Post by Christiaan Hofman
This makes no sense to me. An NSController does not even have *any* properties, let alone a window. It's not an interface element. And NSObjectController only knows (publicly) about things it binds to (its content or selection), but what is needed is the objects that bind *to* it. There's no API for that. So I really fail to see how you could have done this without using SPI.
You know where you use the controller, so you can associate one or more windows with your controller. (It doesn’t have any built-in properties to track this association—you’ll have to add this.)
Or, you can just snapshot the first responder and state for every window, commit editing, and restore it in those windows where it has changed.
For my purposes, the latter performed perfectly acceptably (having hundreds of windows open isn’t a typical use case), so that was the solution that I wrote.
Jim
Sounds like overkill to me. I guess as one has to write and call a custom method for this anyway that one can just as well pass the relevant window as an argument to that method -(BOOL)commitEditingAndRestoreFirstResponderForWindow:(NSWindow *)aWindow.

Christiaan
Jim Correia
2011-12-21 16:52:50 UTC
Permalink
Post by Christiaan Hofman
Sounds like overkill to me. I guess as one has to write and call a custom method for this anyway that one can just as well pass the relevant window as an argument to that method -(BOOL)commitEditingAndRestoreFirstResponderForWindow:(NSWindow *)aWindow.
In the simple case, that should work fine.

In the more complex case, you may have multiple document windows and multiple inspector windows to consider.

If the simple case covers your needs, there’s no reason to worry about the complex case, for now.

Jim

Loading...