From 755c43ceb21005744ef154ac53cba414b5e23a1c Mon Sep 17 00:00:00 2001 From: Simon Barner <barner@fortiss.org> Date: Thu, 15 Nov 2018 14:13:17 +0100 Subject: [PATCH] Improve EReferenceListPropertySectionBase and make list view reusable * EReferenceListPropertySectionBase * Introduce ElementListViewerComposite: Reusable Composite that provides a ListViewer & add/remove buttons * Usability improvements: enabled state of buttons, auto-select next element after "remove" operation * Introduce IListPropertySection * To be implemented by classes that want to embed ElementListViewerComposite * Provides interface to data model Issue-Ref: 3470 Issue-Url: https://af3-developer.fortiss.org/issues/3470 Signed-off-by: Simon Barner <barner@fortiss.org> --- .../tooling/kernel/ui/extension/base/.ratings | 3 +- .../EReferenceListPropertySectionBase.java | 221 +++++++++++++----- .../extension/base/IListPropertySection.java | 47 ++++ 3 files changed, 210 insertions(+), 61 deletions(-) create mode 100644 org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/IListPropertySection.java diff --git a/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/.ratings b/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/.ratings index 290af8e76..520e5f612 100644 --- a/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/.ratings +++ b/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/.ratings @@ -2,9 +2,10 @@ ConstraintUIBases.java 3676a600e0866091db9798763c6eee97eec5b55b GREEN ContextMenuSubMenuContributorBase.java 6275d96fe8690d9d4744bcbaef3c7d14ba8e30ff GREEN DialogMessageHandler.java 8714da09a777c8557de0a5c48ff68c340f9fa91d GREEN EObjectActionBase.java 4ef9f8be59e64d4838acc9e268d418ba5d94fa1a GREEN -EReferenceListPropertySectionBase.java b43469e81cf9f959230e9b2353460c49d8d6a7d3 YELLOW +EReferenceListPropertySectionBase.java bbc5f6851842a9cb0af5e9898d9c6e197a36312a YELLOW EReferencePropertySectionBase.java 3347e99b2fc135dd4b3117407179900ef757092c GREEN EditorBase.java 9c09fff92945256bb8680992ae7bb2c78f47b150 GREEN +IListPropertySection.java a093a8a625d291b8adecec5082e32748a2d99f9e YELLOW ModelEditorBindingBase.java 4c5ac569c0b6e7678fc8191096b26dfd09fdcb98 GREEN ModelElementHandlerBase.java 384727748f125c9d43f19d9c0eba4ba1be5a7a26 GREEN MultiEObjectActionBase.java 9e237d8ea640c4194e4877af4a9cfce88698e543 GREEN diff --git a/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/EReferenceListPropertySectionBase.java b/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/EReferenceListPropertySectionBase.java index b43469e81..bbc5f6851 100644 --- a/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/EReferenceListPropertySectionBase.java +++ b/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/EReferenceListPropertySectionBase.java @@ -23,7 +23,11 @@ import java.util.List; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.jface.viewers.ArrayContentProvider; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.ListViewer; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; @@ -34,7 +38,6 @@ import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage; import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetWidgetFactory; -import org.fortiss.tooling.kernel.ui.extension.base.EReferencePropertySectionBase; import org.fortiss.tooling.kernel.ui.presentation.ModelElementLabelProvider; /** @@ -43,7 +46,7 @@ import org.fortiss.tooling.kernel.ui.presentation.ModelElementLabelProvider; * @author barner */ public abstract class EReferenceListPropertySectionBase<I extends EObject, R extends EObject> - extends EReferencePropertySectionBase<I, R> { + extends EReferencePropertySectionBase<I, R> implements IListPropertySection<I, R> { /** * Element that has been selected the combo box provided by the base class @@ -51,8 +54,144 @@ public abstract class EReferenceListPropertySectionBase<I extends EObject, R ext */ private R selectedElement; - /** {@link ListViewer} to show edited {@link EReference} list. */ - private ListViewer elementListViewer; + /** + * {@link Composite} providing a {@link ListViewer} and an add and a remove {@link Button} to + * edit {@link EReference} list. + */ + public static class ElementListViewerComposite<I extends EObject, R extends EObject> + extends Composite { + /** {@link ListViewer} to show edited {@link EReference} list. */ + private ListViewer listViewer; + + /** {@link Button} to add an element to the {@link #listViewer}. */ + private Button addButton; + + /** {@link Button} to remove an element from the {@link #listViewer}. */ + private Button removeButton; + + /** Underlying {@link IListPropertySection}. */ + private IListPropertySection<I, R> section; + + /** Constructor. */ + public ElementListViewerComposite(Composite parent, int style, + TabbedPropertySheetWidgetFactory wf, IListPropertySection<I, R> section) { + super(parent, style); + this.section = section; + + GridLayout layout = new GridLayout(2, false); + layout.marginWidth = 0; + setLayout(layout); + + listViewer = new ListViewer(this, SWT.BORDER | SWT.V_SCROLL); + GridData gd = new GridData(GridData.FILL_HORIZONTAL); + gd.grabExcessHorizontalSpace = true; + gd.heightHint = 48; + listViewer.getList().setLayoutData(gd); + listViewer.setContentProvider(new ArrayContentProvider()); + listViewer.setLabelProvider(new ModelElementLabelProvider()); + + listViewer.addSelectionChangedListener(new ISelectionChangedListener() { + /** {@inheritDoc} */ + @Override + public void selectionChanged(SelectionChangedEvent event) { + // refresh() does not work here. It is based on isRemoveButtonEnabled(), that + // queries the selection of the 'listViewer' has not been updated yet. When this + // listener is triggered, only the 'event' already contains the new selection. + removeButton.setEnabled(getFirstSelectedElement(event.getSelection()) != null); + } + }); + + Composite buttonComposite = wf.createComposite(this, SWT.NONE); + buttonComposite.setLayout(new FillLayout(SWT.VERTICAL)); + + addButton = wf.createButton(buttonComposite, "Add", SWT.NONE); + addButton.addSelectionListener(new SelectionAdapter() { + /** {@inheritDoc} */ + @Override + public void widgetSelected(SelectionEvent e) { + I input = section.getSectionInput(); + runAsCommand(input, + () -> section.addModelListElement(input, section.getSelectedElement())); + section.refresh(); + } + }); + + removeButton = wf.createButton(buttonComposite, "Remove", SWT.NONE); + removeButton.addSelectionListener(new SelectionAdapter() { + /** {@inheritDoc} */ + + @Override + public void widgetSelected(SelectionEvent e) { + R selectedElement = getFirstSelectedElement(listViewer.getSelection()); + if(selectedElement != null) { + I input = section.getSectionInput(); + runAsCommand(input, + () -> section.removeModelListElement(input, selectedElement)); + section.refresh(); + + List<R> elements = section.getModelListElements(input); + if(!elements.isEmpty()) { + // Select first element (which has been checked to exist) + listViewer.setSelection(new StructuredSelection(elements.get(0))); + } + } + } + }); + } + + /** Provides the first selected element of the give {@link ISelection}. */ + @SuppressWarnings("unchecked") + private R getFirstSelectedElement(ISelection selection) { + return (R)checkAndPickFirst(selection, EObject.class); + } + + /** + * <p> + * Predicate if the {@link #addButton} is currently enabled. + * </p> + * <p> + * <b>NB: </b>Derived classes may override but must return the conjunction of their result + * and this implementation. + * </p> + */ + protected boolean isAddButtonEnabled() { + R selectedElement = section.getSelectedElement(); + return selectedElement != null && !section + .getModelListElements(section.getSectionInput()).contains(selectedElement); + } + + /** + * <p> + * Predicate if the {@link #removeButton} is currently enabled. + * </p> + * <p> + * <b>NB: </b>Derived classes may override but must return the conjunction of their result + * and this implementation. + * </p> + */ + protected boolean isRemoveButtonEnabled() { + return getFirstSelectedElement(listViewer.getSelection()) != null; + } + + /** Refreshes the input and the enabled state of the controls. */ + public void refresh() { + addButton.setEnabled(false); + removeButton.setEnabled(false); + + I input = section.getSectionInput(); + if(input == null) { + return; + } + + List<R> elements = section.getModelListElements(input); + listViewer.setInput(elements); + addButton.setEnabled(isAddButtonEnabled()); + removeButton.setEnabled(isRemoveButtonEnabled()); + } + } + + /** {@link Composite} providing a {@link ListViewer} and an add and remove Buttons. */ + private ElementListViewerComposite<I, R> listViewerComposite; /** The edited object (updated in {@link #setSectionInput(Object)}). */ private I input = null; @@ -67,60 +206,30 @@ public abstract class EReferenceListPropertySectionBase<I extends EObject, R ext public void createControls(Composite parent, TabbedPropertySheetPage aTabbedPropertySheetPage) { super.createControls(parent, aTabbedPropertySheetPage); - TabbedPropertySheetWidgetFactory wf = getWidgetFactory(); - Composite listComposite = wf.createComposite(composite, SWT.NONE); - GridLayout layout = new GridLayout(2, false); - layout.marginWidth = 0; - listComposite.setLayout(layout); - - elementListViewer = new ListViewer(listComposite, SWT.BORDER | SWT.V_SCROLL); - GridData gd = new GridData(GridData.FILL_HORIZONTAL); - gd.grabExcessHorizontalSpace = true; - gd.heightHint = 48; - elementListViewer.getList().setLayoutData(gd); - elementListViewer.setContentProvider(new ArrayContentProvider()); - elementListViewer.setLabelProvider(new ModelElementLabelProvider()); - - Composite buttonComposite = wf.createComposite(listComposite, SWT.NONE); - buttonComposite.setLayout(new FillLayout(SWT.VERTICAL)); - Button addButton = wf.createButton(buttonComposite, "Add", SWT.NONE); - addButton.addSelectionListener(new SelectionAdapter() { - /** {@inheritDoc} */ - @Override - public void widgetSelected(SelectionEvent e) { - if(selectedElement != null && - !getModelListElements(input).contains(selectedElement)) { - runAsCommand(input, () -> addModelListElement(input, selectedElement)); - refresh(); - } - } - }); - - Button removeButton = wf.createButton(buttonComposite, "Remove", SWT.NONE); - removeButton.addSelectionListener(new SelectionAdapter() { - /** {@inheritDoc} */ - @SuppressWarnings("unchecked") - @Override - public void widgetSelected(SelectionEvent e) { - runAsCommand(input, () -> removeModelListElement(input, - (R)checkAndPickFirst(elementListViewer.getSelection(), EObject.class))); - refresh(); - } - }); + listViewerComposite = + new ElementListViewerComposite<>(composite, SWT.NONE, getWidgetFactory(), this); + createFormEntry(listViewerComposite, ""); + } + + // Implementation of IListPropertySection + /** {@inheritDoc} */ + @Override + public final R getSelectedElement() { + return selectedElement; + } - createFormEntry(listComposite, ""); + /** {@inheritDoc} */ + @Override + public final I getSectionInput() { + return input; } + // Implementation of PropertySectionBase /** {@inheritDoc} */ @Override public void refresh() { super.refresh(); - - if(input == null) { - return; - } - - elementListViewer.setInput(getModelListElements(input)); + listViewerComposite.refresh(); } /** {@inheritDoc} */ @@ -131,6 +240,7 @@ public abstract class EReferenceListPropertySectionBase<I extends EObject, R ext this.input = (I)input; } + // Implementation of EReferencePropertySectionBase /** {@inheritDoc} */ @Override protected final EObject getModelValue(I input) { @@ -149,13 +259,4 @@ public abstract class EReferenceListPropertySectionBase<I extends EObject, R ext // for possible "add" operation. selectedElement = newValue; } - - /** Returns the {@link List} of elements in the edited {@link EReference} list. */ - protected abstract List<R> getModelListElements(I input); - - /** Adds the given {@code element} to the edited {@link EReference} list. */ - protected abstract void addModelListElement(I input, R element); - - /** Removes the given {@code element} from the edited {@link EReference} list. */ - protected abstract void removeModelListElement(I input, R element); } diff --git a/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/IListPropertySection.java b/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/IListPropertySection.java new file mode 100644 index 000000000..a093a8a62 --- /dev/null +++ b/org.fortiss.tooling.kernel.ui/src/org/fortiss/tooling/kernel/ui/extension/base/IListPropertySection.java @@ -0,0 +1,47 @@ +/*-------------------------------------------------------------------------+ +| Copyright 2018 fortiss GmbH | +| | +| Licensed under the Apache License, Version 2.0 (the "License"); | +| you may not use this file except in compliance with the License. | +| You may obtain a copy of the License at | +| | +| http://www.apache.org/licenses/LICENSE-2.0 | +| | +| Unless required by applicable law or agreed to in writing, software | +| distributed under the License is distributed on an "AS IS" BASIS, | +| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | +| See the License for the specific language governing permissions and | +| limitations under the License. | ++--------------------------------------------------------------------------*/ +package org.fortiss.tooling.kernel.ui.extension.base; + +import java.util.List; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EReference; + +/** + * Interface for property sections in which a list of elements can be edited. + * + * @author barner + */ +public interface IListPropertySection<I extends EObject, R extends EObject> { + + /** Returns the {@link List} of elements in the edited {@link EReference} list. */ + public List<R> getModelListElements(I input); + + /** Adds the given {@code element} to the edited {@link EReference} list. */ + public void addModelListElement(I input, R element); + + /** Removes the given {@code element} from the edited {@link EReference} list. */ + public void removeModelListElement(I input, R element); + + /** Returns the currently selected element of the viewer used to edit the element list. */ + public R getSelectedElement(); + + /** Returns the input model that contains the edited list. */ + public I getSectionInput(); + + /** Updates the list viewer and the property section that embeds it. */ + public void refresh(); +} -- GitLab