--- old/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java 2018-10-01 13:55:23.000000000 -0700 +++ new/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java 2018-10-01 13:55:23.000000000 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -121,7 +121,8 @@ public static final String WINDOW_FADE_IN = "apple.awt._windowFadeIn"; public static final String WINDOW_FADE_OUT = "apple.awt._windowFadeOut"; public static final String WINDOW_FULLSCREENABLE = "apple.awt.fullscreenable"; - + public static final String WINDOW_FULL_CONTENT = "apple.awt.fullWindowContent"; + public static final String WINDOW_TRANSPARENT_TITLE_BAR = "apple.awt.transparentTitleBar"; // Yeah, I know. But it's easier to deal with ints from JNI static final int MODELESS = 0; @@ -149,7 +150,10 @@ static final int IS_MODAL = 1 << 26; static final int IS_POPUP = 1 << 27; - static final int _STYLE_PROP_BITMASK = DECORATED | TEXTURED | UNIFIED | UTILITY | HUD | SHEET | CLOSEABLE | MINIMIZABLE | RESIZABLE; + static final int FULL_WINDOW_CONTENT = 1 << 14; + + static final int _STYLE_PROP_BITMASK = DECORATED | TEXTURED | UNIFIED | UTILITY | HUD | SHEET | CLOSEABLE + | MINIMIZABLE | RESIZABLE | FULL_WINDOW_CONTENT; // corresponds to method-based properties static final int HAS_SHADOW = 1 << 10; @@ -160,8 +164,11 @@ static final int DRAGGABLE_BACKGROUND = 1 << 19; static final int DOCUMENT_MODIFIED = 1 << 21; static final int FULLSCREENABLE = 1 << 23; + static final int TRANSPARENT_TITLE_BAR = 1 << 18; - static final int _METHOD_PROP_BITMASK = RESIZABLE | HAS_SHADOW | ZOOMABLE | ALWAYS_ON_TOP | HIDES_ON_DEACTIVATE | DRAGGABLE_BACKGROUND | DOCUMENT_MODIFIED | FULLSCREENABLE; + static final int _METHOD_PROP_BITMASK = RESIZABLE | HAS_SHADOW | ZOOMABLE | ALWAYS_ON_TOP | HIDES_ON_DEACTIVATE + | DRAGGABLE_BACKGROUND | DOCUMENT_MODIFIED | FULLSCREENABLE + | TRANSPARENT_TITLE_BAR; // corresponds to callback-based properties static final int SHOULD_BECOME_KEY = 1 << 12; @@ -230,7 +237,19 @@ final String filename = ((java.io.File)value).getAbsolutePath(); c.execute(ptr->nativeSetNSWindowRepresentedFilename(ptr, filename)); - }} + }}, + new Property(WINDOW_FULL_CONTENT) { + public void applyProperty(final CPlatformWindow c, final Object value) { + boolean isFullWindowContent = Boolean.parseBoolean(value.toString()); + c.setStyleBits(FULL_WINDOW_CONTENT, isFullWindowContent); + } + }, + new Property(WINDOW_TRANSPARENT_TITLE_BAR) { + public void applyProperty(final CPlatformWindow c, final Object value) { + boolean isTransparentTitleBar = Boolean.parseBoolean(value.toString()); + c.setStyleBits(TRANSPARENT_TITLE_BAR, isTransparentTitleBar); + } + } }) { @SuppressWarnings("deprecation") public CPlatformWindow convertJComponentToTarget(final JRootPane p) { @@ -468,6 +487,16 @@ if (prop != null) { styleBits = SET(styleBits, DRAGGABLE_BACKGROUND, Boolean.parseBoolean(prop.toString())); } + + prop = rootpane.getClientProperty(WINDOW_FULL_CONTENT); + if (prop != null) { + styleBits = SET(styleBits, FULL_WINDOW_CONTENT, Boolean.parseBoolean(prop.toString())); + } + + prop = rootpane.getClientProperty(WINDOW_TRANSPARENT_TITLE_BAR); + if (prop != null) { + styleBits = SET(styleBits, TRANSPARENT_TITLE_BAR, Boolean.parseBoolean(prop.toString())); + } } if (isDialog) { --- old/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.m 2018-10-01 13:55:24.000000000 -0700 +++ new/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.m 2018-10-01 13:55:24.000000000 -0700 @@ -203,9 +203,10 @@ NSUInteger type = 0; if (IS(styleBits, DECORATED)) { type |= NSTitledWindowMask; - if (IS(styleBits, CLOSEABLE)) type |= NSClosableWindowMask; - if (IS(styleBits, MINIMIZABLE)) type |= NSMiniaturizableWindowMask; - if (IS(styleBits, RESIZABLE)) type |= NSResizableWindowMask; + if (IS(styleBits, CLOSEABLE)) type |= NSClosableWindowMask; + if (IS(styleBits, MINIMIZABLE)) type |= NSMiniaturizableWindowMask; + if (IS(styleBits, RESIZABLE)) type |= NSResizableWindowMask; + if (IS(styleBits, FULL_WINDOW_CONTENT)) type |= NSFullSizeContentViewWindowMask; } else { type |= NSBorderlessWindowMask; } @@ -263,6 +264,10 @@ [self.nsWindow setCollectionBehavior:NSWindowCollectionBehaviorDefault]; } } + + if (IS(mask, TRANSPARENT_TITLE_BAR) && [self.nsWindow respondsToSelector:@selector(setTitlebarAppearsTransparent:)]) { + [self.nsWindow setTitlebarAppearsTransparent:IS(bits, TRANSPARENT_TITLE_BAR)]; + } } - (id) initWithPlatformWindow:(JNFWeakJObjectWrapper *)platformWindow @@ -590,30 +595,33 @@ // NSWindowDelegate methods -- (void) _deliverMoveResizeEvent { -AWT_ASSERT_APPKIT_THREAD; - - // deliver the event if this is a user-initiated live resize or as a side-effect - // of a Java initiated resize, because AppKit can override the bounds and force - // the bounds of the window to avoid the Dock or remain on screen. - [AWTToolkit eventCountPlusPlus]; - JNIEnv *env = [ThreadUtilities getJNIEnv]; - jobject platformWindow = [self.javaPlatformWindow jObjectWithEnv:env]; +static void deliverMoveResizeEvent(JNIEnv *env, NSRect frame, JNFWeakJObjectWrapper *javaPlatformWindow, + BOOL inLiveResize) { + jobject platformWindow = [javaPlatformWindow jObjectWithEnv:env]; if (platformWindow == NULL) { // TODO: create generic AWT assert } - NSRect frame = ConvertNSScreenRect(env, [self.nsWindow frame]); - static JNF_MEMBER_CACHE(jm_deliverMoveResizeEvent, jc_CPlatformWindow, "deliverMoveResizeEvent", "(IIIIZ)V"); JNFCallVoidMethod(env, platformWindow, jm_deliverMoveResizeEvent, (jint)frame.origin.x, (jint)frame.origin.y, (jint)frame.size.width, (jint)frame.size.height, - (jboolean)[self.nsWindow inLiveResize]); + (jboolean)inLiveResize); (*env)->DeleteLocalRef(env, platformWindow); +} + +- (void) _deliverMoveResizeEvent { +AWT_ASSERT_APPKIT_THREAD; + // deliver the event if this is a user-initiated live resize or as a side-effect + // of a Java initiated resize, because AppKit can override the bounds and force + // the bounds of the window to avoid the Dock or remain on screen. + [AWTToolkit eventCountPlusPlus]; + JNIEnv *env = [ThreadUtilities getJNIEnv]; + NSRect frame = ConvertNSScreenRect(env, [self.nsWindow frame]); + deliverMoveResizeEvent(env, frame, self.javaPlatformWindow, [self.nsWindow inLiveResize]); [AWTWindow synthesizeMouseEnteredExitedEventsForAllWindows]; } @@ -1058,14 +1066,39 @@ JNF_COCOA_ENTER(env); NSWindow *nsWindow = OBJC(windowPtr); - [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ + __block BOOL resized = NO; + __block NSRect resizeFrame; + __block JNFWeakJObjectWrapper *javaPlatformWindow; + + BOOL shouldWait = IS(mask, FULL_WINDOW_CONTENT); // delay upcall until window changes are made + + [ThreadUtilities performOnMainThreadWaiting:shouldWait block:^(){ AWTWindow *window = (AWTWindow*)[nsWindow delegate]; // scans the bit field, and only updates the values requested by the mask - // (this implicity handles the _CALLBACK_PROP_BITMASK case, since those are passive reads) + // (this implicitly handles the _CALLBACK_PROP_BITMASK case, since those are passive reads) jint newBits = window.styleBits & ~mask | bits & mask; + // Check for a change to the full window content view option. + // The content view must be resized first, otherwise the window will be resized to fit the existing + // content view. + if (IS(mask, FULL_WINDOW_CONTENT)) { + if (IS(newBits, FULL_WINDOW_CONTENT) != IS(window.styleBits, FULL_WINDOW_CONTENT)) { + NSRect frame = [nsWindow frame]; + NSUInteger styleMask = [AWTWindow styleMaskForStyleBits:newBits]; + NSRect screenContentRect = [NSWindow contentRectForFrameRect:frame styleMask:styleMask]; + NSRect contentFrame = NSMakeRect(screenContentRect.origin.x - frame.origin.x, + screenContentRect.origin.y - frame.origin.y, + screenContentRect.size.width, + screenContentRect.size.height); + nsWindow.contentView.frame = contentFrame; + resized = YES; + resizeFrame = [nsWindow frame]; + javaPlatformWindow = window.javaPlatformWindow; + } + } + // resets the NSWindow's style mask if the mask intersects any of those bits if (mask & MASK(_STYLE_PROP_BITMASK)) { [nsWindow setStyleMask:[AWTWindow styleMaskForStyleBits:newBits]]; @@ -1079,6 +1112,13 @@ window.styleBits = newBits; }]; + if (resized) { + [AWTToolkit eventCountPlusPlus]; + NSRect frame = ConvertNSScreenRect(env, resizeFrame); + deliverMoveResizeEvent(env, frame, javaPlatformWindow, NO); + [AWTWindow synthesizeMouseEnteredExitedEventsForAllWindows]; + } + JNF_COCOA_EXIT(env); } --- /dev/null 2018-10-01 13:55:25.000000000 -0700 +++ new/test/jdk/java/awt/Window/FullWindowContentTest/FullWindowContentTest.java 2018-10-01 13:55:25.000000000 -0700 @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @key headful + * @bug 8211301 + * @summary [macosx] support full window content options + * @author Alan Snyder + * @run main FullWindowContentTest + * @requires (os.family == "mac") +*/ + +import java.awt.AWTException; +import java.awt.Color; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.image.BufferedImage; +import java.lang.reflect.InvocationTargetException; +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JRootPane; +import javax.swing.SwingUtilities; + +public class FullWindowContentTest +{ + static FullWindowContentTest theTest; + private Robot robot; + private JFrame frame; + private JRootPane rootPane; + static boolean isTransparentSupported = getOSVersion() >= 1010; + + private int DELAY = 1000; + + public FullWindowContentTest() { + try { + robot = new Robot(); + } catch (AWTException ex) { + throw new RuntimeException(ex); + } + } + + public void performTest() { + + runSwing(() -> { + frame = new JFrame("Test"); + frame.setBounds(200, 200, 300, 100); + rootPane = frame.getRootPane(); + JComponent contentPane = (JComponent) frame.getContentPane(); + contentPane.setBackground(Color.RED); + rootPane.putClientProperty("apple.awt.fullWindowContent", true); + rootPane.putClientProperty("apple.awt.transparentTitleBar", true); + frame.setVisible(true); + }); + + robot.delay(DELAY); + checkTransparent(); + + runSwing(() -> rootPane.putClientProperty("apple.awt.transparentTitleBar", false)); + + robot.delay(DELAY); + checkTranslucent(); + + runSwing(() -> rootPane.putClientProperty("apple.awt.fullWindowContent", false)); + + robot.delay(DELAY); + checkNormal(); + + runSwing(() -> rootPane.putClientProperty("apple.awt.fullWindowContent", true)); + + robot.delay(DELAY); + checkTranslucent(); + + runSwing(() -> rootPane.putClientProperty("apple.awt.transparentTitleBar", true)); + + robot.delay(DELAY); + checkTransparent(); + + runSwing(() -> frame.dispose()); + + frame = null; + rootPane = null; + } + + private void checkTransparent() { + if (isTransparentSupported) { + Color c = getTestPixel(); + int delta = c.getRed() - c.getBlue(); + if (delta < 200) { + throw new RuntimeException("Test failed: did not find transparent title bar color"); + } + checkContent(); + } else { + checkTranslucent(); + } + } + + private void checkTranslucent() { + Color c = getTestPixel(); + int delta = c.getRed() - c.getBlue(); + if (delta < 50 || delta > 150) { + throw new RuntimeException("Test failed: did not find translucent title bar color"); + } + checkContent(); + } + + private void checkNormal() { + Color c = getTestPixel(); + int delta = c.getRed() - c.getBlue(); + if (delta < -50 || delta > 50) { + throw new RuntimeException("Test failed: did not find normal title bar color"); + } + checkContent(); + } + + private void checkContent() { + // Check the bottom of the content area to make sure the insets were changed. + Color c = getContentPixel(); + int delta = c.getRed() - c.getBlue(); + if (delta < 200) { + throw new RuntimeException("Test failed: did not find content color"); + } + } + + private Color getContentPixel() { + Rectangle bounds = frame.getBounds(); + Color c = robot.getPixelColor(bounds.x + 80, bounds.y + bounds.height - 10); + return c; + } + + private Color getTestPixel() { + Rectangle bounds = frame.getBounds(); + BufferedImage screenImage = robot.createScreenCapture(bounds); + int rgb = screenImage.getRGB(80, 10); + int red = (rgb >> 16) & 0xFF; + int green = (rgb >> 8) & 0xFF; + int blue = rgb & 0xFF; + Color c = new Color(red, green, blue); + + // Note: the following code returns significantly wrong values. + // For example, it returns 42 24 24 for a translucent red that should be more like 243 151 151. + +// Color c = robot.getPixelColor(bounds.x + 80, bounds.y + 10); + + return c; + } + + public void dispose() { + if (frame != null) { + frame.dispose(); + frame = null; + } + } + + private static int getOSVersion() { + String s = System.getProperty("os.version"); + int p = s.indexOf('.'); + int major = Integer.parseInt(s.substring(0, p)); + s = s.substring(p+1); + p = s.indexOf('.'); + int minor = Integer.parseInt(p >= 0 ? s.substring(0, p) : s); + return major * 100 + minor; + } + + private static void runSwing(Runnable r) { + try { + SwingUtilities.invokeAndWait(r); + } catch (InterruptedException e) { + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + public static void main(String[] args) { + if (!System.getProperty("os.name").contains("OS X")) { + System.out.println("This test is for MacOS only. Automatically passed on other platforms."); + return; + } + + try { + runSwing(() -> theTest = new FullWindowContentTest()); + theTest.performTest(); + ; + } finally { + if (theTest != null) { + runSwing(() -> theTest.dispose()); + } + } + } +}