8249548: backward focus traversal gets stuck in button group
Reviewed-by: serb
This commit is contained in:
parent
3d7b524593
commit
22bb597d0d
@ -56,31 +56,6 @@ import java.util.Set;
|
||||
import java.util.Enumeration;
|
||||
|
||||
public class AquaButtonRadioUI extends AquaButtonLabeledUI {
|
||||
private KeyListener keyListener = null;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private class SelectPreviousBtn extends AbstractAction {
|
||||
public SelectPreviousBtn() {
|
||||
super("Previous");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
AquaButtonRadioUI.this.selectRadioButton(e, false);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private class SelectNextBtn extends AbstractAction {
|
||||
public SelectNextBtn() {
|
||||
super("Next");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
AquaButtonRadioUI.this.selectRadioButton(e, true);
|
||||
}
|
||||
}
|
||||
|
||||
private static final RecyclableSingleton<AquaButtonRadioUI> instance = new RecyclableSingletonFromDefaultConstructor<AquaButtonRadioUI>(AquaButtonRadioUI.class);
|
||||
private static final RecyclableSingleton<ImageIcon> sizingIcon = new RecyclableSingleton<ImageIcon>() {
|
||||
@ -115,269 +90,4 @@ public class AquaButtonRadioUI extends AquaButtonLabeledUI {
|
||||
super(other);
|
||||
}
|
||||
}
|
||||
|
||||
private KeyListener createKeyListener() {
|
||||
if (keyListener == null) {
|
||||
keyListener = new KeyHandler();
|
||||
}
|
||||
|
||||
return keyListener;
|
||||
}
|
||||
|
||||
private boolean isValidRadioButtonObj(Object obj) {
|
||||
return ((obj instanceof JRadioButton) &&
|
||||
((JRadioButton)obj).isVisible() &&
|
||||
((JRadioButton)obj).isEnabled());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installListeners(AbstractButton button) {
|
||||
super.installListeners(button);
|
||||
|
||||
//Only for JRadioButton
|
||||
if (!(button instanceof JRadioButton))
|
||||
return;
|
||||
|
||||
keyListener = createKeyListener();
|
||||
button.addKeyListener(keyListener);
|
||||
|
||||
button.setFocusTraversalKeysEnabled(false);
|
||||
|
||||
button.getActionMap().put("Previous", new SelectPreviousBtn());
|
||||
button.getActionMap().put("Next", new SelectNextBtn());
|
||||
|
||||
button.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).
|
||||
put(KeyStroke.getKeyStroke("UP"), "Previous");
|
||||
button.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).
|
||||
put(KeyStroke.getKeyStroke("DOWN"), "Next");
|
||||
button.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).
|
||||
put(KeyStroke.getKeyStroke("LEFT"), "Previous");
|
||||
button.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).
|
||||
put(KeyStroke.getKeyStroke("RIGHT"), "Next");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void uninstallListeners(AbstractButton button) {
|
||||
super.uninstallListeners(button);
|
||||
|
||||
//Only for JRadioButton
|
||||
if (!(button instanceof JRadioButton))
|
||||
return;
|
||||
|
||||
//Unmap actions from the arrow keys.
|
||||
button.getActionMap().remove("Previous");
|
||||
button.getActionMap().remove("Next");
|
||||
|
||||
button.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).
|
||||
remove(KeyStroke.getKeyStroke("UP"));
|
||||
button.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).
|
||||
remove(KeyStroke.getKeyStroke("DOWN"));
|
||||
button.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).
|
||||
remove(KeyStroke.getKeyStroke("LEFT"));
|
||||
button.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).
|
||||
remove(KeyStroke.getKeyStroke("RIGHT"));
|
||||
|
||||
if (keyListener != null ) {
|
||||
button.removeKeyListener(keyListener);
|
||||
keyListener = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Select radio button based on "Previous" or "Next" operation
|
||||
*
|
||||
* @param event, the event object.
|
||||
* @param next, indicate if it's next one
|
||||
*/
|
||||
private void selectRadioButton(ActionEvent event, boolean next) {
|
||||
Object eventSrc = event.getSource();
|
||||
|
||||
//Check whether the source is JRadioButton, if so, whether it is visible
|
||||
if (!isValidRadioButtonObj(eventSrc))
|
||||
return;
|
||||
|
||||
ButtonGroupInfo btnGroupInfo = new ButtonGroupInfo((JRadioButton)eventSrc);
|
||||
btnGroupInfo.selectNewButton(next);
|
||||
}
|
||||
|
||||
/**
|
||||
* ButtonGroupInfo, used to get related info in button group
|
||||
* for given radio button.
|
||||
*/
|
||||
private class ButtonGroupInfo {
|
||||
JRadioButton activeBtn = null;
|
||||
|
||||
JRadioButton firstBtn = null;
|
||||
JRadioButton lastBtn = null;
|
||||
|
||||
JRadioButton previousBtn = null;
|
||||
JRadioButton nextBtn = null;
|
||||
|
||||
HashSet<JRadioButton> btnsInGroup = null;
|
||||
boolean srcFound = false;
|
||||
|
||||
public ButtonGroupInfo(JRadioButton btn) {
|
||||
activeBtn = btn;
|
||||
btnsInGroup = new HashSet<JRadioButton>();
|
||||
}
|
||||
|
||||
//Check if given object is in the button group
|
||||
boolean containsInGroup(Object obj) {
|
||||
return btnsInGroup.contains(obj);
|
||||
}
|
||||
|
||||
//Check if the next object to gain focus belongs
|
||||
//to the button group or not
|
||||
Component getFocusTransferBaseComponent(boolean next) {
|
||||
return firstBtn;
|
||||
}
|
||||
|
||||
boolean getButtonGroupInfo() {
|
||||
if (activeBtn == null)
|
||||
return false;
|
||||
|
||||
btnsInGroup.clear();
|
||||
|
||||
//Get the button model from ths source.
|
||||
ButtonModel model = activeBtn.getModel();
|
||||
if (!(model instanceof DefaultButtonModel))
|
||||
return false;
|
||||
|
||||
// If the button model is DefaultButtonModel, and use it, otherwise return.
|
||||
DefaultButtonModel bm = (DefaultButtonModel) model;
|
||||
|
||||
//get the ButtonGroup of the button from the button model
|
||||
ButtonGroup group = bm.getGroup();
|
||||
if (group == null)
|
||||
return false;
|
||||
|
||||
Enumeration<AbstractButton> e = group.getElements();
|
||||
if (e == null)
|
||||
return false;
|
||||
|
||||
while (e.hasMoreElements()) {
|
||||
AbstractButton curElement = e.nextElement();
|
||||
if (!isValidRadioButtonObj(curElement))
|
||||
continue;
|
||||
|
||||
btnsInGroup.add((JRadioButton) curElement);
|
||||
|
||||
// If firstBtn is not set yet, curElement is that first button
|
||||
if (null == firstBtn)
|
||||
firstBtn = (JRadioButton)curElement;
|
||||
|
||||
if (activeBtn == curElement)
|
||||
srcFound = true;
|
||||
else if (!srcFound) {
|
||||
//The source has not been yet found and the current element
|
||||
// is the last previousBtn
|
||||
previousBtn = (JRadioButton) curElement;
|
||||
} else if (nextBtn == null) {
|
||||
//The source has been found and the current element
|
||||
//is the next valid button of the list
|
||||
nextBtn = (JRadioButton) curElement;
|
||||
}
|
||||
|
||||
//Set new last "valid" JRadioButton of the list
|
||||
lastBtn = (JRadioButton)curElement;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the new radio button that focus needs to be
|
||||
* moved to in the group, select the button
|
||||
*
|
||||
* @param next, indicate if it's arrow up/left or down/right
|
||||
*/
|
||||
void selectNewButton(boolean next) {
|
||||
if (!getButtonGroupInfo())
|
||||
return;
|
||||
|
||||
if (srcFound) {
|
||||
JRadioButton newSelectedBtn = null;
|
||||
if (next) {
|
||||
//Select Next button. Cycle to the first button if the source
|
||||
//button is the last of the group.
|
||||
newSelectedBtn = (null == nextBtn) ? firstBtn : nextBtn;
|
||||
} else {
|
||||
//Select previous button. Cycle to the last button if the source
|
||||
//button is the first button of the group.
|
||||
newSelectedBtn = (null == previousBtn) ? lastBtn: previousBtn;
|
||||
}
|
||||
if (newSelectedBtn != null && newSelectedBtn != activeBtn) {
|
||||
newSelectedBtn.requestFocusInWindow();
|
||||
newSelectedBtn.setSelected(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the button group the passed in JRadioButton belongs to, and
|
||||
* move focus to next component of the last button in the group
|
||||
* or previous compoennt of first button
|
||||
*
|
||||
* @param next, indicate if jump to next component or previous
|
||||
*/
|
||||
void jumpToNextComponent(boolean next) {
|
||||
if (!getButtonGroupInfo()) {
|
||||
//In case the button does not belong to any group, it needs
|
||||
//to be treated as a component
|
||||
if (activeBtn != null) {
|
||||
lastBtn = activeBtn;
|
||||
firstBtn = activeBtn;
|
||||
} else
|
||||
return;
|
||||
}
|
||||
|
||||
//If next component in the parent window is not in the button
|
||||
//group, current active button will be base, otherwise, the base
|
||||
// will be first or last button in the button group
|
||||
Component focusBase = getFocusTransferBaseComponent(next);
|
||||
if (focusBase != null) {
|
||||
if (next) {
|
||||
KeyboardFocusManager.
|
||||
getCurrentKeyboardFocusManager().focusNextComponent(focusBase);
|
||||
} else {
|
||||
KeyboardFocusManager.
|
||||
getCurrentKeyboardFocusManager().focusPreviousComponent(focusBase);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Radiobutton KeyListener
|
||||
*/
|
||||
private class KeyHandler implements KeyListener {
|
||||
//This listener checks if the key event is a focus traversal key event
|
||||
// on a radio button, consume the event if so and move the focus
|
||||
// to next/previous component
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
AWTKeyStroke stroke = AWTKeyStroke.getAWTKeyStrokeForEvent(e);
|
||||
if (stroke != null && e.getSource() instanceof JRadioButton) {
|
||||
JRadioButton source = (JRadioButton) e.getSource();
|
||||
boolean next = isFocusTraversalKey(source,
|
||||
KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, stroke);
|
||||
if (next || isFocusTraversalKey(source,
|
||||
KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, stroke)) {
|
||||
e.consume();
|
||||
ButtonGroupInfo btnGroupInfo = new ButtonGroupInfo(source);
|
||||
btnGroupInfo.jumpToNextComponent(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isFocusTraversalKey(JComponent c, int id,
|
||||
AWTKeyStroke stroke) {
|
||||
Set<AWTKeyStroke> keys = c.getFocusTraversalKeys(id);
|
||||
return keys != null && keys.contains(stroke);
|
||||
}
|
||||
|
||||
@Override public void keyReleased(KeyEvent e) {}
|
||||
|
||||
@Override public void keyTyped(KeyEvent e) {}
|
||||
}
|
||||
}
|
||||
|
@ -185,17 +185,13 @@ public class AquaButtonUI extends BasicButtonUI implements Sizeable {
|
||||
}
|
||||
|
||||
protected void installListeners(final AbstractButton b) {
|
||||
final AquaButtonListener listener = createButtonListener(b);
|
||||
super.installListeners(b);
|
||||
AquaButtonListener listener = getAquaButtonListener(b);
|
||||
if (listener != null) {
|
||||
// put the listener in the button's client properties so that
|
||||
// we can get at it later
|
||||
b.putClientProperty(this, listener);
|
||||
|
||||
b.addMouseListener(listener);
|
||||
b.addMouseMotionListener(listener);
|
||||
b.addFocusListener(listener);
|
||||
b.addPropertyChangeListener(listener);
|
||||
b.addChangeListener(listener);
|
||||
b.addAncestorListener(listener);
|
||||
}
|
||||
installHierListener(b);
|
||||
@ -221,15 +217,10 @@ public class AquaButtonUI extends BasicButtonUI implements Sizeable {
|
||||
}
|
||||
|
||||
protected void uninstallListeners(final AbstractButton b) {
|
||||
super.uninstallListeners(b);
|
||||
final AquaButtonListener listener = (AquaButtonListener)b.getClientProperty(this);
|
||||
b.putClientProperty(this, null);
|
||||
if (listener != null) {
|
||||
b.removeMouseListener(listener);
|
||||
b.removeMouseListener(listener);
|
||||
b.removeMouseMotionListener(listener);
|
||||
b.removeFocusListener(listener);
|
||||
b.removeChangeListener(listener);
|
||||
b.removePropertyChangeListener(listener);
|
||||
b.removeAncestorListener(listener);
|
||||
}
|
||||
uninstallHierListener(b);
|
||||
@ -246,6 +237,23 @@ public class AquaButtonUI extends BasicButtonUI implements Sizeable {
|
||||
return new AquaButtonListener(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the AquaButtonListener for the passed in Button, or null if one
|
||||
* could not be found.
|
||||
*/
|
||||
private AquaButtonListener getAquaButtonListener(AbstractButton b) {
|
||||
MouseMotionListener[] listeners = b.getMouseMotionListeners();
|
||||
|
||||
if (listeners != null) {
|
||||
for (MouseMotionListener listener : listeners) {
|
||||
if (listener instanceof AquaButtonListener) {
|
||||
return (AquaButtonListener) listener;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Paint Methods
|
||||
public void paint(final Graphics g, final JComponent c) {
|
||||
final AbstractButton b = (AbstractButton)c;
|
||||
|
@ -35,6 +35,9 @@ import javax.swing.*;
|
||||
import javax.swing.border.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import javax.swing.plaf.ButtonUI;
|
||||
import javax.swing.plaf.UIResource;
|
||||
import javax.swing.plaf.ComponentUI;
|
||||
@ -68,6 +71,8 @@ public class BasicButtonUI extends ButtonUI{
|
||||
|
||||
private static final Object BASIC_BUTTON_UI_KEY = new Object();
|
||||
|
||||
private KeyListener keyListener = null;
|
||||
|
||||
// ********************************
|
||||
// Create PLAF
|
||||
// ********************************
|
||||
@ -156,6 +161,27 @@ public class BasicButtonUI extends ButtonUI{
|
||||
b.addPropertyChangeListener(listener);
|
||||
b.addChangeListener(listener);
|
||||
}
|
||||
|
||||
if (b instanceof JToggleButton) {
|
||||
keyListener = createKeyListener();
|
||||
b.addKeyListener(keyListener);
|
||||
|
||||
// Need to get traversal key event
|
||||
b.setFocusTraversalKeysEnabled(false);
|
||||
|
||||
// Map actions to the arrow keys
|
||||
b.getActionMap().put("Previous", new BasicButtonUI.SelectPreviousBtn());
|
||||
b.getActionMap().put("Next", new BasicButtonUI.SelectNextBtn());
|
||||
|
||||
b.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).
|
||||
put(KeyStroke.getKeyStroke("UP"), "Previous");
|
||||
b.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).
|
||||
put(KeyStroke.getKeyStroke("DOWN"), "Next");
|
||||
b.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).
|
||||
put(KeyStroke.getKeyStroke("LEFT"), "Previous");
|
||||
b.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).
|
||||
put(KeyStroke.getKeyStroke("RIGHT"), "Next");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -208,6 +234,24 @@ public class BasicButtonUI extends ButtonUI{
|
||||
b.removeChangeListener(listener);
|
||||
b.removePropertyChangeListener(listener);
|
||||
}
|
||||
if (b instanceof JToggleButton) {
|
||||
// Unmap actions from the arrow keys
|
||||
b.getActionMap().remove("Previous");
|
||||
b.getActionMap().remove("Next");
|
||||
b.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
|
||||
.remove(KeyStroke.getKeyStroke("UP"));
|
||||
b.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
|
||||
.remove(KeyStroke.getKeyStroke("DOWN"));
|
||||
b.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
|
||||
.remove(KeyStroke.getKeyStroke("LEFT"));
|
||||
b.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
|
||||
.remove(KeyStroke.getKeyStroke("RIGHT"));
|
||||
|
||||
if (keyListener != null) {
|
||||
b.removeKeyListener(keyListener);
|
||||
keyListener = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -566,4 +610,262 @@ public class BasicButtonUI extends ButtonUI{
|
||||
return null;
|
||||
}
|
||||
|
||||
/////////////////////////// Private functions ////////////////////////
|
||||
/**
|
||||
* Creates the key listener to handle tab navigation in JToggleButton Group.
|
||||
*/
|
||||
private KeyListener createKeyListener() {
|
||||
if (keyListener == null) {
|
||||
keyListener = new BasicButtonUI.KeyHandler();
|
||||
}
|
||||
return keyListener;
|
||||
}
|
||||
|
||||
|
||||
private boolean isValidToggleButtonObj(Object obj) {
|
||||
return ((obj instanceof JToggleButton) &&
|
||||
((JToggleButton) obj).isVisible() &&
|
||||
((JToggleButton) obj).isEnabled());
|
||||
}
|
||||
|
||||
/**
|
||||
* Select toggle button based on "Previous" or "Next" operation
|
||||
*
|
||||
* @param event, the event object.
|
||||
* @param next, indicate if it's next one
|
||||
*/
|
||||
private void selectToggleButton(ActionEvent event, boolean next) {
|
||||
// Get the source of the event.
|
||||
Object eventSrc = event.getSource();
|
||||
|
||||
// Check whether the source is JToggleButton, it so, whether it is visible
|
||||
if (!isValidToggleButtonObj(eventSrc))
|
||||
return;
|
||||
|
||||
BasicButtonUI.ButtonGroupInfo btnGroupInfo = new BasicButtonUI.ButtonGroupInfo((JToggleButton)eventSrc);
|
||||
btnGroupInfo.selectNewButton(next);
|
||||
}
|
||||
|
||||
/////////////////////////// Inner Classes ////////////////////////
|
||||
@SuppressWarnings("serial")
|
||||
private class SelectPreviousBtn extends AbstractAction {
|
||||
public SelectPreviousBtn() {
|
||||
super("Previous");
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
BasicButtonUI.this.selectToggleButton(e, false);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private class SelectNextBtn extends AbstractAction{
|
||||
public SelectNextBtn() {
|
||||
super("Next");
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
BasicButtonUI.this.selectToggleButton(e, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ButtonGroupInfo, used to get related info in button group
|
||||
* for given toggle button
|
||||
*/
|
||||
private class ButtonGroupInfo {
|
||||
|
||||
JToggleButton activeBtn = null;
|
||||
|
||||
JToggleButton firstBtn = null;
|
||||
JToggleButton lastBtn = null;
|
||||
|
||||
JToggleButton previousBtn = null;
|
||||
JToggleButton nextBtn = null;
|
||||
|
||||
HashSet<JToggleButton> btnsInGroup = null;
|
||||
|
||||
boolean srcFound = false;
|
||||
public ButtonGroupInfo(JToggleButton btn) {
|
||||
activeBtn = btn;
|
||||
btnsInGroup = new HashSet<JToggleButton>();
|
||||
}
|
||||
|
||||
// Check if given object is in the button group
|
||||
boolean containsInGroup(Object obj){
|
||||
return btnsInGroup.contains(obj);
|
||||
}
|
||||
|
||||
// Check if the next object to gain focus belongs
|
||||
// to the button group or not
|
||||
Component getFocusTransferBaseComponent(boolean next){
|
||||
return firstBtn;
|
||||
}
|
||||
|
||||
boolean getButtonGroupInfo() {
|
||||
if (activeBtn == null)
|
||||
return false;
|
||||
|
||||
btnsInGroup.clear();
|
||||
|
||||
// Get the button model from the source.
|
||||
ButtonModel model = activeBtn.getModel();
|
||||
if (!(model instanceof DefaultButtonModel))
|
||||
return false;
|
||||
|
||||
// If the button model is DefaultButtonModel, and use it, otherwise return.
|
||||
DefaultButtonModel bm = (DefaultButtonModel) model;
|
||||
|
||||
// get the ButtonGroup of the button from the button model
|
||||
ButtonGroup group = bm.getGroup();
|
||||
if (group == null)
|
||||
return false;
|
||||
|
||||
// Get all the buttons in the group
|
||||
Enumeration<AbstractButton> e = group.getElements();
|
||||
if (e == null)
|
||||
return false;
|
||||
|
||||
while (e.hasMoreElements()) {
|
||||
AbstractButton curElement = e.nextElement();
|
||||
if (!isValidToggleButtonObj(curElement))
|
||||
continue;
|
||||
|
||||
btnsInGroup.add((JToggleButton) curElement);
|
||||
|
||||
// If firstBtn is not set yet, curElement is that first button
|
||||
if (null == firstBtn)
|
||||
firstBtn = (JToggleButton) curElement;
|
||||
|
||||
if (activeBtn == curElement)
|
||||
srcFound = true;
|
||||
else if (!srcFound) {
|
||||
// The source has not been yet found and the current element
|
||||
// is the last previousBtn
|
||||
previousBtn = (JToggleButton) curElement;
|
||||
} else if (nextBtn == null) {
|
||||
// The source has been found and the current element
|
||||
// is the next valid button of the list
|
||||
nextBtn = (JToggleButton) curElement;
|
||||
}
|
||||
|
||||
// Set new last "valid" JToggleButton of the list
|
||||
lastBtn = (JToggleButton) curElement;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the new toggle button that focus needs to be
|
||||
* moved to in the group, select the button
|
||||
*
|
||||
* @param next, indicate if it's arrow up/left or down/right
|
||||
*/
|
||||
void selectNewButton(boolean next) {
|
||||
if (!getButtonGroupInfo())
|
||||
return;
|
||||
|
||||
if (srcFound) {
|
||||
JToggleButton newSelectedBtn = null;
|
||||
if (next) {
|
||||
// Select Next button. Cycle to the first button if the source
|
||||
// button is the last of the group.
|
||||
newSelectedBtn = (null == nextBtn) ? firstBtn : nextBtn;
|
||||
} else {
|
||||
// Select previous button. Cycle to the last button if the source
|
||||
// button is the first button of the group.
|
||||
newSelectedBtn = (null == previousBtn) ? lastBtn : previousBtn;
|
||||
}
|
||||
if (newSelectedBtn != null &&
|
||||
(newSelectedBtn != activeBtn)) {
|
||||
ButtonModel btnModel = newSelectedBtn.getModel();
|
||||
btnModel.setPressed(true);
|
||||
btnModel.setArmed(true);
|
||||
newSelectedBtn.requestFocusInWindow();
|
||||
newSelectedBtn.setSelected(true);
|
||||
btnModel.setPressed(false);
|
||||
btnModel.setArmed(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the button group the passed in JToggleButton belongs to, and
|
||||
* move focus to next component of the last button in the group
|
||||
* or previous component of first button
|
||||
*
|
||||
* @param next, indicate if jump to next component or previous
|
||||
*/
|
||||
void jumpToNextComponent(boolean next) {
|
||||
if (!getButtonGroupInfo()){
|
||||
// In case the button does not belong to any group, it needs
|
||||
// to be treated as a component
|
||||
if (activeBtn != null){
|
||||
lastBtn = activeBtn;
|
||||
firstBtn = activeBtn;
|
||||
}
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the component we will use as base to transfer
|
||||
// focus from
|
||||
JComponent compTransferFocusFrom = activeBtn;
|
||||
|
||||
// If next component in the parent window is not in
|
||||
// the button group, current active button will be
|
||||
// base, otherwise, the base will be first or last
|
||||
// button in the button group
|
||||
Component focusBase = getFocusTransferBaseComponent(next);
|
||||
if (focusBase != null){
|
||||
if (next) {
|
||||
KeyboardFocusManager.
|
||||
getCurrentKeyboardFocusManager().focusNextComponent(focusBase);
|
||||
} else {
|
||||
KeyboardFocusManager.
|
||||
getCurrentKeyboardFocusManager().focusPreviousComponent(focusBase);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Togglebutton KeyListener
|
||||
*/
|
||||
private class KeyHandler implements KeyListener {
|
||||
|
||||
// This listener checks if the key event is a focus traversal key event
|
||||
// on a toggle button, consume the event if so and move the focus
|
||||
// to next/previous component
|
||||
public void keyPressed(KeyEvent e) {
|
||||
AWTKeyStroke stroke = AWTKeyStroke.getAWTKeyStrokeForEvent(e);
|
||||
if (stroke != null && e.getSource() instanceof JToggleButton) {
|
||||
JToggleButton source = (JToggleButton) e.getSource();
|
||||
boolean next = isFocusTraversalKey(source,
|
||||
KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
|
||||
stroke);
|
||||
if (next || isFocusTraversalKey(source,
|
||||
KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,
|
||||
stroke)) {
|
||||
e.consume();
|
||||
BasicButtonUI.ButtonGroupInfo btnGroupInfo = new BasicButtonUI.ButtonGroupInfo(source);
|
||||
btnGroupInfo.jumpToNextComponent(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isFocusTraversalKey(JComponent c, int id,
|
||||
AWTKeyStroke stroke) {
|
||||
Set<AWTKeyStroke> keys = c.getFocusTraversalKeys(id);
|
||||
return keys != null && keys.contains(stroke);
|
||||
}
|
||||
|
||||
public void keyReleased(KeyEvent e) {
|
||||
}
|
||||
|
||||
public void keyTyped(KeyEvent e) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -118,65 +118,6 @@ public class BasicRadioButtonUI extends BasicToggleButtonUI
|
||||
return icon;
|
||||
}
|
||||
|
||||
// ********************************
|
||||
// Install Listeners
|
||||
// ********************************
|
||||
@Override
|
||||
protected void installListeners(AbstractButton button) {
|
||||
super.installListeners(button);
|
||||
|
||||
// Only for JRadioButton
|
||||
if (!(button instanceof JRadioButton))
|
||||
return;
|
||||
|
||||
keyListener = createKeyListener();
|
||||
button.addKeyListener(keyListener);
|
||||
|
||||
// Need to get traversal key event
|
||||
button.setFocusTraversalKeysEnabled(false);
|
||||
|
||||
// Map actions to the arrow keys
|
||||
button.getActionMap().put("Previous", new SelectPreviousBtn());
|
||||
button.getActionMap().put("Next", new SelectNextBtn());
|
||||
|
||||
button.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).
|
||||
put(KeyStroke.getKeyStroke("UP"), "Previous");
|
||||
button.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).
|
||||
put(KeyStroke.getKeyStroke("DOWN"), "Next");
|
||||
button.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).
|
||||
put(KeyStroke.getKeyStroke("LEFT"), "Previous");
|
||||
button.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).
|
||||
put(KeyStroke.getKeyStroke("RIGHT"), "Next");
|
||||
}
|
||||
|
||||
// ********************************
|
||||
// UnInstall Listeners
|
||||
// ********************************
|
||||
@Override
|
||||
protected void uninstallListeners(AbstractButton button) {
|
||||
super.uninstallListeners(button);
|
||||
|
||||
// Only for JRadioButton
|
||||
if (!(button instanceof JRadioButton))
|
||||
return;
|
||||
|
||||
// Unmap actions from the arrow keys
|
||||
button.getActionMap().remove("Previous");
|
||||
button.getActionMap().remove("Next");
|
||||
button.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
|
||||
.remove(KeyStroke.getKeyStroke("UP"));
|
||||
button.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
|
||||
.remove(KeyStroke.getKeyStroke("DOWN"));
|
||||
button.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
|
||||
.remove(KeyStroke.getKeyStroke("LEFT"));
|
||||
button.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
|
||||
.remove(KeyStroke.getKeyStroke("RIGHT"));
|
||||
|
||||
if (keyListener != null) {
|
||||
button.removeKeyListener(keyListener);
|
||||
keyListener = null;
|
||||
}
|
||||
}
|
||||
|
||||
/* These Dimensions/Rectangles are allocated once for all
|
||||
* RadioButtonUI.paint() calls. Re-using rectangles
|
||||
@ -353,262 +294,4 @@ public class BasicRadioButtonUI extends BasicToggleButtonUI
|
||||
height += prefInsets.top + prefInsets.bottom;
|
||||
return new Dimension(width, height);
|
||||
}
|
||||
|
||||
/////////////////////////// Private functions ////////////////////////
|
||||
/**
|
||||
* Creates the key listener to handle tab navigation in JRadioButton Group.
|
||||
*/
|
||||
private KeyListener createKeyListener() {
|
||||
if (keyListener == null) {
|
||||
keyListener = new KeyHandler();
|
||||
}
|
||||
return keyListener;
|
||||
}
|
||||
|
||||
|
||||
private boolean isValidRadioButtonObj(Object obj) {
|
||||
return ((obj instanceof JRadioButton) &&
|
||||
((JRadioButton) obj).isVisible() &&
|
||||
((JRadioButton) obj).isEnabled());
|
||||
}
|
||||
|
||||
/**
|
||||
* Select radio button based on "Previous" or "Next" operation
|
||||
*
|
||||
* @param event, the event object.
|
||||
* @param next, indicate if it's next one
|
||||
*/
|
||||
private void selectRadioButton(ActionEvent event, boolean next) {
|
||||
// Get the source of the event.
|
||||
Object eventSrc = event.getSource();
|
||||
|
||||
// Check whether the source is JRadioButton, it so, whether it is visible
|
||||
if (!isValidRadioButtonObj(eventSrc))
|
||||
return;
|
||||
|
||||
ButtonGroupInfo btnGroupInfo = new ButtonGroupInfo((JRadioButton)eventSrc);
|
||||
btnGroupInfo.selectNewButton(next);
|
||||
}
|
||||
|
||||
/////////////////////////// Inner Classes ////////////////////////
|
||||
@SuppressWarnings("serial")
|
||||
private class SelectPreviousBtn extends AbstractAction {
|
||||
public SelectPreviousBtn() {
|
||||
super("Previous");
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
BasicRadioButtonUI.this.selectRadioButton(e, false);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private class SelectNextBtn extends AbstractAction{
|
||||
public SelectNextBtn() {
|
||||
super("Next");
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
BasicRadioButtonUI.this.selectRadioButton(e, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ButtonGroupInfo, used to get related info in button group
|
||||
* for given radio button
|
||||
*/
|
||||
private class ButtonGroupInfo {
|
||||
|
||||
JRadioButton activeBtn = null;
|
||||
|
||||
JRadioButton firstBtn = null;
|
||||
JRadioButton lastBtn = null;
|
||||
|
||||
JRadioButton previousBtn = null;
|
||||
JRadioButton nextBtn = null;
|
||||
|
||||
HashSet<JRadioButton> btnsInGroup = null;
|
||||
|
||||
boolean srcFound = false;
|
||||
public ButtonGroupInfo(JRadioButton btn) {
|
||||
activeBtn = btn;
|
||||
btnsInGroup = new HashSet<JRadioButton>();
|
||||
}
|
||||
|
||||
// Check if given object is in the button group
|
||||
boolean containsInGroup(Object obj){
|
||||
return btnsInGroup.contains(obj);
|
||||
}
|
||||
|
||||
// Check if the next object to gain focus belongs
|
||||
// to the button group or not
|
||||
Component getFocusTransferBaseComponent(boolean next){
|
||||
return firstBtn;
|
||||
}
|
||||
|
||||
boolean getButtonGroupInfo() {
|
||||
if (activeBtn == null)
|
||||
return false;
|
||||
|
||||
btnsInGroup.clear();
|
||||
|
||||
// Get the button model from the source.
|
||||
ButtonModel model = activeBtn.getModel();
|
||||
if (!(model instanceof DefaultButtonModel))
|
||||
return false;
|
||||
|
||||
// If the button model is DefaultButtonModel, and use it, otherwise return.
|
||||
DefaultButtonModel bm = (DefaultButtonModel) model;
|
||||
|
||||
// get the ButtonGroup of the button from the button model
|
||||
ButtonGroup group = bm.getGroup();
|
||||
if (group == null)
|
||||
return false;
|
||||
|
||||
// Get all the buttons in the group
|
||||
Enumeration<AbstractButton> e = group.getElements();
|
||||
if (e == null)
|
||||
return false;
|
||||
|
||||
while (e.hasMoreElements()) {
|
||||
AbstractButton curElement = e.nextElement();
|
||||
if (!isValidRadioButtonObj(curElement))
|
||||
continue;
|
||||
|
||||
btnsInGroup.add((JRadioButton) curElement);
|
||||
|
||||
// If firstBtn is not set yet, curElement is that first button
|
||||
if (null == firstBtn)
|
||||
firstBtn = (JRadioButton) curElement;
|
||||
|
||||
if (activeBtn == curElement)
|
||||
srcFound = true;
|
||||
else if (!srcFound) {
|
||||
// The source has not been yet found and the current element
|
||||
// is the last previousBtn
|
||||
previousBtn = (JRadioButton) curElement;
|
||||
} else if (nextBtn == null) {
|
||||
// The source has been found and the current element
|
||||
// is the next valid button of the list
|
||||
nextBtn = (JRadioButton) curElement;
|
||||
}
|
||||
|
||||
// Set new last "valid" JRadioButton of the list
|
||||
lastBtn = (JRadioButton) curElement;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the new radio button that focus needs to be
|
||||
* moved to in the group, select the button
|
||||
*
|
||||
* @param next, indicate if it's arrow up/left or down/right
|
||||
*/
|
||||
void selectNewButton(boolean next) {
|
||||
if (!getButtonGroupInfo())
|
||||
return;
|
||||
|
||||
if (srcFound) {
|
||||
JRadioButton newSelectedBtn = null;
|
||||
if (next) {
|
||||
// Select Next button. Cycle to the first button if the source
|
||||
// button is the last of the group.
|
||||
newSelectedBtn = (null == nextBtn) ? firstBtn : nextBtn;
|
||||
} else {
|
||||
// Select previous button. Cycle to the last button if the source
|
||||
// button is the first button of the group.
|
||||
newSelectedBtn = (null == previousBtn) ? lastBtn : previousBtn;
|
||||
}
|
||||
if (newSelectedBtn != null &&
|
||||
(newSelectedBtn != activeBtn)) {
|
||||
ButtonModel btnModel = newSelectedBtn.getModel();
|
||||
btnModel.setPressed(true);
|
||||
btnModel.setArmed(true);
|
||||
newSelectedBtn.requestFocusInWindow();
|
||||
newSelectedBtn.setSelected(true);
|
||||
btnModel.setPressed(false);
|
||||
btnModel.setArmed(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the button group the passed in JRadioButton belongs to, and
|
||||
* move focus to next component of the last button in the group
|
||||
* or previous component of first button
|
||||
*
|
||||
* @param next, indicate if jump to next component or previous
|
||||
*/
|
||||
void jumpToNextComponent(boolean next) {
|
||||
if (!getButtonGroupInfo()){
|
||||
// In case the button does not belong to any group, it needs
|
||||
// to be treated as a component
|
||||
if (activeBtn != null){
|
||||
lastBtn = activeBtn;
|
||||
firstBtn = activeBtn;
|
||||
}
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the component we will use as base to transfer
|
||||
// focus from
|
||||
JComponent compTransferFocusFrom = activeBtn;
|
||||
|
||||
// If next component in the parent window is not in
|
||||
// the button group, current active button will be
|
||||
// base, otherwise, the base will be first or last
|
||||
// button in the button group
|
||||
Component focusBase = getFocusTransferBaseComponent(next);
|
||||
if (focusBase != null){
|
||||
if (next) {
|
||||
KeyboardFocusManager.
|
||||
getCurrentKeyboardFocusManager().focusNextComponent(focusBase);
|
||||
} else {
|
||||
KeyboardFocusManager.
|
||||
getCurrentKeyboardFocusManager().focusPreviousComponent(focusBase);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Radiobutton KeyListener
|
||||
*/
|
||||
private class KeyHandler implements KeyListener {
|
||||
|
||||
// This listener checks if the key event is a focus traversal key event
|
||||
// on a radio button, consume the event if so and move the focus
|
||||
// to next/previous component
|
||||
public void keyPressed(KeyEvent e) {
|
||||
AWTKeyStroke stroke = AWTKeyStroke.getAWTKeyStrokeForEvent(e);
|
||||
if (stroke != null && e.getSource() instanceof JRadioButton) {
|
||||
JRadioButton source = (JRadioButton) e.getSource();
|
||||
boolean next = isFocusTraversalKey(source,
|
||||
KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
|
||||
stroke);
|
||||
if (next || isFocusTraversalKey(source,
|
||||
KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,
|
||||
stroke)) {
|
||||
e.consume();
|
||||
ButtonGroupInfo btnGroupInfo = new ButtonGroupInfo(source);
|
||||
btnGroupInfo.jumpToNextComponent(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isFocusTraversalKey(JComponent c, int id,
|
||||
AWTKeyStroke stroke) {
|
||||
Set<AWTKeyStroke> keys = c.getFocusTraversalKeys(id);
|
||||
return keys != null && keys.contains(stroke);
|
||||
}
|
||||
|
||||
public void keyReleased(KeyEvent e) {
|
||||
}
|
||||
|
||||
public void keyTyped(KeyEvent e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,217 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @key headful
|
||||
* @bug 8249548
|
||||
* @summary Test focus traversal in button group containing JToggleButton
|
||||
* and JRadioButton
|
||||
* @run main TestButtonGroupFocusTraversal
|
||||
*/
|
||||
|
||||
import javax.swing.ButtonGroup;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JRadioButton;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.JToggleButton;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.KeyboardFocusManager;
|
||||
import java.awt.Point;
|
||||
import java.awt.Robot;
|
||||
import java.awt.event.KeyEvent;
|
||||
|
||||
public class TestButtonGroupFocusTraversal {
|
||||
private static JFrame frame;
|
||||
private static JTextField textFieldFirst, textFieldLast;
|
||||
private static JToggleButton toggleButton1, toggleButton2;
|
||||
private static JRadioButton radioButton1, radioButton2;
|
||||
private static Robot robot;
|
||||
|
||||
private static void blockTillDisplayed(Component comp) {
|
||||
Point p = null;
|
||||
while (p == null) {
|
||||
try {
|
||||
p = comp.getLocationOnScreen();
|
||||
} catch (IllegalStateException e) {
|
||||
try {
|
||||
Thread.sleep(500);
|
||||
} catch (InterruptedException ie) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void createUI() throws Exception {
|
||||
SwingUtilities.invokeAndWait(new Runnable() {
|
||||
public void run() {
|
||||
textFieldFirst = new JTextField("First");
|
||||
textFieldLast = new JTextField("Last");
|
||||
toggleButton1 = new JToggleButton("1");
|
||||
toggleButton2 = new JToggleButton("2");
|
||||
radioButton1 = new JRadioButton("1");
|
||||
radioButton2 = new JRadioButton("2");
|
||||
|
||||
ButtonGroup toggleGroup = new ButtonGroup();
|
||||
toggleGroup.add(toggleButton1);
|
||||
toggleGroup.add(toggleButton2);
|
||||
|
||||
ButtonGroup radioGroup = new ButtonGroup();
|
||||
radioGroup.add(radioButton1);
|
||||
radioGroup.add(radioButton2);
|
||||
|
||||
toggleButton2.setSelected(true);
|
||||
radioButton2.setSelected(true);
|
||||
|
||||
frame = new JFrame("Test");
|
||||
frame.setLayout(new FlowLayout());
|
||||
|
||||
Container pane = frame.getContentPane();
|
||||
pane.add(textFieldFirst);
|
||||
pane.add(toggleButton1);
|
||||
pane.add(toggleButton2);
|
||||
pane.add(radioButton1);
|
||||
pane.add(radioButton2);
|
||||
pane.add(textFieldLast);
|
||||
|
||||
frame.pack();
|
||||
frame.setAlwaysOnTop(true);
|
||||
frame.setLocationRelativeTo(null);
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
frame.setVisible(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void pressKey(int ...keys) {
|
||||
int num = keys.length;
|
||||
for (int i=0; i<num; i++)
|
||||
robot.keyPress(keys[i]);
|
||||
for (int i=num; i>0; i--)
|
||||
robot.keyRelease(keys[i-1]);
|
||||
|
||||
robot.waitForIdle();
|
||||
robot.delay(200);
|
||||
}
|
||||
|
||||
private static void checkFocusedComponent (Component component) {
|
||||
Component focusedComponent = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
|
||||
if (!focusedComponent.equals(component)) {
|
||||
System.out.println(component);
|
||||
System.out.println(focusedComponent);
|
||||
throw new RuntimeException("Wrong Component Selected");
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
robot = new Robot();
|
||||
robot.setAutoDelay(100);
|
||||
|
||||
UIManager.LookAndFeelInfo infos[] = UIManager.getInstalledLookAndFeels();
|
||||
for (UIManager.LookAndFeelInfo info : infos) {
|
||||
UIManager.setLookAndFeel(info.getClassName());
|
||||
System.out.println(info.getClassName());
|
||||
try {
|
||||
createUI();
|
||||
|
||||
robot.waitForIdle();
|
||||
robot.delay(200);
|
||||
|
||||
blockTillDisplayed(frame);
|
||||
|
||||
SwingUtilities.invokeAndWait(textFieldFirst::requestFocus);
|
||||
|
||||
if (!textFieldFirst.equals(KeyboardFocusManager.getCurrentKeyboardFocusManager()
|
||||
.getFocusOwner())) {
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
SwingUtilities.invokeAndWait(textFieldFirst::requestFocus);
|
||||
}
|
||||
|
||||
robot.waitForIdle();
|
||||
robot.delay(200);
|
||||
|
||||
pressKey(KeyEvent.VK_TAB);
|
||||
checkFocusedComponent(toggleButton2);
|
||||
|
||||
pressKey(KeyEvent.VK_TAB);
|
||||
checkFocusedComponent(radioButton2);
|
||||
|
||||
pressKey(KeyEvent.VK_TAB);
|
||||
checkFocusedComponent(textFieldLast);
|
||||
|
||||
pressKey(KeyEvent.VK_SHIFT, KeyEvent.VK_TAB);
|
||||
checkFocusedComponent(radioButton2);
|
||||
|
||||
pressKey(KeyEvent.VK_SHIFT, KeyEvent.VK_TAB);
|
||||
checkFocusedComponent(toggleButton2);
|
||||
|
||||
pressKey(KeyEvent.VK_SHIFT, KeyEvent.VK_TAB);
|
||||
checkFocusedComponent(textFieldFirst);
|
||||
|
||||
pressKey(KeyEvent.VK_TAB);
|
||||
checkFocusedComponent(toggleButton2);
|
||||
|
||||
pressKey(KeyEvent.VK_LEFT);
|
||||
checkFocusedComponent(toggleButton1);
|
||||
|
||||
pressKey(KeyEvent.VK_RIGHT);
|
||||
checkFocusedComponent(toggleButton2);
|
||||
|
||||
pressKey(KeyEvent.VK_UP);
|
||||
checkFocusedComponent(toggleButton1);
|
||||
|
||||
pressKey(KeyEvent.VK_DOWN);
|
||||
checkFocusedComponent(toggleButton2);
|
||||
|
||||
pressKey(KeyEvent.VK_TAB);
|
||||
checkFocusedComponent(radioButton2);
|
||||
|
||||
pressKey(KeyEvent.VK_LEFT);
|
||||
checkFocusedComponent(radioButton1);
|
||||
|
||||
pressKey(KeyEvent.VK_RIGHT);
|
||||
checkFocusedComponent(radioButton2);
|
||||
|
||||
pressKey(KeyEvent.VK_UP);
|
||||
checkFocusedComponent(radioButton1);
|
||||
|
||||
pressKey(KeyEvent.VK_DOWN);
|
||||
checkFocusedComponent(radioButton2);
|
||||
|
||||
} finally {
|
||||
if (frame != null) {
|
||||
SwingUtilities.invokeAndWait(frame::dispose);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user