262 lines
6.8 KiB
Java
262 lines
6.8 KiB
Java
package envoy.client.ui.list;
|
|
|
|
import java.awt.event.MouseAdapter;
|
|
import java.awt.event.MouseEvent;
|
|
import java.awt.event.MouseListener;
|
|
import java.util.HashSet;
|
|
import java.util.Set;
|
|
|
|
import javax.swing.*;
|
|
|
|
/**
|
|
* Provides a vertical list layout of components provided in a
|
|
* {@link Model}. Similar to {@link javax.swing.JList} but capable
|
|
* of rendering {@link JPanel}s.<br>
|
|
* <br>
|
|
* Project: <strong>envoy-client</strong><br>
|
|
* File: <strong>ComponentList.java</strong><br>
|
|
* Created: <strong>25.01.2020</strong><br>
|
|
*
|
|
* @param <E> the type of object displayed in this list
|
|
* @author Kai S. K. Engelbart
|
|
* @since Envoy Client v0.3-alpha
|
|
*/
|
|
public class ComponentList<E> extends JPanel {
|
|
|
|
private Model<E> model;
|
|
private Renderer<E> renderer;
|
|
private SelectionHandler<E> selectionHandler;
|
|
private SelectionMode selectionMode = SelectionMode.NONE;
|
|
private Set<Integer> selection = new HashSet<>();
|
|
|
|
private static final long serialVersionUID = 0L;
|
|
|
|
/**
|
|
* Defines the possible modes of selection that can be performed by the user
|
|
*
|
|
* @since Envoy Client v0.1-beta
|
|
*/
|
|
public static enum SelectionMode {
|
|
/**
|
|
* Selection is completely ignored.
|
|
*/
|
|
NONE,
|
|
|
|
/**
|
|
* Only a single element can be selected.
|
|
*/
|
|
SINGLE,
|
|
|
|
/**
|
|
* Multiple elements can be selected regardless of their position.
|
|
*/
|
|
MULTIPLE
|
|
}
|
|
|
|
/**
|
|
* Creates an instance of {@link ComponentList}.
|
|
*
|
|
* @since Envoy Client v0.3-alpha
|
|
*/
|
|
public ComponentList() { setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); }
|
|
|
|
/**
|
|
* Removes all child components and then adds all components representing the
|
|
* elements of the {@link Model}.
|
|
*
|
|
* @since Envoy Client v0.3-alpha
|
|
*/
|
|
public void synchronizeModel() {
|
|
if (model != null) {
|
|
removeAll();
|
|
model.forEach(this::addElement);
|
|
revalidate();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Selects a list element by index. If the element is already selected, it is
|
|
* removed from the selection.
|
|
*
|
|
* @param index the index of the selected component
|
|
* @since Envoy Client v0.1-beta
|
|
*/
|
|
public void selectElement(int index) {
|
|
final JComponent element = getComponent(index);
|
|
if (selection.contains(index)) {
|
|
|
|
// Deselect if clicked again
|
|
if (selectionHandler != null) selectionHandler.selectionChanged(model.get(index), element, true);
|
|
selection.remove(index);
|
|
|
|
} else {
|
|
|
|
// Remove old selection if single selection is enabled
|
|
if (selectionMode == SelectionMode.SINGLE) clearSelection();
|
|
|
|
// Select item
|
|
if (selectionMode != SelectionMode.NONE) {
|
|
|
|
// Assign new selection
|
|
selection.add(index);
|
|
|
|
// Update element
|
|
if (selectionHandler != null) selectionHandler.selectionChanged(model.get(index), element, true);
|
|
}
|
|
}
|
|
|
|
revalidate();
|
|
repaint();
|
|
}
|
|
|
|
/**
|
|
* Removes the current selection.
|
|
*
|
|
* @since Envoy Client v0.1-alpha
|
|
*/
|
|
public void clearSelection() {
|
|
if (selectionHandler != null) selection.forEach(i -> selectionHandler.selectionChanged(model.get(i), getComponent(i), false));
|
|
selection.clear();
|
|
}
|
|
|
|
/**
|
|
* Adds an object to the list by rendering it with the current
|
|
* {@link Renderer}.
|
|
*
|
|
* @param elem the element to add
|
|
* @since Envoy Client v0.3-alpha
|
|
*/
|
|
void addElement(E elem) {
|
|
if (renderer != null) {
|
|
final JComponent component = renderer.getListCellComponent(this, elem);
|
|
component.addMouseListener(getSelectionListener(getComponentCount()));
|
|
add(component, getComponentCount());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param componentIndex the index of the list component to which the mouse
|
|
* listener will be added
|
|
* @return a mouse listener calling the
|
|
* {@link ComponentList#selectElement(int)} method with the
|
|
* component's index when a left click is performed by the user
|
|
* @since Envoy Client v0.1-beta
|
|
*/
|
|
private MouseListener getSelectionListener(int componentIndex) {
|
|
return new MouseAdapter() {
|
|
|
|
@Override
|
|
public void mouseClicked(MouseEvent e) { if (SwingUtilities.isLeftMouseButton(e)) selectElement(componentIndex); }
|
|
};
|
|
}
|
|
|
|
@Override
|
|
public JComponent getComponent(int n) { return (JComponent) super.getComponent(n); }
|
|
|
|
/**
|
|
* @return a set of all selected indices
|
|
* @since Envoy Client v0.1-beta
|
|
*/
|
|
public Set<Integer> getSelection() { return selection; }
|
|
|
|
/**
|
|
* @return a set of all selected elements
|
|
* @since Envoy Client v0.1-beta
|
|
*/
|
|
public Set<E> getSelectedElements() {
|
|
var selectedElements = new HashSet<E>();
|
|
selection.forEach(i -> selectedElements.add(model.get(i)));
|
|
return selectedElements;
|
|
}
|
|
|
|
/**
|
|
* @return the index of an arbitrary selected element
|
|
* @throws java.util.NoSuchElementException if no selection is present
|
|
* @since Envoy Client v0.1-beta
|
|
*/
|
|
public int getSingleSelection() { return selection.stream().findAny().get(); }
|
|
|
|
/**
|
|
* @return an arbitrary selected element
|
|
* @throws java.util.NoSuchElementException if no selection is present
|
|
* @since Envoy Client v0.1-beta
|
|
*/
|
|
public E getSingleSelectedElement() { return model.get(getSingleSelection()); }
|
|
|
|
/**
|
|
* @return the model
|
|
* @since Envoy Client v0.1-beta
|
|
*/
|
|
public Model<E> getModel() { return model; }
|
|
|
|
/**
|
|
* Sets the list model providing the list elements to render. The rendered
|
|
* components will be synchronized with the contents of the new model or removed
|
|
* if the new model is {@code null}.
|
|
*
|
|
* @param model the list model to set
|
|
* @return this component list
|
|
* @since Envoy Client v0.3-alpha
|
|
*/
|
|
public ComponentList<E> setModel(Model<E> model) {
|
|
|
|
// Remove old model
|
|
if (this.model != null) this.model.setComponentList(null);
|
|
|
|
// Synchronize with new model
|
|
this.model = model;
|
|
if (model != null) model.setComponentList(this);
|
|
synchronizeModel();
|
|
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* @return the renderer
|
|
* @since Envoy Client v0.1-beta
|
|
*/
|
|
public Renderer<E> getRenderer() { return renderer; }
|
|
|
|
/**
|
|
* @param renderer the renderer to set
|
|
* @return this component list
|
|
* @since Envoy Client v0.1-beta
|
|
*/
|
|
public ComponentList<E> setRenderer(Renderer<E> renderer) {
|
|
this.renderer = renderer;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* @return the selection mode
|
|
* @since Envoy Client v0.1-beta
|
|
*/
|
|
public SelectionMode getSelectionMode() { return selectionMode; }
|
|
|
|
/**
|
|
* Sets a new selection mode. The current selection will be cleared during this
|
|
* action.
|
|
*
|
|
* @param selectionMode the selection mode to set
|
|
* @return this component list
|
|
* @since Envoy Client v0.1-beta
|
|
*/
|
|
public ComponentList<E> setSelectionMode(SelectionMode selectionMode) {
|
|
this.selectionMode = selectionMode;
|
|
clearSelection();
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* @return the selection handler
|
|
* @since Envoy Client v0.1-beta
|
|
*/
|
|
public SelectionHandler<E> getSelectionHandler() { return selectionHandler; }
|
|
|
|
/**
|
|
* @param selectionHandler the selection handler to set
|
|
* @since Envoy Client v0.1-beta
|
|
*/
|
|
public void setSelectionHandler(SelectionHandler<E> selectionHandler) { this.selectionHandler = selectionHandler; }
|
|
}
|