2 * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 package org
.lttng
.scope
.tmf2
.views
.ui
.jfx
;
12 import static java
.util
.Objects
.requireNonNull
;
14 import java
.lang
.invoke
.MethodHandle
;
15 import java
.lang
.invoke
.MethodHandles
;
16 import java
.lang
.reflect
.Method
;
17 import java
.util
.List
;
19 import javafx
.application
.Platform
;
20 import javafx
.beans
.property
.ReadOnlyDoubleProperty
;
21 import javafx
.beans
.property
.SimpleDoubleProperty
;
22 import javafx
.geometry
.Rectangle2D
;
23 import javafx
.scene
.Node
;
24 import javafx
.scene
.control
.Dialog
;
25 import javafx
.scene
.control
.OverrunStyle
;
26 import javafx
.scene
.text
.Font
;
27 import javafx
.stage
.Screen
;
28 import javafx
.stage
.Window
;
31 * JavaFX-related utilities
33 * @author Alexandre Montplaisir
35 public final class JfxUtils
{
38 * Double property with a non-modifiable value of 0. For things that should
41 public static final ReadOnlyDoubleProperty ZERO_PROPERTY
= new SimpleDoubleProperty(0);
44 private static final MethodHandles
.Lookup LOOKUP
= requireNonNull(MethodHandles
.lookup());
45 private static final MethodHandle COMPUTE_CLIPPED_TEXT_HANDLE
;
47 MethodHandle handle
= null;
49 Class
<?
> c
= Class
.forName("com.sun.javafx.scene.control.skin.Utils"); //$NON-NLS-1$
50 Method method
= c
.getDeclaredMethod("computeClippedText", //$NON-NLS-1$
51 Font
.class, String
.class, double.class, OverrunStyle
.class, String
.class);
52 method
.setAccessible(true);
53 handle
= LOOKUP
.unreflect(method
);
54 } catch (ClassNotFoundException
| NoSuchMethodException
| SecurityException
| IllegalAccessException e
) {
55 /* Should not fail if we did everything correctly. */
57 COMPUTE_CLIPPED_TEXT_HANDLE
= requireNonNull(handle
);
62 * com.sun.javafx.scene.control.skin.Utils.computeClippedText() method.
64 * This method implements the logic to clip Label strings. It can be useful
65 * for other types, like Text. Unfortunately it is not public, but this
66 * accessor allows calling it through reflection. It makes use of
67 * {@link MethodHandle#invokeExact}, which should be close to just as fast
68 * as a standard compiled method call.
71 * The font of the text that will be used
75 * The maximum width we want to limit the string to
77 * The {@link OverrunStyle}
78 * @param ellipsisString
79 * The string to use as ellipsis
80 * @return The clipped string, or "ERROR" if an error happened.
81 * Unfortunately we lose the exception typing due to the reflection
82 * call, so we do not want to throw "Throwable" here.
84 public static String
computeClippedText(Font font
, String text
, double width
,
85 OverrunStyle type
, String ellipsisString
) {
87 String str
= (String
) COMPUTE_CLIPPED_TEXT_HANDLE
.invokeExact(font
, text
, width
, type
, ellipsisString
);
88 return requireNonNull(str
);
89 } catch (Throwable e
) {
90 return "ERROR"; //$NON-NLS-1$
95 * Run the given {@link Runnable} on the UI/main/application thread.
97 * If you know for sure you are *not* on the main thread, you should use
98 * {@link Platform#runLater} to queue the runnable for the main thread.
100 * If you are not sure, you can use this method. The difference with
101 * {@link Platform#runLater} is that if you are actually already on the UI
102 * thread, the runnable will be run immediately. Whereas calling runLater
103 * from the UI will just queue the runnable at the end of the queue.
106 * The runnable to run on the main thread
108 public static void runOnMainThread(Runnable r
) {
109 if (Platform
.isFxApplicationThread()) {
112 Platform
.runLater(r
);
117 * Utility method to center a Dialog/Alert on the middle of the current
118 * screen. Used to workaround a "bug" with the current version of JavaFX (or
119 * the SWT/JavaFX embedding?) where alerts always show on the primary
120 * screen, not necessarily the current one.
123 * The dialog to reposition. It must be already shown, or else
124 * this will do nothing.
125 * @param referenceNode
126 * The dialog should be moved to the same screen as this node's
129 public static void centerDialogOnScreen(Dialog
<?
> dialog
, Node referenceNode
) {
130 Window window
= referenceNode
.getScene().getWindow();
131 Rectangle2D windowRectangle
= new Rectangle2D(window
.getX(), window
.getY(), window
.getWidth(), window
.getHeight());
133 List
<Screen
> screens
= Screen
.getScreensForRectangle(windowRectangle
);
134 Screen screen
= screens
.stream()
136 .orElse(Screen
.getPrimary());
138 Rectangle2D screenBounds
= screen
.getBounds();
139 dialog
.setX((screenBounds
.getWidth() - dialog
.getWidth()) / 2 + screenBounds
.getMinX());
140 // dialog.setY((screenBounds.getHeight() - dialog.getHeight()) / 2 + screenBounds.getMinY());