From d606e6287d69a4183c888bf4147b167c94ebfc83 Mon Sep 17 00:00:00 2001 From: Patrick Ziegler Date: Sat, 25 May 2024 22:12:09 +0200 Subject: [PATCH] Integrate Zest-based examples into our test suite With this change, we instantiate and verify the correctness of the Zest examples as part of our JUnit test suite. Special care needs to be taken, in order to make sure that the examples can be used in the test environment, but also still be executed as standalone applications. This means that those snippets must not create a new Display instance, as one is already created by the test suite and multiple displays are forbidden. The snippet is then instantiated by reflectively calling the main method. Because the snippets block until the shell is closed by the user, a little trick is needed to allow for the tests to be executed. The actual test is scheduled as an asynchronous task before the snippet is created, which is then executed by the call to "Display.readAndDispatch()". For this exact reason has the FileDialog been removed from GraphJFaceSnippet3, as this would block the UI thread prematurely, thus making testing impossible. The example is still functional, as it will instead load the example file from the classpath. In almost all cases, the SpringLayoutAlgorithm of the code snippets has been replaced by a GridLayout, in order to make the test more stable and reproducible. This contribution is done in preparation for the Zest 2.0 migration as a means to increase the test coverage and thus spot any changes that are introduced. --- org.eclipse.zest.core/META-INF/MANIFEST.MF | 2 +- org.eclipse.zest.examples/.classpath | 1 + .../META-INF/MANIFEST.MF | 5 + .../examples/jface/GraphJFaceSnippet1.java | 7 +- .../examples/jface/GraphJFaceSnippet2.java | 8 +- .../examples/jface/GraphJFaceSnippet3.java | 53 +- .../examples/jface/GraphJFaceSnippet4.java | 8 +- .../examples/jface/GraphJFaceSnippet5.java | 8 +- .../examples/jface/GraphJFaceSnippet6.java | 8 +- .../examples/jface/GraphJFaceSnippet7.java | 4 +- .../examples/jface/GraphJFaceSnippet8.java | 21 +- .../zest/examples/swt/AnimationSnippet.java | 7 +- .../zest/examples/swt/CustomLayout.java | 7 +- .../zest/examples/swt/GraphSnippet1.java | 11 +- .../zest/examples/swt/GraphSnippet10.java | 11 +- .../zest/examples/swt/GraphSnippet11.java | 11 +- .../zest/examples/swt/GraphSnippet12.java | 15 +- .../zest/examples/swt/GraphSnippet13.java | 15 +- .../zest/examples/swt/GraphSnippet14.java | 10 +- .../zest/examples/swt/GraphSnippet2.java | 7 +- .../zest/examples/swt/GraphSnippet3.java | 7 +- .../zest/examples/swt/GraphSnippet4.java | 7 +- .../zest/examples/swt/GraphSnippet5.java | 7 +- .../zest/examples/swt/GraphSnippet6.java | 7 +- .../zest/examples/swt/GraphSnippet7.java | 11 +- .../zest/examples/swt/GraphSnippet8.java | 11 +- .../zest/examples/swt/GraphSnippet9.java | 11 +- .../eclipse/zest/examples/swt/HelloWorld.java | 11 +- .../zest/examples/swt/LayoutExample.java | 16 +- .../zest/examples/swt/NestedGraphSnippet.java | 13 +- .../examples/swt/NestedGraphSnippet2.java | 11 +- .../zest/examples/swt/PaintSnippet.java | 11 +- .../zest/examples/swt/ZoomSnippet.java | 13 +- .../eclipse/zest/examples/uml/UMLExample.java | 10 +- org.eclipse.zest.tests/META-INF/MANIFEST.MF | 1 + .../org/eclipse/zest/tests/ZestTestSuite.java | 9 +- .../tests/examples/AbstractGraphTest.java | 442 ++++++++++ .../zest/tests/examples/GraphJFaceTests.java | 257 ++++++ .../zest/tests/examples/GraphSWTTests.java | 757 ++++++++++++++++++ .../zest/tests/examples/GraphUMLTests.java | 81 ++ .../zest/tests/utils/GraphicalRobot.java | 184 +++++ .../org/eclipse/zest/tests/utils/Snippet.java | 40 + .../zest/tests/utils/WidgetVisitor.java | 69 ++ 43 files changed, 2024 insertions(+), 181 deletions(-) create mode 100644 org.eclipse.zest.tests/src/org/eclipse/zest/tests/examples/AbstractGraphTest.java create mode 100644 org.eclipse.zest.tests/src/org/eclipse/zest/tests/examples/GraphJFaceTests.java create mode 100644 org.eclipse.zest.tests/src/org/eclipse/zest/tests/examples/GraphSWTTests.java create mode 100644 org.eclipse.zest.tests/src/org/eclipse/zest/tests/examples/GraphUMLTests.java create mode 100644 org.eclipse.zest.tests/src/org/eclipse/zest/tests/utils/GraphicalRobot.java create mode 100644 org.eclipse.zest.tests/src/org/eclipse/zest/tests/utils/Snippet.java create mode 100644 org.eclipse.zest.tests/src/org/eclipse/zest/tests/utils/WidgetVisitor.java diff --git a/org.eclipse.zest.core/META-INF/MANIFEST.MF b/org.eclipse.zest.core/META-INF/MANIFEST.MF index 705236e67..b022d2466 100644 --- a/org.eclipse.zest.core/META-INF/MANIFEST.MF +++ b/org.eclipse.zest.core/META-INF/MANIFEST.MF @@ -12,6 +12,6 @@ Bundle-ActivationPolicy: lazy Export-Package: org.eclipse.zest.core.viewers, org.eclipse.zest.core.viewers.internal;x-internal:=true, org.eclipse.zest.core.widgets, - org.eclipse.zest.core.widgets.internal;x-internal:=true + org.eclipse.zest.core.widgets.internal;x-friends:="org.eclipse.zest.tests" Bundle-RequiredExecutionEnvironment: JavaSE-17 Automatic-Module-Name: org.eclipse.zest.core diff --git a/org.eclipse.zest.examples/.classpath b/org.eclipse.zest.examples/.classpath index 08e96377e..3b82dfd1f 100644 --- a/org.eclipse.zest.examples/.classpath +++ b/org.eclipse.zest.examples/.classpath @@ -11,5 +11,6 @@ + diff --git a/org.eclipse.zest.examples/META-INF/MANIFEST.MF b/org.eclipse.zest.examples/META-INF/MANIFEST.MF index bb719f75e..e2f366f98 100644 --- a/org.eclipse.zest.examples/META-INF/MANIFEST.MF +++ b/org.eclipse.zest.examples/META-INF/MANIFEST.MF @@ -3,6 +3,9 @@ Bundle-ManifestVersion: 2 Bundle-Name: %Plugin.name Bundle-SymbolicName: org.eclipse.zest.examples Bundle-Version: 3.17.0.qualifier +Export-Package: org.eclipse.zest.examples.jface;x-friends:="org.eclipse.zest.tests", + org.eclipse.zest.examples.swt;x-friends:="org.eclipse.zest.tests", + org.eclipse.zest.examples.uml;x-friends:="org.eclipse.zest.tests" Bundle-Localization: plugin Require-Bundle: org.eclipse.ui, org.eclipse.core.runtime, @@ -18,4 +21,6 @@ Require-Bundle: org.eclipse.ui, Bundle-RequiredExecutionEnvironment: JavaSE-17 Bundle-Vendor: %Plugin.providerName Automatic-Module-Name: org.eclipse.zest.examples +Bundle-ClassPath: icons/, + . diff --git a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/jface/GraphJFaceSnippet1.java b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/jface/GraphJFaceSnippet1.java index e9e2fffe8..6c58e7c84 100644 --- a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/jface/GraphJFaceSnippet1.java +++ b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/jface/GraphJFaceSnippet1.java @@ -1,5 +1,6 @@ /******************************************************************************* - * Copyright 2005-2007, CHISEL Group, University of Victoria, Victoria, BC, Canada. + * Copyright 2005-2007, 2024, CHISEL Group, University of Victoria, Victoria, + * BC, Canada and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -106,8 +107,8 @@ public String getText(Object element) { * @param args */ public static void main(String[] args) { - Display d = new Display(); - Shell shell = new Shell(d); + Shell shell = new Shell(); + Display d = shell.getDisplay(); shell.setLayout(new FillLayout(SWT.VERTICAL)); shell.setSize(400, 400); Button button = new Button(shell, SWT.PUSH); diff --git a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/jface/GraphJFaceSnippet2.java b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/jface/GraphJFaceSnippet2.java index de1db9565..cf63a26ac 100644 --- a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/jface/GraphJFaceSnippet2.java +++ b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/jface/GraphJFaceSnippet2.java @@ -1,6 +1,6 @@ /******************************************************************************* - * Copyright 2005-2007, CHISEL Group, University of Victoria, Victoria, BC, - * Canada. + * Copyright 2005-2007, 2024, CHISEL Group, University of Victoria, Victoria, + * BC, Canada and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -109,8 +109,8 @@ public String getText(Object element) { * @param args */ public static void main(String[] args) { - Display d = new Display(); - Shell shell = new Shell(d); + Shell shell = new Shell(); + Display d = shell.getDisplay(); shell.setText("GraphJFaceSnippet2"); shell.setLayout(new FillLayout(SWT.VERTICAL)); shell.setSize(400, 400); diff --git a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/jface/GraphJFaceSnippet3.java b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/jface/GraphJFaceSnippet3.java index a0c9ae02a..4bd1b971c 100644 --- a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/jface/GraphJFaceSnippet3.java +++ b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/jface/GraphJFaceSnippet3.java @@ -1,6 +1,6 @@ /******************************************************************************* - * Copyright 2005-2007, CHISEL Group, University of Victoria, Victoria, BC, - * Canada. + * Copyright 2005-2007, 2024, CHISEL Group, University of Victoria, Victoria, + * BC, Canada and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -13,9 +13,9 @@ package org.eclipse.zest.examples.jface; import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; @@ -23,7 +23,6 @@ import org.eclipse.swt.SWT; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.FileDialog; import org.eclipse.swt.widgets.Shell; import org.eclipse.jface.viewers.LabelProvider; @@ -46,21 +45,6 @@ */ public class GraphJFaceSnippet3 { - public static final String GRAPH = """ - a calls b - a calls c - b calld d - b calls e - c calls f - c calls g - d calls h - d calls i - e calls j - e calls k - f calls l - f calls m - """; //$NON-NLS-1$ - static class SimpleGraphContentProvider implements IGraphContentProvider { private StringTokenizer graph; @@ -106,24 +90,15 @@ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { } + static GraphViewer viewer = null; + public static void main(String[] args) throws IOException { - Display display = new Display(); - Shell shell = new Shell(display); + Shell shell = new Shell(); + Display display = shell.getDisplay(); shell.setText("Simple Graph File Format"); //$NON-NLS-1$ - FileDialog dialog = new FileDialog(shell, SWT.OPEN); - dialog.setFilterNames(new String[] { "Simple Graph Files (*.sgf)", "All Files (*.*)" }); //$NON-NLS-1$ //$NON-NLS-2$ - dialog.setFilterExtensions(new String[] { "*.sgf", "*.*" }); // Windows wild cards //$NON-NLS-1$ //$NON-NLS-2$ - - String directory = System.getProperty("user.dir") + "/src/org/eclipse/zest/tests/jface/SimpleGraph.sgf"; // eclipse/zest/examples/jface/"; //$NON-NLS-1$ //$NON-NLS-2$ - System.out.println(directory); - dialog.setFilterPath(directory); - // dialog.setFilterPath(System.getProperty("user.dir") + - // "src/org/eclipse/zest/examples/jface/"); //Windows path - shell.setLayout(new FillLayout(SWT.VERTICAL)); shell.setSize(400, 400); - GraphViewer viewer = null; viewer = new GraphViewer(shell, SWT.NONE); viewer.setContentProvider(new SimpleGraphContentProvider()); @@ -131,14 +106,9 @@ public static void main(String[] args) throws IOException { viewer.setLayoutAlgorithm(new RadialLayoutAlgorithm(LayoutStyles.NO_LAYOUT_NODE_RESIZING)); shell.open(); - String fileName = dialog.open(); - - if (fileName == null) { - // use the sample graph - viewer.setInput(GRAPH); - } else { - FileReader fileReader = new FileReader(new File(fileName)); - BufferedReader bufferedReader = new BufferedReader(fileReader); + + try (InputStream is = GraphJFaceSnippet3.class.getResourceAsStream("SimpleGraph.sgf")) { //$NON-NLS-1$ + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is)); StringBuilder stringBuffer = new StringBuilder(); while (bufferedReader.ready()) { stringBuffer.append(bufferedReader.readLine() + "\n"); //$NON-NLS-1$ @@ -151,6 +121,5 @@ public static void main(String[] args) throws IOException { display.sleep(); } } - display.dispose(); } } diff --git a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/jface/GraphJFaceSnippet4.java b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/jface/GraphJFaceSnippet4.java index ed8ad9d48..60a467096 100644 --- a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/jface/GraphJFaceSnippet4.java +++ b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/jface/GraphJFaceSnippet4.java @@ -1,6 +1,6 @@ /******************************************************************************* - * Copyright 2005-2007, CHISEL Group, University of Victoria, Victoria, BC, - * Canada. + * Copyright 2005-2007, 2024, CHISEL Group, University of Victoria, Victoria, + * BC, Canada and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -100,8 +100,8 @@ public String getText(Object element) { * @param args */ public static void main(String[] args) { - Display d = new Display(); - Shell shell = new Shell(d); + Shell shell = new Shell(); + Display d = shell.getDisplay(); shell.setText("GraphJFaceSnippet2"); shell.setLayout(new FillLayout(SWT.VERTICAL)); shell.setSize(400, 400); diff --git a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/jface/GraphJFaceSnippet5.java b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/jface/GraphJFaceSnippet5.java index 2e75682a7..b8c8c76aa 100644 --- a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/jface/GraphJFaceSnippet5.java +++ b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/jface/GraphJFaceSnippet5.java @@ -1,6 +1,6 @@ /******************************************************************************* - * Copyright 2005-2007, CHISEL Group, University of Victoria, Victoria, BC, - * Canada. + * Copyright 2005-2007, 2024, CHISEL Group, University of Victoria, Victoria, + * BC, Canada and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -113,8 +113,8 @@ public Image getImage(Object element) { * @param args */ public static void main(String[] args) { - Display d = new Display(); - Shell shell = new Shell(d); + Shell shell = new Shell(); + Display d = shell.getDisplay(); shell.setText("GraphJFaceSnippet2"); shell.setLayout(new FillLayout(SWT.VERTICAL)); shell.setSize(400, 400); diff --git a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/jface/GraphJFaceSnippet6.java b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/jface/GraphJFaceSnippet6.java index 2878646e1..6c0d675ff 100644 --- a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/jface/GraphJFaceSnippet6.java +++ b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/jface/GraphJFaceSnippet6.java @@ -1,6 +1,6 @@ /******************************************************************************* - * Copyright 2005-2007, CHISEL Group, University of Victoria, Victoria, BC, - * Canada. + * Copyright 2005-2007, 2024, CHISEL Group, University of Victoria, Victoria, + * BC, Canada and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -126,8 +126,8 @@ public String getText(Object element) { * @param args */ public static void main(String[] args) { - Display d = new Display(); - Shell shell = new Shell(d); + Shell shell = new Shell(); + Display d = shell.getDisplay(); shell.setText("GraphJFaceSnippet2"); //$NON-NLS-1$ shell.setLayout(new FillLayout(SWT.VERTICAL)); shell.setSize(400, 400); diff --git a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/jface/GraphJFaceSnippet7.java b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/jface/GraphJFaceSnippet7.java index 6231e095b..77798b824 100644 --- a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/jface/GraphJFaceSnippet7.java +++ b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/jface/GraphJFaceSnippet7.java @@ -173,8 +173,8 @@ public void dispose() { * @param args */ public static void main(String[] args) { - Display d = new Display(); - Shell shell = new Shell(d); + Shell shell = new Shell(); + Display d = shell.getDisplay(); shell.setText("GraphJFaceSnippet2"); //$NON-NLS-1$ shell.setLayout(new FillLayout(SWT.VERTICAL)); shell.setSize(400, 400); diff --git a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/jface/GraphJFaceSnippet8.java b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/jface/GraphJFaceSnippet8.java index 9c6cea026..2e94191cd 100644 --- a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/jface/GraphJFaceSnippet8.java +++ b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/jface/GraphJFaceSnippet8.java @@ -1,9 +1,12 @@ /******************************************************************************* - * Copyright (c) 2011 Fabian Steeg. All rights reserved. This program and - * the accompanying materials are made available under the terms of the Eclipse - * Public License v1.0 which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - *

+ * Copyright (c) 2011, 2024 Fabian Steeg and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * * Contributors: Fabian Steeg - initial implementation *******************************************************************************/ package org.eclipse.zest.examples.jface; @@ -17,6 +20,7 @@ import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.Viewer; +import org.eclipse.zest.core.viewers.EntityConnectionData; import org.eclipse.zest.core.viewers.GraphViewer; import org.eclipse.zest.core.viewers.IGraphEntityContentProvider; import org.eclipse.zest.core.viewers.ISelfStyleProvider; @@ -81,6 +85,9 @@ public Image getImage(Object element) { @Override public String getText(Object element) { + if (element instanceof EntityConnectionData) { + return ""; //$NON-NLS-1$ + } return element.toString(); } @@ -115,8 +122,8 @@ public void selfStyleNode(Object element, GraphNode node) { static GraphViewer viewer = null; public static void main(String[] args) { - Display d = new Display(); - Shell shell = new Shell(d); + Shell shell = new Shell(); + Display d = shell.getDisplay(); shell.setLayout(new FillLayout(SWT.VERTICAL)); shell.setSize(400, 400); viewer = new GraphViewer(shell, SWT.NONE); diff --git a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/AnimationSnippet.java b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/AnimationSnippet.java index 23c65b316..7a8cc14e5 100644 --- a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/AnimationSnippet.java +++ b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/AnimationSnippet.java @@ -23,11 +23,12 @@ * */ public class AnimationSnippet { + private static Graph g; public static void main(String[] args) { - Display d = new Display(); - final Shell shell = new Shell(d); + final Shell shell = new Shell(); + Display d = shell.getDisplay(); shell.setText("Animation Example"); shell.setLayout(new FillLayout(SWT.VERTICAL)); shell.setSize(400, 400); @@ -35,7 +36,7 @@ public static void main(String[] args) { Button b = new Button(shell, SWT.PUSH); b.setText("Animate"); - final Graph g = new Graph(shell, SWT.NONE); + g = new Graph(shell, SWT.NONE); final GraphNode n = new GraphNode(g, SWT.NONE, "Paper"); final GraphNode n2 = new GraphNode(g, SWT.NONE, "Rock"); diff --git a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/CustomLayout.java b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/CustomLayout.java index 7f005c46c..793225902 100644 --- a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/CustomLayout.java +++ b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/CustomLayout.java @@ -22,15 +22,16 @@ * */ public class CustomLayout { + private static Graph g; public static void main(String[] args) { - Display d = new Display(); - Shell shell = new Shell(d); + Shell shell = new Shell(); + Display d = shell.getDisplay(); shell.setText("Custom Layout Example"); shell.setLayout(new FillLayout()); shell.setSize(400, 400); - Graph g = new Graph(shell, SWT.NONE); + g = new Graph(shell, SWT.NONE); GraphNode n = new GraphNode(g, SWT.NONE, "Paper"); GraphNode n2 = new GraphNode(g, SWT.NONE, "Rock"); diff --git a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet1.java b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet1.java index 30b6c1dff..4a79f7131 100644 --- a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet1.java +++ b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet1.java @@ -1,6 +1,6 @@ /******************************************************************************* - * Copyright 2005-2007, CHISEL Group, University of Victoria, Victoria, BC, - * Canada. + * Copyright 2005-2007, 2024, CHISEL Group, University of Victoria, Victoria, + * BC, Canada and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -38,15 +38,16 @@ * */ public class GraphSnippet1 { + private static Graph g; public static void main(String[] args) { - Display d = new Display(); - Shell shell = new Shell(d); + Shell shell = new Shell(); + Display d = shell.getDisplay(); shell.setText("GraphSnippet1"); shell.setLayout(new FillLayout()); shell.setSize(400, 400); - Graph g = new Graph(shell, SWT.NONE); + g = new Graph(shell, SWT.NONE); GraphNode n = new GraphNode(g, SWT.NONE, "Paper"); GraphNode n2 = new GraphNode(g, SWT.NONE, "Rock"); diff --git a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet10.java b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet10.java index bd819f033..f601e6a7d 100644 --- a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet10.java +++ b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet10.java @@ -1,6 +1,6 @@ /******************************************************************************* - * Copyright 2005-2007, CHISEL Group, University of Victoria, Victoria, BC, - * Canada. + * Copyright 2005-2007, 2024, CHISEL Group, University of Victoria, Victoria, + * BC, Canada and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -35,15 +35,16 @@ * */ public class GraphSnippet10 { + private static Graph g; public static void main(String[] args) { - Display d = new Display(); - Shell shell = new Shell(d); + Shell shell = new Shell(); + Display d = shell.getDisplay(); shell.setText("GraphSnippet1"); shell.setLayout(new FillLayout()); shell.setSize(400, 400); - final Graph g = new Graph(shell, SWT.NONE); + g = new Graph(shell, SWT.NONE); GraphNode n = new GraphNode(g, SWT.NONE, "Paper"); n.setBorderColor(org.eclipse.draw2d.ColorConstants.yellow); diff --git a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet11.java b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet11.java index d2e2f4319..dee6bbdb3 100644 --- a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet11.java +++ b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet11.java @@ -1,6 +1,6 @@ /******************************************************************************* - * Copyright 2005-2007, CHISEL Group, University of Victoria, Victoria, BC, - * Canada. + * Copyright 2005-2007, 2024, CHISEL Group, University of Victoria, Victoria, + * BC, Canada and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -34,6 +34,7 @@ * */ public class GraphSnippet11 { + private static Graph g; public static void createConnection(Graph g, GraphNode n1, GraphNode n2, Color color, int curve) { GraphConnection connection = new GraphConnection(g, SWT.NONE, n1, n2); @@ -43,13 +44,13 @@ public static void createConnection(Graph g, GraphNode n1, GraphNode n2, Color c } public static void main(String[] args) { - Display d = new Display(); - Shell shell = new Shell(d); + Shell shell = new Shell(); + Display d = shell.getDisplay(); shell.setText("GraphSnippet11"); //$NON-NLS-1$ shell.setLayout(new FillLayout()); shell.setSize(400, 400); - final Graph g = new Graph(shell, SWT.NONE); + g = new Graph(shell, SWT.NONE); GraphNode n = new GraphNode(g, SWT.NONE, "Node 1"); //$NON-NLS-1$ GraphNode n2 = new GraphNode(g, SWT.NONE, "Node 2"); //$NON-NLS-1$ createConnection(g, n, n2, ColorConstants.darkGreen, 20); diff --git a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet12.java b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet12.java index 717cbcd6a..9cfcaec2d 100644 --- a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet12.java +++ b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet12.java @@ -1,6 +1,6 @@ /******************************************************************************* - * Copyright 2005-2007, CHISEL Group, University of Victoria, Victoria, BC, - * Canada. + * Copyright 2005-2007, 2024, CHISEL Group, University of Victoria, Victoria, + * BC, Canada and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -47,6 +47,7 @@ * */ public class GraphSnippet12 { + private static Graph g; public static IFigure createPersonFigure(Image headImage) { Figure person = new Figure(); @@ -101,13 +102,13 @@ public static IFigure createPersonFigure(Image headImage) { } public static void main(String[] args) { - final Display d = new Display(); - Shell shell = new Shell(d); + Shell shell = new Shell(); + final Display d = shell.getDisplay(); shell.setText("GraphSnippet11"); shell.setLayout(new FillLayout()); shell.setSize(400, 400); - final Graph g = new Graph(shell, SWT.NONE); + g = new Graph(shell, SWT.NONE); g.addSelectionListener(new SelectionListener() { @Override @@ -140,8 +141,8 @@ public void widgetDefaultSelected(SelectionEvent e) { } }); - Image zx = new Image(d, "icons/zx.png"); - Image ibull = new Image(d, "icons/ibull.jpg"); + Image zx = new Image(d, GraphSnippet12.class.getResourceAsStream("/zx.png")); + Image ibull = new Image(d, GraphSnippet12.class.getResourceAsStream("/ibull.jpg")); CGraphNode n = new CGraphNode(g, SWT.NONE, createPersonFigure(zx)); CGraphNode n2 = new CGraphNode(g, SWT.NONE, createPersonFigure(ibull)); GraphNode n3 = new GraphNode(g, SWT.NONE, "PDE"); diff --git a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet13.java b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet13.java index 9bffad3d2..ff115d5e7 100644 --- a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet13.java +++ b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet13.java @@ -1,6 +1,6 @@ /******************************************************************************* - * Copyright 2005-2007, CHISEL Group, University of Victoria, Victoria, BC, - * Canada. + * Copyright 2005-2007, 2024, CHISEL Group, University of Victoria, Victoria, + * BC, Canada and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -52,6 +52,7 @@ * */ public class GraphSnippet13 { + private static Graph g; public static IFigure createPersonFigure(Image headImage) { Figure person = new Figure(); @@ -106,13 +107,13 @@ public static IFigure createPersonFigure(Image headImage) { } public static void main(String[] args) { - final Display d = new Display(); - Shell shell = new Shell(d); + Shell shell = new Shell(); + final Display d = shell.getDisplay(); shell.setText("GraphSnippet11"); shell.setLayout(new FillLayout()); shell.setSize(400, 400); - final Graph g = new Graph(shell, SWT.NONE); + g = new Graph(shell, SWT.NONE); g.addSelectionListener(new SelectionListener() { @Override @@ -145,7 +146,7 @@ public void widgetDefaultSelected(SelectionEvent e) { } }); - Image zx = new Image(d, "icons/zxsnow.png"); + Image zx = new Image(d, GraphSnippet13.class.getResourceAsStream("/zxsnow.png")); IFigure tooltip = new Figure(); tooltip.setBorder(new MarginBorder(5, 5, 5, 5)); FlowLayout layout = new FlowLayout(false); @@ -156,7 +157,7 @@ public void widgetDefaultSelected(SelectionEvent e) { tooltip.add(new Label("Name: " + "Chris Aniszczyk")); tooltip.add(new Label("Location: " + "Austin, Texas")); - Image ibull = new Image(d, "icons/ibull.jpg"); + Image ibull = new Image(d, GraphSnippet13.class.getResourceAsStream("/ibull.jpg")); GraphContainer c1 = new GraphContainer(g, SWT.NONE); c1.setText("Canada"); GraphContainer c2 = new GraphContainer(g, SWT.NONE); diff --git a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet14.java b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet14.java index 551241c28..ba420b0a5 100644 --- a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet14.java +++ b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet14.java @@ -1,5 +1,6 @@ /******************************************************************************* - * Copyright 2023, Sebastian Hollersbacher. + * Copyright 2023, 2024 Sebastian Hollersbacher and others. + * * All rights reserved. This program and the accompanying materials are made * available under the terms of the Eclipse Public License v2.0 which * accompanies this distribution, and is available at @@ -35,15 +36,16 @@ * */ public class GraphSnippet14 { + private static Graph g; public static void main(String[] args) { - Display d = new Display(); - Shell shell = new Shell(d); + Shell shell = new Shell(); + Display d = shell.getDisplay(); shell.setText("GraphSnippet14"); shell.setLayout(new FillLayout()); shell.setSize(400, 400); - Graph g = new Graph(shell, SWT.NONE, true); // enable hide nodes + g = new Graph(shell, SWT.NONE, true); // enable hide nodes GraphNode n = new GraphNode(g, SWT.NONE, "Paper"); GraphNode n2 = new GraphNode(g, SWT.NONE, "Rock"); diff --git a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet2.java b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet2.java index 134b6f72b..7c6fc41af 100644 --- a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet2.java +++ b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet2.java @@ -35,17 +35,18 @@ * */ public class GraphSnippet2 { + private static Graph g; public static void main(String[] args) { - Display d = new Display(); - Shell shell = new Shell(d); + Shell shell = new Shell(); + Display d = shell.getDisplay(); Image image1 = Display.getDefault().getSystemImage(SWT.ICON_INFORMATION); Image image2 = Display.getDefault().getSystemImage(SWT.ICON_WARNING); Image image3 = Display.getDefault().getSystemImage(SWT.ICON_ERROR); shell.setLayout(new FillLayout()); shell.setSize(400, 400); - Graph g = new Graph(shell, SWT.NONE); + g = new Graph(shell, SWT.NONE); g.setConnectionStyle(ZestStyles.CONNECTIONS_DIRECTED); GraphNode n1 = new GraphNode(g, SWT.NONE, "Information", image1); GraphNode n2 = new GraphNode(g, SWT.NONE, "Warning", image2); diff --git a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet3.java b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet3.java index 943aa9bc5..c497d36d8 100644 --- a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet3.java +++ b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet3.java @@ -36,17 +36,18 @@ * */ public class GraphSnippet3 { + private static Graph g; public static void main(String[] args) { - Display d = new Display(); - Shell shell = new Shell(d); + Shell shell = new Shell(); + Display d = shell.getDisplay(); Image image1 = Display.getDefault().getSystemImage(SWT.ICON_INFORMATION); Image image2 = Display.getDefault().getSystemImage(SWT.ICON_WARNING); Image image3 = Display.getDefault().getSystemImage(SWT.ICON_ERROR); shell.setLayout(new FillLayout()); shell.setSize(400, 400); - Graph g = new Graph(shell, SWT.NONE); + g = new Graph(shell, SWT.NONE); g.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { diff --git a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet4.java b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet4.java index 1f90d3d70..9828b7aa8 100644 --- a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet4.java +++ b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet4.java @@ -36,6 +36,7 @@ * */ public class GraphSnippet4 { + private static Graph g; /** * Merges 2 images so they appear beside each other @@ -61,8 +62,8 @@ public static Image mergeImages(Image image1, Image image2) { * @param args */ public static void main(String[] args) { - Display d = new Display(); - Shell shell = new Shell(d); + Shell shell = new Shell(); + Display d = shell.getDisplay(); shell.setText("Graph Snippet 4"); Image image1 = Display.getDefault().getSystemImage(SWT.ICON_INFORMATION); Image image2 = Display.getDefault().getSystemImage(SWT.ICON_WARNING); @@ -70,7 +71,7 @@ public static void main(String[] args) { shell.setLayout(new FillLayout()); shell.setSize(400, 400); - Graph g = new Graph(shell, SWT.NONE); + g = new Graph(shell, SWT.NONE); g.setConnectionStyle(ZestStyles.CONNECTIONS_DIRECTED); GraphNode n1 = new GraphNode(g, SWT.NONE, "Information", image1); GraphNode n2 = new GraphNode(g, SWT.NONE, "Warning", image2); diff --git a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet5.java b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet5.java index 634cc0f97..172098f55 100644 --- a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet5.java +++ b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet5.java @@ -48,6 +48,7 @@ * */ public class GraphSnippet5 { + private static Graph g; public static final int BACKSPACE = 8; public static final int ENTER = 13; @@ -57,13 +58,13 @@ public class GraphSnippet5 { public static void main(String[] args) { final Map figureListing = new HashMap(); final StringBuffer stringBuffer = new StringBuffer(); - final Display d = new Display(); + final Shell shell = new Shell(); + final Display d = shell.getDisplay(); FontData fontData = d.getSystemFont().getFontData()[0]; fontData.height = 42; final Font font = new Font(d, fontData); - Shell shell = new Shell(d); shell.setText("Graph Snippet 5"); Image image1 = Display.getDefault().getSystemImage(SWT.ICON_INFORMATION); Image image2 = Display.getDefault().getSystemImage(SWT.ICON_WARNING); @@ -71,7 +72,7 @@ public static void main(String[] args) { shell.setLayout(new FillLayout()); shell.setSize(400, 400); - final Graph g = new Graph(shell, SWT.NONE); + g = new Graph(shell, SWT.NONE); g.setConnectionStyle(ZestStyles.CONNECTIONS_DIRECTED); GraphNode n1 = new GraphNode(g, SWT.NONE, "org.eclipse.Information", image1); GraphNode n2 = new GraphNode(g, SWT.NONE, "org.eclipse.Warning", image2); diff --git a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet6.java b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet6.java index e3ad060ac..efed924be 100644 --- a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet6.java +++ b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet6.java @@ -34,13 +34,14 @@ * */ public class GraphSnippet6 { + private static Graph g; /** * @param args */ public static void main(String[] args) { - Display d = new Display(); - Shell shell = new Shell(d); + Shell shell = new Shell(); + Display d = shell.getDisplay(); shell.setText("GraphSnippet6"); Image image1 = Display.getDefault().getSystemImage(SWT.ICON_INFORMATION); Image image2 = Display.getDefault().getSystemImage(SWT.ICON_WARNING); @@ -48,7 +49,7 @@ public static void main(String[] args) { shell.setLayout(new FillLayout()); shell.setSize(800, 800); - Graph g = new Graph(shell, SWT.NONE); + g = new Graph(shell, SWT.NONE); g.setConnectionStyle(ZestStyles.CONNECTIONS_DIRECTED); for (int i = 0; i < 80; i++) { GraphNode n1 = new GraphNode(g, ZestStyles.NODES_HIDE_TEXT | ZestStyles.NODES_FISHEYE, "Information", diff --git a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet7.java b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet7.java index 1cce2ce3f..bb69a8e61 100644 --- a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet7.java +++ b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet7.java @@ -1,6 +1,6 @@ /******************************************************************************* - * Copyright 2005-2007, CHISEL Group, University of Victoria, Victoria, BC, - * Canada. + * Copyright 2005-2007, 2024, CHISEL Group, University of Victoria, Victoria, + * BC, Canada and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -33,18 +33,19 @@ * */ public class GraphSnippet7 { + private static Graph g; /** * @param args */ public static void main(String[] args) { - Display d = new Display(); - Shell shell = new Shell(d); + Shell shell = new Shell(); + Display d = shell.getDisplay(); shell.setText("GraphSnippet7"); shell.setLayout(new FillLayout()); shell.setSize(400, 400); - final Graph g = new Graph(shell, SWT.NONE); + g = new Graph(shell, SWT.NONE); GraphNode n = new GraphNode(g, SWT.NONE, "Paper"); GraphNode n2 = new GraphNode(g, SWT.NONE, "Rock"); diff --git a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet8.java b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet8.java index 2132f0d8b..5e5af234f 100644 --- a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet8.java +++ b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet8.java @@ -1,6 +1,6 @@ /******************************************************************************* - * Copyright 2005-2007, CHISEL Group, University of Victoria, Victoria, BC, - * Canada. + * Copyright 2005-2007, 2024, CHISEL Group, University of Victoria, Victoria, + * BC, Canada and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -35,18 +35,19 @@ * */ public class GraphSnippet8 { + private static Graph graph; /** * @param args */ public static void main(String[] args) { - Display display = new Display(); - Shell shell = new Shell(display); + Shell shell = new Shell(); + Display display = shell.getDisplay(); shell.setText("GraphSnippet8"); //$NON-NLS-1$ shell.setLayout(new FillLayout()); shell.setSize(400, 400); - final Graph graph = new Graph(shell, SWT.NONE); + graph = new Graph(shell, SWT.NONE); GraphNode a = new GraphNode(graph, SWT.NONE, "Root"); //$NON-NLS-1$ GraphNode b = new GraphNode(graph, SWT.NONE, "B"); //$NON-NLS-1$ diff --git a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet9.java b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet9.java index 2deee3b07..0b6e37007 100644 --- a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet9.java +++ b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/GraphSnippet9.java @@ -1,6 +1,6 @@ /******************************************************************************* - * Copyright 2005-2007, CHISEL Group, University of Victoria, Victoria, BC, - * Canada. + * Copyright 2005-2007, 2024, CHISEL Group, University of Victoria, Victoria, + * BC, Canada and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -29,18 +29,19 @@ * */ public class GraphSnippet9 { + private static Graph graph; /** * @param args */ public static void main(String[] args) { - Display display = new Display(); - Shell shell = new Shell(display); + Shell shell = new Shell(); + Display display = shell.getDisplay(); shell.setText("GraphSnippet9"); shell.setLayout(new FillLayout()); shell.setSize(400, 400); - final Graph graph = new Graph(shell, SWT.NONE); + graph = new Graph(shell, SWT.NONE); GraphNode a = new GraphNode(graph, ZestStyles.CONNECTIONS_DIRECTED, "Root"); GraphConnection connection = new GraphConnection(graph, SWT.NONE, a, a); diff --git a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/HelloWorld.java b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/HelloWorld.java index db701b49a..f0fc176ae 100644 --- a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/HelloWorld.java +++ b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/HelloWorld.java @@ -1,6 +1,6 @@ /******************************************************************************* - * Copyright 2005-2007, CHISEL Group, University of Victoria, Victoria, BC, - * Canada. + * Copyright 2005-2007, 2024, CHISEL Group, University of Victoria, Victoria, + * BC, Canada and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -30,18 +30,19 @@ * */ public class HelloWorld { + private static Graph g; /** * @param args */ public static void main(String[] args) { - Display d = new Display(); - Shell shell = new Shell(d); + Shell shell = new Shell(); + Display d = shell.getDisplay(); shell.setText("Hello, World"); shell.setLayout(new FillLayout()); shell.setSize(400, 400); - Graph g = new Graph(shell, SWT.NONE); + g = new Graph(shell, SWT.NONE); GraphNode hello = new GraphNode(g, SWT.NONE, "Hello"); GraphNode world = new GraphNode(g, SWT.NONE, "World"); new GraphConnection(g, SWT.NONE, hello, world); diff --git a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/LayoutExample.java b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/LayoutExample.java index cbce01408..791fba38f 100644 --- a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/LayoutExample.java +++ b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/LayoutExample.java @@ -1,6 +1,6 @@ /******************************************************************************* - * Copyright 2005-2007, CHISEL Group, University of Victoria, Victoria, BC, - * Canada. + * Copyright 2005-2007, 2024, CHISEL Group, University of Victoria, Victoria, + * BC, Canada and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -26,26 +26,28 @@ import org.eclipse.zest.layouts.constraints.BasicEdgeConstraints; /** - * This snippet shows how to use the findFigureAt to get the figure under the - * mouse + * This snippet shows how to use constraints. Nodes are attracted to the + * {@code root} node and repelled by {@code non-root} nodes. The attraction is + * proportional to the number of edges. * * @author Ian Bull * */ public class LayoutExample { + private static Graph g; /** * @param args */ public static void main(String[] args) { // Create the shell - Display d = new Display(); - Shell shell = new Shell(d); + Shell shell = new Shell(); + Display d = shell.getDisplay(); shell.setText("GraphSnippet1"); shell.setLayout(new FillLayout()); shell.setSize(400, 400); - final Graph g = new Graph(shell, SWT.NONE); + g = new Graph(shell, SWT.NONE); GraphNode root = new GraphNode(g, SWT.NONE, "Root"); for (int i = 0; i < 3; i++) { GraphNode n = new GraphNode(g, SWT.NONE, "1 - " + i); diff --git a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/NestedGraphSnippet.java b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/NestedGraphSnippet.java index 3513134e9..778e9f6ce 100644 --- a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/NestedGraphSnippet.java +++ b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/NestedGraphSnippet.java @@ -1,6 +1,6 @@ /******************************************************************************* - * Copyright 2005-2007, CHISEL Group, University of Victoria, Victoria, BC, - * Canada. + * Copyright 2005-2007, 2024, CHISEL Group, University of Victoria, Victoria, + * BC, Canada and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -32,7 +32,7 @@ import org.eclipse.zest.layouts.algorithms.TreeLayoutAlgorithm; public class NestedGraphSnippet { - + private static Graph g; private static Image image1; private static Image classImage; @@ -103,17 +103,16 @@ public static void populateContainer(GraphContainer c, Graph g, int number, bool */ public static void main(String[] args) { // Create the shell - Display d = new Display(); - image1 = new Image(Display.getDefault(), NestedGraphSnippet.class.getResourceAsStream("package_obj.gif")); classImage = new Image(Display.getDefault(), NestedGraphSnippet.class.getResourceAsStream("class_obj.gif")); - Shell shell = new Shell(d); + Shell shell = new Shell(); + Display d = shell.getDisplay(); shell.setText("GraphSnippet1"); shell.setLayout(new FillLayout()); shell.setSize(500, 800); - Graph g = new Graph(shell, SWT.NONE); + g = new Graph(shell, SWT.NONE); createContainer(g); CompositeLayoutAlgorithm compositeLayoutAlgorithm = new CompositeLayoutAlgorithm( diff --git a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/NestedGraphSnippet2.java b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/NestedGraphSnippet2.java index 956c0b813..79abb8e03 100644 --- a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/NestedGraphSnippet2.java +++ b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/NestedGraphSnippet2.java @@ -1,6 +1,6 @@ /******************************************************************************* - * Copyright 2005-2007, CHISEL Group, University of Victoria, Victoria, BC, - * Canada. + * Copyright 2005-2007, 2024, CHISEL Group, University of Victoria, Victoria, + * BC, Canada and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -27,16 +27,17 @@ import org.eclipse.zest.layouts.algorithms.SpringLayoutAlgorithm; public class NestedGraphSnippet2 { + private static Graph g; public static void main(String[] args) { // Create the shell - Display d = new Display(); - Shell shell = new Shell(d); + Shell shell = new Shell(); + Display d = shell.getDisplay(); shell.setText("GraphSnippet1"); shell.setLayout(new FillLayout()); shell.setSize(400, 400); - Graph g = new Graph(shell, SWT.NONE); + g = new Graph(shell, SWT.NONE); /* Machines */ GraphContainer machine1 = new GraphContainer(g, SWT.NONE); diff --git a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/PaintSnippet.java b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/PaintSnippet.java index dcfb6f163..1967ea245 100644 --- a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/PaintSnippet.java +++ b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/PaintSnippet.java @@ -1,6 +1,6 @@ /******************************************************************************* - * Copyright 2005-2007, CHISEL Group, University of Victoria, Victoria, BC, - * Canada. + * Copyright 2005-2007, 2024, CHISEL Group, University of Victoria, Victoria, + * BC, Canada and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -44,14 +44,15 @@ * */ public class PaintSnippet { + private static Graph g; /** * @param args */ public static void main(String[] args) { // Create the shell - final Display d = new Display(); - final Shell shell = new Shell(d); + final Shell shell = new Shell(); + final Display d = shell.getDisplay(); shell.setText("GraphSnippet1"); shell.setLayout(new FillLayout()); shell.setSize(400, 400); @@ -59,7 +60,7 @@ public static void main(String[] args) { Button b = new Button(shell, SWT.PUSH); b.setText("Take Screenshot"); - final Graph g = new Graph(shell, SWT.NONE); + g = new Graph(shell, SWT.NONE); GraphNode n = new GraphNode(g, SWT.NONE, "Paper"); GraphNode n2 = new GraphNode(g, SWT.NONE, "Rock"); diff --git a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/ZoomSnippet.java b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/ZoomSnippet.java index a176f4f28..4cfeab6ab 100644 --- a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/ZoomSnippet.java +++ b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/swt/ZoomSnippet.java @@ -1,6 +1,6 @@ /******************************************************************************* - * Copyright 2005-2007, CHISEL Group, University of Victoria, Victoria, BC, - * Canada. + * Copyright 2005-2007, 2024, CHISEL Group, University of Victoria, Victoria, + * BC, Canada and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -35,7 +35,7 @@ import org.eclipse.zest.layouts.algorithms.TreeLayoutAlgorithm; public class ZoomSnippet { - + private static Graph g; private static Image image1; private static Image classImage; @@ -106,17 +106,16 @@ public static void populateContainer(GraphContainer c, Graph g, int number, bool */ public static void main(String[] args) { // Create the shell - Display d = new Display(); - image1 = new Image(Display.getDefault(), ZoomSnippet.class.getResourceAsStream("package_obj.gif")); classImage = new Image(Display.getDefault(), ZoomSnippet.class.getResourceAsStream("class_obj.gif")); - Shell shell = new Shell(d); + Shell shell = new Shell(); + Display d = shell.getDisplay(); shell.setText("GraphSnippet1"); shell.setLayout(new FillLayout()); shell.setSize(500, 800); - final Graph g = new Graph(shell, SWT.NONE); + g = new Graph(shell, SWT.NONE); createContainer(g); CompositeLayoutAlgorithm compositeLayoutAlgorithm = new CompositeLayoutAlgorithm( diff --git a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/uml/UMLExample.java b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/uml/UMLExample.java index 532da8b68..92308b573 100644 --- a/org.eclipse.zest.examples/src/org/eclipse/zest/examples/uml/UMLExample.java +++ b/org.eclipse.zest.examples/src/org/eclipse/zest/examples/uml/UMLExample.java @@ -1,5 +1,6 @@ /******************************************************************************* - * Copyright 2005-2007, CHISEL Group, University of Victoria, Victoria, BC, Canada. + * Copyright 2005-2007, 2024, CHISEL Group, University of Victoria, Victoria, + * BC, Canada and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -41,6 +42,7 @@ */ public class UMLExample { public static Color classColor = null; + private static Graph g; public static IFigure createClassFigure1(Font classFont, Image classImage, Image publicField, Image privateField) { Label classLabel1 = new Label("Table", classImage); @@ -101,8 +103,8 @@ protected IFigure createFigureForModel() { * @param args */ public static void main(String[] args) { - Display d = new Display(); - Shell shell = new Shell(d); + Shell shell = new Shell(); + Display d = shell.getDisplay(); shell.setLayout(new FillLayout()); shell.setSize(400, 400); classColor = new Color(null, 255, 255, 206); @@ -114,7 +116,7 @@ public static void main(String[] args) { Image publicField = new Image(Display.getDefault(), UMLClassFigure.class.getResourceAsStream("methpub_obj.gif")); - Graph g = new Graph(shell, SWT.NONE); + g = new Graph(shell, SWT.NONE); g.setConnectionStyle(ZestStyles.CONNECTIONS_DIRECTED); GraphContainer c = new GraphContainer(g, SWT.NONE); c.setText("A UML Container"); diff --git a/org.eclipse.zest.tests/META-INF/MANIFEST.MF b/org.eclipse.zest.tests/META-INF/MANIFEST.MF index db2a912d1..7f70cff62 100644 --- a/org.eclipse.zest.tests/META-INF/MANIFEST.MF +++ b/org.eclipse.zest.tests/META-INF/MANIFEST.MF @@ -12,6 +12,7 @@ Require-Bundle: org.eclipse.ui, org.eclipse.core.resources, org.eclipse.jdt.core, org.eclipse.zest.core, + org.eclipse.zest.examples, org.eclipse.zest.layouts, org.junit Bundle-ActivationPolicy: lazy diff --git a/org.eclipse.zest.tests/src/org/eclipse/zest/tests/ZestTestSuite.java b/org.eclipse.zest.tests/src/org/eclipse/zest/tests/ZestTestSuite.java index c9c2a5ece..a9b70439a 100644 --- a/org.eclipse.zest.tests/src/org/eclipse/zest/tests/ZestTestSuite.java +++ b/org.eclipse.zest.tests/src/org/eclipse/zest/tests/ZestTestSuite.java @@ -12,6 +12,10 @@ *******************************************************************************/ package org.eclipse.zest.tests; +import org.eclipse.zest.tests.examples.GraphJFaceTests; +import org.eclipse.zest.tests.examples.GraphSWTTests; +import org.eclipse.zest.tests.examples.GraphUMLTests; + import org.junit.runner.RunWith; import org.junit.runners.Suite; @@ -25,7 +29,10 @@ GraphTests.class, GraphSelectionTests.class, GraphViewerTests.class, - LayoutAlgorithmTest.class + LayoutAlgorithmTest.class, + GraphJFaceTests.class, + GraphSWTTests.class, + GraphUMLTests.class }) public class ZestTestSuite { } diff --git a/org.eclipse.zest.tests/src/org/eclipse/zest/tests/examples/AbstractGraphTest.java b/org.eclipse.zest.tests/src/org/eclipse/zest/tests/examples/AbstractGraphTest.java new file mode 100644 index 000000000..b357c3d6f --- /dev/null +++ b/org.eclipse.zest.tests/src/org/eclipse/zest/tests/examples/AbstractGraphTest.java @@ -0,0 +1,442 @@ +/******************************************************************************* + * Copyright (c) 2024 Patrick Ziegler and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Patrick Ziegler - initial API and implementation + *******************************************************************************/ + +package org.eclipse.zest.tests.examples; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.invoke.MethodType; +import java.lang.reflect.Field; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.Semaphore; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Predicate; + +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Canvas; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.Widget; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.zest.core.widgets.Graph; +import org.eclipse.zest.core.widgets.GraphConnection; +import org.eclipse.zest.core.widgets.GraphNode; +import org.eclipse.zest.core.widgets.IContainer; +import org.eclipse.zest.core.widgets.internal.GraphLabel; +import org.eclipse.zest.layouts.Filter; +import org.eclipse.zest.layouts.LayoutAlgorithm; +import org.eclipse.zest.layouts.LayoutStyles; +import org.eclipse.zest.layouts.algorithms.AbstractLayoutAlgorithm; +import org.eclipse.zest.layouts.algorithms.GridLayoutAlgorithm; +import org.eclipse.zest.layouts.algorithms.SpringLayoutAlgorithm; +import org.eclipse.zest.layouts.dataStructures.InternalNode; +import org.eclipse.zest.layouts.dataStructures.InternalRelationship; +import org.eclipse.zest.tests.utils.GraphicalRobot; +import org.eclipse.zest.tests.utils.Snippet; +import org.eclipse.zest.tests.utils.WidgetVisitor; + +import org.eclipse.draw2d.EventDispatcher; +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.LightweightSystem; +import org.eclipse.draw2d.PolylineConnection; +import org.eclipse.draw2d.PopUpHelper; +import org.eclipse.draw2d.SWTEventDispatcher; +import org.eclipse.draw2d.ToolTipHelper; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.PointList; +import org.eclipse.draw2d.geometry.Rectangle; + +import org.junit.Rule; +import org.junit.rules.TestRule; +import org.junit.runners.model.Statement; + +/** + * Abstract base class for all tests related to the Zest examples. + */ +public abstract class AbstractGraphTest { + protected Graph graph; + protected GraphicalRobot robot; + + @Rule + public TestRule rule = (base, description) -> new Statement() { + @Override + public void evaluate() throws Throwable { + Snippet annotation = description.getAnnotation(Snippet.class); + Objects.requireNonNull(annotation, "Test is missing @Snippet annotation."); //$NON-NLS-1$ + doTest(annotation, base); + } + }; + + /** + * Wrapper method to handle the instantiation of the example class and the + * execution of the unit test. Each example must satisfy the following + * requirements: + *

    + *
  • It must have a static main(String[]) method.
  • + *
  • It must not create a display.
  • + *
  • It must store the created viewer in a static {@code viewer} variable. + *
      + * + * @param clazz The example to instantiate. + * @param statement The test to execute once the example has been created. + * @throws Throwable If the example could not be instantiated. + */ + private void doTest(Snippet annotation, Statement statement) throws Throwable { + Class clazz = annotation.type(); + + Semaphore lock = new Semaphore(0); + AtomicReference throwable = new AtomicReference<>(); + + Lookup lookup = MethodHandles.privateLookupIn(clazz, MethodHandles.lookup()); + MethodType type = MethodType.methodType(void.class, String[].class); + MethodHandle methodHandle = lookup.findStatic(clazz, "main", type); //$NON-NLS-1$ + + // Fail early, otherwise the example might block indefinitely + Assert.isTrue(hasGraph(lookup, annotation), "Graph object not found for " + clazz); //$NON-NLS-1$ + + // The actual test has to be executed asynchronously, so that it is run as part + // of the readAndDispatch() call. Otherwise we end up in a deadlock, as both + // snippet and test run in the UI thread. + Display.getCurrent().asyncExec(() -> { + Shell shell = null; + try { + graph = getGraph(lookup, annotation); + + // Make sure the layout is reproducible + if (graph.getLayoutAlgorithm() instanceof SpringLayoutAlgorithm) { + graph.setLayoutAlgorithm(new GridLayoutAlgorithm(LayoutStyles.NO_LAYOUT_NODE_RESIZING), true); + } + + robot = new GraphicalRobot(graph); + shell = graph.getShell(); + // Wait for layout to be applied + waitEventLoop(0); + // Run the actual test + statement.evaluate(); + } catch (Throwable e) { + throwable.set(e); + } finally { + // Close the snippet + if (shell != null) { + shell.dispose(); + } + lock.release(); + } + }); + + methodHandle.invoke(null); + // Wait for asynchronous test execution + lock.acquire(); + // Propagate any errors + if (throwable.get() != null) { + throw throwable.get(); + } + } + + /** + * Checks whether the example class can be tested. + * + * @param lookup The lookup object on the example class. + * @param snippet The annotation of the executed test. + * @return {@code true}, if the graph instance of the example is accessible. + * @throws ReflectiveOperationException If the graph could not + */ + protected abstract boolean hasGraph(Lookup lookup, Snippet snippet) throws ReflectiveOperationException; + + /** + * Returns the graph instance created by the current Zest example. This instance + * is expected to be stored in a static variable. + * + * @param lookup The lookup object on the example class. + * @param snippet The annotation of the executed test. + * @return The graph object created by the example. + * @throws ReflectiveOperationException If the graph could not + */ + protected abstract Graph getGraph(Lookup lookup, Snippet snippet) throws ReflectiveOperationException; + + /** + * The nodes used by the layout algorithm might be less than the nodes in the + * graph when a {@link Filter} is used. + * + * @return All nodes considers by the current layout algorithm. + */ + protected InternalNode[] getInternalNodes() throws ReflectiveOperationException { + LayoutAlgorithm layoutAlgorithm = graph.getLayoutAlgorithm(); + Field field = AbstractLayoutAlgorithm.class.getDeclaredField("internalNodes"); //$NON-NLS-1$ + boolean isAccessible = field.canAccess(layoutAlgorithm); + try { + field.setAccessible(true); + return (InternalNode[]) field.get(layoutAlgorithm); + } finally { + field.setAccessible(isAccessible); + } + } + + /** + * The connections used by the layout algorithm might be less than the nodes in + * the graph when a {@link Filter} is used. + * + * @return All connections considers by the current layout algorithm. + */ + protected InternalRelationship[] getInternalRelationships() throws ReflectiveOperationException { + LayoutAlgorithm layoutAlgorithm = graph.getLayoutAlgorithm(); + Field field = AbstractLayoutAlgorithm.class.getDeclaredField("internalRelationships"); //$NON-NLS-1$ + boolean isAccessible = field.canAccess(layoutAlgorithm); + try { + field.setAccessible(true); + return (InternalRelationship[]) field.get(layoutAlgorithm); + } finally { + field.setAccessible(isAccessible); + } + } + + /** + * The distance is defined as {@code sqrt(x^2 + y^2) } where {@code x} and + * {@code y} are the coordinates of the vector pointing from the {@code source} + * to the {@code destination} node. + * + * @return The Euclidean length of the given {@code connection}. + */ + protected static double getLength(GraphConnection connection) { + Point c1 = getCenter(connection.getSource()); + Point c2 = getCenter(connection.getDestination()); + int x = c1.x - c2.x; + int y = c1.y - c2.y; + Point vec = new Point(x, y); + return getLength(vec); + } + + /** + * The length of a vector is defined as {@code sqrt(x^2 + y^2) }. + * + * @return The Euclidean length of the given {@code vector}. + */ + protected static double getLength(Point vec) { + return Math.sqrt(vec.x * vec.x + vec.y * vec.y); + } + + /** + * The dot product of two vectors is defined as {@code x1 * x2 + y1 * y2 }. + * + * @return The dot product of the given {@code vectors}. + */ + protected static double getDotProduct(Point vec1, Point vec2) { + return vec1.x * vec2.x + vec1.y * vec2.y; + } + + /** + * Calculates the arc (in degrees) that is spanned by the given graph + * connection. The arc is calculated using the cosine of the vector from the + * start to the end point and the vector from the start to the mid point. + * + * @param connection The arc that is spanned by the given connection. + */ + private static double getArc(PolylineConnection connection) { + PointList points = connection.getPoints(); + Point start = points.getFirstPoint(); + Point center = points.getMidpoint(); + Point end = points.getLastPoint(); + + int x1 = start.x - center.x; + int y1 = start.y - center.y; + Point vec1 = new Point(x1, y1); + + int x2 = start.x - end.x; + int y2 = start.y - end.y; + Point vec2 = new Point(x2, y2); + + double cos = getDotProduct(vec1, vec2) / (getLength(vec1) * getLength(vec2)); + return Math.acos(cos) * 360 / (2 * Math.PI); + } + + /** + * The center is defined as {@code (x + width / 2, y + height / 2)}. + * + * @return The center of the given {@code node}. + */ + protected static Point getCenter(GraphNode node) { + Point location = node.getLocation(); + Dimension size = node.getSize(); + return new Rectangle(location, size).getCenter(); + } + + /** + * Returns the tooltip that is shown for the given figure. The tooltip is + * accessed via reflection by first going through the {@link EventDispatcher}, + * followed by the {@link ToolTipHelper}. + * + * @param figure The node figure beneath the mouse cursor. + * @return The tooltip of the node figure that is currently being shown. + */ + protected static IFigure getToolTip(IFigure figure) throws Throwable { + EventDispatcher eventDispatcher = figure.internalGetEventDispatcher(); + + Lookup lookup1 = MethodHandles.privateLookupIn(SWTEventDispatcher.class, MethodHandles.lookup()); + MethodHandle getter1 = lookup1.findGetter(SWTEventDispatcher.class, "toolTipHelper", ToolTipHelper.class); //$NON-NLS-1$ + ToolTipHelper toolTipHelper = (ToolTipHelper) getter1.invoke(eventDispatcher); + + Lookup lookup2 = MethodHandles.privateLookupIn(PopUpHelper.class, MethodHandles.lookup()); + MethodHandle getter2 = lookup2.findGetter(PopUpHelper.class, "lws", LightweightSystem.class); //$NON-NLS-1$ + LightweightSystem lws = (LightweightSystem) getter2.invoke(toolTipHelper); + + return lws.getRootFigure().getChildren().get(0); + } + + /** + * Returns the fish-eye figure at the given coordinates. Note that those figures + * are on a separate layer. + * + * @param x The x coordinate of the fish-eye figure. + * @param y The x coordinate of the fish-eye figure. + */ + protected GraphLabel getFishEyeFigure(int x, int y) { + IFigure fishEyeLayer = graph.getRootLayer().getChildren().get(1); + return (GraphLabel) fishEyeLayer.findFigureAt(x, y); + } + + /** + * Asserts that the given graph node has the expected name. + * + * @param node The graph node to validate. + * @param text The expected name of the graph node. + */ + protected static void assertNode(GraphNode node, String text) { + assertEquals(node.getText(), text); + } + + /** + * Asserts that the given {@code connection} has the expected source and + * destination nodes. + * + * @param connection The graph connection to validate. + * @param source The name of the expected source node. + * @param destination The name of the expected destination node. + */ + protected static void assertConnection(GraphConnection connection, String source, String destination) { + assertEquals(connection.getSource().getText(), source); + assertEquals(connection.getDestination().getText(), destination); + } + + /** + * Asserts that the given {@code connection} uses a {@link PolylineConnection} + * with given {@code arc}. The arc of the connection is calculated using the + * points of the polyline. The test passes if this value is within 5° of the + * expected value. + * + * @param connection The graph connection to validate. + * @param arc The expected arc of the + */ + protected static void assertArc(GraphConnection connection, double arc) { + PolylineConnection connectionFigure = (PolylineConnection) connection.getConnectionFigure(); + assertEquals(getArc(connectionFigure), arc, 5); // tolerance to account for OS differences + } + + /** + * Asserts that no graph nodes in the given {@link IContainer} intersect with + * one another. This method doesn't check nested containers. + * + * @param container The {@link IContainer} to validate. + */ + protected static void assertNoOverlap(IContainer container) { + @SuppressWarnings("unchecked") + List nodes = container.getNodes(); + for (int i = 0; i < nodes.size(); ++i) { + for (int j = i + 1; j < nodes.size(); ++j) { + GraphNode node1 = nodes.get(i); + Rectangle bounds1 = new Rectangle(node1.getLocation(), node1.getSize()); + GraphNode node2 = nodes.get(j); + Rectangle bounds2 = new Rectangle(node2.getLocation(), node2.getSize()); + assertFalse(bounds1.intersects(bounds2)); + } + } + } + + /** + * Asserts that the given {@code object} is of type {@code class}. Subclasses + * are allowed. + * + * @param object The object to validate. + * @param clazz The expected class of the describing {@link IFigure}. + */ + protected static void assertInstanceOf(Object object, Class clazz) { + assertTrue(clazz.isAssignableFrom(object.getClass())); + } + + /** + * @return the {@link Button} with given text. + */ + protected static Button findButtonByName(Shell shell, String text) { + return findWidget(shell, Button.class, b -> text.equals(b.getText())); + } + + /** + * @return the first {@link Text} in the given {@link Shell}. + */ + protected static Text findText(Shell shell) { + return findWidget(shell, Text.class, t -> true); + } + + /** + * @return the first {@link Canvas} in the given {@link Shell}. + */ + protected static Canvas findCanvas(Shell shell) { + return findWidget(shell, Canvas.class, t -> true); + } + + /** + * Convenience method for finding a given {@link Widget} in a {@link Shell}. + * type. + * + * @return The first widget of type {@code T}, matching the predicate. Returns + * {@code null} if no such widget is found. + */ + private static T findWidget(Shell shell, Class clazz, Predicate predicate) { + AtomicReference ref = new AtomicReference<>(); + new WidgetVisitor() { + @Override + public boolean visit(Widget w) { + if (clazz.isAssignableFrom(w.getClass())) { + T widget = clazz.cast(w); + if (predicate.test(widget)) { + ref.set(widget); + return false; + } + } + return true; + } + }.traverse(shell); + return ref.get(); + } + + /** + * Pumps the event loop for the given number of milliseconds. At least one + * events loop will be executed. + */ + protected static void waitEventLoop(int time) { + long start = System.currentTimeMillis(); + do { + while (Display.getCurrent().readAndDispatch()) { + // do nothing + } + } while (System.currentTimeMillis() - start < time); + } +} diff --git a/org.eclipse.zest.tests/src/org/eclipse/zest/tests/examples/GraphJFaceTests.java b/org.eclipse.zest.tests/src/org/eclipse/zest/tests/examples/GraphJFaceTests.java new file mode 100644 index 000000000..805a31ff3 --- /dev/null +++ b/org.eclipse.zest.tests/src/org/eclipse/zest/tests/examples/GraphJFaceTests.java @@ -0,0 +1,257 @@ +/******************************************************************************* + * Copyright (c) 2024 Patrick Ziegler and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Patrick Ziegler - initial API and implementation + *******************************************************************************/ + +package org.eclipse.zest.tests.examples; + +import static org.junit.Assert.assertEquals; + +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.invoke.VarHandle; +import java.util.concurrent.atomic.AtomicReference; + +import org.eclipse.swt.widgets.Button; + +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.zest.core.viewers.GraphViewer; +import org.eclipse.zest.core.viewers.IGraphContentProvider; +import org.eclipse.zest.core.viewers.IGraphEntityContentProvider; +import org.eclipse.zest.core.viewers.INestedContentProvider; +import org.eclipse.zest.core.widgets.Graph; +import org.eclipse.zest.core.widgets.GraphConnection; +import org.eclipse.zest.core.widgets.GraphContainer; +import org.eclipse.zest.core.widgets.GraphNode; +import org.eclipse.zest.core.widgets.internal.ContainerFigure; +import org.eclipse.zest.examples.jface.GraphJFaceSnippet1; +import org.eclipse.zest.examples.jface.GraphJFaceSnippet2; +import org.eclipse.zest.examples.jface.GraphJFaceSnippet3; +import org.eclipse.zest.examples.jface.GraphJFaceSnippet4; +import org.eclipse.zest.examples.jface.GraphJFaceSnippet5; +import org.eclipse.zest.examples.jface.GraphJFaceSnippet6; +import org.eclipse.zest.examples.jface.GraphJFaceSnippet7; +import org.eclipse.zest.examples.jface.GraphJFaceSnippet8; +import org.eclipse.zest.examples.uml.UMLClassFigure; +import org.eclipse.zest.tests.utils.Snippet; + +import org.junit.Test; + +/** + * This class instantiates the {@link GraphViewer}-based Zest examples and tests + * the correctness of the functionality they are supposed to show. + */ +@SuppressWarnings("nls") +public class GraphJFaceTests extends AbstractGraphTest { + protected GraphViewer viewer; + + @Override + protected Graph getGraph(Lookup lookup, Snippet snippet) throws ReflectiveOperationException { + VarHandle varHandle = lookup.findStaticVarHandle(snippet.type(), "viewer", GraphViewer.class); //$NON-NLS-1$ + viewer = (GraphViewer) varHandle.get(); + return viewer.getGraphControl(); + } + + @Override + protected boolean hasGraph(Lookup lookup, Snippet snippet) throws ReflectiveOperationException { + try { + lookup.findStaticVarHandle(snippet.type(), "viewer", GraphViewer.class); //$NON-NLS-1$ + return true; + } catch (NoSuchFieldException ignore) { + return false; + } + } + + /** + * Test using the {@link IGraphEntityContentProvider} when building a graph. + */ + @Test + @Snippet(type = GraphJFaceSnippet1.class) + public void testGraphJFaceSnippet1() { + assertNode(graph.getNodes().get(0), "First"); + assertNode(graph.getNodes().get(1), "Second"); + assertNode(graph.getNodes().get(2), "Third"); + assertEquals(graph.getNodes().size(), 3); + + assertConnection(graph.getConnections().get(0), "Second", "Third"); + assertConnection(graph.getConnections().get(1), "Third", "First"); + assertConnection(graph.getConnections().get(2), "First", "Second"); + assertEquals(graph.getConnections().size(), 3); + } + + /** + * Test using the {@link IGraphContentProvider} and {@link LabelProvider} when + * building a graph. + */ + @Test + @Snippet(type = GraphJFaceSnippet2.class) + public void testGraphJFaceSnippet2() { + assertNode(graph.getNodes().get(0), "Paper"); + assertNode(graph.getNodes().get(1), "Rock"); + assertNode(graph.getNodes().get(2), "Scissors"); + assertEquals(graph.getNodes().size(), 3); + + GraphConnection connection1 = graph.getConnections().get(0); + GraphConnection connection2 = graph.getConnections().get(1); + GraphConnection connection3 = graph.getConnections().get(2); + assertEquals(graph.getConnections().size(), 3); + + assertConnection(connection1, "Paper", "Rock"); + assertConnection(connection2, "Scissors", "Paper"); + assertConnection(connection3, "Rock", "Scissors"); + + assertEquals(connection1.getText(), "Rock2Paper"); + assertEquals(connection2.getText(), "Paper2Scissors"); + assertEquals(connection3.getText(), "Scissors2Rock"); + } + + /** + * Test reading the graph content from the file system. + */ + @Test + @Snippet(type = GraphJFaceSnippet3.class) + public void testGraphJFaceSnippet3() { + // Explicitly checking 30 nodes and 29 connections is just busy work... + assertEquals(graph.getNodes().size(), 30); + assertEquals(graph.getConnections().size(), 29); + assertNoOverlap(graph); + } + + /** + * Test proper handling of selection events. + */ + @Test + @Snippet(type = GraphJFaceSnippet4.class) + public void testGraphJFaceSnippet4() { + assertNode(graph.getNodes().get(0), "Paper"); + assertNode(graph.getNodes().get(1), "Rock"); + assertNode(graph.getNodes().get(2), "Scissors"); + assertEquals(graph.getNodes().size(), 3); + + GraphConnection connection1 = graph.getConnections().get(0); + GraphConnection connection2 = graph.getConnections().get(1); + GraphConnection connection3 = graph.getConnections().get(2); + assertEquals(graph.getConnections().size(), 3); + + assertConnection(connection1, "Paper", "Rock"); + assertConnection(connection2, "Scissors", "Paper"); + assertConnection(connection3, "Rock", "Scissors"); + + assertEquals(connection1.getText(), "Rock2Paper"); + assertEquals(connection2.getText(), "Paper2Scissors"); + assertEquals(connection3.getText(), "Scissors2Rock"); + + AtomicReference selection = new AtomicReference<>(); + viewer.addSelectionChangedListener(event -> { + IStructuredSelection structuredSelection = event.getStructuredSelection(); + assertEquals(structuredSelection.size(), 1); + Object data = structuredSelection.getFirstElement(); + selection.set(data); + }); + + for (GraphNode node : graph.getNodes()) { + robot.select(node); + assertEquals(node.getData(), selection.get()); + } + } + + /** + * Test refreshing the graph. + */ + @Test + @Snippet(type = GraphJFaceSnippet5.class) + public void testGraphJFaceSnippet5() { + assertConnection(graph.getConnections().get(0), "Paper", "Rock"); + assertConnection(graph.getConnections().get(1), "Scissors", "Paper"); + assertConnection(graph.getConnections().get(2), "Rock", "Scissors"); + assertEquals(graph.getConnections().size(), 3); + assertEquals(graph.getNodes().size(), 3); + + Button refresh = findButtonByName(graph.getShell(), "Refresh"); + robot.select(refresh); + + assertConnection(graph.getConnections().get(0), "Paper", "Rock"); + assertConnection(graph.getConnections().get(1), "Scissors", "Paper"); + assertConnection(graph.getConnections().get(2), "Rock", "Scissors"); + assertEquals(graph.getConnections().size(), 3); + assertEquals(graph.getNodes().size(), 3); + } + + /** + * The using the {@link INestedContentProvider} when building a nested graph. + */ + @Test + @Snippet(type = GraphJFaceSnippet6.class) + public void testGraphJFaceSnippet6() { + assertNode(graph.getNodes().get(0), "First"); + assertNode(graph.getNodes().get(1), "Second"); + assertNode(graph.getNodes().get(2), "Third"); + assertEquals(graph.getNodes().size(), 3); + + GraphContainer container = (GraphContainer) graph.getNodes().get(0); + assertNode(container.getNodes().get(0), "rock"); + assertNode(container.getNodes().get(1), "paper"); + assertNode(container.getNodes().get(2), "scissors"); + assertEquals(container.getNodes().size(), 3); + + assertConnection(graph.getConnections().get(0), "rock", "paper"); + assertConnection(graph.getConnections().get(1), "Second", "Third"); + assertConnection(graph.getConnections().get(2), "Second", "rock"); + assertConnection(graph.getConnections().get(3), "Third", "First"); + assertConnection(graph.getConnections().get(4), "First", "Second"); + assertEquals(graph.getConnections().size(), 5); + } + + /** + * Test building a graph with custom figures. + */ + @Test + @Snippet(type = GraphJFaceSnippet7.class) + public void testGraphJFaceSnippet7() { + GraphNode node1 = graph.getNodes().get(0); + GraphNode node2 = graph.getNodes().get(1); + GraphNode node3 = graph.getNodes().get(2); + + assertInstanceOf(node1.getNodeFigure(), ContainerFigure.class); + assertInstanceOf(node2.getNodeFigure(), UMLClassFigure.class); + assertInstanceOf(node3.getNodeFigure(), UMLClassFigure.class); + } + + /** + * Test building a graph with curved edges. + */ + @Test + @Snippet(type = GraphJFaceSnippet8.class) + public void testGraphJFaceSnippet8() { + assertNode(graph.getNodes().get(0), "First"); + assertNode(graph.getNodes().get(1), "Second"); + assertNode(graph.getNodes().get(2), "Third"); + assertEquals(graph.getNodes().size(), 3); + + GraphConnection connection1 = graph.getConnections().get(0); + GraphConnection connection2 = graph.getConnections().get(1); + GraphConnection connection3 = graph.getConnections().get(2); + GraphConnection connection4 = graph.getConnections().get(3); + assertEquals(graph.getConnections().size(), 4); + + assertConnection(connection1, "Third", "Second"); + assertConnection(connection2, "Second", "Third"); + assertConnection(connection3, "First", "First"); + assertConnection(connection4, "First", "Second"); + + assertArc(connection1, 34 /* ° */); + assertArc(connection2, 34 /* ° */); + assertArc(connection3, Double.NaN /* ° */); + assertArc(connection4, 0 /* ° */); + + assertNoOverlap(graph); + } +} diff --git a/org.eclipse.zest.tests/src/org/eclipse/zest/tests/examples/GraphSWTTests.java b/org.eclipse.zest.tests/src/org/eclipse/zest/tests/examples/GraphSWTTests.java new file mode 100644 index 000000000..fa7cfc4dc --- /dev/null +++ b/org.eclipse.zest.tests/src/org/eclipse/zest/tests/examples/GraphSWTTests.java @@ -0,0 +1,757 @@ +/******************************************************************************* + * Copyright (c) 2024 Patrick Ziegler and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Patrick Ziegler - initial API and implementation + *******************************************************************************/ + +package org.eclipse.zest.tests.examples; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.invoke.VarHandle; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Canvas; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; + +import org.eclipse.zest.core.widgets.Graph; +import org.eclipse.zest.core.widgets.GraphConnection; +import org.eclipse.zest.core.widgets.GraphContainer; +import org.eclipse.zest.core.widgets.GraphNode; +import org.eclipse.zest.core.widgets.HideNodeHelper; +import org.eclipse.zest.core.widgets.internal.GraphLabel; +import org.eclipse.zest.core.widgets.internal.NodeSearchDialog; +import org.eclipse.zest.examples.swt.AnimationSnippet; +import org.eclipse.zest.examples.swt.CustomLayout; +import org.eclipse.zest.examples.swt.GraphSnippet1; +import org.eclipse.zest.examples.swt.GraphSnippet10; +import org.eclipse.zest.examples.swt.GraphSnippet11; +import org.eclipse.zest.examples.swt.GraphSnippet12; +import org.eclipse.zest.examples.swt.GraphSnippet13; +import org.eclipse.zest.examples.swt.GraphSnippet14; +import org.eclipse.zest.examples.swt.GraphSnippet2; +import org.eclipse.zest.examples.swt.GraphSnippet3; +import org.eclipse.zest.examples.swt.GraphSnippet4; +import org.eclipse.zest.examples.swt.GraphSnippet5; +import org.eclipse.zest.examples.swt.GraphSnippet6; +import org.eclipse.zest.examples.swt.GraphSnippet7; +import org.eclipse.zest.examples.swt.GraphSnippet8; +import org.eclipse.zest.examples.swt.GraphSnippet9; +import org.eclipse.zest.examples.swt.HelloWorld; +import org.eclipse.zest.examples.swt.LayoutExample; +import org.eclipse.zest.examples.swt.NestedGraphSnippet; +import org.eclipse.zest.examples.swt.NestedGraphSnippet2; +import org.eclipse.zest.examples.swt.PaintSnippet; +import org.eclipse.zest.examples.swt.ZoomSnippet; +import org.eclipse.zest.layouts.Filter; +import org.eclipse.zest.layouts.LayoutStyles; +import org.eclipse.zest.layouts.algorithms.SpringLayoutAlgorithm; +import org.eclipse.zest.layouts.dataStructures.InternalRelationship; +import org.eclipse.zest.tests.utils.Snippet; + +import org.eclipse.draw2d.ColorConstants; +import org.eclipse.draw2d.Connection; +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.Label; +import org.eclipse.draw2d.geometry.Point; + +import org.junit.Ignore; +import org.junit.Test; + +/** + * This class instantiates the {@link Graph}-based Zest examples and tests the + * correctness of the functionality they are supposed to show. + */ +@SuppressWarnings("nls") +public class GraphSWTTests extends AbstractGraphTest { + + @Override + protected Graph getGraph(Lookup lookup, Snippet snippet) throws ReflectiveOperationException { + VarHandle varHandle = lookup.findStaticVarHandle(snippet.type(), snippet.field(), Graph.class); // $NON-NLS-1$ + return (Graph) varHandle.get(); + } + + @Override + protected boolean hasGraph(Lookup lookup, Snippet snippet) throws ReflectiveOperationException { + try { + lookup.findStaticVarHandle(snippet.type(), snippet.field(), Graph.class); + return true; + } catch (NoSuchFieldException ignore) { + return false; + } + } + + /** + * Tests whether the animations are drawn. + * + * @see here + */ + @Test + @Ignore + @Snippet(type = AnimationSnippet.class) + public void testAnimationSnippet() { + AtomicInteger repaintCount = new AtomicInteger(); + graph.addPaintListener(event -> repaintCount.incrementAndGet()); + + Button button = findButtonByName(graph.getShell(), "Animate"); + robot.select(button); + + assertTrue("Animation was likely not drawn!", repaintCount.get() > 0); + assertNoOverlap(graph); + } + + /** + * Tests whether custom layouts are properly executed. This layout simply lays + * the nodes out vertically on the same Y-Axis as they currently have. + */ + @Test + @Snippet(type = CustomLayout.class) + public void testCustomLayout() { + GraphNode node1 = graph.getNodes().get(0); + GraphNode node2 = graph.getNodes().get(1); + GraphNode node3 = graph.getNodes().get(2); + assertEquals(graph.getNodes().size(), 3); + + assertNode(node1, "Paper"); + assertNode(node2, "Rock"); + assertNode(node3, "Scissors"); + + assertConnection(graph.getConnections().get(0), "Paper", "Rock"); + assertConnection(graph.getConnections().get(1), "Rock", "Scissors"); + assertConnection(graph.getConnections().get(2), "Scissors", "Paper"); + assertEquals(graph.getConnections().size(), 3); + + assertEquals(node1.getLocation().y, node2.getLocation().y); + assertEquals(node1.getLocation().y, node3.getLocation().y); + + assertNoOverlap(graph); + } + + /** + * Tests whether the correct graph nodes are selected by the + * {@link NodeSearchDialog}. + */ + @Test + @Snippet(type = GraphSnippet1.class) + public void testGraphSnippet1() { + GraphNode node1 = graph.getNodes().get(0); + GraphNode node2 = graph.getNodes().get(1); + GraphNode node3 = graph.getNodes().get(2); + assertEquals(graph.getNodes().size(), 3); + + assertNode(node1, "Paper"); + assertNode(node2, "Rock"); + assertNode(node3, "Scissors"); + + Shell dialogShell = null; + try { + robot.keyDown(SWT.CONTROL, 'f'); + + // The NodeSearchDialog should now be active + dialogShell = graph.getDisplay().getActiveShell(); + Text text = findText(dialogShell); + Button next = findButtonByName(dialogShell, "Next"); + + text.setText("Paper"); + robot.focusOut(text); + robot.select(next); + assertEquals(graph.getSelection(), List.of(node1)); + + text.setText("Rock"); + robot.focusOut(text); + robot.select(next); + assertEquals(graph.getSelection(), List.of(node2)); + + text.setText("Scissor"); + robot.focusOut(text); + robot.select(next); + assertEquals(graph.getSelection(), List.of(node3)); + } finally { + if (dialogShell != null) { + dialogShell.dispose(); + } + } + } + + /** + * Tests whether images are properly handled. + */ + @Test + @Snippet(type = GraphSnippet2.class) + public void testGraphSnippet2() { + GraphNode node1 = graph.getNodes().get(0); + GraphNode node2 = graph.getNodes().get(1); + GraphNode node3 = graph.getNodes().get(2); + assertEquals(graph.getNodes().size(), 3); + + assertNode(node1, "Information"); + assertNode(node2, "Warning"); + assertNode(node3, "Error"); + + assertConnection(graph.getConnections().get(0), "Information", "Warning"); + assertConnection(graph.getConnections().get(1), "Warning", "Error"); + assertConnection(graph.getConnections().get(2), "Error", "Error"); + assertEquals(graph.getConnections().size(), 3); + + Image image1 = Display.getDefault().getSystemImage(SWT.ICON_INFORMATION); + Image image2 = Display.getDefault().getSystemImage(SWT.ICON_WARNING); + Image image3 = Display.getDefault().getSystemImage(SWT.ICON_ERROR); + + assertEquals(node1.getImage(), image1); + assertEquals(node2.getImage(), image2); + assertEquals(node3.getImage(), image3); + + GraphLabel figure1 = (GraphLabel) node1.getNodeFigure(); + GraphLabel figure2 = (GraphLabel) node2.getNodeFigure(); + GraphLabel figure3 = (GraphLabel) node3.getNodeFigure(); + + assertEquals(figure1.getIcon(), image1); + assertEquals(figure2.getIcon(), image2); + assertEquals(figure3.getIcon(), image3); + } + + /** + * Test proper handling of selection events. + */ + @Test + @Snippet(type = GraphSnippet3.class) + public void testGraphSnippet3() { + assertNode(graph.getNodes().get(0), "Information"); + assertNode(graph.getNodes().get(1), "Warning"); + assertNode(graph.getNodes().get(2), "Error"); + assertEquals(graph.getNodes().size(), 3); + + assertConnection(graph.getConnections().get(0), "Information", "Warning"); + assertConnection(graph.getConnections().get(1), "Warning", "Error"); + assertEquals(graph.getConnections().size(), 2); + + AtomicReference selection = new AtomicReference<>(); + graph.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent event) { + selection.set(event.data); + } + }); + + for (GraphNode node : graph.getNodes()) { + robot.select(node); + assertEquals(node.getData(), selection.get()); + } + } + + /** + * Tests custom tooltips when hovering over an edge. + */ + @Test + @Snippet(type = GraphSnippet4.class) + public void testGraphSnippet4() throws Throwable { + GraphConnection connection = graph.getConnections().get(1); + assertConnection(connection, "Warning", "Error"); + + Connection connectionFigure = connection.getConnectionFigure(); + Point center = connectionFigure.getBounds().getCenter(); + + robot.mouseHover(center.x, center.y); + waitEventLoop(0); + + IFigure figure = graph.getFigureAt(center.x, center.y); + Label label = (Label) getToolTip(figure); + assertEquals(label.getText(), "Warning to Error"); + + assertNoOverlap(graph); + } + + /** + * Tests whether the graph is updated on user input. + */ + @Test + @Snippet(type = GraphSnippet5.class) + public void testGraphSnippet5() { + GraphNode node1 = graph.getNodes().get(0); + GraphNode node2 = graph.getNodes().get(1); + GraphNode node3 = graph.getNodes().get(2); + assertEquals(graph.getNodes().size(), 3); + + assertNode(node1, "org.eclipse.Information"); + assertNode(node2, "org.eclipse.Warning"); + assertNode(node3, "org.eclipse.Error"); + + robot.keyDown('f'); + assertEquals(graph.getSelection(), List.of(node1)); + + robot.keyDown(SWT.BS); + robot.keyDown('w'); + assertEquals(graph.getSelection(), List.of(node2)); + + robot.keyDown(SWT.BS); + robot.keyDown('e'); + robot.keyDown('r'); + assertEquals(graph.getSelection(), List.of(node3)); + + robot.keyDown(SWT.BS); + robot.keyDown(SWT.BS); + assertTrue(graph.getSelection().isEmpty()); + } + + /** + * Tests whether the fisheye figure is shown when hovering over a node. + */ + @Test + @Snippet(type = GraphSnippet6.class) + public void testGraphSnippet6() { + assertEquals(graph.getNodes().size(), 240); + + List labels = List.of("Information", "Warning", "Error"); + for (int i = 0; i < labels.size(); ++i) { + GraphNode node = graph.getNodes().get(i); + Point location = node.getLocation(); + + GraphLabel figure = (GraphLabel) graph.getFigureAt(location.x + 1, location.y + 1); + assertEquals(figure.getText(), ""); + + robot.mouseMove(location.x + 1, location.y + 1); + GraphLabel fishEyeFigure = getFishEyeFigure(location.x + 1, location.y + 1); + assertEquals(fishEyeFigure.getText(), labels.get(i)); + } + } + + /** + * Tests whether {@link Graph#getFigureAt(int, int)} can find both nodes and + * connections. + */ + @Test + @Snippet(type = GraphSnippet7.class) + public void testGraphSnippet7() { + for (GraphNode node : graph.getNodes()) { + IFigure nodeFigure = node.getNodeFigure(); + Point nodeCenter = nodeFigure.getBounds().getCenter(); + assertEquals(nodeFigure, graph.getFigureAt(nodeCenter.x, nodeCenter.y)); + } + for (GraphConnection connection : graph.getConnections()) { + IFigure connectionFigure = connection.getConnectionFigure(); + Point connectionCenter = connectionFigure.getBounds().getCenter(); + assertEquals(connectionFigure, graph.getFigureAt(connectionCenter.x, connectionCenter.y)); + } + } + + /** + * Tests whether connections are hidden when the {@link Filter} is used. + */ + @Test + @Snippet(type = GraphSnippet8.class, field = "graph") + public void testGraphSnippet8() throws ReflectiveOperationException { + assertEquals(graph.getConnections().size(), 13); + assertNoOverlap(graph); + + InternalRelationship[] connections = getInternalRelationships(); + assertEquals(connections.length, 8); + + for (GraphConnection connection : graph.getConnections()) { + connection.setData(Boolean.TRUE); + } + + graph.applyLayout(); + waitEventLoop(0); + + connections = getInternalRelationships(); + assertEquals(connections.length, 0); + } + + /** + * Tests a graph whose nodes contain self-loops. + */ + @Test + @Snippet(type = GraphSnippet9.class, field = "graph") + public void testGraphSnippet9() { + assertNode(graph.getNodes().get(0), "Root"); + assertEquals(graph.getNodes().size(), 1); + + GraphConnection connection = graph.getConnections().get(0); + assertConnection(connection, "Root", "Root"); + assertEquals(connection.getText(), "A to A"); + assertEquals(graph.getConnections().size(), 1); + + assertNoOverlap(graph); + } + + /** + * Tests a graph with curved connections. The curve changes on each button + * press. + */ + @Test + @Snippet(type = GraphSnippet10.class) + public void testGraphSnippet10() { + assertNode(graph.getNodes().get(0), "Paper"); + assertNode(graph.getNodes().get(1), "Rock"); + assertNode(graph.getNodes().get(2), "Scissors"); + assertEquals(graph.getNodes().size(), 3); + + GraphConnection connection1 = graph.getConnections().get(0); + GraphConnection connection2 = graph.getConnections().get(1); + GraphConnection connection3 = graph.getConnections().get(2); + assertEquals(graph.getConnections().size(), 3); + + assertConnection(connection1, "Paper", "Rock"); + assertConnection(connection2, "Rock", "Scissors"); + assertConnection(connection3, "Scissors", "Paper"); + + Button button = findButtonByName(graph.getShell(), "Change Curve"); + for (int i = 0; i < 4; ++i) { + if (i > 0) { + robot.select(button); + } + // Old connection is removed and a new one is added + assertArc(graph.getConnections().get(2), i * 10 /* ° */); + } + } + + /** + * Tests a graph with curved connections. + */ + @Test + @Snippet(type = GraphSnippet11.class) + public void testGraphSnippet11() { + assertNode(graph.getNodes().get(0), "Node 1"); + assertNode(graph.getNodes().get(1), "Node 2"); + assertEquals(graph.getNodes().size(), 2); + assertEquals(graph.getConnections().size(), 7); + + for (GraphConnection connection : graph.getConnections()) { + assertConnection(connection, "Node 1", "Node 2"); + } + + assertArc(graph.getConnections().get(0), 15 /* ° */); + assertArc(graph.getConnections().get(1), 15 /* ° */); + assertArc(graph.getConnections().get(2), 29 /* ° */); + assertArc(graph.getConnections().get(3), 29 /* ° */); + assertArc(graph.getConnections().get(4), 40 /* ° */); + assertArc(graph.getConnections().get(5), 40 /* ° */); + assertArc(graph.getConnections().get(6), 0 /* ° */); + + assertNoOverlap(graph); + } + + /** + * Tests the usage of complex node figures. + */ + @Test + @Snippet(type = GraphSnippet12.class) + public void testGraphSnippet12() { + assertEquals(graph.getNodes().size(), 5); + assertEquals(graph.getConnections().size(), 7); + + assertNode(graph.getNodes().get(2), "PDE"); + assertNode(graph.getNodes().get(3), "Zest"); + assertNode(graph.getNodes().get(4), "PDE Viz tool"); + + GraphNode xz = graph.getNodes().get(0); + IFigure xzFigure = xz.getNodeFigure(); + robot.select(xz); + waitEventLoop(0); + assertEquals(xzFigure.getForegroundColor(), ColorConstants.blue); + assertEquals(xzFigure.getBackgroundColor(), ColorConstants.blue); + assertEquals(xzFigure.getChildren().size(), 6); + + GraphNode ibull = graph.getNodes().get(1); + IFigure ibullFigure = ibull.getNodeFigure(); + robot.select(ibull); + waitEventLoop(0); + assertEquals(ibullFigure.getForegroundColor(), ColorConstants.blue); + assertEquals(ibullFigure.getBackgroundColor(), ColorConstants.blue); + assertEquals(ibullFigure.getChildren().size(), 6); + + assertEquals(xzFigure.getForegroundColor(), ColorConstants.black); + assertEquals(xzFigure.getBackgroundColor(), ColorConstants.black); + } + + /** + * Tests the usage of advanced tooltips. + */ + @Test + @Snippet(type = GraphSnippet13.class) + public void testGraphSnippet13() throws Throwable { + assertNode(graph.getNodes().get(0), "Canada"); + assertNode(graph.getNodes().get(1), "USA"); + assertEquals(graph.getNodes().size(), 2); + assertEquals(graph.getConnections().size(), 2); + + GraphContainer container = (GraphContainer) graph.getNodes().get(1); + container.open(false); + waitEventLoop(0); + + assertNode(container.getNodes().get(0), "Chris A."); + assertEquals(container.getNodes().size(), 1); + + GraphNode node = container.getNodes().get(0); + IFigure nodeFigure = node.getNodeFigure(); + Point center = nodeFigure.getBounds().getCenter(); + nodeFigure.translateToAbsolute(center); + + robot.mouseHover(center.x, center.y); + waitEventLoop(0); + + IFigure figure = graph.getFigureAt(center.x, center.y); + IFigure tooltip = getToolTip(figure); + assertEquals(tooltip.getChildren().size(), 3); + assertEquals(((Label) tooltip.getChildren().get(1)).getText(), "Name: Chris Aniszczyk"); + assertEquals(((Label) tooltip.getChildren().get(2)).getText(), "Location: Austin, Texas"); + } + + /** + * Tests dynamically hiding or revealing nodes using the {@link HideNodeHelper}. + */ + @Test + @Snippet(type = GraphSnippet14.class) + public void testGraphSnippet14() { + assertEquals(graph.getNodes().size(), 3); + assertEquals(graph.getConnections().size(), 3); + + GraphNode node1 = graph.getNodes().get(0); + GraphNode node2 = graph.getNodes().get(1); + GraphNode node3 = graph.getNodes().get(2); + + assertTrue(node1.isVisible()); + assertTrue(node2.isVisible()); + assertTrue(node3.isVisible()); + + robot.clickHide(node1); + robot.clickHide(node3); + graph.applyLayout(); + waitEventLoop(0); + + assertFalse(node1.isVisible()); + assertTrue(node2.isVisible()); + assertFalse(node3.isVisible()); + + robot.clickReveal(node2); + graph.applyLayout(); + waitEventLoop(0); + + assertTrue(node1.isVisible()); + assertTrue(node2.isVisible()); + assertTrue(node3.isVisible()); + } + + /** + * Test the obligatory "Hello World" example. + */ + @Test + @Snippet(type = HelloWorld.class) + public void testHelloWorld() { + assertNode(graph.getNodes().get(0), "Hello"); + assertNode(graph.getNodes().get(1), "World"); + assertEquals(graph.getNodes().size(), 2); + + assertConnection(graph.getConnections().get(0), "Hello", "World"); + assertEquals(graph.getConnections().size(), 1); + } + + /** + * Tests whether the layout is correctly calculated when using constraints. + */ + @Test + @Snippet(type = LayoutExample.class) + public void testLayoutExample() { + graph.setLayoutAlgorithm(new SpringLayoutAlgorithm(LayoutStyles.NO_LAYOUT_NODE_RESIZING), true); + waitEventLoop(0); + + double sumLengthInner = 0; + int countInner = 0; + double sumLengthOuter = 0; + int countOuter = 0; + + for (GraphConnection connection : graph.getConnections()) { + if ("Root".equals(connection.getSource().getText())) { + sumLengthInner += getLength(connection); + countInner++; + } else { + sumLengthOuter += getLength(connection); + countOuter++; + } + } + + assertEquals(countInner, 3); + assertEquals(countOuter, 9); + + double avgLengthInner = sumLengthInner / countInner; + double avgLengthOuter = sumLengthOuter / countOuter; + + // The inner nodes have a higher weight and are thus (a lot) shorter + assertTrue(avgLengthInner + ", " + avgLengthOuter, (avgLengthInner * 1.5) < avgLengthOuter); + } + + /** + * Test whether nested nodes can be accessed correctly. + */ + @Test + @Snippet(type = NestedGraphSnippet.class) + public void testNestedGraphSnippet() { + assertEquals(graph.getNodes().size(), 21); + assertEquals(graph.getConnections().size(), 440); + + GraphContainer container = (GraphContainer) graph.getNodes().get(0); + container.open(false); + + graph.applyLayout(); + waitEventLoop(0); + + assertNode(container.getNodes().get(0), "SomeClass.java"); + assertEquals(container.getNodes().size(), 21); + + GraphNode node = container.getNodes().get(0); + IFigure nodeFigure = node.getNodeFigure(); + + Point nodeLocation = nodeFigure.getBounds().getCenter(); + nodeFigure.translateToAbsolute(nodeLocation); + + robot.mouseDown(nodeLocation.x, nodeLocation.y); + assertEquals(graph.getSelection(), List.of(node)); + } + + /** + * Test whether nested nodes can be accessed correctly. + */ + @Test + @Snippet(type = NestedGraphSnippet2.class) + public void testNestedGraphSnippet2() { + assertNode(graph.getNodes().get(0), "Machine 1 (prop:1)"); + assertNode(graph.getNodes().get(1), "Machine 2"); + assertNode(graph.getNodes().get(2), "Machine 3"); + assertEquals(graph.getNodes().size(), 3); + + GraphContainer container1 = (GraphContainer) graph.getNodes().get(0); + container1.open(false); + + graph.applyLayout(); + waitEventLoop(0); + + assertNode(container1.getNodes().get(0), "Host 1"); + assertNode(container1.getNodes().get(1), "Host 2"); + assertEquals(container1.getNodes().size(), 2); + + GraphContainer container2 = (GraphContainer) container1.getNodes().get(1); + container2.open(false); + + graph.applyLayout(); + waitEventLoop(0); + + assertNode(container2.getNodes().get(0), "JSP Object 3"); + assertEquals(container2.getNodes().size(), 1); + + GraphNode node = container2.getNodes().get(0); + IFigure nodeFigure = node.getNodeFigure(); + + Point nodeLocation = nodeFigure.getBounds().getCenter(); + nodeFigure.translateToAbsolute(nodeLocation); + + robot.mouseDown(nodeLocation.x, nodeLocation.y); + assertEquals(graph.getSelection(), List.of(node)); + + GraphLabel nodeLabel = (GraphLabel) graph.getFigureAt(nodeLocation.x, nodeLocation.y); + assertEquals(nodeLabel.getText(), "JSP Object 3"); + + assertNoOverlap(container1); + assertNoOverlap(container2); + } + + /** + * Tests whether nodes are painted correctly. + */ + @Test + @Snippet(type = PaintSnippet.class) + public void testPaintSnippet() { + assertEquals(graph.getNodes().size(), 3); + + // The canvas has a 10 pixel wide border + int offsetX = 10; + int offsetY = 10; + + graph.selectAll(); + waitEventLoop(0); + + Button button = findButtonByName(graph.getShell(), "Take Screenshot"); + robot.select(button); + waitEventLoop(0); + + Shell popupShell = graph.getDisplay().getActiveShell(); + Canvas popupCanvas = findCanvas(popupShell); + Rectangle bounds = popupCanvas.getBounds(); + + GC gc = new GC(popupCanvas); + Image image = new Image(null, bounds.width, bounds.height); + try { + gc.copyArea(image, 0, 0); + ImageData imageData = image.getImageData(); + + for (GraphNode node : graph.getNodes()) { + Point location = node.getNodeFigure().getBounds().getTop(); + int x = location.x + offsetX; + int y = location.y + offsetY + 2; + int pixelValue = imageData.getPixel(x, y); + RGB pixelColor = imageData.palette.getRGB(pixelValue); + assertEquals(graph.HIGHLIGHT_COLOR.getRGB(), pixelColor); + } + } finally { + gc.dispose(); + image.dispose(); + popupShell.close(); + popupShell.dispose(); + } + } + + /** + * Test whether nested nodes can be accessed correctly (This time check fisheye + * figure). + */ + @Test + @Snippet(type = ZoomSnippet.class) + public void testZoomSnippet() { + assertEquals(graph.getNodes().size(), 21); + assertEquals(graph.getConnections().size(), 440); + + GraphContainer container = (GraphContainer) graph.getNodes().get(0); + robot.select(container); + robot.keyDown(SWT.SPACE); + container.open(false); + + graph.applyLayout(); + waitEventLoop(0); + + GraphNode node = container.getNodes().get(0); + robot.select(node); + + graph.applyLayout(); + waitEventLoop(0); + + IFigure nodeFigure = node.getNodeFigure(); + Point location = nodeFigure.getBounds().getCenter(); + nodeFigure.translateToAbsolute(location); + + robot.mouseMove(location.x, location.y); + GraphLabel fishEyeFigure = getFishEyeFigure(location.x + 1, location.y + 1); + assertEquals(fishEyeFigure.getText(), "SomeClass.java"); + } +} diff --git a/org.eclipse.zest.tests/src/org/eclipse/zest/tests/examples/GraphUMLTests.java b/org.eclipse.zest.tests/src/org/eclipse/zest/tests/examples/GraphUMLTests.java new file mode 100644 index 000000000..1067adac8 --- /dev/null +++ b/org.eclipse.zest.tests/src/org/eclipse/zest/tests/examples/GraphUMLTests.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2024 Patrick Ziegler and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Patrick Ziegler - initial API and implementation + *******************************************************************************/ + +package org.eclipse.zest.tests.examples; + +import static org.junit.Assert.assertEquals; + +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.invoke.VarHandle; + +import org.eclipse.zest.core.widgets.Graph; +import org.eclipse.zest.core.widgets.GraphContainer; +import org.eclipse.zest.core.widgets.GraphNode; +import org.eclipse.zest.core.widgets.internal.ContainerFigure; +import org.eclipse.zest.examples.uml.UMLClassFigure; +import org.eclipse.zest.examples.uml.UMLExample; +import org.eclipse.zest.tests.utils.Snippet; + +import org.junit.Test; + +/** + * This class instantiates the UML-based Zest examples and tests the correctness + * of the functionality they are supposed to show. + */ +@SuppressWarnings("nls") +public class GraphUMLTests extends AbstractGraphTest { + + @Override + protected boolean hasGraph(Lookup lookup, Snippet snippet) throws ReflectiveOperationException { + try { + lookup.findStaticVarHandle(snippet.type(), snippet.field(), Graph.class); + return true; + } catch (NoSuchFieldException ignore) { + return false; + } + } + + @Override + protected Graph getGraph(Lookup lookup, Snippet snippet) throws ReflectiveOperationException { + VarHandle varHandle = lookup.findStaticVarHandle(snippet.type(), snippet.field(), Graph.class); // $NON-NLS-1$ + return (Graph) varHandle.get(); + } + + /** + * Tests whether the UML nodes are created properly and added to the correct + * container. + */ + @Test + @Snippet(type = UMLExample.class) + public void testUMLExample() { + GraphNode node1 = graph.getNodes().get(0); + GraphNode node2 = graph.getNodes().get(1); + GraphNode node3 = graph.getNodes().get(2); + assertEquals(graph.getNodes().size(), 3); + + assertNode(node1, "A UML Container"); + assertNode(node2, ""); + assertNode(node3, ""); + + assertInstanceOf(node1.getNodeFigure(), ContainerFigure.class); + assertInstanceOf(node2.getNodeFigure(), UMLClassFigure.class); + assertInstanceOf(node3.getNodeFigure(), UMLClassFigure.class); + + GraphContainer graphContainer = (GraphContainer) node1; + GraphNode node4 = graphContainer.getNodes().get(0); + assertEquals(graphContainer.getNodes().size(), 1); + + assertNode(node4, ""); + assertInstanceOf(node4.getNodeFigure(), UMLClassFigure.class); + } +} diff --git a/org.eclipse.zest.tests/src/org/eclipse/zest/tests/utils/GraphicalRobot.java b/org.eclipse.zest.tests/src/org/eclipse/zest/tests/utils/GraphicalRobot.java new file mode 100644 index 000000000..332f4a6bf --- /dev/null +++ b/org.eclipse.zest.tests/src/org/eclipse/zest/tests/utils/GraphicalRobot.java @@ -0,0 +1,184 @@ +/******************************************************************************* + * Copyright (c) 2024 Patrick Ziegler and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Patrick Ziegler - initial API and implementation + *******************************************************************************/ + +package org.eclipse.zest.tests.utils; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Widget; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.zest.core.widgets.Graph; +import org.eclipse.zest.core.widgets.GraphItem; +import org.eclipse.zest.core.widgets.GraphNode; +import org.eclipse.zest.core.widgets.HideNodeHelper; + +import org.eclipse.draw2d.Clickable; + +/** + * Utility class to simulate user events (clicks, etc.) on a {@link Graph}. + */ +public class GraphicalRobot { + private final Graph graph; + + public GraphicalRobot(Graph graph) { + this.graph = graph; + } + + /** + * This method simulates a {@link SWT#KeyDown} event using the given + * {@code character}. Events are sent to the current {@link #graph} instance. + * + * @param character The character that has been typed. + */ + public final void keyDown(char character) { + keyDown(0, character); + } + + /** + * This method simulates a {@link SWT#KeyDown} event using the given + * {@code character} and {@code state mask}. Events are sent to the current + * {@link #graph} instance. + * + * @param stateMask The (optional) keyboard modified that has been pressed. + * @param character The character that has been typed. + */ + public void keyDown(int stateMask, char character) { + Event event = createKeyEvent(SWT.KeyDown, stateMask, character); + graph.notifyListeners(event.type, event); + } + + /** + * This method simulates a {@link SWT#MouseHover} event using the given + * {@code x} and {@code y} coordinates. Events are sent to the current + * {@link #graph} instance. The coordinates must be inside the graph. + * + * @param x The x coordinate of the simulated mouse cursor. + * @param y The y coordinate of the simulated mouse cursor. + */ + public void mouseHover(int x, int y) { + Assert.isTrue(graph.getBounds().contains(x, y), "Coordinates are not inside the graph."); //$NON-NLS-1$ + Event event = createMouseEvent(SWT.MouseHover, x, y); + graph.notifyListeners(event.type, event); + } + + /** + * This method simulates a {@link SWT#MouseMove} event using the given {@code x} + * and {@code y} coordinates. Events are sent to the current {@link #graph} + * instance. The coordinates must be inside the graph. + * + * @param x The x coordinate of the simulated mouse cursor. + * @param y The y coordinate of the simulated mouse cursor. + */ + public void mouseMove(int x, int y) { + Assert.isTrue(graph.getBounds().contains(x, y), "Coordinates are not inside the graph."); //$NON-NLS-1$ + Event event = createMouseEvent(SWT.MouseMove, x, y); + graph.notifyListeners(event.type, event); + } + + /** + * This method simulates a {@link SWT#MouseDown} event using the given {@code x} + * and {@code y} coordinates. Events are sent to the current {@link #graph} + * instance. The coordinates must be inside the graph. + * + * @param x The x coordinate of the simulated mouse cursor. + * @param y The y coordinate of the simulated mouse cursor. + */ + public void mouseDown(int x, int y) { + Assert.isTrue(graph.getBounds().contains(x, y), "Coordinates are not inside the graph."); //$NON-NLS-1$ + Event event = createMouseEvent(SWT.MouseDown, x, y); + graph.notifyListeners(event.type, event); + } + + /** + * This method simulates the selection of the given graph node. It first selects + * the given {@code node}, followed by sending an {@link SWT#Selection} event on + * the current {@link #graph}. + * + * @param node The graph node to select. + */ + public void select(GraphNode node) { + graph.setSelection(new GraphItem[] { node }); + select(graph); + } + + /** + * This method simulates a {@link SWT#Selection} event using the given + * {@code widget}. Events are sent to the argument. + * + * @param widget The widget to select. + */ + @SuppressWarnings("static-method") + public void select(Widget widget) { + Event event = createMouseEvent(SWT.Selection, 1, 1); + widget.notifyListeners(event.type, event); + } + + /** + * This method simulates a {@link SWT#FocusOut} event using the given + * {@code widget}. Events are sent to the argument. + * + * @param widget The widget whose focus has been lost. + */ + @SuppressWarnings("static-method") + public void focusOut(Widget widget) { + widget.notifyListeners(SWT.FocusOut, new Event()); + } + + /** + * This method simulates clicking the {@code hide} button of the + * {@link HideNodeHelper} figure, corresponding to the given node. The + * hide-nodes feature must be enabled on the current {@link #graph}. + * + * @param node The node to hide. + */ + @SuppressWarnings("static-method") + public void clickHide(GraphNode node) { + Assert.isTrue(node.getGraphModel().getHideNodesEnabled(), "Hide-feature is not enabled"); //$NON-NLS-1$ + HideNodeHelper helper = node.getHideNodeHelper(); + Clickable hideButton = (Clickable) helper.getChildren().get(0); + hideButton.doClick(); + } + + /** + * This method simulates clicking the {@code reveal} button of the + * {@link HideNodeHelper} figure, corresponding to the given node. The + * hide-nodes feature must be enabled on the current {@link #graph}. + * + * @param node The node to reveal. + */ + @SuppressWarnings("static-method") + public void clickReveal(GraphNode node) { + Assert.isTrue(node.getGraphModel().getHideNodesEnabled(), "Hide-feature is not enabled"); //$NON-NLS-1$ + HideNodeHelper helper = node.getHideNodeHelper(); + Clickable revealButton = (Clickable) helper.getChildren().get(1); + revealButton.doClick(); + } + + private static Event createMouseEvent(int type, int x, int y) { + Event event = new Event(); + event.x = x; + event.y = y; + event.type = type; + return event; + } + + private static Event createKeyEvent(int type, int stateMask, char character) { + Event event = new Event(); + event.type = type; + event.stateMask = stateMask; + event.keyCode = character; + event.character = character; + return event; + } +} diff --git a/org.eclipse.zest.tests/src/org/eclipse/zest/tests/utils/Snippet.java b/org.eclipse.zest.tests/src/org/eclipse/zest/tests/utils/Snippet.java new file mode 100644 index 000000000..7f77b8605 --- /dev/null +++ b/org.eclipse.zest.tests/src/org/eclipse/zest/tests/utils/Snippet.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2024 Patrick Ziegler and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Patrick Ziegler - initial API and implementation + *******************************************************************************/ + +package org.eclipse.zest.tests.utils; + +import static java.lang.annotation.ElementType.METHOD; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.eclipse.zest.core.widgets.Graph; + +/** + * This annotation is used for testing the example classes, in order to indicate + * which test covers which class. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(value = { METHOD }) +public @interface Snippet { + /** + * @return The class under test. + */ + Class type(); + + /** + * @return The name of the static field containing the {@link Graph}. + */ + String field() default "g"; +} diff --git a/org.eclipse.zest.tests/src/org/eclipse/zest/tests/utils/WidgetVisitor.java b/org.eclipse.zest.tests/src/org/eclipse/zest/tests/utils/WidgetVisitor.java new file mode 100644 index 000000000..7b5552c73 --- /dev/null +++ b/org.eclipse.zest.tests/src/org/eclipse/zest/tests/utils/WidgetVisitor.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (c) 2024 Patrick Ziegler and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Patrick Ziegler - initial API and implementation + *******************************************************************************/ + +package org.eclipse.zest.tests.utils; + +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Widget; + +/** + * Utility class for traversing all widgets of a given composite. Entry point of + * this visitor is the {@code traverse()} method. The visitor traverses the + * widget in a depth-first fashion, starting with the first child of each + * composite.
      + * The visitor is interrupted if {@code false} is returned by the + * {@link #visit(Widget)} method.
      + * Example: + * + *
      + * new WidgetVisitor() {
      + *   @Override
      + *   public boolean visit(Widget w) {
      + *     ...
      + *     return true;
      + *   }
      + * }.traverse(widget);
      + * 
      + */ +public interface WidgetVisitor { + /** + * This method is called for each widget that is traversed via + * {@link #traverse(Widget)}. + * + * @param w the widget to traverse + * @return {@code false}, if the traversal should be interrupted, otherwise + * {@code true}. + */ + boolean visit(Widget w); + + /** + * If the given widget is an instance of {@link Composite}, it recursively + * traverses its children. The traversal continues until all widgets have been + * visited or a visit operation returns {@code false}. + * + * @param w the widget to traverse + * @return {@code true} if the traversal completes without interruption, + * otherwise {@code false}. + */ + default boolean traverse(Widget w) { + if (visit(w) && w instanceof Composite composite) { + for (Control child : composite.getChildren()) { + if (!traverse(child)) { + return false; + } + } + } + return true; + } +}