Eclipse JFace TreeViewer tutorial. This tutorial explains the usage of Eclipse JFace TreeViewer. It also shows the usage of a DelegatingStyledCellLabelProvider.
1. Prerequisites
Please see Introduction to JFace for an introduction to the concepts behind this example.
For an example on how to build JFace Tables please see JFace Table Tutorial.
2. JFace TreeViewer
2.1. Using viewers to display a tree
The TreeViewer
class provides viewer support for displaying trees. The usage of this class is similar to the TableViewer
class.
The main difference is that the TreeViewer
class requires a structured content provider.
Typically your content provider has to implement the ITreeContentProvider
interface to be used with your TreeViewer
class.
Basically a TreeViewer can be used similar to a TableViewer, which just shows a list of elements by using the following content provider:
public class TreeContentProvider implements ITreeContentProvider {
@Override
public boolean hasChildren(Object element) {
return false;
}
@Override
public Object getParent(Object element) {
return null;
}
@Override
public Object[] getElements(Object inputElement) {
return ArrayContentProvider.getInstance().getElements(inputElement);
}
@Override
public Object[] getChildren(Object parentElement) {
return null;
}
}
This ITreeContentProvider
just delegates to the ArrayContentProvider
in its getElements
method and the elements have no children.
@PostConstruct
public void postConstruct(Composite parent) {
TreeViewer viewer = new TreeViewer(parent);
viewer.setContentProvider(new TreeContentProvider());
viewer.getTree().setHeaderVisible(true);
viewer.getTree().setLinesVisible(true);
TreeViewerColumn viewerColumn = new TreeViewerColumn(viewer, SWT.NONE);
viewerColumn.getColumn().setWidth(300);
viewerColumn.getColumn().setText("Names");
viewerColumn.setLabelProvider(new ColumnLabelProvider());
viewer.setInput(new String[] { "Simon Scholz", "Lars Vogel", "Dirk Fauth", "Wim Jongman", "Tom Schindl" });
GridLayoutFactory.fillDefaults().generateLayout(parent);
}
2.2. Selection and double-click listener
JFace allows you to access the SWT controls to define listeners on your viewer.
For example you can add a SelectionListener
implementation to the SWT control which is wrapped in the JFace object.
The following code snippet demonstrates how to expand a tree with a mouse click.
// the viewer field is an already configured TreeViewer
Tree tree = (Tree) viewer.getControl();
tree.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
TreeItem item = (TreeItem) e.item;
if (item.getItemCount() > 0) {
item.setExpanded(!item.getExpanded());
// update the viewer
viewer.refresh();
}
}
});
Viewers allows you to add certain listeners directly to them.
The following example shows how to expand an instance of a TreeViewer
with a double click.
viewer.addDoubleClickListener(new IDoubleClickListener() {
@Override
public void doubleClick(DoubleClickEvent event) {
TreeViewer viewer = (TreeViewer) event.getViewer();
IStructuredSelection thisSelection = (IStructuredSelection) event.getSelection();
Object selectedNode = thisSelection.getFirstElement();
viewer.setExpandedState(selectedNode,
!viewer.getExpandedState(selectedNode));
}
});
2.3. Adjusting tree columns TreeColumns on expand
In case a TreeViewer
has multiple columns it does not look good, if the first column, which contains the expandable items is clipped.
In order to let a column fit to it’s contents width you can invoke the columns pack
method like that.
// the viewer field is an already configured TreeViewer
Tree tree = (Tree) viewer.getControl();
Listener listener = new Listener() {
@Override
public void handleEvent(Event event) {
TreeItem treeItem = (TreeItem) event.item;
final TreeColumn[] treeColumns = treeItem.getParent().getColumns();
display.asyncExec(new Runnable() {
@Override
public void run() {
for (TreeColumn treeColumn : treeColumns)
treeColumn.pack();
}
});
}
};
tree.addListener(SWT.Expand, listener);
With this code every column of the Tree
will have the appropriate width, when a TreeItem
is expaned, so that the contents of the column is not clipped.
2.4. Increasing the font size
The font size can be increased by simply setting a Font for the underlying SWT Tree.
@PostConstruct
public void postConstruct(Composite parent) {
ResourceManager resourceManager = new LocalResourceManager(JFaceResources.getResources(), parent);
TreeViewer viewer = new TreeViewer(parent);
viewer.setContentProvider(new TreeContentProvider());
viewer.getTree().setHeaderVisible(true);
viewer.getTree().setLinesVisible(true);
viewer.getTree().setFont(resourceManager.createFont(FontDescriptor.createFrom("Arial", 32, SWT.ITALIC)));
TreeViewerColumn viewerColumn = new TreeViewerColumn(viewer, SWT.NONE);
viewerColumn.getColumn().setWidth(300);
viewerColumn.getColumn().setText("Names");
viewerColumn.setLabelProvider(new ColumnLabelProvider());
viewer.setInput(new String[] { "Simon Scholz", "Lars Vogel", "Dirk Fauth", "Wim Jongman", "Tom Schindl" });
GridLayoutFactory.fillDefaults().generateLayout(parent);
}
3. Optional Exercise: File browser via a TreeViewer
3.1. Create a new application
This exercise is a stand-alone exercise and can be used to repeat the steps of creating an Eclipse 4 application.
Use the Eclipse 4 wizard from Eclipse RCP tutorial. Make sure to uncheck "Create sample content" on the last page of the wizard:
to create a new Eclipse 4 application without sample data called com.example.e4.filebrowser. The exact process is shown in the3.2. Add an image file
Download or create an icon called folder.png and place it into the "icons" folder of your plug-in.
You can use the following example icon:
This icon is taken from the FamFamFam silk icons collection and is licensed under the Creative Commons Attribution 2.5 License.
3.3. Create a part
Add a part stack with a
part
to your application model and display a
TreeViewer
in this
part.
Implement a class for the
ITreeContentProvider
interface
which allows you to browse the file system. Review the
Javadoc
of
this class to understand the methods of this interface.
Also implement your custom
LabelProvider
for the tree.
Use
viewer.setInput(File.listRoots());
to set the initial input to the
viewer.
The following listing contains an example implementation for
this
exercise. It assumes that you added the "folder.png" icon to the
"icons" folder. It also demonstrates the usage of a
ViewLabelProvider
.
package com.vogella.jface.treeviewer.parts;
import java.io.File;
import java.net.URL;
import jakarta.annotation.PostConstruct;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.Path;
import org.eclipse.e4.ui.di.Focus;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.resource.LocalResourceManager;
import org.eclipse.jface.resource.ResourceManager;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
public class FileBrowserPart {
private TreeViewer viewer;
@PostConstruct
public void createControls(Composite parent) {
viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
viewer.setContentProvider(new ViewContentProvider());
viewer.setLabelProvider(new DelegatingStyledCellLabelProvider(
new ViewLabelProvider(createImageDescriptor())));
viewer.setInput(File.listRoots());
}
private ImageDescriptor createImageDescriptor() {
Bundle bundle = FrameworkUtil.getBundle(ViewLabelProvider.class);
URL url = FileLocator.find(bundle, new Path("icons/folder.png"), null);
return ImageDescriptor.createFromURL(url);
}
class ViewContentProvider implements ITreeContentProvider {
public void inputChanged(Viewer v, Object oldInput, Object newInput) {
}
@Override
public void dispose() {
}
@Override
public Object[] getElements(Object inputElement) {
return (File[]) inputElement;
}
@Override
public Object[] getChildren(Object parentElement) {
File file = (File) parentElement;
return file.listFiles();
}
@Override
public Object getParent(Object element) {
File file = (File) element;
return file.getParentFile();
}
@Override
public boolean hasChildren(Object element) {
File file = (File) element;
if (file.isDirectory()) {
return true;
}
return false;
}
}
class ViewLabelProvider extends LabelProvider implements IStyledLabelProvider {
private ImageDescriptor directoryImage;
private ResourceManager resourceManager;
public ViewLabelProvider(ImageDescriptor directoryImage) {
this.directoryImage = directoryImage;
}
@Override
public StyledString getStyledText(Object element) {
if(element instanceof File) {
File file = (File) element;
StyledString styledString = new StyledString(getFileName(file));
String[] files = file.list();
if (files != null) {
styledString.append(" ( " + files.length + " ) ",
StyledString.COUNTER_STYLER);
}
return styledString;
}
return null;
}
@Override
public Image getImage(Object element) {
if(element instanceof File) {
if(((File) element).isDirectory()) {
return getResourceManager().createImage(directoryImage);
}
}
return super.getImage(element);
}
@Override
public void dispose() {
// garbage collect system resources
if(resourceManager != null) {
resourceManager.dispose();
resourceManager = null;
}
}
protected ResourceManager getResourceManager() {
if(resourceManager == null) {
resourceManager = new LocalResourceManager(JFaceResources.getResources());
}
return resourceManager;
}
private String getFileName(File file) {
String name = file.getName();
return name.isEmpty() ? file.getPath() : name;
}
}
@Focus
public void setFocus() {
viewer.getControl().setFocus();
}
}
Link from your part in the application model to your new class.
3.4. Validate
Start your new application ensure that you see the content of your file system in your tree.
4. Optional exercise: Add multiple columns to a tree viewer
This exercise assumes that the previous exercise: File browser via a TreeViewer has been done, since this exercise is based on it.
|
4.1. Using TreeViewerColumns in the file browser
To add multiple columns in a
TreeViewer
, the
TreeViewerColumn
class can be used to define columns for the underlying tree.
A
TreeViewerColumn
itself is a wrapper around the
TreeColumn
widget
from SWT.
// make the table header visible to see the column's text
viewer.getTree().setHeaderVisible(true);
TreeViewerColumn viewerColumn = new TreeViewerColumn(viewer, SWT.NONE);
TreeColumn column = viewerColumn.getColumn();
column.setText("Name");
column.setWidth(300);
Instead of applying a
CellLabelProvider
for the whole viewer each
TreeViewerColumn
gets its own
CellLabelProvider
assigned. This allows that label for each column can
be defined separately. To
assign a label provider to the tree
viewer column
the
TreeViewerColumn#setLabelProvider
method is used.
package com.vogella.jface.treeviewer.parts;
import java.io.File;
import java.net.URL;
import java.text.DateFormat;
import java.util.Date;
import jakarta.annotation.PostConstruct;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.Path;
import org.eclipse.e4.ui.di.Focus;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.resource.LocalResourceManager;
import org.eclipse.jface.resource.ResourceManager;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.TreeViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.TreeColumn;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
public class FileBrowserPart {
private TreeViewer viewer;
private static final DateFormat dateFormat = DateFormat.getDateInstance();
@PostConstruct
public void createControls(Composite parent) {
viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
viewer.setContentProvider(new ViewContentProvider());
viewer.getTree().setHeaderVisible(true);
TreeViewerColumn mainColumn = new TreeViewerColumn(viewer, SWT.NONE);
mainColumn.getColumn().setText("Name");
mainColumn.getColumn().setWidth(300);
mainColumn.setLabelProvider(
new DelegatingStyledCellLabelProvider(
new ViewLabelProvider(createImageDescriptor())));
TreeViewerColumn modifiedColumn = new TreeViewerColumn(viewer, SWT.NONE);
modifiedColumn.getColumn().setText("Last Modified");
modifiedColumn.getColumn().setWidth(100);
modifiedColumn.getColumn().setAlignment(SWT.RIGHT);
modifiedColumn
.setLabelProvider(new DelegatingStyledCellLabelProvider(
new FileModifiedLabelProvider(dateFormat)));
TreeViewerColumn fileSizeColumn = new TreeViewerColumn(viewer, SWT.NONE);
fileSizeColumn.getColumn().setText("Size");
fileSizeColumn.getColumn().setWidth(100);
fileSizeColumn.getColumn().setAlignment(SWT.RIGHT);
fileSizeColumn.setLabelProvider(new DelegatingStyledCellLabelProvider(
new FileSizeLabelProvider()));
viewer.setInput(File.listRoots());
}
private ImageDescriptor createImageDescriptor() {
Bundle bundle = FrameworkUtil.getBundle(ViewLabelProvider.class);
URL url = FileLocator.find(bundle, new Path("icons/folder.png"), null);
return ImageDescriptor.createFromURL(url);
}
class ViewContentProvider implements ITreeContentProvider {
public void inputChanged(Viewer v, Object oldInput, Object newInput) {
}
@Override
public void dispose() {
}
@Override
public Object[] getElements(Object inputElement) {
return (File[]) inputElement;
}
@Override
public Object[] getChildren(Object parentElement) {
File file = (File) parentElement;
return file.listFiles();
}
@Override
public Object getParent(Object element) {
File file = (File) element;
return file.getParentFile();
}
@Override
public boolean hasChildren(Object element) {
File file = (File) element;
if (file.isDirectory()) {
return true;
}
return false;
}
}
class ViewLabelProvider extends LabelProvider implements IStyledLabelProvider {
private ImageDescriptor directoryImage;
private ResourceManager resourceManager;
public ViewLabelProvider(ImageDescriptor directoryImage) {
this.directoryImage = directoryImage;
}
@Override
public StyledString getStyledText(Object element) {
if (element instanceof File) {
File file = (File) element;
StyledString styledString = new StyledString(getFileName(file));
String[] files = file.list();
if (files != null) {
styledString.append(" ( " + files.length + " ) ", StyledString.COUNTER_STYLER);
}
return styledString;
}
return null;
}
@Override
public Image getImage(Object element) {
if (element instanceof File) {
if (((File) element).isDirectory()) {
return getResourceManager().createImage(directoryImage);
}
}
return super.getImage(element);
}
@Override
public void dispose() {
// garbage collection system resources
if (resourceManager != null) {
resourceManager.dispose();
resourceManager = null;
}
}
protected ResourceManager getResourceManager() {
if (resourceManager == null) {
resourceManager = new LocalResourceManager(JFaceResources.getResources());
}
return resourceManager;
}
private String getFileName(File file) {
String name = file.getName();
return name.isEmpty() ? file.getPath() : name;
}
}
class FileModifiedLabelProvider extends LabelProvider implements IStyledLabelProvider {
private DateFormat dateLabelFormat;
public FileModifiedLabelProvider(DateFormat dateFormat) {
dateLabelFormat = dateFormat;
}
@Override
public StyledString getStyledText(Object element) {
if (element instanceof File) {
File file = (File) element;
long lastModified = file.lastModified();
return new StyledString(dateLabelFormat.format(new Date(lastModified)));
}
return null;
}
}
class FileSizeLabelProvider extends LabelProvider implements IStyledLabelProvider {
@Override
public StyledString getStyledText(Object element) {
if (element instanceof File) {
File file = (File) element;
if (file.isDirectory()) {
// a directory is just a container and has no size
return new StyledString("0");
}
return new StyledString(String.valueOf(file.length()));
}
return null;
}
}
@Focus
public void setFocus() {
viewer.getControl().setFocus();
}
}
4.2. Validate
Start your new application ensure that you see the content of your file system in your tree.
5. Searchable TreeViewer
This example shows how to make a JFace TreeViewer searchable. Pressing Ctrl-f opens a search dialog. If a tree item is found it gets selected.
public class SearchDialog extends Dialog {
public SearchDialog(Shell parent) {
super(parent);
}
private Tree tree;
private SearchableTreeViewer searchableTreeViewer;
private Button searchButton;
private Text searchField;
private TreeSearchAlgorithm algorithm;
public SearchDialog(Shell parent, SearchableTreeViewer searchableTreeViewer, TreeSearchAlgorithm algorithm) {
super(parent);
this.searchableTreeViewer = searchableTreeViewer;
this.algorithm = algorithm;
tree = searchableTreeViewer.getTree();
}
@Override
protected Control createDialogArea(Composite parent) {
Composite container = (Composite) super.createDialogArea(parent);
container.setLayout(new FillLayout());
searchField = new Text(container, SWT.NONE);
return container;
}
@Override
protected void createButtonsForButtonBar(Composite parent) {
searchButton = createButton(parent, IDialogConstants.PROCEED_ID, "Search", false);
searchButton.addSelectionListener(SelectionListener.widgetSelectedAdapter(this::search));
}
private void search(SelectionEvent event) {
TreeItem found = algorithm.search(tree.getItems(), searchField.getText());
if (found != null) {
searchableTreeViewer.setSelection(new StructuredSelection(found.getData()), true);
close();
} else {
System.out.println("Nothing Found");
}
}
}
public interface TreeSearchAlgorithm {
public TreeItem search(TreeItem[] treeItems, String searchTerm);
}
public class TreeContentProvider implements ITreeContentProvider {
@Override
public boolean hasChildren(Object element) {
return false;
}
@Override
public Object getParent(Object element) {
return null;
}
@Override
public Object[] getElements(Object inputElement) {
return ArrayContentProvider.getInstance().getElements(inputElement);
}
@Override
public Object[] getChildren(Object parentElement) {
return null;
}
}
public class SearchableTreeViewer extends TreeViewer {
private Composite parent;
public SearchableTreeViewer(Composite parent, TreeSearchAlgorithm algorithm) {
super(parent);
this.parent = parent;
parent.addKeyListener(KeyListener.keyPressedAdapter(event -> {
if (event.stateMask == SWT.CTRL && event.keyCode == 'f') {
startSearchDialog(algorithm);
}
}));
}
private void startSearchDialog(TreeSearchAlgorithm algorithm) {
SearchDialog searchDialog = new SearchDialog(parent.getShell(), this, algorithm);
searchDialog.open();
}
}
public class TreeWindow extends ApplicationWindow {
public TreeWindow() {
super(null);
// Don't return from open() until window closes
setBlockOnOpen(true);
// Open the main window
open();
// Dispose the display
Display.getCurrent().dispose();
}
@Override
protected void configureShell(Shell shell) {
super.configureShell(shell);
// Set the title bar text and the size
shell.setText("Searchable Tree");
shell.setSize(400, 400);
}
@Override
protected Control createContents(Composite parent) {
Composite composite = new Composite(parent, SWT.NONE);
composite.setLayout(new FillLayout());
SearchableTreeViewer treeViewer = new SearchableTreeViewer(composite,
(treeItems, searchText) -> {
for (TreeItem item : treeItems) {
if (item.getText().toUpperCase().contains(searchText.toUpperCase())) {
return item;
};
}
return null;
});
treeViewer.setContentProvider(new TreeContentProvider());
TreeViewerColumn viewerColumn = new TreeViewerColumn(treeViewer, SWT.NONE);
treeViewer.getTree().setHeaderVisible(true);
treeViewer.getTree().setLinesVisible(true);
viewerColumn.getColumn().setWidth(300);
viewerColumn.getColumn().setText("Names");
viewerColumn.setLabelProvider(new ColumnLabelProvider());
treeViewer.setInput(new String[] { "Simon Scholz", "Lars Vogel", "Dirk Fauth", "Wim Jongman", "Tom Schindl" });
return composite;
}
public static void main(String[] args) {
new TreeWindow();
}
}
6. Learn more and get support
This tutorial continues on Eclipse RCP online training or Eclipse IDE extensions with lots of video material, additional exercises and much more content.
7. Links and Literature
7.2. vogella Java example code
If you need more assistance we offer Online Training and Onsite training as well as consulting