propane 2.8.0.pre-java → 2.9.0-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1069 @@
1
+ /* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
2
+
3
+ /*
4
+ Part of the Processing project - http://processing.org
5
+
6
+ Copyright (c) 2015 The Processing Foundation
7
+
8
+ This library is free software; you can redistribute it and/or
9
+ modify it under the terms of the GNU Lesser General Public
10
+ License as published by the Free Software Foundation, version 2.1.
11
+
12
+ This library is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
+ Lesser General Public License for more details.
16
+
17
+ You should have received a copy of the GNU Lesser General
18
+ Public License along with this library; if not, write to the
19
+ Free Software Foundation, Inc., 59 Temple Place, Suite 330,
20
+ Boston, MA 02111-1307 USA
21
+ */
22
+
23
+ package processing.javafx;
24
+
25
+ import com.sun.glass.ui.Screen;
26
+
27
+ import java.awt.GraphicsDevice;
28
+ import java.awt.GraphicsEnvironment;
29
+ import java.awt.Rectangle;
30
+ import java.net.URL;
31
+ import java.util.ArrayList;
32
+ import java.util.HashMap;
33
+ import java.util.List;
34
+ import java.util.Map;
35
+ import java.util.concurrent.SynchronousQueue;
36
+
37
+ import javafx.animation.Animation;
38
+ import javafx.animation.KeyFrame;
39
+ import javafx.animation.Timeline;
40
+ import javafx.application.Application;
41
+ import javafx.application.Platform;
42
+ import javafx.beans.value.ChangeListener;
43
+ import javafx.beans.value.ObservableValue;
44
+ import javafx.event.ActionEvent;
45
+ import javafx.event.EventHandler;
46
+ import javafx.event.EventType;
47
+ import javafx.scene.Cursor;
48
+ import javafx.scene.ImageCursor;
49
+ import javafx.scene.Scene;
50
+ import javafx.scene.SceneAntialiasing;
51
+ import javafx.scene.canvas.Canvas;
52
+ import javafx.scene.image.Image;
53
+ import javafx.scene.image.PixelFormat;
54
+ import javafx.scene.image.WritableImage;
55
+ import javafx.scene.input.KeyCode;
56
+ import javafx.scene.input.KeyEvent;
57
+ import javafx.scene.input.MouseEvent;
58
+ import javafx.scene.input.ScrollEvent;
59
+ import javafx.scene.layout.StackPane;
60
+ import javafx.stage.Stage;
61
+ import javafx.stage.StageStyle;
62
+ import javafx.stage.WindowEvent;
63
+ import javafx.util.Duration;
64
+ import processing.core.*;
65
+
66
+
67
+ public class PSurfaceFX implements PSurface {
68
+ PApplet sketch;
69
+
70
+ PGraphicsFX2D fx;
71
+ Stage stage;
72
+ Canvas canvas;
73
+
74
+ final Animation animation;
75
+ float frameRate = 60;
76
+
77
+ private SynchronousQueue<Throwable> drawExceptionQueue = new SynchronousQueue<>();
78
+
79
+ public PSurfaceFX(PGraphicsFX2D graphics) {
80
+ fx = graphics;
81
+ canvas = new ResizableCanvas();
82
+
83
+ // set up main drawing loop
84
+ KeyFrame keyFrame = new KeyFrame(Duration.millis(1000),
85
+ new EventHandler<ActionEvent>() {
86
+ public void handle(ActionEvent event) {
87
+ long startNanoTime = System.nanoTime();
88
+ try {
89
+ sketch.handleDraw();
90
+ } catch (Throwable e) {
91
+ // Let exception handler thread crash with our exception
92
+ drawExceptionQueue.offer(e);
93
+ // Stop animating right now so nothing runs afterwards
94
+ // and crash frame can be for example traced by println()
95
+ animation.stop();
96
+ return;
97
+ }
98
+ long drawNanos = System.nanoTime() - startNanoTime;
99
+
100
+ if (sketch.exitCalled()) {
101
+ // using Platform.runLater() didn't work
102
+ // Platform.runLater(new Runnable() {
103
+ // public void run() {
104
+ // instead of System.exit(), safely shut down JavaFX this way
105
+ Platform.exit();
106
+ // }
107
+ // });
108
+ }
109
+ if (sketch.frameCount > 5) {
110
+ animation.setRate(-PApplet.min(1e9f / drawNanos, frameRate));
111
+ }
112
+ }
113
+ });
114
+ animation = new Timeline(keyFrame);
115
+ animation.setCycleCount(Animation.INDEFINITE);
116
+
117
+ // key frame has duration of 1 second, so the rate of the animation
118
+ // should be set to frames per second
119
+
120
+ // setting rate to negative so that event fires at the start of
121
+ // the key frame and first frame is drawn immediately
122
+ animation.setRate(-frameRate);
123
+ }
124
+
125
+
126
+ public Object getNative() {
127
+ return canvas;
128
+ }
129
+
130
+
131
+ class ResizableCanvas extends Canvas {
132
+
133
+ public ResizableCanvas() {
134
+ widthProperty().addListener(new ChangeListener<Number>() {
135
+ @Override
136
+ public void changed(ObservableValue<? extends Number> value,
137
+ Number oldWidth, Number newWidth) {
138
+ // sketch.width = newWidth.intValue();
139
+ sketch.setSize(newWidth.intValue(), sketch.height);
140
+ // draw();
141
+ fx.setSize(sketch.width, sketch.height);
142
+ }
143
+ });
144
+ heightProperty().addListener(new ChangeListener<Number>() {
145
+ @Override
146
+ public void changed(ObservableValue<? extends Number> value,
147
+ Number oldHeight, Number newHeight) {
148
+ // sketch.height = newHeight.intValue();
149
+ sketch.setSize(sketch.width, newHeight.intValue());
150
+ // draw();
151
+ fx.setSize(sketch.width, sketch.height);
152
+ }
153
+ });
154
+
155
+ //addEventHandler(eventType, eventHandler);
156
+
157
+ EventHandler<MouseEvent> mouseHandler = new EventHandler<MouseEvent>() {
158
+ public void handle(MouseEvent e) {
159
+ fxMouseEvent(e);
160
+ }
161
+ };
162
+
163
+ setOnMousePressed(mouseHandler);
164
+ setOnMouseReleased(mouseHandler);
165
+ setOnMouseClicked(mouseHandler);
166
+ setOnMouseEntered(mouseHandler);
167
+ setOnMouseExited(mouseHandler);
168
+
169
+ setOnMouseDragged(mouseHandler);
170
+ setOnMouseMoved(mouseHandler);
171
+
172
+ setOnScroll(new EventHandler<ScrollEvent>() {
173
+ public void handle(ScrollEvent e) {
174
+ fxScrollEvent(e);
175
+ }
176
+ });
177
+
178
+ EventHandler<KeyEvent> keyHandler = new EventHandler<KeyEvent>() {
179
+ public void handle(KeyEvent e) {
180
+ fxKeyEvent(e);
181
+ }
182
+ };
183
+
184
+ setOnKeyPressed(keyHandler);
185
+ setOnKeyReleased(keyHandler);
186
+ setOnKeyTyped(keyHandler);
187
+
188
+ setFocusTraversable(false); // prevent tab from de-focusing
189
+
190
+ focusedProperty().addListener(new ChangeListener<Boolean>() {
191
+ public void changed(ObservableValue<? extends Boolean> value,
192
+ Boolean oldValue, Boolean newValue) {
193
+ if (newValue.booleanValue()) {
194
+ sketch.focused = true;
195
+ sketch.focusGained();
196
+ } else {
197
+ sketch.focused = false;
198
+ sketch.focusLost();
199
+ }
200
+ }
201
+ });
202
+ }
203
+
204
+ public Stage getStage() {
205
+ return stage;
206
+ }
207
+
208
+ @Override
209
+ public boolean isResizable() {
210
+ return true;
211
+ }
212
+
213
+ @Override
214
+ public double prefWidth(double height) {
215
+ return getWidth();
216
+ }
217
+
218
+ @Override
219
+ public double prefHeight(double width) {
220
+ return getHeight();
221
+ }
222
+ }
223
+
224
+
225
+ public void initOffscreen(PApplet sketch) {
226
+ }
227
+
228
+
229
+ // public Component initComponent(PApplet sketch) {
230
+ // return null;
231
+ // }
232
+
233
+
234
+ static public class PApplicationFX extends Application {
235
+ static public PSurfaceFX surface;
236
+ // static String title; // title set at launch
237
+ // static boolean resizable; // set at launch
238
+
239
+ public PApplicationFX() { }
240
+
241
+ @Override
242
+ public void start(final Stage stage) {
243
+ // if (title != null) {
244
+ // stage.setTitle(title);
245
+ // }
246
+
247
+ PApplet sketch = surface.sketch;
248
+
249
+ float renderScale = Screen.getMainScreen().getRenderScale();
250
+ if (PApplet.platform == PConstants.MACOSX) {
251
+ for (Screen s : Screen.getScreens()) {
252
+ renderScale = Math.max(renderScale, s.getRenderScale());
253
+ }
254
+ }
255
+ float uiScale = Screen.getMainScreen().getUIScale();
256
+ if (sketch.pixelDensity == 2 && renderScale < 2) {
257
+ sketch.pixelDensity = 1;
258
+ sketch.g.pixelDensity = 1;
259
+ System.err.println("pixelDensity(2) is not available for this display");
260
+ }
261
+
262
+ // Use AWT display code, because FX orders screens in different way
263
+ GraphicsDevice displayDevice = null;
264
+
265
+ GraphicsEnvironment environment =
266
+ GraphicsEnvironment.getLocalGraphicsEnvironment();
267
+
268
+ int displayNum = sketch.sketchDisplay();
269
+ if (displayNum > 0) { // if -1, use the default device
270
+ GraphicsDevice[] devices = environment.getScreenDevices();
271
+ if (displayNum <= devices.length) {
272
+ displayDevice = devices[displayNum - 1];
273
+ } else {
274
+ System.err.format("Display %d does not exist, " +
275
+ "using the default display instead.%n", displayNum);
276
+ for (int i = 0; i < devices.length; i++) {
277
+ System.err.format("Display %d is %s%n", (i+1), devices[i]);
278
+ }
279
+ }
280
+ }
281
+ if (displayDevice == null) {
282
+ displayDevice = environment.getDefaultScreenDevice();
283
+ }
284
+
285
+ boolean fullScreen = sketch.sketchFullScreen();
286
+ boolean spanDisplays = sketch.sketchDisplay() == PConstants.SPAN;
287
+
288
+ Rectangle primaryScreenRect = displayDevice.getDefaultConfiguration().getBounds();
289
+ Rectangle screenRect = primaryScreenRect;
290
+ if (fullScreen || spanDisplays) {
291
+ double minX = screenRect.getMinX();
292
+ double maxX = screenRect.getMaxX();
293
+ double minY = screenRect.getMinY();
294
+ double maxY = screenRect.getMaxY();
295
+ if (spanDisplays) {
296
+ for (GraphicsDevice s : environment.getScreenDevices()) {
297
+ Rectangle bounds = s.getDefaultConfiguration().getBounds();
298
+ minX = Math.min(minX, bounds.getMinX());
299
+ maxX = Math.max(maxX, bounds.getMaxX());
300
+ minY = Math.min(minY, bounds.getMinY());
301
+ maxY = Math.max(maxY, bounds.getMaxY());
302
+ }
303
+ }
304
+ screenRect = new Rectangle((int) minX, (int) minY,
305
+ (int) (maxX - minX), (int) (maxY - minY));
306
+ }
307
+
308
+ // Set the displayWidth/Height variables inside PApplet, so that they're
309
+ // usable and can even be returned by the sketchWidth()/Height() methods.
310
+ sketch.displayWidth = (int) screenRect.getWidth();
311
+ sketch.displayHeight = (int) screenRect.getHeight();
312
+
313
+ int sketchWidth = sketch.sketchWidth();
314
+ int sketchHeight = sketch.sketchHeight();
315
+
316
+ if (fullScreen || spanDisplays) {
317
+ sketchWidth = (int) (screenRect.getWidth() / uiScale);
318
+ sketchHeight = (int) (screenRect.getHeight() / uiScale);
319
+
320
+ stage.initStyle(StageStyle.UNDECORATED);
321
+ stage.setX(screenRect.getMinX() / uiScale);
322
+ stage.setY(screenRect.getMinY() / uiScale);
323
+ stage.setWidth(screenRect.getWidth() / uiScale);
324
+ stage.setHeight(screenRect.getHeight() / uiScale);
325
+ }
326
+
327
+ Canvas canvas = surface.canvas;
328
+ surface.fx.context = canvas.getGraphicsContext2D();
329
+ StackPane stackPane = new StackPane();
330
+ stackPane.getChildren().add(canvas);
331
+ canvas.widthProperty().bind(stackPane.widthProperty());
332
+ canvas.heightProperty().bind(stackPane.heightProperty());
333
+
334
+ int width = sketchWidth;
335
+ int height = sketchHeight;
336
+ int smooth = sketch.sketchSmooth();
337
+
338
+ // Workaround for https://bugs.openjdk.java.net/browse/JDK-8136495
339
+ // https://github.com/processing/processing/issues/3823
340
+ if ((PApplet.platform == PConstants.MACOSX ||
341
+ PApplet.platform == PConstants.LINUX) &&
342
+ PApplet.javaVersionName.compareTo("1.8.0_60") >= 0 &&
343
+ PApplet.javaVersionName.compareTo("1.8.0_72") < 0) {
344
+ System.err.println("smooth() disabled for JavaFX with this Java version due to Oracle bug");
345
+ System.err.println("https://github.com/processing/processing/issues/3795");
346
+ smooth = 0;
347
+ }
348
+
349
+ SceneAntialiasing sceneAntialiasing = (smooth == 0) ?
350
+ SceneAntialiasing.DISABLED : SceneAntialiasing.BALANCED;
351
+
352
+ stage.setScene(new Scene(stackPane, width, height, false, sceneAntialiasing));
353
+
354
+ // initFrame in different thread is waiting for
355
+ // the stage, assign it only when it is all set up
356
+ surface.stage = stage;
357
+ }
358
+
359
+ @Override
360
+ public void stop() throws Exception {
361
+ surface.sketch.dispose();
362
+ }
363
+ }
364
+
365
+
366
+ //public Frame initFrame(PApplet sketch, java.awt.Color backgroundColor,
367
+ public void initFrame(PApplet sketch) {/*, int backgroundColor,
368
+ int deviceIndex, boolean fullScreen,
369
+ boolean spanDisplays) {*/
370
+ this.sketch = sketch;
371
+ PApplicationFX.surface = this;
372
+ //Frame frame = new DummyFrame();
373
+ new Thread(new Runnable() {
374
+ public void run() {
375
+ Application.launch(PApplicationFX.class);
376
+ }
377
+ }).start();
378
+
379
+ // wait for stage to be initialized on its own thread before continuing
380
+ while (stage == null) {
381
+ try {
382
+ //System.out.println("waiting for launch");
383
+ Thread.sleep(5);
384
+ } catch (InterruptedException e) { }
385
+ }
386
+
387
+ startExceptionHandlerThread();
388
+
389
+ setProcessingIcon(stage);
390
+ }
391
+
392
+
393
+ private void startExceptionHandlerThread() {
394
+ Thread exceptionHandlerThread = new Thread(() -> {
395
+ Throwable drawException;
396
+ try {
397
+ drawException = drawExceptionQueue.take();
398
+ } catch (InterruptedException e) {
399
+ return;
400
+ }
401
+ // Adapted from PSurfaceJOGL
402
+ if (drawException != null) {
403
+ if (drawException instanceof ThreadDeath) {
404
+ // System.out.println("caught ThreadDeath");
405
+ // throw (ThreadDeath)cause;
406
+ } else if (drawException instanceof RuntimeException) {
407
+ throw (RuntimeException) drawException;
408
+ } else if (drawException instanceof UnsatisfiedLinkError) {
409
+ throw new UnsatisfiedLinkError(drawException.getMessage());
410
+ } else {
411
+ throw new RuntimeException(drawException);
412
+ }
413
+ }
414
+ });
415
+ exceptionHandlerThread.setDaemon(true);
416
+ exceptionHandlerThread.setName("Processing-FX-ExceptionHandler");
417
+ exceptionHandlerThread.start();
418
+ }
419
+
420
+
421
+ /** Set the window (and dock, or whatever necessary) title. */
422
+ public void setTitle(String title) {
423
+ // PApplicationFX.title = title; // store this in case the stage still null
424
+ // if (stage != null) {
425
+ stage.setTitle(title);
426
+ // }
427
+ }
428
+
429
+
430
+ /** Show or hide the window. */
431
+ @Override
432
+ public void setVisible(final boolean visible) {
433
+ Platform.runLater(new Runnable() {
434
+ public void run() {
435
+ if (visible) {
436
+ stage.show();
437
+ canvas.requestFocus();
438
+ } else {
439
+ stage.hide();
440
+ }
441
+ }
442
+ });
443
+ }
444
+
445
+
446
+ /** Set true if we want to resize things (default is not resizable) */
447
+ public void setResizable(boolean resizable) {
448
+ // PApplicationFX.resizable = resizable;
449
+ // if (stage != null) {
450
+ stage.setResizable(resizable);
451
+ // }
452
+ }
453
+
454
+
455
+ public void setIcon(PImage icon) {
456
+ int w = icon.pixelWidth;
457
+ int h = icon.pixelHeight;
458
+ WritableImage im = new WritableImage(w, h);
459
+ im.getPixelWriter().setPixels(0, 0, w, h,
460
+ PixelFormat.getIntArgbInstance(),
461
+ icon.pixels,
462
+ 0, w);
463
+
464
+ Stage stage = (Stage) canvas.getScene().getWindow();
465
+ stage.getIcons().clear();
466
+ stage.getIcons().add(im);
467
+ }
468
+
469
+
470
+ List<Image> iconImages;
471
+
472
+ protected void setProcessingIcon(Stage stage) {
473
+ // Adapted from PSurfaceAWT
474
+ // Note: FX chooses wrong icon size, should be fixed in Java 9, see:
475
+ // https://bugs.openjdk.java.net/browse/JDK-8091186
476
+ // Removing smaller sizes helps a bit, but big ones are downsized
477
+ try {
478
+ if (iconImages == null) {
479
+ iconImages = new ArrayList<>();
480
+ final int[] sizes = { 48, 64, 128, 256, 512 };
481
+
482
+ for (int sz : sizes) {
483
+ URL url = PApplet.class.getResource("/icon/icon-" + sz + ".png");
484
+ Image image = new Image(url.toString());
485
+ iconImages.add(image);
486
+ }
487
+ }
488
+ List<Image> icons = stage.getIcons();
489
+ icons.clear();
490
+ icons.addAll(iconImages);
491
+ } catch (Exception e) { } // harmless; keep this to ourselves
492
+ }
493
+
494
+
495
+ @Override
496
+ public void setAlwaysOnTop(boolean always) {
497
+ stage.setAlwaysOnTop(always);
498
+ }
499
+
500
+
501
+ /*
502
+ @Override
503
+ public void placeWindow(int[] location) {
504
+ //setFrameSize();
505
+
506
+ if (location != null) {
507
+ // a specific location was received from the Runner
508
+ // (applet has been run more than once, user placed window)
509
+ stage.setX(location[0]);
510
+ stage.setY(location[1]);
511
+
512
+ } else { // just center on screen
513
+ // Can't use frame.setLocationRelativeTo(null) because it sends the
514
+ // frame to the main display, which undermines the --display setting.
515
+ // frame.setLocation(screenRect.x + (screenRect.width - sketchWidth) / 2,
516
+ // screenRect.y + (screenRect.height - sketchHeight) / 2);
517
+ }
518
+ if (stage.getY() < 0) {
519
+ // Windows actually allows you to place frames where they can't be
520
+ // closed. Awesome. http://dev.processing.org/bugs/show_bug.cgi?id=1508
521
+ //frame.setLocation(frameLoc.x, 30);
522
+ stage.setY(30);
523
+ }
524
+
525
+ //setCanvasSize();
526
+
527
+ // TODO add window closing behavior
528
+ // frame.addWindowListener(new WindowAdapter() {
529
+ // @Override
530
+ // public void windowClosing(WindowEvent e) {
531
+ // System.exit(0);
532
+ // }
533
+ // });
534
+
535
+ // TODO handle frame resizing events
536
+ // setupFrameResizeListener();
537
+
538
+ if (sketch.getGraphics().displayable()) {
539
+ setVisible(true);
540
+ }
541
+ }
542
+ */
543
+
544
+
545
+ @Override
546
+ public void placeWindow(int[] location, int[] editorLocation) {
547
+ if (sketch.sketchFullScreen()) {
548
+ PApplet.hideMenuBar();
549
+ return;
550
+ }
551
+
552
+ int wide = sketch.width; // stage.getWidth() is NaN here
553
+ //int high = sketch.height; // stage.getHeight()
554
+
555
+ if (location != null) {
556
+ // a specific location was received from the Runner
557
+ // (applet has been run more than once, user placed window)
558
+ stage.setX(location[0]);
559
+ stage.setY(location[1]);
560
+
561
+ } else if (editorLocation != null) {
562
+ int locationX = editorLocation[0] - 20;
563
+ int locationY = editorLocation[1];
564
+
565
+ if (locationX - wide > 10) {
566
+ // if it fits to the left of the window
567
+ stage.setX(locationX - wide);
568
+ stage.setY(locationY);
569
+
570
+ } else { // doesn't fit
571
+ stage.centerOnScreen();
572
+ }
573
+ } else { // just center on screen
574
+ stage.centerOnScreen();
575
+ }
576
+ }
577
+
578
+
579
+ // http://download.java.net/jdk8/jfxdocs/javafx/stage/Stage.html#setFullScreenExitHint-java.lang.String-
580
+ // http://download.java.net/jdk8/jfxdocs/javafx/stage/Stage.html#setFullScreenExitKeyCombination-javafx.scene.input.KeyCombination-
581
+ public void placePresent(int stopColor) {
582
+ // TODO Auto-generated method stub
583
+ PApplet.hideMenuBar();
584
+ }
585
+
586
+
587
+ @Override
588
+ public void setupExternalMessages() {
589
+ stage.xProperty().addListener(new ChangeListener<Number>() {
590
+ @Override
591
+ public void changed(ObservableValue<? extends Number> value,
592
+ Number oldX, Number newX) {
593
+ sketch.frameMoved(newX.intValue(), stage.yProperty().intValue());
594
+ }
595
+ });
596
+
597
+ stage.yProperty().addListener(new ChangeListener<Number>() {
598
+ @Override
599
+ public void changed(ObservableValue<? extends Number> value,
600
+ Number oldY, Number newY) {
601
+ sketch.frameMoved(stage.xProperty().intValue(), newY.intValue());
602
+ }
603
+ });
604
+
605
+ stage.setOnCloseRequest(new EventHandler<WindowEvent>() {
606
+ public void handle(WindowEvent we) {
607
+ sketch.exit();
608
+ }
609
+ });
610
+ }
611
+
612
+
613
+ public void setLocation(int x, int y) {
614
+ stage.setX(x);
615
+ stage.setY(y);
616
+ }
617
+
618
+
619
+ public void setSize(int wide, int high) {
620
+ // When the surface is set to resizable via surface.setResizable(true),
621
+ // a crash may occur if the user sets the window to size zero.
622
+ // https://github.com/processing/processing/issues/5052
623
+ if (high <= 0) {
624
+ high = 1;
625
+ }
626
+ if (wide <= 0) {
627
+ wide = 1;
628
+ }
629
+
630
+ //System.out.format("%s.setSize(%d, %d)%n", getClass().getSimpleName(), width, height);
631
+ Scene scene = stage.getScene();
632
+ double decorH = stage.getWidth() - scene.getWidth();
633
+ double decorV = stage.getHeight() - scene.getHeight();
634
+ stage.setWidth(wide + decorH);
635
+ stage.setHeight(high + decorV);
636
+ fx.setSize(wide, high);
637
+ }
638
+
639
+
640
+ // public Component getComponent() {
641
+ // return null;
642
+ // }
643
+
644
+
645
+ public void setSmooth(int level) {
646
+ // TODO Auto-generated method stub
647
+
648
+ }
649
+
650
+
651
+ public void setFrameRate(float fps) {
652
+ // setting rate to negative so that event fires at the start of
653
+ // the key frame and first frame is drawn immediately
654
+ if (fps > 0) {
655
+ frameRate = fps;
656
+ animation.setRate(-frameRate);
657
+ }
658
+ }
659
+
660
+
661
+ // @Override
662
+ // public void requestFocus() {
663
+ // canvas.requestFocus();
664
+ // }
665
+
666
+ Cursor lastCursor = Cursor.DEFAULT;
667
+
668
+ public void setCursor(int kind) {
669
+ Cursor c;
670
+ switch (kind) {
671
+ case PConstants.ARROW: c = Cursor.DEFAULT; break;
672
+ case PConstants.CROSS: c = Cursor.CROSSHAIR; break;
673
+ case PConstants.HAND: c = Cursor.HAND; break;
674
+ case PConstants.MOVE: c = Cursor.MOVE; break;
675
+ case PConstants.TEXT: c = Cursor.TEXT; break;
676
+ case PConstants.WAIT: c = Cursor.WAIT; break;
677
+ default: c = Cursor.DEFAULT; break;
678
+ }
679
+ lastCursor = c;
680
+ canvas.getScene().setCursor(c);
681
+ }
682
+
683
+
684
+ public void setCursor(PImage image, int hotspotX, int hotspotY) {
685
+ int w = image.pixelWidth;
686
+ int h = image.pixelHeight;
687
+ WritableImage im = new WritableImage(w, h);
688
+ im.getPixelWriter().setPixels(0, 0, w, h,
689
+ PixelFormat.getIntArgbInstance(),
690
+ image.pixels,
691
+ 0, w);
692
+ ImageCursor c = new ImageCursor(im, hotspotX, hotspotY);
693
+ lastCursor = c;
694
+ canvas.getScene().setCursor(c);
695
+ }
696
+
697
+
698
+ public void showCursor() {
699
+ canvas.getScene().setCursor(lastCursor);
700
+ }
701
+
702
+
703
+ public void hideCursor() {
704
+ canvas.getScene().setCursor(Cursor.NONE);
705
+ }
706
+
707
+
708
+ public void startThread() {
709
+ animation.play();
710
+ }
711
+
712
+
713
+ public void pauseThread() {
714
+ animation.pause();
715
+ }
716
+
717
+
718
+ public void resumeThread() {
719
+ animation.play();
720
+ }
721
+
722
+
723
+ public boolean stopThread() {
724
+ animation.stop();
725
+ return true;
726
+ }
727
+
728
+
729
+ public boolean isStopped() {
730
+ return animation.getStatus() == Animation.Status.STOPPED;
731
+ }
732
+
733
+
734
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
735
+
736
+
737
+ /*
738
+ protected void addListeners() {
739
+
740
+ canvas.addMouseListener(new MouseListener() {
741
+
742
+ public void mousePressed(java.awt.event.MouseEvent e) {
743
+ nativeMouseEvent(e);
744
+ }
745
+
746
+ public void mouseReleased(java.awt.event.MouseEvent e) {
747
+ nativeMouseEvent(e);
748
+ }
749
+
750
+ public void mouseClicked(java.awt.event.MouseEvent e) {
751
+ nativeMouseEvent(e);
752
+ }
753
+
754
+ public void mouseEntered(java.awt.event.MouseEvent e) {
755
+ nativeMouseEvent(e);
756
+ }
757
+
758
+ public void mouseExited(java.awt.event.MouseEvent e) {
759
+ nativeMouseEvent(e);
760
+ }
761
+ });
762
+
763
+ canvas.addMouseMotionListener(new MouseMotionListener() {
764
+
765
+ public void mouseDragged(java.awt.event.MouseEvent e) {
766
+ nativeMouseEvent(e);
767
+ }
768
+
769
+ public void mouseMoved(java.awt.event.MouseEvent e) {
770
+ nativeMouseEvent(e);
771
+ }
772
+ });
773
+
774
+ canvas.addMouseWheelListener(new MouseWheelListener() {
775
+
776
+ public void mouseWheelMoved(MouseWheelEvent e) {
777
+ nativeMouseEvent(e);
778
+ }
779
+ });
780
+
781
+ canvas.addKeyListener(new KeyListener() {
782
+
783
+ public void keyPressed(java.awt.event.KeyEvent e) {
784
+ nativeKeyEvent(e);
785
+ }
786
+
787
+
788
+ public void keyReleased(java.awt.event.KeyEvent e) {
789
+ nativeKeyEvent(e);
790
+ }
791
+
792
+
793
+ public void keyTyped(java.awt.event.KeyEvent e) {
794
+ nativeKeyEvent(e);
795
+ }
796
+ });
797
+
798
+ canvas.addFocusListener(new FocusListener() {
799
+
800
+ public void focusGained(FocusEvent e) {
801
+ sketch.focused = true;
802
+ sketch.focusGained();
803
+ }
804
+
805
+ public void focusLost(FocusEvent e) {
806
+ sketch.focused = false;
807
+ sketch.focusLost();
808
+ }
809
+ });
810
+ }
811
+ */
812
+
813
+
814
+ static Map<EventType<? extends MouseEvent>, Integer> mouseMap =
815
+ new HashMap<EventType<? extends MouseEvent>, Integer>();
816
+ static {
817
+ mouseMap.put(MouseEvent.MOUSE_PRESSED, processing.event.MouseEvent.PRESS);
818
+ mouseMap.put(MouseEvent.MOUSE_RELEASED, processing.event.MouseEvent.RELEASE);
819
+ mouseMap.put(MouseEvent.MOUSE_CLICKED, processing.event.MouseEvent.CLICK);
820
+ mouseMap.put(MouseEvent.MOUSE_DRAGGED, processing.event.MouseEvent.DRAG);
821
+ mouseMap.put(MouseEvent.MOUSE_MOVED, processing.event.MouseEvent.MOVE);
822
+ mouseMap.put(MouseEvent.MOUSE_ENTERED, processing.event.MouseEvent.ENTER);
823
+ mouseMap.put(MouseEvent.MOUSE_EXITED, processing.event.MouseEvent.EXIT);
824
+ }
825
+
826
+ protected void fxMouseEvent(MouseEvent fxEvent) {
827
+ // the 'amount' is the number of button clicks for a click event,
828
+ // or the number of steps/clicks on the wheel for a mouse wheel event.
829
+ int count = fxEvent.getClickCount();
830
+
831
+ int action = mouseMap.get(fxEvent.getEventType());
832
+
833
+ int modifiers = 0;
834
+ if (fxEvent.isShiftDown()) {
835
+ modifiers |= processing.event.Event.SHIFT;
836
+ }
837
+ if (fxEvent.isControlDown()) {
838
+ modifiers |= processing.event.Event.CTRL;
839
+ }
840
+ if (fxEvent.isMetaDown()) {
841
+ modifiers |= processing.event.Event.META;
842
+ }
843
+ if (fxEvent.isAltDown()) {
844
+ modifiers |= processing.event.Event.ALT;
845
+ }
846
+
847
+ int button = 0;
848
+ switch (fxEvent.getButton()) {
849
+ case PRIMARY:
850
+ button = PConstants.LEFT;
851
+ break;
852
+ case SECONDARY:
853
+ button = PConstants.RIGHT;
854
+ break;
855
+ case MIDDLE:
856
+ button = PConstants.CENTER;
857
+ break;
858
+ case NONE:
859
+ // not currently handled
860
+ break;
861
+ }
862
+
863
+ // If running on Mac OS, allow ctrl-click as right mouse.
864
+ // Verified to be necessary with Java 8u45.
865
+ if (PApplet.platform == PConstants.MACOSX &&
866
+ fxEvent.isControlDown() &&
867
+ button == PConstants.LEFT) {
868
+ button = PConstants.RIGHT;
869
+ }
870
+
871
+ //long when = nativeEvent.getWhen(); // from AWT
872
+ long when = System.currentTimeMillis();
873
+ int x = (int) fxEvent.getX(); // getSceneX()?
874
+ int y = (int) fxEvent.getY();
875
+
876
+ sketch.postEvent(new processing.event.MouseEvent(fxEvent, when,
877
+ action, modifiers,
878
+ x, y, button, count));
879
+ }
880
+
881
+ // https://docs.oracle.com/javase/8/javafx/api/javafx/scene/input/ScrollEvent.html
882
+ protected void fxScrollEvent(ScrollEvent fxEvent) {
883
+ // the number of steps/clicks on the wheel for a mouse wheel event.
884
+ int count = (int) -(fxEvent.getDeltaY() / fxEvent.getMultiplierY());
885
+
886
+ int action = processing.event.MouseEvent.WHEEL;
887
+
888
+ int modifiers = 0;
889
+ if (fxEvent.isShiftDown()) {
890
+ modifiers |= processing.event.Event.SHIFT;
891
+ }
892
+ if (fxEvent.isControlDown()) {
893
+ modifiers |= processing.event.Event.CTRL;
894
+ }
895
+ if (fxEvent.isMetaDown()) {
896
+ modifiers |= processing.event.Event.META;
897
+ }
898
+ if (fxEvent.isAltDown()) {
899
+ modifiers |= processing.event.Event.ALT;
900
+ }
901
+
902
+ // FX does not supply button info
903
+ int button = 0;
904
+
905
+ long when = System.currentTimeMillis();
906
+ int x = (int) fxEvent.getX(); // getSceneX()?
907
+ int y = (int) fxEvent.getY();
908
+
909
+ sketch.postEvent(new processing.event.MouseEvent(fxEvent, when,
910
+ action, modifiers,
911
+ x, y, button, count));
912
+ }
913
+
914
+
915
+ protected void fxKeyEvent(javafx.scene.input.KeyEvent fxEvent) {
916
+ int action = 0;
917
+ EventType<? extends KeyEvent> et = fxEvent.getEventType();
918
+ if (et == KeyEvent.KEY_PRESSED) {
919
+ action = processing.event.KeyEvent.PRESS;
920
+ } else if (et == KeyEvent.KEY_RELEASED) {
921
+ action = processing.event.KeyEvent.RELEASE;
922
+ } else if (et == KeyEvent.KEY_TYPED) {
923
+ action = processing.event.KeyEvent.TYPE;
924
+ }
925
+
926
+ int modifiers = 0;
927
+ if (fxEvent.isShiftDown()) {
928
+ modifiers |= processing.event.Event.SHIFT;
929
+ }
930
+ if (fxEvent.isControlDown()) {
931
+ modifiers |= processing.event.Event.CTRL;
932
+ }
933
+ if (fxEvent.isMetaDown()) {
934
+ modifiers |= processing.event.Event.META;
935
+ }
936
+ if (fxEvent.isAltDown()) {
937
+ modifiers |= processing.event.Event.ALT;
938
+ }
939
+
940
+ long when = System.currentTimeMillis();
941
+
942
+ char keyChar = getKeyChar(fxEvent);
943
+ int keyCode = getKeyCode(fxEvent);
944
+ sketch.postEvent(new processing.event.KeyEvent(fxEvent, when,
945
+ action, modifiers,
946
+ keyChar, keyCode));
947
+ }
948
+
949
+
950
+ @SuppressWarnings("deprecation")
951
+ private int getKeyCode(KeyEvent fxEvent) {
952
+ if (fxEvent.getEventType() == KeyEvent.KEY_TYPED) {
953
+ return 0;
954
+ }
955
+
956
+ KeyCode kc = fxEvent.getCode();
957
+ switch (kc) {
958
+ case ALT_GRAPH:
959
+ return PConstants.ALT;
960
+ default:
961
+ break;
962
+ }
963
+ return kc.impl_getCode();
964
+ }
965
+
966
+
967
+ @SuppressWarnings("deprecation")
968
+ private char getKeyChar(KeyEvent fxEvent) {
969
+ KeyCode kc = fxEvent.getCode();
970
+
971
+ // Overriding chars for some
972
+ // KEY_PRESSED and KEY_RELEASED events
973
+ switch (kc) {
974
+ case UP:
975
+ case KP_UP:
976
+ case DOWN:
977
+ case KP_DOWN:
978
+ case LEFT:
979
+ case KP_LEFT:
980
+ case RIGHT:
981
+ case KP_RIGHT:
982
+ case ALT:
983
+ case ALT_GRAPH:
984
+ case CONTROL:
985
+ case SHIFT:
986
+ case CAPS:
987
+ case META:
988
+ case WINDOWS:
989
+ case CONTEXT_MENU:
990
+ case HOME:
991
+ case PAGE_UP:
992
+ case PAGE_DOWN:
993
+ case END:
994
+ case PAUSE:
995
+ case PRINTSCREEN:
996
+ case INSERT:
997
+ case NUM_LOCK:
998
+ case SCROLL_LOCK:
999
+ case F1:
1000
+ case F2:
1001
+ case F3:
1002
+ case F4:
1003
+ case F5:
1004
+ case F6:
1005
+ case F7:
1006
+ case F8:
1007
+ case F9:
1008
+ case F10:
1009
+ case F11:
1010
+ case F12:
1011
+ return PConstants.CODED;
1012
+ case ENTER:
1013
+ return '\n';
1014
+ case DIVIDE:
1015
+ return '/';
1016
+ case MULTIPLY:
1017
+ return '*';
1018
+ case SUBTRACT:
1019
+ return '-';
1020
+ case ADD:
1021
+ return '+';
1022
+ case NUMPAD0:
1023
+ return '0';
1024
+ case NUMPAD1:
1025
+ return '1';
1026
+ case NUMPAD2:
1027
+ return '2';
1028
+ case NUMPAD3:
1029
+ return '3';
1030
+ case NUMPAD4:
1031
+ return '4';
1032
+ case NUMPAD5:
1033
+ return '5';
1034
+ case NUMPAD6:
1035
+ return '6';
1036
+ case NUMPAD7:
1037
+ return '7';
1038
+ case NUMPAD8:
1039
+ return '8';
1040
+ case NUMPAD9:
1041
+ return '9';
1042
+ case DECIMAL:
1043
+ // KEY_TYPED does not go through here and will produce
1044
+ // dot or comma based on the keyboard layout.
1045
+ // For KEY_PRESSED and KEY_RELEASED, let's just go with
1046
+ // the dot. Users can detect the key by its keyCode.
1047
+ return '.';
1048
+ case UNDEFINED:
1049
+ // KEY_TYPED has KeyCode: UNDEFINED
1050
+ // and falls through here
1051
+ break;
1052
+ default:
1053
+ break;
1054
+ }
1055
+
1056
+ // Just go with what FX gives us for the rest of
1057
+ // KEY_PRESSED and KEY_RELEASED and all of KEY_TYPED
1058
+ String ch;
1059
+ if (fxEvent.getEventType() == KeyEvent.KEY_TYPED) {
1060
+ ch = fxEvent.getCharacter();
1061
+ } else {
1062
+ ch = kc.impl_getChar();
1063
+ }
1064
+
1065
+ if (ch.length() < 1) return PConstants.CODED;
1066
+ if (ch.startsWith("\r")) return '\n'; // normalize enter key
1067
+ return ch.charAt(0);
1068
+ }
1069
+ }