1 /* 2 * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 /* 27 * StubToolkit.java 28 */ 29 30 package test.com.sun.javafx.pgstub; 31 32 import com.sun.glass.ui.CommonDialogs.FileChooserResult; 33 import com.sun.javafx.application.PlatformImpl; 34 import com.sun.javafx.embed.HostInterface; 35 import com.sun.javafx.geom.Path2D; 36 import com.sun.javafx.geom.Shape; 37 import com.sun.javafx.geom.transform.BaseTransform; 38 import com.sun.javafx.menu.MenuBase; 39 import com.sun.javafx.perf.PerformanceTracker; 40 import com.sun.javafx.runtime.async.AsyncOperation; 41 import com.sun.javafx.runtime.async.AsyncOperationListener; 42 import com.sun.javafx.scene.text.TextLayoutFactory; 43 import com.sun.javafx.tk.*; 44 import com.sun.prism.BasicStroke; 45 import com.sun.scenario.DelayedRunnable; 46 import com.sun.scenario.animation.AbstractMasterTimer; 47 import com.sun.scenario.effect.FilterContext; 48 import com.sun.scenario.effect.Filterable; 49 import javafx.application.ConditionalFeature; 50 import javafx.geometry.Dimension2D; 51 import javafx.scene.image.Image; 52 import javafx.scene.input.*; 53 import javafx.scene.paint.Color; 54 import javafx.scene.paint.ImagePattern; 55 import javafx.scene.paint.LinearGradient; 56 import javafx.scene.paint.RadialGradient; 57 import javafx.scene.shape.*; 58 import javafx.stage.FileChooser.ExtensionFilter; 59 import javafx.stage.Modality; 60 import javafx.stage.StageStyle; 61 import javafx.stage.Window; 62 import javafx.util.Pair; 63 64 import java.io.File; 65 import java.io.InputStream; 66 import java.security.AccessControlContext; 67 import java.util.*; 68 import java.util.concurrent.Future; 69 70 /** 71 * A Toolkit implementation for use with Testing. 72 * 73 * @author Richard 74 */ 75 public class StubToolkit extends Toolkit { 76 77 private Map<Object, Object> contextMap = new HashMap<Object, Object>(); 78 79 private StubMasterTimer masterTimer = new StubMasterTimer(); 80 81 private PerformanceTracker performanceTracker = new StubPerformanceTracker(); 82 83 private final StubImageLoaderFactory imageLoaderFactory = 84 new StubImageLoaderFactory(); 85 86 private CursorSizeConverter cursorSizeConverter = 87 CursorSizeConverter.NO_CURSOR_SUPPORT; 88 89 private int maximumCursorColors = 2; 90 91 private TKScreenConfigurationListener screenConfigurationListener; 92 93 private static final ScreenConfiguration[] DEFAULT_SCREEN_CONFIG = { 94 new ScreenConfiguration(0, 0, 1920, 1200, 0, 0, 1920, 1172, 96) 95 }; 96 97 private ScreenConfiguration[] screenConfigurations = DEFAULT_SCREEN_CONFIG; 98 99 static { 100 try { 101 // ugly hack to initialize "runLater" method in Platform.java 102 PlatformImpl.startup(() -> {}); 103 } catch (Exception ex) {} 104 105 // allow tests to access PG scenegraph 106 // so that they can run with assertion enabled 107 javafx.scene.Scene.impl_setAllowPGAccess(true); 108 } 109 private boolean pulseRequested; 110 111 /* 112 * overrides of Toolkit's abstract functions 113 */ 114 115 @Override 116 public boolean init() { 117 return true; 118 } 119 120 @Override 121 public TKStage createTKStage(Window peerWindow, boolean securityDialog, StageStyle stageStyle, boolean primary, Modality modality, TKStage owner, boolean rtl, AccessControlContext acc) { 122 123 return new StubStage(); 124 } 125 126 @Override 127 public TKStage createTKPopupStage(Window peerWindow, StageStyle popupStyle, TKStage owner, AccessControlContext acc) { 128 return new StubPopupStage(); 129 } 130 131 @Override 132 public TKStage createTKEmbeddedStage(HostInterface host, AccessControlContext acc) { 133 return new StubStage(); 134 } 135 136 @Override 137 public AppletWindow createAppletWindow(long parent, String serverName) { 138 // unsupported 139 return null; 140 } 141 142 @Override 143 public void closeAppletWindow() { 144 // unsupported 145 } 146 147 private final TKSystemMenu systemMenu = new StubSystemMenu(); 148 @Override 149 public TKSystemMenu getSystemMenu() { 150 return systemMenu; 151 } 152 153 @Override 154 public void startup(Runnable runnable) { 155 runnable.run(); 156 } 157 158 @Override 159 public void checkFxUserThread() { 160 // Do nothing 161 } 162 163 @Override 164 public boolean isFxUserThread() { 165 // Always on the FX app thread 166 return true; 167 } 168 169 @Override 170 public void defer(Runnable runnable) { 171 runnable.run(); 172 } 173 174 @Override 175 public void exit() { 176 System.exit(0); 177 } 178 179 @Override 180 public Future addRenderJob(RenderJob rj) { 181 return rj; 182 } 183 184 @Override 185 public Map<Object, Object> getContextMap() { 186 return contextMap; 187 } 188 189 @Override public int getRefreshRate() { 190 return -1; 191 } 192 193 private DelayedRunnable animationRunnable; 194 195 @Override 196 public void setAnimationRunnable(DelayedRunnable animationRunnable) { 197 this.animationRunnable = animationRunnable; 198 } 199 200 @Override 201 public PerformanceTracker getPerformanceTracker() { 202 return performanceTracker; 203 } 204 205 @Override public PerformanceTracker createPerformanceTracker() { 206 return new StubPerformanceTracker(); 207 } 208 209 @Override 210 protected Object createColorPaint(Color paint) { 211 return new com.sun.prism.paint.Color((float) paint.getRed(), 212 (float) paint.getGreen(), 213 (float) paint.getBlue(), 214 (float) paint.getOpacity()); 215 } 216 217 @Override 218 protected Object createLinearGradientPaint(LinearGradient paint) { 219 // Non functioning but compiles 220 return new com.sun.prism.paint.Color(1, 1, 1, 1); 221 } 222 223 @Override 224 protected Object createRadialGradientPaint(RadialGradient paint) { 225 // Non functioning but compiles 226 return new com.sun.prism.paint.Color(1, 1, 1, 1); 227 } 228 229 @Override 230 protected Object createImagePatternPaint(ImagePattern paint) { 231 // Non functioning but compiles 232 return new com.sun.prism.paint.Color(1, 1, 1, 1); 233 } 234 235 static BasicStroke tmpStroke = new BasicStroke(); 236 void initStroke(StrokeType pgtype, double strokewidth, 237 StrokeLineCap pgcap, 238 StrokeLineJoin pgjoin, float miterLimit) 239 { 240 int type; 241 if (pgtype == StrokeType.CENTERED) { 242 type = BasicStroke.TYPE_CENTERED; 243 } else if (pgtype == StrokeType.INSIDE) { 244 type = BasicStroke.TYPE_INNER; 245 } else { 246 type = BasicStroke.TYPE_OUTER; 247 } 248 249 int cap; 250 if (pgcap == StrokeLineCap.BUTT) { 251 cap = BasicStroke.CAP_BUTT; 252 } else if (pgcap == StrokeLineCap.SQUARE) { 253 cap = BasicStroke.CAP_SQUARE; 254 } else { 255 cap = BasicStroke.CAP_ROUND; 256 } 257 258 int join; 259 if (pgjoin == StrokeLineJoin.BEVEL) { 260 join = BasicStroke.JOIN_BEVEL; 261 } else if (pgjoin == StrokeLineJoin.MITER) { 262 join = BasicStroke.JOIN_MITER; 263 } else { 264 join = BasicStroke.JOIN_ROUND; 265 } 266 267 tmpStroke.set(type, (float) strokewidth, cap, join, miterLimit); 268 } 269 270 @Override 271 public void accumulateStrokeBounds(Shape shape, float bbox[], 272 StrokeType pgtype, 273 double strokewidth, 274 StrokeLineCap pgcap, 275 StrokeLineJoin pgjoin, 276 float miterLimit, 277 BaseTransform tx) 278 { 279 280 initStroke(pgtype, strokewidth, pgcap, pgjoin, miterLimit); 281 // TODO: The accumulation could be done directly without creating a Shape 282 Shape.accumulate(bbox, tmpStroke.createStrokedShape(shape), tx); 283 } 284 285 @Override 286 public boolean strokeContains(Shape shape, double x, double y, 287 StrokeType pgtype, 288 double strokewidth, 289 StrokeLineCap pgcap, 290 StrokeLineJoin pgjoin, 291 float miterLimit) 292 { 293 initStroke(pgtype, strokewidth, pgcap, pgjoin, miterLimit); 294 // TODO: The contains testing could be done directly without creating a Shape 295 return tmpStroke.createStrokedShape(shape).contains((float) x, (float) y); 296 } 297 298 @Override 299 public Shape createStrokedShape(Shape shape, 300 StrokeType pgtype, 301 double strokewidth, 302 StrokeLineCap pgcap, 303 StrokeLineJoin pgjoin, 304 float miterLimit, 305 float[] dashArray, 306 float dashOffset) { 307 initStroke(pgtype, strokewidth, pgcap, pgjoin, miterLimit); 308 return tmpStroke.createStrokedShape(shape); 309 } 310 311 public CursorSizeConverter getCursorSizeConverter() { 312 return cursorSizeConverter; 313 } 314 315 public void setCursorSizeConverter( 316 CursorSizeConverter cursorSizeConverter) { 317 this.cursorSizeConverter = cursorSizeConverter; 318 } 319 320 @Override 321 public Dimension2D getBestCursorSize(int preferredWidth, int preferredHeight) { 322 return cursorSizeConverter.getBestCursorSize(preferredWidth, 323 preferredHeight); 324 } 325 326 @Override 327 public int getMaximumCursorColors() { 328 return maximumCursorColors; 329 } 330 331 public void setMaximumCursorColors(int maximumCursorColors) { 332 this.maximumCursorColors = maximumCursorColors; 333 } 334 335 @Override 336 public AbstractMasterTimer getMasterTimer() { 337 return masterTimer; 338 } 339 340 @Override 341 public FontLoader getFontLoader() { 342 return new StubFontLoader(); 343 } 344 345 @Override 346 public TextLayoutFactory getTextLayoutFactory() { 347 return new StubTextLayoutFactory(); 348 } 349 350 @Override public boolean isSupported(ConditionalFeature feature) { 351 if (feature == ConditionalFeature.SCENE3D) { 352 return true; 353 } else if (feature == ConditionalFeature.TRANSPARENT_WINDOW) { 354 return true; 355 } 356 return false; 357 } 358 359 /* 360 * additional testing functions 361 */ 362 public void fireTestPulse() { 363 firePulse(); 364 } 365 366 public boolean isPulseRequested() { 367 return pulseRequested; 368 } 369 370 public void clearPulseRequested() { 371 pulseRequested = false; 372 } 373 374 // do nothing -- bringing in FrameJob and MasterTimer also bring in 375 // Settings and crap which isn't setup for the testing stuff because 376 // we don't run through a RuntimeProvider or do normal startup 377 // public @Override public void triggerNextPulse():Void { } 378 @Override public void requestNextPulse() { 379 pulseRequested = true; 380 } 381 382 private TKClipboard clipboard = new TKClipboard() { 383 private Map<DataFormat, Object> map = new HashMap<DataFormat, Object>(); 384 private Image image; 385 private double offsetX; 386 private double offsetY; 387 388 @Override 389 public void setSecurityContext(AccessControlContext ctx) { 390 } 391 392 @Override public Set<DataFormat> getContentTypes() { 393 return map.keySet(); 394 } 395 396 @Override public boolean putContent(Pair<DataFormat, Object>... content) { 397 boolean good; 398 for (Pair<DataFormat,Object> pair : content) { 399 good = map.put(pair.getKey(), pair.getValue()) == pair.getValue(); 400 if (!good) return false; 401 } 402 return true; 403 } 404 405 @Override public Object getContent(DataFormat dataFormat) { 406 return map.get(dataFormat); 407 } 408 409 @Override public boolean hasContent(DataFormat dataFormat) { 410 return map.containsKey(dataFormat); 411 } 412 413 @Override public Set<TransferMode> getTransferModes() { 414 Set<TransferMode> modes = new HashSet<TransferMode>(); 415 modes.add(TransferMode.COPY); 416 return modes; 417 } 418 419 @Override 420 public void setDragView(Image image) { 421 this.image = image; 422 } 423 424 @Override 425 public void setDragViewOffsetX(double offsetX) { 426 this.offsetX = offsetX; 427 } 428 429 @Override 430 public void setDragViewOffsetY(double offsetY) { 431 this.offsetY = offsetY; 432 } 433 434 @Override 435 public Image getDragView() { 436 return image; 437 } 438 439 @Override 440 public double getDragViewOffsetX() { 441 return offsetX; 442 } 443 444 @Override 445 public double getDragViewOffsetY() { 446 return offsetY; 447 } 448 }; 449 450 451 @Override 452 public TKClipboard getSystemClipboard() { 453 return clipboard; 454 } 455 456 @Override public TKClipboard getNamedClipboard(String name) { 457 return null; 458 } 459 460 public static TKClipboard createDragboard() { 461 StubToolkit tk = (StubToolkit)Toolkit.getToolkit(); 462 if (tk.dndDelegate != null) { 463 return tk.dndDelegate.createDragboard(); 464 } 465 return null; 466 } 467 468 @Override 469 public void enableDrop(TKScene s, TKDropTargetListener l) { 470 if (dndDelegate != null) { 471 dndDelegate.enableDrop(l); 472 } 473 } 474 475 private ScreenConfigurationAccessor accessor = new ScreenConfigurationAccessor() { 476 @Override 477 public int getMinX(Object obj) { 478 return ((ScreenConfiguration) obj).getMinX(); 479 } 480 481 @Override 482 public int getMinY(Object obj) { 483 return ((ScreenConfiguration) obj).getMinY(); 484 } 485 486 @Override 487 public int getWidth(Object obj) { 488 return ((ScreenConfiguration) obj).getWidth(); 489 } 490 491 @Override 492 public int getHeight(Object obj) { 493 return ((ScreenConfiguration) obj).getHeight(); 494 } 495 496 @Override 497 public int getVisualMinX(Object obj) { 498 return ((ScreenConfiguration) obj).getVisualMinX(); 499 } 500 501 @Override 502 public int getVisualMinY(Object obj) { 503 return ((ScreenConfiguration) obj).getVisualMinY(); 504 } 505 506 @Override 507 public int getVisualWidth(Object obj) { 508 return ((ScreenConfiguration) obj).getVisualWidth(); 509 } 510 511 @Override 512 public int getVisualHeight(Object obj) { 513 return ((ScreenConfiguration) obj).getVisualHeight(); 514 } 515 516 @Override 517 public float getDPI(Object obj) { 518 return ((ScreenConfiguration) obj).getDPI(); 519 } 520 521 @Override 522 public float getUIScale(Object obj) { 523 return ((ScreenConfiguration) obj).getScale(); 524 } 525 526 @Override 527 public float getRenderScale(Object obj) { 528 return ((ScreenConfiguration) obj).getScale(); 529 } 530 }; 531 532 @Override 533 public ScreenConfigurationAccessor setScreenConfigurationListener( 534 TKScreenConfigurationListener listener) { 535 screenConfigurationListener = listener; 536 return accessor; 537 } 538 539 @Override 540 public ScreenConfiguration getPrimaryScreen() { 541 return screenConfigurations[0]; 542 } 543 544 public void setScreens(ScreenConfiguration... screenConfigurations) { 545 this.screenConfigurations = screenConfigurations.clone(); 546 if (screenConfigurationListener != null) { 547 screenConfigurationListener.screenConfigurationChanged(); 548 } 549 } 550 551 public void resetScreens() { 552 setScreens(DEFAULT_SCREEN_CONFIG); 553 } 554 555 @Override 556 public List<ScreenConfiguration> getScreens() { 557 return Arrays.asList(screenConfigurations); 558 } 559 560 @Override 561 public ScreenConfigurationAccessor getScreenConfigurationAccessor() { 562 return accessor; 563 } 564 565 @Override public void registerDragGestureListener(TKScene s, Set<TransferMode> tm, TKDragGestureListener l) { 566 if (dndDelegate != null) { 567 dndDelegate.registerListener(l); 568 } 569 } 570 571 @Override public void startDrag(TKScene scene, Set<TransferMode> tm, TKDragSourceListener l, Dragboard dragboard) { 572 if (dndDelegate != null) { 573 dndDelegate.startDrag(scene, tm, l, dragboard); 574 } 575 } 576 577 @Override 578 public ImageLoader loadImage(String url, int width, int height, 579 boolean preserveRatio, boolean smooth) { 580 return imageLoaderFactory.createImageLoader(url, width, height, 581 preserveRatio, smooth); 582 } 583 584 @Override 585 public ImageLoader loadImage(InputStream stream, int width, int height, 586 boolean preserveRatio, boolean smooth) { 587 return imageLoaderFactory.createImageLoader(stream, width, height, 588 preserveRatio, smooth); 589 } 590 591 @Override 592 public AsyncOperation loadImageAsync( 593 AsyncOperationListener listener, String url, int width, int height, 594 boolean preserveRatio, boolean smooth) { 595 return imageLoaderFactory.createAsyncImageLoader( 596 listener, url, width, height, preserveRatio, smooth); 597 } 598 599 @Override 600 public ImageLoader loadPlatformImage(Object platformImage) { 601 return imageLoaderFactory.createImageLoader(platformImage, 602 0, 0, false, false); 603 } 604 605 @Override 606 public PlatformImage createPlatformImage(int w, int h) { 607 PlatformImage image = new StubWritablePlatformImage(w, h); 608 imageLoaderFactory.registerImage(image, new StubPlatformImageInfo(w, h)); 609 return image; 610 } 611 612 @Override 613 public void waitFor(Task t) { 614 throw new UnsupportedOperationException(); 615 } 616 617 @Override 618 public int getKeyCodeForChar(String character) { 619 if (charToKeyCodeMap != null) { 620 final KeyCode keyCode = charToKeyCodeMap.get(character); 621 if (keyCode != null) { 622 return keyCode.getCode(); 623 } 624 } 625 626 return 0; 627 } 628 629 @Override 630 public PathElement[] convertShapeToFXPath(Object shape) { 631 // Had to be mocked up for TextField tests (for the caret!) 632 // Since the "shape" could be anything, I'm just returning 633 // something here, doesn't matter what. 634 return new PathElement[0]; 635 } 636 637 @Override 638 public Filterable toFilterable(Image img) { 639 return StubFilterable.create((StubPlatformImage)img.impl_getPlatformImage()); 640 } 641 642 @Override 643 public FilterContext getFilterContext(Object config) { 644 throw new UnsupportedOperationException(); 645 } 646 647 @Override 648 public boolean isForwardTraversalKey(KeyEvent e) { 649 throw new UnsupportedOperationException(); 650 } 651 652 @Override 653 public boolean isBackwardTraversalKey(KeyEvent e) { 654 throw new UnsupportedOperationException(); 655 } 656 657 @Override 658 public Object createSVGPathObject(SVGPath svgpath) { 659 int windingRule = (svgpath.getFillRule() == FillRule.NON_ZERO) ? 660 Path2D.WIND_NON_ZERO : Path2D.WIND_EVEN_ODD; 661 662 return new SVGPathImpl(svgpath.getContent(), windingRule); 663 } 664 665 @Override 666 public Path2D createSVGPath2D(SVGPath svgpath) { 667 int windingRule = (svgpath.getFillRule() == FillRule.NON_ZERO) ? 668 Path2D.WIND_NON_ZERO : Path2D.WIND_EVEN_ODD; 669 670 return new SVGPathImpl(svgpath.getContent(), windingRule); 671 } 672 673 @Override 674 public boolean imageContains(Object image, float x, float y) { 675 return ((StubPlatformImage) image).getImageInfo() 676 .contains((int) x, (int) y); 677 } 678 679 public void setCurrentTime(long millis) { 680 masterTimer.setCurrentTime(millis); 681 } 682 683 public void handleAnimation() { 684 if (animationRunnable != null) { 685 try { 686 animationRunnable.run(); 687 } catch (Throwable t) { 688 Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), t); 689 } 690 } 691 } 692 693 public StubImageLoaderFactory getImageLoaderFactory() { 694 return imageLoaderFactory; 695 } 696 697 public void setAnimationTime(final long millis) { 698 setCurrentTime(millis); 699 handleAnimation(); 700 fireTestPulse(); 701 } 702 703 @Override 704 public void installInputMethodRequests(TKScene scene, InputMethodRequests requests) { 705 // just do nothing here. 706 } 707 708 private Map<String, KeyCode> charToKeyCodeMap; 709 710 public void setCharToKeyCodeMap(Map<String, KeyCode> charToKeyCodeMap) { 711 this.charToKeyCodeMap = charToKeyCodeMap; 712 } 713 714 @Override 715 public Object renderToImage(ImageRenderingContext context) { 716 throw new UnsupportedOperationException(); 717 } 718 719 @Override public boolean canStartNestedEventLoop() { 720 return false; 721 } 722 723 @Override public Object enterNestedEventLoop(Object key) { 724 throw new UnsupportedOperationException("Not supported yet."); 725 } 726 727 @Override public void exitNestedEventLoop(Object key, Object rval) { 728 throw new UnsupportedOperationException("Not supported yet."); 729 } 730 731 @Override 732 public boolean isNestedLoopRunning() { 733 return false; 734 } 735 736 private KeyCode platformShortcutKey = KeyCode.SHORTCUT; 737 738 public void setPlatformShortcutKey(final KeyCode platformShortcutKey) { 739 this.platformShortcutKey = platformShortcutKey; 740 } 741 742 public KeyCode getPlatformShortcutKey() { 743 return platformShortcutKey; 744 } 745 746 private DndDelegate dndDelegate; 747 public void setDndDelegate(DndDelegate dndDelegate) { 748 this.dndDelegate = dndDelegate; 749 } 750 751 752 public interface DndDelegate { 753 void startDrag(TKScene scene, Set<TransferMode> tm, 754 TKDragSourceListener l, Dragboard dragboard); 755 756 TKClipboard createDragboard(); 757 758 DragEvent convertDragEventToFx(Object event, Dragboard dragboard); 759 760 void registerListener(TKDragGestureListener l); 761 762 void enableDrop(TKDropTargetListener l); 763 } 764 765 public interface CommonDialogsSupport { 766 FileChooserResult showFileChooser(TKStage ownerWindow, 767 String title, 768 File initialDirectory, 769 String initialFileName, 770 FileChooserType fileChooserType, 771 List<ExtensionFilter> extensionFilters, 772 ExtensionFilter selectedFilter); 773 774 File showDirectoryChooser(TKStage ownerWindow, 775 String title, 776 File initialDirectory); 777 } 778 779 private CommonDialogsSupport commonDialogsSupport; 780 public void setCommonDialogsSupport( 781 final CommonDialogsSupport commonDialogsSupport) { 782 this.commonDialogsSupport = commonDialogsSupport; 783 } 784 785 @Override 786 public FileChooserResult showFileChooser(TKStage ownerWindow, 787 String title, 788 File initialDirectory, 789 String initialFileName, 790 FileChooserType fileChooserType, 791 List<ExtensionFilter> extensionFilters, 792 ExtensionFilter selectedFilter) { 793 return commonDialogsSupport.showFileChooser( 794 ownerWindow, 795 title, 796 initialDirectory, 797 initialFileName, 798 fileChooserType, 799 extensionFilters, 800 selectedFilter); 801 } 802 803 804 @Override 805 public File showDirectoryChooser(TKStage ownerWindow, 806 String title, 807 File initialDirectory) { 808 return commonDialogsSupport.showDirectoryChooser( 809 ownerWindow, 810 title, 811 initialDirectory); 812 } 813 814 @Override 815 public long getMultiClickTime() { 816 return 500L; 817 } 818 819 @Override 820 public int getMultiClickMaxX() { 821 return 5; 822 } 823 824 @Override 825 public int getMultiClickMaxY() { 826 return 5; 827 } 828 829 public static final class ScreenConfiguration { 830 private final int minX; 831 private final int minY; 832 private final int width; 833 private final int height; 834 private final int visualMinX; 835 private final int visualMinY; 836 private final int visualWidth; 837 private final int visualHeight; 838 private final float dpi; 839 private final float scale; 840 841 public ScreenConfiguration(final int minX, final int minY, 842 final int width, final int height, 843 final int visualMinX, 844 final int visualMinY, 845 final int visualWidth, 846 final int visualHeight, 847 final float dpi) { 848 this.minX = minX; 849 this.minY = minY; 850 this.width = width; 851 this.height = height; 852 this.visualMinX = visualMinX; 853 this.visualMinY = visualMinY; 854 this.visualWidth = visualWidth; 855 this.visualHeight = visualHeight; 856 this.dpi = dpi; 857 this.scale = 1; // TODO: add a constructor that takes scale 858 } 859 860 public int getMinX() { 861 return minX; 862 } 863 864 public int getMinY() { 865 return minY; 866 } 867 868 public int getWidth() { 869 return width; 870 } 871 872 public int getHeight() { 873 return height; 874 } 875 876 public int getVisualMinX() { 877 return visualMinX; 878 } 879 880 public int getVisualMinY() { 881 return visualMinY; 882 } 883 884 public int getVisualWidth() { 885 return visualWidth; 886 } 887 888 public int getVisualHeight() { 889 return visualHeight; 890 } 891 892 public float getDPI() { 893 return dpi; 894 } 895 896 public float getScale() { 897 return scale; 898 } 899 } 900 901 public static class StubSystemMenu implements TKSystemMenu { 902 903 private List<MenuBase> menus = null; 904 905 @Override 906 public boolean isSupported() { 907 // Although not all platforms have a system menu, the only real 908 // interaction with the system menu is this TKSystemMenu instance 909 // so we'll return true on all platforms. 910 return true; 911 // final String os = System.getProperty("os.name"); 912 // return (os != null && os.startsWith("Mac")); 913 } 914 915 @Override 916 public void setMenus(List<MenuBase> menus) { 917 this.menus = menus; 918 } 919 920 // make menus accessible to unit tests 921 public List<MenuBase> getMenus() { 922 return menus; 923 } 924 925 } 926 }