Reputation: 1337
After applying setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT)
to the JMenuBar the arrow keys started to behave the opposite of what expected. Right arrow goes left and Left arrow goes right when trying to traverse through keyboard.
By the way, I'm using Java 11.0.12
on Windows 10.
Here is the code:
import java.awt.BorderLayout;
import java.awt.ComponentOrientation;
import java.awt.Dimension;
import java.awt.Toolkit;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
@SuppressWarnings("serial")
public class MenuBarProblem extends JFrame {
private JMenuBar menuBar;
private JMenu firstMenu;
private JMenuItem a;
private JMenuItem b;
private JMenu secondMenu;
private JMenuItem c;
private JMenuItem d;
private JMenu thirdMenu;
private JMenuItem e;
private JMenuItem f;
public MenuBarProblem() {
this.setLayout(new BorderLayout());
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
this.setExtendedState(JFrame.MAXIMIZED_BOTH);
this.setSize(screenSize);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.firstMenu = new JMenu("first");
this.a = new JMenuItem("a");
this.b = new JMenuItem("b");
this.firstMenu.add(this.a);
this.firstMenu.add(this.b);
this.secondMenu = new JMenu("second");
this.c = new JMenuItem("c");
this.d = new JMenuItem("d");
this.secondMenu.add(this.c);
this.secondMenu.add(this.d);
this.thirdMenu = new JMenu("third");
this.e = new JMenuItem("e");
this.f = new JMenuItem("f");
this.thirdMenu.add(this.e);
this.thirdMenu.add(this.f);
this.menuBar = new JMenuBar();
this.menuBar.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT); //Here is the reason
this.menuBar.add(this.firstMenu);
this.menuBar.add(this.secondMenu);
this.menuBar.add(this.thirdMenu);
this.setJMenuBar(this.menuBar);
}
}
Here is also the main class which contains the main method:
import java.lang.reflect.InvocationTargetException;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Main {
public static void main(String[] args) {
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException
| UnsupportedLookAndFeelException e) {
e.printStackTrace();
}
}
});
} catch (InvocationTargetException | InterruptedException e) {
e.printStackTrace();
}
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
MenuBarProblem test = new MenuBarProblem();
test.setVisible(true);
}
});
}
}
Update: The issue turned out to be a bug and now it's been added to the bug database here.
Upvotes: 1
Views: 139
Reputation: 81
Meanwhile the problem has been accepted as a bug (see above) and so the suggested solution is a temporary patch, build upon the internal implementation of the WindowsLookAndFeel.
import java.awt.BorderLayout;
import java.awt.ComponentOrientation;
import java.awt.Dimension;
import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.MenuElement;
import javax.swing.MenuSelectionManager;
@SuppressWarnings("serial")
public class MenuBarProblem extends JFrame {
private JMenuBar menuBar;
private JMenu firstMenu;
private JMenuItem a;
private JMenuItem b;
private JMenu secondMenu;
private JMenuItem c;
private JMenuItem d;
private JMenu thirdMenu;
private JMenuItem e;
private JMenuItem f;
private JMenu fourthMenu;
private JMenuItem g;
private JMenuItem h;
private KeyboardFocusManager keyboardFocusManager;
public MenuBarProblem() {
setLayout(new BorderLayout());
JPanel panel = new JPanel();
panel.setPreferredSize(new Dimension(400, 200));
add(panel);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
firstMenu = new JMenu("first");
a = new JMenuItem("a");
b = new JMenuItem("b");
firstMenu.add(a);
firstMenu.add(b);
secondMenu = new JMenu("second");
c = new JMenuItem("c");
d = new JMenuItem("d");
secondMenu.add(c);
secondMenu.add(d);
thirdMenu = new JMenu("third");
e = new JMenuItem("e");
f = new JMenuItem("f");
thirdMenu.add(e);
thirdMenu.add(f);
fourthMenu = new JMenu("fourth");
g = new JMenuItem("g");
h = new JMenuItem("h");
fourthMenu.add(g);
fourthMenu.add(h);
menuBar = new JMenuBar();
menuBar.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT); // Here is the reason
menuBar.add(firstMenu);
menuBar.add(secondMenu);
menuBar.add(thirdMenu);
menuBar.add(fourthMenu);
setJMenuBar(menuBar);
pack();
keyboardFocusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
keyboardFocusManager.addKeyEventDispatcher(new MSMKeyEventDispatcher());
}
private class MSMKeyEventDispatcher implements KeyEventDispatcher {
int index = -1;
private void select(int index) {
JMenu menu = menuBar.getMenu(index);
if (menu != null) {
MenuSelectionManager msm = MenuSelectionManager.defaultManager();
MenuElement path[] = new MenuElement[2];
path[0] = menuBar;
path[1] = menu;
msm.setSelectedPath(path);
}
}
@Override
public boolean dispatchKeyEvent(KeyEvent e) {
if (MenuBarProblem.this.getFocusOwner() == null) {
return false;
}
if (e.getID() == KeyEvent.KEY_PRESSED) {
if (e.getKeyCode() == KeyEvent.VK_ALT) {
MenuSelectionManager msm = MenuSelectionManager.defaultManager();
if (msm.getSelectedPath().length == 0) {
index = 0;
select(index);
} else {
msm.setSelectedPath(null);
index = -1;
}
return true;
} else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
if (index >= 0) {
--index;
if (index < 0) {
index = menuBar.getComponentCount() - 1;
}
select(index);
return true;
}
} else if (e.getKeyCode() == KeyEvent.VK_LEFT) {
if (index >= 0) {
++index;
if (index >= menuBar.getComponentCount()) {
index = 0;
}
select(index);
return true;
}
}
}
return false;
}
}
}
Addendum: The most simple idea for a solution is probably a KeyEventDispatcher switching the KeyCodes KeyEvent.VK_LEFT and KeyEvent.VK_RIGHT of the received KeyEvent and redispatching the changed KeyEvent. However, it turned out that this has absolutely no effect.
Upvotes: 0