0

I have a NSTextField which is nested by a custom view and I want to change the default behavior of multiple clicks in a row (double click, tripple click etc.), similarly to the behavior of text nodes MindNode (see the image below).

I want the first click to "activate" the text field and then go on from the beginning (like reseting the click count of the event).

I have following ideas, but I don't know how to implement them and if they actually make sense:

  • Somehow change the time using +[NSEvent doubleClickInterval] and slow down the second click.
  • Reduce the click count programmatically?
  • Make the NSTextField non-selectable using -hitTest:, forward the click to the superview, change some parameter of the text field and accept the next clicks. In this case, the click count of the second click is still 2.
  • Override -mouseDown: and not call super. This breaks the NSTextField's selection functionality.

I hope there is an easier way to achieve this, which I have overlooked.

Thanks for your answers!

Here is a graphical representation of the problem: Changing the behavior of multiple click for NSTextField

Lukáš Kubánek
  • 946
  • 1
  • 15
  • 27
  • So is your desired first click doing anything? Why do you want to do this, it seems like you're just adding one useless click. – rdelmar Oct 01 '12 at 21:36
  • It's actually making the text field "active", so that it can be handled like a normal ```NSTextField```. Additionally it will do some sort of visual thing (something like the MindNode's solution in the picture), but that's not the problem. – Lukáš Kubánek Oct 01 '12 at 21:45
  • The first click/tap in MindNode selects the node itself. That means actions are done to the graphical representation of the entire node (note the resize handles in the second stage of your third image). The next click focuses on the node's field. – Joshua Nozzi Oct 01 '12 at 23:32
  • That is actually possible and it would solve my problem. I would appriciate an advice on how to implement it. – Lukáš Kubánek Oct 02 '12 at 19:49

2 Answers2

0

I would do this by embedding the text field and a custom view in an NSBox, which would be set to the custom type, initially with no background color or border (so it would be invisible). Initially, the custom view would be on top and it would have a mouseDown: method that would receive the first click. In that method you could rearrange the box's subviews so that the text field would then be on top and receive the next clicks. If you wanted, the box could be somewhat bigger than the text field so you could give it a background color or other drawing that would look like a custom activation ring around the text field. In the text field's controlTextDidEndEditing: method, you could reset the system back to the beginning state, so it would be ready for the next time you click on it.

After Edit: Here is the code I'm using in my overlay class:

@implementation Overlay

static NSComparisonResult rdComparator( NSView *view1, NSView *view2, void *context ) {
    if ([view1 isKindOfClass:[NSTextField class]])
        return NSOrderedDescending;
    else if ([view2 isKindOfClass:[NSTextField class]])
        return NSOrderedAscending;
    return NSOrderedSame;
}


-(void)mouseDown:(NSEvent *)theEvent {
    self.box.fillColor = [NSColor redColor];
    NSView *contentView = self.box.subviews.lastObject;
    [contentView sortSubviewsUsingFunction:rdComparator context:nil];
}
rdelmar
  • 103,982
  • 12
  • 207
  • 218
  • Thanks for your response! I've implemented it using a transparent custom view in front of the text field and reordering it in ```mouseDown:```. The problem is that the second click has still the click count equal 2 and the ```NSTextField``` selects a word instead of just putting a cursor at the clicked position. Any idea how to change it? – Lukáš Kubánek Oct 03 '12 at 09:23
  • I'm not sure why you're seeing that behavior, mine doesn't do that. One other thing I did was to set the window's first responder to the overlay in the applicationDidFinishLaunching method. I've edited my post to show my overlay code. – rdelmar Oct 03 '12 at 15:20
  • I tried it with your solution and I was almost there, but it didn't work as expected. I think there was a problem with multiple clicks and handling the clicks of the layer when the layer was in background. I found an easier solution and posted it here. Again, thanks for your advices. – Lukáš Kubánek Oct 06 '12 at 13:21
0

I've solved it by subclassing NSTextField and decrementing click count of mouse down events programmatically. Using a boolean property of the subclass I am able to turn this special behavior on and off.

- (void)mouseDown:(NSEvent *)theEvent
{
    if (self.specialBehavior) {
        theEvent = [NSEvent mouseEventWithType:theEvent.type
                                      location:theEvent.locationInWindow
                                 modifierFlags:theEvent.modifierFlags
                                     timestamp:theEvent.timestamp
                                  windowNumber:theEvent.windowNumber
                                       context:theEvent.context
                                   eventNumber:theEvent.eventNumber
                                    clickCount:theEvent.clickCount - 1
                                      pressure:theEvent.pressure];
    }

    [super mouseDown:theEvent];
}

To simplify this long method call, I wrote a category method for NSEvent which decrements the click count of an event.

Lukáš Kubánek
  • 946
  • 1
  • 15
  • 27