Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there SWT Combo Box with any Object as data and LabelProvider for display?

I'm searching for an implementation of a Combo Box in SWT that will allow me to set the the entries in it with a list of any Object, in a similar way to Trees and Tables.

Ideally I want the String when dropped down to be able to be different to the final String once selected. IE a drop down to select a person that shows their age next to their name in the list, but only their name when selected and placed in the box.

All I can find that exists are String based Combo Boxes with no way of describing different display options, so I guess i's going to have to build a new component if I want the thing to work, but I was hoping someone has already implemented such a thing (since I've seen this sort of functionality in some applications) and I just can't find it?

I hope to create something like this.

enter image description here

like image 225
Link19 Avatar asked Sep 20 '12 11:09

Link19


2 Answers

The JFace ComboViewer seems to be what you want. It is backed by a ModelProvider which holds your Objects. A LabelProvider is used to display the text inside the combo.

Here is an excellent tutorial by Vogella.

Here is an example that does what you want. It basically saves the current combo selection if a boolean of the displayed objects:

public static void main(String[] args) {
    final Display display = new Display();
    final Shell shell = new Shell(display);
    shell.setLayout(new FillLayout());

    final ComboViewer viewer = new ComboViewer(shell, SWT.READ_ONLY);

    viewer.setContentProvider(ArrayContentProvider.getInstance());

    /* if the current person is selected, show text */
    viewer.setLabelProvider(new LabelProvider() {
        @Override
        public String getText(Object element) {
            if (element instanceof Person) {
                Person current = (Person) element;

                if(current.isSelected())
                    return current.getName();
                else
                    return "";
            }
            return super.getText(element);
        }
    });

    final Person[] persons = new Person[] { new Person("Baz"),
            new Person("BazBaz"), new Person("BazBazBaz") };

    viewer.setInput(persons);

    /* within the selection event, tell the object it was selected */
    viewer.addSelectionChangedListener(new ISelectionChangedListener() {
        @Override
        public void selectionChanged(SelectionChangedEvent event) {
            IStructuredSelection selection = (IStructuredSelection) event.getSelection();
            Person person = (Person)selection.getFirstElement();

            for(Person p : persons)
                p.setSelected(false);

            person.setSelected(true);

            viewer.refresh();
        }
    });

    viewer.setSelection(new StructuredSelection(viewer.getElementAt(0)), true);

    shell.pack();
    shell.setSize(200, shell.getSize().y);
    shell.open();
    while (!shell.isDisposed()) {
        if (!display.readAndDispatch()) {
            display.sleep();
        }
    }
    display.dispose();
}

public static class Person {
    private String name;

    /* this will be true for the selected person */
    boolean isSelected;

    public Person(String name) {
        this.name = name;
        this.setSelected(false);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public boolean isSelected() {
        return isSelected;
    }

    public void setSelected(boolean isSelected) {
        this.isSelected = isSelected;
    }

}
like image 103
Baz Avatar answered Oct 06 '22 01:10

Baz


Many thanks to Baz for putting me on the right track and providing me with some initial code. But having taken all this in I wanted something cleaner to use in my RCP view itself and also though a generic instance of a combo box would be a nice to have in future, so I've wrapped all the boilerplate stuff in a new class that you can use like this:

    List<Person> persons = new ArrayList<Person>();
    persons.add(new Person("Baz",26));
    persons.add(new Person("Glen",27));
    persons.add(new Person("Jimmy",18));

    TypedComboBox<Person> box = new TypedComboBox<Person>(parent);

    box.addSelectionListener(new TypedComboBoxSelectionListener<Person>() {

        @Override
        public void selectionChanged(TypedComboBox<Person> typedComboBox,
                Person newSelection) {
            System.out.println(newSelection);
        }
    });

    box.setLabelProvider(new TypedComboBoxLabelProvider<Person>() {

        @Override
        public String getSelectedLabel(Person element) {
            return element.getName();
        }

        @Override
        public String getListLabel(Person element) {
            return element.getName() + " | " + element.getAge();
        }
    });


    box.setContent(persons);

    box.selectFirstItem();

For my view where I'm going to need multiple selection boxes with various objects I am much more happy using the typed box as I'm not casting stuff and reproducing boilerplate code in the body of the view's code.

You don't have to set a label provider if you just want the toSting() method used, otherwise there are two labels to provide, one for the selected item, and one for all the others.

Just in case anyone stumbles upon this question with the same issue as me here's my code, feedback would be greatly appreciated.

import java.util.ArrayList;
import java.util.List;

import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

public class TypedComboBox<T> {

    private ComboViewer viewer;
    private TypedComboBoxLabelProvider<T> labelProvider;
    private List<T> content;
    private List<TypedComboBoxSelectionListener<T>> selectionListeners;
    private T currentSelection;

    public TypedComboBox(Composite parent) {
        this.viewer = new ComboViewer(parent, SWT.READ_ONLY);
        this.viewer.setContentProvider(ArrayContentProvider.getInstance());

        viewer.setLabelProvider(new LabelProvider() {
            @Override
            public String getText(Object element) {
                T typedElement = getTypedObject(element);
                if (labelProvider != null && typedElement != null) {
                    if (typedElement == currentSelection) {
                        return labelProvider.getSelectedLabel(typedElement);
                    } else {
                        return labelProvider.getListLabel(typedElement);
                    }

                } else {
                    return element.toString();
                }
            }
        });

        viewer.addSelectionChangedListener(new ISelectionChangedListener() {
            @Override
            public void selectionChanged(SelectionChangedEvent event) {
                IStructuredSelection selection = (IStructuredSelection) event
                        .getSelection();
                T typedSelection = getTypedObject(selection.getFirstElement());
                if (typedSelection != null) {
                    currentSelection = typedSelection;
                    viewer.refresh();
                    notifySelectionListeners(typedSelection);
                }

            }
        });

        this.content = new ArrayList<T>();
        this.selectionListeners = new ArrayList<TypedComboBoxSelectionListener<T>>();
    }

    public void setLabelProvider(TypedComboBoxLabelProvider<T> labelProvider) {
        this.labelProvider = labelProvider;
    }

    public void setContent(List<T> content) {
        this.content = content;
        this.viewer.setInput(content.toArray());
    }

    public T getSelection() {
        return currentSelection;
    }

    public void setSelection(T selection) {
        if (content.contains(selection)) {
            viewer.setSelection(new StructuredSelection(selection), true);
        }
    }

    public void selectFirstItem() {
        if (content.size()>0) {
            setSelection(content.get(0));
        }
    }
    public void addSelectionListener(TypedComboBoxSelectionListener<T> listener) {
        this.selectionListeners.add(listener);
    }

    public void removeSelectionListener(
            TypedComboBoxSelectionListener<T> listener) {
        this.selectionListeners.remove(listener);
    }

    private T getTypedObject(Object o) {
        if (content.contains(o)) {
            return content.get(content.indexOf(o));
        } else {
            return null;
        }
    }

    private void notifySelectionListeners(T newSelection) {
        for (TypedComboBoxSelectionListener<T> listener : selectionListeners) {
            listener.selectionChanged(this, newSelection);
        }
    }

And the label provider interface.

public interface TypedComboBoxLabelProvider<T> {

    public String getSelectedLabel(T element);

    public String getListLabel(T element);

}

And the selection listener:

public interface TypedComboBoxSelectionListener<T> {

    public void selectionChanged(TypedComboBox<T> typedComboBox, T newSelection);
}
like image 38
Link19 Avatar answered Oct 06 '22 00:10

Link19