4
votes

I have a subclass of NSTextField that I made so that when a user is done editing the field, the text field will lose focus. I also have it set up so whenever the user clicks on the main view, this will act as losing focus on the textfield. And this all works great. Now I want to add some additional capabilities to the subclass.

I want the textfield to send a textDidEndEditing every time a user clicks anywhere outside of the box. This includes when a user clicks on another UI component. The behavior I'm seeing right now is that when a user clicks on another UI component (let's say a combo box) the action does not trigger. Is there a way to force this? Besides manually adding it as a part of the other components actions?

Any help would be appreciated!

Here's the code for my textDidEndEditing function

- (void)textDidEndEditing:(NSNotification *)notification
{
  NSString *file = nil;
  char c = ' ';
  int index = 0;

  [super textDidEndEditing:notification];

  if ([self isEditable])
  {
    // is there a valid string to display?
    file = [self stringValue];
    if ([file length] > 0)
    {
      c = [file characterAtIndex:([file length] - 1)];
      if (c == '\n') // check for white space at the end
      {
        // whitespace at the end... remove
        NSMutableString *newfile = [[NSMutableString alloc] init];
        c = [file characterAtIndex:index++];
        do
        {
          [newfile appendFormat:@"%c", c];
          c = [file characterAtIndex:index++];
        }
        while ((c != '\n') && (index < [file length]));

        [self setStringValue:newfile];
        file = newfile;
      }

      [[NSNotificationCenter defaultCenter]
       postNotificationName:@"inputFileEntered" object:self];
    }
  }

  // since we're leaving this box, show no text in this box as selected.
  // and deselect this box as the first responder
  [self setSelectedText:0];
  [[NSNotificationCenter defaultCenter]
   postNotificationName:@"setResponderToNil" object:self];
}

Where "setSelectedText" is a public function in the text field subclass:

- (void)setSelectedText:(int) length
{
  int start = 0;
  NSText *editor = [self.window fieldEditor:YES forObject:self];
  NSRange range = {start, length};
  [editor setSelectedRange:range];
}

And the "setResponderToNil" notification is a part of my NSView subclass:

- (void)setResponderToNil
{
  AppDelegate *delegate = (AppDelegate *)[NSApp delegate];
  [delegate.window makeFirstResponder:nil];
}
1
you want this to happen only on user clicks outside the text field or there are any other events involved??Aneesh Dangayach
Whenever a user interacts with the UI outside of the text field. So if a user clicks on a button or if there interact with a drop down box, I want the textDidEndEditing to trigger. I just rather not have to do it as part of another UI component's action.bhawley
you can add local mouse event monitor for your text field to check, it will be triggered each time you click out.Aneesh Dangayach
Where would you put the event monitor? If you put a mouse down handler in the text field it will only trigger when the user clicks into the field, not when the user clicks outside. Or am I misunderstanding the way the event works?bhawley

1 Answers

0
votes

I think I found a way to do this. It may not be the most eloquent, but it seems to work with the type of behavior I want.

I added an mouse event listener to the app's main controller:

event_monitor_mousedown_ = [NSEvent addLocalMonitorForEventsMatchingMask:NSRightMouseDown
                                                               handler:^NSEvent *(NSEvent * event)
{
  NSResponder *resp = [[[NSApplication sharedApplication] keyWindow] firstResponder];

    if ([resp isKindOfClass:[NSTextView class]])
    {
      // set UI in proper state - remove focus from text field
      // even when touching a new window for the first time
      [[NSNotificationCenter defaultCenter]
       postNotificationName:@"setResponderToNil" object:self];
      [self setStopState];
    }

  return event;
}];

This event checks the current responder in the application on any mouseDown action. If it's a textView object (which is type of the object that would be the first responder when editing an NSTextField) it will send the notification to set the firstResponder to nil. This forces the textDidEndEditing notification. I want to play around with it some more to see if I'm getting the right expected behavior. I hope this helps someone out there!