Home » Eclipse Projects » GEF » GEF4 model part, how to draw a roundedrectangle with text
|
Re: GEF4 model part, how to draw a roundedrectangle with text [message #1670729 is a reply to message #1669293] |
Fri, 13 March 2015 10:08 |
|
Hello Frank,
the GEF4 Geometry API is independant from the visualization, i.e. a RoundedRectangle is not a visual by itself. We provide o.e.g4.fx.nodes.FXGeometryNode to display arbitrary geometries of the GEF4 Geometry API in JavaFX, but you can also use a JavaFX Rectangle to display a rectangle with round corners.
There are some differences between using FXGeometryNode and JavaFX Rectangle:
- The FXGeometryNode is resizable, therefore you can use the FXResizeNodeOperation to resize it.
- By using an FXGeometryNode<IGeometry> you can display arbitrary geometries with the node.
- The JavaFX Rectangle provides properties which you can bind to some value/register for changes/etc.
To combine visuals in JavaFX you have to insert them into one parent visual. For example, you could insert the Rectangle and the Text into a StackPane (which stacks its children) and return that StackPane from the createVisual() method.
So, here you are a small example for a NodePart using JavaFX StackPane, Rectangle, and Text:
public class NodePart extends AbstractFXContentPart<StackPane> {
private Rectangle rect;
private Text text;
@Override
protected StackPane createVisual() {
// create rounded rectangle
rect = new Rectangle();
rect.setArcWidth(20);
rect.setArcHeight(20);
// create text
text = new Text("-");
// ensure rectangle surrounds text
text.layoutBoundsProperty().addListener(new ChangeListener<Bounds>() {
@Override
public void changed(ObservableValue<? extends Bounds> observable,
Bounds oldValue, Bounds newValue) {
rect.setWidth(newValue.getWidth() + 20);
rect.setHeight(newValue.getHeight() + 20);
}
});
// combine rectangle and text in a StackPane
StackPane stackPane = new StackPane();
stackPane.getChildren().addAll(rect, text);
// add style class and style sheet
stackPane.getStyleClass().add("NodePart");
stackPane.getStylesheets().add(
getClass().getResource("nodestyle.css").toExternalForm());
return stackPane;
}
@Override
protected void doRefreshVisual(StackPane visual) {
// update visualization with data from model
text.setText(getContent());
}
@Override
public String getContent() {
// cast to content type
return (String) super.getContent();
}
}
This NodePart implementation does also load a stylesheet which can be used to style nodes, for example:
.NodePart Rectangle {
-fx-fill: black;
-fx-stroke: lightblue;
-fx-stroke-width: 1.5;
-fx-effect: dropshadow(gaussian, blue, 10, 0, 0, 0)
}
.NodePart Text {
-fx-fill: white;
-fx-font-size: 15pt;
-fx-font-weight: bold;
}
For a complete example, the only things missing are:
- A ContentPartFactory
- A Module defining all bindings
- An Application for set-up
For which examples can be found in the MVC Logo example:
- o.e.g4.mvc.examples.logo.parts.FXLogoContentPartFactory
- o.e.g4.mvc.examples.logo.MvcLogoExampleModule
- o.e.g4.mvc.examples.logo.MvcLogoExample
If you have any more questions, feel free to ask
Best regards,
Matthias
|
|
| |
Re: GEF4 model part, how to draw a roundedrectangle with text [message #1680155 is a reply to message #1674231] |
Mon, 16 March 2015 22:30 |
|
Hi Frank,
Quote:this gives some more light. Thanks a lot!
You're welcome
Quote:1. How can this text be made editable in the Node?
Currently, GEF4 does not provide direct support for "direct editing", i.e. replacing the label with a text field with a click, and committing the change with <Enter> after typing. However, this functionality can be implemented completely within the NodeContentPart, for example, by providing two methods: switchToEditMode() and commitEditChanges(). The first of which replaces the label with a text field and the second one writes the text from the text field to the label and replaces the text field with that label.
public void switchToEditMode() {
if (!isEditing) {
getVisual().getChildren().remove(1);
getVisual().getChildren().add(new Group(editText));
editText.setText(text.getText());
isEditing = true;
}
}
public void commitEditChanges() {
if (isEditing) {
text.setText(editText.getText());
getVisual().getChildren().remove(1);
getVisual().getChildren().add(new Group(text));
isEditing = false;
}
}
You can use JavaFX event handlers to call those methods on double click and <Enter>, respectively, as follows:
// make label editable on double-click
text.setOnMouseClicked(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
if (event.getClickCount() == 2) {
switchToEditMode();
}
}
});
// commit edit changes on <Return>
editText.setOnKeyPressed(new EventHandler<KeyEvent>() {
@Override
public void handle(KeyEvent event) {
if (KeyCode.ENTER.equals(event.getCode())) {
commitEditChanges();
}
}
});
When this is implemented, you can edit the text of the nodes, but the changes will not be persisted to the underlying model, i.e. when the editor reloads the model, the changes will be lost. Therefore, the next thing on the list is writing the changes to the underlying model. This can be done within commitEditChanges().
Now, let's take a look at the mechanism GEF4 uses for interactions: All interactions are carried out by tools and policies. The tools are translating interaction gestures into policy calls. The currently provided tools with corresponding policies are:
- FXClickDragTool: AbstractFXClickPolicy, AbstractFXDragPolicy (mouse press/drag/release)
- FXHoverTool: AbstractFXHoverPolicy (mouse hover/move)
- FXPinchSpreadTool: AbstractFXPinchSpreadPolicy (touchpad pinch/spread)
- FXScrollTool: AbstractFXScrollPolicy (mouse wheel)
- FXTypeTool: AbstractFXTypePolicy (keyboard)
Therefore, the JavaFX event handlers can be replaced by corresponding policies: An EditNodeLabelOnDoubleClickPolicy which extends the AbstractFXClickPolicy and an ExitEditingNodeLabelOnEnterPolicy which extends AbstractFXTypePolicy. The policies need to be provided by the NodeContentPart via an adapter mechanism so that they can be found by the tools. This can be configured as bindings in the Module of the application:
@Override
protected void configure() {
super.configure();
bindIContentPartFactory();
bindNodeContentPartAdapters(AdapterMaps.getAdapterMapBinder(binder(),
NodeContentPart.class));
}
protected void bindNodeContentPartAdapters(
MapBinder<AdapterKey<?>, Object> adapterMapBinder) {
// edit node label policies
adapterMapBinder.addBinding(
AdapterKey.get(FXClickDragTool.CLICK_TOOL_POLICY_KEY,
"EditNodeLabelOnDoubleClickPolicy")).to(
EditNodeLabelOnDoubleClickPolicy.class);
adapterMapBinder.addBinding(
AdapterKey.get(FXTypeTool.TOOL_POLICY_KEY,
"ExitEditingNodeLabelOnEnterPolicy")).to(
ExitEditingNodeLabelOnEnterPolicy.class);
}
Additionally, the NodeContentPart needs to expose the visuals that should be sensitive for events by overriding the registerAtVisualPartMap() and unregisterFromVisualPartMap() methods accordingly.
I attached a toy example which covers the Module, ContentPartFactory, NodeContentPart, and editing the node label (without writing the change to the underlying model, though).
Quote:2. How can I apply a fixed layout.
You have multiple options for a fixed layout:
- You can use the JavaFX layout panes if your fixed layout can be implemented this way.
- You can compute the layout yourself within the parent VisualPart of the to-be-layouted parts.
- You can use the layout algorithms provided within GEF4 Layout, although you would have to compute a layout model based on your data model for this.
- If your application is only displaying graph structures, you can use GEF4 Zest.FX as a basis which uses GEF4 Graph as contents and integrates GEF4 Layout already. Moreover, it is currently making great progress!
Quote:I want to be able to change the sequence of nodes in the model.
This can be accomplished by implementing a MoveNodeOnDragPolicy (extending AbstractFXDragPolicy). Not every visual change needs to be transferred to the model. That's why the MoveNodeOnDragPolicy can adjust the visual position during drag and only commit the changes on release. For generating feedback similar to your example, the ContentPart could provide a feedback state which would only render the grey outline. However, you could also just render the full node.
Quote:Do you have some pointers for me?
The documentation on the Eclipse Wiki [1] is still incomplete, nonetheless the presentations/articles/posts referenced there are quite nice and definitely worth a look. Additionally, I would recommend looking at the existing GEF4 examples: o.e.g4.mvc.examples.logo [2] and o.e.g4.zest.examples.graph [3].
[1] wiki.eclipse.org/GEF/GEF4
[2] git.eclipse.org/c/gef/org.eclipse.gef4.git/tree/org.eclipse.gef4.mvc.examples.logo
[3] git.eclipse.org/c/gef/org.eclipse.gef4.git/tree/org.eclipse.gef4.zest.examples.graph
Best regards,
Matthias
-
Attachment: nodes.zip
(Size: 4.63KB, Downloaded 196 times)
|
|
|
Re: GEF4 model part, how to draw a roundedrectangle with text [message #1682505 is a reply to message #1680155] |
Tue, 17 March 2015 19:17 |
Frank Benoit Messages: 179 Registered: July 2009 |
Senior Member |
|
|
Hi Matthias,
thanks for the great help and the detailed information!
Today I am looking at the (1) editing text.
With your example code, i can now double click and edit.
Storing to the model and adding cancelation via 'ESC' is trivial now.
I see 2 problematic behaviors...
When I double click, it is needed to again click into the TextField or press 'TAB'.
By pressing TAB, I get the whole text selected. Then i can easily decide to start typing and replace remaining text, or press 'END' and append, ...
1.
I tried to set the focus on the TextField in switchToEditMode()
But this seems to lead to a situation where the focus is not handled correctly:
2.
If, while in edit mode, I click on other nodes, the one in edit mode does not get the key pressed event. Hence it stays with the edit text.
If I click in the TextField again and press ENTER/ESC it is working. (implementation with policies)
In that case, i need first to click onto the surrounding rectangle, so the node get selection, then press ESC.
3.
Beside that, a questions about the adapter role.
adapterMapBinder
.addBinding( AdapterKey.get(FXClickDragTool.CLICK_TOOL_POLICY_KEY, "EditNodeLabelOnDoubleClickPolicy"))
.to( EditNodeLabelOnDoubleClickPolicy.class);
Role: "EditNodeLabelOnDoubleClickPolicy"
Do I understand correctly, that this role here is just needed to have multple bindings for FXClickDragTool.CLICK_TOOL_POLICY_KEY (vs. replacing a existing one). So the value of that string was not important, only its uniqueness?
thx
Frank
|
|
|
Re: GEF4 model part, how to draw a roundedrectangle with text [message #1684260 is a reply to message #1682505] |
Wed, 18 March 2015 10:51 |
|
Hi Frank,
currently, I have not much time, therefore, I will give you a quick response now, and elaborate on that later, if you have more questions.
1) For the editing to work as expected (with focus and not leaving the rectangle), you need to add some JavaFX-specific bits, which I left out in my explanations, but it is actually included in the example attached to my previous post. So if you look at the NodeContentPart there, you will find some differences to the code examples I inlined in the post.
First of all, the Text and TextField have to be wrapped in Groups, so that their bounds-in-parent are used for layouting by the StackPane. This will ensure, that the nodes are always stacked correctly, i.e. sharing the same center point. Additionally, the rectangle needs to be updated when the size of the Text or the TextField is changed. Furthermore, you can requestFocus() and selectAll() on the TextField as soon as its size changes, so that you can start typing directly after double clicking.
2) In GEF4 the keyboard events are send to the focus part, which is set in the FocusModel. Per default, a part is focused when it is clicked, via the FXFocusAndSelectOnClickPolicy. I would recommend disabling the TextField when the NodeContentPart loses focus and enabling it again when the part gains focus. This can be implemented as a PropertyChangeListener on the FocusModel, which can be retrieved via getViewer().getAdapter(FocusModel.class).
On the other hand, not all interactions have to be implemented using policies. Especially, when you encounter that the GEF4 default tools and policies are not sufficient, or cumbersome to use, because the tools and policies are still in an early stage.
3) Yes, you hit the nail on the head. The other policy that uses the FXClickDragTool.CLICK_TOOL_POLICY_KEY is the FXFocusAndSelectOnClickPolicy. Forgetting to assign an adapter role is one of my recurring mistakes
Best regards,
Matthias
|
|
| |
Re: GEF4 model part, how to draw a roundedrectangle with text [message #1690272 is a reply to message #1688739] |
Thu, 26 March 2015 09:28 |
|
Hi Frank,
GEF4 Graph is not the layout model. GEF4 Zest uses GEF4 Graph as its "data model" and provides an implementation of the layout interfaces defined within GEF4 Layout for that data model. Therefore, when using GEF4 Zest, you have to build a Graph from your custom data model to pass it along to GEF4 Zest. However, you could use GEF4 Layout without using GEF4 Zest. Then you would have to provide an implementation of the layout interfaces for your custom data model, i.e. implement the layout integration yourself.
To transition from your custom data model to GEF4 Graph, you can use the JFace API provided within o.e.g4.zest.fx.ui.jface.
Best regards,
Matthias
|
|
|
Goto Forum:
Current Time: Tue Sep 24 22:25:08 GMT 2024
Powered by FUDForum. Page generated in 0.04642 seconds
|