├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── component-inspector-core
├── pom.xml
└── src
│ └── main
│ └── java
│ └── com
│ └── tangorabox
│ └── componentinspector
│ └── core
│ ├── AbstractComponentInspector.java
│ ├── CSSStyleClass.java
│ ├── ComponentDetails.java
│ └── ObjectMetadataExtractor.java
├── component-inspector-fx
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── tangorabox
│ │ └── componentinspector
│ │ └── fx
│ │ ├── FXComponentInspector.java
│ │ └── FXComponentInspectorHandler.java
│ └── resources
│ ├── component-inspector.css
│ └── highlight.css
├── component-inspector-swing
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── tangorabox
│ │ └── componentinspector
│ │ └── swing
│ │ ├── SwingComponentInspector.java
│ │ ├── SwingComponentInspectorHandler.java
│ │ └── styling
│ │ ├── DropShadowPanel.java
│ │ └── SwingCSSStyleDecorator.java
│ └── resources
│ └── component-inspector.css
├── doc
└── images
│ ├── FXInspector.png
│ ├── JavaFXWithSwingNode.png
│ ├── SwingInspector.png
│ ├── SwingWithJavaFXPanel.png
│ ├── css-class.png
│ ├── demo.gif
│ └── field-name.png
├── hybrid-example
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── tangorabox
│ │ └── componentinspector
│ │ └── hybrid
│ │ ├── javafxwithswing
│ │ ├── JavaFXExampleWithSwingLauncher.java
│ │ ├── JavaFXExampleWithSwingNodeApp.java
│ │ └── JavaFXWindowWithSwingPanel.java
│ │ └── swingwithjavafx
│ │ ├── SwingExampleWithJavaFXLauncher.java
│ │ └── SwingWindowWithJavaFXPanel.java
│ └── resources
│ └── JavaFxWindow.fxml
├── javafx-example
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── tangorabox
│ │ └── componentinspector
│ │ └── fx
│ │ ├── JavaFXExampleApp.java
│ │ ├── JavaFXExampleLauncher.java
│ │ └── JavaFXWindowPanel.java
│ └── resources
│ └── JavaFxWindow.fxml
├── pom.xml
└── swing-example
├── pom.xml
└── src
└── main
└── java
└── com
└── tangorabox
└── componentinspector
└── swing
├── SwingExampleLauncher.java
└── SwingWindowPanel.java
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | pom.xml.tag
3 | pom.xml.releaseBackup
4 | pom.xml.versionsBackup
5 | pom.xml.next
6 | release.properties
7 | dependency-reduced-pom.xml
8 | buildNumber.properties
9 | .mvn/timing.properties
10 | # https://github.com/takari/maven-wrapper#usage-without-binary-jar
11 | .mvn/wrapper/maven-wrapper.jar
12 | /.idea/
13 | *.iml
14 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | languague: java
2 | addons:
3 | sonarcloud:
4 | organization: "tangorabox"
5 |
6 | script:
7 | # the following command line builds the project and then execute the SonarCloud analysis
8 | - mvn clean install sonar:sonar -Dsonar.login=${SONAR_TOKEN}
9 |
10 |
11 | cache:
12 | directories:
13 | - '$HOME/.m2/repository'
14 | - '$HOME/.sonar/cache'
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Java Desktop (JavaFX and Swing) Component Inspector
2 |
3 | > A Tool for help you to inspect the location and some properties (see features section) of the component under mouse, in a window hierarchy
4 |
5 | [](https://opensource.org/licenses/LGPL-3.0)
6 | [](https://travis-ci.com/TangoraBox/ComponentInspector)
7 | [](https://sonarcloud.io/dashboard?id=com.tangorabox%3Acomponent-inspector)
8 |
9 |
10 | ## Features
11 |
12 | - CSS class name in javafx node components []()
13 | - Field name of component declaration in parent (when possible) []()
14 | - The inspected component is highlighted _(since v1.1.0)_
15 | ---
16 |
17 | ## Usage
18 |
19 | > The inspector window is only shown if you hold down the CONTROL key when you move the mouse
20 |
21 | ---
22 |
23 | ## Example Demo
24 |
25 | 
26 |
27 | ## ScreenShots
28 |
29 | ***JavaFX Component Inspector***
30 |
31 | []()
32 |
33 | ***Swing Component Inspector***
34 |
35 | []()
36 |
37 | ***Swing inside JavaFX***
38 |
39 | []()
40 |
41 | ***JavaFX inside Swing***
42 |
43 | []()
44 |
45 | ---
46 |
47 | ## How to use
48 |
49 | Simple, one line of code to handle all:
50 |
51 | ***Java FX Example***
52 |
53 | Add this line in the `public void start(Stage primaryStage)` of your main JavaFX class that extends `Application`:
54 |
55 | ```java
56 | FXComponentInspectorHandler.handleAll();
57 | ```
58 |
59 | ***Swing Example***
60 |
61 | Add this line in the `public static void main(String[] args)` of your application launch class:
62 |
63 | ```java
64 | SwingComponentInspectorHandler.handleAll();
65 | ```
66 |
67 | ---
68 |
69 | ## Library import with Maven
70 |
71 | The artifacts have been published to maven central:
72 |
73 |
74 | ### FXComponentInspector
75 |
76 | ***Java 11+***
77 |
78 | ```xml
79 |
80 | com.tangorabox
81 | component-inspector-fx
82 | 1.1.0
83 |
84 | ```
85 |
86 | ***Java 8***
87 |
88 | ```xml
89 |
90 | com.tangorabox
91 | component-inspector-fx
92 | 1.1.0-java8
93 |
94 | ```
95 |
96 | ---
97 |
98 | ### SwingComponentInspector
99 |
100 | ***Java 11+***
101 |
102 | ```xml
103 |
104 | com.tangorabox
105 | component-inspector-swing
106 | 1.1.0
107 |
108 | ```
109 |
110 | ***Java 8***
111 |
112 | ```xml
113 |
114 | com.tangorabox
115 | component-inspector-swing
116 | 1.1.0-java8
117 |
118 | ```
119 |
120 | ---
121 |
122 |
123 | ## Contributing
124 |
125 | > If you want to contribute to upgrade this project with new features or fixing bugs, you're welcome, please make a pull request.
126 |
127 | ---
128 |
129 | ## Team
130 |
131 |
132 | | **GaRzY** |
133 | | :---:
134 | | [](https://github.com/garzy)
135 | | `github.com/garzy` |
136 |
137 |
138 | ---
139 |
140 | ## Support
141 |
142 | Reach out to me at one of the following places!
143 |
144 | - Mail to [info@tangorabox.com](mailto:info@tangorabox.com)
145 |
146 | ---
147 |
148 |
149 | ## License
150 |
151 | [](https://opensource.org/licenses/LGPL-3.0)
152 |
--------------------------------------------------------------------------------
/component-inspector-core/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | com.tangorabox
6 | component-inspector
7 | 1.1.1-SNAPSHOT
8 |
9 |
10 | component-inspector-core
11 | component-inspector-core
12 | 1.1.1-SNAPSHOT
13 |
14 |
--------------------------------------------------------------------------------
/component-inspector-core/src/main/java/com/tangorabox/componentinspector/core/AbstractComponentInspector.java:
--------------------------------------------------------------------------------
1 | package com.tangorabox.componentinspector.core;
2 |
3 |
4 | import java.util.ArrayList;
5 | import java.util.Collections;
6 | import java.util.List;
7 | import java.util.Optional;
8 | import java.util.stream.Collectors;
9 |
10 | public abstract class AbstractComponentInspector {
11 |
12 | protected static final int HORIZONTAL_SPACING = 25;
13 | protected static final int VERTICAL_SPACING = 25;
14 |
15 |
16 | public List inspect(T component) {
17 | return inspectDetails(component).stream().map(this::createComponentDetailsPanel).collect(Collectors.toList());
18 | }
19 |
20 | private List> inspectDetails(T component) {
21 | List> hierarchy = buildHierarchyRecursive(component);
22 | buildCascade(hierarchy);
23 | return hierarchy;
24 | }
25 |
26 | private List> buildHierarchyRecursive(T component) {
27 | if (component == null) {
28 | return Collections.emptyList();
29 | }
30 | List> hierarchy = new ArrayList<>();
31 | hierarchy.add(createComponentDetails(component));
32 | hierarchy.addAll(buildHierarchyRecursive(getParent(component)));
33 | return hierarchy;
34 | }
35 |
36 | private ComponentDetails createComponentDetails(T component) {
37 | ComponentDetails details = new ComponentDetails<>();
38 | details.setFieldNameComponent(createFieldNameComponent(component).orElse(null));
39 | details.setClassComponent(createClassComponent(component));
40 | details.setStylesComponent(createStylesComponent(component));
41 | return details;
42 | }
43 |
44 | protected abstract T getParent(T component);
45 |
46 | protected abstract Optional createFieldNameComponent(T component);
47 |
48 | protected abstract T createClassComponent(T component);
49 |
50 | protected abstract T createStylesComponent(T component);
51 |
52 | protected abstract T createComponentDetailsPanel(ComponentDetails details);
53 |
54 | protected abstract void buildCascade(List> hierarchy);
55 | }
56 |
--------------------------------------------------------------------------------
/component-inspector-core/src/main/java/com/tangorabox/componentinspector/core/CSSStyleClass.java:
--------------------------------------------------------------------------------
1 | package com.tangorabox.componentinspector.core;
2 |
3 | public enum CSSStyleClass {
4 |
5 | COMPONENT_DETAILS("component-details"),
6 | FIELD_COMPONENT("field-component"),
7 | CLASS_COMPONENT("class-component"),
8 | STYLES_COMPONENT("styles-component");
9 |
10 | private final String cssClassName;
11 |
12 | CSSStyleClass(String cssClassName) {
13 | this.cssClassName = cssClassName;
14 | }
15 |
16 | public String getCssClassName() {
17 | return cssClassName;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/component-inspector-core/src/main/java/com/tangorabox/componentinspector/core/ComponentDetails.java:
--------------------------------------------------------------------------------
1 | package com.tangorabox.componentinspector.core;
2 |
3 | import java.util.Optional;
4 |
5 | public class ComponentDetails {
6 |
7 | private T fieldNameComponent = null;
8 | private T classComponent;
9 | private T stylesComponent;
10 |
11 | private int locationX;
12 | private int locationY;
13 |
14 |
15 | public void setLocation(int x, int y) {
16 | this.locationX = x;
17 | this.locationY = y;
18 | }
19 |
20 | public int getLocationX() {
21 | return locationX;
22 | }
23 |
24 | public int getLocationY() {
25 | return locationY;
26 | }
27 |
28 | public void setFieldNameComponent(T fieldNameComponent) {
29 | this.fieldNameComponent = fieldNameComponent;
30 | }
31 |
32 | public Optional getFieldNameComponent() {
33 | return Optional.ofNullable(fieldNameComponent);
34 | }
35 |
36 | public void setClassComponent(T classComponent) {
37 | this.classComponent = classComponent;
38 | }
39 |
40 | public T getClassComponent() {
41 | return classComponent;
42 | }
43 |
44 | public T getStylesComponent() {
45 | return stylesComponent;
46 | }
47 |
48 | public void setStylesComponent(T stylesComponent) {
49 | this.stylesComponent = stylesComponent;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/component-inspector-core/src/main/java/com/tangorabox/componentinspector/core/ObjectMetadataExtractor.java:
--------------------------------------------------------------------------------
1 | package com.tangorabox.componentinspector.core;
2 |
3 | import java.lang.reflect.Field;
4 | import java.util.Arrays;
5 | import java.util.Objects;
6 | import java.util.Optional;
7 |
8 |
9 | public class ObjectMetadataExtractor {
10 |
11 | private final T component;
12 | private final AbstractComponentInspector inspector;
13 |
14 | public ObjectMetadataExtractor(T component, AbstractComponentInspector inspector) {
15 | this.component = component;
16 | this.inspector = inspector;
17 | }
18 |
19 | public Optional getDeclaredFieldNameInParent() {
20 | return Optional.ofNullable(findFieldNameInHierarchy(inspector.getParent(component)));
21 | }
22 |
23 | private String findFieldNameInHierarchy(T parentOfComponent) {
24 | if (parentOfComponent == null) {
25 | return null;
26 | }
27 | return Arrays.stream(parentOfComponent.getClass().getDeclaredFields())
28 | .filter(field -> field.getType().isAssignableFrom(component.getClass()))
29 | .map(parentField -> getFieldNameInParent(component, parentOfComponent, parentField))
30 | .filter(Objects::nonNull)
31 | .findFirst()
32 | .orElseGet(() -> findFieldNameInHierarchy(inspector.getParent(parentOfComponent)));
33 | }
34 |
35 |
36 | private String getFieldNameInParent(Object component, Object parent, Field field) {
37 | try {
38 | // Allow access to private fields
39 | field.setAccessible(true);
40 | Object fieldObj = field.get(parent);
41 | if (fieldObj == component) {
42 | return field.getName();
43 | }
44 | } catch (Exception ex) {
45 | // sssssshhhh!
46 | }
47 | return null;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/component-inspector-fx/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | com.tangorabox
6 | component-inspector
7 | 1.1.1-SNAPSHOT
8 |
9 |
10 | component-inspector-fx
11 | component-inspector-fx
12 | 1.1.1-SNAPSHOT
13 |
14 |
15 |
16 | com.tangorabox
17 | component-inspector-core
18 | 1.1.1-SNAPSHOT
19 |
20 |
21 | org.openjfx
22 | javafx-graphics
23 | 11.0.2
24 |
25 |
26 | org.openjfx
27 | javafx-controls
28 | 11.0.1
29 | compile
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/component-inspector-fx/src/main/java/com/tangorabox/componentinspector/fx/FXComponentInspector.java:
--------------------------------------------------------------------------------
1 | package com.tangorabox.componentinspector.fx;
2 |
3 | import com.tangorabox.componentinspector.core.AbstractComponentInspector;
4 | import com.tangorabox.componentinspector.core.CSSStyleClass;
5 | import com.tangorabox.componentinspector.core.ComponentDetails;
6 | import com.tangorabox.componentinspector.core.ObjectMetadataExtractor;
7 | import javafx.scene.Node;
8 | import javafx.scene.control.Label;
9 | import javafx.scene.layout.HBox;
10 |
11 | import java.util.List;
12 | import java.util.Optional;
13 |
14 | class FXComponentInspector extends AbstractComponentInspector {
15 |
16 | @Override
17 | protected Node getParent(Node component) {
18 | if (component == null) {
19 | return null;
20 | }
21 | return component.getParent();
22 | }
23 |
24 | @Override
25 | protected Optional createFieldNameComponent(Node component) {
26 | return new ObjectMetadataExtractor<>(component, this).getDeclaredFieldNameInParent()
27 | .map(fieldName -> {
28 | Label result = new Label(fieldName);
29 | result.getStyleClass().add(CSSStyleClass.FIELD_COMPONENT.getCssClassName());
30 | return result;
31 | });
32 | }
33 |
34 | @Override
35 | protected Node createClassComponent(Node component) {
36 | Label result = new Label(component.getClass().getName());
37 | result.getStyleClass().add(CSSStyleClass.CLASS_COMPONENT.getCssClassName());
38 | return result;
39 | }
40 |
41 | @Override
42 | protected Node createStylesComponent(Node component) {
43 | String labelText = String.join(", ",component.getStyleClass());
44 | if (labelText.isBlank()) {
45 | labelText = "(empty)";
46 | }
47 | Label result = new Label(labelText);
48 | result.getStyleClass().add(CSSStyleClass.STYLES_COMPONENT.getCssClassName());
49 | return result;
50 | }
51 |
52 | @Override
53 | protected Node createComponentDetailsPanel(ComponentDetails details) {
54 | HBox result = new HBox(details.getClassComponent(), details.getStylesComponent());
55 | result.getStyleClass().add(CSSStyleClass.COMPONENT_DETAILS.getCssClassName());
56 | result.relocate(details.getLocationX(), details.getLocationY());
57 | details.getFieldNameComponent().ifPresent(fieldNameComponent -> result.getChildren().add(fieldNameComponent));
58 | return result;
59 | }
60 |
61 | @Override
62 | protected void buildCascade(List> hierarchy) {
63 | for (int componentLevel = 0, componentIndex = hierarchy.size() - 1; componentIndex >= 0; componentIndex--, componentLevel++) {
64 | ComponentDetails componentChild = hierarchy.get(componentIndex);
65 | componentChild.setLocation(componentLevel * HORIZONTAL_SPACING, componentLevel * (VERTICAL_SPACING + 5));
66 | }
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/component-inspector-fx/src/main/java/com/tangorabox/componentinspector/fx/FXComponentInspectorHandler.java:
--------------------------------------------------------------------------------
1 | package com.tangorabox.componentinspector.fx;
2 |
3 | import javafx.geometry.Bounds;
4 | import javafx.scene.Node;
5 | import javafx.scene.Scene;
6 | import javafx.scene.input.MouseEvent;
7 | import javafx.scene.layout.Pane;
8 | import javafx.stage.Popup;
9 | import javafx.stage.Window;
10 |
11 | import java.util.HashSet;
12 | import java.util.List;
13 | import java.util.Objects;
14 | import java.util.Set;
15 |
16 | public class FXComponentInspectorHandler {
17 |
18 | private static final int OFFSET = 25;
19 | private final Popup popup = new Popup();
20 | private final Popup highlightPopup = new Popup();
21 | private final Pane pane = new Pane();
22 | private final Pane highlightPane = new Pane();
23 | private final FXComponentInspector componentInspector = new FXComponentInspector();
24 | private final Set handledWindows = new HashSet<>();
25 | private Scene currentScene = null;
26 |
27 | private static FXComponentInspectorHandler instance = null;
28 |
29 | public static void handleAll() {
30 | getInstance().inspectAllWindows();
31 | }
32 |
33 | public static void handle(Node node) {
34 | getInstance().inspectNode(node);
35 | }
36 |
37 | private static FXComponentInspectorHandler getInstance() {
38 | if (instance == null) {
39 | instance = new FXComponentInspectorHandler();
40 | }
41 | return instance;
42 | }
43 |
44 | private FXComponentInspectorHandler() {
45 | popup.getContent().add(pane);
46 | pane.getStyleClass().add("content-pane");
47 | pane.getStylesheets().add(Objects.requireNonNull(this.getClass().getResource("/component-inspector.css")).toExternalForm());
48 |
49 | highlightPopup.getContent().add(highlightPane);
50 | highlightPane.getStyleClass().add("highlight-pane");
51 | highlightPane.getStylesheets().add(Objects.requireNonNull(this.getClass().getResource("/highlight.css")).toExternalForm());
52 |
53 | }
54 |
55 |
56 | private void inspectAllWindows() {
57 | Window.getWindows().stream().filter(window -> window != popup && window != highlightPopup && !handledWindows.contains(window)).forEach(window -> {
58 | handledWindows.add(window);
59 | window.addEventFilter(MouseEvent.MOUSE_MOVED, event -> handle(event, window.getScene()));
60 | });
61 | }
62 |
63 | private void inspectNode(Node node) {
64 | node.addEventFilter(MouseEvent.MOUSE_MOVED, event -> handle(event, null));
65 | }
66 |
67 |
68 | private void handle(MouseEvent event, Scene scene) {
69 | if (!event.isControlDown()) {
70 | popup.hide();
71 | highlightPopup.hide();
72 | return;
73 | }
74 |
75 | if (!(event.getTarget() instanceof Node)) {
76 | return;
77 | }
78 |
79 | Node componentUnderMouse = (Node) event.getTarget();
80 | if (scene == null) {
81 | scene = componentUnderMouse.getScene();
82 | }
83 |
84 | List hierarchyNodes = componentInspector.inspect(componentUnderMouse);
85 |
86 | pane.getChildren().setAll(hierarchyNodes);
87 | popup.sizeToScene();
88 | if (currentScene != scene) {
89 | popup.hide();
90 | highlightPopup.hide();
91 | currentScene = scene;
92 | }
93 | highlightComponent(componentUnderMouse, scene);
94 | popup.show(scene.getRoot(), event.getScreenX() - pane.getWidth(), event.getScreenY() - pane.getHeight() - OFFSET);
95 | inspectAllWindows();
96 | }
97 |
98 | private void highlightComponent(Node component, Scene scene) {
99 | final Bounds bounds = component.localToScreen(component.getBoundsInLocal());
100 | highlightPane.setPrefSize(bounds.getWidth(), bounds.getHeight());
101 | highlightPopup.sizeToScene();
102 | highlightPopup.show(scene.getRoot(), bounds.getMinX(), bounds.getMinY());
103 | }
104 |
105 | }
106 |
--------------------------------------------------------------------------------
/component-inspector-fx/src/main/resources/component-inspector.css:
--------------------------------------------------------------------------------
1 | .content-pane {
2 | -fx-padding: 10;
3 | }
4 |
5 | .content-pane > * {
6 | -fx-font-size: 10;
7 | -fx-focus-traversable: false;
8 | }
9 |
10 | .component-details {
11 | -fx-spacing: 5;
12 | -fx-padding: 5;
13 | -fx-background-color: white;
14 | -fx-border-color: black;
15 | -fx-border-radius: 8;
16 | -fx-background-radius: 8;
17 | -fx-effect:dropshadow(gaussian, black, 10, 0.5, 0, 0);
18 | }
19 |
20 |
21 | .class-component {
22 | -fx-text-fill: black;
23 | -fx-font-style: italic;
24 | -fx-font-weight: bold;
25 | }
26 |
27 | .field-component, .styles-component {
28 | -fx-opacity: 0.75;
29 | -fx-background-color: lightgray;
30 | -fx-text-fill: black;
31 | -fx-font-weight: bold;
32 | -fx-border-color: black;
33 | -fx-border-width: 2;
34 | -fx-border-radius: 4;
35 | -fx-background-radius: 8;
36 | -fx-padding: 0 10 0 10;
37 | -fx-alignment: center-right;
38 | }
39 |
40 | .styles-component {
41 | -fx-background-color: lightskyblue;
42 | }
43 |
44 |
--------------------------------------------------------------------------------
/component-inspector-fx/src/main/resources/highlight.css:
--------------------------------------------------------------------------------
1 | .highlight-pane {
2 | -fx-background-color: transparent;
3 | -fx-border-color: blue;
4 | -fx-border-style: dotted;
5 | }
--------------------------------------------------------------------------------
/component-inspector-swing/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | com.tangorabox
6 | component-inspector
7 | 1.1.1-SNAPSHOT
8 |
9 |
10 | component-inspector-swing
11 | component-inspector-swing
12 | 1.1.1-SNAPSHOT
13 |
14 |
15 |
16 | com.tangorabox
17 | component-inspector-core
18 | 1.1.1-SNAPSHOT
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/component-inspector-swing/src/main/java/com/tangorabox/componentinspector/swing/SwingComponentInspector.java:
--------------------------------------------------------------------------------
1 | package com.tangorabox.componentinspector.swing;
2 |
3 | import com.tangorabox.componentinspector.core.AbstractComponentInspector;
4 | import com.tangorabox.componentinspector.core.CSSStyleClass;
5 | import com.tangorabox.componentinspector.core.ComponentDetails;
6 | import com.tangorabox.componentinspector.core.ObjectMetadataExtractor;
7 | import com.tangorabox.componentinspector.swing.styling.SwingCSSStyleDecorator;
8 |
9 | import javax.swing.*;
10 | import java.awt.*;
11 | import java.util.List;
12 | import java.util.Optional;
13 |
14 | class SwingComponentInspector extends AbstractComponentInspector {
15 |
16 | private final SwingCSSStyleDecorator cssStyleDecorator = new SwingCSSStyleDecorator();
17 |
18 | @Override
19 | protected Component getParent(Component component) {
20 | if (component == null) {
21 | return null;
22 | }
23 | return component.getParent();
24 | }
25 |
26 | @Override
27 | protected Optional createFieldNameComponent(Component component) {
28 | return new ObjectMetadataExtractor<>(component,this)
29 | .getDeclaredFieldNameInParent()
30 | .map(fieldName -> cssStyleDecorator.decorate(new JLabel(fieldName), CSSStyleClass.FIELD_COMPONENT));
31 | }
32 |
33 | @Override
34 | protected Component createClassComponent(Component component) {
35 | JLabel result = new JLabel(component.getClass().getName());
36 | return cssStyleDecorator.decorate(result, CSSStyleClass.CLASS_COMPONENT);
37 | }
38 |
39 | @Override
40 | protected Component createStylesComponent(Component component) {
41 | return null;
42 | }
43 |
44 | @Override
45 | protected Component createComponentDetailsPanel(ComponentDetails details) {
46 | JPanel jPanel = new JPanel(new FlowLayout());
47 | jPanel.add(details.getClassComponent());
48 | details.getFieldNameComponent().ifPresent(jPanel::add);
49 | Component result = cssStyleDecorator.decorate(jPanel, CSSStyleClass.COMPONENT_DETAILS);
50 | result.setBounds(details.getLocationX(), details.getLocationY(),
51 | result.getPreferredSize().width, result.getPreferredSize().height);
52 | return result;
53 | }
54 |
55 | @Override
56 | protected void buildCascade(List> hierarchy) {
57 | for (int componentLevel = 0, componentIndex = hierarchy.size() - 1; componentIndex >= 0; componentIndex--, componentLevel++) {
58 | ComponentDetails componentChild = hierarchy.get(componentIndex);
59 | componentChild.setLocation(componentIndex * HORIZONTAL_SPACING, componentLevel * VERTICAL_SPACING);
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/component-inspector-swing/src/main/java/com/tangorabox/componentinspector/swing/SwingComponentInspectorHandler.java:
--------------------------------------------------------------------------------
1 | package com.tangorabox.componentinspector.swing;
2 |
3 | import javax.swing.*;
4 | import java.awt.*;
5 | import java.awt.event.MouseEvent;
6 | import java.awt.event.MouseMotionAdapter;
7 | import java.util.Collections;
8 | import java.util.List;
9 | import java.util.Optional;
10 |
11 | public class SwingComponentInspectorHandler {
12 |
13 | private static final int OFFSET = 25;
14 | private final JDialog popup = new JDialog();
15 | private final JDialog highlightPopup = new JDialog();
16 | private final JPanel pane = new JPanel(null);
17 | private final SwingComponentInspector componentInspector = new SwingComponentInspector();
18 |
19 | private static SwingComponentInspectorHandler instance = null;
20 |
21 | public static void handleAll() {
22 | getInstance().inspectAllWindows();
23 | }
24 |
25 | public static void handle(Component component) {
26 | getInstance().inspectComponent(component);
27 | }
28 |
29 |
30 | private static SwingComponentInspectorHandler getInstance() {
31 | if (instance == null) {
32 | instance = new SwingComponentInspectorHandler();
33 | }
34 | return instance;
35 | }
36 |
37 |
38 | private SwingComponentInspectorHandler() {
39 | initPopup(popup);
40 | initPopup(highlightPopup);
41 | highlightPopup.getRootPane().setBorder(BorderFactory.createDashedBorder(Color.BLUE));
42 | popup.getContentPane().add(pane, BorderLayout.CENTER);
43 | pane.setOpaque(false);
44 | }
45 |
46 | private void initPopup(JDialog popup) {
47 | popup.setAlwaysOnTop(true);
48 | popup.setUndecorated(true);
49 | popup.setBackground(new Color(0, 0, 0, 0));
50 | popup.setFocusableWindowState(false);
51 | }
52 |
53 |
54 | private void inspectAllWindows() {
55 | long eventMask = AWTEvent.MOUSE_MOTION_EVENT_MASK;
56 | Toolkit.getDefaultToolkit().addAWTEventListener(event -> handleMouseEvent((MouseEvent) event), eventMask);
57 | }
58 |
59 | private void inspectComponent(Component component) {
60 | component.addMouseMotionListener(new MouseMotionAdapter() {
61 | @Override
62 | public void mouseMoved(MouseEvent e) {
63 | handleMouseEvent(e);
64 | }
65 | });
66 | }
67 |
68 | private void handleMouseEvent(MouseEvent mouseEvent) {
69 |
70 | if (!mouseEvent.isControlDown()) {
71 | popup.setVisible(false);
72 | highlightPopup.setVisible(false);
73 | return;
74 | }
75 |
76 | final Component componentUnderMouse = findComponentUnderMouse(mouseEvent);
77 | List hierarchyNodes = componentInspector.inspect(componentUnderMouse);
78 | if (hierarchyNodes.isEmpty()) {
79 | popup.setVisible(false);
80 | highlightPopup.setVisible(false);
81 | } else {
82 | highlightComponent(componentUnderMouse);
83 | showHierarchyPopup(hierarchyNodes);
84 | }
85 | }
86 |
87 |
88 | private void highlightComponent(Component componentUnderMouse) {
89 | final Rectangle bounds = componentUnderMouse.getBounds();
90 | highlightPopup.getContentPane().setPreferredSize(new Dimension(bounds.width, bounds.height));
91 | highlightPopup.setSize(bounds.width, bounds.height);
92 | highlightPopup.setVisible(true);
93 | highlightPopup.setLocation(componentUnderMouse.getLocationOnScreen());
94 | }
95 |
96 | private Component findComponentUnderMouse(MouseEvent mouseEvent) {
97 | final Point location = MouseInfo.getPointerInfo().getLocation();
98 | final Component topLevelAncestor = findWindowUnderMouse().orElse(mouseEvent.getComponent());
99 | SwingUtilities.convertPointFromScreen(location, topLevelAncestor);
100 | return SwingUtilities.getDeepestComponentAt(topLevelAncestor, location.x, location.y);
101 | }
102 |
103 | private static Optional findWindowUnderMouse() {
104 | for (Window window : Window.getWindows()) {
105 | if (window.getMousePosition(true) != null) {
106 | return Optional.of(window);
107 | }
108 | }
109 | return Optional.empty();
110 | }
111 |
112 | private void showHierarchyPopup(List hierarchyNodes) {
113 | Dimension panelSize = calculatePanelSize(hierarchyNodes);
114 | pane.invalidate();
115 | pane.removeAll();
116 | Collections.reverse(hierarchyNodes);
117 | hierarchyNodes.forEach(pane::add);
118 | pane.revalidate();
119 | pane.repaint();
120 | popup.setSize(panelSize.width, panelSize.height);
121 | popup.setVisible(true);
122 | Point absoluteLocation = MouseInfo.getPointerInfo().getLocation();
123 | popup.setLocation(absoluteLocation.x, absoluteLocation.y - pane.getHeight() - OFFSET);
124 | }
125 |
126 | private Dimension calculatePanelSize(List hierarchyNodes) {
127 | final int maxWidth = hierarchyNodes.stream()
128 | .mapToInt(hierarchyNode -> hierarchyNode.getX() + hierarchyNode.getPreferredSize().width)
129 | .max().orElse(0);
130 |
131 | final int maxHeight = hierarchyNodes.stream().findFirst()
132 | .map(firstComponent -> firstComponent.getPreferredSize().height + firstComponent.getY())
133 | .orElse(0);
134 |
135 | return new Dimension(maxWidth, maxHeight);
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/component-inspector-swing/src/main/java/com/tangorabox/componentinspector/swing/styling/DropShadowPanel.java:
--------------------------------------------------------------------------------
1 | package com.tangorabox.componentinspector.swing.styling;
2 |
3 | import javax.swing.*;
4 | import javax.swing.border.Border;
5 | import java.awt.*;
6 |
7 | class DropShadowPanel extends JPanel {
8 |
9 | private final int pixels;
10 |
11 | public DropShadowPanel(int pix) {
12 | this.pixels = pix;
13 | Border border = BorderFactory.createEmptyBorder(pixels, pixels, pixels, pixels);
14 | this.setBorder(BorderFactory.createCompoundBorder(this.getBorder(), border));
15 | this.setLayout(new BorderLayout());
16 | }
17 |
18 | @Override
19 | protected void paintComponent(Graphics g) {
20 | int shade = 0;
21 | int topOpacity = 255;
22 | for (int i = 0; i < pixels; i++) {
23 | g.setColor(new Color(shade, shade, shade, ((topOpacity / pixels) * i)));
24 | g.drawRect(i, i, this.getWidth() - ((i * 2) + 1), this.getHeight() - ((i * 2) + 1));
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/component-inspector-swing/src/main/java/com/tangorabox/componentinspector/swing/styling/SwingCSSStyleDecorator.java:
--------------------------------------------------------------------------------
1 | package com.tangorabox.componentinspector.swing.styling;
2 |
3 | import com.tangorabox.componentinspector.core.CSSStyleClass;
4 |
5 | import javax.swing.*;
6 | import javax.swing.border.Border;
7 | import java.awt.*;
8 |
9 | public class SwingCSSStyleDecorator {
10 |
11 | private static final float FONT_SIZE = 10f;
12 | private static final int PADDING = 1;
13 |
14 | public Component decorate(JComponent component, CSSStyleClass styleClass) {
15 | if (styleClass == null) {
16 | return component;
17 | }
18 | Font font = component.getFont().deriveFont(FONT_SIZE);
19 | component.setFont(font);
20 |
21 | switch (styleClass) {
22 | case COMPONENT_DETAILS: return decorateComponentDetails(component);
23 | case CLASS_COMPONENT: return decorateClassComponent(component);
24 | case FIELD_COMPONENT: return decorateFieldComponent(component);
25 | default: return component;
26 | }
27 | }
28 |
29 |
30 | private Component decorateComponentDetails(JComponent component) {
31 | Border paddingBorder = BorderFactory.createEmptyBorder(PADDING, PADDING, PADDING, PADDING);
32 | component.setBorder(paddingBorder);
33 | DropShadowPanel dropShadowPanel = new DropShadowPanel(5);
34 |
35 | dropShadowPanel.add(component, BorderLayout.CENTER);
36 | return dropShadowPanel;
37 | }
38 |
39 | private Component decorateClassComponent(Component component) {
40 | Font font = component.getFont().deriveFont(Font.BOLD + Font.ITALIC);
41 | component.setFont(font);
42 | return component;
43 | }
44 |
45 | private Component decorateFieldComponent(JComponent component) {
46 | component.setOpaque(true);
47 | component.setBackground(Color.LIGHT_GRAY);
48 | Font font = component.getFont().deriveFont(Font.BOLD);
49 | component.setFont(font);
50 |
51 | Border lineBorder = BorderFactory.createLineBorder(Color.BLACK, 1, true);
52 | Border paddingBorder = BorderFactory.createEmptyBorder(0, 10, 0, 10);
53 |
54 | component.setBorder(BorderFactory.createCompoundBorder(lineBorder, paddingBorder));
55 | return component;
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/component-inspector-swing/src/main/resources/component-inspector.css:
--------------------------------------------------------------------------------
1 | .content-pane > * {
2 | -fx-font-size: 10;
3 | }
4 |
5 | .component-details {
6 | -fx-spacing: 5;
7 | -fx-padding: 5;
8 | -fx-background-color: white;
9 | -fx-border-color: black;
10 | -fx-border-radius: 8;
11 | -fx-background-radius: 8;
12 | -fx-effect:dropshadow(gaussian, black, 10, 0.5, 0, 0);
13 | }
14 |
15 |
16 | .class-component {
17 | -fx-text-fill: black;
18 | -fx-font-style: italic;
19 | -fx-font-weight: bold;
20 | }
21 |
22 | .field-component, .styles-component {
23 | -fx-opacity: 0.75;
24 | -fx-background-color: lightgray;
25 | -fx-text-fill: black;
26 | -fx-font-weight: bold;
27 | -fx-border-color: black;
28 | -fx-border-width: 2;
29 | -fx-border-radius: 4;
30 | -fx-background-radius: 8;
31 | -fx-padding: 0 10 0 10;
32 | -fx-alignment: center-right;
33 | }
34 |
35 | .styles-component {
36 | -fx-background-color: lightskyblue;
37 | }
38 |
39 |
--------------------------------------------------------------------------------
/doc/images/FXInspector.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TangoraBox/ComponentInspector/9d2707132473eab25a148dc04a7435e399746137/doc/images/FXInspector.png
--------------------------------------------------------------------------------
/doc/images/JavaFXWithSwingNode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TangoraBox/ComponentInspector/9d2707132473eab25a148dc04a7435e399746137/doc/images/JavaFXWithSwingNode.png
--------------------------------------------------------------------------------
/doc/images/SwingInspector.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TangoraBox/ComponentInspector/9d2707132473eab25a148dc04a7435e399746137/doc/images/SwingInspector.png
--------------------------------------------------------------------------------
/doc/images/SwingWithJavaFXPanel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TangoraBox/ComponentInspector/9d2707132473eab25a148dc04a7435e399746137/doc/images/SwingWithJavaFXPanel.png
--------------------------------------------------------------------------------
/doc/images/css-class.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TangoraBox/ComponentInspector/9d2707132473eab25a148dc04a7435e399746137/doc/images/css-class.png
--------------------------------------------------------------------------------
/doc/images/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TangoraBox/ComponentInspector/9d2707132473eab25a148dc04a7435e399746137/doc/images/demo.gif
--------------------------------------------------------------------------------
/doc/images/field-name.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TangoraBox/ComponentInspector/9d2707132473eab25a148dc04a7435e399746137/doc/images/field-name.png
--------------------------------------------------------------------------------
/hybrid-example/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | com.tangorabox
6 | component-inspector
7 | 1.1.1-SNAPSHOT
8 |
9 |
10 | hybrid-example
11 | hybrid-example
12 | 1.1.1-SNAPSHOT
13 |
14 |
15 |
16 | com.tangorabox
17 | component-inspector-fx
18 | 1.1.1-SNAPSHOT
19 |
20 |
21 | com.tangorabox
22 | component-inspector-swing
23 | 1.1.1-SNAPSHOT
24 |
25 |
26 | com.tangorabox
27 | javafx-example
28 | 1.1.1-SNAPSHOT
29 |
30 |
31 | com.tangorabox
32 | swing-example
33 | 1.1.1-SNAPSHOT
34 |
35 |
36 | org.openjfx
37 | javafx-graphics
38 | 11.0.2
39 |
40 |
41 | org.openjfx
42 | javafx-fxml
43 | 11.0.2
44 |
45 |
46 | org.openjfx
47 | javafx-swing
48 | 11.0.2
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/hybrid-example/src/main/java/com/tangorabox/componentinspector/hybrid/javafxwithswing/JavaFXExampleWithSwingLauncher.java:
--------------------------------------------------------------------------------
1 | package com.tangorabox.componentinspector.hybrid.javafxwithswing;
2 |
3 | import javafx.application.Application;
4 |
5 | public class JavaFXExampleWithSwingLauncher {
6 |
7 | public static void main(String[] args) {
8 | Application.launch(JavaFXExampleWithSwingNodeApp.class, args);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/hybrid-example/src/main/java/com/tangorabox/componentinspector/hybrid/javafxwithswing/JavaFXExampleWithSwingNodeApp.java:
--------------------------------------------------------------------------------
1 | package com.tangorabox.componentinspector.hybrid.javafxwithswing;
2 |
3 | import com.tangorabox.componentinspector.fx.FXComponentInspectorHandler;
4 | import javafx.application.Application;
5 | import javafx.scene.Scene;
6 | import javafx.stage.Stage;
7 |
8 | public class JavaFXExampleWithSwingNodeApp extends Application {
9 | @Override
10 | public void start(Stage primaryStage) {
11 | JavaFXWindowWithSwingPanel windowPanel = new JavaFXWindowWithSwingPanel();
12 | primaryStage.setScene(new Scene(windowPanel));
13 | primaryStage.setWidth(800);
14 | primaryStage.setHeight(600);
15 | primaryStage.show();
16 | FXComponentInspectorHandler.handleAll();
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/hybrid-example/src/main/java/com/tangorabox/componentinspector/hybrid/javafxwithswing/JavaFXWindowWithSwingPanel.java:
--------------------------------------------------------------------------------
1 | package com.tangorabox.componentinspector.hybrid.javafxwithswing;
2 |
3 | import com.tangorabox.componentinspector.swing.SwingComponentInspectorHandler;
4 | import com.tangorabox.componentinspector.swing.SwingWindowPanel;
5 | import javafx.embed.swing.SwingNode;
6 | import javafx.fxml.FXML;
7 | import javafx.fxml.FXMLLoader;
8 | import javafx.scene.control.ComboBox;
9 | import javafx.scene.control.TitledPane;
10 | import javafx.scene.layout.BorderPane;
11 | import javafx.scene.layout.HBox;
12 | import javafx.scene.layout.VBox;
13 |
14 | import javax.swing.*;
15 | import java.io.IOException;
16 | import java.io.UncheckedIOException;
17 |
18 | public class JavaFXWindowWithSwingPanel extends BorderPane {
19 |
20 | @FXML
21 | private SwingNode swingNode;
22 |
23 | @FXML
24 | private HBox panelUp;
25 |
26 | @FXML
27 | private ComboBox comboBox;
28 |
29 | @FXML
30 | private HBox panelDown;
31 |
32 | @FXML
33 | private TitledPane titledPane;
34 |
35 | @FXML
36 | private VBox panelLeft;
37 |
38 | @FXML
39 | private VBox panelRight;
40 |
41 | public JavaFXWindowWithSwingPanel() {
42 | FXMLLoader loader = new FXMLLoader(getClass().getResource("/JavaFxWindow.fxml"));
43 | loader.setRoot(this);
44 | loader.setController(this);
45 | try {
46 | loader.load();
47 | comboBox.getItems().setAll("One", "Two", "Tree");
48 | comboBox.getSelectionModel().selectFirst();
49 | createSwingContent();
50 | } catch (IOException ex) {
51 | throw new UncheckedIOException(ex);
52 | }
53 | }
54 |
55 | private void createSwingContent() {
56 | SwingUtilities.invokeLater(() -> {
57 | SwingWindowPanel swingWindowPanel = new SwingWindowPanel();
58 | swingNode.setContent(swingWindowPanel);
59 | SwingComponentInspectorHandler.handle(swingWindowPanel);
60 | });
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/hybrid-example/src/main/java/com/tangorabox/componentinspector/hybrid/swingwithjavafx/SwingExampleWithJavaFXLauncher.java:
--------------------------------------------------------------------------------
1 | package com.tangorabox.componentinspector.hybrid.swingwithjavafx;
2 |
3 | import com.tangorabox.componentinspector.swing.SwingComponentInspectorHandler;
4 |
5 | import javax.swing.*;
6 | import java.awt.*;
7 | import java.awt.event.WindowAdapter;
8 | import java.awt.event.WindowEvent;
9 |
10 | public class SwingExampleWithJavaFXLauncher {
11 |
12 | public static void main(String[] args) {
13 | SwingUtilities.invokeLater(() -> {
14 | JFrame frame = new JFrame();
15 | frame.getContentPane().add(new SwingWindowWithJavaFXPanel(), BorderLayout.CENTER);
16 | frame.setSize(800, 600);
17 | frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
18 | frame.setVisible(true);
19 | frame.addWindowListener(new WindowAdapter() {
20 | @Override
21 | public void windowClosing(WindowEvent e) {
22 | System.exit(0);
23 | }
24 | });
25 | SwingComponentInspectorHandler.handleAll();
26 | });
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/hybrid-example/src/main/java/com/tangorabox/componentinspector/hybrid/swingwithjavafx/SwingWindowWithJavaFXPanel.java:
--------------------------------------------------------------------------------
1 | package com.tangorabox.componentinspector.hybrid.swingwithjavafx;
2 |
3 | import com.tangorabox.componentinspector.fx.FXComponentInspectorHandler;
4 | import com.tangorabox.componentinspector.fx.JavaFXWindowPanel;
5 | import javafx.application.Platform;
6 | import javafx.embed.swing.JFXPanel;
7 | import javafx.scene.Scene;
8 |
9 | import javax.swing.*;
10 | import java.awt.*;
11 |
12 | public class SwingWindowWithJavaFXPanel extends JPanel {
13 |
14 | private final JFXPanel javaFXPanel = new JFXPanel();
15 |
16 | private final Component panelUp = createUpPanel();
17 | private final Component panelDown = createPanelDown();
18 | private final Component panelLeft = createPanelLeft();
19 | private final Component panelRight = createPanelRight();
20 | private final Component panelCenter = createPanelCenter();
21 |
22 |
23 | public SwingWindowWithJavaFXPanel() {
24 | setLayout(new BorderLayout());
25 | add(panelUp, BorderLayout.NORTH);
26 | add(panelDown, BorderLayout.SOUTH);
27 | add(panelLeft, BorderLayout.WEST);
28 | add(panelRight, BorderLayout.EAST);
29 | add(panelCenter, BorderLayout.CENTER);
30 | createJavaFXContent();
31 | }
32 |
33 | private void createJavaFXContent() {
34 | Platform.runLater(() -> {
35 | // This method is invoked on the JavaFX thread
36 | JavaFXWindowPanel javaFXWindowPanel = new JavaFXWindowPanel();
37 | Scene scene = new Scene(javaFXWindowPanel);
38 | javaFXPanel.setScene(scene);
39 | FXComponentInspectorHandler.handle(javaFXWindowPanel);
40 | });
41 | }
42 |
43 | private Component createUpPanel() {
44 | return new JComboBox<>(new String[]{"One", "Two", "Tree"});
45 | }
46 |
47 | private Component createPanelDown() {
48 | JPanel result = new JPanel(new FlowLayout(FlowLayout.CENTER));
49 | result.setBorder(BorderFactory.createTitledBorder("South Panel"));
50 | int buttonCount = 4;
51 | for (int i = 1; i <= buttonCount; i++) {
52 | JButton button = new JButton("Button" + i);
53 | result.add(button);
54 | }
55 | return result;
56 | }
57 |
58 | private Component createPanelLeft() {
59 | JPanel result = new JPanel(new BorderLayout());
60 | result.add(new JButton("Left Panel"), BorderLayout.CENTER);
61 | return result;
62 | }
63 |
64 | private Component createPanelRight() {
65 | JPanel result = new JPanel(new BorderLayout());
66 | result.add(new JButton("Right Panel"), BorderLayout.CENTER);
67 | return result;
68 | }
69 |
70 | private Component createPanelCenter() {
71 | JPanel result = new JPanel(new BorderLayout());
72 | result.setBorder(BorderFactory.createTitledBorder("ParentPanel"));
73 |
74 | int panelCount = 4;
75 |
76 | JPanel targetPanel = result;
77 |
78 | for (int i = 1; i <= panelCount; i++) {
79 | JPanel child = new JPanel(new BorderLayout());
80 | child.setBorder(BorderFactory.createTitledBorder("ChildPanel" + i));
81 | targetPanel.add(child);
82 | targetPanel = child;
83 | }
84 |
85 | JPanel helloWorldPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
86 | helloWorldPanel.add(javaFXPanel);
87 | targetPanel.add(helloWorldPanel, BorderLayout.CENTER);
88 |
89 | return result;
90 | }
91 |
92 |
93 | }
94 |
--------------------------------------------------------------------------------
/hybrid-example/src/main/resources/JavaFxWindow.fxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
22 |
23 |
24 |
25 |
27 |
29 |
31 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
53 |
54 |
55 |
56 |
57 |
59 |
60 |
62 |
63 |
65 |
66 |
68 |
69 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/javafx-example/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | com.tangorabox
6 | component-inspector
7 | 1.1.1-SNAPSHOT
8 |
9 |
10 | javafx-example
11 | javafx-example
12 | 1.1.1-SNAPSHOT
13 |
14 |
15 |
16 | com.tangorabox
17 | component-inspector-fx
18 | 1.1.1-SNAPSHOT
19 |
20 |
21 | org.openjfx
22 | javafx-graphics
23 | 11.0.2
24 |
25 |
26 | org.openjfx
27 | javafx-fxml
28 | 11.0.2
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/javafx-example/src/main/java/com/tangorabox/componentinspector/fx/JavaFXExampleApp.java:
--------------------------------------------------------------------------------
1 | package com.tangorabox.componentinspector.fx;
2 |
3 | import javafx.application.Application;
4 | import javafx.scene.Scene;
5 | import javafx.stage.Stage;
6 |
7 | public class JavaFXExampleApp extends Application {
8 | @Override
9 | public void start(Stage primaryStage) throws Exception {
10 | JavaFXWindowPanel windowPanel = new JavaFXWindowPanel();
11 | primaryStage.setScene(new Scene(windowPanel));
12 | primaryStage.sizeToScene();
13 | primaryStage.show();
14 | FXComponentInspectorHandler.handleAll();
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/javafx-example/src/main/java/com/tangorabox/componentinspector/fx/JavaFXExampleLauncher.java:
--------------------------------------------------------------------------------
1 | package com.tangorabox.componentinspector.fx;
2 |
3 | import javafx.application.Application;
4 |
5 | public class JavaFXExampleLauncher {
6 |
7 | public static void main(String[] args) {
8 | Application.launch(JavaFXExampleApp.class, args);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/javafx-example/src/main/java/com/tangorabox/componentinspector/fx/JavaFXWindowPanel.java:
--------------------------------------------------------------------------------
1 | package com.tangorabox.componentinspector.fx;
2 |
3 | import javafx.fxml.FXML;
4 | import javafx.fxml.FXMLLoader;
5 | import javafx.scene.control.ComboBox;
6 | import javafx.scene.control.TitledPane;
7 | import javafx.scene.layout.BorderPane;
8 | import javafx.scene.layout.HBox;
9 | import javafx.scene.layout.VBox;
10 |
11 | import java.io.IOException;
12 | import java.io.UncheckedIOException;
13 |
14 | public class JavaFXWindowPanel extends BorderPane {
15 |
16 | @FXML
17 | private HBox panelUp;
18 |
19 | @FXML
20 | private ComboBox comboBox;
21 |
22 | @FXML
23 | private HBox panelDown;
24 |
25 | @FXML
26 | private TitledPane titledPane;
27 |
28 | @FXML
29 | private VBox panelLeft;
30 |
31 | @FXML
32 | private VBox panelRight;
33 |
34 | public JavaFXWindowPanel() {
35 | FXMLLoader loader = new FXMLLoader(getClass().getResource("/JavaFxWindow.fxml"));
36 | loader.setRoot(this);
37 | loader.setController(this);
38 | try {
39 | loader.load();
40 | comboBox.getItems().setAll("One", "Two", "Tree");
41 | comboBox.getSelectionModel().selectFirst();
42 | } catch (IOException ex) {
43 | throw new UncheckedIOException(ex);
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/javafx-example/src/main/resources/JavaFxWindow.fxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | pom
5 |
6 | com.tangorabox
7 | component-inspector
8 | 1.1.1-SNAPSHOT
9 | component-inspector
10 |
11 | https://github.com/TangoraBox/ComponentInspector
12 | Java Desktop (JavaFX and Swing) Component Inspector on mouse over
13 |
14 |
15 | component-inspector-core
16 | component-inspector-fx
17 | component-inspector-swing
18 | javafx-example
19 | swing-example
20 | hybrid-example
21 |
22 |
23 |
24 | UTF-8
25 | UTF-8
26 | 11
27 | 2020
28 |
29 |
30 |
31 | https://github.com/TangoraBox/ComponentInspector/issues
32 | GitHub Issues
33 |
34 |
35 |
36 |
37 | GNU LGPL v3
38 | https://www.gnu.org/licenses/lgpl-3.0.txt
39 |
40 |
41 |
42 |
43 |
44 |
45 | ossrh
46 | https://oss.sonatype.org/content/repositories/snapshots
47 |
48 |
49 | ossrh
50 | https://oss.sonatype.org/service/local/staging/deploy/maven2/
51 |
52 |
53 |
54 |
55 |
56 | scm:git:git://github.com/TangoraBox/ComponentInspector.git
57 | scm:git:git@github.com:TangoraBox/ComponentInspector.git
58 | https://github.com/TangoraBox/ComponentInspector
59 | HEAD
60 |
61 |
62 |
63 | ComponentInspector by Tangorabox.com
64 | https://tangorabox.com
65 |
66 |
67 |
68 |
69 | GaRzY
70 | info@tangorabox.com
71 | https://github.com/garzy
72 | GaRzY
73 |
74 | Founder
75 | Maintainer
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 | org.apache.maven.plugins
84 | maven-compiler-plugin
85 | 3.8.1
86 |
87 | 11
88 | 11
89 |
90 |
91 |
92 | maven-deploy-plugin
93 | 2.8.2
94 |
95 |
96 | default-deploy
97 | deploy
98 |
99 | deploy
100 |
101 |
102 |
103 |
104 |
105 | org.apache.maven.plugins
106 | maven-release-plugin
107 | 2.5.3
108 |
109 | true
110 | false
111 | forked-path
112 | -Dgpg.passphrase=${gpg.passphrase}
113 |
114 |
115 |
116 | org.apache.maven.scm
117 | maven-scm-provider-gitexe
118 | 1.9.4
119 |
120 |
121 |
122 |
123 | org.sonatype.plugins
124 | nexus-staging-maven-plugin
125 | 1.6.8
126 | true
127 |
128 | ossrh
129 | https://oss.sonatype.org/
130 | true
131 |
132 |
133 |
134 | org.apache.maven.plugins
135 | maven-source-plugin
136 | 3.1.0
137 |
138 |
139 | attach-sources
140 |
141 | jar
142 |
143 |
144 |
145 |
146 |
147 | org.apache.maven.plugins
148 | maven-javadoc-plugin
149 | 3.1.1
150 |
151 |
152 | attach-javadocs
153 |
154 | jar
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 | release-sign-artifacts
166 |
167 |
168 | performRelease
169 | true
170 |
171 |
172 |
173 |
174 |
175 | org.apache.maven.plugins
176 | maven-gpg-plugin
177 | 1.6
178 |
179 |
180 | sign-artifacts
181 | verify
182 |
183 | sign
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
--------------------------------------------------------------------------------
/swing-example/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | com.tangorabox
6 | component-inspector
7 | 1.1.1-SNAPSHOT
8 |
9 |
10 | swing-example
11 | swing-example
12 | 1.1.1-SNAPSHOT
13 |
14 |
15 |
16 | com.tangorabox
17 | component-inspector-swing
18 | 1.1.1-SNAPSHOT
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/swing-example/src/main/java/com/tangorabox/componentinspector/swing/SwingExampleLauncher.java:
--------------------------------------------------------------------------------
1 | package com.tangorabox.componentinspector.swing;
2 |
3 | import javax.swing.*;
4 | import java.awt.*;
5 | import java.awt.event.WindowAdapter;
6 | import java.awt.event.WindowEvent;
7 |
8 | public class SwingExampleLauncher {
9 |
10 | public static void main(String[] args) {
11 | SwingUtilities.invokeLater(() -> {
12 | JFrame frame = new JFrame();
13 | frame.getContentPane().add(new SwingWindowPanel(), BorderLayout.CENTER);
14 | frame.setSize(600, 400);
15 | frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
16 | frame.setVisible(true);
17 | frame.addWindowListener(new WindowAdapter() {
18 | @Override
19 | public void windowClosing(WindowEvent e) {
20 | System.exit(0);
21 | }
22 | });
23 | SwingComponentInspectorHandler.handleAll();
24 | });
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/swing-example/src/main/java/com/tangorabox/componentinspector/swing/SwingWindowPanel.java:
--------------------------------------------------------------------------------
1 | package com.tangorabox.componentinspector.swing;
2 |
3 | import javax.swing.*;
4 | import java.awt.*;
5 |
6 | public class SwingWindowPanel extends JPanel {
7 |
8 | private final JPanel helloWorldPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
9 | private final JLabel helloWorldLabel = new JLabel("Hello World!!");
10 |
11 | private final Component panelUp = createUpPanel();
12 | private final Component panelDown = createPanelDown();
13 | private final Component panelLeft = createPanelLeft();
14 | private final Component panelRight = createPanelRight();
15 | private final Component panelCenter = createPanelCenter();
16 |
17 | public SwingWindowPanel() {
18 | setLayout(new BorderLayout());
19 | add(panelUp, BorderLayout.NORTH);
20 | add(panelDown, BorderLayout.SOUTH);
21 | add(panelLeft, BorderLayout.WEST);
22 | add(panelRight, BorderLayout.EAST);
23 | add(panelCenter, BorderLayout.CENTER);
24 | }
25 |
26 | private Component createUpPanel() {
27 | return new JComboBox<>(new String[]{"One", "Two", "Tree"});
28 | }
29 |
30 | private Component createPanelDown() {
31 | JPanel result = new JPanel(new FlowLayout(FlowLayout.CENTER));
32 | result.setBorder(BorderFactory.createTitledBorder("South Panel"));
33 | int buttonCount = 4;
34 | for (int i = 1; i <= buttonCount; i++) {
35 | JButton button = new JButton("Button" + i);
36 | result.add(button);
37 | }
38 | return result;
39 | }
40 |
41 | private Component createPanelLeft() {
42 | JPanel result = new JPanel(new BorderLayout());
43 | result.add(new JButton("Left Panel"), BorderLayout.CENTER);
44 | return result;
45 | }
46 |
47 | private Component createPanelRight() {
48 | JPanel result = new JPanel(new BorderLayout());
49 | result.add(new JButton("Right Panel"), BorderLayout.CENTER);
50 | return result;
51 | }
52 |
53 | private Component createPanelCenter() {
54 | JPanel result = new JPanel(new BorderLayout());
55 | result.setBorder(BorderFactory.createTitledBorder("ParentPanel"));
56 |
57 | int panelCount = 4;
58 |
59 | JPanel targetPanel = result;
60 |
61 | for (int i = 1; i <= panelCount; i++) {
62 | JPanel child = new JPanel(new BorderLayout());
63 | child.setBorder(BorderFactory.createTitledBorder("ChildPanel" + i));
64 | targetPanel.add(child);
65 | targetPanel = child;
66 | }
67 |
68 | helloWorldPanel.add(helloWorldLabel);
69 | targetPanel.add(helloWorldPanel, BorderLayout.CENTER);
70 |
71 | return result;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------