118 * launch(args);
119 * }
120 * }
121 * </pre>
122 * @since JavaFX 8.0
123 */
124 public class SwingNode extends Node {
125 static {
126 // This is used by classes in different packages to get access to
127 // private and package private methods.
128 SwingNodeHelper.setSwingNodeAccessor(new SwingNodeHelper.SwingNodeAccessor() {
129 @Override
130 public NGNode doCreatePeer(Node node) {
131 return ((SwingNode) node).doCreatePeer();
132 }
133
134 @Override
135 public void doUpdatePeer(Node node) {
136 ((SwingNode) node).doUpdatePeer();
137 }
138 });
139 }
140
141 private double fxWidth;
142 private double fxHeight;
143
144 private int swingPrefWidth;
145 private int swingPrefHeight;
146 private int swingMaxWidth;
147 private int swingMaxHeight;
148 private int swingMinWidth;
149 private int swingMinHeight;
150
151 private volatile JComponent content;
152 private volatile JLightweightFrame lwFrame;
153 final JLightweightFrame getLightweightFrame() { return lwFrame; }
154
155 private NGExternalNode peer;
156
157 private final ReentrantLock paintLock = new ReentrantLock();
382 @Override public boolean isResizable() {
383 return true;
384 }
385
386 /**
387 * Invoked by the {@code SwingNode}'s parent during layout to set the {@code SwingNode}'s
388 * width and height. <b>Applications should not invoke this method directly</b>.
389 * If an application needs to directly set the size of the {@code SwingNode}, it should
390 * set the Swing component's minimum/preferred/maximum size constraints which will
391 * be propagated correspondingly to the {@code SwingNode} and it's parent will honor those
392 * settings during layout.
393 *
394 * @param width the target layout bounds width
395 * @param height the target layout bounds height
396 */
397 @Override public void resize(final double width, final double height) {
398 super.resize(width, height);
399 if (width != this.fxWidth || height != this.fxHeight) {
400 this.fxWidth = width;
401 this.fxHeight = height;
402 impl_geomChanged();
403 NodeHelper.markDirty(this, DirtyBits.NODE_GEOMETRY);
404 SwingFXUtils.runOnEDT(() -> {
405 if (lwFrame != null) {
406 locateLwFrame();
407 }
408 });
409 }
410 }
411
412 /**
413 * Returns the {@code SwingNode}'s preferred width for use in layout calculations.
414 * This value corresponds to the preferred width of the Swing component.
415 *
416 * @return the preferred width that the node should be resized to during layout
417 */
418 @Override
419 public double prefWidth(double height) {
420 return swingPrefWidth / getPlatformScaleX();
421 }
422
454 /**
455 * Returns the {@code SwingNode}'s minimum width for use in layout calculations.
456 * This value corresponds to the minimum width of the Swing component.
457 *
458 * @return the minimum width that the node should be resized to during layout
459 */
460 @Override public double minWidth(double height) {
461 return swingMinWidth / getPlatformScaleX();
462 }
463
464 /**
465 * Returns the {@code SwingNode}'s minimum height for use in layout calculations.
466 * This value corresponds to the minimum height of the Swing component.
467 *
468 * @return the minimum height that the node should be resized to during layout
469 */
470 @Override public double minHeight(double width) {
471 return swingMinHeight / getPlatformScaleY();
472 }
473
474 /**
475 * @treatAsPrivate implementation detail
476 * @deprecated This is an internal API that is not intended for use and will be removed in the next version
477 */
478 @Deprecated
479 @Override
480 protected boolean impl_computeContains(double localX, double localY) {
481 return true;
482 }
483
484 private final InvalidationListener locationListener = observable -> {
485 locateLwFrame();
486 };
487
488 private final EventHandler<FocusUngrabEvent> ungrabHandler = event -> {
489 if (!skipBackwardUnrgabNotification) {
490 if (lwFrame != null) {
491 AccessController.doPrivileged(new PostEventAction(new UngrabEvent(lwFrame)));
492 }
493 }
494 };
495
496 private final ChangeListener<Boolean> windowVisibleListener = (observable, oldValue, newValue) -> {
497 if (!newValue) {
498 disposeLwFrame();
499 } else {
500 setContent(content);
563
564 if (getScene() != null) {
565 addSceneListeners(getScene());
566 }
567
568 sceneProperty().addListener((observable, oldValue, newValue) -> {
569 if (oldValue != null) {
570 // Removed from scene
571 removeSceneListeners(oldValue);
572 disposeLwFrame();
573 }
574 if (newValue != null) {
575 // Added to another scene
576 if (content != null && lwFrame == null) {
577 setContent(content);
578 }
579 addSceneListeners(newValue);
580 }
581 });
582
583 impl_treeVisibleProperty().addListener((observable, oldValue, newValue) -> {
584 setLwFrameVisible(newValue);
585 });
586
587 return peer;
588 }
589
590 /*
591 * Note: This method MUST only be called via its accessor method.
592 */
593 private void doUpdatePeer() {
594 if (NodeHelper.isDirty(this, DirtyBits.NODE_VISIBLE)
595 || NodeHelper.isDirty(this, DirtyBits.NODE_BOUNDS)) {
596 locateLwFrame(); // initialize location
597 }
598 if (NodeHelper.isDirty(this, DirtyBits.NODE_CONTENTS)) {
599 peer.markContentDirty();
600 }
601 }
602
603 /**
677 if (lwFrame != null) {
678 lwFrame.setVisible(visible);
679 }
680 });
681 }
682
683 private void setLwFrameScale(final int scale) {
684 if (lwFrame == null) {
685 return;
686 }
687 SwingFXUtils.runOnEDT(new Runnable() {
688 @Override
689 public void run() {
690 if (lwFrame != null) {
691 jlfNotifyDisplayChanged.invoke(lwFrame, scale);
692 }
693 }
694 });
695 }
696
697 /**
698 * @treatAsPrivate implementation detail
699 * @deprecated This is an internal API that is not intended for use and will be removed in the next version
700 */
701 @Deprecated
702 @Override
703 public BaseBounds impl_computeGeomBounds(BaseBounds bounds, BaseTransform tx) {
704 bounds.deriveWithNewBounds(0, 0, 0, (float)fxWidth, (float)fxHeight, 0);
705 tx.transform(bounds, bounds);
706 return bounds;
707 }
708
709 /**
710 * @treatAsPrivate implementation detail
711 * @deprecated This is an internal API that is not intended for use and will be removed in the next version
712 */
713 @Deprecated
714 @Override
715 public Object impl_processMXNode(MXNodeAlgorithm alg, MXNodeAlgorithmContext ctx) {
716 return alg.processLeafNode(this, ctx);
717 }
718
719 private class SwingNodeContent implements LightweightContent {
720 private JComponent comp;
721 private volatile FXDnD dnd;
722
723 public SwingNodeContent(JComponent comp) {
724 this.comp = comp;
725 }
726 @Override
727 public JComponent getComponent() {
728 return comp;
729 }
730 @Override
731 public void paintLock() {
732 paintLock.lock();
733 }
734 @Override
735 public void paintUnlock() {
763
764 if (getScene() != null &&
765 getScene().getWindow() != null &&
766 WindowHelper.getPeer(getScene().getWindow()) != null) {
767 WindowHelper.getPeer(getScene().getWindow()).grabFocus();
768 grabbed = true;
769 }
770 });
771 }
772 @Override
773 public void focusUngrabbed() {
774 SwingFXUtils.runOnFxThread(() -> {
775 ungrabFocus(false);
776 });
777 }
778 @Override
779 public void preferredSizeChanged(final int width, final int height) {
780 SwingFXUtils.runOnFxThread(() -> {
781 SwingNode.this.swingPrefWidth = width;
782 SwingNode.this.swingPrefHeight = height;
783 SwingNode.this.impl_notifyLayoutBoundsChanged();
784 });
785 }
786 @Override
787 public void maximumSizeChanged(final int width, final int height) {
788 SwingFXUtils.runOnFxThread(() -> {
789 SwingNode.this.swingMaxWidth = width;
790 SwingNode.this.swingMaxHeight = height;
791 SwingNode.this.impl_notifyLayoutBoundsChanged();
792 });
793 }
794 @Override
795 public void minimumSizeChanged(final int width, final int height) {
796 SwingFXUtils.runOnFxThread(() -> {
797 SwingNode.this.swingMinWidth = width;
798 SwingNode.this.swingMinHeight = height;
799 SwingNode.this.impl_notifyLayoutBoundsChanged();
800 });
801 }
802
803 //@Override
804 public void setCursor(Cursor cursor) {
805 SwingFXUtils.runOnFxThread(() -> {
806 SwingNode.this.setCursor(SwingCursors.embedCursorToCursor(cursor));
807 });
808 }
809
810 private void initDnD() {
811 // This is a part of AWT API, so the method may be invoked on any thread
812 synchronized (SwingNodeContent.this) {
813 if (this.dnd == null) {
814 this.dnd = new FXDnD(SwingNode.this);
815 }
816 }
817 }
818
819 //@Override
|
118 * launch(args);
119 * }
120 * }
121 * </pre>
122 * @since JavaFX 8.0
123 */
124 public class SwingNode extends Node {
125 static {
126 // This is used by classes in different packages to get access to
127 // private and package private methods.
128 SwingNodeHelper.setSwingNodeAccessor(new SwingNodeHelper.SwingNodeAccessor() {
129 @Override
130 public NGNode doCreatePeer(Node node) {
131 return ((SwingNode) node).doCreatePeer();
132 }
133
134 @Override
135 public void doUpdatePeer(Node node) {
136 ((SwingNode) node).doUpdatePeer();
137 }
138
139 @Override
140 public BaseBounds doComputeGeomBounds(Node node,
141 BaseBounds bounds, BaseTransform tx) {
142 return ((SwingNode) node).doComputeGeomBounds(bounds, tx);
143 }
144
145 @Override
146 public boolean doComputeContains(Node node, double localX, double localY) {
147 return ((SwingNode) node).doComputeContains(localX, localY);
148 }
149
150 @Override
151 public Object doProcessMXNode(Node node, MXNodeAlgorithm alg, MXNodeAlgorithmContext ctx) {
152 return ((SwingNode) node).doProcessMXNode(alg, ctx);
153 }
154 });
155 }
156
157 private double fxWidth;
158 private double fxHeight;
159
160 private int swingPrefWidth;
161 private int swingPrefHeight;
162 private int swingMaxWidth;
163 private int swingMaxHeight;
164 private int swingMinWidth;
165 private int swingMinHeight;
166
167 private volatile JComponent content;
168 private volatile JLightweightFrame lwFrame;
169 final JLightweightFrame getLightweightFrame() { return lwFrame; }
170
171 private NGExternalNode peer;
172
173 private final ReentrantLock paintLock = new ReentrantLock();
398 @Override public boolean isResizable() {
399 return true;
400 }
401
402 /**
403 * Invoked by the {@code SwingNode}'s parent during layout to set the {@code SwingNode}'s
404 * width and height. <b>Applications should not invoke this method directly</b>.
405 * If an application needs to directly set the size of the {@code SwingNode}, it should
406 * set the Swing component's minimum/preferred/maximum size constraints which will
407 * be propagated correspondingly to the {@code SwingNode} and it's parent will honor those
408 * settings during layout.
409 *
410 * @param width the target layout bounds width
411 * @param height the target layout bounds height
412 */
413 @Override public void resize(final double width, final double height) {
414 super.resize(width, height);
415 if (width != this.fxWidth || height != this.fxHeight) {
416 this.fxWidth = width;
417 this.fxHeight = height;
418 NodeHelper.geomChanged(this);
419 NodeHelper.markDirty(this, DirtyBits.NODE_GEOMETRY);
420 SwingFXUtils.runOnEDT(() -> {
421 if (lwFrame != null) {
422 locateLwFrame();
423 }
424 });
425 }
426 }
427
428 /**
429 * Returns the {@code SwingNode}'s preferred width for use in layout calculations.
430 * This value corresponds to the preferred width of the Swing component.
431 *
432 * @return the preferred width that the node should be resized to during layout
433 */
434 @Override
435 public double prefWidth(double height) {
436 return swingPrefWidth / getPlatformScaleX();
437 }
438
470 /**
471 * Returns the {@code SwingNode}'s minimum width for use in layout calculations.
472 * This value corresponds to the minimum width of the Swing component.
473 *
474 * @return the minimum width that the node should be resized to during layout
475 */
476 @Override public double minWidth(double height) {
477 return swingMinWidth / getPlatformScaleX();
478 }
479
480 /**
481 * Returns the {@code SwingNode}'s minimum height for use in layout calculations.
482 * This value corresponds to the minimum height of the Swing component.
483 *
484 * @return the minimum height that the node should be resized to during layout
485 */
486 @Override public double minHeight(double width) {
487 return swingMinHeight / getPlatformScaleY();
488 }
489
490 /*
491 * Note: This method MUST only be called via its accessor method.
492 */
493 private boolean doComputeContains(double localX, double localY) {
494 return true;
495 }
496
497 private final InvalidationListener locationListener = observable -> {
498 locateLwFrame();
499 };
500
501 private final EventHandler<FocusUngrabEvent> ungrabHandler = event -> {
502 if (!skipBackwardUnrgabNotification) {
503 if (lwFrame != null) {
504 AccessController.doPrivileged(new PostEventAction(new UngrabEvent(lwFrame)));
505 }
506 }
507 };
508
509 private final ChangeListener<Boolean> windowVisibleListener = (observable, oldValue, newValue) -> {
510 if (!newValue) {
511 disposeLwFrame();
512 } else {
513 setContent(content);
576
577 if (getScene() != null) {
578 addSceneListeners(getScene());
579 }
580
581 sceneProperty().addListener((observable, oldValue, newValue) -> {
582 if (oldValue != null) {
583 // Removed from scene
584 removeSceneListeners(oldValue);
585 disposeLwFrame();
586 }
587 if (newValue != null) {
588 // Added to another scene
589 if (content != null && lwFrame == null) {
590 setContent(content);
591 }
592 addSceneListeners(newValue);
593 }
594 });
595
596 NodeHelper.treeVisibleProperty(this).addListener((observable, oldValue, newValue) -> {
597 setLwFrameVisible(newValue);
598 });
599
600 return peer;
601 }
602
603 /*
604 * Note: This method MUST only be called via its accessor method.
605 */
606 private void doUpdatePeer() {
607 if (NodeHelper.isDirty(this, DirtyBits.NODE_VISIBLE)
608 || NodeHelper.isDirty(this, DirtyBits.NODE_BOUNDS)) {
609 locateLwFrame(); // initialize location
610 }
611 if (NodeHelper.isDirty(this, DirtyBits.NODE_CONTENTS)) {
612 peer.markContentDirty();
613 }
614 }
615
616 /**
690 if (lwFrame != null) {
691 lwFrame.setVisible(visible);
692 }
693 });
694 }
695
696 private void setLwFrameScale(final int scale) {
697 if (lwFrame == null) {
698 return;
699 }
700 SwingFXUtils.runOnEDT(new Runnable() {
701 @Override
702 public void run() {
703 if (lwFrame != null) {
704 jlfNotifyDisplayChanged.invoke(lwFrame, scale);
705 }
706 }
707 });
708 }
709
710 /*
711 * Note: This method MUST only be called via its accessor method.
712 */
713 private BaseBounds doComputeGeomBounds(BaseBounds bounds, BaseTransform tx) {
714 bounds.deriveWithNewBounds(0, 0, 0, (float)fxWidth, (float)fxHeight, 0);
715 tx.transform(bounds, bounds);
716 return bounds;
717 }
718
719 /*
720 * Note: This method MUST only be called via its accessor method.
721 */
722 private Object doProcessMXNode(MXNodeAlgorithm alg, MXNodeAlgorithmContext ctx) {
723 return alg.processLeafNode(this, ctx);
724 }
725
726 private class SwingNodeContent implements LightweightContent {
727 private JComponent comp;
728 private volatile FXDnD dnd;
729
730 public SwingNodeContent(JComponent comp) {
731 this.comp = comp;
732 }
733 @Override
734 public JComponent getComponent() {
735 return comp;
736 }
737 @Override
738 public void paintLock() {
739 paintLock.lock();
740 }
741 @Override
742 public void paintUnlock() {
770
771 if (getScene() != null &&
772 getScene().getWindow() != null &&
773 WindowHelper.getPeer(getScene().getWindow()) != null) {
774 WindowHelper.getPeer(getScene().getWindow()).grabFocus();
775 grabbed = true;
776 }
777 });
778 }
779 @Override
780 public void focusUngrabbed() {
781 SwingFXUtils.runOnFxThread(() -> {
782 ungrabFocus(false);
783 });
784 }
785 @Override
786 public void preferredSizeChanged(final int width, final int height) {
787 SwingFXUtils.runOnFxThread(() -> {
788 SwingNode.this.swingPrefWidth = width;
789 SwingNode.this.swingPrefHeight = height;
790 NodeHelper.notifyLayoutBoundsChanged(SwingNode.this);
791 });
792 }
793 @Override
794 public void maximumSizeChanged(final int width, final int height) {
795 SwingFXUtils.runOnFxThread(() -> {
796 SwingNode.this.swingMaxWidth = width;
797 SwingNode.this.swingMaxHeight = height;
798 NodeHelper.notifyLayoutBoundsChanged(SwingNode.this);
799 });
800 }
801 @Override
802 public void minimumSizeChanged(final int width, final int height) {
803 SwingFXUtils.runOnFxThread(() -> {
804 SwingNode.this.swingMinWidth = width;
805 SwingNode.this.swingMinHeight = height;
806 NodeHelper.notifyLayoutBoundsChanged(SwingNode.this);
807 });
808 }
809
810 //@Override
811 public void setCursor(Cursor cursor) {
812 SwingFXUtils.runOnFxThread(() -> {
813 SwingNode.this.setCursor(SwingCursors.embedCursorToCursor(cursor));
814 });
815 }
816
817 private void initDnD() {
818 // This is a part of AWT API, so the method may be invoked on any thread
819 synchronized (SwingNodeContent.this) {
820 if (this.dnd == null) {
821 this.dnd = new FXDnD(SwingNode.this);
822 }
823 }
824 }
825
826 //@Override
|