As reported in Bug#23527 the accelerators were not painted (correctly) in the current BasicMenuItemUI. I looked at the code and found that it was quite messed up. I moved the code around, rewrote parts of it and fixed the issue. The accelerators are still not painted 100% correctly, but I believe that this is caused by the getPreferredSize() returning too small dimension (the accelerators are positioned relative to the right border of the menu item). I will look into this soon.
2006-03-29 Roman Kennke <[EMAIL PROTECTED]> PR 23527 * javax/swing/plaf/basic/BasicMenuItemUI.java (viewRect): New field. (textRect): New field. (accelRect): New field. (iconRect): New field. (arrowIconRect): New field. (checkIconRect): New field. (BasicMenuItemUI): Initialize new fields. (paintMenuItem): Rewritten to correctly layout and paint the menu item in a more straightforward way. Use cached rectangle objects for layout. (paintAccelerator): Pulled inside the paintMenuItem method. /Roman -- “Improvement makes straight roads, but the crooked roads, without Improvement, are roads of Genius.” - William Blake
Index: javax/swing/plaf/basic/BasicMenuItemUI.java =================================================================== RCS file: /cvsroot/classpath/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java,v retrieving revision 1.44 diff -u -1 -0 -r1.44 BasicMenuItemUI.java --- javax/swing/plaf/basic/BasicMenuItemUI.java 28 Mar 2006 10:35:05 -0000 1.44 +++ javax/swing/plaf/basic/BasicMenuItemUI.java 29 Mar 2006 11:56:54 -0000 @@ -75,20 +75,21 @@ import javax.swing.UIManager; import javax.swing.event.MenuDragMouseEvent; import javax.swing.event.MenuDragMouseListener; import javax.swing.event.MenuKeyEvent; import javax.swing.event.MenuKeyListener; import javax.swing.event.MouseInputListener; import javax.swing.plaf.ActionMapUIResource; import javax.swing.plaf.ComponentInputMapUIResource; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.MenuItemUI; +import javax.swing.text.View; /** * UI Delegate for JMenuItem. */ public class BasicMenuItemUI extends MenuItemUI { /** * Font to be used when displaying menu item's accelerator. */ protected Font acceleratorFont; @@ -176,21 +177,51 @@ */ private int defaultAcceleratorLabelGap = 10; /** * The gap between different menus on the MenuBar. */ private int MenuGap = 10; /** A PropertyChangeListener to make UI updates after property changes **/ PropertyChangeHandler propertyChangeListener; - + + /** + * The view rectangle used for layout of the menu item. + */ + private Rectangle viewRect; + + /** + * The rectangle that holds the area of the label. + */ + private Rectangle textRect; + + /** + * The rectangle that holds the area of the accelerator. + */ + private Rectangle accelRect; + + /** + * The rectangle that holds the area of the icon. + */ + private Rectangle iconRect; + + /** + * The rectangle that holds the area of the icon. + */ + private Rectangle arrowIconRect; + + /** + * The rectangle that holds the area of the check icon. + */ + private Rectangle checkIconRect; + /** * A class to handle PropertChangeEvents for the JMenuItem * @author Anthony Balkissoon abalkiss at redhat dot com. */ class PropertyChangeHandler implements PropertyChangeListener { /** * This method is called when a property of the menuItem is changed. * Currently it is only used to update the accelerator key bindings. * @@ -235,20 +266,28 @@ /** * Creates a new BasicMenuItemUI object. */ public BasicMenuItemUI() { mouseInputListener = createMouseInputListener(menuItem); menuDragMouseListener = createMenuDragMouseListener(menuItem); menuKeyListener = createMenuKeyListener(menuItem); itemListener = new ItemHandler(); propertyChangeListener = new PropertyChangeHandler(); + + // Initialize rectangles for layout. + viewRect = new Rectangle(); + textRect = new Rectangle(); + iconRect = new Rectangle(); + arrowIconRect = new Rectangle(); + checkIconRect = new Rectangle(); + accelRect = new Rectangle(); } /** * Create MenuDragMouseListener to listen for mouse dragged events. * * @param c * menu item to listen to * @return The MenuDragMouseListener */ protected MenuDragMouseListener createMenuDragMouseListener(JComponent c) @@ -590,101 +629,209 @@ * @param foreground * Foreground color of the menu item * @param defaultTextIconGap * space to use between icon and text when painting menu item */ protected void paintMenuItem(Graphics g, JComponent c, Icon checkIcon, Icon arrowIcon, Color background, Color foreground, int defaultTextIconGap) { JMenuItem m = (JMenuItem) c; - Rectangle tr = new Rectangle(); // text rectangle - Rectangle ir = new Rectangle(); // icon rectangle - Rectangle vr = new Rectangle(); // view rectangle - Rectangle br = new Rectangle(); // border rectangle - Rectangle ar = new Rectangle(); // accelerator rectangle - Rectangle cr = new Rectangle(); // checkIcon rectangle - - int vertAlign = m.getVerticalAlignment(); - int horAlign = m.getHorizontalAlignment(); - int vertTextPos = m.getVerticalTextPosition(); - int horTextPos = m.getHorizontalTextPosition(); - - Font f = m.getFont(); - g.setFont(f); - FontMetrics fm = g.getFontMetrics(f); - SwingUtilities.calculateInnerArea(m, vr); - paintBackground(g, m, background); + int width = m.getWidth(); + int height = m.getHeight(); - /* - * MenuItems insets are equal to menuItems margin, space between text and - * menuItems border. We need to paint insets region as well. - */ + // Reset rectangles. + iconRect.setBounds(0, 0, 0, 0); + textRect.setBounds(0, 0, 0, 0); + accelRect.setBounds(0, 0, 0, 0); + checkIconRect.setBounds(0, 0, 0, 0); + arrowIconRect.setBounds(0, 0, 0, 0); + viewRect.setBounds(0, 0, width, height); + + // Substract insets to the view rect. Insets insets = m.getInsets(); - br.x -= insets.left; - br.y -= insets.top; - br.width += insets.right + insets.left; - br.height += insets.top + insets.bottom; + viewRect.x += insets.left; + viewRect.y += insets.top; + viewRect.width -= (insets.left + insets.right); + viewRect.height -= (insets.top + insets.bottom); + + // Fetch fonts. + Font oldFont = g.getFont(); + Font font = c.getFont(); + g.setFont(font); + FontMetrics fm = m.getFontMetrics(font); + FontMetrics accelFm = m.getFontMetrics(acceleratorFont); + + // Create accelerator string. + KeyStroke accel = m.getAccelerator(); + String accelText = ""; + if (accel != null) + { + int mods = accel.getModifiers(); + if (mods > 0) + { + accelText = KeyEvent.getKeyModifiersText(mods); + accelText += acceleratorDelimiter; + } + int keycode = accel.getKeyCode(); + if (keycode != 0) + accelText += KeyEvent.getKeyText(keycode); + else + accelText += accel.getKeyChar(); + } + // Layout text and icon. + String text = m.getText(); + SwingUtilities.layoutCompoundLabel(m, fm, text, m.getIcon(), + m.getVerticalAlignment(), + m.getHorizontalAlignment(), + m.getVerticalTextPosition(), + m.getHorizontalTextPosition(), + viewRect, iconRect, textRect, + defaultTextIconGap); - // If this menu item is a JCheckBoxMenuItem then paint check icon - if (checkIcon != null) + // Initialize accelerator width and height. + if (! accelText.equals("")) { - SwingUtilities.layoutCompoundLabel(m, fm, null, checkIcon, vertAlign, - horAlign, vertTextPos, horTextPos, - vr, cr, tr, defaultTextIconGap); - checkIcon.paintIcon(m, g, cr.x, cr.y); - // We need to calculate position of the menu text and position of - // user menu icon if there exists one relative to the check icon. - // So we need to adjust view rectangle s.t. its starting point is at - // checkIcon.width + defaultTextIconGap. - vr.x = cr.x + cr.width + defaultTextIconGap; + accelRect.width = accelFm.stringWidth(accelText); + accelRect.height = accelFm.getHeight(); } - // if this is a submenu, then paint arrow icon to indicate it. - if (arrowIcon != null && (c instanceof JMenu)) + // Initialize check and arrow icon width and height. + if (! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu())) { - if (!((JMenu) c).isTopLevelMenu()) + if (checkIcon != null) { - int width = arrowIcon.getIconWidth(); - int height = arrowIcon.getIconHeight(); - int offset = (vr.height - height) / 2; - arrowIcon.paintIcon(m, g, vr.width - width, vr.y + offset); + checkIconRect.width = checkIcon.getIconWidth(); + checkIconRect.height = checkIcon.getIconHeight(); } + if (arrowIcon != null) + { + arrowIconRect.width = arrowIcon.getIconWidth(); + arrowIconRect.height = arrowIcon.getIconHeight(); + } + } + + Rectangle labelRect = iconRect.union(textRect); + textRect.x += defaultTextIconGap; + iconRect.x += defaultTextIconGap; + + // Layout accelerator rect. + accelRect.x = viewRect.x + viewRect.width - arrowIconRect.width + - defaultTextIconGap - accelRect.width; + // Layout check and arrow icons only when not in toplevel menu. + if (! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu())) + { + checkIconRect.x = viewRect.x + defaultTextIconGap; + textRect.x += defaultTextIconGap + checkIconRect.width; + iconRect.x += defaultTextIconGap + checkIconRect.width; + arrowIconRect.x = viewRect.x + viewRect.width - defaultTextIconGap + - arrowIconRect.width; + } + + // Align the accelerator text and all the icons vertically centered to + // the menu text. + accelRect.y = labelRect.y + (labelRect.height / 2) + - (accelRect.height / 2); + if (! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu())) + { + arrowIconRect.y = labelRect.y + (labelRect.height / 2) + - (arrowIconRect.height / 2); + checkIconRect.y = labelRect.y + (labelRect.height / 2) + - (checkIconRect.height / 2); + } + + // Paint the background. + paintBackground(g, m, background); + + Color oldColor = g.getColor(); + + // Paint the check icon. + if (checkIcon != null) + { + checkIcon.paintIcon(m, g, checkIconRect.x, checkIconRect.y); } - // paint text and user menu icon if it exists - Icon i = m.getIcon(); - SwingUtilities.layoutCompoundLabel(c, fm, m.getText(), i, vertAlign, - horAlign, vertTextPos, horTextPos, vr, - ir, tr, defaultTextIconGap); - if (i != null) - i.paintIcon(c, g, ir.x, ir.y); - paintText(g, m, tr, m.getText()); + // Paint the icon. + ButtonModel model = m.getModel(); + if (m.getIcon() != null) + { + // Determine icon depending on the menu item + // state (normal/disabled/pressed). + Icon icon; + if (! m.isEnabled()) + { + icon = m.getDisabledIcon(); + } + else if (model.isPressed() && model.isArmed()) + { + icon = m.getPressedIcon(); + if (icon == null) + { + icon = m.getIcon(); + } + } + else + { + icon = m.getIcon(); + } - // paint accelerator - String acceleratorText = ""; + if (icon != null) + { + icon.paintIcon(m, g, iconRect.x, iconRect.y); + } + } - if (m.getAccelerator() != null) + // Paint the text. + if (text != null) { - acceleratorText = getAcceleratorText(m.getAccelerator()); - fm = g.getFontMetrics(acceleratorFont); - ar.width = fm.stringWidth(acceleratorText); - ar.x = br.width - ar.width; - vr.x = br.width - ar.width - defaultTextIconGap; - - SwingUtilities.layoutCompoundLabel(m, fm, acceleratorText, null, - vertAlign, horAlign, vertTextPos, - horTextPos, vr, ir, ar, - defaultTextIconGap); + // Handle HTML. + View html = (View) m.getClientProperty(BasicHTML.propertyKey); + if (html != null) + { + html.paint(g, textRect); + } + else + { + paintText(g, m, textRect, text); + } + } - paintAccelerator(g, m, ar, acceleratorText); + // Paint accelerator text. + if (! accelText.equals("")) + { + g.setFont(acceleratorFont); + if (! m.isEnabled()) + { + // Paint accelerator disabled. + g.setColor(disabledForeground); + } + else + { + if (m.isArmed() || (m instanceof JMenu && m.isSelected())) + g.setColor(acceleratorSelectionForeground); + else + g.setColor(acceleratorForeground); + } + g.drawString(accelText, accelRect.x, + accelRect.y + accelFm.getAscent()); } + + // Paint arrow. + if (arrowIcon != null + && ! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu())) + { + arrowIcon.paintIcon(m, g, arrowIconRect.x, arrowIconRect.y); + } + + g.setFont(oldFont); + g.setColor(oldColor); + } /** * Paints label for the given menu item * * @param g * The graphics context used to paint this menu item * @param menuItem * menu item for which to draw its label * @param textRect @@ -855,51 +1002,20 @@ * @return $Rectangle$ reactangle which will be used to display accelerator */ private Rectangle getAcceleratorRect(KeyStroke accelerator, FontMetrics fm) { int width = fm.stringWidth(getAcceleratorText(accelerator)); int height = fm.getHeight(); return new Rectangle(0, 0, width, height); } /** - * Paints accelerator inside menu item - * - * @param g - * The graphics context used to paint the border - * @param menuItem - * Menu item for which to draw accelerator - * @param acceleratorRect - * rectangle representing position of the accelerator relative to the - * menu item - * @param acceleratorText - * accelerator's text - */ - private void paintAccelerator(Graphics g, JMenuItem menuItem, - Rectangle acceleratorRect, - String acceleratorText) - { - g.setFont(acceleratorFont); - FontMetrics fm = g.getFontMetrics(acceleratorFont); - - if (menuItem.isEnabled()) - g.setColor(acceleratorForeground); - else - // FIXME: should fix this to use 'disabledForeground', but its - // default value in BasicLookAndFeel is null. - g.setColor(Color.gray); - - BasicGraphicsUtils.drawString(g, acceleratorText, 0, acceleratorRect.x, - acceleratorRect.y + fm.getAscent()); - } - - /** * This class handles mouse events occuring inside the menu item. Most of the * events are forwarded for processing to MenuSelectionManager of the current * menu hierarchy. */ protected class MouseInputHandler implements MouseInputListener { /** * Creates a new MouseInputHandler object. */ protected MouseInputHandler()
signature.asc
Description: Dies ist ein digital signierter Nachrichtenteil