How can I programmatically expand items in a TreeViewer? - treeview

I have a TreeViewer with an ILazyTreeContentProvider. I want to control the expansion of the top-level tree items: Initially they are unexpanded, but when an Action "Expand" is selected, the item should be expanded.
In the action.run() I currently do
final EntityTreeNode projectNode = (EntityTreeNode) ((ITreeSelection)
viewer.getSelection()).getFirstElement();
// notify the content provider that the node should be expanded; it creates child nodes
// and updates the child count and calls viewer.setChildCount
((LazyEntityModelViewContentProvider) viewer.getContentProvider()).expandProject(projectNode);
viewer.expandToLevel(projectNode, 2);
viewer.refresh(projectNode);
But the node is not expanded. It should at least change it's icon to show that it has children...

Related

Create PySimpleGUI list with a key

I wish to have a list of text items in a PySimpleGUI that I can update later. That is, I want to have a key for the list. This might be vertical or horizontal, and I do not know how many items there will be.
I end up with different use cases, but the current one is to make a single line of text items with different colors. Other times, I need to write and update a customized table, just different enough that the table widget does not work.
Conceptually, I want to do something like this:
layout = [ [sg.T('Titles and Things')], sg.ListThing(key='-data-', [[]]) ]
so that I can:
window['-data-'].update(values=[ [sg.T(v, color=c)] for (v,c) in my_data ])
Another, invalid syntax, way of saying what I want is to use [key="-data-", sg.T('Item1'), sg.T('Item2')].
Is this possible?
You can update individual layout elements but you cannot dynamically change the layout itself.
It is possible to create 2 or more elements, whereby only one of them is visible, and switch them later as needed. Or you can close and re-create the window with another layout. Or combine both approaches.
An example of switching layouts:
def change_layout():
left_col_1 = sg.Column([[sg.Text(f'Text {i}') for i in range(4)]], visible=True, key='col_1')
left_col_2 = sg.Column([[sg.Text(f'Text {i}')] for i in range(6)], visible=False, key='col_2')
visible_1 = True
layout = [[sg.Column([[left_col_1, left_col_2]]), sg.Button('Change layout', key='change')]]
window = sg.Window('window', layout=layout, finalize=True)
while True:
event, values = window.read()
print(event)
print(values)
print(visible_1)
if event in ('Exit', sg.WIN_CLOSED):
break
if event == 'change':
window['col_1'].update(visible=not visible_1)
window['col_2'].update(visible=visible_1)
visible_1 = not visible_1
Please notice that the alternative layouts for the left part (left_col_1, left_col_2) need to be enclosed in a container (column, frame) to keep their position in the window in the moment they are invisible.

NSOutlineView reloadItem/reloadData not working when replacing an item

I have a view-based NSOutlineView with a dataSource/delegate model instead of binding to a tree controller (I want control over the insert/update animations).
I'm replacing an item in my model and would like to update that specific row in the outline view without having to call reloadData().
I cannot get this to work. Either the item does not update at all or the item's expanded state doesn't update. There seems to be some caching being done inside of NSOutlineView according to this, but even with these suggestions, I could not get it to work.
What I have:
(1) The outline view represents a folder structure
(2) At first, there is a singe file:
(3) The file is then replaced with a folder item:
// Model update
let oldFileItem = rootItem.children.first!
rootItem.children.remove(at: 0)
rootItem.children.append(Item(title: "Folder", children:[], isExpandable:true))
Expected result:
Actual result (reloadItem):
outlineView.reloadItem(oldFileItem) // I kept a reference
Icon and title reloaded, but note that the expansion triangle is missing.
I can somewhat understand that reloadItem() might not work in this case, because the old item is not part of the data model anymore. Strangely enough, the item's title and icon update, but not the expansion state.
Actual result (reloadData(forRowIndexes:columnIndexes:):
outlineView.reloadData(forRowIndexes: IndexSet(integer:0), columnIndexes: IndexSet(integer:0))
No effect whatsoever. This is the one that I would have expected to work.
Actual result (remove/insert):
outlineView.removeItems(at: IndexSet(integer:0), inParent: rootItem, withAnimation: [])
outlineView.insertItems(at: IndexSet(integer:0), inParent: rootItem, withAnimation: [])
No effect whatsoever.
The docs say about removeItems(): "The method does nothing if parent is not expanded." and isExpanded does indeed return false for the root node, although its children are visible. Is this special behavior for items that are direct children of the root node? What am I missing here?
For reference, my data model:
class Item:NSObject {
var title:String
var children:[Item]
var isExpandable:Bool
init(title:String, children:[Item], isExpandable:Bool) {
self.title = title
self.children = children
self.isExpandable = isExpandable
}
}
For reference:
It turned out to be an issue with how I used the API. NSOutlineView.removeItems/insertItems expect nil for the inParent parameter for the root item. I was handing in the actual root item. Using nil instead of the root item solved the problem.

NSOutlineView detect when children are collapsed

I'm trying to store the actual collapsed/expanded state of items in a NSOutlineView, such that it can be restored later. There are two methods available on NSOutlineViewDelegate:
outlineViewItemDidExpand(Notification)
outlineViewItemDidCollapse(Notification)
The problem is that these methods are not only called for the item the user clicks on, but for collapsible children as well. Example:
- a
-- b
--- c
When a is collapsed outlineViewItemDidCollapse is called twice, once for b and once for a. Marking both as collapsed is incorrect, since b should still be expanded and c should be visible after expanding a again. So the actual state for b should be expanded.
When a user Option-clicks on a all children are collapsed as well (outlineView.collapseItem(item, collapseChildren: true)). After expanding a again, b should stay collapsed. The state for b should be collapsed in this case.
The two different states:
a: collapsed, b: expanded (but hidden due to parent)
a: collapsed, b: collapsed (and hidden due to parent)
Is there any way to differentiate between these two actions/states, such that I can properly restore it later?
Some ideas:
NSOutlineView can save and restore the expanded items (autosaveExpandedItems). The settings can be retrieved from NSUserDefaults. The key is NSOutlineView Items <autosaveName>.
Subclass NSOutlineView and override expandItem(_:expandChildren:) and collapseItem(_:collapseChildren:). The methods are not called for the children.
It might be possible to figure out which item is expanded or collapsed using the current event in outlineViewItemWillExpand(_:) and outlineViewItemWillCollapse(_:).
Edited to give a revised answer...
Apparently there is no easy way to recover whether a given container is expanded or collapsed once its parent object has been collapsed. Clearly something in the inner workings of the outline view remembers — possibly it's something as simple as storing the state of the cell view's disclosure button cell, or possibly it sets up a flag in the tree controller or its nodes — but in any case there's no direct programmatic interface. I suspect you'll have to keep track of it in the model object.
To do that, add a boolean property to your model item, such as:
#property BOOL currentlyExpanded;
Then you'll want to implement the two delegate methods outlineViewItemDidExpand: and outlineViewItemWillCollapse:, like so (this is assuming you are using a tree controller for the outline view):
- (void)outlineViewItemDidExpand:(NSNotification *)notification {
NSTreeNode * node = [notification.userInfo objectForKey:#"NSObject"];
NSOutlineView * ov = notification.object;
MyModelItem * item = [node representedObject];
/*
because we can only expand a visible container, we merely note
that this container is now expanded in our view. This will be
called for every container that is expanded, so we don't have to
think about it much.
*/
item.currentlyExpanded = YES;
}
- (void)outlineViewItemWillCollapse:(NSNotification *)notification {
NSTreeNode * node = [notification.userInfo objectForKey:#"NSObject"];
NSOutlineView * ov = notification.object;
MyModelItem * item = [node representedObject];
/*
Elements are collapsed from top to bottom. A collapsed parent
means the collapse started someplace farther up the chain than
our current item, so the expansion state of the current item is
not going to change unless the option key is held down, or you
implement a collapseItem:collapseChildren: with the second
parameter as YES. This accounts for the first; you'll have to
deal with the second in code.
*/
BOOL optionKeyIsDown = [[NSApp currentEvent] modifierFlags] && NSEventModifierFlagOption;
if ([ov isItemExpanded:[node parentNode]] || optionKeyIsDown) {
item.currentlyExpanded = NO;
}
}
These should keep the model item property currentlyExpanded synced with the outline view's internal expansion table (whatever that is). If you want to refer to it or store it in a database you can access it straight from the model objects.
The way I handled the bitmask throws a warning, but I'm too lazy to fix it...
Preserving this last part after the edit, because I think it's good info...
Normally you do not have to worry about any of this; NSOutlineView will 'do the right thing' of its own accord. If the user clicks the disclosure triangle of a container and then reopens it, all of the subcontainers will retain their expanded/collapsed states; if a user option-clicks the control triangle, all of the subcontainers will be marked as expanded or collapsed (depending on whether the user is option-opening or option-closing the parent). Don't bother with it unless you want some specialized behavior (which you would generally set up in the delegate methods outlineView:shouldCollapseItem: and outlineView:shouldExpandItem:).
If you're trying to retain the expansion state across app invocations, set the NSOutlineView property autosaveExpandedItems to true. No bookkeeping necessary...

Referencing elements in RadListView itemtemplate

I'm using RadListView and intercepting onItemLoading event.
Within that event, can I reference individual view elements inside the itemTemplate.
I see args.view._subViews - but I was wondering whether i could find element by name or something else. I assume id would not work because each item would have the same id.
You are correct getting by Id would only return the first one. However, if you have the reference to the ListView child group; using id will work to get the element out of a group.
Now if you use my NativeScript-Dom plugin then it is very simple; you can do:
var elements = RadListView.getElementsByClassName('someClass'); or RadListView.getElementsByTagName('Label'); or the newer functionality `
RadListView.runAgainstTagNames('Label', function(element) {
/* do something with this element */
});
And work with an array of elements that match your criteria.
Please also note that in a ListView that not all elements are preset; ListViews only have as many elements are needed to fill the ListView + 1 typically; so even if you have 2,000 items in the list view; you might only have 10 actual child ListView groups of elements. So when you did a GetElementsByTagNames('Label') you would only get 10 of them...

E4 RCP How to set selection of ToolBarItem that contains Radio Buttons

In Eclipse E4 (Luna), using the application model to create parts, handlers, commands, handled menu items etc, (these are not created programatically). I have a toolbar. This contains a sub-Menu item called "Filter" that contains another sub-menu of two filters. The two filters are two Handled Menu Items which are set up as "Radio" Buttons.
When I select the appropriate in the UI of my running app from the selection, the Radio button switches just fine to the selected Item. However I would like this selection to update (deselecting one Radio button and selecting the appropriate radio button of the handled menu item) when my ViewPart changes through other UI selection. Currently my ViewPart updates, but the Radio buttons are on the same previous selection through the UI.
Is there a way in which I get access both Handled Menu Item's IDs and set the selection (one to false, the other to true) when the viewer is updated.
Image of design is attached below:
Hierarchy of the application model is as follows:
Thanks in advance,
Marv
You can use the model service to find menu items. Use something like:
#Inject
EModelService modelService;
#Inject
MApplication app;
List<MMenuItem> items = modelService.findElements(app, "menu item id", MMenuItem.class, Collections.emptyList(), EModelService.IN_MAIN_MENU);
Once you have the MMenuItem you can call the setSelected(boolean) method to change the selection.
To find a menu item which is in a Part menu use:
modelService.findElements(app, "menu item id", MMenuItem.class, Collections.emptyList(), EModelService.IN_PART);
(IN_PART argument instead of IN_MAIN_MENU).
You could also specify the MPart rather than the Application as the first argument to findElements which may speed up the search.
For menus as a child of a Tool Bar Item it appears that the model services cannot find these directly. However you can find the Tool Bar Item and look at the menu yourself:
List<MToolItem> items = modelService.findElements(app, "tool bar item id", MToolItem.class, Collections.emptyList(), EModelService.IN_PART);
MToolItem item = items.get(0);
MMenu menu = item.getMenu();
List<MMenuElement> children = menu.getChildren();
... search menu elements
I solved this by starting with MPart PartID and drilling down to the HandledMenuItems on which I wanted to set the Radio Button selections, then setting the selection property for each individual HandledMenuItem.
This can probably be refactored to be more concise, but I've left the code with each step to have the solution easier to read.
BTW, in every instance / combination of the EModelService methods, the list returned a size of 0. So I'm not certain if that will work for what I'm trying to achieve. The following does work, although I'm not certain it is the most efficient means.
I hope this helps others.
// Get view part
MPart viewPart = _partService.findPart("part_id");
// get list of all menu items from the Part
List<MMenu> viewPartMenu = viewPart.getMenus();
// Get list of ViewMenus from viewPartMenu there is only one View Menu so it will be index 0
MMenu viewMenu = viewPartMenu .get(0);
// Get list of MMenuElements from the viewMenu - the children in the view menu
List<MMenuElement> viewMenuElements = viewMenu.getChildren();
// This gets me to the 2 HandledMenuItems
// Upper Most HandledMenuItem Radio Button is at viewMenuElements index 0. This is cast to MHandledMenuItem
MHandledMenuItem upperHandledMenuItem = (MHandledMenuItem) viewMenuElements.get(0);
// Set Selection
upperHandledMenuItem.setSelected(false);
// Lower Most HandledMenuItem Radio Button is at viewMenuElements index 1. This is cast to MHandledMenuItem
MHandledMenuItem lowerHandledMenuItem = (MHandledMenuItem) viewMenuElements.get(1);
// Set selection
lowerHandledMenuItem.setSelected(true);

Resources