Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJens Lidestrom2020-06-05 21:46:44 +0000
committerJens Lideström2020-06-19 11:18:21 +0000
commited4c96aea6a0d1e77b8a5b9e8793144ddda2ded0 (patch)
treeb91003eb5f3738eedb7bdab4e22a025a368a3e1c
parent90384b2caf1e05a0906142c13cf1aad5948a3957 (diff)
downloadeclipse.platform.ui-ed4c96aea6a0d1e77b8a5b9e8793144ddda2ded0.tar.gz
eclipse.platform.ui-ed4c96aea6a0d1e77b8a5b9e8793144ddda2ded0.tar.xz
eclipse.platform.ui-ed4c96aea6a0d1e77b8a5b9e8793144ddda2ded0.zip
Bug 564010 - Add property versions of WorkbenchObservables methods
And move implementation of the new property classes to an internal package. This is done to prepare for the deprecation of WorkbenchObservables. Change-Id: I271aee33ff8f99031dfc970c7702ffe6db244bae Signed-off-by: Jens Lidestrom <jens@lidestrom.se>
-rw-r--r--bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/IWorkbenchPreferenceConstants.java2
-rw-r--r--bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/IWorkingSetUpdater2.java2
-rw-r--r--bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/databinding/WorkbenchObservables.java265
-rw-r--r--bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/databinding/typed/WorkbenchProperties.java80
-rw-r--r--bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/databinding/ActivePageProperty.java74
-rw-r--r--bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/databinding/ActivePartProperty.java70
-rw-r--r--bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/databinding/ActiveWindowProperty.java78
-rw-r--r--bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/databinding/EditorInputProperty.java62
-rw-r--r--bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/databinding/ListeningValue.java112
-rw-r--r--bundles/org.eclipse.ui.workbench/META-INF/MANIFEST.MF2
-rw-r--r--bundles/org.eclipse.ui.workbench/pom.xml2
-rw-r--r--tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/UiTestSuite.java2
-rw-r--r--tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/databinding/WorkbenchDatabindingTest.java337
-rw-r--r--tests/org.eclipse.ui.tests/META-INF/MANIFEST.MF1
14 files changed, 824 insertions, 265 deletions
diff --git a/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/IWorkbenchPreferenceConstants.java b/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/IWorkbenchPreferenceConstants.java
index 1c46b90917c..911217c07e9 100644
--- a/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/IWorkbenchPreferenceConstants.java
+++ b/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/IWorkbenchPreferenceConstants.java
@@ -388,7 +388,7 @@ public interface IWorkbenchPreferenceConstants {
*
* @deprecated No longer in use. Use swt-corner-radius CSS property to override
* when round or square corners are desired.
- * @since 3.119
+ * @since 3.120
*/
@Deprecated
String USE_ROUND_TABS = "USE_ROUND_TABS"; //$NON-NLS-1$
diff --git a/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/IWorkingSetUpdater2.java b/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/IWorkingSetUpdater2.java
index b91d4bad186..dd1ee5b273a 100644
--- a/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/IWorkingSetUpdater2.java
+++ b/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/IWorkingSetUpdater2.java
@@ -19,7 +19,7 @@ import org.eclipse.core.runtime.IAdaptable;
* <code>IWorkingSetUpdater2</code> can be used to restore the content of a
* working set, if the working set content shouldn't be persisted by the framework.
*
- * @since 3.119
+ * @since 3.120
*/
public interface IWorkingSetUpdater2 extends IWorkingSetUpdater {
diff --git a/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/databinding/WorkbenchObservables.java b/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/databinding/WorkbenchObservables.java
index 71b449311cb..a04a75b7d28 100644
--- a/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/databinding/WorkbenchObservables.java
+++ b/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/databinding/WorkbenchObservables.java
@@ -16,10 +16,6 @@
*******************************************************************************/
package org.eclipse.ui.databinding;
-import org.eclipse.core.databinding.observable.Diffs;
-import org.eclipse.core.databinding.observable.IObservable;
-import org.eclipse.core.databinding.observable.Realm;
-import org.eclipse.core.databinding.observable.value.AbstractObservableValue;
import org.eclipse.core.databinding.observable.value.ComputedValue;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.runtime.Assert;
@@ -28,15 +24,10 @@ import org.eclipse.core.runtime.Platform;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference;
-import org.eclipse.ui.IPageListener;
-import org.eclipse.ui.IPartListener2;
import org.eclipse.ui.IPartService;
-import org.eclipse.ui.IPropertyListener;
import org.eclipse.ui.ISelectionService;
-import org.eclipse.ui.IWindowListener;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPage;
-import org.eclipse.ui.IWorkbenchPartConstants;
import org.eclipse.ui.IWorkbenchPartReference;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.databinding.typed.WorkbenchProperties;
@@ -119,44 +110,7 @@ public class WorkbenchObservables {
*/
public static IObservableValue<IWorkbenchWindow> observeActiveWorkbenchWindow(IWorkbench workbench) {
Assert.isNotNull(workbench);
- return new ListeningValue<IWorkbenchWindow>() {
- private final IWindowListener listener = new IWindowListener() {
- @Override
- public void windowActivated(IWorkbenchWindow window) {
- protectedSetValue(window);
- }
-
- @Override
- public void windowDeactivated(IWorkbenchWindow window) {
- if (window == doGetValue()) {
- protectedSetValue(null);
- }
- }
-
- @Override
- public void windowClosed(IWorkbenchWindow window) {
- }
-
- @Override
- public void windowOpened(IWorkbenchWindow window) {
- }
- };
-
- @Override
- protected void startListening() {
- workbench.addWindowListener(listener);
- }
-
- @Override
- protected void stopListening() {
- workbench.removeWindowListener(listener);
- }
-
- @Override
- protected IWorkbenchWindow calculate() {
- return workbench.getActiveWorkbenchWindow();
- }
- };
+ return WorkbenchProperties.activeWindow().observe(workbench);
}
/**
@@ -169,40 +123,7 @@ public class WorkbenchObservables {
*/
public static IObservableValue<IWorkbenchPage> observeActiveWorkbenchPage(IWorkbenchWindow window) {
Assert.isNotNull(window);
- return new ListeningValue<IWorkbenchPage>() {
- private final IPageListener listener = new IPageListener() {
- @Override
- public void pageActivated(IWorkbenchPage page) {
- protectedSetValue(page);
- }
-
- @Override
- public void pageClosed(IWorkbenchPage page) {
- if (page == doGetValue()) {
- protectedSetValue(null);
- }
- }
-
- @Override
- public void pageOpened(IWorkbenchPage page) {
- }
- };
-
- @Override
- protected void startListening() {
- window.addPageListener(listener);
- }
-
- @Override
- protected void stopListening() {
- window.removePageListener(listener);
- }
-
- @Override
- protected IWorkbenchPage calculate() {
- return window.getActivePage();
- }
- };
+ return WorkbenchProperties.activePage().observe(window);
}
/**
@@ -216,60 +137,7 @@ public class WorkbenchObservables {
*/
public static IObservableValue<IWorkbenchPartReference> observeActivePart(IPartService partService) {
Assert.isNotNull(partService);
- return new ListeningValue<IWorkbenchPartReference>() {
- private final IPartListener2 listener = new IPartListener2() {
- @Override
- public void partActivated(IWorkbenchPartReference partRef) {
- protectedSetValue(partRef);
- }
-
- @Override
- public void partDeactivated(IWorkbenchPartReference partRef) {
- if (partRef == doGetValue()) {
- protectedSetValue(null);
- }
- }
-
- @Override
- public void partBroughtToTop(IWorkbenchPartReference partRef) {
- }
-
- @Override
- public void partClosed(IWorkbenchPartReference partRef) {
- }
-
- @Override
- public void partOpened(IWorkbenchPartReference partRef) {
- }
-
- @Override
- public void partHidden(IWorkbenchPartReference partRef) {
- }
-
- @Override
- public void partVisible(IWorkbenchPartReference partRef) {
- }
-
- @Override
- public void partInputChanged(IWorkbenchPartReference partRef) {
- }
- };
-
- @Override
- protected void startListening() {
- partService.addPartListener(listener);
- }
-
- @Override
- protected void stopListening() {
- partService.removePartListener(listener);
- }
-
- @Override
- protected IWorkbenchPartReference calculate() {
- return partService.getActivePartReference();
- }
- };
+ return WorkbenchProperties.activePartReference().observe(partService);
}
/**
@@ -299,131 +167,6 @@ public class WorkbenchObservables {
*/
public static IObservableValue<IEditorInput> observeEditorInput(IEditorPart editor) {
Assert.isNotNull(editor);
- return new ListeningValue<IEditorInput>() {
- private final IPropertyListener listener = (Object source, int propId) -> {
- if (propId == IWorkbenchPartConstants.PROP_INPUT) {
- protectedSetValue(editor.getEditorInput());
- }
- };
-
- @Override
- protected void startListening() {
- editor.addPropertyListener(listener);
- }
-
- @Override
- protected void stopListening() {
- editor.removePropertyListener(listener);
- }
-
- @Override
- protected IEditorInput calculate() {
- return editor.getEditorInput();
- }
- };
- }
-
- /**
- * A base class for creating observable values that track the state of a
- * non-{@link IObservable} objects.
- */
- private abstract static class ListeningValue<T> extends AbstractObservableValue<T> {
- private T value;
- private boolean isListening;
- private volatile boolean hasListeners;
-
- @Override
- protected final T doGetValue() {
- // The value is not kept up to date when we are not listening.
- if (isListening) {
- return value;
- }
- return calculate();
- }
-
- /**
- * Sets the value. Must be invoked in the {@link Realm} of the observable.
- * Subclasses must call this method instead of {@link #setValue} or
- * {@link #doSetValue}.
- *
- * @param value the value to set
- */
- protected final void protectedSetValue(T value) {
- checkRealm();
- if (!isListening)
- throw new IllegalStateException();
- if (this.value != value) {
- fireValueChange(Diffs.createValueDiff(this.value, this.value = value));
- }
- }
-
- @Override
- protected final void firstListenerAdded() {
- if (getRealm().isCurrent()) {
- startListeningInternal();
- } else {
- getRealm().asyncExec(() -> {
- if (hasListeners && !isListening) {
- startListeningInternal();
- }
- });
- }
- hasListeners = true;
- super.firstListenerAdded();
- }
-
- @Override
- protected final void lastListenerRemoved() {
- if (getRealm().isCurrent()) {
- stopListeningInternal();
- } else {
- getRealm().asyncExec(() -> {
- if (!hasListeners && isListening) {
- stopListeningInternal();
- }
- });
- }
- hasListeners = false;
- super.lastListenerRemoved();
- }
-
- private void startListeningInternal() {
- isListening = true;
- value = calculate();
- startListening();
- }
-
- private void stopListeningInternal() {
- isListening = false;
- value = null;
- stopListening();
- }
-
- /**
- * Subclasses must override this method to attach listeners to the
- * non-{@link IObservable} objects the state of which is tracked by this
- * observable.
- */
- protected abstract void startListening();
-
- /**
- * Subclasses must override this method to detach listeners from the
- * non-{@link IObservable} objects the state of which is tracked by this
- * observable.
- */
- protected abstract void stopListening();
-
- /**
- * Subclasses must override this method to provide the object's value that will
- * be used when the value is not set explicitly by {@link #doSetValue(Object)}.
- *
- * @return the object's value
- */
- protected abstract T calculate();
-
- @Override
- public Object getValueType() {
- return null;
- }
+ return WorkbenchProperties.editorInput().observe(editor);
}
}
diff --git a/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/databinding/typed/WorkbenchProperties.java b/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/databinding/typed/WorkbenchProperties.java
index ce24631916d..db3ae9a1c02 100644
--- a/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/databinding/typed/WorkbenchProperties.java
+++ b/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/databinding/typed/WorkbenchProperties.java
@@ -14,12 +14,26 @@
*******************************************************************************/
package org.eclipse.ui.databinding.typed;
+import org.eclipse.core.databinding.property.Properties;
import org.eclipse.core.databinding.property.list.IListProperty;
import org.eclipse.core.databinding.property.value.IValueProperty;
import org.eclipse.core.runtime.IAdapterManager;
import org.eclipse.core.runtime.Platform;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IEditorReference;
+import org.eclipse.ui.IPageService;
+import org.eclipse.ui.IPartService;
import org.eclipse.ui.ISelectionService;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPartReference;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.internal.databinding.ActivePageProperty;
+import org.eclipse.ui.internal.databinding.ActivePartProperty;
+import org.eclipse.ui.internal.databinding.ActiveWindowProperty;
import org.eclipse.ui.internal.databinding.AdaptedValueProperty;
+import org.eclipse.ui.internal.databinding.EditorInputProperty;
import org.eclipse.ui.internal.databinding.MultiSelectionProperty;
import org.eclipse.ui.internal.databinding.SingleSelectionProperty;
@@ -175,4 +189,70 @@ public class WorkbenchProperties {
return new MultiSelectionProperty<>(partId, postSelection, elementType);
}
+ /**
+ * Returns a property for observing the active window of a workbench. The value
+ * is null if there is no active window.
+ *
+ * @return the property
+ * @see IWorkbench#getActiveWorkbenchWindow
+ * @see IWorkbench#addWindowListener
+ * @since 3.120
+ */
+ public static <S extends IWorkbench> IValueProperty<S, IWorkbenchWindow> activeWindow() {
+ return new ActiveWindowProperty<>();
+ }
+
+ /**
+ * Returns a property for observing the active page of a workbench window. The
+ * value is null if there is no active page.
+ *
+ * @return the property
+ * @see IWorkbenchWindow#getActivePage
+ * @see IWorkbenchWindow#addPageListener
+ * @since 3.120
+ */
+ public static <S extends IPageService> IValueProperty<S, IWorkbenchPage> activePage() {
+ return new ActivePageProperty<>();
+ }
+
+ /**
+ * Returns a property for observing the active part reference of a part service.
+ * The value is null if there is no active part.
+ *
+ * @return the property
+ * @see IPartService#getActivePart
+ * @see IPartService#addPartListener
+ * @since 3.120
+ */
+ public static <S extends IPartService> IValueProperty<S, IWorkbenchPartReference> activePartReference() {
+ return new ActivePartProperty<>();
+ }
+
+ /**
+ * Returns a property for observing the active part reference of a part service,
+ * casted to {@link IEditorReference}. The value is null if the active part is
+ * not an {@code IEditorReference}. Note that this value is different from
+ * {@link IWorkbenchPage#getActiveEditor}.
+ *
+ * @return the property
+ * @see IPartService#getActivePart
+ * @see IPartService#addPartListener
+ * @since 3.120
+ */
+ public static <S extends IPartService> IValueProperty<S, IEditorReference> activePartAsEditorReference() {
+ return WorkbenchProperties.<S>activePartReference().value(Properties.convertedValue(IEditorReference.class,
+ part -> part instanceof IEditorReference ? (IEditorReference) part : null));
+ }
+
+ /**
+ * Returns a property for observing the editor input an editor part.
+ *
+ * @return the property
+ * @see IEditorPart#getEditorInput
+ * @see IEditorPart#addPropertyListener
+ * @since 3.120
+ */
+ public static <S extends IEditorPart> IValueProperty<S, IEditorInput> editorInput() {
+ return new EditorInputProperty<>();
+ }
}
diff --git a/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/databinding/ActivePageProperty.java b/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/databinding/ActivePageProperty.java
new file mode 100644
index 00000000000..5e6ff7fa29c
--- /dev/null
+++ b/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/databinding/ActivePageProperty.java
@@ -0,0 +1,74 @@
+package org.eclipse.ui.internal.databinding;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.value.ValueProperty;
+import org.eclipse.ui.IPageListener;
+import org.eclipse.ui.IPageService;
+import org.eclipse.ui.IWorkbenchPage;
+
+/**
+ * A property for observing the active {@link IWorkbenchPage} of a
+ * {@link IPageService}.
+ *
+ * @param <S> the source type
+ */
+public class ActivePageProperty<S extends IPageService> extends ValueProperty<S, IWorkbenchPage> {
+ @Override
+ public Object getValueType() {
+ return IWorkbenchPage.class;
+ }
+
+ @Override
+ protected IWorkbenchPage doGetValue(S source) {
+ return source.getActivePage();
+ }
+
+ @Override
+ protected void doSetValue(S source, IWorkbenchPage value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public IObservableValue<IWorkbenchPage> observe(Realm realm, S source) {
+ return new ListeningValue<IWorkbenchPage>(realm) {
+ private final IPageListener listener = new IPageListener() {
+ @Override
+ public void pageActivated(IWorkbenchPage page) {
+ protectedSetValue(page);
+ }
+
+ @Override
+ public void pageClosed(IWorkbenchPage page) {
+ if (page == doGetValue()) {
+ protectedSetValue(null);
+ }
+ }
+
+ @Override
+ public void pageOpened(IWorkbenchPage page) {
+ }
+ };
+
+ @Override
+ protected void startListening() {
+ source.addPageListener(listener);
+ }
+
+ @Override
+ protected void stopListening() {
+ source.removePageListener(listener);
+ }
+
+ @Override
+ protected IWorkbenchPage calculate() {
+ return ActivePageProperty.this.getValue(source);
+ }
+
+ @Override
+ public Object getValueType() {
+ return ActivePageProperty.this.getValueType();
+ }
+ };
+ }
+}
diff --git a/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/databinding/ActivePartProperty.java b/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/databinding/ActivePartProperty.java
new file mode 100644
index 00000000000..4a92e534187
--- /dev/null
+++ b/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/databinding/ActivePartProperty.java
@@ -0,0 +1,70 @@
+package org.eclipse.ui.internal.databinding;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.value.ValueProperty;
+import org.eclipse.ui.IPartListener2;
+import org.eclipse.ui.IPartService;
+import org.eclipse.ui.IWorkbenchPartReference;
+
+/**
+ * A property for observing the active {@link IWorkbenchPartReference} of a
+ * {@link IPartService}.
+ *
+ * @param <S> the source type
+ */
+public class ActivePartProperty<S extends IPartService> extends ValueProperty<S, IWorkbenchPartReference> {
+ @Override
+ public Object getValueType() {
+ return IWorkbenchPartReference.class;
+ }
+
+ @Override
+ protected IWorkbenchPartReference doGetValue(S source) {
+ return source.getActivePartReference();
+ }
+
+ @Override
+ protected void doSetValue(S source, IWorkbenchPartReference value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public IObservableValue<IWorkbenchPartReference> observe(Realm realm, S source) {
+ return new ListeningValue<IWorkbenchPartReference>(realm) {
+ private final IPartListener2 listener = new IPartListener2() {
+ @Override
+ public void partActivated(IWorkbenchPartReference partRef) {
+ protectedSetValue(partRef);
+ }
+
+ @Override
+ public void partDeactivated(IWorkbenchPartReference partRef) {
+ if (partRef == doGetValue()) {
+ protectedSetValue(null);
+ }
+ }
+ };
+
+ @Override
+ protected void startListening() {
+ source.addPartListener(listener);
+ }
+
+ @Override
+ protected void stopListening() {
+ source.removePartListener(listener);
+ }
+
+ @Override
+ protected IWorkbenchPartReference calculate() {
+ return ActivePartProperty.this.getValue(source);
+ }
+
+ @Override
+ public Object getValueType() {
+ return ActivePartProperty.this.getValueType();
+ }
+ };
+ }
+}
diff --git a/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/databinding/ActiveWindowProperty.java b/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/databinding/ActiveWindowProperty.java
new file mode 100644
index 00000000000..b2c278b922b
--- /dev/null
+++ b/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/databinding/ActiveWindowProperty.java
@@ -0,0 +1,78 @@
+package org.eclipse.ui.internal.databinding;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.value.ValueProperty;
+import org.eclipse.ui.IWindowListener;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchWindow;
+
+/**
+ * A property for observing the active {@link IWorkbenchWindow} of a
+ * {@link IWorkbench}.
+ *
+ * @param <S> the source type
+ */
+public class ActiveWindowProperty<S extends IWorkbench> extends ValueProperty<S, IWorkbenchWindow> {
+ @Override
+ public Object getValueType() {
+ return IWorkbenchWindow.class;
+ }
+
+ @Override
+ protected IWorkbenchWindow doGetValue(S source) {
+ return source.getActiveWorkbenchWindow();
+ }
+
+ @Override
+ protected void doSetValue(S source, IWorkbenchWindow value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public IObservableValue<IWorkbenchWindow> observe(Realm realm, S source) {
+ return new ListeningValue<IWorkbenchWindow>(realm) {
+ private final IWindowListener listener = new IWindowListener() {
+ @Override
+ public void windowActivated(IWorkbenchWindow window) {
+ protectedSetValue(window);
+ }
+
+ @Override
+ public void windowDeactivated(IWorkbenchWindow window) {
+ if (window == doGetValue()) {
+ protectedSetValue(null);
+ }
+ }
+
+ @Override
+ public void windowClosed(IWorkbenchWindow window) {
+ }
+
+ @Override
+ public void windowOpened(IWorkbenchWindow window) {
+ }
+ };
+
+ @Override
+ protected void startListening() {
+ source.addWindowListener(listener);
+ }
+
+ @Override
+ protected void stopListening() {
+ source.removeWindowListener(listener);
+ }
+
+ @Override
+ protected IWorkbenchWindow calculate() {
+ return ActiveWindowProperty.this.getValue(source);
+ }
+
+ @Override
+ public Object getValueType() {
+ return ActiveWindowProperty.this.getValueType();
+ }
+ };
+ }
+}
diff --git a/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/databinding/EditorInputProperty.java b/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/databinding/EditorInputProperty.java
new file mode 100644
index 00000000000..6716db25add
--- /dev/null
+++ b/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/databinding/EditorInputProperty.java
@@ -0,0 +1,62 @@
+package org.eclipse.ui.internal.databinding;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.value.ValueProperty;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IPropertyListener;
+import org.eclipse.ui.IWorkbenchPartConstants;
+
+/**
+ * A property for observing the {@link IEditorInput} of and {@link IEditorPart}.
+ *
+ * @param <S> the source type
+ */
+public class EditorInputProperty<S extends IEditorPart> extends ValueProperty<S, IEditorInput> {
+ @Override
+ public Object getValueType() {
+ return IEditorInput.class;
+ }
+
+ @Override
+ protected IEditorInput doGetValue(S source) {
+ return source.getEditorInput();
+ }
+
+ @Override
+ protected void doSetValue(S source, IEditorInput value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public IObservableValue<IEditorInput> observe(Realm realm, S source) {
+ return new ListeningValue<IEditorInput>(realm) {
+ private final IPropertyListener listener = (Object s, int propId) -> {
+ if (propId == IWorkbenchPartConstants.PROP_INPUT) {
+ protectedSetValue(source.getEditorInput());
+ }
+ };
+
+ @Override
+ protected void startListening() {
+ source.addPropertyListener(listener);
+ }
+
+ @Override
+ protected void stopListening() {
+ source.removePropertyListener(listener);
+ }
+
+ @Override
+ protected IEditorInput calculate() {
+ return EditorInputProperty.this.getValue(source);
+ }
+
+ @Override
+ public Object getValueType() {
+ return EditorInputProperty.this.getValueType();
+ }
+ };
+ }
+}
diff --git a/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/databinding/ListeningValue.java b/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/databinding/ListeningValue.java
new file mode 100644
index 00000000000..b9371dd2abb
--- /dev/null
+++ b/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/databinding/ListeningValue.java
@@ -0,0 +1,112 @@
+package org.eclipse.ui.internal.databinding;
+
+import org.eclipse.core.databinding.observable.Diffs;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.AbstractObservableValue;
+
+/**
+ * A base class for creating observable values that track the state of a
+ * non-{@link IObservable} objects.
+ *
+ * @param <T> the value type
+ */
+abstract class ListeningValue<T> extends AbstractObservableValue<T> {
+ private T value;
+ private boolean isListening;
+ private volatile boolean hasListeners;
+
+ public ListeningValue(Realm realm) {
+ super(realm);
+ }
+
+ @Override
+ protected final T doGetValue() {
+ // The value is not kept up to date when we are not listening.
+ if (isListening) {
+ return value;
+ }
+ return calculate();
+ }
+
+ /**
+ * Sets the value. Must be invoked in the {@link Realm} of the observable.
+ * Subclasses must call this method instead of {@link #setValue} or
+ * {@link #doSetValue}.
+ *
+ * @param value the value to set
+ */
+ protected final void protectedSetValue(T value) {
+ checkRealm();
+ if (!isListening)
+ throw new IllegalStateException();
+ if (this.value != value) {
+ fireValueChange(Diffs.createValueDiff(this.value, this.value = value));
+ }
+ }
+
+ @Override
+ protected final void firstListenerAdded() {
+ if (getRealm().isCurrent()) {
+ startListeningInternal();
+ } else {
+ getRealm().asyncExec(() -> {
+ if (hasListeners && !isListening) {
+ startListeningInternal();
+ }
+ });
+ }
+ hasListeners = true;
+ super.firstListenerAdded();
+ }
+
+ @Override
+ protected final void lastListenerRemoved() {
+ if (getRealm().isCurrent()) {
+ stopListeningInternal();
+ } else {
+ getRealm().asyncExec(() -> {
+ if (!hasListeners && isListening) {
+ stopListeningInternal();
+ }
+ });
+ }
+ hasListeners = false;
+ super.lastListenerRemoved();
+ }
+
+ private void startListeningInternal() {
+ isListening = true;
+ value = calculate();
+ startListening();
+ }
+
+ private void stopListeningInternal() {
+ isListening = false;
+ value = null;
+ stopListening();
+ }
+
+ /**
+ * Subclasses must override this method to attach listeners to the
+ * non-{@link IObservable} objects the state of which is tracked by this
+ * observable.
+ */
+ protected abstract void startListening();
+
+ /**
+ * Subclasses must override this method to detach listeners from the
+ * non-{@link IObservable} objects the state of which is tracked by this
+ * observable.
+ */
+ protected abstract void stopListening();
+
+ /**
+ * Subclasses must override this method to provide the object's value that will
+ * be used when the value is not set explicitly by
+ * {@link AbstractObservableValue#doSetValue(Object)}.
+ *
+ * @return the object's value
+ */
+ protected abstract T calculate();
+}
diff --git a/bundles/org.eclipse.ui.workbench/META-INF/MANIFEST.MF b/bundles/org.eclipse.ui.workbench/META-INF/MANIFEST.MF
index 33a2a2123da..526c4b35f26 100644
--- a/bundles/org.eclipse.ui.workbench/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.ui.workbench/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %pluginName
Bundle-SymbolicName: org.eclipse.ui.workbench; singleton:=true
-Bundle-Version: 3.119.100.qualifier
+Bundle-Version: 3.120.0.qualifier
Bundle-ClassPath: .
Bundle-Activator: org.eclipse.ui.internal.WorkbenchPlugin
Bundle-ActivationPolicy: lazy
diff --git a/bundles/org.eclipse.ui.workbench/pom.xml b/bundles/org.eclipse.ui.workbench/pom.xml
index 41b5507167d..aa0f58105d2 100644
--- a/bundles/org.eclipse.ui.workbench/pom.xml
+++ b/bundles/org.eclipse.ui.workbench/pom.xml
@@ -20,7 +20,7 @@
</parent>
<groupId>org.eclipse.ui</groupId>
<artifactId>org.eclipse.ui.workbench</artifactId>
- <version>3.119.100-SNAPSHOT</version>
+ <version>3.120.0-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
<properties>
diff --git a/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/UiTestSuite.java b/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/UiTestSuite.java
index 21b02de901c..f4a073f8bb2 100644
--- a/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/UiTestSuite.java
+++ b/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/UiTestSuite.java
@@ -25,6 +25,7 @@ import org.eclipse.ui.tests.api.StartupTest;
import org.eclipse.ui.tests.commands.CommandsTestSuite;
import org.eclipse.ui.tests.concurrency.ConcurrencyTestSuite;
import org.eclipse.ui.tests.contexts.ContextsTestSuite;
+import org.eclipse.ui.tests.databinding.WorkbenchDatabindingTest;
import org.eclipse.ui.tests.datatransfer.DataTransferTestSuite;
import org.eclipse.ui.tests.decorators.DecoratorsTestSuite;
import org.eclipse.ui.tests.dialogs.FilteredResourcesSelectionDialogTestSuite;
@@ -100,6 +101,7 @@ import org.junit.runners.Suite;
IntroTestSuite.class,
MultiEditorTestSuite.class,
OpenSystemInPlaceEditorTest.class,
+ WorkbenchDatabindingTest.class,
})
public class UiTestSuite {
}
diff --git a/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/databinding/WorkbenchDatabindingTest.java b/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/databinding/WorkbenchDatabindingTest.java
new file mode 100644
index 00000000000..c4619351419
--- /dev/null
+++ b/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/databinding/WorkbenchDatabindingTest.java
@@ -0,0 +1,337 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Jens Lidestrom and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Jens Lidestrom - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.ui.tests.databinding;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.observable.value.IValueChangeListener;
+import org.eclipse.core.databinding.observable.value.ValueChangeEvent;
+import org.eclipse.core.databinding.observable.value.ValueDiff;
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IEditorReference;
+import org.eclipse.ui.IPageListener;
+import org.eclipse.ui.IPageService;
+import org.eclipse.ui.IPartListener2;
+import org.eclipse.ui.IPartService;
+import org.eclipse.ui.IPropertyListener;
+import org.eclipse.ui.IWindowListener;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPartReference;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.databinding.typed.WorkbenchProperties;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * Tests for {@link WorkbenchProperties}.
+ */
+public class WorkbenchDatabindingTest {
+ private static TestRealm realm;
+
+ @BeforeClass
+ public static void setup() {
+ realm = new TestRealm();
+ }
+
+ @Test
+ public void testActiveWindow() {
+ IWorkbench workbench = mock(IWorkbench.class);
+ IWorkbenchWindow win1 = mock(IWorkbenchWindow.class);
+ IWorkbenchWindow win2 = mock(IWorkbenchWindow.class);
+
+ when(workbench.getActiveWorkbenchWindow()) //
+ .thenReturn(win1, win1, win2) // One initial value, on when listener is added, one after change
+ .thenThrow(new AssertionError());
+
+ AtomicReference<IWindowListener> listener = new AtomicReference<>();
+ doAnswer(inv -> {
+ listener.set((IWindowListener) inv.getArgument(0));
+ return null;
+ }).when(workbench).addWindowListener(any());
+
+ List<ValueChangeEvent<?>> events = new ArrayList<>();
+
+ IObservableValue<IWorkbenchWindow> value = WorkbenchProperties.activeWindow().observe(workbench);
+
+ // Check initial value
+ assertSame(win1, value.getValue());
+
+ IValueChangeListener<Object> changeListener = events::add;
+ value.addValueChangeListener(changeListener);
+
+ // Update using listener
+ listener.get().windowActivated(win2);
+
+ assertSame(win2, value.getValue());
+ assertEquals(1, events.size());
+ ValueDiff<?> diff = events.get(0).diff;
+ assertEquals(win1, diff.getOldValue());
+ assertEquals(win2, diff.getNewValue());
+
+ // Other window is deactivated
+ listener.get().windowDeactivated(win1);
+
+ assertSame(win2, value.getValue());
+ assertEquals(1, events.size());
+
+ // Active window is deactivated
+ listener.get().windowDeactivated(win2);
+
+ assertNull(value.getValue());
+ assertEquals(2, events.size());
+
+ value.removeValueChangeListener(changeListener);
+ value.dispose();
+ }
+
+ @Test
+ public void testActiveWindowValueType() {
+ IObservableValue<IWorkbenchWindow> value = WorkbenchProperties.activeWindow().observe(mock(IWorkbench.class));
+ assertEquals(IWorkbenchWindow.class, value.getValueType());
+ assertEquals(IWorkbenchWindow.class, WorkbenchProperties.activeWindow().getValueType());
+ }
+
+ @Test
+ public void testActivePage() {
+ IWorkbenchWindow window = mock(IWorkbenchWindow.class);
+ IWorkbenchPage page1 = mock(IWorkbenchPage.class);
+ IWorkbenchPage page2 = mock(IWorkbenchPage.class);
+
+ when(window.getActivePage()) //
+ .thenReturn(page1, page1, page2) // One initial value, on when listener is added, one after change
+ .thenThrow(new AssertionError());
+
+ AtomicReference<IPageListener> listener = new AtomicReference<>();
+ doAnswer(inv -> {
+ listener.set((IPageListener) inv.getArgument(0));
+ return null;
+ }).when(window).addPageListener(any());
+
+ List<ValueChangeEvent<?>> events = new ArrayList<>();
+
+ IObservableValue<IWorkbenchPage> value = WorkbenchProperties.activePage().observe(window);
+
+ // Check initial value
+ assertSame(page1, value.getValue());
+
+ IValueChangeListener<Object> changeListener = events::add;
+ value.addValueChangeListener(changeListener);
+
+ // Update using listener
+ listener.get().pageActivated(page2);
+
+ assertSame(page2, value.getValue());
+ assertEquals(1, events.size());
+ ValueDiff<?> diff = events.get(0).diff;
+ assertEquals(page1, diff.getOldValue());
+ assertEquals(page2, diff.getNewValue());
+
+ // Other page is closed
+ listener.get().pageClosed(page1);
+
+ assertSame(page2, value.getValue());
+ assertEquals(1, events.size());
+
+ // Active page is closed
+ listener.get().pageClosed(page2);
+
+ assertNull(value.getValue());
+ assertEquals(2, events.size());
+
+ value.removeValueChangeListener(changeListener);
+ value.dispose();
+ }
+
+ @Test
+ public void testActivePageValueType() {
+ IObservableValue<IWorkbenchPage> value = WorkbenchProperties.activePage().observe(mock(IPageService.class));
+ assertEquals(IWorkbenchPage.class, value.getValueType());
+ assertEquals(IWorkbenchPage.class, WorkbenchProperties.activePage().getValueType());
+ }
+
+ @Test
+ public void testActivePartReference() {
+ IPartService service = mock(IPartService.class);
+ IWorkbenchPartReference part1 = mock(IWorkbenchPartReference.class);
+ IWorkbenchPartReference part2 = mock(IWorkbenchPartReference.class);
+
+ when(service.getActivePartReference()) //
+ .thenReturn(part1, part1, part2) // One initial value, on when listener is added, one after change
+ .thenThrow(new AssertionError());
+
+ AtomicReference<IPartListener2> listener = new AtomicReference<>();
+ doAnswer(inv -> {
+ listener.set((IPartListener2) inv.getArgument(0));
+ return null;
+ }).when(service).addPartListener(any(IPartListener2.class));
+
+ List<ValueChangeEvent<?>> events = new ArrayList<>();
+
+ IObservableValue<IWorkbenchPartReference> value = WorkbenchProperties.activePartReference().observe(service);
+
+ // Check initial value
+ assertSame(part1, value.getValue());
+
+ IValueChangeListener<Object> changeListener = events::add;
+ value.addValueChangeListener(changeListener);
+
+ // Update using listener
+ listener.get().partActivated(part2);
+
+ assertSame(part2, value.getValue());
+ assertEquals(1, events.size());
+ ValueDiff<?> diff = events.get(0).diff;
+ assertEquals(part1, diff.getOldValue());
+ assertEquals(part2, diff.getNewValue());
+
+ // Other part is deactivated
+ listener.get().partDeactivated(part1);
+
+ assertSame(part2, value.getValue());
+ assertEquals(1, events.size());
+
+ // Active part is deactivated
+ listener.get().partDeactivated(part2);
+
+ assertNull(value.getValue());
+ assertEquals(2, events.size());
+
+ value.removeValueChangeListener(changeListener);
+ value.dispose();
+ }
+
+ @Test
+ public void testActivePartReferenceValueType() {
+ IObservableValue<IWorkbenchPartReference> value = WorkbenchProperties.activePartReference().observe(mock(IPartService.class));
+ assertEquals(IWorkbenchPartReference.class, value.getValueType());
+ assertEquals(IWorkbenchPartReference.class, WorkbenchProperties.activePartReference().getValueType());
+ }
+
+ /**
+ * {@link WorkbenchProperties#activeEditorReference} is implemented using
+ * {@link WorkbenchProperties#activePartReference}, so we only need to verify
+ * that the conversion works.
+ */
+ @Test
+ public void testActiveEditorReference() {
+ IPartService service = mock(IPartService.class);
+ IWorkbenchPartReference part = mock(IWorkbenchPartReference.class);
+ IEditorReference editor = mock(IEditorReference.class);
+
+ when(service.getActivePartReference()) //
+ .thenReturn(part, editor) // One initial value, on when listener is added, one after change
+ .thenThrow(new AssertionError());
+
+ IValueProperty<IPartService, IEditorReference> prop = WorkbenchProperties.activePartAsEditorReference();
+
+ assertNull(prop.getValue(service));
+ assertSame(editor, prop.getValue(service));
+ }
+
+ @Test
+ public void testActiveEditorReferenceValueType() {
+ IObservableValue<IEditorReference> value = WorkbenchProperties.activePartAsEditorReference().observe(mock(IPartService.class));
+ assertEquals(IEditorReference.class, value.getValueType());
+ assertEquals(IEditorReference.class, WorkbenchProperties.activePartAsEditorReference().getValueType());
+ }
+
+ /**
+ * {@link WorkbenchProperties#activeEditorReference} is implemented using
+ * {@link WorkbenchProperties#activePartReference}, so we only need to verify
+ * that the conversion works.
+ */
+ @Test
+ public void testEditorInput() {
+ IEditorPart editor = mock(IEditorPart.class);
+ IEditorInput input1 = mock(IEditorInput.class);
+ IEditorInput input2 = mock(IEditorInput.class);
+
+ when(editor.getEditorInput()) //
+ .thenReturn(input1, input1, input2) // One initial value, on when listener is added, one after change
+ .thenThrow(new AssertionError());
+
+ AtomicReference<IPropertyListener> listener = new AtomicReference<>();
+ doAnswer(inv -> {
+ listener.set((IPropertyListener) inv.getArgument(0));
+ return null;
+ }).when(editor).addPropertyListener(any(IPropertyListener.class));
+
+ List<ValueChangeEvent<?>> events = new ArrayList<>();
+
+ IObservableValue<IEditorInput> value = WorkbenchProperties.editorInput().observe(editor);
+
+ // Check initial value
+ assertSame(input1, value.getValue());
+
+ IValueChangeListener<Object> changeListener = events::add;
+ value.addValueChangeListener(changeListener);
+
+ // Update using listener
+ listener.get().propertyChanged(editor, IEditorPart.PROP_INPUT);
+
+ assertSame(input2, value.getValue());
+ assertEquals(1, events.size());
+ ValueDiff<?> diff = events.get(0).diff;
+ assertEquals(input1, diff.getOldValue());
+ assertEquals(input2, diff.getNewValue());
+
+ value.removeValueChangeListener(changeListener);
+ value.dispose();
+ }
+
+ @Test
+ public void testEditorInputValueType() {
+ IObservableValue<IEditorInput> value = WorkbenchProperties.editorInput().observe(mock(IEditorPart.class));
+ assertEquals(IEditorInput.class, value.getValueType());
+ assertEquals(IEditorInput.class, WorkbenchProperties.editorInput().getValueType());
+ }
+
+ @AfterClass
+ public static void teardown() {
+ realm.restore();
+ }
+
+ private static class TestRealm extends Realm {
+ private Realm old;
+
+ public TestRealm() {
+ setDefault(this);
+ }
+
+ @Override
+ public boolean isCurrent() {
+ return true;
+ }
+
+ public void restore() {
+ setDefault(old);
+ }
+ }
+}
diff --git a/tests/org.eclipse.ui.tests/META-INF/MANIFEST.MF b/tests/org.eclipse.ui.tests/META-INF/MANIFEST.MF
index a2300c40afa..2a53ecf60d7 100644
--- a/tests/org.eclipse.ui.tests/META-INF/MANIFEST.MF
+++ b/tests/org.eclipse.ui.tests/META-INF/MANIFEST.MF
@@ -23,6 +23,7 @@ Require-Bundle: org.eclipse.core.resources,
org.eclipse.core.filesystem,
org.eclipse.core.databinding,
org.eclipse.core.databinding.beans,
+ org.eclipse.core.databinding.property,
org.eclipse.jface.databinding,
org.eclipse.ui.navigator.resources,
org.eclipse.core.runtime,

Back to the top