Browse Source

Decorations: Allow for back-stealing of the unified menubar

Fixes #258
macos/dialog_size
weisj 3 years ago
parent
commit
73692487ac
No known key found for this signature in database
GPG Key ID: 31124CB75461DA2A
  1. 13
      core/src/main/java/com/github/weisj/darklaf/ui/rootpane/DarkSubstanceRootLayout.java
  2. 169
      windows/src/main/java/com/github/weisj/darklaf/platform/windows/ui/MenuBarStealer.java
  3. 123
      windows/src/main/java/com/github/weisj/darklaf/platform/windows/ui/WindowsTitlePane.java

13
core/src/main/java/com/github/weisj/darklaf/ui/rootpane/DarkSubstanceRootLayout.java

@ -47,6 +47,11 @@ class DarkSubstanceRootLayout implements LayoutManager2 {
public void addLayoutComponent(final Component comp, final Object constraints) {}
private boolean hasValidMenuBar(final JRootPane root) {
return root.getJMenuBar() != null && root.getJMenuBar().getParent() == root.getLayeredPane();
}
public Dimension preferredLayoutSize(final Container parent) {
Dimension cpd, mbd, tpd;
int cpWidth = 0;
@ -72,7 +77,7 @@ class DarkSubstanceRootLayout implements LayoutManager2 {
cpHeight = cpd.height;
}
if (root.getJMenuBar() != null) {
if (hasValidMenuBar(root)) {
mbd = root.getJMenuBar().getPreferredSize();
if (mbd != null) {
mbWidth = mbd.width;
@ -116,7 +121,7 @@ class DarkSubstanceRootLayout implements LayoutManager2 {
cpHeight = cpd.height;
}
if (root.getJMenuBar() != null) {
if (hasValidMenuBar(root)) {
mbd = root.getJMenuBar().getMinimumSize();
if (mbd != null) {
mbWidth = mbd.width;
@ -176,7 +181,7 @@ class DarkSubstanceRootLayout implements LayoutManager2 {
nextY += tpHeight;
}
}
if (root.getJMenuBar() != null) {
if (hasValidMenuBar(root)) {
Dimension mbd = root.getJMenuBar().getPreferredSize();
root.getJMenuBar().setBounds(x, nextY, w, mbd.height);
nextY += mbd.height;
@ -205,7 +210,7 @@ class DarkSubstanceRootLayout implements LayoutManager2 {
}
}
if (root.getJMenuBar() != null) {
if (hasValidMenuBar(root)) {
mbd = root.getJMenuBar().getMaximumSize();
if (mbd != null) {
mbWidth = mbd.width;

169
windows/src/main/java/com/github/weisj/darklaf/platform/windows/ui/MenuBarStealer.java

@ -0,0 +1,169 @@
/*
* MIT License
*
* Copyright (c) 2021 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.windows.ui;
import java.awt.event.ContainerEvent;
import java.awt.event.ContainerListener;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import javax.swing.JComponent;
import javax.swing.JLayeredPane;
import javax.swing.JMenuBar;
import javax.swing.JRootPane;
import com.github.weisj.darklaf.util.PropertyUtil;
public class MenuBarStealer {
private final JRootPane rootPane;
private final JComponent target;
private JMenuBar menuBar;
private boolean unifiedMenuBar;
private ContainerListener rootPaneContainerListener;
private ContainerListener layeredPaneContainerListener;
private HierarchyListener menuBarHierarchyListener;
public MenuBarStealer(final JRootPane rootPane, final JComponent target) {
this.rootPane = rootPane;
this.target = target;
}
public void install() {
updateMenuBar(true);
}
public void uninstall() {
updateMenuBar(false);
}
public JMenuBar getMenuBar() {
return menuBar;
}
public boolean hasMenuBar() {
return menuBar != null;
}
public boolean isMenuBarEmpty() {
return !unifiedMenuBar || menuBar == null || !menuBar.isVisible();
}
public void updateMenuBar(final boolean install) {
unifiedMenuBar = PropertyUtil.getBooleanProperty(rootPane, "JRootPane.unifiedMenuBar");
if (unifiedMenuBar && install) {
if (rootPaneContainerListener == null) {
rootPaneContainerListener = createRootPaneContainerListener();
}
if (layeredPaneContainerListener == null) {
layeredPaneContainerListener = createLayeredPaneContainerListener();
}
rootPane.addContainerListener(rootPaneContainerListener);
rootPane.getLayeredPane().addContainerListener(layeredPaneContainerListener);
addMenuBar(rootPane.getJMenuBar());
} else {
rootPane.removeContainerListener(rootPaneContainerListener);
rootPane.getLayeredPane().removeContainerListener(layeredPaneContainerListener);
if (menuBar != null) {
returnMenuBar();
}
}
rootPane.revalidate();
}
private void addMenuBar(final JMenuBar bar) {
if (bar != null && unifiedMenuBar) {
if (bar.getParent() != rootPane.getLayeredPane()) {
return;
}
if (menuBarHierarchyListener == null) {
menuBarHierarchyListener = e -> {
if (((e.getChangeFlags() & HierarchyEvent.PARENT_CHANGED) != 0)
&& menuBar != null
&& menuBar.getParent() != null
&& menuBar.getParent() != target) {
removeMenuBar();
}
};
}
this.menuBar = bar;
menuBar.addHierarchyListener(menuBarHierarchyListener);
menuBar.setOpaque(false);
target.add(menuBar);
target.setComponentZOrder(menuBar, target.getComponentCount() - 1);
target.revalidate();
target.repaint();
}
}
private void returnMenuBar() {
JMenuBar oldMenuBar = menuBar;
removeMenuBar();
rootPane.setJMenuBar(oldMenuBar);
}
private void removeMenuBar() {
target.remove(menuBar);
menuBar.removeHierarchyListener(menuBarHierarchyListener);
menuBarHierarchyListener = null;
menuBar.setOpaque(true);
menuBar = null;
}
private ContainerListener createLayeredPaneContainerListener() {
return new ContainerListener() {
@Override
public void componentAdded(final ContainerEvent e) {
if (e.getChild() instanceof JMenuBar) {
addMenuBar(rootPane.getJMenuBar());
}
if (rootPane.getJMenuBar() == null && menuBar != null) {
removeMenuBar();
}
}
@Override
public void componentRemoved(final ContainerEvent e) {}
};
}
private ContainerListener createRootPaneContainerListener() {
return new ContainerListener() {
@Override
public void componentAdded(final ContainerEvent e) {
if (e.getChild() instanceof JLayeredPane) {
((JLayeredPane) e.getChild()).addContainerListener(layeredPaneContainerListener);
}
}
@Override
public void componentRemoved(final ContainerEvent e) {
if (e.getChild() instanceof JLayeredPane) {
((JLayeredPane) e.getChild()).removeContainerListener(layeredPaneContainerListener);
}
}
};
}
}

123
windows/src/main/java/com/github/weisj/darklaf/platform/windows/ui/WindowsTitlePane.java

@ -59,10 +59,8 @@ public class WindowsTitlePane extends CustomTitlePane {
private static final int ICON_SIZE = ICON_WIDTH - 3 * PAD;
private final JRootPane rootPane;
private boolean unifiedMenuBar;
private boolean titleBarHidden;
private ContainerListener rootPaneContainerListener;
private ContainerListener layeredPaneContainerListener;
private final MenuBarStealer menuBarStealer;
private boolean oldResizable;
private PropertyChangeListener windowPropertyChangeListener;
@ -83,7 +81,6 @@ public class WindowsTitlePane extends CustomTitlePane {
private JLabel titleLabel;
private final Window window;
private long windowHandle;
private JMenuBar menuBar;
private int state;
private Color inactiveBackground;
@ -105,81 +102,23 @@ public class WindowsTitlePane extends CustomTitlePane {
this.rootPane = root;
this.window = window;
this.decorationStyle = decorationStyle;
this.menuBarStealer = new MenuBarStealer(rootPane, this);
state = -1;
oldResizable = true;
installSubcomponents();
updateMenuBar(true);
menuBarStealer.install();
updateTitleBarVisibility();
installDefaults();
setLayout(createLayout());
}
private void updateMenuBar(final boolean install) {
unifiedMenuBar = PropertyUtil.getBooleanProperty(rootPane, "JRootPane.unifiedMenuBar");
if (unifiedMenuBar && install) {
if (rootPaneContainerListener == null) {
rootPaneContainerListener = createRootPaneContainerListener();
}
if (layeredPaneContainerListener == null) {
layeredPaneContainerListener = createLayeredPaneContainerListener();
}
rootPane.addContainerListener(rootPaneContainerListener);
rootPane.getLayeredPane().addContainerListener(layeredPaneContainerListener);
addMenuBar(getRootPane().getJMenuBar());
} else {
rootPane.removeContainerListener(rootPaneContainerListener);
rootPane.getLayeredPane().removeContainerListener(layeredPaneContainerListener);
if (menuBar != null) {
getRootPane().setJMenuBar(menuBar);
menuBar.setPreferredSize(null);
menuBar = null;
}
}
rootPane.revalidate();
}
private void updateTitleBarVisibility() {
titleBarHidden = PropertyUtil.getBooleanProperty(rootPane, "JRootPane.hideTitleBar");
rootPane.doLayout();
rootPane.repaint();
}
private ContainerListener createRootPaneContainerListener() {
return new ContainerListener() {
@Override
public void componentAdded(final ContainerEvent e) {
if (e.getChild() instanceof JLayeredPane) {
((JLayeredPane) e.getChild()).addContainerListener(layeredPaneContainerListener);
}
}
@Override
public void componentRemoved(final ContainerEvent e) {
if (e.getChild() instanceof JLayeredPane) {
((JLayeredPane) e.getChild()).removeContainerListener(layeredPaneContainerListener);
}
}
};
}
private ContainerListener createLayeredPaneContainerListener() {
return new ContainerListener() {
@Override
public void componentAdded(final ContainerEvent e) {
if (e.getChild() instanceof JMenuBar) {
addMenuBar(getRootPane().getJMenuBar());
}
if (getRootPane().getJMenuBar() == null && menuBar != null) {
removeMenuBar();
}
}
@Override
public void componentRemoved(final ContainerEvent e) {}
};
}
private static JButton createButton(final Icon icon, final Action action) {
return createButton(icon, action, false);
}
@ -212,7 +151,7 @@ public class WindowsTitlePane extends CustomTitlePane {
uninstallListeners();
uninstallDecorations(removeDecorations);
removeAll();
updateMenuBar(false);
menuBarStealer.uninstall();
}
private void uninstallListeners() {
@ -232,12 +171,6 @@ public class WindowsTitlePane extends CustomTitlePane {
}
windowHandle = 0;
}
rootPane.removeContainerListener(rootPaneContainerListener);
rootPane.getLayeredPane().removeContainerListener(layeredPaneContainerListener);
if (menuBar != null) {
menuBar.setPreferredSize(null);
rootPane.setJMenuBar(menuBar);
}
}
public void install() {
@ -340,23 +273,6 @@ public class WindowsTitlePane extends CustomTitlePane {
setComponentZOrder(titleLabel, 4);
}
protected void addMenuBar(final JMenuBar menuBar) {
if (menuBar != null && unifiedMenuBar) {
this.menuBar = menuBar;
// Otherwise, a white bar will appear where the menuBar used to be.
menuBar.setPreferredSize(new Dimension(0, 0));
menuBar.setOpaque(false);
add(menuBar);
setComponentZOrder(menuBar, 5);
}
}
protected void removeMenuBar() {
remove(menuBar);
menuBar.setOpaque(true);
menuBar = null;
}
private void determineColors() {
switch (getDecorationStyle()) {
case JRootPane.ERROR_DIALOG:
@ -535,7 +451,7 @@ public class WindowsTitlePane extends CustomTitlePane {
}
protected boolean isDrawBorder() {
return getDecorationStyle() != JRootPane.NONE && menuBar != null;
return getDecorationStyle() != JRootPane.NONE && menuBarStealer.hasMenuBar();
}
public JRootPane getRootPane() {
@ -759,9 +675,11 @@ public class WindowsTitlePane extends CustomTitlePane {
if (title == null) title = "";
// e.g. VCLJ achieves fullscreen by hiding the titlebar through jni and setting visibility
// of the menubar.
boolean emptyMenuBar = !unifiedMenuBar || menuBar == null || !menuBar.isVisible();
boolean emptyMenuBar = menuBarStealer.isMenuBarEmpty();
boolean emptyContent = getDecorationStyle() == JRootPane.NONE && emptyMenuBar && title.length() == 0;
return windowHandle == 0 || emptyContent || (menuBar != null && !menuBar.isVisible());
return windowHandle == 0
|| emptyContent
|| (menuBarStealer.hasMenuBar() && !menuBarStealer.getMenuBar().isVisible());
}
@Override
@ -777,8 +695,8 @@ public class WindowsTitlePane extends CustomTitlePane {
if (hideTitleBar()) return 0;
FontMetrics fm = rootPane.getFontMetrics(getFont());
int height = fm.getHeight() + 7;
if (menuBar != null) {
height = Math.max(height, menuBar.getMinimumSize().height);
if (menuBarStealer.hasMenuBar()) {
height = Math.max(height, menuBarStealer.getMenuBar().getMinimumSize().height);
}
return Math.max(BAR_HEIGHT, height);
}
@ -790,7 +708,7 @@ public class WindowsTitlePane extends CustomTitlePane {
width += Math.min(ICON_WIDTH, Math.max(windowIcon.getIconHeight(), windowIcon.getIconWidth()));
width += 2 * PAD;
}
if (menuBar != null) {
if (menuBarStealer.hasMenuBar()) {
width += getPreferredMenuSize().width;
width += PAD;
}
@ -818,12 +736,7 @@ public class WindowsTitlePane extends CustomTitlePane {
}
private Dimension getPreferredMenuSize() {
LayoutManager menuBarLayout = menuBar.getLayout();
Dimension size = null;
if (menuBarLayout != null) {
size = menuBarLayout.preferredLayoutSize(menuBar);
}
return (size != null) ? size : menuBar.getPreferredSize();
return menuBarStealer.getMenuBar().getPreferredSize();
}
private class TitlePaneLayout implements LayoutManager {
@ -868,10 +781,10 @@ public class WindowsTitlePane extends CustomTitlePane {
left = start;
}
if (menuBar != null) {
if (menuBarStealer.hasMenuBar()) {
int menuWidth = getPreferredMenuSize().width;
Insets menuInsets = menuBar.getInsets();
menuBar.setBounds(start, y, menuWidth, height + menuInsets.bottom);
Insets menuInsets = menuBarStealer.getMenuBar().getInsets();
menuBarStealer.getMenuBar().setBounds(start, y, menuWidth, height + menuInsets.bottom);
start += menuWidth + PAD;
left += menuWidth;
}
@ -905,7 +818,7 @@ public class WindowsTitlePane extends CustomTitlePane {
if (!leftToRight) {
mirror(windowIconButton, w);
mirror(menuBar, w);
mirror(menuBarStealer.getMenuBar(), w);
mirror(closeButton, w);
mirror(minimizeButton, w);
mirror(maximizeToggleButton, w);
@ -962,7 +875,7 @@ public class WindowsTitlePane extends CustomTitlePane {
@Override
public void propertyChange(final PropertyChangeEvent evt) {
if ("JRootPane.unifiedMenuBar".equals(evt.getPropertyName())) {
updateMenuBar(true);
menuBarStealer.updateMenuBar(true);
} else if ("JRootPane.hideTitleBar".equals(evt.getPropertyName())) {
updateTitleBarVisibility();
}

Loading…
Cancel
Save