Dynamically calculating the position constraints for a figure in a GEF editor layout in relation to another figure

I’ve worked on the implementation of a GEF editor for an EMF-based model, using both the GEF book and vainolo‘s great GEF tutorials. The model consists of several types of nodes and connections, with different layout requirements for the display of their figures in the editor, as follows.

  • “Tokens” are nodes which have a specific order. They should be displayed as graphical nodes, ordered, on the same horizontal level, i.e., in a line, i.e., with the same y coordinate, but a specific x coordinate. These nodes should display the model element’s name (a java.lang.String) in a label. These name Strings can be of different length.
  • “Nodes” are nodes which don’t have a specific order, and have connections to Tokens or other Nodes. Graphically, the should be visualised as graphical nodes, centered above the graphical nodes of the Tokens or Nodes they’re connected with. The value of their x coordinate should be calculated as follows.
    ((x coordinate (rightmost child figure) + width (rightmost child figure)) – (x coordinate (leftmost child figure))) – (width (node figure itself) / 2)
    Vertically they should be placed one “level” (a fixed negative number of pixels, say 50) above the “level” of those of their immediate children that are furthest away from the Token line. These nodes should display the model element’s name (a java.lang.String) in a label. These name Strings can be of different length.

Figure 1 shows an example visualization (created with yEd). The alignments aren’t exact, but you get the idea…

Figure 1: Example graph.

Figure 1: Example graph.

The main issue I with the alignments is that my model – unlike those used in the GEF book and vainolo’s tutorials – does not allow for inclusion of visual data. Which is the way it should be, cf. the following quote from the GEF book.

In general, it is best that the model contain only domain information that is persisted. [...] If you were creating a commercial [...] application, depending on the larger context of the model and application, it isn’t clear that you would include presentation information such as x, y, width, and height in the domain model [...].
(Rubel et al.: 115)

My model provides an annotation mechanism for model elements, so in theory I could use an annotation to persist presentation information for model elements. However, I’m trying to implement the editor as cleanly as possible (and when I say “trying” I mean “struggling”), hence a dynamic solution is needed. Before I turn to that though, let’s have a look at how figures are generally given a position in the editor. Consider the following code snippets, representing the Figure class and the EditPart class for a given Token respectively. Both snippets are missing some methods and calls that are irrelevant for this post.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* @author New Code on the Block
*
*/
public class TokenFigure extends Figure {
 
    private Label label;
    private RectangleFigure rectangle;
 
    public TokenFigure() {
        setLayoutManager(new XYLayout());
        add(new RectangleFigure());
        add(new Label());
    }
 
    @Override
        protected void paintFigure(Graphics graphics) {
        Rectangle r = getBounds().getCopy();
        setConstraint(rectangle, new Rectangle(0, 0, r.width, r.height));
        setConstraint(label, new Rectangle(0, 0, r.width, r.height));
    }
}

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* @author New Code on the Block
*
*/
public class TokenEditPart extends AbstractGraphicalEditPart {
 
    @Override
    protected IFigure createFigure() {
        return new TokenFigure();
    }
 
    @Override
    protected void refreshVisuals() {
        TokenFigure figure = (TokenFigure) getFigure();
        Token model = (Token) getModel();
        GraphEditPart parent = (GraphEditPart) getParent();
        figure.getLabel().setText(model.getName());
        Rectangle layout = new Rectangle([x], [y], [width], [height]);
        parent.setLayoutConstraint(this, figure, layout);
    }
}

The TokenFigure class code is given for the sake of completeness only, the interesting part is the refreshVisuals() method in TokenEditPart, which is called whenever, well, the figure visuals should be refreshed (e.g., after changes in the EditPart‘s model, or its visuals, or when resizing the viewer, etc.). On line 18, a new Rectangle is constructed which represents the actual coordinates the figure will have on the parent EditPart, the rest is fairly self-explanatory. The tricky question is: Where to get the values from? Both the example from the GEF book as well as vainolo simply read in the values persisted in the model object in question. As this isn’t an option for me, I’ll calculate them dynamically. The next section shows how it can be done, but please note that for reasons of space and readability, I’ll only include the solution for one problem (the Tokens’ alignment on one line), and I’m sure my solution is far from perfect, so please add your fixes/corrections in the comments!

Dynamically calculating the position for a GEF figure in relation to another figure

In the context of my editor (cf. description above), there will always be one node with hardcoded coordinates: the first (leftmost) Token. The coordinates of this Token can be used to dynamically calculate the position of the other Tokens (the position of which can ultimately be used to calculate the position of Nodes).

Let’s assume we want to place the graphical node for the first Token at x = 20 (i.e., 20 pixels away from the left hand border of the parent’s EditPart, which is in my case a FreeformLayer), and y = 200 (i.e., 100 pixels away from the upper border, etc.). The size of the graphical node, its width and height, will be calculated from the preferred size of the label containing the Token‘s name. Note that this is a preliminary solution, I’m sure there are better ways to achieve this. In code terms, think of this as being annotated // FIXME.

In order to calculate the coordinates for the figures for the Tokens following the first one, we simply need to find the position of the preceding TokenFigure on the parent figure, and take it from there. This is the part that has actually cost me a good while to figure out. I had already abandoned the idea of calculating positions dynamically, but stumbled upon the solution by pure chance, so there ya go.

To find the position of the preceding Token we need the following objects in our “toolbox” so to speak: the TokenEditPart‘s figure and model, and its parent’s figure and model. The parent model (in this case, Graph) should provide a getter for all model objects that need to be laid out (here, Graph provides a method getTokens()).

The steps needed to calculate the coordinates for any given token but the first are:

  1. Get a List of all Tokens in the Graph.
  2. In this List, find the index for the EditPart‘s model.
  3. Get the model object for the Token coming, in the above List, before the one we’re calculating coordinates for (lastToken).
  4. Get a List of all children of the parent EditPart, and in this list, find the one child EditPart that has lastToken as model (lastTokenEditPart).
  5. Get the constraints for lastTokenEditPart‘s figure from the parent figure’s LayoutManager.
  6. Calculate the position for the current EditPart‘s figure from these constraints.

This, and the calculation of the current EditPart‘s figure’s width and height based on the Label‘s preferred size (see above), will allow you to correctly place the current EditPart‘s figure in relation to its preceding figure. While I only give the solution to one of the problems mentioned above here, the rest are easy enough to deduce. The source code snippet is given below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**
* @author New Code on the Block
*
*/
public class TokenEditPart extends AbstractGraphicalEditPart {
 
    @Override
    protected IFigure createFigure() {
        return new TokenFigure();
    }
 
    @Override
    protected void refreshVisuals() {
        TokenFigure figure = (TokenFigure) getFigure();
        Token model = (Token) getModel();
        GraphEditPart parent = (GraphEditPart) getParent();
        Graph graph = (Graph) parent.getModel();
 
        figure.getLabel().setText(model.getName());
 
        Rectangle layout = calculateLayout(figure, parent, model, graph);
 
        parent.setLayoutConstraint(this, figure, layout);
    }
 
    private Rectangle calculateLayout(TokenFigure figure, GraphEditPart parent, Token model, Graph graph) {
        Rectangle calculatedLayout = null;
        List tokenList = graph.getTokens();
        int indexOfThisTokenInTokenList = tokenList.indexOf(model);
        Dimension labelPreferredSize = figure.getLabel().getPreferredSize();
 
        if (indexOfThisTokenInTokenList != 0) { // If model isn't the first token in the list
            Token lastToken = tokenList.get(indexOfThisTokenInTokenList - 1);
            Rectangle lastTokenFigureConstraints = null;
            IFigure parentFigure = parent.getFigure();
            for (Object ep : parent.getChildren()) {
                if (ep instanceof TokenEditPart && ((TokenEditPart) ep).getModel() == lastToken)
                lastTokenFigureConstraints = (Rectangle) parentFigure.getLayoutManager().getConstraint(((TokenEditPart) ep).getFigure());
            }
            calculatedLayout = new Rectangle(lastTokenFigureConstraints.x + lastTokenFigureConstraints.width + 5,
                                            lastTokenFigureConstraints.y,
                                            labelPreferredSize.width + 4,
                                            20);
        }
        else
            calculatedLayout = new Rectangle(20, 100, labelPreferredSize.width + 4, 20);
 
        return calculatedLayout;
    }
 
}

References

Dan Rubel, Jaime Wren, Eric Clayberg: The Eclipse Graphical Editing Framework (GEF). Boston (Addison-Wesley): 2012. 267 pp. In English. ISBN-13: 978-0-321-71838-9.

 

 

 

다른 그림과 관련하여 GEF 편집기 레이아웃에서 그림의 위치 제한 조건을 동적으로 계산

GEF 책 과 vainolo 의 훌륭한 GEF 튜토리얼 을 모두 사용하여 EMF 기반 모델을 위한 GEF 편집기 구현에 대해 작업했습니다 이 모델은 여러 유형의 노드와 연결로 구성 되며 다음과 같이 편집기에 그림을 표시하기위한 레이아웃 요구 사항 이 다릅니다 .

경축! 아무것도 안하여 에스천사게임즈가 새로운 모습으로 재오픈 하였습니다.
어린이용이며, 설치가 필요없는 브라우저 게임입니다.
https://s1004games.com

  • "토큰"은 특정 순서를 가진 노드입니다. 그것들은 같은 수평 레벨에서, 즉 같은 y 좌표를 가지고 있지만 특정 x 좌표를 가진 순서로 그래픽 노드로 표시되어야합니다. 이 노드는 모델 요소의 이름 ( java.lang.String )을 레이블에 표시해야합니다 이러한 이름 String 은 길이가 다를 수 있습니다.
  • "노드"는 특정 순서가없고 토큰 또는 다른 노드에 연결되어있는 노드 입니다. 그래픽 적으로 토큰 은 그래픽 노드 와 연결되어 있는 토큰 또는 노드 의 그래픽 노드를 중심으로 그래픽 노드로 시각화되어야합니다 x 좌표의 값은 다음과 같이 계산해야합니다.
    ((x 좌표 (가장 오른쪽 자식 그림) + 너비 (가장 오른쪽 자식 그림))) – (x 좌표 (가장 왼쪽 자식 그림))) – (너비 (노드 그림 자체) / 2)
    세로로 하나의 "레벨"(a 토큰 에서 가장 멀리 떨어져있는 직계 자녀의 "레벨"보다 높은 음수의 픽셀 (예 : 50) 고정선. 이 노드는 모델 요소의 이름 ( java.lang.String )을 레이블에 표시해야합니다 이러한 이름 String 은 길이가 다를 수 있습니다.

그림 1은 시각화 예 ( yEd로 작성)를 보여줍니다 정렬은 정확하지 않지만 아이디어는…

그림 1 : 예제 그래프.

그림 1 : 예제 그래프.

내가 정렬에 대한 주요 문제는 GEF 책 및 vainolo의 자습서에서 사용 된 것과 달리 내 모델 이 시각적 데이터를 포함 할 수 없다는 것입니다 . 어느 쪽이되어야합니까, cf. GEF 책의 다음 인용문.

일반적으로 모델에 유지되는 도메인 정보 만 포함하는 것이 가장 좋습니다. [...] 상용 [...] 응용 프로그램을 작성하는 경우 모델 및 응용 프로그램의 더 큰 컨텍스트에 따라 x, y, 너비 및 높이와 같은 프리젠 테이션 정보를 포함하는지 확실하지 않습니다. 도메인 모델에서 [...].
(Rubel et al .: 115)

내 모델은 모델 요소에 대한 주석 메커니즘을 제공하므로 이론적으로 주석을 사용하여 모델 요소에 대한 프레젠테이션 정보를 유지할 수 있습니다. 그러나 가능한 한 깔끔하게 편집기 를 구현 하려고 합니다 (그리고 "시도"라고 말하면 "투쟁"을 의미합니다). 따라서 동적 솔루션이 필요합니다. 그래도 설명하기 전에 그림이 일반적으로 에디터에서 어떻게 위치를 결정하는지 봅시다. 주어진 토큰에 대한 Figure 클래스와 EditPart 클래스를 각각 나타내는 다음 코드 스 니펫을 고려하십시오 두 스 니펫 모두이 게시물과 관련이없는 일부 메소드 및 호출이 누락되었습니다.

1
2
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* @author New Code on the Block
*
*/
public class TokenFigure extends Figure {
 
    private Label label;
    private RectangleFigure rectangle;
 
    public TokenFigure() {
        setLayoutManager(new XYLayout());
        add(new RectangleFigure());
        add(new Label());
    }
 
    @Override
        protected void paintFigure(Graphics graphics) {
        Rectangle r = getBounds().getCopy();
        setConstraint(rectangle, new Rectangle(0, 0, r.width, r.height));
        setConstraint(label, new Rectangle(0, 0, r.width, r.height));
    }
}

 

1
2
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* @author New Code on the Block
*
*/
public class TokenEditPart extends AbstractGraphicalEditPart {
 
    @Override
    protected IFigure createFigure() {
        return new TokenFigure();
    }
 
    @Override
    protected void refreshVisuals() {
        TokenFigure figure = (TokenFigure) getFigure();
        Token model = (Token) getModel();
        GraphEditPart parent = (GraphEditPart) getParent();
        figure.getLabel().setText(model.getName());
        Rectangle layout = new Rectangle([x], [y], [width], [height]);
        parent.setLayoutConstraint(this, figure, layout);
    }
}

TokenFigure의 클래스 코드 만 완전성을 위해 제공되고, 흥미로운 부분이있다 refreshVisuals () 메서드 TokenEditPart , 음, 그림의 비주얼 (갱신해야 할 때마다 호출, 예를 들면, 변화 이후 의 EditPart 의 모델, 또는 시각 자료 또는 뷰어 크기를 조정할 때 등). 18 번째 줄에, 그림이 부모 EditPart 에서 가질 실제 좌표를 나타내는 새로운 Rectangle 이 구성되며 나머지는 설명이 필요 없습니다. 까다로운 질문은 어디에서 가치를 얻는가? GEF 책의 예제와 vainolo는 단순히 해당 모델 객체에 유지 된 값을 읽습니다. 이것은 나를위한 옵션이 아니므로 동적으로 계산합니다 . 다음 섹션에서는이를 수행하는 방법을 보여 주지만 공간과 가독성을 위해 한 가지 문제에 대한 솔루션 ( 한 줄 의 토큰 정렬) 만 포함하고 솔루션이 확실합니다. 완벽하지는 않으므로 의견에 수정 사항 / 수정 사항을 추가하십시오!

다른 그림과 관련하여 GEF 그림의 위치를 ​​동적으로 계산

내 에디터의 맥락에서 (위의 설명 참조) 항상 하드 코딩 된 좌표를 가진 하나의 노드가 있습니다 : 첫 번째 (가장 왼쪽) 토큰 . 이 토큰 의 좌표를 사용하여 다른 토큰 의 위치를 동적으로 계산할 수 있습니다 (이 위치는 궁극적으로 노드 의 위치를 계산하는 데 사용될 수 있음 ).

첫 번째 토큰 의 그래픽 노드를 x = 20 (즉, 부모의 EditPart 왼쪽 테두리에서 20 픽셀 떨어진 경우 FreeformLayer ) 및 y = 200 (즉, 100) 상단 테두리에서 떨어진 픽셀 등). 그래픽 노드의 크기, 너비 및 높이는 토큰 이름이 포함 된 레이블의 기본 크기에서 계산됩니다 이것은 예비 솔루션이므로이를 달성하는 더 좋은 방법이있을 것입니다. 코드 용어로 이것을 주석이 달린 // FIXME 로 생각하십시오 .

첫 번째 토큰 다음 의 토큰 에 대한 그림의 좌표를 계산하려면 부모 그림 에서 이전 토큰 그림 의 위치를 ​​찾아서 가져옵니다. 이것은 실제로 알아내는 동안 실제로 비용이 많이 드는 부분입니다. 나는 위치를 동적으로 계산한다는 아이디어를 이미 포기했지만 순수한 우연히 솔루션을 우연히 발견 했으므로 나중에 갈 수 있습니다.

이전 토큰 의 위치를 ​​찾으려면 TokenEditPart 의 그림 및 모델과 부모의 그림 및 모델과 같이 "도구 상자"에 다음과 같은 객체가 필요합니다 부모 모델 (이 경우 Graph )은 배치해야하는 모든 모델 객체에 대해 getter를 제공해야합니다 (여기서 Graph 는 getTokens () 메소드 제공 ).

주어진 토큰의 좌표를 계산하는 데 필요한 단계이지만 첫 번째 단계는 다음과 같습니다.

  1. 오는 Get 목록 모든 토큰 s를의 그래프 .
  2. 이 목록 에서 EditPart 모델 의 색인을 찾으십시오 .
  3. 좌표를 계산하는 ( lastToken ) 전에 위의 List 에서 토큰 의 모델 객체를 가져 옵니다 .
  4. 오는 Get 목록 부모의 모든 어린이 의 EditPart를 ,이 목록에서 하나의 하위 발견 의 EditPart 가 lastToken 모델로 ( lastTokenEditPart을 ).
  5. 부모 그림의 LayoutManager 에서 lastTokenEditPart 그림 의 제약 조건을 가져옵니다 .
  6. 이 구속 조건으로부터 현재 EditPart 그림 의 위치를 ​​계산하십시오 .

현재의이, 그리고 계산 의 EditPart '에 기초의 그림의 폭과 높이 라벨 의 적절한 사이즈 (위 참조)', 올바르게 현재 배치 할 수 의 EditPart 는 앞의 그림과 관련의 그림. 위에서 언급 한 문제 중 하나에 대해서만 해결책을 제시하지만 나머지는 쉽게 추론 할 수 있습니다. 소스 코드 스 니펫은 다음과 같습니다.

1
2
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**
* @author New Code on the Block
*
*/
public class TokenEditPart extends AbstractGraphicalEditPart {
 
    @Override
    protected IFigure createFigure() {
        return new TokenFigure();
    }
 
    @Override
    protected void refreshVisuals() {
        TokenFigure figure = (TokenFigure) getFigure();
        Token model = (Token) getModel();
        GraphEditPart parent = (GraphEditPart) getParent();
        Graph graph = (Graph) parent.getModel();
 
        figure.getLabel().setText(model.getName());
 
        Rectangle layout = calculateLayout(figure, parent, model, graph);
 
        parent.setLayoutConstraint(this, figure, layout);
    }
 
    private Rectangle calculateLayout(TokenFigure figure, GraphEditPart parent, Token model, Graph graph) {
        Rectangle calculatedLayout = null;
        List tokenList = graph.getTokens();
        int indexOfThisTokenInTokenList = tokenList.indexOf(model);
        Dimension labelPreferredSize = figure.getLabel().getPreferredSize();
 
        if (indexOfThisTokenInTokenList != 0) { // If model isn't the first token in the list
            Token lastToken = tokenList.get(indexOfThisTokenInTokenList - 1);
            Rectangle lastTokenFigureConstraints = null;
            IFigure parentFigure = parent.getFigure();
            for (Object ep : parent.getChildren()) {
                if (ep instanceof TokenEditPart && ((TokenEditPart) ep).getModel() == lastToken)
                lastTokenFigureConstraints = (Rectangle) parentFigure.getLayoutManager().getConstraint(((TokenEditPart) ep).getFigure());
            }
            calculatedLayout = new Rectangle(lastTokenFigureConstraints.x + lastTokenFigureConstraints.width + 5,
                                            lastTokenFigureConstraints.y,
                                            labelPreferredSize.width + 4,
                                            20);
        }
        else
            calculatedLayout = new Rectangle(20, 100, labelPreferredSize.width + 4, 20);
 
        return calculatedLayout;
    }
 
}

참고 문헌

Dan Rubel, Jaime Wren, Eric Clayberg :  Eclipse 그래픽 편집 프레임 워크 (GEF) . 보스턴 (애디슨-웨슬리) : 2012. 267 pp. 영어로. ISBN-13 : 978-0-321-71838-9.

 

 

[출처] http://blog.sdruskat.net/dynamically-calculating-the-position-constraints-for-a-figure-in-a-gef-editor-layout-in-relation-to-another-figure/

 

 

 

본 웹사이트는 광고를 포함하고 있습니다.
광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.
번호 제목 글쓴이 날짜 조회 수
35 [EMF] EMF Tutorial EMF 튜터리얼 file 졸리운_곰 2023.08.23 20
34 Eclipse RAP Tutorial for Beginners - Workbench Application (OLD) file 졸리운_곰 2021.01.30 66
33 Learn Eclipse GMF in 15 minutes file 졸리운_곰 2019.11.27 42
32 [Eclipse] GEF entry series (10, an implementation of the form) file 졸리운_곰 2019.11.25 45
31 GEF Programmer Guide 번역 졸리운_곰 2019.11.25 53
30 Learn Eclipse GMF in 15 minutes file 졸리운_곰 2019.11.20 41
29 RCP 에디터 정리 졸리운_곰 2019.11.20 122
» 다른 그림과 관련하여 GEF 편집기 레이아웃에서 그림의 위치 제한 조건을 동적으로 계산 Dynamically calculating the position constraints for a figure in a GEF editor layout in relation to another figure file 졸리운_곰 2019.11.20 90
27 RCP 등에서 .mf 파일로 다른 프로젝트 익스포트 포함시 라이브러리(메소드)를 찾지 못할 때, Eclipse RCP - cant resolve importing libraries in final build file 졸리운_곰 2019.10.15 181
26 ESE2006-EclipseModelingSymposium15_GMF.pdf file 졸리운_곰 2019.09.21 64
25 GMF_Creation_Review.pdf file 졸리운_곰 2019.09.21 74
24 Eclipse EMF and GMF Tutorial file 졸리운_곰 2019.09.21 46
23 GMF Tutorial/ko file 졸리운_곰 2019.09.20 162
22 Model Driven Architecture approach to domain of graphical editors file 졸리운_곰 2019.09.20 44
21 Single_Sourcing_RAP_RCP_en.pdf file 졸리운_곰 2019.05.15 27
20 Rich client platform 설명 및 배우기 참고 졸리운_곰 2019.05.15 89
19 Rich Ajax Platform, Part 1: 소개 file 졸리운_곰 2019.05.15 126
18 또 하나의 크로스 플랫폼: Eclipse RAP file 졸리운_곰 2019.05.15 143
17 Eclipse 4 RCP 튜토리얼(완료) file 졸리운_곰 2019.05.14 670
16 Updating UI in Eclipse RCP 졸리운_곰 2015.11.07 183
대표 김성준 주소 : 경기 용인 분당수지 U타워 등록번호 : 142-07-27414
통신판매업 신고 : 제2012-용인수지-0185호 출판업 신고 : 수지구청 제 123호 개인정보보호최고책임자 : 김성준 sjkim70@stechstar.com
대표전화 : 010-4589-2193 [fax] 02-6280-1294 COPYRIGHT(C) stechstar.com ALL RIGHTS RESERVED