firstResponder and multiple NSTextFields

  • Hi all,

    I've got a panel wherein users enter a multi-part
    serial number and I'm trying to simplify the process
    for them a bit.

    As part of that goal, I'd like to have our
    registration boxes work a bit more like the one you
    see when you install Office, where you can enter one
    part of the serial number at a time, with the focus
    automatically being set to the next NSTextView when
    the right number of characters has been entered.

    I've set up an NSFormatter to check the length and
    then tell the controller to forward the focus to the
    next NSTextView. The NSFormatter is working just fine
    and sends the correct message to the controller.

    The controller, however, seems very confused.

    I'd originally tried:

    NSControl *field = (NSControl *)[licenseSheet
    firstResponder];
    if (field == txtPartialSN1)

    but that test always failed. If I did an NSLog of
    [field className] it was clearly NSTextView... So, I
    tried a slightly different approach, setting the tag
    values of each of the serial number text fields:

    NSControl *field = (NSControl *)[licenseSheet
    firstResponder];
    if ([field tag] == 1)
    [licenseSheet makeFirstResponder: txtPartialSN2];

    Here's where it gets weirder - the [field tag] value
    is always -1. Now I'm really confused.

    Can anyone offer insight on this? My guess would be
    that it has something to do with objects created in
    the nib versus at run-time, but I'm not well-versed
    enough in that topic to figure it out.

    Any help would be appreciated.

    Thank you.

    __________________________________________________
    Do You Yahoo!?
    Tired of spam?  Yahoo! Mail has the best spam protection around
    http://mail.yahoo.com
  • The firstResponder while editing a text field is typically the containing window's field editor.

    See Apple's documentation and past discussions on this list about the field editor.



    As it happens, while editing a text field, the delegate of the field editor is the field being edited.

    You can edit your code as follows:



    NSText *fieldEditor = [[licenseSheet firstResponder];

    NSControl *field = nil;



    if([fieldEditor respondsToSelector:@selector(delegate)])

    {
      field = [fieldEditor delegate];

    }
    if (field == txtPartialSN1)

    - or-



    NSText *fieldEditor = [[licenseSheet firstResponder];

    int        tag = -1;



    if([fieldEditor respondsToSelector:@selector(delegate)])

    {
      tag = (NSControl *)[[fieldEditor delegate] tag];

    }
    if (tag == txtPartialSN1)



    However, I think that is all unnecessary because I think you can set up your formatter to end editing when the correct number of digits are entered and you can set up the nextField to automatically move to the next field when editing ends.  You could also use the controlTextDidEndEditing or some such notification or you could use an action with the text field.  Both of these choices avoid having to perform comparisons with the field editor because the field itself is sent as the object of the notification or the sender of the action.
  • Hi,

    First, thank you. :)

    > if([fieldEditor
    > respondsToSelector:@selector(delegate)])
    >
    > {
    > field = [fieldEditor delegate];
    >
    > }
    > if (field == txtPartialSN1)

    Ta-da! That did it! :)

    >
    > However, I think that is all unnecessary because I
    > think you can set up your formatter to end editing
    > when the correct number of digits are entered and
    > you can set up the nextField to automatically move
    > to the next field when editing ends.

    Ok, fair enough. I do see the nextKeyView, and you're
    surely right. However, I don't see how to set it up so
    that the user is automatically forwarded when the max
    # of characters has been reached. For that matter, I'm
    not sure how to get the formatter to prohibit editing.

    At the moment, I'm using the isPartialStringValid:
    method, but I don't know how I'd explicitly end
    editing other than to simply continue to trim any
    additional characters the user might try to type
    (which I already do).

    It sounds like you had something more specific in
    mind.

    Thanks again!

    __________________________________________________
    Do You Yahoo!?
    Tired of spam?  Yahoo! Mail has the best spam protection around
    http://mail.yahoo.com
  • Hi.

    If the formatter idea doesn't work out, here is another suggestion:

    Set your controller class (the window controller for this license
    window/sheet) as the delegate of the license textfields.
    Have outlets in the controller class to the various textfields that
    make up the license fields.

    Implement the controlTextDidChange: delegate method.

    You could do something like this:

    - (void)controlTextDidChange: (NSNotification*)aNotif;
    {
        id notif_obj = [aNotif object];

        if (notif_obj == licenseFieldOne)
        {
            NSString* val = [notif_obj stringValue];
            // Validate the string thus far
            //  If valid, end editing and go to the next field
            [notif_obj resignFirstResponder];
            [[notif_obj nextKeyView] becomeFirstResponder]; // If you
    set the nextKeyView, otherwise use something like licenseFieldTwo here
        }
        else if (notif_obj == licenseFieldTwo)
        {
            // Repeat
        }
        // Repeat for as many fields as you have
    }

    I can't remember if the object of the notif is the textfield or the
    field editor, you would have to test that.  If it is the field editor
    Erik's trick for getting the textfield will work.

    It assumes that you set the nextKeyView on the textfields to be logical.
    Also, I am assuming that different fields in your license string
    could have different formats.  If not, you could generalize and
    remove the messy if chain.  And probably get rid of your outlets to
    the fields.

    I haven't tested this, but I think it should work.

    Hope it helps!
    Michael.

    On 24-Aug-05, at 7:09 PM, Brad Peterson wrote:

    > Hi,
    >
    > First, thank you. :)
    >
    >
    >> if([fieldEditor
    >> respondsToSelector:@selector(delegate)])
    >>
    >> {
    >> field = [fieldEditor delegate];
    >>
    >> }
    >> if (field == txtPartialSN1)
    >>
    >
    > Ta-da! That did it! :)
    >
    >
    >>
    >> However, I think that is all unnecessary because I
    >> think you can set up your formatter to end editing
    >> when the correct number of digits are entered and
    >> you can set up the nextField to automatically move
    >> to the next field when editing ends.
    >>
    >
    > Ok, fair enough. I do see the nextKeyView, and you're
    > surely right. However, I don't see how to set it up so
    > that the user is automatically forwarded when the max
    > # of characters has been reached. For that matter, I'm
    > not sure how to get the formatter to prohibit editing.
    >
    >
    > At the moment, I'm using the isPartialStringValid:
    > method, but I don't know how I'd explicitly end
    > editing other than to simply continue to trim any
    > additional characters the user might try to type
    > (which I already do).
    >
    > It sounds like you had something more specific in
    > mind.
    >
    > Thanks again!
    >
    > __________________________________________________
    > Do You Yahoo!?
    > Tired of spam?  Yahoo! Mail has the best spam protection around
    > http://mail.yahoo.com
    > _______________________________________________
    > Do not post admin requests to the list. They will be ignored.
    > Cocoa-dev mailing list      (<Cocoa-dev...>)
    > Help/Unsubscribe/Update your Subscription:
    > http://lists.apple.com/mailman/options/cocoa-dev/mclark.list%
    > 40marketcircle.com
    >
    > This email sent to <mclark.list...>
    >
  • Doh, brainfart!

    Actually, don't do the resign/becomeFirstResponder combo, that is a
    no no.
    tell the window makeFirstResponder with the next field, and it does
    the right thing.

    Michael.

    On 24-Aug-05, at 11:24 PM, Michael Clark wrote:

    > Hi.
    >
    > If the formatter idea doesn't work out, here is another suggestion:
    >
    > Set your controller class (the window controller for this license
    > window/sheet) as the delegate of the license textfields.
    > Have outlets in the controller class to the various textfields that
    > make up the license fields.
    >
    > Implement the controlTextDidChange: delegate method.
    >
    > You could do something like this:
    >
    > - (void)controlTextDidChange: (NSNotification*)aNotif;
    > {
    > id notif_obj = [aNotif object];
    >
    > if (notif_obj == licenseFieldOne)
    > {
    > NSString* val = [notif_obj stringValue];
    > // Validate the string thus far
    > //  If valid, end editing and go to the next field
    > [notif_obj resignFirstResponder];
    > [[notif_obj nextKeyView] becomeFirstResponder]; // If you
    > set the nextKeyView, otherwise use something like licenseFieldTwo here
    > }
    > else if (notif_obj == licenseFieldTwo)
    > {
    > // Repeat
    > }
    > // Repeat for as many fields as you have
    > }
    >
    > I can't remember if the object of the notif is the textfield or the
    > field editor, you would have to test that.  If it is the field
    > editor Erik's trick for getting the textfield will work.
    >
    > It assumes that you set the nextKeyView on the textfields to be
    > logical.
    > Also, I am assuming that different fields in your license string
    > could have different formats.  If not, you could generalize and
    > remove the messy if chain.  And probably get rid of your outlets to
    > the fields.
    >
    > I haven't tested this, but I think it should work.
    >
    > Hope it helps!
    > Michael.
    >
    >
    > On 24-Aug-05, at 7:09 PM, Brad Peterson wrote:
    >
    >
    >> Hi,
    >>
    >> First, thank you. :)
    >>
    >>
    >>
    >>> if([fieldEditor
    >>> respondsToSelector:@selector(delegate)])
    >>>
    >>> {
    >>> field = [fieldEditor delegate];
    >>>
    >>> }
    >>> if (field == txtPartialSN1)
    >>>
    >>>
    >>
    >> Ta-da! That did it! :)
    >>
    >>
    >>
    >>>
    >>> However, I think that is all unnecessary because I
    >>> think you can set up your formatter to end editing
    >>> when the correct number of digits are entered and
    >>> you can set up the nextField to automatically move
    >>> to the next field when editing ends.
    >>>
    >>>
    >>
    >> Ok, fair enough. I do see the nextKeyView, and you're
    >> surely right. However, I don't see how to set it up so
    >> that the user is automatically forwarded when the max
    >> # of characters has been reached. For that matter, I'm
    >> not sure how to get the formatter to prohibit editing.
    >>
    >>
    >> At the moment, I'm using the isPartialStringValid:
    >> method, but I don't know how I'd explicitly end
    >> editing other than to simply continue to trim any
    >> additional characters the user might try to type
    >> (which I already do).
    >>
    >> It sounds like you had something more specific in
    >> mind.
    >>
    >> Thanks again!
    >>
    >> __________________________________________________
    >> Do You Yahoo!?
    >> Tired of spam?  Yahoo! Mail has the best spam protection around
    >> http://mail.yahoo.com
    >> _______________________________________________
    >> Do not post admin requests to the list. They will be ignored.
    >> Cocoa-dev mailing list      (<Cocoa-dev...>)
    >> Help/Unsubscribe/Update your Subscription:
    >> http://lists.apple.com/mailman/options/cocoa-dev/mclark.list%
    >> 40marketcircle.com
    >>
    >> This email sent to <mclark.list...>
    >>
    >>
    >
    > _______________________________________________
    > Do not post admin requests to the list. They will be ignored.
    > Cocoa-dev mailing list      (<Cocoa-dev...>)
    > Help/Unsubscribe/Update your Subscription:
    > http://lists.apple.com/mailman/options/cocoa-dev/mclark.list%
    > 40marketcircle.com
    >
    > This email sent to <mclark.list...>
    >