1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.github.tonywasher.joceanus.tethys.javafx.base;
18
19 import io.github.tonywasher.joceanus.oceanus.convert.OceanusDataConverter;
20 import javafx.geometry.Bounds;
21 import javafx.geometry.Dimension2D;
22 import javafx.geometry.Insets;
23 import javafx.geometry.Point2D;
24 import javafx.geometry.Pos;
25 import javafx.geometry.Rectangle2D;
26 import javafx.geometry.Side;
27 import javafx.scene.Node;
28 import javafx.scene.Scene;
29 import javafx.scene.control.Label;
30 import javafx.scene.image.Image;
31 import javafx.scene.image.ImageView;
32 import javafx.scene.layout.HBox;
33 import javafx.scene.layout.Pane;
34 import javafx.scene.layout.Priority;
35 import javafx.scene.layout.StackPane;
36 import javafx.scene.paint.Color;
37 import javafx.stage.Screen;
38 import javafx.stage.Window;
39 import io.github.tonywasher.joceanus.tethys.api.base.TethysUIIconId;
40
41 import java.util.List;
42
43
44
45
46 public final class TethysUIFXUtils {
47
48
49
50 public static final String CSS_STYLE_BASE = "-jtethys";
51
52
53
54
55 private static final String STYLE_TITLED = CSS_STYLE_BASE + "-titled";
56
57
58
59
60 private static final String STYLE_TITLE = STYLE_TITLED + "-title";
61
62
63
64
65 private static final String STYLE_BORDER = STYLE_TITLED + "-border";
66
67
68
69
70 private static final String STYLE_CONTENT = STYLE_TITLED + "-content";
71
72
73
74
75 private static final String RGB_HDR = "#";
76
77
78
79
80 private TethysUIFXUtils() {
81 }
82
83
84
85
86
87
88
89 public static String colorToHexString(final Color pValue) {
90
91 final StringBuilder myBuilder = new StringBuilder();
92 myBuilder.append(RGB_HDR);
93 appendColorPart(myBuilder, pValue.getRed());
94 appendColorPart(myBuilder, pValue.getGreen());
95 appendColorPart(myBuilder, pValue.getBlue());
96 return myBuilder.toString();
97 }
98
99
100
101
102
103
104
105 private static void appendColorPart(final StringBuilder pBuilder,
106 final double pValue) {
107
108 final int myMax = OceanusDataConverter.BYTE_MASK + 1;
109 int myValue = (int) (pValue * myMax);
110
111
112 if (myValue == myMax) {
113 myValue--;
114 }
115
116
117 int myDigit = myValue >>> OceanusDataConverter.NYBBLE_SHIFT;
118 char myChar = Character.forDigit(myDigit, OceanusDataConverter.HEX_RADIX);
119 pBuilder.append(myChar);
120
121
122 myDigit = myValue
123 & OceanusDataConverter.NYBBLE_MASK;
124 myChar = Character.forDigit(myDigit, OceanusDataConverter.HEX_RADIX);
125 pBuilder.append(myChar);
126 }
127
128
129
130
131
132
133
134
135
136 static Pane getBorderedPane(final String pTitle,
137 final Integer pPadding,
138 final Node pNode) {
139
140 final Pane myPane;
141 if (!(pNode instanceof Pane p)) {
142
143 final HBox myBox = new HBox();
144 myBox.getChildren().add(pNode);
145 myPane = myBox;
146
147
148 HBox.setHgrow(pNode, Priority.ALWAYS);
149 } else {
150 myPane = p;
151 }
152
153
154 if (pTitle == null
155 && pPadding == null) {
156 return myPane;
157 }
158
159
160 final StackPane myPanel = new StackPane();
161
162
163 if (pTitle != null) {
164 final Label myTitle = new Label(pTitle);
165 StackPane.setAlignment(myTitle, Pos.TOP_LEFT);
166 StackPane.setAlignment(myPane, Pos.CENTER);
167 myPanel.getChildren().add(myTitle);
168
169
170 myPane.getStyleClass().add(STYLE_CONTENT);
171 myTitle.getStyleClass().add(STYLE_TITLE);
172 myPanel.getStyleClass().add(STYLE_BORDER);
173 }
174
175
176 if (pPadding != null) {
177 myPanel.setPadding(new Insets(pPadding, pPadding, pPadding, pPadding));
178 }
179
180
181 myPanel.getChildren().add(myPane);
182
183
184 return myPanel;
185 }
186
187
188
189
190
191
192
193
194
195 public static Point2D obtainDisplayPoint(final Node pAnchor,
196 final Point2D pLocation,
197 final Dimension2D pSize) {
198
199 final Screen myScreen = getScreenForNode(pAnchor);
200
201
202 final Point2D myLocation = getLocationForNode(pAnchor, pLocation);
203
204
205 Rectangle2D myArea = new Rectangle2D(myLocation.getX(), myLocation.getY(),
206 pSize.getWidth(), pSize.getHeight());
207 myArea = adjustDisplayLocation(myArea, myScreen);
208
209
210 return new Point2D(myArea.getMinX(), myArea.getMinY());
211 }
212
213
214
215
216
217
218
219
220
221 public static Point2D obtainDisplayPoint(final Node pAnchor,
222 final Side pSide,
223 final Dimension2D pSize) {
224
225 final Screen myScreen = getScreenForNode(pAnchor);
226
227
228 final Point2D myLocation = getOriginForNode(pAnchor);
229
230
231 Rectangle2D myArea = new Rectangle2D(myLocation.getX(), myLocation.getY(),
232 pSize.getWidth(), pSize.getHeight());
233 myArea = adjustDisplayLocation(myArea, pAnchor, pSide, myScreen);
234
235
236 return new Point2D(myArea.getMinX(), myArea.getMinY());
237 }
238
239
240
241
242
243
244
245 private static Screen getScreenForNode(final Node pAnchor) {
246
247 final List<Screen> myScreens = Screen.getScreens();
248
249
250 final Point2D myOrigin = getOriginForNode(pAnchor);
251
252
253 final Bounds myLocalBounds = pAnchor.getBoundsInLocal();
254 final Rectangle2D myBounds = new Rectangle2D(myOrigin.getX(),
255 myOrigin.getY(),
256 myLocalBounds.getWidth(),
257 myLocalBounds.getHeight());
258
259
260 double myBest = 0;
261 Screen myBestScreen = null;
262
263
264 for (final Screen myScreen : myScreens) {
265 final Rectangle2D myScreenBounds = myScreen.getBounds();
266
267
268 final double myIntersection = getIntersection(myBounds, myScreenBounds);
269 if (myIntersection > myBest) {
270 myBest = myIntersection;
271 myBestScreen = myScreen;
272 }
273 }
274
275
276 return myBestScreen == null
277 ? Screen.getPrimary()
278 : myBestScreen;
279 }
280
281
282
283
284
285
286
287 private static Point2D getOriginForNode(final Node pNode) {
288
289 final Scene myScene = pNode.getScene();
290 final Window myWindow = myScene == null
291 ? null
292 : myScene.getWindow();
293 final boolean bVisible = myScene != null && myWindow != null;
294
295
296 final double mySceneX = bVisible
297 ? myWindow.getX() + myScene.getX()
298 : 0;
299 final double mySceneY = bVisible
300 ? myWindow.getY() + myScene.getY()
301 : 0;
302
303
304 final Bounds myLocalBounds = pNode.getBoundsInLocal();
305 final Bounds myNodeBounds = pNode.localToScene(myLocalBounds);
306
307
308 return new Point2D(myNodeBounds.getMinX() + mySceneX,
309 myNodeBounds.getMinY() + mySceneY);
310 }
311
312
313
314
315
316
317
318
319 private static Point2D getLocationForNode(final Node pAnchor,
320 final Point2D pLocation) {
321
322 final Point2D myOrigin = getOriginForNode(pAnchor);
323
324
325 return new Point2D(myOrigin.getX() + pLocation.getX(),
326 myOrigin.getY() + pLocation.getY());
327 }
328
329
330
331
332
333
334
335
336 private static double getIntersection(final Rectangle2D pBounds,
337 final Rectangle2D pScreen) {
338
339
340 final double myMinX = Math.max(pBounds.getMinX(), pScreen.getMinX());
341 final double myMaxX = Math.min(pBounds.getMaxX(), pScreen.getMaxX());
342 final double myMinY = Math.max(pBounds.getMinY(), pScreen.getMinY());
343 final double myMaxY = Math.min(pBounds.getMaxY(), pScreen.getMaxY());
344
345
346 final double myX = Math.max(myMaxX - myMinX, 0);
347 final double myY = Math.max(myMaxY - myMinY, 0);
348
349
350 return myX * myY;
351 }
352
353
354
355
356
357
358
359
360 private static Rectangle2D adjustDisplayLocation(final Rectangle2D pSource,
361 final Screen pScreen) {
362
363 final Rectangle2D myScreenBounds = pScreen.getBounds();
364 double myAdjustX = 0;
365 double myAdjustY = 0;
366
367
368 if (pSource.getMaxX() > myScreenBounds.getMaxX()) {
369 myAdjustX = myScreenBounds.getMaxX() - pSource.getMaxX();
370 }
371
372
373 if (pSource.getMaxY() > myScreenBounds.getMaxY()) {
374 myAdjustY = myScreenBounds.getMaxY() - pSource.getMaxY();
375 }
376
377
378 if (pSource.getMinX() + myAdjustX < myScreenBounds.getMinX()) {
379 myAdjustX = myScreenBounds.getMinX() - pSource.getMinX();
380 }
381
382
383 if (pSource.getMinY() + myAdjustY < myScreenBounds.getMinY()) {
384 myAdjustY = myScreenBounds.getMinY() - pSource.getMinY();
385 }
386
387
388 return (Double.doubleToRawLongBits(myAdjustX) != 0)
389 || (Double.doubleToRawLongBits(myAdjustY) != 0)
390 ? new Rectangle2D(pSource.getMinX() + myAdjustX,
391 pSource.getMinY() + myAdjustY,
392 pSource.getWidth(),
393 pSource.getHeight())
394 : pSource;
395 }
396
397
398
399
400
401
402
403
404
405
406 private static Rectangle2D adjustDisplayLocation(final Rectangle2D pSource,
407 final Node pAnchor,
408 final Side pSide,
409 final Screen pScreen) {
410
411 final Rectangle2D myScreenBounds = pScreen.getBounds();
412 final Bounds myBounds = pAnchor.getBoundsInLocal();
413 double myAdjustX = 0;
414 double myAdjustY = 0;
415
416
417 switch (pSide) {
418 case RIGHT:
419 myAdjustX = myBounds.getWidth();
420 if (pSource.getMaxX() + myAdjustX > myScreenBounds.getMaxX()) {
421 myAdjustX = -pSource.getWidth();
422 }
423 break;
424 case LEFT:
425 myAdjustX = -pSource.getWidth();
426 if (pSource.getMinX() + myAdjustX < myScreenBounds.getMinX()) {
427 myAdjustX = myBounds.getWidth();
428 }
429 break;
430 case BOTTOM:
431 myAdjustY = myBounds.getHeight();
432 if (pSource.getMaxY() + myAdjustY > myScreenBounds.getMaxY()) {
433 myAdjustY = -pSource.getHeight();
434 }
435 break;
436 case TOP:
437 myAdjustY = -pSource.getHeight();
438 if (pSource.getMinY() + myAdjustY < myScreenBounds.getMinY()) {
439 myAdjustY = myBounds.getHeight();
440 }
441 break;
442 default:
443 break;
444 }
445
446
447 final Rectangle2D myArea = (Double.doubleToRawLongBits(myAdjustX) != 0)
448 || (Double.doubleToRawLongBits(myAdjustY) != 0)
449 ? new Rectangle2D(pSource.getMinX() + myAdjustX,
450 pSource.getMinY() + myAdjustY,
451 pSource.getWidth(),
452 pSource.getHeight())
453 : pSource;
454 return adjustDisplayLocation(myArea, pScreen);
455 }
456
457
458
459
460
461
462
463 public static TethysUIFXIcon getIcon(final TethysUIIconId pId) {
464 final Image myImage = new Image(pId.getInputStream());
465 final ImageView myView = new ImageView(myImage);
466 return new TethysUIFXIcon(myView);
467 }
468
469
470
471
472
473
474
475 public static Image[] getIcons(final TethysUIIconId[] pIds) {
476 final Image[] myIcons = new Image[pIds.length];
477 for (int i = 0; i < pIds.length; i++) {
478 myIcons[i] = getIcon(pIds[i]).getImage();
479 }
480 return myIcons;
481 }
482
483
484
485
486
487
488
489
490 public static TethysUIFXIcon getIconAtSize(final TethysUIIconId pId,
491 final int pSize) {
492 final ImageView myNewImage = getIcon(pId).getImageView();
493 myNewImage.setFitWidth(pSize);
494 myNewImage.setPreserveRatio(true);
495 myNewImage.setSmooth(true);
496 myNewImage.setCache(true);
497 return new TethysUIFXIcon(myNewImage);
498 }
499 }