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