Performing action when NSRuleEditor criterion is selected - macos

How can I determine which criterion is being selected in the NSPopUpButton menu of an NSRuleEditor? Inside ruleEditorRowsDidChange(_:)
I could track the criteria for each row and process the changes manually, but it seems like there should be an easier way.
I tried returning an NSMenuItem instead of a String for the criterion's value in the rule editor's delegate; then setting the target and action properties on the menu item. That allows me to have a method called when the menu item (criterion) is selected in the rule editor. It also means I know which criterion triggered the action method because each menu item's target is the criterion itself. However, setting the target and action properties on the menu item overrides the private target and action values that the rule editor sets on the menu item. This means that after selecting a criterion in the rule editor, the target/action that I set fires, but the selection change is not reflected in the rule editor.
My current solution is to return an NSPopUpButton as the criterion's displayValue (since it inherits from NSView) and manage the button's items and target/action manually. This works well but feels like it breaks the design of the rule editor a bit by having a single criterion managing multiple values itself instead of relying on the rule editor's delegate).
Am I going about this the wrong way? Is there a simpler method to run criterion-specific code when a criterion is selected within a rule editor?
Thanks!

Set the action of the menu item and inside the action call - (void)setCriteria:(NSArray *)criteria andDisplayValues:(NSArray *)values forRowAtIndex:(NSInteger)rowIndex of the rule editor to refresh the row. You can get the row with - (NSInteger)rowForDisplayValue:(id)displayValue.

Related

Using NSArrayController to bind model objects to an inspector view, how to catch special cases like empty and multiple selections?

I have inspector view and because it contains a lot of UI I want to explore cocoa bindings to connect it to the model. The inspector view will update depending on the currently selected object in an outline view and the inspector view displays controls to alter properties (like colour, name, shape etc.) that can be changed.
This works well when only one row in the outline view is selected because the view has a one-to-one relationship with the model object. However, there are two special cases that I want to be able to catch:
Empty selection
If no items in the outline view are selected I want to display a special "No items selected view" placeholder view.
Multiple selection
If multiple items are selected I can either display a "Multiple items selected view" placeholder view or I can decide to let the changes apply globally to all selected model objects.
My question is how can these special cases be caught when using bindings? Moreover, how can I do behaviour "A" when there is an empty selection and behaviour "B" when there is a multiple section. This sort of thing is trivial when using target/action because you can just code the logic into the action method.
Bindings seem great when propagating changes to values but I'm unsure how to go about implementing bindings that require different decisions based on current selection state.

Unique ContextMenu for each TreeItem

I can set ContextMenu for a TreeView through the custom cellFactory, but the problem is that this menu is applied to all items in the tree. Is there are an way to bind different contextMenus to different items?
By design, the custom TreeCell you have defined will be reused multiple times while rendering the tree view item values and it is good thing. In updateItem method, you can change the content of the "current" contextMenu according to the item passed as parameter to this updateItem method.

Basic Cocoa Bindings: Toggle a boolean from menu in IB

I'm just getting started with Cocoa Bindings and while I've read through much of the documentation, I'm still struggling to implement a basic feature, making me question wether I'm doing it wrong or perhaps it's just not possible via IB.
Goal: Have a menu item called "Toggle visibility" toggle the state of a Boolean property in my application controller (AppController) called "visibility." I think this should be possible completely through IB.
(Note: CoreData is not in use here)
What I think needs to happen is I need to have an NSObjectController who's content outlet is set to my AppController class. Then I think I need to use an Action Innvocation binding. But here's where I run into trouble.
Should the Controller Key be selection? Should the Model Key Path be the name of my Boolean Property? What should Selector Name be?
Or do I need to setup a separate action method called "toggleVisibility" that I can bind to? If I did, why wouldn't I just use standard target/action associations, rather than bindings?
Any input / direction appreciated.
Goal: Have a menu item called "Toggle visibility" toggle the state of a Boolean property in my application controller (AppController) called "visibility."
Your goal tells you why you are failing.
In your description of your goal, you say that you want to set this menu item up as a command, a verb, an imperative sentence—“toggle the visibility”. But that's not the right title for the menu item.
Moreover, implementing a verb menu item with Bindings is inherently difficult because Bindings is the wrong tool for the job. Bindings is for exposing properties in your UI. It isn't for performing commands.
The correct title is supported by the correct Bindings-based solution.
The correct title of the menu item is, simply, “Visible”.
This alone should give you a hint as to how to proceed. Here's another: When the selection is visible, the menu item should have a check mark; when it is not visible, the menu item should have no mark.
You want to bind a property of the menu item to, ultimately, a Boolean property of your controller. Normally, this property of the menu item would be state, but for whatever reason, the binding for it is named value.
So, bind the value binding of the menu item to the Boolean property of your controller.

Enabling NSButton with bindings, based on NSTableView selection

I have a NSWindow containing an NSButton and an NSTableView.
I'd like the button to be enabled if and only if the table contains at least one item, and exactly one item is selected. (The table does not allow multiple selection.)
What can I bind the button's enabled binding to to make this happen?
This is an old thread, but here are my 2 cents:
Use and array controller and bind the button's enabled state to
Controller Key: selectedObjects
Model Key Path: #count
Works fine.
Try binding to the array controller's selectedObjects, model key path count, with no value transformer.
Note that this would be unsafe if you allowed multiple selection: For one thing, the count could easily be neither YES nor NO; for another, if the user selected a multiple of 256 items, the lowest byte of the count would be 0, so the BOOL value would be NO even though there is a selection.
I ran into this today and I got it to work after some efforts.
My button should be disabled if nothing is selected in the "Master Table":
Problems I ran into:
Use the actual button and not the enclosed Button cell
Specify NO = disabled for Multiple values, No selection etc.
Bind the Enabled property to the Master Table's selection and use a property (code in my case), which is present.
Use the transformer NSIsNotNil to enable the button if something is selected in the master table.

What's the cleanest way to bind enumerated properties?

I have a menu which has an item for each value in an enum.
The menu:
[ ] Sort by Due Date
[ ] Sort by Priority
[√] Sort by Title
The enum:
typedef enum CW_TASK_SORT_METHOD {
CWTaskSortMethodDueDate,
CWTaskSortMethodPriority,
CWTaskSortMethodTitle
} CWTaskSortMethod;
The property:
#property(readwrite, assign) CWTaskSortMethod taskSortMethod;
What's the cleanest way to wire this up? I have two ideas but both strike me as unsatisfactory.
1st idea: Create properties for each value (sortMethodIsDueDate, setSortMethodIsDueDate: etc) These properties would call setTaskSortMethod: and call valueDidChange:#"sortMethodIsDueDate" etc. The menu items would then bind to these properties. This seems like a lot of work.
2nd idea: Connect each menu item to -(IBAction)updateSortMethod:(id)sender which could then iterate the menu items and set the value depending on sender. This approach is fine until taskSortMethod is changed by a different section of code at which point code needs to be added to keep the menu in sync with taskSortMethod.
I'm leaning towards the first approach as it has better separation between the V & C.
Any better ideas?
I think you're on the right track with your second idea, but there's something to consider:
NSMenu / NSMenuItem don't have a concept of "selected item" like NSPopUpButton for instance. I'd use the target/action mechanism to change the sort method and menu validation (Introduction to User Interface Validation) or even the NSMenu delegate method -menu:updateItem:atIndex:shouldCancel: (NSMenuDelegate Protocol Reference) to update the state of the item based on the result of -taskSortMethod.
Since the menu items only need to be updated when they're shown (which this mechanism does for you), you don't have to worry about updating the menu items yourself when -taskSortMethod changes elsewhere.
Ex:
[sortByDueDateMenuItem setState: ([self taskSortMethod] == CWTaskSortMethodDueDate) ];
IMO, this is a lot cleaner than trying to over-engineer a bindings-powered solution.

Resources