Jface ViewerFilter filtering full tree depth - eclipse

I have a FilteredTree that displays many nodes, where each node can have multiple children and subchildren (n-depth).
The filter filters all nodes (and children in any depth) that match the pattern.
Now I want to add another search functionality:
Every node represent an object , each object has some fields, so i want to list the nodes that have fields that match the text.
I tired to use ViewerFilter.
The problem with it that it doesn't invoked for every node in the tree only for current opened branch.
public class TheFilter extends ViewerFilter {
private String searchString;
public void setSearchText(String s) {
}
public boolean select(Viewer viewer,
Object parentElement,
Object element) { //triggered Only for one level
return true;
}
}
Update:
When the searched item is found I want to show not just that node that contains the searched item but also it's parent (all parents).

FilteredTree accepts an org.eclipse.ui.dialogs.PatternFilter as argument to determine it's filter strategy.
You can achieve the desired result by subclassing PatternFilter and overriding it's isLeafMatch method.
For example:
PatternFilter filter = new PatternFilter() {
#Override
protected boolean isLeafMatch(Viewer viewer, Object element) {
String field = ((Node) element).getField();
if(field== null) {
return false;
}
return wordMatches(field);
}
};
FilteredTree tree = new FilteredTree(parent, SWT.MULTI | SWT.H_SCROLL
| SWT.V_SCROLL, filter, true);

As I understand you want the search to be applied to all elements regardless of the fact that they are currently expanded or not. Although I am not sure why would you want the filtering to applied to the deepest leaf as well, is it because you want root nodes that will not show any children to disappear as well?
There could be several ways here, if you want to force your filter to be applied to all elements, you can expand all items in the tree using TreeViewer#expandAll, unless you are using a lazy content provider it should invoke filtering on all elements. You may collapse the tree again if you don't want to keep it expanded (may want to disable drawing while expanding and collapsing to avoid flicker through Tree#setRedraw(boolean)). Usually in trees when something is being searched, all filtered content is shown as expanded so that the deepest leaf that also matches the search criteria is also visible. If you want to maintain tree's expansion states of each element just as the user had done, it will require a little more work to expand all and then restore expansions.
Another approach is to implement the searching within the content provider. So your content provider can take the current search criterion and return only elements that match it. This means that you will have to manage yourself whether a certain parent should be visible or not. So on change of search field you only need to refresh tree and your content provider will ensure filtering.

Related

How do I arrange a list of buttons into rows of 4

I am trying to display the same game object in a table form of 4 columns and 2 rows so it would look like this:
GO GO GO GO
GO GO GO GO
G0 - gameObject
My gameObject is a button that can be pressed and has a text element on it to display the name of the profile on the button.
I have a List of strings of the names that i need to display on these GO buttons in the table form but i am struggling to position them correctly.
At the moment i have gotten to the point where i can Instantiate them so they all appear on the screen when the game is running, now i just need some advice on how i can position them properly in the format mentioned above.
How do i do this?
This is my code that i used to get the names and add them to the List:
private void GetProfiles()
{
List<string> profileNamesList = new List<string>();
if (Directory.Exists(filePath))
{
string[] files = Directory.GetFiles(filePath);
profileTileTemplate.SetActive(true);
foreach (var file in files)
{
string name;
name = file;
int index = name.IndexOf(filePath + "/", System.StringComparison.OrdinalIgnoreCase);
if (index >= 0)
{
int pathIndexEnd = index + filePath.Length + 1;
int stringLength = name.Length - pathIndexEnd - 5;
name = name.Substring(pathIndexEnd, stringLength);
}
profileNamesList.Add(name);
}
}
DisplayProfiles(profileNamesList);
}
private void DisplayProfiles(List<string> names)
{
}
I have tried using a for loop with in another for loop but i just end up instantiating multiple of the same game object.
This is what i get now:
And this is what i want it to look like:
This is kind of two questions, and I just realized that Unity has a built in component that will do this automatically, so I'll leave the three answers below.
How do I arrange UI gameobjects in rows?
Since it seems like you want to do this for UI elements there's actually a very easy solution. Add an empty GameObject as a child of your Canvas and add a Vertical LayoutGroup component. Add two children to that with horizontal layoutgroups. Add four placeholder prefabs of your gameobject to each horizontal layout group. Arrange them and configure the settings to get them looking the way you want (and make note of what happens if you add fewer than four items!)
Once you have it all set up, delete the placeholders. Then you can add your gameobjects to the horizontal group using Instantiate(Object original, Transform parent) (link) to parent them to the layout group, which will keep them arranged neatly. You can keep a list of those groups and each time you add four, switch to the next parent.
A neater way that seems to fit your use case (assuming there can potentially be more than 8 profiles) is to make a Scroll View that holds horizontal layout groups, which will each be a row of entries. That way instead of tracking which parent you want to add gameobjects to, you can just instantiate a new row every time you pass four entries.
If you're sure there will only ever be eight or fewer, the easiest thing to do would just be arrange eight blank buttons in the UI however you want them to appear. Then keep a list of the eight buttons and edit the text/image on them, no instantiation or looping necessary.
How do I split up a list of gameobjects into rows?
The actual code to process the list is below. This is just an example and there are plenty of different ways to do it, but I thought this demonstrated the logic clearly without depending on what UI elements you choose. As a rules of thumb, make sure to figure out the layout elements you like in Unity first using placeholders (like scroll view etc) and then figure out the code to fill them in. In other words, instantiating and laying out UI elements at runtime is a great way to give yourself a headache so it's best to only do it when you need to.
List<string> profileNamesList = new List<string>();
public int entriesPerRow; //only public so you can edit in the inspector. Otherwise, just use the number per row in your prefab.
public GameObject profileRowPrefab;
public GameObject scrollViewLayout;
private void DisplayProfiles(List<string> names)
{
int i = 0;
while( i < names.Count ) //"while the list has more names"
{
//create a new row as a child of the scroll view content area
//and save a reference for later
GameObject go = Instantiate(profileRowPrefab, scrollViewLayout);
for(j = 0; j < entriesPerRow; j++) //"add this many names"
{
if(i < names.Count)
{
//instantiate a button, or edit one in the row prefab, etc.
//depending on how you're doing it, this is where you'd use the go variable above
YourProfileButtonCreationMethod(names[i]);
i++;
}
else
{
//we've finished the list, so we're done
//you can add empty placeholders here if they aren't in the prefab
break;
}
}
}
}
YourProfileButtonCreationMethod will depend completely on how you want to implement the UI.
I wish I had thought of this an hour ago, but I've never used it myself. Unity has a built in layout feature that will do this for you (minus the scrolling, but you may be able to nest this in a scroll view).
How do I arrange UI elements in a grid?
Instead of making your own grid with horizontal and vertical layout groups, you can use the Grid Layout Group. Then just instantiate each item in the list as a button with the grid layout as their parent (see above). Here's a short video tutorial that shows what the result looks like.

Unity - How to react to scene picking? How to force select a parent by picking its child in the sceneview

I have the following situation I need an answer to:
I have a parent object with children. These children all have unique meshes. Whenever these children are selected in the SceneView, the parent needs to be selected in stead. The children should never have their inspectors exposed (not even a fraction of a second).
How do I do this?
There are two possible solutions that I have come up with which are not the solution I wish to go for.
First being using [SelectionBase] attribute on the parent object. This work perfectly, but only once. The first time the child is selected, the parent gets selected. However, when I click the child again, it still gets selected.
Second solution was to react on Selecten.onSelectionChanged. This however is too slow. I can set the selection to the parent if a child gets selected, but the child gets exposed for a few frames still.
Is there an instant solution to this which can guarantee me the functionality of SelectionBase, but then every time in stead of only the first time I click it?
Thanks in advance! :)
I have found a way to do exactly what i want. I combine the [SelectionBase] attribute with a piece of code in the editor OnSceneGui.
First add the [SelectionBase] attribute to your class
Second add this code to its editor class
private void OnSceneGUI()
{
HandleUtility.AddDefaultControl(0);
//Get the transform of the component with the selection base attribute
Transform selectionBaseTransform = component.transform;
//Detect mouse events
if (Event.current.type == EventType.MouseDown)
{
//get picked object at mouse position
GameObject pickedObject = HandleUtility.PickGameObject(Event.current.mousePosition, true);
//If selected null or a non child of the component gameobject
if (pickedObject == null || !pickedObject.transform.IsChildOf(selectionBaseTransform))
{
//Set selection to the picked object
Selection.activeObject = pickedObject;
}
}
}
This allows the first pick to select the component. From then on, only when you select non-child objects in the scene, selection will actually change.

Locating TreeObject in large Tree

I have a large tree and I can 'select' (i.e.highlight) any node of it. But if I have a large tree with all nodes expanded the user still needs to manually scroll down or up in order to locate the highlighted element. Is there a way which not only highlights the selected element but also locates it by automatically scrolling up/down in the tree?
TreePath path = createTreePath(editorID, treeObject);
getTreeViewer().setSelection(new TreeSelection(path), true);
getTreeViewer().refresh();
getTreeViewer().jumpToSelectedElement(true); // I need something like this. I made up the name of this imaginary method.
Use
public void reveal(Object elementOrTreePath)
As its name suggests elementOrTreePath can be a tree path or just an element.

Is there a way to highlight a specific item in the search results?

I have been trying to implement a slight change in the behavior of the chosen dropdown search results, but there doesn't seem to be any straightforward way to get at them. I have a list of countries preceded by three-letter ISO country code. I would like to be able to search as usual, but highlight the country if there is an exact match on the country code. The problem is that the matching country code will not always be the first item in the list. A more general request would be able to access the result list, and set the selection to some item other than the first.
By inspection, it looks to me like the search results only exist as a jquery object and within that the list items include a data-option-array-index with the original option list index, so I could find the item that I know I want highlighted in that DOM object and set the class "highlighted". But I also might need to scroll it into view, and messing with the underlying DOM directly is not ideal, so it would really be nice to have access to Chosen functions that would do that.
So, I figured it out, by accessing DOM. I have the index in the initial array that I want to highlight, and the following code will do it, assuming it is in the result list (using lodash _.find):
var highlightChosenResult = function(index) {
var resultList = $('li.active-result');
var result = _.find(resultList, function(el) {
return +$(el).attr('data-option-array-index') === index;
});
if (result) {
this.chosenApi.result_do_highlight($(result));
}
}

Always show the file path in FilteredResourcesSelectionDialog

In the FilteredResourcesSelectionDialog, some items show the file path beside them in grey color while others don"t show the path.
Why does this happen and how do I make sure that the file paths are always shown?
The path is shown when there are multiple entries with the same name. This is to allow you to distinguish the entries.
If you create a class derived from FilteredResourcesSelectionDialog (or anything based on FilteredItemsSelectionDialog) you can override the isDuplicateElement method to control this, for example:
#Override
public boolean isDuplicateElement(Object item)
{
return true;
}