Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCamille Letavernier2019-02-20 08:11:10 -0500
committerCamille Letavernier2019-03-29 04:50:41 -0400
commit2a192f7b9a6bbdacf1248be204c58f5d10c94dcf (patch)
tree6863d10ebaaab7d59c1964e87e1056f7659cb43a
parent2ab83af9a2eb3ae61dae691acf751954635da2d5 (diff)
downloadorg.eclipse.papyrus-2a192f7b9a6bbdacf1248be204c58f5d10c94dcf.tar.gz
org.eclipse.papyrus-2a192f7b9a6bbdacf1248be204c58f5d10c94dcf.tar.xz
org.eclipse.papyrus-2a192f7b9a6bbdacf1248be204c58f5d10c94dcf.zip
Bug 544476: [Properties - Profile] Support Optional Enums & Primitivesbugs/544476-optionalProperties
https://bugs.eclipse.org/bugs/show_bug.cgi?id=544476 - Do not fire a value change in text fields when the content of the text field didn't change (e.g. on FocusIn + FocusOut), to avoid replacing a null value with an empty value - Add support for unsetting or clearing a text field Change-Id: I927060ad55a73ac674d35c09090c1f12a5870a52 Signed-off-by: Camille Letavernier <cletavernier@eclipsesource.com>
-rw-r--r--plugins/infra/properties/org.eclipse.papyrus.infra.properties.ui/src/org/eclipse/papyrus/infra/properties/ui/widgets/StringEditor.java15
-rw-r--r--plugins/infra/ui/org.eclipse.papyrus.infra.widgets/src/org/eclipse/papyrus/infra/widgets/databinding/TextObservableValue.java55
-rw-r--r--plugins/infra/ui/org.eclipse.papyrus.infra.widgets/src/org/eclipse/papyrus/infra/widgets/editors/StringEditor.java222
3 files changed, 276 insertions, 16 deletions
diff --git a/plugins/infra/properties/org.eclipse.papyrus.infra.properties.ui/src/org/eclipse/papyrus/infra/properties/ui/widgets/StringEditor.java b/plugins/infra/properties/org.eclipse.papyrus.infra.properties.ui/src/org/eclipse/papyrus/infra/properties/ui/widgets/StringEditor.java
index a7425aef8e0..5810bdb4b0d 100644
--- a/plugins/infra/properties/org.eclipse.papyrus.infra.properties.ui/src/org/eclipse/papyrus/infra/properties/ui/widgets/StringEditor.java
+++ b/plugins/infra/properties/org.eclipse.papyrus.infra.properties.ui/src/org/eclipse/papyrus/infra/properties/ui/widgets/StringEditor.java
@@ -13,6 +13,7 @@
*****************************************************************************/
package org.eclipse.papyrus.infra.properties.ui.widgets;
+import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
/**
@@ -33,6 +34,18 @@ public class StringEditor extends AbstractPropertyEditor {
* The style for the widget
*/
public StringEditor(Composite parent, int style) {
- super(new org.eclipse.papyrus.infra.widgets.editors.StringEditor(parent, style));
+ super(new org.eclipse.papyrus.infra.widgets.editors.StringEditor(parent, style | SWT.SEARCH | SWT.ICON_CANCEL));
+ }
+
+ /**
+ * @see org.eclipse.papyrus.infra.properties.ui.widgets.AbstractPropertyEditor#doBinding()
+ *
+ */
+ @Override
+ protected void doBinding() {
+ super.doBinding();
+ if (input != null && propertyPath != null && !input.isMandatory(propertyPath)) {
+ ((org.eclipse.papyrus.infra.widgets.editors.StringEditor) getEditor()).setOptional(true);
+ }
}
}
diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.widgets/src/org/eclipse/papyrus/infra/widgets/databinding/TextObservableValue.java b/plugins/infra/ui/org.eclipse.papyrus.infra.widgets/src/org/eclipse/papyrus/infra/widgets/databinding/TextObservableValue.java
index 0ec2e903c7e..9357969ba93 100644
--- a/plugins/infra/ui/org.eclipse.papyrus.infra.widgets/src/org/eclipse/papyrus/infra/widgets/databinding/TextObservableValue.java
+++ b/plugins/infra/ui/org.eclipse.papyrus.infra.widgets/src/org/eclipse/papyrus/infra/widgets/databinding/TextObservableValue.java
@@ -11,7 +11,7 @@
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
* Calin Glitia (Esterel Technologies SAS) - Bug 497496
- *
+ *
*****************************************************************************/
package org.eclipse.papyrus.infra.widgets.databinding;
@@ -20,6 +20,7 @@ import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.databinding.observable.value.ValueDiff;
import org.eclipse.papyrus.infra.tools.databinding.AggregatedObservable;
import org.eclipse.papyrus.infra.widgets.providers.UnchangedObject;
+import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Text;
@@ -32,6 +33,11 @@ import org.eclipse.swt.widgets.Text;
*/
public class TextObservableValue extends AbstractObservableValue implements Listener {
+ // Flag to avoid firing a change event when focus comes in and out,
+ // without any user change occurring in the text field
+ private boolean wasChanged = false;
+ private boolean isReset = false;
+
private Text text;
private int eventType;
@@ -64,6 +70,16 @@ public class TextObservableValue extends AbstractObservableValue implements List
this.modelProperty = (AggregatedObservable) modelProperty;
}
this.text.addListener(eventType, this);
+ this.text.addModifyListener(event -> {
+ this.wasChanged = true;
+ this.isReset = false;
+ });
+
+ this.text.addListener(SWT.DefaultSelection, event -> {
+ if (event.detail == SWT.ICON_CANCEL) {
+ clear();
+ }
+ });
}
@Override
@@ -77,10 +93,19 @@ public class TextObservableValue extends AbstractObservableValue implements List
return null;
}
- if (UnchangedObject.instance.toString().equals(text.getText())) {
- return null;
+ if (wasChanged) {
+ // XXX We don't support special values, so we have to rely on null in two
+ // distinct cases. In case of single-selection, 'null' means 'null' (Unset or set(null))
+ // In case of multi-selection, 'null' means 'unchanged'
+ if (isReset) {
+ return null;
+ } else if (UnchangedObject.instance.toString().equals(text.getText())) {
+ return null;
+ } else {
+ return text.getText();
+ }
} else {
- return text.getText();
+ return currentValue;
}
}
@@ -91,14 +116,14 @@ public class TextObservableValue extends AbstractObservableValue implements List
}
if (modelProperty != null && modelProperty.hasDifferentValues()) {
this.text.setText(UnchangedObject.instance.toString());
- this.currentValue = UnchangedObject.instance;
+ storeValue(UnchangedObject.instance);
} else {
if (value instanceof String) {
this.text.setText((String) value);
- this.currentValue = value;
+ storeValue(value);
} else if (value == null) {
this.text.setText(""); //$NON-NLS-1$
- this.currentValue = null;
+ storeValue(null);
}
}
}
@@ -111,10 +136,10 @@ public class TextObservableValue extends AbstractObservableValue implements List
final Object oldValue = currentValue;
final Object newValue = getValue();
- if (newValue == null) {
+ if (newValue == null && !isReset) {
return;
}
- currentValue = newValue;
+ storeValue(newValue);
if ((eventType & event.type) != 0) {
fireValueChange(new ValueDiff() {
@@ -133,4 +158,16 @@ public class TextObservableValue extends AbstractObservableValue implements List
}
}
+ public void clear() {
+ this.text.setText("");
+ this.wasChanged = true;
+ this.isReset = true;
+ }
+
+ private void storeValue(Object value) {
+ this.currentValue = value;
+ this.wasChanged = false;
+ this.isReset = false;
+ }
+
}
diff --git a/plugins/infra/ui/org.eclipse.papyrus.infra.widgets/src/org/eclipse/papyrus/infra/widgets/editors/StringEditor.java b/plugins/infra/ui/org.eclipse.papyrus.infra.widgets/src/org/eclipse/papyrus/infra/widgets/editors/StringEditor.java
index 6d513afb03c..84a227797ce 100644
--- a/plugins/infra/ui/org.eclipse.papyrus.infra.widgets/src/org/eclipse/papyrus/infra/widgets/editors/StringEditor.java
+++ b/plugins/infra/ui/org.eclipse.papyrus.infra.widgets/src/org/eclipse/papyrus/infra/widgets/editors/StringEditor.java
@@ -22,17 +22,38 @@ import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.fieldassist.ControlDecoration;
import org.eclipse.jface.fieldassist.FieldDecoration;
import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.papyrus.infra.widgets.databinding.TextObservableValue;
import org.eclipse.papyrus.infra.widgets.selectors.StringSelector;
import org.eclipse.swt.SWT;
+import org.eclipse.swt.accessibility.ACC;
+import org.eclipse.swt.accessibility.AccessibleAdapter;
+import org.eclipse.swt.accessibility.AccessibleControlAdapter;
+import org.eclipse.swt.accessibility.AccessibleControlEvent;
+import org.eclipse.swt.accessibility.AccessibleEvent;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.MouseTrackListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.internal.WorkbenchMessages;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
/**
* A Property Editor representing a single-line or multi-line String value as a
@@ -46,11 +67,33 @@ import org.eclipse.swt.widgets.Text;
*/
public class StringEditor extends AbstractValueEditor implements KeyListener, ModifyListener {
+
+ private static final String CLEAR_ICON = "org.eclipse.ui.internal.dialogs.CLEAR_ICON"; //$NON-NLS-1$
+ private static final String DISABLED_CLEAR_ICON = "org.eclipse.ui.internal.dialogs.DCLEAR_ICON"; //$NON-NLS-1$
+
+ static {
+ ImageDescriptor descriptor = AbstractUIPlugin
+ .imageDescriptorFromPlugin(PlatformUI.PLUGIN_ID,
+ "$nl$/icons/full/etool16/clear_co.png"); //$NON-NLS-1$
+ if (descriptor != null) {
+ JFaceResources.getImageRegistry().put(CLEAR_ICON, descriptor);
+ }
+ descriptor = AbstractUIPlugin.imageDescriptorFromPlugin(
+ PlatformUI.PLUGIN_ID, "$nl$/icons/full/dtool16/clear_co.png"); //$NON-NLS-1$
+ if (descriptor != null) {
+ JFaceResources.getImageRegistry().put(DISABLED_CLEAR_ICON, descriptor);
+ }
+ }
+
/**
* The text box for editing this editor's value
*/
protected final Text text;
+ private boolean wasChanged = false;
+
+ private String initialText;
+
private int delay = 600;
private boolean validateOnDelay = false;
@@ -61,6 +104,10 @@ public class StringEditor extends AbstractValueEditor implements KeyListener, Mo
private TimerTask changeColorTask;
+ private boolean isOptional;
+
+ private Label clearButton;
+
private final static int DEFAULT_HEIGHT_HINT = 55;
private final static int DEFAULT_WIDTH_HINT = 100;
@@ -141,13 +188,54 @@ public class StringEditor extends AbstractValueEditor implements KeyListener, Mo
style = style | SWT.V_SCROLL;
}
- text = factory.createText(this, null, style);
- text.setLayoutData(data);
+ int clearStyle = SWT.SEARCH | SWT.ICON_CANCEL;
+
+ if ((style & clearStyle) == clearStyle) {
+ Text testText = factory.createText(this, null, style);
+ // Test if SEARCH is natively supported
+ if ((testText.getStyle() & clearStyle) != clearStyle) {
+ // Not natively supported (e.g. Windows); create a custom clear icon
+ testText.dispose();
+
+ final int borderStyle = factory.getBorderStyle();
+ final Composite textWrapper = factory.createComposite(this, borderStyle);
+ textWrapper.setLayoutData(data);
+
+ GridLayoutFactory.fillDefaults().numColumns(2).margins(0, 0).applyTo(textWrapper);
+ try {
+ // Remove border from the Text Control and add it to the wrapping composite
+ factory.setBorderStyle(SWT.NONE);
+ text = factory.createText(textWrapper, null, SWT.NONE);
+ } finally {
+ factory.setBorderStyle(borderStyle);
+ }
+ text.setLayoutData(GridDataFactory.copyData(data));
+
+ addClearIcon(text, textWrapper);
+ } else {
+ // Natively supported; just use it.
+ text = testText;
+ text.setLayoutData(data);
+ // Implement custom clear behavior
+ text.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ if (e.detail == SWT.CANCEL) {
+ clearText();
+ e.doit = false;
+ }
+ }
+ });
+ }
+ } else {
+ text = factory.createText(this, null, style);
+ text.setLayoutData(data);
+ }
if (label != null) {
super.label.setLayoutData(getLabelLayoutData());
-
}
+
text.addKeyListener(this);
text.addModifyListener(this);
setCommitOnFocusLost(text);
@@ -158,6 +246,108 @@ public class StringEditor extends AbstractValueEditor implements KeyListener, Mo
}
+ // From org.eclipse.ui.dialogs.FilteredTree
+ private void addClearIcon(Text text, Composite parent) {
+ // only create the button if the text widget doesn't support one
+ // natively
+ if ((text.getStyle() & SWT.ICON_CANCEL) == 0) {
+ final Image inactiveImage = JFaceResources.getImageRegistry().getDescriptor(DISABLED_CLEAR_ICON).createImage();
+ final Image activeImage = JFaceResources.getImageRegistry().getDescriptor(CLEAR_ICON).createImage();
+ final Image pressedImage = new Image(getDisplay(), activeImage, SWT.IMAGE_GRAY);
+
+ final Label clearButton = new Label(parent, SWT.NONE);
+ clearButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, false));
+ clearButton.setImage(inactiveImage);
+ clearButton.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND));
+ clearButton.setToolTipText("Clear");
+ clearButton.addMouseListener(new MouseAdapter() {
+ private MouseMoveListener fMoveListener;
+
+ @Override
+ public void mouseDown(MouseEvent e) {
+ clearButton.setImage(pressedImage);
+ fMoveListener = new MouseMoveListener() {
+ private boolean fMouseInButton = true;
+
+ @Override
+ public void mouseMove(MouseEvent e) {
+ boolean mouseInButton = isMouseInButton(e);
+ if (mouseInButton != fMouseInButton) {
+ fMouseInButton = mouseInButton;
+ clearButton.setImage(mouseInButton ? pressedImage : inactiveImage);
+ }
+ }
+ };
+ clearButton.addMouseMoveListener(fMoveListener);
+ }
+
+ @Override
+ public void mouseUp(MouseEvent e) {
+ if (fMoveListener != null) {
+ clearButton.removeMouseMoveListener(fMoveListener);
+ fMoveListener = null;
+ boolean mouseInButton = isMouseInButton(e);
+ clearButton.setImage(mouseInButton ? activeImage : inactiveImage);
+ if (mouseInButton) {
+ clearText();
+ text.setFocus();
+ }
+ }
+ }
+
+ private boolean isMouseInButton(MouseEvent e) {
+ Point buttonSize = clearButton.getSize();
+ return 0 <= e.x && e.x < buttonSize.x && 0 <= e.y && e.y < buttonSize.y;
+ }
+ });
+ clearButton.addMouseTrackListener(new MouseTrackListener() {
+ @Override
+ public void mouseEnter(MouseEvent e) {
+ clearButton.setImage(activeImage);
+ }
+
+ @Override
+ public void mouseExit(MouseEvent e) {
+ clearButton.setImage(inactiveImage);
+ }
+
+ @Override
+ public void mouseHover(MouseEvent e) {
+ }
+ });
+ clearButton.addDisposeListener(e -> {
+ inactiveImage.dispose();
+ activeImage.dispose();
+ pressedImage.dispose();
+ });
+ clearButton.getAccessible().addAccessibleListener(
+ new AccessibleAdapter() {
+ @Override
+ public void getName(AccessibleEvent e) {
+ e.result = WorkbenchMessages.FilteredTree_AccessibleListenerClearButton;
+ }
+ });
+ clearButton.getAccessible().addAccessibleControlListener(
+ new AccessibleControlAdapter() {
+ @Override
+ public void getRole(AccessibleControlEvent e) {
+ e.detail = ACC.ROLE_PUSHBUTTON;
+ }
+ });
+
+ this.clearButton = clearButton;
+ }
+ }
+
+ protected void clearText() {
+ if (isOptional && widgetObservable instanceof TextObservableValue) {
+ ((TextObservableValue) widgetObservable).clear();
+ } else {
+ text.setText("");
+ }
+ notifyChange();
+ }
+
@Override
protected GridData getLabelLayoutData() {
GridData result = super.getLabelLayoutData();
@@ -235,12 +425,19 @@ public class StringEditor extends AbstractValueEditor implements KeyListener, Mo
*/
@Override
public Object getValue() {
- return text.getText();
+ // If the user never typed anything, return the raw (potentially null) original value
+ // This is to avoid changing from "null" to "" (empty string) when the user simply
+ // focuses the control in and out.
+ return wasChanged ? text.getText() : initialText;
}
@Override
public void setReadOnly(boolean readOnly) {
text.setEnabled(!readOnly);
+ if (clearButton != null) {
+ clearButton.setVisible(!readOnly);
+ setExclusion(clearButton, readOnly);
+ }
}
@Override
@@ -269,9 +466,12 @@ public class StringEditor extends AbstractValueEditor implements KeyListener, Mo
public void setValue(Object value) {
if (value instanceof String) {
this.text.setText((String) value);
+ initialText = (String) value;
} else {
- this.text.setText(""); //$NON-NLS-1$;
+ this.text.setText(""); //$NON-NLS-1$ ;
+ initialText = null;
}
+ wasChanged = false;
}
/**
@@ -353,6 +553,7 @@ public class StringEditor extends AbstractValueEditor implements KeyListener, Mo
};
timer.schedule(currentValidateTask, delay);
}
+ wasChanged = true;
if (targetValidator != null) {
IStatus status = targetValidator.validate(text.getText());
updateStatus(status);
@@ -454,7 +655,7 @@ public class StringEditor extends AbstractValueEditor implements KeyListener, Mo
text.setBackground(ERROR);
text.update();
} else {
- IStatus status = (IStatus) binding.getValidationStatus().getValue();
+ IStatus status = binding.getValidationStatus().getValue();
switch (status.getSeverity()) {
case IStatus.OK:
case IStatus.WARNING:
@@ -479,4 +680,13 @@ public class StringEditor extends AbstractValueEditor implements KeyListener, Mo
}
}
+ /**
+ * Indicate that this editor handles an optional value. If optional is true,
+ * clearing this editor (via the Clear button) will set a <code>null</code> value;
+ * otherwise it will set an empty string value ("")
+ */
+ public void setOptional(boolean optional) {
+ this.isOptional = optional;
+ }
+
}

Back to the top