• category: swing folder: MenuItemAcceleratorAlignment title: JMenuItemのAccelerator表示を右揃えにする tags: [JMenuItem, Locale, KeyEvent, ResourceBundle, Alignment, GridBagLayout] author: aterai pubdate: 2013-09-30T05:06:53+09:00 description: JMenuItemのAccelerator表示を右揃えに変更します。 image: https://lh5.googleusercontent.com/-vl8nnt_tGvQ/UkhDq1YV-VI/AAAAAAAAB2o/B0-hGdv35Ns/s800/MenuItemAcceleratorAlignment.png hreflang:
       href: https://java-swing-tips.blogspot.com/2014/10/jmenuitem-accelerator-text-alignment.html
       lang: en

概要

JMenuItemAccelerator表示を右揃えに変更します。

サンプルコード

public static void paintAccText(
  Graphics g, MenuItemLayoutHelper lh, MenuItemLayoutHelper.LayoutResult lr,
  Color disabledForeground, Color acceleratorForeground,
  Color acceleratorSelectionForeground) {
  if (!lh.getAccText().equals("")) {
    ButtonModel model = lh.getMenuItem().getModel();
    g.setFont(lh.getAccFontMetrics().getFont());
    if (!model.isEnabled()) {
      // *** paint the accText disabled
      if (disabledForeground != null) {
        g.setColor(disabledForeground);
        SwingUtilities2.drawString(
          lh.getMenuItem(), g, lh.getAccText(),
          lr.getAccRect().x,
          lr.getAccRect().y + lh.getAccFontMetrics().getAscent());
      } else {
        g.setColor(lh.getMenuItem().getBackground().brighter());
        SwingUtilities2.drawString(
          lh.getMenuItem(), g, lh.getAccText(),
          lr.getAccRect().x,
          lr.getAccRect().y + lh.getAccFontMetrics().getAscent());
        g.setColor(lh.getMenuItem().getBackground().darker());
        SwingUtilities2.drawString(
          lh.getMenuItem(), 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 && model.isSelected())) {
        g.setColor(acceleratorSelectionForeground);
      } else {
        g.setColor(acceleratorForeground);
      }
      SwingUtilities2.drawString(
        lh.getMenuItem(), g, lh.getAccText(),
        // lr.getAccRect().x, >>>
        lh.getViewRect().x + lh.getViewRect().width
        - lh.getMenuItem().getIconTextGap() - lr.getAccRect().width,
        // <<<
        lr.getAccRect().y + lh.getAccFontMetrics().getAscent());
    }
  }
}
View in GitHub: Java, Kotlin

解説

  • Default
    • デフォルトでは、WindowsLookAndFeelの場合でもAcceleratorは左寄せで表示される
  • RightAcc
    • BasicMenuItemUI#paintMenuItem(...)メソッドをオーバーライドして、JMenuItemAcceleratorを左寄せではなく右寄せで表示
    • Web Startで実行するとjava.security.AccessControlException: access denied ("java.lang.RuntimePermission" "accessClassInPackage.sun.swing")と例外が発生してメニューが表示されない
    • sun.swing.MenuItemLayoutHelperや、sun.swing.MenuItemLayoutHelper.LayoutResultなどの内部所有のAPIを使用しているので、今後も使用できるか不明
    • paintText(...)paintCheckIcon(...)paintIcon(...)paintArrowIcon(...)などのBasicMenuItemUIのプライベートなメソッドをほぼそのままコピーして使用している
    • ComponentOrientation.RIGHT_TO_LEFTが設定されて、文字列が右から左に配置される場合は考慮していない

  • EastAcc
    • Windows 7環境のWindowsLookAndFeelでのみ正常に動作?
    • JMenuItemGridBagLayoutを設定し、c.anchor = GridBagConstraints.EAST;で、Accelerator文字列を設定したJLabelを配置
    • 本来のAccelerator文字列は、UIManager.put("MenuItem.acceleratorForeground", background);などで背景色と同化して非表示
    • 参考: JMenuItemの内部にJButtonを配置する
      private static JMenuItem makeMenuItem2(JMenuItem mi) {
        final JLabel label = new JLabel(MenuItemUIHelper.getAccText(mi, "+"));
        label.setOpaque(true);
        JMenuItem item = new JMenuItem(mi.getText()) {
          @Override public void updateUI() {
            super.updateUI();
            if (getUI() instanceof WindowsMenuItemUI) {
              setUI(new WindowsMenuItemUI() {
                @Override protected void installDefaults() {
                  super.installDefaults();
                  acceleratorForeground = UIManager.getColor("MenuItem.background");
                  acceleratorSelectionForeground = acceleratorForeground;
                }
              });
            }
          }
        };
      
        GridBagConstraints c = new GridBagConstraints();
        item.setLayout(new GridBagLayout());
        c.gridheight = 1;
        c.gridwidth  = 1;
        c.gridy = 0;
        c.gridx = 0;
        c.insets = new Insets(0, 0, 0, 4);
      
        c.weightx = 1d;
        c.fill = GridBagConstraints.HORIZONTAL;
        item.add(Box.createHorizontalGlue(), c);
        c.gridx = 1;
        c.fill = GridBagConstraints.NONE;
        c.weightx = 0d;
        c.anchor = GridBagConstraints.EAST;
        item.add(label, c);
      
        item.setMnemonic(mi.getMnemonic());
        item.setAccelerator(mi.getAccelerator());
        return item;
      }
      

  • JMenuItemAcceleratorJMenuItem#setLocale(Locale.ENGLISH)としても変化しない
  • JDK 1.7.0からLocale.getLocale()Locale.JAPANなどの場合、KeyEvent#getKeyText(...)で取得できる文字列が翻訳されている
    • 例: Spaceが「スペース」
  • KeyEvent#getKeyText(int) (Java Platform SE 8)では、「これらの文字列はawt.propertiesファイルを変更することによりローカライズが可能です。」となっているが、%JAVA_HOME%/jre/lib/rt.jar内にsun/awt/resources/awt.classなどの優先順位が高いクラスがあるため、awt_ja.propertiesなどを作成しても読み込まれない
    • -Xbootclasspath/p:などで、rt.jarより先に以下のようなsun.awt.resources.awt_ja.classを読み込むよう指定
      package sun.awt.resources;
      import java.util.ListResourceBundle;
      // ant package
      // cd target
      // "%JAVA_HOME%\bin\java" -Xbootclasspath/p:example.jar -jar example.jar
      // Class names should begin with an uppercase character
      @SuppressWarnings({"PMD.ClassNamingConventions", "checkstyle:typename"})
      public final class awt_ja extends ListResourceBundle {
        @Override protected Object[][] getContents() {
          System.out.println("awt_ja");
          return new Object[][] {
            {"AWT.space", "XXXXX"}
          };
        }
      }
      

参考リンク

コメント