1 /* 2 * Copyright (c) 2010, 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 package com.sun.glass.ui; 26 27 import com.sun.glass.events.MouseEvent; 28 import com.sun.glass.events.WindowEvent; 29 import com.sun.prism.impl.PrismSettings; 30 31 import java.util.Collections; 32 import java.util.LinkedList; 33 import java.util.List; 34 import java.util.Map; 35 36 public abstract class Window { 37 38 public static class EventHandler { 39 public void handleWindowEvent(Window window, long time, int type) { 40 } 41 42 /** 43 * Notifies a listener that the screen object for this Window instance 44 * has been updated. 45 * 46 * Note that while the old and new screen objects may be different, 47 * they can still represent the same physical screen. This can happen 48 * if e.g. only a certain parameter of the screen has been updated such 49 * as its scale factor. 50 * 51 * On some platforms when a window is moved to another physical screen 52 * an app can receive this event twice. One representing the physical 53 * screen change, and another - the display's parameters change. Note 54 * that sending two events instead of just one is platform-specific. 55 * 56 * The event handler can use the {@link Screen#getNativeScreen} method 57 * to determine if this is the same physical screen or not. If the 58 * native system always creates new native screen instances, there's no 59 * way for the app to distinguish between a real move to another screen 60 * or jsut a parameters update event. Since this is a somewhat rare 61 * event, an app is advised to always process it the same way. 62 * 63 * @see Window#getScreen 64 */ 65 public void handleScreenChangedEvent(Window window, long time, Screen oldScreen, Screen newScreen) { 66 } 67 68 /** 69 * Notifies the listener that the window level has changed. The Level should be one of 70 * {@link com.sun.glass.ui.Window.Level#NORMAL}, {@link com.sun.glass.ui.Window.Level#FLOATING}, 71 * {@link com.sun.glass.ui.Window.Level#TOPMOST}. 72 * @param level Level from {@link com.sun.glass.ui.Window.Level} class 73 */ 74 public void handleLevelEvent(int level) { 75 } 76 } 77 78 // Native object handle (HWND, or NSWindow*, etc.) 79 private long ptr; 80 81 // 'Delegate window' ptr. Used in e.g. the Full Screen mode. 82 private volatile long delegatePtr = 0L; 83 84 // window list 85 static private final LinkedList<Window> visibleWindows = new LinkedList<Window>(); 86 // Return a list of all visible windows. Note that on platforms without a native window manager, 87 // this list will be sorted in proper z-order 88 static public synchronized List<Window> getWindows() { 89 Application.checkEventThread(); 90 return Collections.unmodifiableList(Window.visibleWindows); 91 } 92 93 static public List<Window> getWindowsClone() { 94 Application.checkEventThread(); 95 return (List<Window>)visibleWindows.clone(); 96 } 97 98 // used by Lens Native 99 static protected void add(Window window) { 100 visibleWindows.add(window); 101 } 102 103 static protected void addFirst(Window window) { 104 visibleWindows.addFirst(window); 105 } 106 107 // used by Lens Native 108 static protected void remove(Window window) { 109 visibleWindows.remove(window); 110 } 111 112 // window style mask 113 114 // visual kind: mutually exclusive 115 public static final int UNTITLED = 0; 116 public static final int TITLED = 1 << 0; 117 public static final int TRANSPARENT = 1 << 1; 118 119 // functional type: mutually exclusive 120 /** 121 * Normal window. 122 * 123 * Usual top-level window. 124 */ 125 public static final int NORMAL = 0; 126 /** 127 * An utility window. 128 * 129 * Often used for floating toolbars. It has smaller than usual decorations 130 * and doesn't display a taskbar button. 131 */ 132 public static final int UTILITY = 1 << 2; 133 /** 134 * A popup window. 135 * 136 * Used to display popups (tooltips, popup menus, etc.) Note that by 137 * default it may display a task-bar button. To hide it the window must be 138 * owned. 139 */ 140 public static final int POPUP = 1 << 3; 141 142 // These affect window decorations as well as system menu actions, 143 // so applicable to both decorated and undecorated windows 144 public static final int CLOSABLE = 1 << 4; 145 public static final int MINIMIZABLE = 1 << 5; 146 public static final int MAXIMIZABLE = 1 << 6; 147 148 /** 149 * Indicates that the window trim will draw from right to left. 150 */ 151 public static final int RIGHT_TO_LEFT = 1 << 7; 152 153 /** 154 * Indicates that a window will have a client area textured the same way as the platform decorations 155 * and will not have a border between decorations and the client area. 156 * This is supported not on all platforms, the client should check if the feature is supported by using 157 * {@link com.sun.glass.ui.Application#supportsUnifiedWindows()} 158 */ 159 public static final int UNIFIED = 1 << 8; 160 161 final static public class State { 162 public static final int NORMAL = 1; 163 public static final int MINIMIZED = 2; 164 public static final int MAXIMIZED = 3; 165 } 166 167 /** 168 * Available window levels. 169 * 170 * Note that on some platforms both {@code FLOATING} and {@code TOPMOST} 171 * may represent the same window level. 172 * 173 * @see #setLevel 174 */ 175 public static final class Level { 176 private static final int _MIN = 1; 177 178 /** Normal window level. */ 179 public static final int NORMAL = 1; 180 181 /** A window level that is above all other NORMAL windows. */ 182 public static final int FLOATING = 2; 183 184 /** A very topmost window level. May cover system UI elements such as dock, taskbar, etc. */ 185 public static final int TOPMOST = 3; 186 187 private static final int _MAX = 3; 188 } 189 190 private final Window owner; 191 private final long parent; 192 private final int styleMask; 193 private final boolean isDecorated; 194 private boolean shouldStartUndecoratedMove = false; 195 196 private View view = null; 197 private Screen screen = null; 198 private MenuBar menubar = null; 199 private String title = ""; 200 private UndecoratedMoveResizeHelper helper = null; 201 202 private int state = State.NORMAL; 203 private int level = Level.NORMAL; 204 private int x = 0; 205 private int y = 0; 206 private int width = 0; 207 private int height = 0; 208 private float alpha = 1.0f; 209 private float platformScaleX = 1.0f; 210 private float platformScaleY = 1.0f; 211 private float outputScaleX = 1.0f; 212 private float outputScaleY = 1.0f; 213 private float renderScaleX = 1.0f; 214 private float renderScaleY = 1.0f; 215 216 // This is a workaround for RT-15970: as for embedded windows we don't 217 // receive any MOVE notifications from the native platform, we poll 218 // the window location on screen from timer and post synthetic events 219 // if it has changed 220 private Timer embeddedLocationTimer = null; 221 private int lastKnownEmbeddedX = 0; 222 private int lastKnownEmbeddedY = 0; 223 224 private volatile boolean isResizable = false; 225 private volatile boolean isVisible = false; 226 private volatile boolean isFocused = false; 227 private volatile boolean isFocusable = true; 228 private volatile boolean isModal = false; 229 230 // Indicates how many times setEnabled(false) has been called. 231 // A value of 0 means the window is enabled. 232 private volatile int disableCount = 0; 233 234 private int minimumWidth = 0, minimumHeight = 0; 235 private int maximumWidth = Integer.MAX_VALUE, maximumHeight = Integer.MAX_VALUE; 236 237 private EventHandler eventHandler; 238 239 protected abstract long _createWindow(long ownerPtr, long screenPtr, int mask); 240 protected Window(Window owner, Screen screen, int styleMask) { 241 Application.checkEventThread(); 242 switch (styleMask & (TITLED | TRANSPARENT)) { 243 case UNTITLED: 244 case TITLED: 245 case TRANSPARENT: 246 break; 247 default: 248 throw new RuntimeException("The visual kind should be UNTITLED, TITLED, or TRANSPARENT, but not a combination of these"); 249 } 250 switch (styleMask & (POPUP | UTILITY)) { 251 case NORMAL: 252 case POPUP: 253 case UTILITY: 254 break; 255 default: 256 throw new RuntimeException("The functional type should be NORMAL, POPUP, or UTILITY, but not a combination of these"); 257 } 258 259 if (((styleMask & UNIFIED) != 0) 260 && !Application.GetApplication().supportsUnifiedWindows()) { 261 styleMask &= ~UNIFIED; 262 } 263 264 if (((styleMask & TRANSPARENT) != 0) 265 && !Application.GetApplication().supportsTransparentWindows()) { 266 styleMask &= ~TRANSPARENT; 267 } 268 269 270 this.owner = owner; 271 this.parent = 0L; 272 this.styleMask = styleMask; 273 this.isDecorated = (this.styleMask & Window.TITLED) != 0; 274 275 this.screen = screen != null ? screen : Screen.getMainScreen(); 276 if (PrismSettings.allowHiDPIScaling) { 277 this.platformScaleX = this.screen.getPlatformScaleX(); 278 this.platformScaleY = this.screen.getPlatformScaleY(); 279 this.outputScaleX = this.screen.getRecommendedOutputScaleX(); 280 this.outputScaleY = this.screen.getRecommendedOutputScaleY(); 281 } 282 283 this.ptr = _createWindow(owner != null ? owner.getNativeHandle() : 0L, 284 this.screen.getNativeScreen(), this.styleMask); 285 if (this.ptr == 0L) { 286 throw new RuntimeException("could not create platform window"); 287 } 288 } 289 290 protected abstract long _createChildWindow(long parent); 291 /** 292 * Constructs a child window of the specified native parent window. 293 */ 294 protected Window(long parent) { 295 Application.checkEventThread(); 296 this.owner = null; 297 this.parent = parent; 298 this.styleMask = Window.UNTITLED; 299 this.isDecorated = false; 300 301 // Note: we can't always catch screen changes when parent is moved... 302 this.screen = null; // should infer from the parent 303 304 this.ptr = _createChildWindow(parent); 305 if (this.ptr == 0L) { 306 throw new RuntimeException("could not create platform window"); 307 } 308 } 309 310 public boolean isClosed() { 311 Application.checkEventThread(); 312 return this.ptr == 0L; 313 } 314 315 private void checkNotClosed() { 316 if (this.ptr == 0L) { 317 throw new IllegalStateException("The window has already been closed"); 318 } 319 } 320 321 protected abstract boolean _close(long ptr); 322 public void close() { 323 Application.checkEventThread(); 324 if (this.view != null) { 325 if (this.ptr != 0L) { 326 _setView(this.ptr, null); 327 } 328 this.view.setWindow(null); 329 this.view.close(); 330 this.view = null; 331 } 332 if (this.ptr != 0L) { 333 _close(this.ptr); 334 } 335 } 336 337 private boolean isChild() { 338 Application.checkEventThread(); 339 return this.parent != 0L; 340 } 341 342 /** This method returns "lowest-level" native window handle 343 * (HWND on Windows, NSWindow on Mac, X11 Window handle on linux-gtk etc.) 344 */ 345 public long getNativeWindow() { 346 Application.checkEventThread(); 347 checkNotClosed(); 348 return this.delegatePtr != 0L ? this.delegatePtr : this.ptr; 349 } 350 351 /** 352 * This method returns "higher-level" native window handle. 353 * glass-mat-lib-gtk GtkWindow.java returns GtkWindow pointer for example. 354 */ 355 public long getNativeHandle() { 356 Application.checkEventThread(); 357 return this.delegatePtr != 0L ? this.delegatePtr : this.ptr; 358 } 359 360 /** 361 * return the "raw' pointer needed by subclasses to pass to native routines 362 * @return the native pointer. 363 */ 364 protected long getRawHandle() { 365 return ptr; 366 } 367 368 public Window getOwner() { 369 Application.checkEventThread(); 370 return this.owner; 371 } 372 373 public View getView() { 374 Application.checkEventThread(); 375 return this.view; 376 } 377 378 protected abstract boolean _setView(long ptr, View view); 379 public void setView(final View view) { 380 Application.checkEventThread(); 381 checkNotClosed(); 382 View oldView = getView(); 383 if (oldView == view) { 384 return; 385 } 386 387 if (oldView != null) { 388 oldView.setWindow(null); 389 } 390 if (view != null) { 391 Window host = view.getWindow(); 392 if (host != null) { 393 host.setView(null); 394 } 395 } 396 397 if (view != null && _setView(this.ptr, view)) { 398 this.view = view; 399 this.view.setWindow(this); 400 if (this.isDecorated == false) { 401 this.helper = new UndecoratedMoveResizeHelper(); 402 } 403 } else { 404 _setView(this.ptr, null); 405 this.view = null; 406 } 407 } 408 409 public Screen getScreen() { 410 Application.checkEventThread(); 411 return this.screen; 412 } 413 414 protected void setScreen(Screen screen) { 415 Application.checkEventThread(); 416 417 final Screen old = this.screen; 418 this.screen = screen; 419 420 if (this.eventHandler != null) { 421 if ((old == null && this.screen != null) || 422 (old != null && !old.equals(this.screen))) { 423 this.eventHandler.handleScreenChangedEvent(this, System.nanoTime(), old, this.screen); 424 } 425 } 426 } 427 428 public int getStyleMask() { 429 Application.checkEventThread(); 430 return this.styleMask; 431 } 432 433 public MenuBar getMenuBar() { 434 Application.checkEventThread(); 435 return this.menubar; 436 } 437 438 protected abstract boolean _setMenubar(long ptr, long menubarPtr); 439 public void setMenuBar(final MenuBar menubar) { 440 Application.checkEventThread(); 441 checkNotClosed(); 442 if (_setMenubar(this.ptr, menubar.getNativeMenu())) { 443 this.menubar = menubar; 444 } 445 } 446 447 public boolean isDecorated() { 448 Application.checkEventThread(); 449 return this.isDecorated; 450 } 451 452 public boolean isMinimized() { 453 Application.checkEventThread(); 454 return (this.state == State.MINIMIZED); 455 } 456 457 protected abstract boolean _minimize(long ptr, boolean minimize); 458 public boolean minimize(final boolean minimize) { 459 Application.checkEventThread(); 460 checkNotClosed(); 461 _minimize(this.ptr, minimize); 462 //XXX: this is synchronous? On X11 this may not work 463 return isMinimized(); 464 } 465 466 public boolean isMaximized() { 467 Application.checkEventThread(); 468 return (this.state == State.MAXIMIZED); 469 } 470 471 protected abstract boolean _maximize(long ptr, boolean maximize, boolean wasMaximized); 472 public boolean maximize(final boolean maximize) { 473 Application.checkEventThread(); 474 checkNotClosed(); 475 _maximize(ptr, maximize, isMaximized()); 476 return isMaximized(); 477 } 478 479 protected void notifyScaleChanged(float platformScaleX, float platformScaleY, 480 float outputScaleX, float outputScaleY) 481 { 482 if (!PrismSettings.allowHiDPIScaling) return; 483 this.platformScaleX = platformScaleX; 484 this.platformScaleY = platformScaleY; 485 this.outputScaleX = outputScaleX; 486 this.outputScaleY = outputScaleY; 487 notifyRescale(); 488 } 489 490 /** 491 * Return the horizontal scale used to communicate window locations, 492 * sizes, and event coordinates to/from the platform. 493 * @return the horizontal platform scaling for screen locations 494 */ 495 public final float getPlatformScaleX() { 496 return platformScaleX; 497 } 498 499 /** 500 * Return the vertical scale used to communicate window locations, 501 * sizes, and event coordinates to/from the platform. 502 * @return the vertical platform scaling for screen locations 503 */ 504 public final float getPlatformScaleY() { 505 return platformScaleY; 506 } 507 508 public void setRenderScaleX(float renderScaleX) { 509 if (!PrismSettings.allowHiDPIScaling) return; 510 this.renderScaleX = renderScaleX; 511 } 512 513 public void setRenderScaleY(float renderScaleY) { 514 if (!PrismSettings.allowHiDPIScaling) return; 515 this.renderScaleY = renderScaleY; 516 } 517 518 /** 519 * Return the horizontal scale used for rendering the back buffer. 520 * @return the horizontal scaling for rendering 521 */ 522 public final float getRenderScaleX() { 523 return renderScaleX; 524 } 525 526 /** 527 * Return the vertical scale used for rendering to the back buffer. 528 * @return the vertical scaling for rendering 529 */ 530 public final float getRenderScaleY() { 531 return renderScaleY; 532 } 533 534 public float getOutputScaleX() { 535 return outputScaleX; 536 } 537 538 public float getOutputScaleY() { 539 return outputScaleY; 540 } 541 542 protected abstract int _getEmbeddedX(long ptr); 543 protected abstract int _getEmbeddedY(long ptr); 544 545 private void checkScreenLocation() { 546 this.x = _getEmbeddedX(ptr); 547 this.y = _getEmbeddedY(ptr); 548 if ((this.x != lastKnownEmbeddedX) || (this.y != lastKnownEmbeddedY)) { 549 lastKnownEmbeddedX = this.x; 550 lastKnownEmbeddedY = this.y; 551 handleWindowEvent(System.nanoTime(), WindowEvent.MOVE); 552 } 553 } 554 555 public int getX() { 556 Application.checkEventThread(); 557 return this.x; 558 } 559 560 public int getY() { 561 Application.checkEventThread(); 562 return this.y; 563 } 564 565 public int getWidth() { 566 Application.checkEventThread(); 567 return this.width; 568 } 569 570 public int getHeight() { 571 Application.checkEventThread(); 572 return this.height; 573 } 574 575 protected abstract void _setBounds(long ptr, int x, int y, boolean xSet, boolean ySet, 576 int w, int h, int cw, int ch, 577 float xGravity, float yGravity); 578 579 /** 580 * Sets the window bounds to the specified values. 581 * 582 * Gravity values specify how to correct window location if only its size 583 * changes (for example when stage decorations are added). User initiated 584 * resizing should be ignored and must not influence window location through 585 * this mechanism. 586 * 587 * The corresponding correction formulas are: 588 * 589 * {@code x -= xGravity * deltaW} 590 * {@code y -= yGravity * deltaH} 591 * 592 * @param x the new window horizontal position, ignored if xSet is set to 593 * false 594 * @param y the new window vertical position, ignored if ySet is set to 595 * false 596 * @param xSet indicates whether the x parameter is valid 597 * @param ySet indicates whether the y parameter is valid 598 * @param w the new window width, ignored if set to -1 599 * @param h the new window height, ignored if set to -1 600 * @param cw the new window content width, ignored if set to -1 601 * @param ch the new window content height, ignored if set to -1 602 * @param xGravity the xGravity coefficient 603 * @param yGravity the yGravity coefficient 604 */ 605 public void setBounds(int x, int y, boolean xSet, boolean ySet, 606 int w, int h, int cw, int ch, 607 float xGravity, float yGravity) 608 { 609 Application.checkEventThread(); 610 checkNotClosed(); 611 _setBounds(ptr, x, y, xSet, ySet, w, h, cw, ch, xGravity, yGravity); 612 } 613 614 public void setPosition(int x, int y) { 615 Application.checkEventThread(); 616 setBounds(x, y, true, true, 0, 0, 0, 0, 0, 0); 617 } 618 619 public void setSize(int w, int h) { 620 Application.checkEventThread(); 621 setBounds(0, 0, false, false, w, h, 0, 0, 0, 0); 622 } 623 624 public void setContentSize(int cw, int ch) { 625 Application.checkEventThread(); 626 setBounds(0, 0, false, false, 0, 0, cw, ch, 0, 0); 627 } 628 629 public boolean isVisible() { 630 Application.checkEventThread(); 631 return this.isVisible; 632 } 633 634 /** 635 * Generates a ViewEvent.MOVE aka insets (might have) changed. 636 */ 637 private void synthesizeViewMoveEvent() { 638 final View view = getView(); 639 if (view != null) { 640 view.notifyView(com.sun.glass.events.ViewEvent.MOVE); 641 } 642 } 643 644 protected abstract boolean _setVisible(long ptr, boolean visible); 645 public void setVisible(final boolean visible) { 646 Application.checkEventThread(); 647 if (this.isVisible != visible) { 648 if (!visible) { 649 if (getView() != null) { 650 getView().setVisible(visible); 651 } 652 // Avoid native call if the window has been closed already 653 if (this.ptr != 0L) { 654 this.isVisible = _setVisible(this.ptr, visible); 655 } else { 656 this.isVisible = visible; 657 } 658 remove(this); 659 if (parent != 0) { 660 embeddedLocationTimer.stop(); 661 } 662 } else { 663 checkNotClosed(); 664 this.isVisible = _setVisible(this.ptr, visible); 665 666 if (getView() != null) { 667 getView().setVisible(this.isVisible); 668 } 669 add(this); 670 if (parent != 0) { 671 final Runnable checkRunnable = () -> checkScreenLocation(); 672 final Runnable timerRunnable = () -> Application.invokeLater(checkRunnable); 673 embeddedLocationTimer = 674 Application.GetApplication().createTimer(timerRunnable); 675 embeddedLocationTimer.start(16); 676 } 677 678 synthesizeViewMoveEvent(); 679 } 680 } 681 } 682 683 protected abstract boolean _setResizable(long ptr, boolean resizable); 684 public boolean setResizable(final boolean resizable) { 685 Application.checkEventThread(); 686 checkNotClosed(); 687 if (this.isResizable != resizable) { 688 if (_setResizable(this.ptr, resizable)) { 689 this.isResizable = resizable; 690 synthesizeViewMoveEvent(); 691 } 692 } 693 return isResizable; 694 } 695 696 public boolean isResizable() { 697 Application.checkEventThread(); 698 return this.isResizable; 699 } 700 701 public boolean isUnifiedWindow() { 702 //The UNIFIED flag is set only if it is supported 703 return (this.styleMask & Window.UNIFIED) != 0; 704 } 705 706 public boolean isTransparentWindow() { 707 //The TRANSPARENT flag is set only if it is supported 708 return (this.styleMask & Window.TRANSPARENT) != 0; 709 } 710 711 private static volatile Window focusedWindow = null; 712 public static Window getFocusedWindow() { 713 Application.checkEventThread(); 714 return Window.focusedWindow; 715 } 716 717 private static void setFocusedWindow(final Window window) { 718 Window.focusedWindow = window; 719 } 720 721 public boolean isFocused() { 722 Application.checkEventThread(); 723 return this.isFocused; 724 } 725 726 protected abstract boolean _requestFocus(long ptr, int event); 727 /** 728 * Requests or resigns focus on this window. 729 * 730 * If this is a top-level window (owned or not), then the only possible 731 * value for the {@code event} argument is WindowEvent.FOCUS_GAINED. 732 * Otherwise, if the window is a child window, the argument may be 733 * WindowEvent.FOCUS_LOST, FOCUS_GAINED, FOCUS_GAINED_FORWARD, or 734 * FOCUS_GAINED_BACKWARD. 735 * 736 * @param event one of WindowEvent.FOCUS_LOST, FOCUS_GAINED, FOCUS_GAINED_FORWARD, FOCUS_GAINED_BACKWARD 737 * 738 * @throws IllegalArgumentException if the argument value is invalid for this window 739 * 740 * @return {@code true} if the operation succeeded 741 */ 742 public boolean requestFocus(int event) { 743 Application.checkEventThread(); 744 checkNotClosed(); 745 746 if (!isChild() && event != WindowEvent.FOCUS_GAINED) { 747 throw new IllegalArgumentException("Invalid focus event ID for top-level window"); 748 } 749 750 if (isChild() && (event < WindowEvent._FOCUS_MIN || event > WindowEvent._FOCUS_MAX)) { 751 throw new IllegalArgumentException("Invalid focus event ID for child window"); 752 } 753 754 if (event == WindowEvent.FOCUS_LOST && !isFocused()) { 755 // Already unfocused, nothing to do 756 return true; 757 } 758 759 // At this point either A) the user requests focus for a focused or unfocused window, 760 // or B) the window is focused and the user requests FOCUS_LOST 761 if (!this.isFocusable) { 762 // It's obviously A). Fail. 763 return false; 764 } 765 766 return _requestFocus(this.ptr, event); 767 } 768 769 public boolean requestFocus() { 770 Application.checkEventThread(); 771 return requestFocus(WindowEvent.FOCUS_GAINED); 772 } 773 774 protected abstract void _setFocusable(long ptr, boolean isFocusable); 775 /** 776 * Sets whether this window is focusable. 777 * 778 * Clicking an unfocusable window doesn't activate it. 779 */ 780 public void setFocusable(final boolean isFocusable) { 781 Application.checkEventThread(); 782 checkNotClosed(); 783 this.isFocusable = isFocusable; 784 if (isEnabled()) { 785 _setFocusable(this.ptr, isFocusable); 786 } 787 } 788 789 protected abstract boolean _grabFocus(long ptr); 790 protected abstract void _ungrabFocus(long ptr); 791 /** 792 * Grabs focus on this window. 793 * 794 * All mouse clicks that occur in this window's client area or client-areas 795 * of any of its unfocusable owned windows are delivered as usual. Whenever 796 * a click occurs on another app's window (not related via the ownership 797 * relation with this one, or a focusable owned window), or on non-client 798 * area of any window (titlebar, etc.), or any third-party app's window, or 799 * native OS GUI (e.g. a taskbar), the grab is automatically reset, and the 800 * window that held the grab receives the FOCUS_UNGRAB event. 801 * 802 * Note that for this functionality to work correctly, the window must have 803 * a focus upon calling this method. All owned popup windows that should be 804 * operable during the grabbed focus state (e.g. nested popup menus) must 805 * be unfocusable (see {@link #setFocusable}). Clicking a focusable owned 806 * window will reset the grab due to a focus transfer. 807 * 808 * The click that occurs in another window and causes resetting of the grab 809 * may or may not be delivered to that other window depending on the native 810 * OS behavior. 811 * 812 * If any of the application's windows already holds the grab, it is reset 813 * prior to grabbing the focus for this window. The method may be called 814 * multiple times for one window. Subsequent calls do not affect the grab 815 * status unless it is reset between the calls, in which case the focus 816 * is grabbed again. 817 * 818 * Note that grabbing the focus on an application window may prevent 819 * delivering certain events to other applications until the grab is reset. 820 * Therefore, if the application has finished showing popup windows based 821 * on a user action (e.g. clicking a menu item), and doesn't require the 822 * grab any more, it should call the {@link #ungrabFocus} method. The 823 * FOCUS_UNGRAB event signals that the grab has been reset. 824 * 825 * A user event handler associated with a menu item must be invoked after 826 * resetting the grab. Otherwise, if a developer debugs the application and 827 * has installed a breakpoint in the event handler, the debugger may become 828 * unoperable due to events blocking for other applications on some 829 * platforms. 830 * 831 * @return {@code true} if the operation is successful 832 * @throws IllegalStateException if the window isn't focused currently 833 */ 834 public boolean grabFocus() { 835 Application.checkEventThread(); 836 checkNotClosed(); 837 838 if (!isFocused()) { 839 throw new IllegalStateException("The window must be focused when calling grabFocus()"); 840 } 841 842 return _grabFocus(this.ptr); 843 } 844 845 /** 846 * Manually ungrabs focus grabbed on this window previously. 847 * 848 * This method resets the grab, and forces sending of the FOCUS_UNGRAB 849 * event. It should be used when popup windows (such as menus) should be 850 * dismissed manually, e.g. when a user clicks a menu item which usually 851 * causes the menus to hide. 852 * 853 * @see #grabFocus 854 */ 855 public void ungrabFocus() { 856 Application.checkEventThread(); 857 checkNotClosed(); 858 _ungrabFocus(this.ptr); 859 } 860 861 public String getTitle() { 862 Application.checkEventThread(); 863 return this.title; 864 } 865 866 protected abstract boolean _setTitle(long ptr, String title); 867 public void setTitle(String title) { 868 Application.checkEventThread(); 869 checkNotClosed(); 870 if (title == null) { 871 title = ""; 872 } 873 if (!title.equals(this.title)) { 874 if (_setTitle(this.ptr, title)) { 875 this.title = title; 876 } 877 } 878 } 879 880 protected abstract void _setLevel(long ptr, int level); 881 /** 882 * Set the level of this window in the z-order. 883 * 884 * @param level one of the constants from {@link Window.Level} 885 * @see Window.Level 886 */ 887 public void setLevel(final int level) { 888 Application.checkEventThread(); 889 checkNotClosed(); 890 if (level < Level._MIN || level > Level._MAX) { 891 throw new IllegalArgumentException("Level should be in the range [" + Level._MIN + ".." + Level._MAX + "]"); 892 } 893 if (this.level != level) { 894 _setLevel(this.ptr, level); 895 this.level = level; 896 } 897 } 898 899 public int getLevel() { 900 Application.checkEventThread(); 901 return this.level; 902 } 903 904 private boolean isInFullscreen() { 905 final View view = getView(); 906 return view == null ? false : view.isInFullscreen(); 907 } 908 909 // Invoked from the View class before sending FULLSCREEN_ to the View.EventHandler 910 void notifyFullscreen(boolean entered) { 911 final float alpha = getAlpha(); 912 if (alpha < 1f) { 913 if (entered) { 914 // Reset alpha at native level 915 _setAlpha(this.ptr, 1f); 916 } else { 917 // restore the current opacity level 918 setAlpha(alpha); 919 } 920 } 921 } 922 923 protected abstract void _setAlpha(long ptr, float alpha); 924 /** 925 * Sets the uniform translucency level for this window. 926 * 927 * In the full screen mode the native window is always fully opaque. 928 * The requested opacity level is applied upon exiting the full screen 929 * mode only. 930 * 931 * @param alpha a value in the range [0..1f] (transparent..fully-opaque) 932 */ 933 public void setAlpha(final float alpha) { 934 Application.checkEventThread(); 935 checkNotClosed(); 936 if (alpha < 0f || alpha > 1f) { 937 throw new IllegalArgumentException("Alpha should be in the range [0f..1f]"); 938 } 939 940 this.alpha = alpha; 941 942 if (alpha < 1f && isInFullscreen()) { 943 return; 944 } 945 946 _setAlpha(this.ptr, this.alpha); 947 } 948 949 public float getAlpha() { 950 Application.checkEventThread(); 951 return this.alpha; 952 } 953 954 protected abstract boolean _setBackground(long ptr, float r, float g, float b); 955 /** 956 * Set the background of the window. 957 * 958 * In most cases the View covers the whole window, so the background color 959 * of the window is never seen by the user. However, a window w/o a view 960 * does display the background color in its content area. 961 * 962 * On some platforms setting the background color may produce flickering 963 * when painting the content area of the View (even though the View covers 964 * the whole window). Therefore it is recommended to set the background 965 * color to windows w/o views only. 966 */ 967 public boolean setBackground(final float r, final float g, final float b) { 968 Application.checkEventThread(); 969 checkNotClosed(); 970 return _setBackground(this.ptr, r, g, b); 971 } 972 973 public boolean isEnabled() { 974 Application.checkEventThread(); 975 return this.disableCount == 0; 976 } 977 978 protected abstract void _setEnabled(long ptr, boolean enabled); 979 /** 980 * Enables or disables the window. 981 * 982 * A disabled window is unfocusable by definition. 983 * Also, key or mouse events aren't generated for disabled windows. 984 * 985 * When a user tries to activate a disabled window, or the window gets 986 * accidentally brought to the top of the stacking order, the window 987 * generates the FOCUS_DISABLED window event. A Glass client should react 988 * to this event and bring the currently active modal blocker of the 989 * disabled window to top by calling blocker's minimize(false), toFront(), 990 * and requestFocus() methods. It may also 'blink' the blocker window to 991 * further attract user's attention. 992 * 993 * It's strongly recommended to process the FOCUS_DISABLED event 994 * synchronously and as fast as possible to avoid any possible visual and 995 * behavioral artifacts. Note that a disabled window may by no means gain 996 * the input focus. The purpose of this event is to make sure that the 997 * current modal blocker window is always visible to the user, and the user 998 * understands why he can't interact with a disabled window. 999 * 1000 * The method supports nested calls. If you disable the window twice 1001 * with two calls to setEnabled(false), you must call setEnabled(true) 1002 * twice as well in order to enable it afterwards. This is to support 1003 * 'nested' modal dialogs when one modal dialog opens another one. 1004 */ 1005 public void setEnabled(boolean enabled) { 1006 Application.checkEventThread(); 1007 checkNotClosed(); 1008 if (!enabled) { 1009 if (++this.disableCount > 1) { 1010 // already disabled 1011 return; 1012 } 1013 } else { 1014 if (this.disableCount == 0) { 1015 //should report a warning about an extra enable call ? 1016 return; 1017 } 1018 if (--this.disableCount > 0) { 1019 // not yet enabled 1020 return; 1021 } 1022 } 1023 1024 //TODO: on Windows _setFocusable(this.ptr, isEnabled() ? this.isFocusable : false); 1025 _setEnabled(this.ptr, isEnabled()); 1026 } 1027 1028 public int getMinimumWidth() { 1029 Application.checkEventThread(); 1030 return this.minimumWidth; 1031 } 1032 1033 public int getMinimumHeight() { 1034 Application.checkEventThread(); 1035 return this.minimumHeight; 1036 } 1037 1038 public int getMaximumWidth() { 1039 Application.checkEventThread(); 1040 return this.maximumWidth; 1041 } 1042 1043 public int getMaximumHeight() { 1044 Application.checkEventThread(); 1045 return this.maximumHeight; 1046 } 1047 1048 protected abstract boolean _setMinimumSize(long ptr, int width, int height); 1049 /** 1050 * Sets the minimum size for this window. 1051 * A value of zero indicates no restriction. 1052 * If the native platform is unable to apply the constraints, 1053 * the values returned by getMinimumWidth()/Height() won't change. 1054 * 1055 * @throws IllegalArgumentException if width or height < 0 1056 */ 1057 public void setMinimumSize(int width, int height) { 1058 Application.checkEventThread(); 1059 if (width < 0 || height < 0) { 1060 throw new IllegalArgumentException("The width and height must be >= 0. Got: width=" + width + "; height=" + height); 1061 } 1062 checkNotClosed(); 1063 if (_setMinimumSize(this.ptr, width, height)) { 1064 this.minimumWidth = width; 1065 this.minimumHeight = height; 1066 } 1067 } 1068 1069 protected abstract boolean _setMaximumSize(long ptr, int width, int height); 1070 /** 1071 * Sets the maximum size for this window. 1072 * A value of {@code Integer.MAX_VALUE} indicates no restriction. 1073 * If the native platform is unable to apply the constraints, 1074 * the values returned by getMaximumWidth()/Height() won't change. 1075 * 1076 * @throws IllegalArgumentException if width or height < 0 1077 */ 1078 public void setMaximumSize(int width, int height) { 1079 Application.checkEventThread(); 1080 if (width < 0 || height < 0) { 1081 throw new IllegalArgumentException("The width and height must be >= 0. Got: width=" + width + "; height=" + height); 1082 } 1083 checkNotClosed(); 1084 if (_setMaximumSize(this.ptr, 1085 // for easier handling in native: 1086 width == Integer.MAX_VALUE ? -1 : width, 1087 height == Integer.MAX_VALUE ? -1 : height)) 1088 { 1089 this.maximumWidth = width; 1090 this.maximumHeight = height; 1091 } 1092 } 1093 1094 1095 protected abstract void _setIcon(long ptr, Pixels pixels); 1096 1097 // In the future we may want to pass a collection of Pixels, so that 1098 // the native platform could pick up the icon with the best dimensions 1099 public void setIcon(final Pixels pixels) { 1100 Application.checkEventThread(); 1101 checkNotClosed(); 1102 _setIcon(this.ptr, pixels); 1103 } 1104 1105 protected abstract void _setCursor(long ptr, Cursor cursor); 1106 1107 /** 1108 * Sets given cursor as the cursor for this window. 1109 * If the cursor is NONE, it is automatically hidden, 1110 * otherwise it is automatically shown. 1111 * @see Cursor#setVisible(boolean) 1112 */ 1113 public void setCursor(Cursor cursor) { 1114 Application.checkEventThread(); 1115 _setCursor(this.ptr, cursor); 1116 } 1117 1118 protected abstract void _toFront(long ptr); 1119 /** 1120 * Bring the window to front in the z-order. 1121 * This method DOES NOT activate the window. To make it active use 1122 * the requestFocus() method right after calling toFront(). 1123 */ 1124 public void toFront() { 1125 Application.checkEventThread(); 1126 checkNotClosed(); 1127 _toFront(ptr); 1128 } 1129 1130 protected abstract void _toBack(long ptr); 1131 /** 1132 * Send the window to the bottom of the stacking order. 1133 * This method may or may not de-focus this window 1134 * depending on the native platform. To make sure some other 1135 * window is activated, call requestFocus() on that other window. 1136 */ 1137 public void toBack() { 1138 Application.checkEventThread(); 1139 checkNotClosed(); 1140 _toBack(this.ptr); 1141 } 1142 1143 // ***************************************************** 1144 // modality (prototype using native platform feature) 1145 // ***************************************************** 1146 protected abstract void _enterModal(long ptr); 1147 /** 1148 * Enter modal state blocking everything except our window. 1149 */ 1150 public void enterModal() { 1151 checkNotClosed(); 1152 if (this.isModal == false) { 1153 this.isModal = true; 1154 _enterModal(this.ptr); 1155 } 1156 } 1157 1158 protected abstract void _enterModalWithWindow(long dialog, long window); 1159 /** 1160 * Enter modal state only blocking the given window. 1161 * On Mac OS X this is done using a dialog sheet. 1162 */ 1163 public void enterModal(final Window window) { 1164 checkNotClosed(); 1165 if (this.isModal == false) { 1166 this.isModal = true; 1167 _enterModalWithWindow(this.ptr, window.getNativeHandle()); 1168 } 1169 } 1170 1171 protected abstract void _exitModal(long ptr); 1172 public void exitModal() { 1173 checkNotClosed(); 1174 if (this.isModal == true) { 1175 _exitModal(this.ptr); 1176 this.isModal = false; 1177 } 1178 } 1179 1180 public boolean isModal() { 1181 return this.isModal; 1182 } 1183 1184 /** Only used on Mac when run inside a plugin */ 1185 public void dispatchNpapiEvent(Map eventInfo) { 1186 Application.checkEventThread(); 1187 throw new RuntimeException("This operation is not supported on this platform"); 1188 } 1189 1190 public EventHandler getEventHandler() { 1191 Application.checkEventThread(); 1192 return eventHandler; 1193 } 1194 1195 public void setEventHandler(EventHandler eventHandler) { 1196 Application.checkEventThread(); 1197 this.eventHandler = eventHandler; 1198 } 1199 1200 /** 1201 * Enables unconditional start of window move operation when 1202 * mouse is dragged in the client area. 1203 */ 1204 public void setShouldStartUndecoratedMove(boolean v) { 1205 Application.checkEventThread(); 1206 this.shouldStartUndecoratedMove = v; 1207 } 1208 1209 // ***************************************************** 1210 // notification callbacks 1211 // ***************************************************** 1212 protected void notifyClose() { 1213 handleWindowEvent(System.nanoTime(), WindowEvent.CLOSE); 1214 } 1215 1216 protected void notifyDestroy() { 1217 // Mac is known to send multiple WillClose notifications for some reason 1218 if (this.ptr == 0) { 1219 return; 1220 } 1221 1222 handleWindowEvent(System.nanoTime(), WindowEvent.DESTROY); 1223 1224 this.ptr = 0; 1225 1226 // Do this after setting ptr to 0 to avoid a call to _setVisible() 1227 setVisible(false); 1228 } 1229 1230 protected void notifyMove(final int x, final int y) { 1231 this.x = x; 1232 this.y = y; 1233 handleWindowEvent(System.nanoTime(), WindowEvent.MOVE); 1234 } 1235 1236 protected void notifyRescale() { 1237 handleWindowEvent(System.nanoTime(), WindowEvent.RESCALE); 1238 } 1239 1240 protected void notifyMoveToAnotherScreen(Screen newScreen) { 1241 setScreen(newScreen); 1242 } 1243 1244 protected void setState(int state) { 1245 this.state = state; 1246 } 1247 1248 /** 1249 * type values: 1250 * - WindowEvent.RESIZE 1251 * - WindowEvent.MINIMIZE 1252 * - WindowEvent.MAXIMIZE 1253 * - WindowEvent.RESTORE 1254 */ 1255 protected void notifyResize(final int type, final int width, final int height) { 1256 if (type == WindowEvent.MINIMIZE) { 1257 this.state = State.MINIMIZED; 1258 } else { 1259 if (type == WindowEvent.MAXIMIZE) { 1260 this.state = State.MAXIMIZED; 1261 } else { // WindowEvent.RESIZE or WindowEvent.RESTORE 1262 this.state = State.NORMAL; 1263 } 1264 this.width = width; 1265 this.height = height; 1266 1267 // update moveRect/resizeRect 1268 if (this.helper != null){ 1269 this.helper.updateRectangles(); 1270 } 1271 } 1272 handleWindowEvent(System.nanoTime(), type); 1273 1274 /* 1275 * Send RESIZE notification as MAXIMIZE and RESTORE change the window size 1276 */ 1277 if (type == WindowEvent.MAXIMIZE || type == WindowEvent.RESTORE) { 1278 handleWindowEvent(System.nanoTime(), WindowEvent.RESIZE); 1279 } 1280 } 1281 1282 protected void notifyFocus(int event) { 1283 final boolean focused = event != WindowEvent.FOCUS_LOST; 1284 1285 if (this.isFocused != focused) { 1286 this.isFocused = focused; 1287 if (this.isFocused) { 1288 setFocusedWindow(this); 1289 } else { 1290 setFocusedWindow(null); 1291 } 1292 handleWindowEvent(System.nanoTime(), event); 1293 } 1294 } 1295 1296 protected void notifyFocusDisabled() { 1297 handleWindowEvent(System.nanoTime(), WindowEvent.FOCUS_DISABLED); 1298 } 1299 1300 protected void notifyFocusUngrab() { 1301 handleWindowEvent(System.nanoTime(), WindowEvent.FOCUS_UNGRAB); 1302 } 1303 1304 protected void notifyDelegatePtr(long ptr) { 1305 this.delegatePtr = ptr; 1306 } 1307 1308 // ***************************************************** 1309 // window event handlers 1310 // ***************************************************** 1311 protected void handleWindowEvent(long time, int type) { 1312 if (this.eventHandler != null) { 1313 this.eventHandler.handleWindowEvent(this, time, type); 1314 } 1315 } 1316 1317 // ***************************************************** 1318 // programmatical move/resize 1319 // ***************************************************** 1320 /** Sets "programmatical move" rectangle. 1321 * The rectangle is measured from top of the View: 1322 * width is View.width, height is size. 1323 * 1324 * throws RuntimeException for decorated window. 1325 */ 1326 public void setUndecoratedMoveRectangle(int size) { 1327 Application.checkEventThread(); 1328 if (this.isDecorated == true) { 1329 //throw new RuntimeException("setUndecoratedMoveRectangle is only valid for Undecorated Window"); 1330 System.err.println("Glass Window.setUndecoratedMoveRectangle is only valid for Undecorated Window. In the future this will be hard error."); 1331 Thread.dumpStack(); 1332 return; 1333 } 1334 1335 if (this.helper != null) { 1336 this.helper.setMoveRectangle(size); 1337 } 1338 } 1339 /** The method called only for undecorated windows 1340 * x, y: mouse coordinates (in View space). 1341 * 1342 * throws RuntimeException for decorated window. 1343 */ 1344 public boolean shouldStartUndecoratedMove(final int x, final int y) { 1345 Application.checkEventThread(); 1346 if (this.shouldStartUndecoratedMove == true) { 1347 return true; 1348 } 1349 if (this.isDecorated == true) { 1350 return false; 1351 } 1352 1353 if (this.helper != null) { 1354 return this.helper.shouldStartMove(x, y); 1355 } else { 1356 return false; 1357 } 1358 } 1359 1360 /** Sets "programmatical resize" rectangle. 1361 * The rectangle is measured from top of the View: 1362 * width is View.width, height is size. 1363 * 1364 * throws RuntimeException for decorated window. 1365 */ 1366 public void setUndecoratedResizeRectangle(int size) { 1367 Application.checkEventThread(); 1368 if ((this.isDecorated == true) || (this.isResizable == false)) { 1369 //throw new RuntimeException("setUndecoratedMoveRectangle is only valid for Undecorated Resizable Window"); 1370 System.err.println("Glass Window.setUndecoratedResizeRectangle is only valid for Undecorated Resizable Window. In the future this will be hard error."); 1371 Thread.dumpStack(); 1372 return; 1373 } 1374 1375 if (this.helper != null) { 1376 this.helper.setResizeRectangle(size); 1377 } 1378 } 1379 1380 /** The method called only for undecorated windows 1381 * x, y: mouse coordinates (in View space). 1382 * 1383 * throws RuntimeException for decorated window. 1384 */ 1385 public boolean shouldStartUndecoratedResize(final int x, final int y) { 1386 Application.checkEventThread(); 1387 if ((this.isDecorated == true) || (this.isResizable == false)) { 1388 return false; 1389 } 1390 1391 if (this.helper != null) { 1392 return this.helper.shouldStartResize(x, y); 1393 } else { 1394 return false; 1395 } 1396 } 1397 1398 /** Mouse event handler for processing programmatical resize/move 1399 * (for undecorated windows only). 1400 * Must be called by View. 1401 * x & y are View coordinates. 1402 * NOTE: it's package private! 1403 * @return true if the event is processed by the window, 1404 * false if it has to be delivered to the app 1405 */ 1406 boolean handleMouseEvent(int type, int button, int x, int y, int xAbs, int yAbs) { 1407 if (this.isDecorated == false) { 1408 return this.helper.handleMouseEvent(type, button, x, y, xAbs, yAbs); 1409 } 1410 return false; 1411 } 1412 1413 @Override 1414 public String toString() { 1415 Application.checkEventThread(); 1416 return "Window:"+"\n" 1417 + " ptr: " + getNativeWindow() + "\n" 1418 + " screen ptr: " + (screen != null ? screen.getNativeScreen() : "null") + "\n" 1419 + " isDecorated: " + isDecorated() + "\n" 1420 + " title: " + getTitle() + "\n" 1421 + " visible: " + isVisible() + "\n" 1422 + " focused: " + isFocused() + "\n" 1423 + " modal: " + isModal() + "\n" 1424 + " state: " + state + "\n" 1425 + " x: " + getX() + ", y: " + getY() + ", w: " + getWidth() + ", h: " + getHeight() + "\n" 1426 + ""; 1427 } 1428 1429 // "programmical" move/resize support for undecorated windows 1430 1431 static private class TrackingRectangle { 1432 int size = 0; 1433 int x = 0, y = 0, width = 0, height = 0; 1434 boolean contains(final int x, final int y) { 1435 return ((size > 0) && 1436 (x >= this.x) && (x < (this.x + this.width)) && 1437 (y >= this.y) && (y < (this.y + this.height))); 1438 } 1439 } 1440 1441 protected void notifyLevelChanged(int level) { 1442 this.level = level; 1443 if (this.eventHandler != null) { 1444 this.eventHandler.handleLevelEvent(level); 1445 } 1446 } 1447 1448 private class UndecoratedMoveResizeHelper { 1449 TrackingRectangle moveRect = null; 1450 TrackingRectangle resizeRect = null; 1451 1452 boolean inMove = false; // we are in "move" mode 1453 boolean inResize = false; // we are in "resize" mode 1454 1455 int startMouseX, startMouseY; // start mouse coords 1456 int startX, startY; // start window location (for move) 1457 int startWidth, startHeight; // start window size (for resize) 1458 1459 UndecoratedMoveResizeHelper() { 1460 this.moveRect = new TrackingRectangle(); 1461 this.resizeRect = new TrackingRectangle(); 1462 } 1463 1464 void setMoveRectangle(final int size) { 1465 this.moveRect.size = size; 1466 1467 this.moveRect.x = 0; 1468 this.moveRect.y = 0; 1469 this.moveRect.width = getWidth(); 1470 this.moveRect.height = this.moveRect.size; 1471 } 1472 1473 boolean shouldStartMove(final int x, final int y) { 1474 return this.moveRect.contains(x, y); 1475 } 1476 1477 boolean inMove() { 1478 return this.inMove; 1479 } 1480 1481 void startMove(final int x, final int y) { 1482 this.inMove = true; 1483 1484 this.startMouseX = x; 1485 this.startMouseY = y; 1486 1487 this.startX = getX(); 1488 this.startY = getY(); 1489 } 1490 1491 void deltaMove(final int x, final int y) { 1492 int deltaX = x - this.startMouseX; 1493 int deltaY = y - this.startMouseY; 1494 1495 setPosition(this.startX + deltaX, this.startY + deltaY); 1496 } 1497 1498 void stopMove() { 1499 this.inMove = false; 1500 } 1501 1502 void setResizeRectangle(final int size) { 1503 this.resizeRect.size = size; 1504 1505 // set the rect (bottom right corner of the Window) 1506 this.resizeRect.x = getWidth() - this.resizeRect.size; 1507 this.resizeRect.y = getHeight() - this.resizeRect.size; 1508 this.resizeRect.width = this.resizeRect.size; 1509 this.resizeRect.height = this.resizeRect.size; 1510 } 1511 1512 boolean shouldStartResize(final int x, final int y) { 1513 return this.resizeRect.contains(x, y); 1514 } 1515 1516 boolean inResize() { 1517 return this.inResize; 1518 } 1519 1520 void startResize(final int x, final int y) { 1521 this.inResize = true; 1522 1523 this.startMouseX = x; 1524 this.startMouseY = y; 1525 1526 this.startWidth = getWidth(); 1527 this.startHeight = getHeight(); 1528 } 1529 1530 void deltaResize(final int x, final int y) { 1531 int deltaX = x - this.startMouseX; 1532 int deltaY = y - this.startMouseY; 1533 1534 setSize(this.startWidth + deltaX, this.startHeight + deltaY); 1535 } 1536 1537 protected void stopResize() { 1538 this.inResize = false; 1539 } 1540 1541 void updateRectangles() { 1542 if (this.moveRect.size > 0) { 1543 setMoveRectangle(this.moveRect.size); 1544 } 1545 if (this.resizeRect.size > 0) { 1546 setResizeRectangle(this.resizeRect.size); 1547 } 1548 } 1549 1550 boolean handleMouseEvent(final int type, final int button, final int x, final int y, final int xAbs, final int yAbs) { 1551 switch (type) { 1552 case MouseEvent.DOWN: 1553 if (button == MouseEvent.BUTTON_LEFT) { 1554 if (shouldStartUndecoratedMove(x, y) == true) { 1555 startMove(xAbs, yAbs); 1556 return true; 1557 } else if (shouldStartUndecoratedResize(x, y) == true) { 1558 startResize(xAbs, yAbs); 1559 return true; 1560 } 1561 } 1562 break; 1563 1564 case MouseEvent.MOVE: 1565 case MouseEvent.DRAG: 1566 if (inMove() == true) { 1567 deltaMove(xAbs, yAbs); 1568 return true; 1569 } else if (inResize() == true) { 1570 deltaResize(xAbs, yAbs); 1571 return true; 1572 } 1573 break; 1574 1575 case MouseEvent.UP: 1576 boolean wasProcessed = inMove() || inResize(); 1577 stopResize(); 1578 stopMove(); 1579 return wasProcessed; 1580 } 1581 return false; 1582 } 1583 } 1584 1585 /** 1586 * Requests text input in form of native keyboard for text component 1587 * contained by this Window. Native text input component is drawn on the place 1588 * of JavaFX component to cover it completely and to provide native text editing 1589 * techniques. Any change of text is immediately reflected in JavaFX text component. 1590 * 1591 * @param text text to be shown in the native text input component 1592 * @param type type of text input component @see com.sun.javafx.scene.control.behavior.TextInputTypes 1593 * @param width width of JavaFX text input component 1594 * @param height height of JavaFX text input component 1595 * @param M standard transformation matrix for drawing the native text component derived from JavaFX component 1596 */ 1597 public void requestInput(String text, int type, double width, double height, 1598 double Mxx, double Mxy, double Mxz, double Mxt, 1599 double Myx, double Myy, double Myz, double Myt, 1600 double Mzx, double Mzy, double Mzz, double Mzt) { 1601 Application.checkEventThread(); 1602 _requestInput(this.ptr, text, type, width, height, 1603 Mxx, Mxy, Mxz, Mxt, 1604 Myx, Myy, Myz, Myt, 1605 Mzx, Mzy, Mzz, Mzt); 1606 } 1607 1608 /** 1609 * Native keyboard for text input is no longer necessary. 1610 * Keyboard will be hidden and native text input component too. 1611 */ 1612 public void releaseInput() { 1613 Application.checkEventThread(); 1614 _releaseInput(this.ptr); 1615 } 1616 1617 protected abstract void _requestInput(long ptr, String text, int type, double width, double height, 1618 double Mxx, double Mxy, double Mxz, double Mxt, 1619 double Myx, double Myy, double Myz, double Myt, 1620 double Mzx, double Mzy, double Mzz, double Mzt); 1621 1622 protected abstract void _releaseInput(long ptr); 1623 1624 }