From eb2a7057ceb18458ea8b8840f2cd21b215d3e77f Mon Sep 17 00:00:00 2001 From: Jannis Weis <31143295+weisJ@users.noreply.github.com> Date: Wed, 13 Apr 2022 01:20:49 +0200 Subject: [PATCH] Expose bounds of title bar buttons This is especially useful on macOS with fullSizeContentView enabled to ensure components in the title bar area don't get obscured by the buttons. --- .../platform/macos/JNIDecorationsMacOS.java | 4 ++- .../macos/MacOSDecorationsProvider.java | 14 +++++--- .../platform/macos/ui/MacOSTitlePane.java | 4 +++ macos/src/main/objcpp/Decorations.mm | 26 ++++++++++++++ .../darklaf/platform/DecorationsProvider.java | 8 +++++ .../darklaf/platform/TitlePaneLayoutInfo.java | 35 +++++++++++++++++++ .../DefaultDecorationsProvider.java | 5 +++ .../decorations/NativeDecorationsManager.java | 8 +++++ .../windows/WindowsDecorationsProvider.java | 12 ++++--- .../platform/windows/ui/WindowsTitlePane.java | 15 ++++++++ 10 files changed, 122 insertions(+), 9 deletions(-) create mode 100644 platform-base/src/main/java/com/github/weisj/darklaf/platform/TitlePaneLayoutInfo.java diff --git a/macos/src/main/java/com/github/weisj/darklaf/platform/macos/JNIDecorationsMacOS.java b/macos/src/main/java/com/github/weisj/darklaf/platform/macos/JNIDecorationsMacOS.java index d58f8097..af64fdf6 100644 --- a/macos/src/main/java/com/github/weisj/darklaf/platform/macos/JNIDecorationsMacOS.java +++ b/macos/src/main/java/com/github/weisj/darklaf/platform/macos/JNIDecorationsMacOS.java @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2019-2021 Jannis Weis + * Copyright (c) 2019-2022 Jannis Weis * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and * associated documentation files (the "Software"), to deal in the Software without restriction, @@ -44,4 +44,6 @@ public final class JNIDecorationsMacOS { public static native boolean isFullscreen(final long hwnd); public static native double getTitleFontSize(final long hwnd); + + public static native float[] windowButtonRect(final long hwnd); } diff --git a/macos/src/main/java/com/github/weisj/darklaf/platform/macos/MacOSDecorationsProvider.java b/macos/src/main/java/com/github/weisj/darklaf/platform/macos/MacOSDecorationsProvider.java index 47c67ab5..2eb5c07c 100644 --- a/macos/src/main/java/com/github/weisj/darklaf/platform/macos/MacOSDecorationsProvider.java +++ b/macos/src/main/java/com/github/weisj/darklaf/platform/macos/MacOSDecorationsProvider.java @@ -26,10 +26,7 @@ import java.util.List; import javax.swing.*; -import com.github.weisj.darklaf.platform.CustomTitlePane; -import com.github.weisj.darklaf.platform.DecorationsProvider; -import com.github.weisj.darklaf.platform.SystemInfo; -import com.github.weisj.darklaf.platform.UnsupportedProviderException; +import com.github.weisj.darklaf.platform.*; import com.github.weisj.darklaf.platform.macos.ui.MacOSTitlePane; public class MacOSDecorationsProvider implements DecorationsProvider { @@ -65,4 +62,13 @@ public class MacOSDecorationsProvider implements DecorationsProvider { public boolean supportsNativeTitleLabel() { return SystemInfo.isMacOSMojave; } + + @Override + public TitlePaneLayoutInfo titlePaneLayoutInfo(final CustomTitlePane customTitlePane) { + if (!(customTitlePane instanceof MacOSTitlePane)) throw new IllegalStateException(); + long hwnd = ((MacOSTitlePane) customTitlePane).windowHandle(); + if (hwnd == 0) return new TitlePaneLayoutInfo(new Rectangle(0, 0, -1, -1)); + float[] b = JNIDecorationsMacOS.windowButtonRect(((MacOSTitlePane) customTitlePane).windowHandle()); + return new TitlePaneLayoutInfo(new Rectangle((int) b[0], (int) b[1], (int) b[2], (int) b[3])); + } } diff --git a/macos/src/main/java/com/github/weisj/darklaf/platform/macos/ui/MacOSTitlePane.java b/macos/src/main/java/com/github/weisj/darklaf/platform/macos/ui/MacOSTitlePane.java index 755dc767..ffc5c125 100644 --- a/macos/src/main/java/com/github/weisj/darklaf/platform/macos/ui/MacOSTitlePane.java +++ b/macos/src/main/java/com/github/weisj/darklaf/platform/macos/ui/MacOSTitlePane.java @@ -198,6 +198,10 @@ public class MacOSTitlePane extends CustomTitlePane { return new RootPanePropertyChangeListener(); } + public long windowHandle() { + return decorationInformation.windowHandle; + } + protected class RootPanePropertyChangeListener implements PropertyChangeListener { @Override diff --git a/macos/src/main/objcpp/Decorations.mm b/macos/src/main/objcpp/Decorations.mm index e9a49ac8..c599b542 100644 --- a/macos/src/main/objcpp/Decorations.mm +++ b/macos/src/main/objcpp/Decorations.mm @@ -155,3 +155,29 @@ JNF_COCOA_ENTER(env); }]; JNF_COCOA_EXIT(env); } + +NSRect join(NSRect r, NSButton* button) { + NSRect buttonRect = [button convertRect: [button bounds] toView: nil]; + return NSUnionRect(r, buttonRect); +} + +JNIEXPORT jfloatArray JNICALL +Java_com_github_weisj_darklaf_platform_macos_JNIDecorationsMacOS_windowButtonRect(JNIEnv *env, jclass, jlong hwnd) { +JNF_COCOA_ENTER(env); + NSWindow *nsWindow = OBJC(hwnd); + NSButton *closeButton = [nsWindow standardWindowButton: NSWindowCloseButton]; + NSButton *minimizeButton = [nsWindow standardWindowButton: NSWindowMiniaturizeButton]; + NSButton *zoomButton = [nsWindow standardWindowButton: NSWindowZoomButton]; + + NSRect rect = NSMakeRect(0,0, -1, -1); + if (closeButton) rect = join(rect, closeButton); + if (minimizeButton) rect = join(rect, minimizeButton); + if (zoomButton) rect = join(rect, zoomButton); + + jfloatArray bounds = env->NewFloatArray(4); + jfloat rawBounds[4] = {rect.origin.x, rect.origin.y, rect.size.width, rect.size.height}; + env->SetFloatArrayRegion(bounds, 0, 0, rawBounds); + + return bounds; +JNF_COCOA_EXIT(env); +} diff --git a/platform-base/src/main/java/com/github/weisj/darklaf/platform/DecorationsProvider.java b/platform-base/src/main/java/com/github/weisj/darklaf/platform/DecorationsProvider.java index dd81086f..75049df1 100644 --- a/platform-base/src/main/java/com/github/weisj/darklaf/platform/DecorationsProvider.java +++ b/platform-base/src/main/java/com/github/weisj/darklaf/platform/DecorationsProvider.java @@ -77,4 +77,12 @@ public interface DecorationsProvider { default boolean supportsNativeTitleLabel() { return false; } + + /** + * Compute the layout information for the title pane. + * + * @param customTitlePane the given title pane + * @return information about the layout of the title pane + */ + TitlePaneLayoutInfo titlePaneLayoutInfo(final CustomTitlePane customTitlePane); } diff --git a/platform-base/src/main/java/com/github/weisj/darklaf/platform/TitlePaneLayoutInfo.java b/platform-base/src/main/java/com/github/weisj/darklaf/platform/TitlePaneLayoutInfo.java new file mode 100644 index 00000000..78f4fd5b --- /dev/null +++ b/platform-base/src/main/java/com/github/weisj/darklaf/platform/TitlePaneLayoutInfo.java @@ -0,0 +1,35 @@ +/* + * MIT License + * + * Copyright (c) 2022 Jannis Weis + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.github.weisj.darklaf.platform; + +import java.awt.*; + +public class TitlePaneLayoutInfo { + private final Rectangle windowButtonRect; + + public TitlePaneLayoutInfo(final Rectangle windowButtonRect) { + this.windowButtonRect = windowButtonRect; + } + + public Rectangle windowButtonRect() { + return windowButtonRect; + } +} diff --git a/platform-decorations/src/main/java/com/github/weisj/darklaf/platform/decorations/DefaultDecorationsProvider.java b/platform-decorations/src/main/java/com/github/weisj/darklaf/platform/decorations/DefaultDecorationsProvider.java index 286de026..5f2b5541 100644 --- a/platform-decorations/src/main/java/com/github/weisj/darklaf/platform/decorations/DefaultDecorationsProvider.java +++ b/platform-decorations/src/main/java/com/github/weisj/darklaf/platform/decorations/DefaultDecorationsProvider.java @@ -28,6 +28,7 @@ import javax.swing.*; import com.github.weisj.darklaf.platform.CustomTitlePane; import com.github.weisj.darklaf.platform.DecorationsProvider; +import com.github.weisj.darklaf.platform.TitlePaneLayoutInfo; class DefaultDecorationsProvider implements DecorationsProvider { @@ -59,4 +60,8 @@ class DefaultDecorationsProvider implements DecorationsProvider { return Collections.emptyList(); } + @Override + public TitlePaneLayoutInfo titlePaneLayoutInfo(final CustomTitlePane customTitlePane) { + return new TitlePaneLayoutInfo(new Rectangle()); + } } diff --git a/platform-decorations/src/main/java/com/github/weisj/darklaf/platform/decorations/NativeDecorationsManager.java b/platform-decorations/src/main/java/com/github/weisj/darklaf/platform/decorations/NativeDecorationsManager.java index fccbc5a3..8151180a 100644 --- a/platform-decorations/src/main/java/com/github/weisj/darklaf/platform/decorations/NativeDecorationsManager.java +++ b/platform-decorations/src/main/java/com/github/weisj/darklaf/platform/decorations/NativeDecorationsManager.java @@ -24,10 +24,12 @@ import java.awt.*; import java.util.Properties; import javax.swing.*; +import javax.swing.plaf.RootPaneUI; import com.github.weisj.darklaf.platform.CustomTitlePane; import com.github.weisj.darklaf.platform.DecorationsProvider; import com.github.weisj.darklaf.platform.SystemInfo; +import com.github.weisj.darklaf.platform.TitlePaneLayoutInfo; import com.github.weisj.darklaf.platform.macos.MacOSDecorationsProvider; import com.github.weisj.darklaf.platform.windows.WindowsDecorationsProvider; import com.github.weisj.darklaf.properties.PropertyLoader; @@ -114,4 +116,10 @@ public class NativeDecorationsManager { public boolean supportsNativeTitleText() { return decorationsProvider.supportsNativeTitleLabel(); } + + public TitlePaneLayoutInfo titlePaneLayoutInfo(final RootPaneContainer frameOrDialog) { + RootPaneUI ui = frameOrDialog.getRootPane().getUI(); + if (!(ui instanceof AbstractNativeDecorationsRootPaneUI)) throw new IllegalStateException(); + return decorationsProvider.titlePaneLayoutInfo(((AbstractNativeDecorationsRootPaneUI) ui).titlePane()); + } } diff --git a/windows/src/main/java/com/github/weisj/darklaf/platform/windows/WindowsDecorationsProvider.java b/windows/src/main/java/com/github/weisj/darklaf/platform/windows/WindowsDecorationsProvider.java index d96a8b74..75f3bca2 100644 --- a/windows/src/main/java/com/github/weisj/darklaf/platform/windows/WindowsDecorationsProvider.java +++ b/windows/src/main/java/com/github/weisj/darklaf/platform/windows/WindowsDecorationsProvider.java @@ -27,10 +27,7 @@ import java.util.List; import javax.swing.*; import javax.swing.border.Border; -import com.github.weisj.darklaf.platform.CustomTitlePane; -import com.github.weisj.darklaf.platform.DecorationsProvider; -import com.github.weisj.darklaf.platform.SystemInfo; -import com.github.weisj.darklaf.platform.UnsupportedProviderException; +import com.github.weisj.darklaf.platform.*; import com.github.weisj.darklaf.platform.windows.ui.WindowsTitlePane; public class WindowsDecorationsProvider implements DecorationsProvider { @@ -96,6 +93,13 @@ public class WindowsDecorationsProvider implements DecorationsProvider { } } + @Override + public TitlePaneLayoutInfo titlePaneLayoutInfo(final CustomTitlePane customTitlePane) { + if (!(customTitlePane instanceof WindowsTitlePane)) throw new IllegalStateException(); + WindowsTitlePane titlePane = (WindowsTitlePane) customTitlePane; + return new TitlePaneLayoutInfo(titlePane.windowButtonRect()); + } + @Override public List getPropertyResourcePaths() { ArrayList properties = new ArrayList<>(); diff --git a/windows/src/main/java/com/github/weisj/darklaf/platform/windows/ui/WindowsTitlePane.java b/windows/src/main/java/com/github/weisj/darklaf/platform/windows/ui/WindowsTitlePane.java index 3483c835..d8d413f4 100644 --- a/windows/src/main/java/com/github/weisj/darklaf/platform/windows/ui/WindowsTitlePane.java +++ b/windows/src/main/java/com/github/weisj/darklaf/platform/windows/ui/WindowsTitlePane.java @@ -26,6 +26,7 @@ import java.awt.image.BufferedImage; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.List; +import java.util.function.Consumer; import java.util.logging.Logger; import javax.accessibility.AccessibleContext; @@ -689,6 +690,20 @@ public class WindowsTitlePane extends CustomTitlePane { } } + public Rectangle windowButtonRect() { + Rectangle rect = new Rectangle(0, 0, -1, -1); + if (hideTitleBar()) return rect; + Consumer joinRect = c -> { + if (c != null && c.isVisible()) { + rect.setBounds(rect.union(c.getBounds())); + } + }; + joinRect.accept(closeButton); + joinRect.accept(maximizeToggleButton); + joinRect.accept(minimizeButton); + return rect; + } + private abstract class TitlePaneAction extends AbstractAction { private TitlePaneAction(Icon icon, String... resourceNames) { super("", icon);