1 /* 2 * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.lwawt.macosx; 27 28 import java.awt.Color; 29 import java.awt.Component; 30 import java.awt.DefaultKeyboardFocusManager; 31 import java.awt.Dialog; 32 import java.awt.Dialog.ModalityType; 33 import java.awt.Font; 34 import java.awt.FontMetrics; 35 import java.awt.Frame; 36 import java.awt.GraphicsDevice; 37 import java.awt.Insets; 38 import java.awt.MenuBar; 39 import java.awt.Point; 40 import java.awt.Rectangle; 41 import java.awt.Toolkit; 42 import java.awt.Window; 43 import java.awt.event.FocusEvent; 44 import java.awt.event.WindowEvent; 45 import java.beans.PropertyChangeEvent; 46 import java.beans.PropertyChangeListener; 47 import java.lang.reflect.InvocationTargetException; 48 import java.util.ArrayList; 49 import java.util.Arrays; 50 import java.util.Comparator; 51 import java.util.concurrent.atomic.AtomicBoolean; 52 import java.util.concurrent.atomic.AtomicLong; 53 import java.util.concurrent.atomic.AtomicReference; 54 55 import javax.swing.JRootPane; 56 import javax.swing.RootPaneContainer; 57 import javax.swing.SwingUtilities; 58 59 import com.apple.laf.ClientPropertyApplicator; 60 import com.apple.laf.ClientPropertyApplicator.Property; 61 import sun.awt.AWTAccessor; 62 import sun.awt.AWTAccessor.ComponentAccessor; 63 import sun.awt.AWTAccessor.WindowAccessor; 64 import sun.java2d.SurfaceData; 65 import sun.java2d.opengl.CGLSurfaceData; 66 import sun.lwawt.LWLightweightFramePeer; 67 import sun.lwawt.LWToolkit; 68 import sun.lwawt.LWWindowPeer; 69 import sun.lwawt.LWWindowPeer.PeerType; 70 import sun.lwawt.PlatformWindow; 71 import sun.util.logging.PlatformLogger; 72 73 public class CPlatformWindow extends CFRetainedResource implements PlatformWindow { 74 private native long nativeCreateNSWindow(long nsViewPtr,long ownerPtr, long styleBits, double x, double y, double w, double h); 75 private static native void nativeSetNSWindowStyleBits(long nsWindowPtr, int mask, int data); 76 private static native void nativeSetNSWindowMenuBar(long nsWindowPtr, long menuBarPtr); 77 private static native Insets nativeGetNSWindowInsets(long nsWindowPtr); 78 private static native void nativeSetNSWindowBounds(long nsWindowPtr, double x, double y, double w, double h); 79 private static native void nativeSetNSWindowLocationByPlatform(long nsWindowPtr); 80 private static native void nativeSetNSWindowStandardFrame(long nsWindowPtr, 81 double x, double y, double w, double h); 82 private static native void nativeSetNSWindowMinMax(long nsWindowPtr, double minW, double minH, double maxW, double maxH); 83 private static native void nativePushNSWindowToBack(long nsWindowPtr); 84 private static native void nativePushNSWindowToFront(long nsWindowPtr); 85 private static native void nativeSetNSWindowTitle(long nsWindowPtr, String title); 86 private static native void nativeRevalidateNSWindowShadow(long nsWindowPtr); 87 private static native void nativeSetNSWindowMinimizedIcon(long nsWindowPtr, long nsImage); 88 private static native void nativeSetNSWindowRepresentedFilename(long nsWindowPtr, String representedFilename); 89 private static native void nativeSetEnabled(long nsWindowPtr, boolean isEnabled); 90 private static native void nativeSynthesizeMouseEnteredExitedEvents(); 91 private static native void nativeSynthesizeMouseEnteredExitedEvents(long nsWindowPtr, int eventType); 92 private static native void nativeDispose(long nsWindowPtr); 93 private static native void nativeEnterFullScreenMode(long nsWindowPtr); 94 private static native void nativeExitFullScreenMode(long nsWindowPtr); 95 static native CPlatformWindow nativeGetTopmostPlatformWindowUnderMouse(); 96 97 // Loger to report issues happened during execution but that do not affect functionality 98 private static final PlatformLogger logger = PlatformLogger.getLogger("sun.lwawt.macosx.CPlatformWindow"); 99 private static final PlatformLogger focusLogger = PlatformLogger.getLogger("sun.lwawt.macosx.focus.CPlatformWindow"); 100 101 // for client properties 102 public static final String WINDOW_BRUSH_METAL_LOOK = "apple.awt.brushMetalLook"; 103 public static final String WINDOW_DRAGGABLE_BACKGROUND = "apple.awt.draggableWindowBackground"; 104 105 public static final String WINDOW_ALPHA = "Window.alpha"; 106 public static final String WINDOW_SHADOW = "Window.shadow"; 107 108 public static final String WINDOW_STYLE = "Window.style"; 109 public static final String WINDOW_SHADOW_REVALIDATE_NOW = "apple.awt.windowShadow.revalidateNow"; 110 111 public static final String WINDOW_DOCUMENT_MODIFIED = "Window.documentModified"; 112 public static final String WINDOW_DOCUMENT_FILE = "Window.documentFile"; 113 114 public static final String WINDOW_CLOSEABLE = "Window.closeable"; 115 public static final String WINDOW_MINIMIZABLE = "Window.minimizable"; 116 public static final String WINDOW_ZOOMABLE = "Window.zoomable"; 117 public static final String WINDOW_HIDES_ON_DEACTIVATE="Window.hidesOnDeactivate"; 118 119 public static final String WINDOW_DOC_MODAL_SHEET = "apple.awt.documentModalSheet"; 120 public static final String WINDOW_FADE_DELEGATE = "apple.awt._windowFadeDelegate"; 121 public static final String WINDOW_FADE_IN = "apple.awt._windowFadeIn"; 122 public static final String WINDOW_FADE_OUT = "apple.awt._windowFadeOut"; 123 public static final String WINDOW_FULLSCREENABLE = "apple.awt.fullscreenable"; 124 public static final String WINDOW_FULL_CONTENT = "apple.awt.fullWindowContent"; 125 public static final String WINDOW_TRANSPARENT_TITLE_BAR = "apple.awt.transparentTitleBar"; 126 127 // Yeah, I know. But it's easier to deal with ints from JNI 128 static final int MODELESS = 0; 129 static final int DOCUMENT_MODAL = 1; 130 static final int APPLICATION_MODAL = 2; 131 static final int TOOLKIT_MODAL = 3; 132 133 // window style bits 134 static final int _RESERVED_FOR_DATA = 1 << 0; 135 136 // corresponds to native style mask bits 137 static final int DECORATED = 1 << 1; 138 static final int TEXTURED = 1 << 2; 139 static final int UNIFIED = 1 << 3; 140 static final int UTILITY = 1 << 4; 141 static final int HUD = 1 << 5; 142 static final int SHEET = 1 << 6; 143 144 static final int CLOSEABLE = 1 << 7; 145 static final int MINIMIZABLE = 1 << 8; 146 147 static final int RESIZABLE = 1 << 9; // both a style bit and prop bit 148 static final int NONACTIVATING = 1 << 24; 149 static final int IS_DIALOG = 1 << 25; 150 static final int IS_MODAL = 1 << 26; 151 static final int IS_POPUP = 1 << 27; 152 153 static final int FULL_WINDOW_CONTENT = 1 << 14; 154 155 static final int _STYLE_PROP_BITMASK = DECORATED | TEXTURED | UNIFIED | UTILITY | HUD | SHEET | CLOSEABLE 156 | MINIMIZABLE | RESIZABLE | FULL_WINDOW_CONTENT; 157 158 // corresponds to method-based properties 159 static final int HAS_SHADOW = 1 << 10; 160 static final int ZOOMABLE = 1 << 11; 161 162 static final int ALWAYS_ON_TOP = 1 << 15; 163 static final int HIDES_ON_DEACTIVATE = 1 << 17; 164 static final int DRAGGABLE_BACKGROUND = 1 << 19; 165 static final int DOCUMENT_MODIFIED = 1 << 21; 166 static final int FULLSCREENABLE = 1 << 23; 167 static final int TRANSPARENT_TITLE_BAR = 1 << 18; 168 169 static final int _METHOD_PROP_BITMASK = RESIZABLE | HAS_SHADOW | ZOOMABLE | ALWAYS_ON_TOP | HIDES_ON_DEACTIVATE 170 | DRAGGABLE_BACKGROUND | DOCUMENT_MODIFIED | FULLSCREENABLE 171 | TRANSPARENT_TITLE_BAR; 172 173 // corresponds to callback-based properties 174 static final int SHOULD_BECOME_KEY = 1 << 12; 175 static final int SHOULD_BECOME_MAIN = 1 << 13; 176 static final int MODAL_EXCLUDED = 1 << 16; 177 178 static final int _CALLBACK_PROP_BITMASK = SHOULD_BECOME_KEY | SHOULD_BECOME_MAIN | MODAL_EXCLUDED; 179 180 static int SET(final int bits, final int mask, final boolean value) { 181 if (value) return (bits | mask); 182 return bits & ~mask; 183 } 184 185 static boolean IS(final int bits, final int mask) { 186 return (bits & mask) != 0; 187 } 188 189 @SuppressWarnings({"unchecked", "rawtypes"}) 190 static ClientPropertyApplicator<JRootPane, CPlatformWindow> CLIENT_PROPERTY_APPLICATOR = new ClientPropertyApplicator<JRootPane, CPlatformWindow>(new Property[] { 191 new Property<CPlatformWindow>(WINDOW_DOCUMENT_MODIFIED) { public void applyProperty(final CPlatformWindow c, final Object value) { 192 c.setStyleBits(DOCUMENT_MODIFIED, value == null ? false : Boolean.parseBoolean(value.toString())); 193 }}, 194 new Property<CPlatformWindow>(WINDOW_BRUSH_METAL_LOOK) { public void applyProperty(final CPlatformWindow c, final Object value) { 195 c.setStyleBits(TEXTURED, Boolean.parseBoolean(value.toString())); 196 }}, 197 new Property<CPlatformWindow>(WINDOW_ALPHA) { public void applyProperty(final CPlatformWindow c, final Object value) { 198 c.target.setOpacity(value == null ? 1.0f : Float.parseFloat(value.toString())); 199 }}, 200 new Property<CPlatformWindow>(WINDOW_SHADOW) { public void applyProperty(final CPlatformWindow c, final Object value) { 201 c.setStyleBits(HAS_SHADOW, value == null ? true : Boolean.parseBoolean(value.toString())); 202 }}, 203 new Property<CPlatformWindow>(WINDOW_MINIMIZABLE) { public void applyProperty(final CPlatformWindow c, final Object value) { 204 c.setStyleBits(MINIMIZABLE, Boolean.parseBoolean(value.toString())); 205 }}, 206 new Property<CPlatformWindow>(WINDOW_CLOSEABLE) { public void applyProperty(final CPlatformWindow c, final Object value) { 207 c.setStyleBits(CLOSEABLE, Boolean.parseBoolean(value.toString())); 208 }}, 209 new Property<CPlatformWindow>(WINDOW_ZOOMABLE) { public void applyProperty(final CPlatformWindow c, final Object value) { 210 boolean zoomable = Boolean.parseBoolean(value.toString()); 211 if (c.target instanceof RootPaneContainer 212 && c.getPeer().getPeerType() == PeerType.FRAME) { 213 if (c.isInFullScreen && !zoomable) { 214 c.toggleFullScreen(); 215 } 216 } 217 c.setStyleBits(ZOOMABLE, zoomable); 218 }}, 219 new Property<CPlatformWindow>(WINDOW_FULLSCREENABLE) { public void applyProperty(final CPlatformWindow c, final Object value) { 220 boolean fullscrenable = Boolean.parseBoolean(value.toString()); 221 if (c.target instanceof RootPaneContainer 222 && c.getPeer().getPeerType() == PeerType.FRAME) { 223 if (c.isInFullScreen && !fullscrenable) { 224 c.toggleFullScreen(); 225 } 226 } 227 c.setStyleBits(FULLSCREENABLE, fullscrenable); 228 }}, 229 new Property<CPlatformWindow>(WINDOW_SHADOW_REVALIDATE_NOW) { public void applyProperty(final CPlatformWindow c, final Object value) { 230 c.execute(ptr -> nativeRevalidateNSWindowShadow(ptr)); 231 }}, 232 new Property<CPlatformWindow>(WINDOW_DOCUMENT_FILE) { public void applyProperty(final CPlatformWindow c, final Object value) { 233 if (value == null || !(value instanceof java.io.File)) { 234 c.execute(ptr->nativeSetNSWindowRepresentedFilename(ptr, null)); 235 return; 236 } 237 238 final String filename = ((java.io.File)value).getAbsolutePath(); 239 c.execute(ptr->nativeSetNSWindowRepresentedFilename(ptr, filename)); 240 }}, 241 new Property<CPlatformWindow>(WINDOW_FULL_CONTENT) { 242 public void applyProperty(final CPlatformWindow c, final Object value) { 243 boolean isFullWindowContent = Boolean.parseBoolean(value.toString()); 244 c.setStyleBits(FULL_WINDOW_CONTENT, isFullWindowContent); 245 } 246 }, 247 new Property<CPlatformWindow>(WINDOW_TRANSPARENT_TITLE_BAR) { 248 public void applyProperty(final CPlatformWindow c, final Object value) { 249 boolean isTransparentTitleBar = Boolean.parseBoolean(value.toString()); 250 c.setStyleBits(TRANSPARENT_TITLE_BAR, isTransparentTitleBar); 251 } 252 } 253 }) { 254 @SuppressWarnings("deprecation") 255 public CPlatformWindow convertJComponentToTarget(final JRootPane p) { 256 Component root = SwingUtilities.getRoot(p); 257 final ComponentAccessor acc = AWTAccessor.getComponentAccessor(); 258 if (root == null || acc.getPeer(root) == null) return null; 259 return (CPlatformWindow)((LWWindowPeer)acc.getPeer(root)).getPlatformWindow(); 260 } 261 }; 262 private final Comparator<Window> siblingsComparator = (w1, w2) -> { 263 if (w1 == w2) { 264 return 0; 265 } 266 ComponentAccessor componentAccessor = AWTAccessor.getComponentAccessor(); 267 Object p1 = componentAccessor.getPeer(w1); 268 Object p2 = componentAccessor.getPeer(w2); 269 long time1 = 0; 270 if (p1 instanceof LWWindowPeer) { 271 time1 = ((CPlatformWindow) (((LWWindowPeer) p1).getPlatformWindow())).lastBecomeMainTime; 272 } 273 long time2 = 0; 274 if (p2 instanceof LWWindowPeer) { 275 time2 = ((CPlatformWindow) (((LWWindowPeer) p2).getPlatformWindow())).lastBecomeMainTime; 276 } 277 return Long.compare(time1, time2); 278 }; 279 280 // Bounds of the native widget but in the Java coordinate system. 281 // In order to keep it up-to-date we will update them on 282 // 1) setting native bounds via nativeSetBounds() call 283 // 2) getting notification from the native level via deliverMoveResizeEvent() 284 private Rectangle nativeBounds = new Rectangle(0, 0, 0, 0); 285 private volatile boolean isFullScreenMode; 286 private boolean isFullScreenAnimationOn; 287 288 private volatile boolean isInFullScreen; 289 private volatile boolean isIconifyAnimationActive; 290 private volatile boolean isZoomed; 291 292 private Window target; 293 private LWWindowPeer peer; 294 protected CPlatformView contentView; 295 protected CPlatformWindow owner; 296 protected boolean visible = false; // visibility status from native perspective 297 private boolean undecorated; // initialized in getInitialStyleBits() 298 private Rectangle normalBounds = null; // not-null only for undecorated maximized windows 299 private CPlatformResponder responder; 300 private long lastBecomeMainTime; // this is necessary to preserve right siblings order 301 302 public CPlatformWindow() { 303 super(0, true); 304 } 305 306 /* 307 * Delegate initialization (create native window and all the 308 * related resources). 309 */ 310 @Override // PlatformWindow 311 public void initialize(Window _target, LWWindowPeer _peer, PlatformWindow _owner) { 312 initializeBase(_target, _peer, _owner, new CPlatformView()); 313 314 final int styleBits = getInitialStyleBits(); 315 316 responder = createPlatformResponder(); 317 contentView = createContentView(); 318 contentView.initialize(peer, responder); 319 320 Rectangle bounds; 321 if (!IS(DECORATED, styleBits)) { 322 // For undecorated frames the move/resize event does not come if the frame is centered on the screen 323 // so we need to set a stub location to force an initial move/resize. Real bounds would be set later. 324 bounds = new Rectangle(0, 0, 1, 1); 325 } else { 326 bounds = _peer.constrainBounds(_target.getBounds()); 327 } 328 AtomicLong ref = new AtomicLong(); 329 contentView.execute(viewPtr -> { 330 boolean hasOwnerPtr = false; 331 332 if (owner != null) { 333 hasOwnerPtr = 0L != owner.executeGet(ownerPtr -> { 334 ref.set(nativeCreateNSWindow(viewPtr, ownerPtr, styleBits, 335 bounds.x, bounds.y, 336 bounds.width, bounds.height)); 337 return 1; 338 }); 339 } 340 341 if (!hasOwnerPtr) { 342 ref.set(nativeCreateNSWindow(viewPtr, 0, 343 styleBits, bounds.x, bounds.y, 344 bounds.width, bounds.height)); 345 } 346 }); 347 setPtr(ref.get()); 348 349 if (target instanceof javax.swing.RootPaneContainer) { 350 final javax.swing.JRootPane rootpane = ((javax.swing.RootPaneContainer)target).getRootPane(); 351 if (rootpane != null) rootpane.addPropertyChangeListener("ancestor", new PropertyChangeListener() { 352 public void propertyChange(final PropertyChangeEvent evt) { 353 CLIENT_PROPERTY_APPLICATOR.attachAndApplyClientProperties(rootpane); 354 rootpane.removePropertyChangeListener("ancestor", this); 355 } 356 }); 357 } 358 359 validateSurface(); 360 } 361 362 protected void initializeBase(Window target, LWWindowPeer peer, PlatformWindow owner, CPlatformView view) { 363 this.peer = peer; 364 this.target = target; 365 if (owner instanceof CPlatformWindow) { 366 this.owner = (CPlatformWindow)owner; 367 } 368 this.contentView = view; 369 } 370 371 protected CPlatformResponder createPlatformResponder() { 372 return new CPlatformResponder(peer, false); 373 } 374 375 protected CPlatformView createContentView() { 376 return new CPlatformView(); 377 } 378 379 protected int getInitialStyleBits() { 380 // defaults style bits 381 int styleBits = DECORATED | HAS_SHADOW | CLOSEABLE | MINIMIZABLE | ZOOMABLE | RESIZABLE; 382 383 if (isNativelyFocusableWindow()) { 384 styleBits = SET(styleBits, SHOULD_BECOME_KEY, true); 385 styleBits = SET(styleBits, SHOULD_BECOME_MAIN, true); 386 } 387 388 final boolean isFrame = (target instanceof Frame); 389 final boolean isDialog = (target instanceof Dialog); 390 final boolean isPopup = (target.getType() == Window.Type.POPUP); 391 if (isDialog) { 392 styleBits = SET(styleBits, MINIMIZABLE, false); 393 } 394 395 // Either java.awt.Frame or java.awt.Dialog can be undecorated, however java.awt.Window always is undecorated. 396 { 397 this.undecorated = isFrame ? ((Frame)target).isUndecorated() : (isDialog ? ((Dialog)target).isUndecorated() : true); 398 if (this.undecorated) styleBits = SET(styleBits, DECORATED, false); 399 } 400 401 // Either java.awt.Frame or java.awt.Dialog can be resizable, however java.awt.Window is never resizable 402 { 403 final boolean resizable = isFrame ? ((Frame)target).isResizable() : (isDialog ? ((Dialog)target).isResizable() : false); 404 styleBits = SET(styleBits, RESIZABLE, resizable); 405 if (!resizable) { 406 styleBits = SET(styleBits, ZOOMABLE, false); 407 } 408 } 409 410 if (target.isAlwaysOnTop()) { 411 styleBits = SET(styleBits, ALWAYS_ON_TOP, true); 412 } 413 414 if (target.getModalExclusionType() == Dialog.ModalExclusionType.APPLICATION_EXCLUDE) { 415 styleBits = SET(styleBits, MODAL_EXCLUDED, true); 416 } 417 418 // If the target is a dialog, popup or tooltip we want it to ignore the brushed metal look. 419 if (isPopup) { 420 styleBits = SET(styleBits, TEXTURED, false); 421 // Popups in applets don't activate applet's process 422 styleBits = SET(styleBits, NONACTIVATING, true); 423 styleBits = SET(styleBits, IS_POPUP, true); 424 } 425 426 if (Window.Type.UTILITY.equals(target.getType())) { 427 styleBits = SET(styleBits, UTILITY, true); 428 } 429 430 if (target instanceof javax.swing.RootPaneContainer) { 431 javax.swing.JRootPane rootpane = ((javax.swing.RootPaneContainer)target).getRootPane(); 432 Object prop = null; 433 434 prop = rootpane.getClientProperty(WINDOW_BRUSH_METAL_LOOK); 435 if (prop != null) { 436 styleBits = SET(styleBits, TEXTURED, Boolean.parseBoolean(prop.toString())); 437 } 438 439 if (isDialog && ((Dialog)target).getModalityType() == ModalityType.DOCUMENT_MODAL) { 440 prop = rootpane.getClientProperty(WINDOW_DOC_MODAL_SHEET); 441 if (prop != null) { 442 styleBits = SET(styleBits, SHEET, Boolean.parseBoolean(prop.toString())); 443 } 444 } 445 446 prop = rootpane.getClientProperty(WINDOW_STYLE); 447 if (prop != null) { 448 if ("small".equals(prop)) { 449 styleBits = SET(styleBits, UTILITY, true); 450 if (target.isAlwaysOnTop() && rootpane.getClientProperty(WINDOW_HIDES_ON_DEACTIVATE) == null) { 451 styleBits = SET(styleBits, HIDES_ON_DEACTIVATE, true); 452 } 453 } 454 if ("textured".equals(prop)) styleBits = SET(styleBits, TEXTURED, true); 455 if ("unified".equals(prop)) styleBits = SET(styleBits, UNIFIED, true); 456 if ("hud".equals(prop)) styleBits = SET(styleBits, HUD, true); 457 } 458 459 prop = rootpane.getClientProperty(WINDOW_HIDES_ON_DEACTIVATE); 460 if (prop != null) { 461 styleBits = SET(styleBits, HIDES_ON_DEACTIVATE, Boolean.parseBoolean(prop.toString())); 462 } 463 464 prop = rootpane.getClientProperty(WINDOW_CLOSEABLE); 465 if (prop != null) { 466 styleBits = SET(styleBits, CLOSEABLE, Boolean.parseBoolean(prop.toString())); 467 } 468 469 prop = rootpane.getClientProperty(WINDOW_MINIMIZABLE); 470 if (prop != null) { 471 styleBits = SET(styleBits, MINIMIZABLE, Boolean.parseBoolean(prop.toString())); 472 } 473 474 prop = rootpane.getClientProperty(WINDOW_ZOOMABLE); 475 if (prop != null) { 476 styleBits = SET(styleBits, ZOOMABLE, Boolean.parseBoolean(prop.toString())); 477 } 478 479 prop = rootpane.getClientProperty(WINDOW_FULLSCREENABLE); 480 if (prop != null) { 481 styleBits = SET(styleBits, FULLSCREENABLE, Boolean.parseBoolean(prop.toString())); 482 } 483 484 prop = rootpane.getClientProperty(WINDOW_SHADOW); 485 if (prop != null) { 486 styleBits = SET(styleBits, HAS_SHADOW, Boolean.parseBoolean(prop.toString())); 487 } 488 489 prop = rootpane.getClientProperty(WINDOW_DRAGGABLE_BACKGROUND); 490 if (prop != null) { 491 styleBits = SET(styleBits, DRAGGABLE_BACKGROUND, Boolean.parseBoolean(prop.toString())); 492 } 493 494 prop = rootpane.getClientProperty(WINDOW_FULL_CONTENT); 495 if (prop != null) { 496 styleBits = SET(styleBits, FULL_WINDOW_CONTENT, Boolean.parseBoolean(prop.toString())); 497 } 498 499 prop = rootpane.getClientProperty(WINDOW_TRANSPARENT_TITLE_BAR); 500 if (prop != null) { 501 styleBits = SET(styleBits, TRANSPARENT_TITLE_BAR, Boolean.parseBoolean(prop.toString())); 502 } 503 } 504 505 if (isDialog) { 506 styleBits = SET(styleBits, IS_DIALOG, true); 507 if (((Dialog) target).isModal()) { 508 styleBits = SET(styleBits, IS_MODAL, true); 509 } 510 } 511 512 peer.setTextured(IS(TEXTURED, styleBits)); 513 514 return styleBits; 515 } 516 517 // this is the counter-point to -[CWindow _nativeSetStyleBit:] 518 private void setStyleBits(final int mask, final boolean value) { 519 execute(ptr -> nativeSetNSWindowStyleBits(ptr, mask, value ? mask : 0)); 520 } 521 522 private native void _toggleFullScreenMode(final long model); 523 524 public void toggleFullScreen() { 525 execute(this::_toggleFullScreenMode); 526 } 527 528 @Override // PlatformWindow 529 public void setMenuBar(MenuBar mb) { 530 CMenuBar mbPeer = (CMenuBar)LWToolkit.targetToPeer(mb); 531 execute(nsWindowPtr->{ 532 if (mbPeer != null) { 533 mbPeer.execute(ptr -> nativeSetNSWindowMenuBar(nsWindowPtr, ptr)); 534 } else { 535 nativeSetNSWindowMenuBar(nsWindowPtr, 0); 536 } 537 }); 538 } 539 540 @Override // PlatformWindow 541 public void dispose() { 542 contentView.dispose(); 543 execute(CPlatformWindow::nativeDispose); 544 CPlatformWindow.super.dispose(); 545 } 546 547 @Override // PlatformWindow 548 public FontMetrics getFontMetrics(Font f) { 549 // TODO: not implemented 550 (new RuntimeException("unimplemented")).printStackTrace(); 551 return null; 552 } 553 554 @Override // PlatformWindow 555 public Insets getInsets() { 556 AtomicReference<Insets> ref = new AtomicReference<>(); 557 execute(ptr -> { 558 ref.set(nativeGetNSWindowInsets(ptr)); 559 }); 560 return ref.get() != null ? ref.get() : new Insets(0, 0, 0, 0); 561 } 562 563 @Override // PlatformWindow 564 public Point getLocationOnScreen() { 565 return new Point(nativeBounds.x, nativeBounds.y); 566 } 567 568 @Override 569 public GraphicsDevice getGraphicsDevice() { 570 return contentView.getGraphicsDevice(); 571 } 572 573 @Override // PlatformWindow 574 public SurfaceData getScreenSurface() { 575 // TODO: not implemented 576 return null; 577 } 578 579 @Override // PlatformWindow 580 public SurfaceData replaceSurfaceData() { 581 return contentView.replaceSurfaceData(); 582 } 583 584 @Override // PlatformWindow 585 public void setBounds(int x, int y, int w, int h) { 586 execute(ptr -> nativeSetNSWindowBounds(ptr, x, y, w, h)); 587 } 588 589 public void setMaximizedBounds(int x, int y, int w, int h) { 590 execute(ptr -> nativeSetNSWindowStandardFrame(ptr, x, y, w, h)); 591 } 592 593 private boolean isMaximized() { 594 return undecorated ? this.normalBounds != null 595 : isZoomed; 596 } 597 598 private void maximize() { 599 if (peer == null || isMaximized()) { 600 return; 601 } 602 if (!undecorated) { 603 execute(CWrapper.NSWindow::zoom); 604 } else { 605 deliverZoom(true); 606 607 // We need an up to date size of the peer, so we flush the native events 608 // to be sure that there are no setBounds requests in the queue. 609 LWCToolkit.flushNativeSelectors(); 610 this.normalBounds = peer.getBounds(); 611 Rectangle maximizedBounds = peer.getMaximizedBounds(); 612 setBounds(maximizedBounds.x, maximizedBounds.y, 613 maximizedBounds.width, maximizedBounds.height); 614 } 615 } 616 617 private void unmaximize() { 618 if (!isMaximized()) { 619 return; 620 } 621 if (!undecorated) { 622 execute(CWrapper.NSWindow::zoom); 623 } else { 624 deliverZoom(false); 625 626 Rectangle toBounds = this.normalBounds; 627 this.normalBounds = null; 628 setBounds(toBounds.x, toBounds.y, toBounds.width, toBounds.height); 629 } 630 } 631 632 public boolean isVisible() { 633 return this.visible; 634 } 635 636 @Override // PlatformWindow 637 public void setVisible(boolean visible) { 638 // Configure stuff 639 updateIconImages(); 640 updateFocusabilityForAutoRequestFocus(false); 641 642 boolean wasMaximized = isMaximized(); 643 644 if (visible && target.isLocationByPlatform()) { 645 execute(CPlatformWindow::nativeSetNSWindowLocationByPlatform); 646 } 647 648 // Actually show or hide the window 649 LWWindowPeer blocker = (peer == null)? null : peer.getBlocker(); 650 if (blocker == null || !visible) { 651 // If it ain't blocked, or is being hidden, go regular way 652 if (visible) { 653 contentView.execute(viewPtr -> { 654 execute(ptr -> CWrapper.NSWindow.makeFirstResponder(ptr, 655 viewPtr)); 656 }); 657 658 boolean isPopup = (target.getType() == Window.Type.POPUP); 659 execute(ptr -> { 660 if (isPopup) { 661 // Popups in applets don't activate applet's process 662 CWrapper.NSWindow.orderFrontRegardless(ptr); 663 } else { 664 CWrapper.NSWindow.orderFront(ptr); 665 } 666 667 boolean isKeyWindow = CWrapper.NSWindow.isKeyWindow(ptr); 668 if (!isKeyWindow) { 669 CWrapper.NSWindow.makeKeyWindow(ptr); 670 } 671 672 if (owner != null 673 && owner.getPeer() instanceof LWLightweightFramePeer) { 674 LWLightweightFramePeer peer = 675 (LWLightweightFramePeer) owner.getPeer(); 676 677 long ownerWindowPtr = peer.getOverriddenWindowHandle(); 678 if (ownerWindowPtr != 0) { 679 //Place window above JavaFX stage 680 CWrapper.NSWindow.addChildWindow( 681 ownerWindowPtr, ptr, 682 CWrapper.NSWindow.NSWindowAbove); 683 } 684 } 685 }); 686 } else { 687 execute(ptr->{ 688 // immediately hide the window 689 CWrapper.NSWindow.orderOut(ptr); 690 // process the close 691 CWrapper.NSWindow.close(ptr); 692 }); 693 } 694 } else { 695 // otherwise, put it in a proper z-order 696 CPlatformWindow bw 697 = (CPlatformWindow) blocker.getPlatformWindow(); 698 bw.execute(blockerPtr -> { 699 execute(ptr -> { 700 CWrapper.NSWindow.orderWindow(ptr, 701 CWrapper.NSWindow.NSWindowBelow, 702 blockerPtr); 703 }); 704 }); 705 } 706 this.visible = visible; 707 708 // Manage the extended state when showing 709 if (visible) { 710 /* Frame or Dialog should be set property WINDOW_FULLSCREENABLE to true if the 711 Frame or Dialog is resizable. 712 **/ 713 final boolean resizable = (target instanceof Frame) ? ((Frame)target).isResizable() : 714 ((target instanceof Dialog) ? ((Dialog)target).isResizable() : false); 715 if (resizable) { 716 setCanFullscreen(true); 717 } 718 719 // Apply the extended state as expected in shared code 720 if (target instanceof Frame) { 721 if (!wasMaximized && isMaximized()) { 722 // setVisible could have changed the native maximized state 723 deliverZoom(true); 724 } else { 725 int frameState = ((Frame)target).getExtendedState(); 726 if ((frameState & Frame.ICONIFIED) != 0) { 727 // Treat all state bit masks with ICONIFIED bit as ICONIFIED state. 728 frameState = Frame.ICONIFIED; 729 } 730 731 switch (frameState) { 732 case Frame.ICONIFIED: 733 execute(CWrapper.NSWindow::miniaturize); 734 break; 735 case Frame.MAXIMIZED_BOTH: 736 maximize(); 737 break; 738 default: // NORMAL 739 unmaximize(); // in case it was maximized, otherwise this is a no-op 740 break; 741 } 742 } 743 } 744 } 745 746 nativeSynthesizeMouseEnteredExitedEvents(); 747 748 // Configure stuff #2 749 updateFocusabilityForAutoRequestFocus(true); 750 751 // Manage parent-child relationship when showing 752 final ComponentAccessor acc = AWTAccessor.getComponentAccessor(); 753 754 if (visible) { 755 // Order myself above my parent 756 if (owner != null && owner.isVisible()) { 757 owner.execute(ownerPtr -> { 758 execute(ptr -> { 759 CWrapper.NSWindow.orderWindow(ptr, CWrapper.NSWindow.NSWindowAbove, ownerPtr); 760 }); 761 }); 762 execute(CWrapper.NSWindow::orderFront); 763 applyWindowLevel(target); 764 } 765 766 // Order my own children above myself 767 for (Window w : target.getOwnedWindows()) { 768 final Object p = acc.getPeer(w); 769 if (p instanceof LWWindowPeer) { 770 CPlatformWindow pw = (CPlatformWindow)((LWWindowPeer)p).getPlatformWindow(); 771 if (pw != null && pw.isVisible()) { 772 pw.execute(childPtr -> { 773 execute(ptr -> { 774 CWrapper.NSWindow.orderWindow(childPtr, CWrapper.NSWindow.NSWindowAbove, ptr); 775 }); 776 }); 777 pw.applyWindowLevel(w); 778 } 779 } 780 } 781 } 782 783 // Deal with the blocker of the window being shown 784 if (blocker != null && visible) { 785 // Make sure the blocker is above its siblings 786 ((CPlatformWindow)blocker.getPlatformWindow()).orderAboveSiblings(); 787 } 788 } 789 790 @Override // PlatformWindow 791 public void setTitle(String title) { 792 execute(ptr -> nativeSetNSWindowTitle(ptr, title)); 793 } 794 795 // Should be called on every window key property change. 796 @Override // PlatformWindow 797 public void updateIconImages() { 798 final CImage cImage = getImageForTarget(); 799 execute(ptr -> { 800 if (cImage == null) { 801 nativeSetNSWindowMinimizedIcon(ptr, 0L); 802 } else { 803 cImage.execute(imagePtr -> { 804 nativeSetNSWindowMinimizedIcon(ptr, imagePtr); 805 }); 806 } 807 }); 808 } 809 810 public SurfaceData getSurfaceData() { 811 return contentView.getSurfaceData(); 812 } 813 814 @Override // PlatformWindow 815 public void toBack() { 816 execute(CPlatformWindow::nativePushNSWindowToBack); 817 } 818 819 @Override // PlatformWindow 820 public void toFront() { 821 LWCToolkit lwcToolkit = (LWCToolkit) Toolkit.getDefaultToolkit(); 822 Window w = DefaultKeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow(); 823 final ComponentAccessor acc = AWTAccessor.getComponentAccessor(); 824 if( w != null && acc.getPeer(w) != null 825 && ((LWWindowPeer)acc.getPeer(w)).getPeerType() == LWWindowPeer.PeerType.EMBEDDED_FRAME 826 && !lwcToolkit.isApplicationActive()) { 827 lwcToolkit.activateApplicationIgnoringOtherApps(); 828 } 829 updateFocusabilityForAutoRequestFocus(false); 830 execute(CPlatformWindow::nativePushNSWindowToFront); 831 updateFocusabilityForAutoRequestFocus(true); 832 } 833 834 private void setCanFullscreen(final boolean canFullScreen) { 835 if (target instanceof RootPaneContainer 836 && getPeer().getPeerType() == PeerType.FRAME) { 837 838 if (isInFullScreen && !canFullScreen) { 839 toggleFullScreen(); 840 } 841 842 final RootPaneContainer rpc = (RootPaneContainer) target; 843 rpc.getRootPane().putClientProperty( 844 CPlatformWindow.WINDOW_FULLSCREENABLE, canFullScreen); 845 } 846 } 847 848 @Override 849 public void setResizable(final boolean resizable) { 850 setCanFullscreen(resizable); 851 setStyleBits(RESIZABLE, resizable); 852 setStyleBits(ZOOMABLE, resizable); 853 } 854 855 @Override 856 public void setSizeConstraints(int minW, int minH, int maxW, int maxH) { 857 execute(ptr -> nativeSetNSWindowMinMax(ptr, minW, minH, maxW, maxH)); 858 } 859 860 @Override 861 public boolean rejectFocusRequest(FocusEvent.Cause cause) { 862 // Cross-app activation requests are not allowed. 863 if (cause != FocusEvent.Cause.MOUSE_EVENT && 864 !((LWCToolkit)Toolkit.getDefaultToolkit()).isApplicationActive()) 865 { 866 focusLogger.fine("the app is inactive, so the request is rejected"); 867 return true; 868 } 869 return false; 870 } 871 872 @Override 873 public boolean requestWindowFocus() { 874 execute(ptr -> { 875 if (CWrapper.NSWindow.canBecomeMainWindow(ptr)) { 876 CWrapper.NSWindow.makeMainWindow(ptr); 877 } 878 CWrapper.NSWindow.makeKeyAndOrderFront(ptr); 879 }); 880 return true; 881 } 882 883 @Override 884 public boolean isActive() { 885 AtomicBoolean ref = new AtomicBoolean(); 886 execute(ptr -> { 887 ref.set(CWrapper.NSWindow.isKeyWindow(ptr)); 888 }); 889 return ref.get(); 890 } 891 892 @Override 893 public void updateFocusableWindowState() { 894 final boolean isFocusable = isNativelyFocusableWindow(); 895 setStyleBits(SHOULD_BECOME_KEY | SHOULD_BECOME_MAIN, isFocusable); // set both bits at once 896 } 897 898 @Override 899 public void setAlwaysOnTop(boolean isAlwaysOnTop) { 900 setStyleBits(ALWAYS_ON_TOP, isAlwaysOnTop); 901 } 902 903 @Override 904 public void setOpacity(float opacity) { 905 execute(ptr -> CWrapper.NSWindow.setAlphaValue(ptr, opacity)); 906 } 907 908 @Override 909 public void setOpaque(boolean isOpaque) { 910 execute(ptr -> CWrapper.NSWindow.setOpaque(ptr, isOpaque)); 911 boolean isTextured = (peer == null) ? false : peer.isTextured(); 912 if (!isTextured) { 913 if (!isOpaque) { 914 execute(ptr -> CWrapper.NSWindow.setBackgroundColor(ptr, 0)); 915 } else if (peer != null) { 916 Color color = peer.getBackground(); 917 if (color != null) { 918 int rgb = color.getRGB(); 919 execute(ptr->CWrapper.NSWindow.setBackgroundColor(ptr, rgb)); 920 } 921 } 922 } 923 924 //This is a temporary workaround. Looks like after 7124236 will be fixed 925 //the correct place for invalidateShadow() is CGLayer.drawInCGLContext. 926 SwingUtilities.invokeLater(this::invalidateShadow); 927 } 928 929 @Override 930 public void enterFullScreenMode() { 931 isFullScreenMode = true; 932 execute(CPlatformWindow::nativeEnterFullScreenMode); 933 } 934 935 @Override 936 public void exitFullScreenMode() { 937 execute(CPlatformWindow::nativeExitFullScreenMode); 938 isFullScreenMode = false; 939 } 940 941 @Override 942 public boolean isFullScreenMode() { 943 return isFullScreenMode; 944 } 945 946 @Override 947 public void setWindowState(int windowState) { 948 if (peer == null || !peer.isVisible()) { 949 // setVisible() applies the state 950 return; 951 } 952 953 int prevWindowState = peer.getState(); 954 if (prevWindowState == windowState) return; 955 956 if ((windowState & Frame.ICONIFIED) != 0) { 957 // Treat all state bit masks with ICONIFIED bit as ICONIFIED state. 958 windowState = Frame.ICONIFIED; 959 } 960 961 switch (windowState) { 962 case Frame.ICONIFIED: 963 if (prevWindowState == Frame.MAXIMIZED_BOTH) { 964 // let's return into the normal states first 965 // the zoom call toggles between the normal and the max states 966 unmaximize(); 967 } 968 execute(CWrapper.NSWindow::miniaturize); 969 break; 970 case Frame.MAXIMIZED_BOTH: 971 if (prevWindowState == Frame.ICONIFIED) { 972 // let's return into the normal states first 973 execute(CWrapper.NSWindow::deminiaturize); 974 } 975 maximize(); 976 break; 977 case Frame.NORMAL: 978 if (prevWindowState == Frame.ICONIFIED) { 979 execute(CWrapper.NSWindow::deminiaturize); 980 } else if (prevWindowState == Frame.MAXIMIZED_BOTH) { 981 // the zoom call toggles between the normal and the max states 982 unmaximize(); 983 } 984 break; 985 default: 986 throw new RuntimeException("Unknown window state: " + windowState); 987 } 988 989 // NOTE: the SWP.windowState field gets updated to the newWindowState 990 // value when the native notification comes to us 991 } 992 993 @Override 994 public void setModalBlocked(boolean blocked) { 995 if (target.getModalExclusionType() == Dialog.ModalExclusionType.APPLICATION_EXCLUDE) { 996 return; 997 } 998 999 if (blocked) { 1000 // We are going to show a modal window. Previously displayed window will be 1001 // blocked/disabled. So we have to send mouse exited event to it now, since 1002 // all mouse events are discarded for blocked/disabled windows. 1003 execute(ptr -> nativeSynthesizeMouseEnteredExitedEvents(ptr, CocoaConstants.NSMouseExited)); 1004 } 1005 1006 execute(ptr -> nativeSetEnabled(ptr, !blocked)); 1007 checkBlockingAndOrder(); 1008 } 1009 1010 public final void invalidateShadow() { 1011 execute(ptr -> nativeRevalidateNSWindowShadow(ptr)); 1012 } 1013 1014 // ---------------------------------------------------------------------- 1015 // UTILITY METHODS 1016 // ---------------------------------------------------------------------- 1017 1018 /** 1019 * Find image to install into Title or into Application icon. First try 1020 * icons installed for toplevel. Null is returned, if there is no icon and 1021 * default Duke image should be used. 1022 */ 1023 private CImage getImageForTarget() { 1024 CImage icon = null; 1025 try { 1026 icon = CImage.getCreator().createFromImages(target.getIconImages()); 1027 } catch (Exception ignored) { 1028 // Perhaps the icon passed into Java is broken. Skipping this icon. 1029 } 1030 return icon; 1031 } 1032 1033 /* 1034 * Returns LWWindowPeer associated with this delegate. 1035 */ 1036 @Override 1037 public LWWindowPeer getPeer() { 1038 return peer; 1039 } 1040 1041 @Override 1042 public boolean isUnderMouse() { 1043 return contentView.isUnderMouse(); 1044 } 1045 1046 public CPlatformView getContentView() { 1047 return contentView; 1048 } 1049 1050 @Override 1051 public long getLayerPtr() { 1052 return contentView.getWindowLayerPtr(); 1053 } 1054 1055 private void validateSurface() { 1056 SurfaceData surfaceData = getSurfaceData(); 1057 if (surfaceData instanceof CGLSurfaceData) { 1058 ((CGLSurfaceData)surfaceData).validate(); 1059 } 1060 } 1061 1062 void flushBuffers() { 1063 if (isVisible() && !nativeBounds.isEmpty() && !isFullScreenMode) { 1064 try { 1065 LWCToolkit.invokeAndWait(new Runnable() { 1066 @Override 1067 public void run() { 1068 //Posting an empty to flush the EventQueue without blocking the main thread 1069 } 1070 }, target); 1071 } catch (InvocationTargetException e) { 1072 e.printStackTrace(); 1073 } 1074 } 1075 } 1076 1077 /** 1078 * Helper method to get a pointer to the native view from the PlatformWindow. 1079 */ 1080 static long getNativeViewPtr(PlatformWindow platformWindow) { 1081 long nativePeer = 0L; 1082 if (platformWindow instanceof CPlatformWindow) { 1083 nativePeer = ((CPlatformWindow) platformWindow).getContentView().getAWTView(); 1084 } else if (platformWindow instanceof CViewPlatformEmbeddedFrame){ 1085 nativePeer = ((CViewPlatformEmbeddedFrame) platformWindow).getNSViewPtr(); 1086 } 1087 return nativePeer; 1088 } 1089 1090 /************************************************************* 1091 * Callbacks from the AWTWindow and AWTView objc classes. 1092 *************************************************************/ 1093 private void deliverWindowFocusEvent(boolean gained, CPlatformWindow opposite){ 1094 // Fix for 7150349: ingore "gained" notifications when the app is inactive. 1095 if (gained && !((LWCToolkit)Toolkit.getDefaultToolkit()).isApplicationActive()) { 1096 focusLogger.fine("the app is inactive, so the notification is ignored"); 1097 return; 1098 } 1099 1100 LWWindowPeer oppositePeer = (opposite == null)? null : opposite.getPeer(); 1101 responder.handleWindowFocusEvent(gained, oppositePeer); 1102 } 1103 1104 protected void deliverMoveResizeEvent(int x, int y, int width, int height, 1105 boolean byUser) { 1106 AtomicBoolean ref = new AtomicBoolean(); 1107 execute(ptr -> { 1108 ref.set(CWrapper.NSWindow.isZoomed(ptr)); 1109 }); 1110 isZoomed = ref.get(); 1111 checkZoom(); 1112 1113 final Rectangle oldB = nativeBounds; 1114 nativeBounds = new Rectangle(x, y, width, height); 1115 if (peer != null) { 1116 peer.notifyReshape(x, y, width, height); 1117 // System-dependent appearance optimization. 1118 if ((byUser && !oldB.getSize().equals(nativeBounds.getSize())) 1119 || isFullScreenAnimationOn) { 1120 flushBuffers(); 1121 } 1122 } 1123 } 1124 1125 private void deliverWindowClosingEvent() { 1126 if (peer != null && peer.getBlocker() == null) { 1127 peer.postEvent(new WindowEvent(target, WindowEvent.WINDOW_CLOSING)); 1128 } 1129 } 1130 1131 private void deliverIconify(final boolean iconify) { 1132 if (peer != null) { 1133 peer.notifyIconify(iconify); 1134 } 1135 if (iconify) { 1136 isIconifyAnimationActive = false; 1137 } 1138 } 1139 1140 private void deliverZoom(final boolean isZoomed) { 1141 if (peer != null) { 1142 peer.notifyZoom(isZoomed); 1143 } 1144 } 1145 1146 private void checkZoom() { 1147 if (peer != null) { 1148 int state = peer.getState(); 1149 if (state != Frame.MAXIMIZED_BOTH && isMaximized()) { 1150 deliverZoom(true); 1151 } else if (state == Frame.MAXIMIZED_BOTH && !isMaximized()) { 1152 deliverZoom(false); 1153 } 1154 } 1155 } 1156 1157 private void deliverNCMouseDown() { 1158 if (peer != null) { 1159 peer.notifyNCMouseDown(); 1160 } 1161 } 1162 1163 /* 1164 * Our focus model is synthetic and only non-simple window 1165 * may become natively focusable window. 1166 */ 1167 private boolean isNativelyFocusableWindow() { 1168 if (peer == null) { 1169 return false; 1170 } 1171 1172 return !peer.isSimpleWindow() && target.getFocusableWindowState(); 1173 } 1174 1175 private boolean isBlocked() { 1176 LWWindowPeer blocker = (peer != null) ? peer.getBlocker() : null; 1177 return (blocker != null); 1178 } 1179 1180 /* 1181 * An utility method for the support of the auto request focus. 1182 * Updates the focusable state of the window under certain 1183 * circumstances. 1184 */ 1185 private void updateFocusabilityForAutoRequestFocus(boolean isFocusable) { 1186 if (target.isAutoRequestFocus() || !isNativelyFocusableWindow()) return; 1187 setStyleBits(SHOULD_BECOME_KEY | SHOULD_BECOME_MAIN, isFocusable); // set both bits at once 1188 } 1189 1190 private boolean checkBlockingAndOrder() { 1191 LWWindowPeer blocker = (peer == null)? null : peer.getBlocker(); 1192 if (blocker == null) { 1193 return false; 1194 } 1195 1196 if (blocker instanceof CPrinterDialogPeer) { 1197 return true; 1198 } 1199 1200 CPlatformWindow pWindow = (CPlatformWindow)blocker.getPlatformWindow(); 1201 1202 pWindow.orderAboveSiblings(); 1203 1204 pWindow.execute(ptr -> { 1205 CWrapper.NSWindow.orderFrontRegardless(ptr); 1206 CWrapper.NSWindow.makeKeyAndOrderFront(ptr); 1207 CWrapper.NSWindow.makeMainWindow(ptr); 1208 }); 1209 return true; 1210 } 1211 1212 private boolean isIconified() { 1213 boolean isIconified = false; 1214 if (target instanceof Frame) { 1215 int state = ((Frame)target).getExtendedState(); 1216 if ((state & Frame.ICONIFIED) != 0) { 1217 isIconified = true; 1218 } 1219 } 1220 return isIconifyAnimationActive || isIconified; 1221 } 1222 1223 private boolean isOneOfOwnersOrSelf(CPlatformWindow window) { 1224 while (window != null) { 1225 if (this == window) { 1226 return true; 1227 } 1228 window = window.owner; 1229 } 1230 return false; 1231 } 1232 1233 private CPlatformWindow getRootOwner() { 1234 CPlatformWindow rootOwner = this; 1235 while (rootOwner.owner != null) { 1236 rootOwner = rootOwner.owner; 1237 } 1238 return rootOwner; 1239 } 1240 1241 private void orderAboveSiblings() { 1242 // Recursively pop up the windows from the very bottom, (i.e. root owner) so that 1243 // the windows are ordered above their nearest owner; ancestors of the window, 1244 // which is going to become 'main window', are placed above their siblings. 1245 CPlatformWindow rootOwner = getRootOwner(); 1246 if (rootOwner.isVisible() && !rootOwner.isIconified() && !rootOwner.isActive()) { 1247 rootOwner.execute(CWrapper.NSWindow::orderFront); 1248 } 1249 1250 // Do not order child windows of iconified owner. 1251 if (!rootOwner.isIconified()) { 1252 final WindowAccessor windowAccessor = AWTAccessor.getWindowAccessor(); 1253 orderAboveSiblingsImpl(windowAccessor.getOwnedWindows(rootOwner.target)); 1254 } 1255 } 1256 1257 private void orderAboveSiblingsImpl(Window[] windows) { 1258 ArrayList<Window> childWindows = new ArrayList<Window>(); 1259 1260 final ComponentAccessor componentAccessor = AWTAccessor.getComponentAccessor(); 1261 final WindowAccessor windowAccessor = AWTAccessor.getWindowAccessor(); 1262 Arrays.sort(windows, siblingsComparator); 1263 // Go through the list of windows and perform ordering. 1264 CPlatformWindow pwUnder = null; 1265 for (Window w : windows) { 1266 boolean iconified = false; 1267 final Object p = componentAccessor.getPeer(w); 1268 if (p instanceof LWWindowPeer) { 1269 CPlatformWindow pw = (CPlatformWindow)((LWWindowPeer)p).getPlatformWindow(); 1270 iconified = isIconified(); 1271 if (pw != null && pw.isVisible() && !iconified) { 1272 // If the window is one of ancestors of 'main window' or is going to become main by itself, 1273 // the window should be ordered above its siblings; otherwise the window is just ordered 1274 // above its nearest parent. 1275 if (pw.isOneOfOwnersOrSelf(this)) { 1276 pw.execute(CWrapper.NSWindow::orderFront); 1277 } else { 1278 if (pwUnder == null) { 1279 pwUnder = pw.owner; 1280 } 1281 pwUnder.execute(underPtr -> { 1282 pw.execute(ptr -> { 1283 CWrapper.NSWindow.orderWindow(ptr, CWrapper.NSWindow.NSWindowAbove, underPtr); 1284 }); 1285 }); 1286 pwUnder = pw; 1287 } 1288 pw.applyWindowLevel(w); 1289 } 1290 } 1291 // Retrieve the child windows for each window from the list except iconified ones 1292 // and store them for future use. 1293 // Note: we collect data about child windows even for invisible owners, since they may have 1294 // visible children. 1295 if (!iconified) { 1296 childWindows.addAll(Arrays.asList(windowAccessor.getOwnedWindows(w))); 1297 } 1298 } 1299 // If some windows, which have just been ordered, have any child windows, let's start new iteration 1300 // and order these child windows. 1301 if (!childWindows.isEmpty()) { 1302 orderAboveSiblingsImpl(childWindows.toArray(new Window[0])); 1303 } 1304 } 1305 1306 protected void applyWindowLevel(Window target) { 1307 if (target.isAlwaysOnTop() && target.getType() != Window.Type.POPUP) { 1308 execute(ptr->CWrapper.NSWindow.setLevel(ptr, CWrapper.NSWindow.NSFloatingWindowLevel)); 1309 } else if (target.getType() == Window.Type.POPUP) { 1310 execute(ptr->CWrapper.NSWindow.setLevel(ptr, CWrapper.NSWindow.NSPopUpMenuWindowLevel)); 1311 } 1312 } 1313 1314 private Window getOwnerFrameOrDialog(Window window) { 1315 Window owner = window.getOwner(); 1316 while (owner != null && !(owner instanceof Frame || owner instanceof Dialog)) { 1317 owner = owner.getOwner(); 1318 } 1319 return owner; 1320 } 1321 1322 private boolean isSimpleWindowOwnedByEmbeddedFrame() { 1323 if (peer != null && peer.isSimpleWindow()) { 1324 return (getOwnerFrameOrDialog(target) instanceof CEmbeddedFrame); 1325 } 1326 return false; 1327 } 1328 // ---------------------------------------------------------------------- 1329 // NATIVE CALLBACKS 1330 // ---------------------------------------------------------------------- 1331 1332 private void windowWillMiniaturize() { 1333 isIconifyAnimationActive = true; 1334 } 1335 1336 private void windowDidBecomeMain() { 1337 lastBecomeMainTime = System.currentTimeMillis(); 1338 if (checkBlockingAndOrder()) return; 1339 // If it's not blocked, make sure it's above its siblings 1340 orderAboveSiblings(); 1341 } 1342 1343 private void windowWillEnterFullScreen() { 1344 isFullScreenAnimationOn = true; 1345 } 1346 1347 private void windowDidEnterFullScreen() { 1348 isInFullScreen = true; 1349 isFullScreenAnimationOn = false; 1350 } 1351 1352 private void windowWillExitFullScreen() { 1353 isFullScreenAnimationOn = true; 1354 } 1355 1356 private void windowDidExitFullScreen() { 1357 isInFullScreen = false; 1358 isFullScreenAnimationOn = false; 1359 } 1360 }