Browse Source

Fixed NPE when reopening a popupmenu.

pull/130/head
weisj 5 years ago
parent
commit
5b2aa9dffd
  1. 141
      core/src/main/java/com/github/weisj/darklaf/ui/menu/DarkMenuItemUIBase.java
  2. 127
      core/src/main/java/com/github/weisj/darklaf/ui/menu/MenuItemLayoutDelegate.java
  3. 4
      core/src/main/java/com/github/weisj/darklaf/ui/popupmenu/DarkPopupMenuUI.java
  4. 39
      core/src/main/java/com/github/weisj/darklaf/ui/togglebutton/radiobutton/DarkRadioButtonMenuItemUI.java
  5. 6
      core/src/main/resources/com/github/weisj/darklaf/properties/ui/menuItem.properties
  6. 2
      core/src/test/java/ui/popupMenu/PopupMenuDemo.java

141
core/src/main/java/com/github/weisj/darklaf/ui/menu/DarkMenuItemUIBase.java

@ -41,6 +41,9 @@ import java.awt.event.ActionEvent;
public class DarkMenuItemUIBase extends BasicMenuItemUI {
protected int acceleratorTextOffset;
protected boolean useEvenHeight;
public static ComponentUI createUI(final JComponent c) {
return new DarkMenuItemUIBase();
}
@ -52,11 +55,20 @@ public class DarkMenuItemUIBase extends BasicMenuItemUI {
@Override
public void installUI(final JComponent c) {
super.installUI(c);
useEvenHeight = !Boolean.TRUE.equals(UIManager.get(getPropertyPrefix() + ".evenHeight"));
acceleratorTextOffset = UIManager.getInt(getPropertyPrefix() + ".acceleratorTextOffset");
acceleratorFont = UIManager.getFont("MenuItem.font");
acceleratorForeground = UIManager.getColor("MenuItem.foreground");
acceleratorSelectionForeground = UIManager.getColor("MenuItem.selectionForeground");
}
@Override
protected void installKeyboardActions() {
super.installKeyboardActions();
LazyActionMap.installLazyActionMap(menuItem, DarkMenuItemUIBase.class,
getPropertyPrefix() + ".actionMap");
}
public void paint(final Graphics g, final JComponent c) {
paintMenuItem(g, c, checkIcon, arrowIcon,
selectionBackground, isSelected(c) ? selectionForeground : c.getForeground(),
@ -92,81 +104,88 @@ public class DarkMenuItemUIBase extends BasicMenuItemUI {
Rectangle viewRect = new Rectangle(0, 0, mi.getWidth(), mi.getHeight());
DarkUIUtil.applyInsets(viewRect, mi.getInsets());
MenuItemLayoutHelper lh = new MenuItemLayoutHelper(mi, checkIcon,
arrowIcon, viewRect, defaultTextIconGap,
acceleratorDelimiter,
mi.getComponentOrientation().isLeftToRight(), mi.getFont(),
acceleratorFont,
MenuItemLayoutHelper.useCheckAndArrow(menuItem),
getPropertyPrefix());
MenuItemLayoutHelper lh = getMenuItemLayoutHelper(checkIcon, arrowIcon, defaultTextIconGap, mi, viewRect);
MenuItemLayoutHelper.LayoutResult lr = lh.layoutMenuItem();
paintBackground(g, mi, background);
paintCheckIcon(g, lh, lr, holdc, foreground);
paintIcon(g, lh, lr, holdc);
paintCheckIcon(g, mi, lh, lr, holdc, foreground);
paintIcon(g, mi, lh, lr, holdc);
g.setColor(foreground);
paintText(g, lh, lr);
paintAccText(g, lh, lr);
paintArrowIcon(g, lh, lr, foreground);
paintText(g, mi, lh, lr);
paintAccText(g, mi, lh, lr);
paintArrowIcon(g, mi, lh, lr, foreground);
// Restore original graphics font and color
g.setColor(holdc);
g.setFont(holdf);
}
protected void paintCheckIcon(final Graphics g, final MenuItemLayoutHelper lh,
protected MenuItemLayoutHelper getMenuItemLayoutHelper(final Icon checkIcon, final Icon arrowIcon,
final int defaultTextIconGap,
final JMenuItem mi, final Rectangle viewRect) {
return new MenuItemLayoutHelper(mi, checkIcon,
arrowIcon, viewRect, defaultTextIconGap,
acceleratorDelimiter,
mi.getComponentOrientation().isLeftToRight(), mi.getFont(),
acceleratorFont,
MenuItemLayoutHelper.useCheckAndArrow(menuItem),
getPropertyPrefix());
}
protected void paintCheckIcon(final Graphics g, final JMenuItem mi, final MenuItemLayoutHelper lh,
final MenuItemLayoutHelper.LayoutResult lr,
final Color holdc, final Color foreground) {
if (lh.getCheckIcon() != null) {
ButtonModel model = lh.getMenuItem().getModel();
if (model.isArmed() || (lh.getMenuItem() instanceof JMenu
ButtonModel model = mi.getModel();
if (model.isArmed() || (mi instanceof JMenu
&& model.isSelected())) {
g.setColor(foreground);
} else {
g.setColor(holdc);
}
if (lh.useCheckAndArrow()) {
lh.getCheckIcon().paintIcon(lh.getMenuItem(), g,
lh.getCheckIcon().paintIcon(mi, g,
lr.getCheckRect().x, lr.getCheckRect().y);
}
g.setColor(holdc);
}
}
protected void paintAccText(final Graphics g, final MenuItemLayoutHelper lh,
protected void paintAccText(final Graphics g, final JMenuItem mi,
final MenuItemLayoutHelper lh,
final MenuItemLayoutHelper.LayoutResult lr) {
GraphicsContext config = GraphicsUtil.setupAntialiasing(g);
rightAlignAccText(lh, lr);
if (!StringUtil.isBlank(lh.getAccText())) {
ButtonModel model = lh.getMenuItem().getModel();
ButtonModel model = mi.getModel();
g.setFont(lh.getAccFontMetrics().getFont());
if (!model.isEnabled()) {
// *** paint the accText disabled
if (disabledForeground != null) {
g.setColor(disabledForeground);
SwingUtilities2.drawString(lh.getMenuItem(), g,
SwingUtilities2.drawString(mi, g,
lh.getAccText(), lr.getAccRect().x,
lr.getAccRect().y + lh.getAccFontMetrics().getAscent());
} else {
g.setColor(lh.getMenuItem().getBackground().brighter());
SwingUtilities2.drawString(lh.getMenuItem(), g,
g.setColor(mi.getBackground().brighter());
SwingUtilities2.drawString(mi, g,
lh.getAccText(), lr.getAccRect().x,
lr.getAccRect().y + lh.getAccFontMetrics().getAscent());
g.setColor(lh.getMenuItem().getBackground().darker());
SwingUtilities2.drawString(lh.getMenuItem(), g,
g.setColor(mi.getBackground().darker());
SwingUtilities2.drawString(mi, g,
lh.getAccText(), lr.getAccRect().x - 1,
lr.getAccRect().y + lh.getFontMetrics().getAscent() - 1);
}
} else {
// *** paint the accText normally
if (model.isArmed()
|| (lh.getMenuItem() instanceof JMenu
|| (mi instanceof JMenu
&& model.isSelected())) {
g.setColor(acceleratorSelectionForeground);
} else {
g.setColor(acceleratorForeground);
}
SwingUtilities2.drawString(lh.getMenuItem(), g, lh.getAccText(),
SwingUtilities2.drawString(mi, g, lh.getAccText(),
lr.getAccRect().x, lr.getAccRect().y +
lh.getAccFontMetrics().getAscent());
}
@ -174,31 +193,32 @@ public class DarkMenuItemUIBase extends BasicMenuItemUI {
config.restore();
}
protected void paintIcon(final Graphics g, final MenuItemLayoutHelper lh,
protected void paintIcon(final Graphics g, final JMenuItem mi, final MenuItemLayoutHelper lh,
final MenuItemLayoutHelper.LayoutResult lr, final Color holdc) {
if (lh.getIcon() != null) {
Icon icon;
ButtonModel model = lh.getMenuItem().getModel();
ButtonModel model = mi.getModel();
if (!model.isEnabled()) {
icon = lh.getMenuItem().getDisabledIcon();
icon = mi.getDisabledIcon();
} else if (model.isPressed() && model.isArmed()) {
icon = lh.getMenuItem().getPressedIcon();
icon = mi.getPressedIcon();
if (icon == null) {
// Use default icon
icon = lh.getMenuItem().getIcon();
icon = mi.getIcon();
}
} else {
icon = lh.getMenuItem().getIcon();
icon = mi.getIcon();
}
if (icon != null) {
icon.paintIcon(lh.getMenuItem(), g, lr.getIconRect().x, lr.getIconRect().y);
icon.paintIcon(mi, g, lr.getIconRect().x, lr.getIconRect().y);
g.setColor(holdc);
}
}
}
protected void paintText(final Graphics g, final MenuItemLayoutHelper lh,
protected void paintText(final Graphics g, final JMenuItem mi,
final MenuItemLayoutHelper lh,
final MenuItemLayoutHelper.LayoutResult lr) {
GraphicsContext config = GraphicsUtil.setupAntialiasing(g);
if (!StringUtil.isBlank(lh.getText())) {
@ -207,23 +227,24 @@ public class DarkMenuItemUIBase extends BasicMenuItemUI {
lh.getHtmlView().paint(g, lr.getTextRect());
} else {
// Text isn't HTML
paintText(g, lh.getMenuItem(), lr.getTextRect(), lh.getText());
paintText(g, mi, lr.getTextRect(), lh.getText());
}
}
config.restore();
}
protected void paintArrowIcon(final Graphics g, final MenuItemLayoutHelper lh,
protected void paintArrowIcon(final Graphics g, final JMenuItem mi,
final MenuItemLayoutHelper lh,
final MenuItemLayoutHelper.LayoutResult lr,
final Color foreground) {
if (lh.getArrowIcon() != null) {
ButtonModel model = lh.getMenuItem().getModel();
if (model.isArmed() || (lh.getMenuItem() instanceof JMenu
ButtonModel model = mi.getModel();
if (model.isArmed() || (mi instanceof JMenu
&& model.isSelected())) {
g.setColor(foreground);
}
if (lh.useCheckAndArrow()) {
lh.getArrowIcon().paintIcon(lh.getMenuItem(), g,
lh.getArrowIcon().paintIcon(mi, g,
lr.getArrowRect().x, lr.getArrowRect().y);
}
}
@ -254,6 +275,50 @@ public class DarkMenuItemUIBase extends BasicMenuItemUI {
}
}
@Override
protected Dimension getPreferredMenuItemSize(final JComponent c,
final Icon checkIcon,
final Icon arrowIcon,
final int defaultTextIconGap) {
JMenuItem mi = (JMenuItem) c;
MenuItemLayoutHelper lh = getMenuItemLayoutHelper(checkIcon, arrowIcon, defaultTextIconGap, mi,
MenuItemLayoutHelper.createMaxRect());
Dimension result = new Dimension();
// Calculate the result width
result.width = lh.getLeadingGap();
MenuItemLayoutHelper.addMaxWidth(lh.getCheckSize(), lh.getAfterCheckIconGap(), result);
// Take into account minimal text offset.
if ((!lh.isTopLevelMenu())
&& (lh.getMinTextOffset() > 0)
&& (result.width < lh.getMinTextOffset())) {
result.width = lh.getMinTextOffset();
}
MenuItemLayoutHelper.addMaxWidth(lh.getLabelSize(), acceleratorTextOffset, result);
MenuItemLayoutHelper.addMaxWidth(lh.getAccSize(), acceleratorTextOffset, result);
MenuItemLayoutHelper.addMaxWidth(lh.getArrowSize(), lh.getGap(), result);
// Calculate the result height
result.height = MenuItemLayoutHelper.max(lh.getCheckSize().getHeight(), lh.getLabelSize().getHeight(),
lh.getAccSize().getHeight(), lh.getArrowSize().getHeight());
// Take into account menu item insets
Insets insets = mi.getInsets();
if (insets != null) {
result.width += insets.left + insets.right;
result.height += insets.top + insets.bottom;
}
// if the height is even, bump it up one. This is critical.
// for the text to center properly
if (result.height % 2 == 0 && useEvenHeight) {
result.height++;
}
return result;
}
protected static class Actions extends UIAction {
protected static final String CLICK = "doClick";

127
core/src/main/java/com/github/weisj/darklaf/ui/menu/MenuItemLayoutDelegate.java

@ -0,0 +1,127 @@
/*
* MIT License
*
* Copyright (c) 2020 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.ui.menu;
import com.github.weisj.darklaf.ui.html.DarkHTML;
import javax.swing.*;
import java.awt.*;
import java.awt.font.TextAttribute;
public class MenuItemLayoutDelegate extends JMenuItem {
private JMenuItem delegate;
public MenuItemLayoutDelegate(final JMenuItem delegate) {
setDelegate(delegate);
}
public void setDelegate(final JMenuItem delegate) {
this.delegate = delegate;
if (delegate != null) {
putClientProperty(DarkHTML.propertyKey, delegate.getClientProperty(DarkHTML.propertyKey));
putClientProperty(TextAttribute.NUMERIC_SHAPING, delegate.getClientProperty(TextAttribute.NUMERIC_SHAPING));
}
}
@Override
public Insets getInsets(final Insets insets) {
if (delegate != null) return delegate.getInsets(insets);
return super.getInsets(insets);
}
@Override
public Insets getInsets() {
if (delegate != null) return delegate.getInsets();
return super.getInsets();
}
@Override
public Font getFont() {
if (delegate != null) return delegate.getFont();
return super.getFont();
}
@Override
public ComponentOrientation getComponentOrientation() {
if (delegate != null) return delegate.getComponentOrientation();
return super.getComponentOrientation();
}
@Override
public Container getParent() {
if (delegate != null) return delegate.getParent();
return super.getParent();
}
@Override
public KeyStroke getAccelerator() {
if (delegate != null) return delegate.getAccelerator();
return super.getAccelerator();
}
@Override
public int getVerticalAlignment() {
if (delegate != null) return delegate.getVerticalAlignment();
return super.getVerticalAlignment();
}
@Override
public int getHorizontalAlignment() {
if (delegate != null) return delegate.getHorizontalAlignment();
return super.getHorizontalAlignment();
}
@Override
public int getVerticalTextPosition() {
if (delegate != null) return delegate.getVerticalTextPosition();
return super.getVerticalTextPosition();
}
@Override
public int getHorizontalTextPosition() {
if (delegate != null) return delegate.getHorizontalTextPosition();
return super.getHorizontalTextPosition();
}
@Override
public FontMetrics getFontMetrics(final Font font) {
if (delegate != null) return delegate.getFontMetrics(font);
return super.getFontMetrics(font);
}
@Override
public Icon getIcon() {
if (delegate != null) return delegate.getIcon();
return super.getIcon();
}
@Override
public String getText() {
if (delegate != null) return delegate.getText();
return super.getText();
}
}

4
core/src/main/java/com/github/weisj/darklaf/ui/popupmenu/DarkPopupMenuUI.java

@ -122,7 +122,6 @@ public class DarkPopupMenuUI extends BasicPopupMenuUI {
*/
private void removeOldMouseGrabber() {
MenuSelectionManager menuSelectionManager = MenuSelectionManager.defaultManager();
ChangeListener mouseGrabber = null;
for (ChangeListener listener : menuSelectionManager.getChangeListeners()) {
if (listener == null) continue;
Class<?> listenerClass = listener.getClass();
@ -130,11 +129,10 @@ public class DarkPopupMenuUI extends BasicPopupMenuUI {
Class<?> enclosingClass = listenerClass.getEnclosingClass();
if (enclosingClass == null) continue;
if (enclosingClass.getName().endsWith("BasicPopupMenuUI")) {
mouseGrabber = listener;
menuSelectionManager.removeChangeListener(listener);
break;
}
}
menuSelectionManager.removeChangeListener(mouseGrabber);
}
public static class MouseGrabber implements ChangeListener,

39
core/src/main/java/com/github/weisj/darklaf/ui/togglebutton/radiobutton/DarkRadioButtonMenuItemUI.java

@ -25,6 +25,7 @@ package com.github.weisj.darklaf.ui.togglebutton.radiobutton;
import com.github.weisj.darklaf.decorators.MouseClickListener;
import com.github.weisj.darklaf.ui.menu.DarkMenuItemUIBase;
import com.github.weisj.darklaf.ui.menu.MenuItemLayoutDelegate;
import com.github.weisj.darklaf.ui.togglebutton.ToggleButtonMenuItemConstants;
import sun.swing.MenuItemLayoutHelper;
@ -41,6 +42,7 @@ public class DarkRadioButtonMenuItemUI extends DarkMenuItemUIBase implements Tog
if (menuItem != null) menuItem.setArmed(true);
});
protected MenuItemLayoutDelegate layoutDelegate = new ToggleButtonMenuItemLayoutDelegate(null);
protected int iconBaselineOffset;
private Icon stateIcon;
@ -87,28 +89,43 @@ public class DarkRadioButtonMenuItemUI extends DarkMenuItemUIBase implements Tog
}
@Override
protected Dimension getPreferredMenuItemSize(final JComponent c, final Icon checkIcon, final Icon arrowIcon,
final int defaultTextIconGap) {
if (c instanceof JMenuItem && ((JMenuItem) c).getIcon() == null) {
JMenuItem mi = (JMenuItem) c;
mi.setIcon(checkIcon);
Dimension dim = super.getPreferredMenuItemSize(c, null, arrowIcon, defaultTextIconGap);
mi.setIcon(null);
return dim;
protected MenuItemLayoutHelper getMenuItemLayoutHelper(final Icon checkIcon, final Icon arrowIcon,
final int defaultTextIconGap, final JMenuItem mi,
final Rectangle viewRect) {
if (mi.getIcon() == null) {
layoutDelegate.setDelegate(mi);
return super.getMenuItemLayoutHelper(null, arrowIcon, defaultTextIconGap, layoutDelegate, viewRect);
} else {
return super.getPreferredMenuItemSize(c, checkIcon, arrowIcon, defaultTextIconGap);
return super.getMenuItemLayoutHelper(checkIcon, arrowIcon, defaultTextIconGap, mi, viewRect);
}
}
@Override
protected void paintCheckIcon(final Graphics g2, final MenuItemLayoutHelper lh,
protected void paintCheckIcon(final Graphics g2, final JMenuItem mi,
final MenuItemLayoutHelper lh,
final MenuItemLayoutHelper.LayoutResult lr,
final Color holdc, final Color foreground) {
Rectangle rect = lr.getCheckRect();
getStateIcon(lh.getMenuItem()).paintIcon(lh.getMenuItem(), g2, rect.x, rect.y + iconBaselineOffset);
if (mi.getIcon() == null) {
rect.y = lr.getIconRect().y;
rect.height = lr.getIconRect().height;
}
getStateIcon(mi).paintIcon(mi, g2, rect.x, rect.y + iconBaselineOffset);
}
protected Icon getStateIcon(final AbstractButton b) {
return stateIcon;
}
protected class ToggleButtonMenuItemLayoutDelegate extends MenuItemLayoutDelegate {
public ToggleButtonMenuItemLayoutDelegate(final JMenuItem delegate) {
super(delegate);
}
@Override
public Icon getIcon() {
return checkIcon;
}
}
}

6
core/src/main/resources/com/github/weisj/darklaf/properties/ui/menuItem.properties

@ -81,12 +81,12 @@ Menu.arrowIcon = navigation/arrowRight.svg[t
Menu.arrowHover.icon = navigation/arrowRightHover.svg[themed]
Menu.checkIcon = empty(0,0)
MenuItem.checkIcon = empty(0,0)
MenuItem.arrowIcon = empty(7,7)
MenuItem.arrowIcon = empty(0,0)
CheckBoxMenuItem.checkIcon = empty(19,19)
CheckBoxMenuItem.arrowIcon = empty(16,16)
CheckBoxMenuItem.arrowIcon = empty(0,0)
CheckBoxMenuItem.dashIcon = empty(0,0)
RadioButtonMenuItem.checkIcon = empty(19,19)
RadioButtonMenuItem.arrowIcon = empty(16,16)
RadioButtonMenuItem.arrowIcon = empty(0,0)
RadioButtonMenuItem.dashIcon = empty(0,0)

2
core/src/test/java/ui/popupMenu/PopupMenuDemo.java

@ -63,7 +63,7 @@ public class PopupMenuDemo implements ComponentDemo {
add(new JMenuItem("Item 1", icon) {{
setAccelerator(KeyStroke.getKeyStroke("alt A"));
}});
add(new JCheckBoxMenuItem("CheckBox", icon));
add(new JCheckBoxMenuItem("CheckBox"));
add(new JMenuItem("Item 2") {{
setAccelerator(KeyStroke.getKeyStroke("alt shift B"));
}});

Loading…
Cancel
Save