Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » GEF » GEF4 model part, how to draw a roundedrectangle with text
GEF4 model part, how to draw a roundedrectangle with text [message #1669293] Thu, 12 March 2015 21:15 Go to next message
Frank Benoit is currently offline Frank BenoitFriend
Messages: 179
Registered: July 2009
Senior Member
Hi

I want to try my first steps in GEF4.
Now my model element, has a part class that extends AbstractFXContentPart<???>

And it should be drawn as rounded rectangle containing a text (attribute of my model element).

How could an impl look like?

There is RoundedRectangle from gef4.geometry, but it has no Text. javafc.scene.text.Text is there.

Can they be combined?

public class NodePart extends AbstractFXContentPart<???> {

	@Override
	protected ??? createVisual() {
		// how to implement?
	}
	@Override
	protected void doRefreshVisual(??? visual) {
		// nothing for now
	}
}


thx Frank



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 Go to previous messageGo to next message
Matthias Wienand is currently offline Matthias WienandFriend
Messages: 230
Registered: March 2015
Senior Member
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 Smile

Best regards,
Matthias
Re: GEF4 model part, how to draw a roundedrectangle with text [message #1674231 is a reply to message #1670729] Sat, 14 March 2015 17:16 Go to previous messageGo to next message
Frank Benoit is currently offline Frank BenoitFriend
Messages: 179
Registered: July 2009
Senior Member
Hi Matthias,

this gives some more light. Thanks a lot!

In fact I have some more questions:

1. How can this text be made editable in the Node?

2. How can I apply a fixed layout.
Like i have model with a tree like structure.
I don't want that the user can change it, it is fixed by the sequence of parent-child relations.
So a resulting graph can look like this:

https://docs.oracle.com/javafx/2/scenegraph/img/figure1.png

3.
I want to be able to change the sequence of nodes in the model.
Perhaps you know XmlSpy and its XML schema editor?
Here you can drag an item, and it show in grey the position/layout how this would like, if you drop.
index.php/fa/21183/0/
index.php/fa/21184/0/
index.php/fa/21185/0/

Do you have some pointers for me?

cu
Frank




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 Go to previous messageGo to next message
Matthias Wienand is currently offline Matthias WienandFriend
Messages: 230
Registered: March 2015
Senior Member
Hi Frank,

Quote:
this gives some more light. Thanks a lot!

You're welcome Smile

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:

  1. You can use the JavaFX layout panes if your fixed layout can be implemented this way.
  2. You can compute the layout yourself within the parent VisualPart of the to-be-layouted parts.
  3. 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.
  4. 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! Smile


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 184 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 Go to previous messageGo to next message
Frank Benoit is currently offline Frank BenoitFriend
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.

index.php/fa/21217/0/

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()
editText.requestFocus()


But this seems to lead to a situation where the focus is not handled correctly:
index.php/fa/21218/0/

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 Go to previous messageGo to next message
Matthias Wienand is currently offline Matthias WienandFriend
Messages: 230
Registered: March 2015
Senior Member
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 Wink

Best regards,
Matthias
Re: GEF4 model part, how to draw a roundedrectangle with text [message #1688739 is a reply to message #1684260] Sat, 21 March 2015 15:23 Go to previous messageGo to next message
Frank Benoit is currently offline Frank BenoitFriend
Messages: 179
Registered: July 2009
Senior Member
Hi Matthias,

I noticed the Group in the zipped files, but had no explanation Smile
Now it seems to work. I cancel the editing if the FocusModel changes to another model part.

About this:
Quote:
3. 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.


In the org.eclipse.gef4.zest.examples.graph, a graph is build with Nodes and Edges. You ment this with the "layout model"?

How does this map to a custom data model?

If I build a Graph, where is the link to my model?
Where do i build the graph from the model and keep it updated when the model changes?

thx
Frank


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 Go to previous message
Matthias Wienand is currently offline Matthias WienandFriend
Messages: 230
Registered: March 2015
Senior Member
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
Previous Topic:Asynchronous layouting of zest graph
Next Topic: Selection is lost after element alignment
Goto Forum:
  


Current Time: Fri Mar 29 05:05:25 GMT 2024

Powered by FUDForum. Page generated in 0.03275 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software

Back to the top