Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Not being able to edit NSTextField on NSPopover even though Editable behavior is set

I have an application, which open popover with NSTextField. The text field is not editable. Behavior for text field is set to Editable. I still can paste and copy text to this field but i can't edit it.

Anyone knows, what can be wrong?

like image 717
agent-10 Avatar asked Aug 27 '11 11:08

agent-10


5 Answers

Not sure if you still need the answer, but there may be some others still looking. I found a solution on apple developer forums. Quoting the original author:

The main problem is the way keyboard events works. Although the NSTextField (and all its superviews) receives keyboard events, it doesn't make any action. That happens because the view where the popover is atached, is in a window which can't become a key window. You can't access that window in any way, at least I couldn't. So the solution is override the method canBecomeKeyWindow for every NSWindow in our application using a category.

NSWindow+canBecomeKeyWindow.h
@interface NSWindow (canBecomeKeyWindow)

@end

NSWindow+canBecomeKeyWindow.m
@implementation NSWindow (canBecomeKeyWindow)

//This is to fix a bug with 10.7 where an NSPopover with a text field cannot be edited if its parent window won't become key
//The pragma statements disable the corresponding warning for overriding an already-implemented method
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
- (BOOL)canBecomeKeyWindow
{
    return YES;
}
#pragma clang diagnostic pop

@end

That makes the popover fully resposive. If you need another window which must respond NO to canBecomeKeyWindow, you can always make a subclass.

like image 132
Balazs Toth Avatar answered Oct 17 '22 13:10

Balazs Toth


I struggled with this for a while as well, until I realized it was a bug.

However, instead of relying on an isActive state of a NSStatusItem view, I find it much more reliable to use the isShown property of the NSPopover you have implemented.

In my code, I have a NSPopover in a NSViewController:

  - (BOOL)canBecomeKeyWindow
    {
        if([self class]==NSClassFromString(@"NSStatusBarWindow"))
        {
            NSPopover *mainPopover = [[((AppDelegate*)[NSApp delegate]) mainViewController] mainPopover];
            if(![mainPopover isShown])
                return NO;
        }

        return YES;
    }
like image 20
Richard Nees Avatar answered Oct 17 '22 13:10

Richard Nees


Balazs Toth's answer works, but if you're attaching the popover to NSStatusItem.view the status item becomes unresponsive - requiring two clicks to focus.

like image 25
Andrew McCloud Avatar answered Oct 17 '22 13:10

Andrew McCloud


What i found when working with this solution is that when NSStatusItem becomes unresponsive, you can easily override this behavior like this

- (BOOL)canBecomeKeyWindow {
    if([self class]==NSClassFromString(@"NSStatusBarWindow")) {
        CBStatusBarView* view = [((CBAppDelegate*)[NSApp delegate]) statusItemView];
        if(![view isActive]) return NO;
    }
    return YES;
}

You will check for the class of the window, if it matches the NSStatusBarWindow we can then check somehow if the NSStatusItem is active. If it is, that means we have to return YES, because this way the NSPopover from NSStatusItem will have all keyboard events.

What I'm using for checking if the NSStatusItem was clicked (or is active) is that in my own custom view i have a bool value which changes when user clicks on the NSStatusItem, system automatically checks for "canBecomeKeyWindow" and when it does it will return NO and after user clicks on it (while it is returning the NO) it will change the bool value and return YES when system asks again (when NSPopover is being clicked for NSTextField editing).

Sidenotes:

  • CBStatusBarView is my custom view for NSStatusItem
  • CBAppDelegate is my App Delegate class
like image 1
Vlastimil Šenfeld Avatar answered Oct 17 '22 14:10

Vlastimil Šenfeld


If anyone is still looking for an answer to this, I am working in Swift.

At the time where you wish the field to allow text entry, I have used myTextField.becomeFirstReponder()

To opt out; just use myTextField.resignFirstResponder()

like image 1
Joey Slomowitz Avatar answered Oct 17 '22 14:10

Joey Slomowitz