Commit 60bc9df2 authored by Simon Barner's avatar Simon Barner
Browse files

Usability improvements

- Remember column width when new target elements are added
- Update column sorting when new target elements are added (-> use TreeViewer's column order array instead of actually shuffling the underlying collection of target elements)

Performance improvements
- Avoid full update (i.e., perform more efficient refresh) when names of source or target elements are changed
- Avoid multiple (redundant) updates when editor is opened
- Avoid updating combo boxes when selected element has not changed
- Avoid full update when an allocation is added/removed (simple refresh is enough)
refs 2950
parent c626df5a
AllocationTableEditor.java 7b87dbccdcfbe05efb2d3de090b164926f6b1c14 GREEN
ModelListenerEditorBase.java 231089cfa54ccfdd9919201373a338df88b0a11d GREEN
AllocationTableEditor.java 1991d88a9d76de614fa9d581019dc04430dd0ee6 YELLOW
ModelListenerEditorBase.java 868be127eef8c3723476767afb14278941293144 YELLOW
ParameterTableEditor.java 10b1d1d3c05f75f9ad4d7f7031fe69d733894919 GREEN
......@@ -17,6 +17,7 @@ package org.fortiss.af3.allocation.ui.editor;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.stream.Collectors.toList;
import static org.conqat.lib.commons.reflect.ReflectionUtils.isInstanceOfAny;
import static org.eclipse.jface.dialogs.MessageDialog.openConfirm;
import static org.eclipse.jface.viewers.AbstractTreeViewer.ALL_LEVELS;
......@@ -35,7 +36,6 @@ import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.emf.ecore.EStructuralFeature;
......@@ -211,7 +211,7 @@ public abstract class AllocationTableEditor<T extends AllocationTable> extends
return isInstanceOfAny(object, getSourceModelType(), getTargetModelType());
}
/** Performs a refresh for the given root element. */
/** Performs a refresh for the given {@link IProjectRootElement}. */
protected void refresh(IProjectRootElement model) {
if(model != null) {
Class<? extends IProjectRootElement> sourceModelType = getSourceModelType();
......@@ -245,6 +245,12 @@ public abstract class AllocationTableEditor<T extends AllocationTable> extends
this::setTargetEntityType);
}
treeViewerManager.refreshTreeViewer();
}
/** Performs a full update for the given {@link IProjectRootElement}. */
public void update(IProjectRootElement model) {
refresh(model);
treeViewerManager.updateTreeViewer();
}
......@@ -307,7 +313,7 @@ public abstract class AllocationTableEditor<T extends AllocationTable> extends
});
super.modelSelectionChanged(model, modelSetter, modelGetter);
refresh(model);
update(model);
}
}
......@@ -329,7 +335,6 @@ public abstract class AllocationTableEditor<T extends AllocationTable> extends
// For now update the entire table
treeViewerManager.updateTreeViewer();
}
/** {@inheritDoc} */
......@@ -347,8 +352,9 @@ public abstract class AllocationTableEditor<T extends AllocationTable> extends
/** {@inheritDoc} */
@Override
protected void renameEntity(IModelElement entity) {
// Update/refresh needed on rootElements to handle models where the visible top-model
// element is not a IProjectRootElement
IProjectRootElement rootElement = getParentElement(entity, IProjectRootElement.class, true);
// Needed to handle models where the visible top-model element is not a IProjectRootElement
refresh(rootElement);
if(rootElement == getEditedObject().getSourceView() ||
......@@ -374,6 +380,7 @@ public abstract class AllocationTableEditor<T extends AllocationTable> extends
gui = new AllocationTableEditorGUI(parent, SWT.NONE);
treeViewerManager = createTreeViewerManager(gui.getTreeViewer());
treeViewerManager.setUpdateRefreshEnabled(false);
final IAllocationService as = IAllocationService.getInstance();
IProjectRootElement sourceView = null;
......@@ -411,14 +418,16 @@ public abstract class AllocationTableEditor<T extends AllocationTable> extends
Collection<Class<? extends IModelElement>> sourceEntityTypes =
as.getSourceEntityTypes(allocationEntryType);
setupEntityTypeComboBox(gui.getComboViewerSourceEntityType(), this::setSourceEntityType);
setupEntityTypeComboBox(gui.getComboViewerSourceEntityType(), this::setSourceEntityType,
this::getSourceEntityType);
updateEntityTypeComboBox(gui.getComboViewerSourceEntityType(), sourceEntityTypes,
sourceView, this::setSourceEntityType);
Collection<Class<? extends IModelElement>> targetEntityTypes =
as.getTargetEntityTypes(allocationEntryType);
setupEntityTypeComboBox(gui.getComboViewerTargetEntityType(), this::setTargetEntityType);
setupEntityTypeComboBox(gui.getComboViewerTargetEntityType(), this::setTargetEntityType,
this::getTargetEntityType);
updateEntityTypeComboBox(gui.getComboViewerTargetEntityType(), targetEntityTypes,
targetView, this::setTargetEntityType);
......@@ -437,6 +446,9 @@ public abstract class AllocationTableEditor<T extends AllocationTable> extends
new SortColumnsCheckBoxSelectionListener());
super.createPartControl(parent);
treeViewerManager.setUpdateRefreshEnabled(true);
treeViewerManager.updateTreeViewer();
}
/** Selection listener to update how the {@link TreeViewer} displays the model's hierarchy. */
......@@ -510,7 +522,8 @@ public abstract class AllocationTableEditor<T extends AllocationTable> extends
/** Sets up the given entity type selection {@link ComboViewer}. */
private void setupEntityTypeComboBox(ComboViewer comboViewer,
Consumer<Class<? extends IModelElement>> setter) {
Consumer<Class<? extends IModelElement>> setter,
Supplier<Class<? extends IModelElement>> getter) {
comboViewer.setContentProvider(new ArrayContentProvider());
......@@ -532,9 +545,8 @@ public abstract class AllocationTableEditor<T extends AllocationTable> extends
@SuppressWarnings("unchecked") Class<? extends IModelElement> entityType =
checkAndPickFirst(event.getSelection(), Class.class);
setter.accept(entityType);
if(entityType != null) {
if(entityType != null && !entityType.equals(getter.get())) {
setter.accept(entityType);
treeViewerManager.updateTreeViewer();
}
}
......@@ -552,17 +564,21 @@ public abstract class AllocationTableEditor<T extends AllocationTable> extends
} else {
final Predicate<Class<? extends IModelElement>> hasEntity =
t -> getFirstChildWithType(model, t) != null;
usedEntityTypes = entityTypes.stream().filter(hasEntity).collect(Collectors.toList());
usedEntityTypes = entityTypes.stream().filter(hasEntity).collect(toList());
}
final Combo combo = comboViewer.getCombo();
combo.setEnabled(usedEntityTypes.size() > 1);
if(!usedEntityTypes.isEmpty()) {
// Select first element (existence implied by 'enabled')
final Class<? extends IModelElement> entityType =
usedEntityTypes.stream().findFirst().get();
setter.accept(entityType);
@SuppressWarnings("unchecked") Class<? extends IModelElement> entityType =
checkAndPickFirst(comboViewer.getSelection(), Class.class);
if(entityType == null || !usedEntityTypes.contains(entityType)) {
// Select first element (existence implied by 'enabled')
entityType = usedEntityTypes.stream().findFirst().get();
setter.accept(entityType);
}
comboViewer.setInput(usedEntityTypes);
comboViewer.setSelection(new StructuredSelection(entityType));
......@@ -612,7 +628,7 @@ public abstract class AllocationTableEditor<T extends AllocationTable> extends
if(elementFilter != null) {
modelElements = modelElements.filter(elementFilter);
}
return modelElements.collect(Collectors.toList());
return modelElements.collect(toList());
}
/** Determines the list of source model elements offered in this {@link AllocationTableEditor}. */
......
......@@ -43,6 +43,7 @@ import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.fortiss.af3.allocation.model.AllocationEntry;
import org.fortiss.af3.allocation.model.AllocationTable;
import org.fortiss.af3.project.model.FileProject;
import org.fortiss.af3.project.utils.ProjectUtils;
......@@ -87,28 +88,28 @@ public abstract class ModelListenerEditorBase<T extends EObject> extends GEFEdit
return;
}
Object notifier = notification.getNotifier();
final Display display = Display.getDefault();
if(eventType == ADD) {
Object newValue = notification.getNewValue();
if(isEditedEntity(newValue)) {
if(isEditedEntity(newValue) && !(notifier instanceof AllocationEntry)) {
IModelElement entity = (IModelElement)newValue;
display.syncExec(() -> addEntity(entity));
}
} else if(eventType == REMOVE) {
Object oldValue = notification.getOldValue();
if(isEditedEntity(oldValue)) {
if(isEditedEntity(oldValue) && !(notifier instanceof AllocationEntry)) {
IModelElement entity = (IModelElement)oldValue;
display.syncExec(() -> removeEntity(entity));
}
} else if(eventType == SET) {
Object changedValue = notification.getNotifier();
if(notification.getFeature() == INAMED_ELEMENT__NAME) {
if(changedValue instanceof IModelElement) {
display.syncExec(() -> renameEntity((IModelElement)changedValue));
if(notifier instanceof IModelElement) {
display.syncExec(() -> renameEntity((IModelElement)notifier));
}
} else if(observedFeatures.contains(notification.getFeature())) {
if(changedValue instanceof EObject) {
display.syncExec(() -> valueSet((EObject)changedValue));
if(notifier instanceof EObject) {
display.syncExec(() -> valueSet((EObject)notifier));
}
}
}
......
......@@ -2,9 +2,9 @@ ParameterEntryTreeViewerEditingSupport.java 759d5a5462aef5ec2ccd5e0b61b5858d3196
ParameterEntryTreeViewerLabelProvider.java b7d60c50c0c98bbd862cb092981408e8ca93af32 GREEN
ParameterSourceElementTreeViewerEditingSupport.java e1aa243eb4dd924ba79bc73ebf82bdd16b6a1543 GREEN
ParameterSourceElementTreeViewerLabelProvider.java cc7c7b11e0d74a3f5c5a68fa1b82885962e797b4 GREEN
ParameterTableTreeViewerManager.java d1205500f59668b6b2083295bdade48844bd60f8 GREEN
ParameterTableTreeViewerManager.java 00b54e90f06f218020239f144e7b950dc75bc1d4 YELLOW
TreeViewerCheckBoxEditingSupport.java ae801bf9e495b983774224444549cfc8c26fee58 GREEN
TreeViewerCheckBoxLabelProvider.java 4ef8bf1d20be01aaf9e62db25a25e48f3954bebf GREEN
TreeViewerContentProvider.java 6b97e361c3c8a633eb29963d44f7d4b3a9b9089a GREEN
TreeViewerFirstColumnLabelProvider.java 667f74dd10c1eebfb62976f846ff555b9da77988 GREEN
TreeViewerManager.java 026479aec225d681390a1366b904e1c7a9b1aae7 GREEN
TreeViewerManager.java 27441577968f9650d75625acf9b5691846f90640 YELLOW
......@@ -18,6 +18,7 @@ package org.fortiss.af3.allocation.ui.editor.treeviewer;
import static org.fortiss.af3.allocation.utils.AllocationUtils.getParameterEntryName;
import java.util.Collection;
import java.util.List;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.EditingSupport;
......@@ -40,6 +41,9 @@ import org.fortiss.tooling.base.model.element.IModelElement;
*/
public class ParameterTableTreeViewerManager<T extends ParameterTable> extends TreeViewerManager<T> {
/** Default width of source entity annotation columns. */
private final static int SOURCE_ENTITY_ANNOTATION_COLUMN_DEFAULT_WIDTH = 100;
/** Annotation type used to implement this {@link ParameterEntry}. */
private Class<? extends IAnnotatedSpecification> annotationType;
......@@ -102,12 +106,20 @@ public class ParameterTableTreeViewerManager<T extends ParameterTable> extends T
/** {@inheritDoc} */
@Override
protected TreeViewerFirstColumnLabelProvider<T> createFirstColumns() {
protected TreeViewerFirstColumnLabelProvider<T> createFirstColumns(List<Integer> columnWidths) {
TreeViewerFirstColumnLabelProvider<T> firstColumnLabelProvider = super.createFirstColumns();
TreeViewerFirstColumnLabelProvider<T> firstColumnLabelProvider =
super.createFirstColumns(columnWidths);
// TreeViewerManager.createFirstColumns() creates one column
int i = 1;
for(Class<? extends IAnnotatedSpecification> at : sourceEntityAnnotationTypes) {
createSourceEntityAnnotationColumn(at);
int width = SOURCE_ENTITY_ANNOTATION_COLUMN_DEFAULT_WIDTH;
if(i < columnWidths.size()) {
width = columnWidths.get(i);
}
createSourceEntityAnnotationColumn(at, width, i);
i++;
}
return firstColumnLabelProvider;
......@@ -118,7 +130,7 @@ public class ParameterTableTreeViewerManager<T extends ParameterTable> extends T
* directly in the {@link ParameterTableEditor}.
*/
private void createSourceEntityAnnotationColumn(
Class<? extends IAnnotatedSpecification> annotationType) {
Class<? extends IAnnotatedSpecification> annotationType, int width, int number) {
String columnLabel;
if(allocationTableEditor.getEditedObject() != null &&
!allocationTableEditor.getSourceViewModelElements().isEmpty()) {
......@@ -139,7 +151,8 @@ public class ParameterTableTreeViewerManager<T extends ParameterTable> extends T
new ParameterSourceElementTreeViewerEditingSupport(annotationType,
allocationTableEditor.getSourceEntityType(), treeViewer);
createTreeViewerColumn(100, columnLabel, null, null, labelProvider, editingSupport);
createTreeViewerColumn(width, columnLabel, null, null, labelProvider, editingSupport,
number);
}
/** Returns the annotation type of the given {@link AnnotationEntry}. */
......
......@@ -15,15 +15,18 @@
+--------------------------------------------------------------------------*/
package org.fortiss.af3.allocation.ui.editor.treeviewer;
import static java.util.Arrays.sort;
import static java.util.Collections.emptyList;
import static java.util.Collections.sort;
import static org.fortiss.af3.allocation.ui.DefaultStyle.DOWN_ARROW;
import static org.fortiss.af3.allocation.ui.DefaultStyle.RIGHT_ARROW;
import static org.fortiss.tooling.kernel.ui.util.KernelUIUtils.getName;
import static org.fortiss.tooling.kernel.utils.KernelModelElementUtils.computeFullyQualifiedName;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.EditingSupport;
......@@ -71,22 +74,37 @@ public class TreeViewerManager<T extends AllocationTable> {
/** The {@link TreeViewer} that is managed by this {@link TreeViewerManager}. */
protected TreeViewer treeViewer;
/** Flag if {@link #updateTreeViewer()} or {@link #refreshTreeViewer()} are enabled. */
private boolean updateRefreshEnabled;
/** Constructor. */
public TreeViewerManager(AllocationTableEditor<T> allocationTableEditor, TreeViewer treeViewer) {
this.allocationTableEditor = allocationTableEditor;
this.treeViewer = treeViewer;
this.updateRefreshEnabled = true;
treeViewer.setContentProvider(new TreeViewerContentProvider<T>(allocationTableEditor));
}
/**
* Updates (or creates) the {@link TreeViewerManager} with the edited source/target entities and
* Fully updates (or creates) the {@link TreeViewer} with the edited source/target entities and
* adds the first (source) column to it.
*/
public void updateTreeViewer() {
if(!updateRefreshEnabled) {
return;
}
// Dispose old tree's columns. NOOP if they do not exist.
Tree tree = treeViewer.getTree();
tree.setRedraw(false);
List<Integer> firstColumnsWidth = new ArrayList<Integer>();
Map<IModelElement, Integer> entryColumnsWidthMap = new HashMap<IModelElement, Integer>();
for(TreeColumn column : tree.getColumns()) {
if(column.getData() == null) {
firstColumnsWidth.add(column.getWidth());
} else if(column.getData() instanceof IModelElement) {
entryColumnsWidthMap.put((IModelElement)column.getData(), column.getWidth());
}
column.dispose();
}
......@@ -94,24 +112,50 @@ public class TreeViewerManager<T extends AllocationTable> {
T allocationTable = allocationTableEditor.getEditedObject();
treeViewer.setInput(allocationTable != null ? allocationTable.getSourceView() : null);
TreeViewerFirstColumnLabelProvider<T> firstColumnLabelProvider = createFirstColumns();
TreeViewerFirstColumnLabelProvider<T> firstColumnLabelProvider =
createFirstColumns(firstColumnsWidth);
createEntryColumns(firstColumnLabelProvider);
createEntryColumns(firstColumnLabelProvider, entryColumnsWidthMap);
// Update
treeViewer.refresh();
refreshTreeViewer();
tree.setRedraw(true);
}
/** Refreshes the {@link TreeViewer} . */
public void refreshTreeViewer() {
if(!updateRefreshEnabled) {
return;
}
treeViewer.refresh();
// Update column header and order
for(TreeColumn treeColumn : treeViewer.getTree().getColumns()) {
if(treeColumn.getData() instanceof INamedElement) {
INamedElement modelElement = (INamedElement)treeColumn.getData();
treeColumn.setText(modelElement.getName());
}
}
sortColumns();
}
/** Setter for {@link #updateRefreshEnabled}. */
public void setUpdateRefreshEnabled(boolean updateRefreshEnabled) {
this.updateRefreshEnabled = updateRefreshEnabled;
}
/** Creates and initializes a {@link TreeViewerColumn}. */
protected TreeViewerColumn createTreeViewerColumn(int width, String text, String toolTipText,
Image image, ColumnLabelProvider labelProvider, EditingSupport editingSupport) {
Image image, ColumnLabelProvider labelProvider, EditingSupport editingSupport,
Object columnData) {
TreeViewerColumn treeViewerColumn = new TreeViewerColumn(treeViewer, SWT.NONE);
TreeColumn treeColumn = treeViewerColumn.getColumn();
treeColumn.setWidth(width);
treeColumn.setText(text);
treeColumn.setToolTipText(toolTipText);
treeColumn.setImage(image);
treeColumn.setData(columnData);
if(labelProvider != null) {
treeViewerColumn.setLabelProvider(labelProvider);
......@@ -127,12 +171,16 @@ public class TreeViewerManager<T extends AllocationTable> {
* Creates the first allocation-independent columns of the {@link TreeViewer} (e.g., list of
* source elements.).
*/
protected TreeViewerFirstColumnLabelProvider<T> createFirstColumns() {
protected TreeViewerFirstColumnLabelProvider<T> createFirstColumns(List<Integer> columnWidths) {
final String columnLabel = DOWN_ARROW + " Src. | Tgt. " + RIGHT_ARROW;
int width = FIRST_COLUMN_DEFAULT_WIDTH;
if(!columnWidths.isEmpty()) {
// List is checked to be non-empty
width = columnWidths.get(0);
}
TreeViewerColumn firstColumn =
createTreeViewerColumn(FIRST_COLUMN_DEFAULT_WIDTH, columnLabel, null, null, null,
null);
createTreeViewerColumn(width, columnLabel, null, null, null, null, 0);
final TreeViewerFirstColumnLabelProvider<T> labelProvider =
new TreeViewerFirstColumnLabelProvider<T>(allocationTableEditor, treeViewer,
......@@ -151,38 +199,24 @@ public class TreeViewerManager<T extends AllocationTable> {
}
/** Create columns for the {@link AllocationTable}'s entries. */
private void createEntryColumns(TreeViewerFirstColumnLabelProvider<T> firstColumnLabelProvider) {
private void createEntryColumns(TreeViewerFirstColumnLabelProvider<T> firstColumnLabelProvider,
Map<IModelElement, Integer> columnWidthMap) {
AllocationTable allocationTable = allocationTableEditor.getEditedObject();
// Create entries' columns
List<? extends IModelElement> targetModelElements;
if(allocationTable != null) {
targetModelElements = allocationTableEditor.getTargetViewModelElements();
if(allocationTableEditor.isSortColumns()) {
sort(targetModelElements, new Comparator<IModelElement>() {
/** {@inheritDoc} */
@Override
public int compare(IModelElement e1, IModelElement e2) {
String n1;
String n2;
if(e1 instanceof INamedElement && e2 instanceof INamedElement) {
n1 = computeFullyQualifiedName((INamedElement)e1);
n2 = computeFullyQualifiedName((INamedElement)e2);
} else {
n1 = getName(e1);
n2 = getName(e2);
}
return n1.compareTo(n2);
}
});
}
} else {
targetModelElements = emptyList();
}
for(IModelElement targetElement : targetModelElements) {
int columnWidth =
columnWidthMap.get(targetElement) != null ? columnWidthMap.get(targetElement)
: ENTRY_COLUMN_DEFAULT_WIDTH;
final boolean showContainerName = targetElement instanceof IConnector;
final String label =
getColumnLabel(ENTRY_COLUMN_DEFAULT_WIDTH, targetElement, showContainerName,
getColumnLabel(columnWidth, targetElement, showContainerName,
firstColumnLabelProvider);
final String toolTipText = firstColumnLabelProvider.getToolTipText(targetElement);
final Image image = KernelUIUtils.getImage(targetElement);
......@@ -195,8 +229,8 @@ public class TreeViewerManager<T extends AllocationTable> {
allocationTableEditor.getAllocationEntryType(), targetElement);
TreeViewerColumn treeViewerColumn =
createTreeViewerColumn(ENTRY_COLUMN_DEFAULT_WIDTH, label, toolTipText, image,
labelProvider, editingSupport);
createTreeViewerColumn(columnWidth, label, toolTipText, image, labelProvider,
editingSupport, targetElement);
TreeColumn treeColumn = treeViewerColumn.getColumn();
treeColumn.addControlListener(new ControlAdapter() {
......@@ -209,6 +243,67 @@ public class TreeViewerManager<T extends AllocationTable> {
}
});
}
sortColumns();
}
/** Sorts the columns. */
private void sortColumns() {
if(allocationTableEditor.isSortColumns()) {
Tree tree = treeViewer.getTree();
TreeColumn[] sortedColumns = tree.getColumns();
sort(sortedColumns, new Comparator<TreeColumn>() {
/** {@inheritDoc} */
@Override
public int compare(TreeColumn t1, TreeColumn t2) {
Object d1 = t1.getData();
Object d2 = t2.getData();
if(d1 == null) {
return -1;
}
if(d2 == null) {
return 1;
}
// The order of the first columns are specified as ints
if(d1 instanceof Integer) {
if(d2 instanceof Integer) {
return ((Integer)d1).compareTo((Integer)d2);
}
return -1;
} else if(d2 instanceof Integer) {
return 1;
}
// Entry columns are sorted lexicographically
String n1;
String n2;
if(d1 instanceof INamedElement && d2 instanceof INamedElement) {
n1 = computeFullyQualifiedName((INamedElement)d1);
n2 = computeFullyQualifiedName((INamedElement)d2);
} else {
n1 = getName(d1);
n2 = getName(d2);
}
return n1.compareTo(n2);
}
});
int columnOrder[] = new int[sortedColumns.length];
int i = 0;
TreeColumn[] treeColumns = tree.getColumns();
for(TreeColumn column : treeColumns) {
int index = 0;
for(TreeColumn sc : sortedColumns) {
if(column == sc) {
columnOrder[index] = i;
break;
}
index++;
}
i++;
}
tree.setColumnOrder(columnOrder);
}
}
/** Determines the label of the given {@code treeViewerColumn}. */
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment