Changing visibility of NSPopUpButton's items based on keypress - objective-c

I've got an application here that needs to read in a bunch of data from an external file and display it as a NSPopUpButton in a Cocoa user interface. The catch here is that the data that is being read in needs to have a flag that states if it is considered "hidden" or not.
If the data is hidden, it needs to be added to the NSPopUpButton as an NSMenuItem, but the hidden flag needs to be set to YES so it does not normally appear in the NSPopUpButton menu. If the user holds down a "magic key" on their keyboard (usually ALT, in this case) then those hidden objects need to be unhidden. If the user lets go of the ALT key, then they need to be automatically re-hidden, except for the one that may have been selected -- which would become hidden if another NSMenuItem were chosen.
I'm kind of having a heck of a time figuring this out, actually.
I was wondering if there is a straight forward way of doing this using NSArrayController and an NSPopUpButton, but thus far I have not been able to find anything resembling a solution -- not when it comes to managing the hidden property of the NSMenuItem objects.
Does anyone know how this can be achieved using Cocoa Bindings?

You can wire the popup to an array controller and alter the filter predicate. From an MVC design standpoint, you wouldn't use an attribute like "hidden", which is a view characteristic, but maybe "advanced". Normally, set a filter predicate on your array controller to "advanced = no". Then when the user holds your preferred modifier, remove the predicate. The popup will update automatically. The array controller should be bound to an array property on another object (in your data model). The popup should be bound to arrangedObjects on the array controller.

Related

ArrayController's CoreData selection binding not refreshed across multiple NIB files

I've a hard time getting my Cocoa application to work as expected. It consists of a toolbar in the main.nib and a custom view in a details.nib file. Now I want the user to select an entry in a NSPopupButton in the toolbar and the content of the custom view should be changed accordingly.
To achive this I've added an ArrayController to my main.nib, showing the following configuration:
Furthermore the Managed Object Context is bound to the Model Key Path delegate.managedObjectContext (it is no document based application).
With this configuration the NSPopupButton works just fine and if I add a Label to the toolbar (also in the main.nib) and bind it's value to the selection (Controller Key), name (Key Value Path) the content is refreshed whenever I change the selection.
The Bindings of the NSPopupButton look like shown in the following screenshot:
So in my details.nib I tried the following to achieve the same effect. I've added an ArrayController, whichs Managed Object Context is also bound to the Model Key Path delegate.managedObjectContext. Also the configuration is exactly the same as shown in the above pictures. I've then added a label and bound it's value to the selection (Controller Key), name (Key Value Path) of this ArrayController.
The problem is that the Label only displays the the initial selection after the application did launched correctly. Afterwards, when I change the selection of my NSPopupButton, the label does not change accordingly.
What are my options to get the ArrayController working accross multiple NIB files?
BTW: I've tried to follow this blog post to get it working but it seems I'm missing something here.
Update:
If I replace the Label in the details.nib by a NSTextField and change the text of it, the changes are reflected in the related NSPopupButton entry. So I guess I made something right, but the main problem remains: I can only edit the entry which was loaded during application startup. Switching to another NSPopupButton entry does not change the text in the NSTextField.
Update 2:
I've created a small sample project with exactly the same configuration and uploaded it on GitHub. So feel free to check it out or create a pull request with a solution approach.
It seems you're missing the fact that, when you create the second array controller on the Details.xib it has no relation to the array controller on the MainMenu.xib. They are two separate instances.
When you change the selection on the PopUp the only array controller affected is the one on MainMenu.xib.
You have several options here:
When you create your DetailViewController pass a reference to the array controller on the Controller and bind to that (don't create a new one on the details.xib)
Just use simple KVO to observe the selection on Controller, and programatically change your label value.
Just use simple KVO to observe the selection on Controller and update the array controller on the DetailsViewController to keep them in sync.
your solution here...
As long as you understand what's going on I'm sure you'll find the best solution to your original problem.

Cocoa : Detect changes to any form element

I have a form built in interface builder.
Now I want to receive a notification when any element that is a child of a particular view is changed (be it text, or the select element, or a checkbox, etc).
Basically I want to know whether or not the form is "dirty" (changed).
How can I accomplish this?
One simple answer is to connect those UI elements which might change the value of the data model to an IBAction in the parent view controller. In that IBAction you can decide if you want to save the data, present a dialog, etc.

NSPopUpButton + Bindings + Show All Option

I'm trying to develop a NSPopUpButton that will serve as a filter to some datasource, let's say a NSArrayController that fills a table.
I can bind the NSArrayController from the menu to the selection keypath so the data is properly filtered, no problem with that.
Tricky part is, I want the content of this NSPopUpButton to rely on an NSArrayController using bindings, but I'd like to add a "Show All" menu item, or at least some item that doesn't come from the Core Data and performs some special action other than filtering the table using bindings and core data.
I'm trying to perform something like the NSPopUpButton used by finder in the filter bar, the last item of the menu performs a special action, while the others just filter the result.
I understand that the approach is to forget about bindings and do it everything programmatically, because I believe there's no way to mess up with the NSArrayController and bindings to add this custom menu item that doesn't rely on core data, but since I haven't found anything on the Apple Docs and here, I'd like to share my thoughts... any ideas?
sounds to me like you're looking for the NSContentPlacementTagBindingOption.
you edit the NSMenu that's attached to the NSPopupButton as follows
you then edit the settings on the NSMenuItem you want to have replaced with your array controller contents so that it has a meaningful tag associated with it
you then specify that tag as the content placement tag value on the NSPopupButton's bindings for the content/content* bindings.

NSArrayController selection being reset

Short story:
The NSArrayController's selection is being reset whenever setContent is issued. I am wondering if there is a way to turn this off.
Of course, this would be the only acceptable behaviour if I would let NSArrayController use its internal selectionIndexes, because then it wouldn't be able to keep track of both. However, the selectionIndexes are rewired as well, and this part goes off without a hitch. It still feels the need to reset the selection, though.
Update: Ugly hack solution
I've moved this to an answer. I would be pleased to see another more insightful answer though.
Long story:
I have a Cocoa Document-based Application with an inspector panel that is shared between documents (modelled after the TextEdit source code that ships with xcode). Inside the Document class I have an NSMutableArray and an NSMutableIndexSet that are linked up with bindings to an NSArrayController.
The inspector panel is in a separate nib file, and I have two identical NSArrayControllers, one from the main document window, and one from the inspector panel, so that both can interact with the document. This is why I do a manual binding to the selectionIndexes, so that I don't get two separate selections with the two separate NSArrayControllers.
The inspector panel keeps track of which document is being inspected by a:
Document *inspectedDocument;
which is updated whenever the document is switched, or no document has focus at all. An NSObjectController is linked up to inspectedDocument, and the NSArrayController I mentioned before is linked up to that controller.
Now, my problem is that when the inspector panel is in place, and the inspectedDocument is changed, the selection indexes are reset. The problem goes away if I don't use the inspector panel, so I assume it is its NSArrayController that is issuing this reset. I don't have any controls that bind to the selection and could change it (such as a table view).
In the inspector panel's NSArrayController, if I have "Avoid Empty Selection" ticked, the selection resets to the first object, otherwise it resets to no selection, so it is definitely a legitimate resetting of the selection. Actually, I don't even want the inspector panel to ever be able to change the selection, so ideally I would like to establish a read-only binding for that NSArrayController's selection indexes.
I still don't understand why it happens, or if it can be turned off, but the solution I am using is very simple, I just save the selection before I rewire the NSArrayController, and then restore it right after.
This is called when the inspectedDocument is changed and the selection is reset:
NSIndexSet *indexSet = nil;
if (inspectedDocument != doc) {
indexSet = [doc.selectedIndexes copy]; // Backup selection
}
[self setValue:doc forKey:#"inspectedDocument"]; // Selection is reset here
if (indexSet) {
// The following function basically does doc.selectedIndexes = indexSet;
[inspectedDocument selectObjectsAtIndices:indexSet]; // Restore selection
[indexSet release];
}

Is assigning the same Action-method to multiple Cocoa UI objects e.g. NSButton possible?

I'm currently learning ObjC and Cocoa programming, coming from the Java world.
To test my current skills and learning progress I'm creating a small calculator app from scratch (OSX not iOS).
My UI has 10 digit buttons 0-9 among others.
My first thought was, since the action receives the senders reference, to make one action
like -(IBAction)captureDigit:(id)sender and then just grab the digit from the button title.
But the interface builder only allows an action to be connected with one sender it seems.
So I ended up creating 10 captureDigit actions in my controller.
My Question:
is the first option possible somehow? I thought of adding the actions programmatically (is this possible?) to the buttons, but then I would have to add all digit buttons as outlets to my controller.
Bonus Question:
can a NSButton hold some kind of non visible value? Could not find this in the documentation.
Maybe this would violate the MVC pattern as the UI would then know of application specific data?
Thanks for any useful and kind answer in advance, I'm still learning
You can connect many senders to one target/action if you Control-drag from senders to the target, so that's not a problem.
WRT your bonus question, any NSView has an integer tag which you can set in Interface Builder. That's a convenient way to differentiate multiple similar views.
You can definitely connect more than more button to a single action. Also, you can use the tag field of any object to give it a "behind the scenes" value.
It's perfectly possible to add as many actions to a single controller. How is Interface Builder preventing you from doing this?
You could have a NSDictionary instance in your controller, in which you could match NSButtons to whatever data you want.
To make it easy, in IB create one button and drag from NSButton to File's owner it then shows all of the methods that we can send to NSButton, then select captureDigit:. Now copy and paste the button change the title, copy and paste in IB keeps the connection and use tag field as costique, nitrex have already said.