This article describes how you can react on changes of the application model.
1. Listening to Eclipse framework events
If the application model changes the Eclipse framework sends out notifications. The Eclipse 4 framework allows to notify event listeners if this happens.
For example the UIEvents.UILifeCycle.APP_STARTUP_COMPLETE
event is triggered once the application has started.
Application components can register to this event and are called by the framework once this happens.
Also if your application model changes like your windows, perspectives, parts and their containers, e.g., application, perspective stacks, part sash container or part stacks, an event is propagated by the IEventBroker.
The central class which defines the Eclipse framework events is the UIEvents
class from the org.eclipse.e4.ui.workbench
package.
It contains most of the events or topics and also helper methods to work with the events.
This class also contains information about the purpose of events in its Javadoc.
The Eclipse framework sends out objects of type org.osgi.service.event.Event
.
1.1. Example: the part activation event
A topic, which is fired by Eclipse is the UIEvents.UILifeCycle.ACTIVATE
, which is fired every time a part is activated.
@Inject
@Optional
public void subscribeTopicPartActivation(@UIEventTopic(UIEvents.UILifeCycle.ACTIVATE) Event event) {
Object element = event.getProperty(EventTags.ELEMENT);
if (!(element instanceof MPart)) {
return;
}
MPart part = (MPart) element;
System.out.println("Part activated: " + part.getLabel());
}
The Event
class contains properties like EventTags.ELEMENT
, from which we can receive the element, which is concerned in this event.
But there are more and therefore you should have a look at the UIEvents
class and its internal interfaces like UIEvents.EventTags
.
1.2. Extract event information
The UIEvents.EventTags
interface define the possible event tags.
These event tags are explained in the following sections.
1.2.1. ATTNAME event tag
The ATTNAME
property of the event is the attribute of the model, which has been changed.
So if you for instance listen to changes of a model element’s label, the ATTNAME
of the event will be label
.
@Inject
@Optional
public void subscribeTopicLabelChange(@EventTopic(UIEvents.UILabel.TOPIC_LABEL) Event event) {
Object element = event.getProperty(EventTags.ATTNAME);
// will print "label" to the system output
System.out.println(element);
}
The label is an XML attribute and can be found is the source of your Application.e4xmi file.
|
<children xsi:type="basic:Part" xmi:id="_9CJOwNgYEeKX2Jv3m0M6IA"
elementId="com.sample.application.part.attname"
contributionURI="bundleclass://com.sample.application/yourpackage.attname"
label="ATTNAME sample
label"/>
1.2.2. ELEMENT event tag
The ELEMENT
event tag in general is the model element, which is concerned in the event.
1.2.3. NEW_VALUE and OLD_VALUE event tags
The NEW_VALUE
and OLD_VALUE
properties contain the changed value of the element’s attribute.
In the old Eclipse 3.x days, you would have registered an IPerspectiveListener for the WorkbenchWindow and implement the perspectiveChanged(…) method.
But in Eclipse 4 you’d rather have to think in a DOM(Document Object Model) manner, where <ELEMENT ATTNAME="NEW_VALUE"> are manipulated and fired within events.
|
A example for using the values of an attribute in the model is a listener for a perspective change.
@Inject
@Optional
public void subscribeTopicSelectedElement(@EventTopic
(UIEvents.ElementContainer.TOPIC_SELECTEDELEMENT) Event event) {
Object element = event.getProperty(EventTags.ELEMENT);
Object newValue = event.getProperty(EventTags.NEW_VALUE);
// ensure that the selected element of a perspective stack is changed and that this is a perspective
if (!(element instanceof MPerspectiveStack) || !(newValue instanceof MPerspective)) {
return;
}
MPerspectiveStack perspectiveStack = (MPerspectiveStack) element;
MPerspective perspective = (MPerspective) newValue;
System.out.println("Selected perspective of " + perspectiveStack + " is " + perspective.getLabel());
}
In case you want to listen to a kind of "perspective changed" event, you need to listen to the change of the selectedElement
attribute of the MPerspectiveStack
and can get the new and old MPerspective
from EventTags.NEW_VALUE
and EventTags.OLD_VALUE
properties.
So you actually listen to the changed selectedElement
attribute value of the MPerspectiveStack
, rather than looking for a "perspective change" event.
So it is good to keep the DOM structure of the application model in mind and think of the changes, which can occur within the application model.
1.2.4. TYPE event tag
With the TYPE
event tag you may determine what action has been done to the application model.
The different types can be found in the UIEvents.EventTypes
interface.
With these event types you are able to figure out, whether a model element was added, created, moved, removed or an attribute was set.
The UIEvents
class, also offers convenience methods to check for some types.
The isADD(Event)
and isREMOVE(Event)
methods apply for both single and many, e.g., ADD and ADD_MANY)
1.2.5. POSITION event tag
The POSITION event tag contains the new position of model elements. So this property is only set in case a collection of elements is concerned.
If you move parts in a MPartStack or between different MPartStacks, you can also listen to changes of the children of an element container like this:
@Inject
@Optional
public void subscribeTopicElementContainerChildren(@EventTopic
(UIEvents.ElementContainer.TOPIC_CHILDREN) Event event) {
Object element = event.getProperty(EventTags.ELEMENT);
if (!(element instanceof MPartStack)) {
return;
}
if (UIEvents.isADD(event)) {
// check new value, because we check for addition and old value will be null
Object newValue = event.getProperty(EventTags.NEW_VALUE);
if (newValue instanceof MPart) {
MPart part = (MPart) newValue;
System.out.println("Added " + part.getLabel() + " at position: "
+ event.getProperty(EventTags.POSITION));
}
} else if (UIEvents.isREMOVE(event)) {
// check old value, because we check for remove and new value will be null
Object oldValue = event.getProperty(EventTags.OLD_VALUE);
if (oldValue instanceof MPart) {
MPart part = (MPart) oldValue;
System.out.println("Removed " + part.getLabel() + " from position: "
+ event.getProperty(EventTags.POSITION));
}
}
}
In case you drag and drop a MPart
within a MPartStack
, the MPart
will be removed and afterwards added to the MPartStack
.
The details can be seen in the UIEventPublisher.formatData()
method from the org.eclipse.e4.ui.internal.workbench
package, where the position on is only the for move, add or remove events.
1.2.6. WIDGET event tag
The WIDGET event tag can be used to get the underlying widget, e.g., a Composite
, of the model element, which is concerned in the event.
So you are also able to directly interact with the underlying widget. But in most cases this is not necessary, because the model elements themselves provide the most of the methods to interact with the widget, like setLabel
or setVisible
methods and so on.
1.3. Convenience methods for working with events
Besides the convenience methods for the event types, the UIEvents class offers two more convenience methods:
1.3.1. The contains method
The contains
method can be used to check whether a certain object is part of an event’s property.
Imagine you listen for changed tags on a certain model element and want to determine, if a certain tag is part of the tags of the given model element.
In this case the contains
method helps you with that task.
An example for this can be found in the subscribeTopicTagsChanged(Event)
method of the ToolBarManagerRenderer
class from the Eclipse framework.
@Inject
@Optional
private void subscribeTopicTagsChanged(@UIEventTopic
(UIEvents.ApplicationElement.TOPIC_TAGS) Event event) {
Object changedObj = event.getProperty(EventTags.ELEMENT);
// this should only be applied to MToolbar elements
if (!(changedObj instanceof MToolBar)) {
return;
}
final MUIElement changedElement = (MUIElement) changedObj;
// In case tags have been added and the new tag list contains the
// IPresentationEngine.HIDDEN_EXPLICITLY String, the element should be hidden
if (UIEvents.isADD(event)) {
if (UIEvents.contains(event, UIEvents.EventTags.NEW_VALUE,
IPresentationEngine.HIDDEN_EXPLICITLY)) {
changedElement.setVisible(false);
changedElement.setToBeRendered(false);
}
// If IPresentationEngine.HIDDEN_EXPLICITLY tag was removed the element should be visible again
} else if (UIEvents.isREMOVE(event)) {
if (UIEvents.contains(event, UIEvents.EventTags.OLD_VALUE,
IPresentationEngine.HIDDEN_EXPLICITLY)) {
changedElement.setVisible(true);
changedElement.setToBeRendered(true);
}
}
}
This code demonstrates how the existence of a certain tag (IPresentationEngine.HIDDEN_EXPLICITLY
) is checked with the contains method.
1.3.2. The asIterable method
The asIterable method can be used to get an Iterable
of an event’s property.
Let’s take the example where we listened to the UIEvents.ElementContainer.TOPIC_CHILDREN
event and checked the new position.
This sample lacks in case we add more than one child to the MPartStack
, so that we have the UIEvents.EventTypes.ADD_MANY
event type.
In this case nothing would be printed to the system out, because we have a list in the EventTags.NEW_VALUE
property rather than an MPart
.
So we need to enhance our subscribeTopicElementContainerChildren
method like this:
@Inject
@Optional
public void subscribeTopicElementContainerChildren(@EventTopic
(UIEvents.ElementContainer.TOPIC_CHILDREN) Event event) {
Object element = event.getProperty(EventTags.ELEMENT);
if (!(element instanceof MPartStack)) {
return;
}
if (UIEvents.isADD(event)) {
// check new value, because we check for addition and old value will
// be null
Iterable<?> asIterable = UIEvents.asIterable(event, EventTags.NEW_VALUE);
for (Object object : asIterable) {
if (object instanceof MPart) {
MPart part = (MPart) object;
System.out.println("Added " + part.getLabel() + " at position: "
+ event.getProperty(EventTags.POSITION));
}
}
} else if (UIEvents.isREMOVE(event)) {
// check old value, because we check for remove and new value will
// be null
Iterable<?> asIterable = UIEvents.asIterable(event, EventTags.OLD_VALUE);
for (Object object : asIterable) {
if (object instanceof MPart) {
MPart part = (MPart) object;
System.out.println("Removed " + part.getLabel() + " from position: "
+ event.getProperty(EventTags.POSITION));
}
}
}
}
2. Exercise: Changing the window title on perspective change
In this exercise we will change the window title according to the active perspective.
2.1. Add a new model add-on which listen to perspective switches
Now we add a new model-addon via an model fragment. This add-on will listen to the perspective change event.
The implementation of the PerspectiveSwitchAddon
looks like this:
package com.vogella.tasks.ui.addon;
import jakarta.inject.Inject;
import org.eclipse.e4.core.di.annotations.Optional;
import org.eclipse.e4.core.di.extensions.EventTopic;
import org.eclipse.e4.ui.model.application.ui.advanced.MPerspective;
import org.eclipse.e4.ui.model.application.ui.basic.MWindow;
import org.eclipse.e4.ui.workbench.UIEvents;
import org.eclipse.e4.ui.workbench.UIEvents.EventTags;
import org.eclipse.e4.ui.workbench.modeling.EModelService;
import org.osgi.service.event.Event;
public class PerspectiveSwitchAddon {
@Inject
private EModelService modelService;
@Inject
@Optional
public void subscribeTopicSelectedElement(
@EventTopic(UIEvents.ElementContainer.TOPIC_SELECTEDELEMENT) Event event) {
Object newValue = event.getProperty(EventTags.NEW_VALUE);
// only run this, if the NEW_VALUE is a MPerspective
if (!(newValue instanceof MPerspective)) {
return;
}
MPerspective perspective = (MPerspective) newValue;
// Get the MWindow, where we want to change the label
MWindow topLevelWindowOfPerspective = modelService.getTopLevelWindowFor(perspective);
// for demo purpose only print out attributes of topLevelWindow
System.out.println(topLevelWindowOfPerspective);
}
}
2.2. Validate
Start your application again. Switch perspectives and check for the output on the Console.
If the add-in is not visible, try to start your runtime with -clearPersistedState to remove persisted model information. |
3. Application Model Events online resources
3.1. vogella Java example code
If you need more assistance we offer Online Training and Onsite training as well as consulting