TabbedPane: support Ctrl+TAB/Ctrl+Shift+TAB to switch to next/previous tab if a child of tabbedpane has focus

This commit is contained in:
Karl Tauber
2020-02-25 17:25:48 +01:00
parent 0d4f33ac6e
commit d1415a8c53
7 changed files with 113 additions and 3 deletions

View File

@@ -4,6 +4,8 @@ FlatLaf Change Log
## Unreleased ## Unreleased
- PasswordField: Warn about enabled Caps Lock. - PasswordField: Warn about enabled Caps Lock.
- TabbedPane: Support <kbd>Ctrl+TAB</kbd> / <kbd>Ctrl+Shift+TAB</kbd> to switch
to next / previous tab.
- TextField, FormattedTextField and PasswordField: Support round borders (see UI - TextField, FormattedTextField and PasswordField: Support round borders (see UI
default value `TextComponent.arc`). (issue #65) default value `TextComponent.arc`). (issue #65)
- IntelliJ Themes: Added Gradianto themes to demo. - IntelliJ Themes: Added Gradianto themes to demo.

View File

@@ -69,7 +69,14 @@ class FlatInputMaps
"ctrl PAGE_DOWN", "negativeBlockIncrement", "ctrl PAGE_DOWN", "negativeBlockIncrement",
"ctrl PAGE_UP", "positiveBlockIncrement" "ctrl PAGE_UP", "positiveBlockIncrement"
); );
}
modifyInputMap( defaults, "TabbedPane.ancestorInputMap",
"ctrl TAB", "navigateNext",
"shift ctrl TAB", "navigatePrevious"
);
if( !SystemInfo.IS_MAC ) {
modifyInputMap( defaults, "Tree.focusInputMap", modifyInputMap( defaults, "Tree.focusInputMap",
"ADD", "expand", "ADD", "expand",
"SUBTRACT", "collapse" "SUBTRACT", "collapse"

View File

@@ -26,15 +26,21 @@ import java.awt.FontMetrics;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.Insets; import java.awt.Insets;
import java.awt.KeyboardFocusManager;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.awt.Shape; import java.awt.Shape;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.geom.Path2D; import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.util.Collections;
import java.util.Set;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JTabbedPane; import javax.swing.JTabbedPane;
import javax.swing.KeyStroke;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource; import javax.swing.plaf.UIResource;
@@ -90,6 +96,9 @@ import com.formdev.flatlaf.util.UIScale;
public class FlatTabbedPaneUI public class FlatTabbedPaneUI
extends BasicTabbedPaneUI extends BasicTabbedPaneUI
{ {
private static Set<KeyStroke> focusForwardTraversalKeys;
private static Set<KeyStroke> focusBackwardTraversalKeys;
protected Color disabledForeground; protected Color disabledForeground;
protected Color selectedBackground; protected Color selectedBackground;
protected Color selectedForeground; protected Color selectedForeground;
@@ -142,11 +151,27 @@ public class FlatTabbedPaneUI
tabHeight = scale( tabHeight ); tabHeight = scale( tabHeight );
tabSelectionHeight = scale( tabSelectionHeight ); tabSelectionHeight = scale( tabSelectionHeight );
// replace focus forward/backward traversal keys with TAB/Shift+TAB because
// the default also includes Ctrl+TAB/Ctrl+Shift+TAB, which we need to switch tabs
if( focusForwardTraversalKeys == null ) {
focusForwardTraversalKeys = Collections.singleton( KeyStroke.getKeyStroke( KeyEvent.VK_TAB, 0 ) );
focusBackwardTraversalKeys = Collections.singleton( KeyStroke.getKeyStroke( KeyEvent.VK_TAB, InputEvent.SHIFT_MASK ) );
}
// Ideally we should use `LookAndFeel.installProperty( tabPane, "focusTraversalKeysForward", keys )` here
// instead of `tabPane.setFocusTraversalKeys()`, but WindowsTabbedPaneUI also uses later method
// and switching from Windows LaF to FlatLaf would not replace the keys and Ctrl+TAB would not work.
tabPane.setFocusTraversalKeys( KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, focusForwardTraversalKeys );
tabPane.setFocusTraversalKeys( KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, focusBackwardTraversalKeys );
MigLayoutVisualPadding.install( tabPane, null ); MigLayoutVisualPadding.install( tabPane, null );
} }
@Override @Override
protected void uninstallDefaults() { protected void uninstallDefaults() {
// restore focus forward/backward traversal keys
tabPane.setFocusTraversalKeys( KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, null );
tabPane.setFocusTraversalKeys( KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, null );
super.uninstallDefaults(); super.uninstallDefaults();
disabledForeground = null; disabledForeground = null;

View File

@@ -81,7 +81,11 @@ public class FlatContainerTest
JSplitPane splitPane3 = new JSplitPane(); JSplitPane splitPane3 = new JSplitPane();
JSplitPane splitPane1 = new JSplitPane(); JSplitPane splitPane1 = new JSplitPane();
JPanel panel10 = new JPanel(); JPanel panel10 = new JPanel();
JTextField textField2 = new JTextField();
JButton button1 = new JButton();
JPanel panel11 = new JPanel(); JPanel panel11 = new JPanel();
JTextField textField3 = new JTextField();
JButton button2 = new JButton();
JSplitPane splitPane2 = new JSplitPane(); JSplitPane splitPane2 = new JSplitPane();
JPanel panel12 = new JPanel(); JPanel panel12 = new JPanel();
JPanel panel13 = new JPanel(); JPanel panel13 = new JPanel();
@@ -89,7 +93,11 @@ public class FlatContainerTest
tabbedPane1 = new JTabbedPane(); tabbedPane1 = new JTabbedPane();
JPanel panel1 = new JPanel(); JPanel panel1 = new JPanel();
JLabel label1 = new JLabel(); JLabel label1 = new JLabel();
JTextField textField4 = new JTextField();
JButton button3 = new JButton();
JPanel panel2 = new JPanel(); JPanel panel2 = new JPanel();
JTextField textField5 = new JTextField();
JButton button4 = new JButton();
JLabel label2 = new JLabel(); JLabel label2 = new JLabel();
tabbedPane3 = new JTabbedPane(); tabbedPane3 = new JTabbedPane();
JPanel panel5 = new JPanel(); JPanel panel5 = new JPanel();
@@ -145,6 +153,14 @@ public class FlatContainerTest
{ {
panel10.setBackground(Color.orange); panel10.setBackground(Color.orange);
panel10.setLayout(new FlowLayout()); panel10.setLayout(new FlowLayout());
//---- textField2 ----
textField2.setText("some text");
panel10.add(textField2);
//---- button1 ----
button1.setText("...");
panel10.add(button1);
} }
splitPane1.setLeftComponent(panel10); splitPane1.setLeftComponent(panel10);
@@ -152,6 +168,14 @@ public class FlatContainerTest
{ {
panel11.setBackground(Color.magenta); panel11.setBackground(Color.magenta);
panel11.setLayout(new FlowLayout()); panel11.setLayout(new FlowLayout());
//---- textField3 ----
textField3.setText("some text");
panel11.add(textField3);
//---- button2 ----
button2.setText("...");
panel11.add(button2);
} }
splitPane1.setRightComponent(panel11); splitPane1.setRightComponent(panel11);
} }
@@ -195,6 +219,14 @@ public class FlatContainerTest
//---- label1 ---- //---- label1 ----
label1.setText("TOP"); label1.setText("TOP");
panel1.add(label1); panel1.add(label1);
//---- textField4 ----
textField4.setText("some text");
panel1.add(textField4);
//---- button3 ----
button3.setText("...");
panel1.add(button3);
} }
tabbedPane1.addTab("Tab 1", panel1); tabbedPane1.addTab("Tab 1", panel1);
@@ -202,6 +234,14 @@ public class FlatContainerTest
{ {
panel2.setBorder(new LineBorder(Color.magenta)); panel2.setBorder(new LineBorder(Color.magenta));
panel2.setLayout(new FlowLayout()); panel2.setLayout(new FlowLayout());
//---- textField5 ----
textField5.setText("some text");
panel2.add(textField5);
//---- button4 ----
button4.setText("...");
panel2.add(button4);
} }
tabbedPane1.addTab("Tab 2", panel2); tabbedPane1.addTab("Tab 2", panel2);

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "7.0.0.0.194" Java: "11.0.2" encoding: "UTF-8" JFDML JFormDesigner: "7.0.1.0.272" Java: "13.0.1" encoding: "UTF-8"
new FormModel { new FormModel {
contentType: "form/swing" contentType: "form/swing"
@@ -32,12 +32,28 @@ new FormModel {
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.FlowLayout ) ) { add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.FlowLayout ) ) {
name: "panel10" name: "panel10"
"background": sfield java.awt.Color orange "background": sfield java.awt.Color orange
add( new FormComponent( "javax.swing.JTextField" ) {
name: "textField2"
"text": "some text"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "button1"
"text": "..."
} )
}, new FormLayoutConstraints( class java.lang.String ) { }, new FormLayoutConstraints( class java.lang.String ) {
"value": "left" "value": "left"
} ) } )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.FlowLayout ) ) { add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.FlowLayout ) ) {
name: "panel11" name: "panel11"
"background": sfield java.awt.Color magenta "background": sfield java.awt.Color magenta
add( new FormComponent( "javax.swing.JTextField" ) {
name: "textField3"
"text": "some text"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "button2"
"text": "..."
} )
}, new FormLayoutConstraints( class java.lang.String ) { }, new FormLayoutConstraints( class java.lang.String ) {
"value": "right" "value": "right"
} ) } )
@@ -86,12 +102,28 @@ new FormModel {
name: "label1" name: "label1"
"text": "TOP" "text": "TOP"
} ) } )
add( new FormComponent( "javax.swing.JTextField" ) {
name: "textField4"
"text": "some text"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "button3"
"text": "..."
} )
}, new FormLayoutConstraints( null ) { }, new FormLayoutConstraints( null ) {
"title": "Tab 1" "title": "Tab 1"
} ) } )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.FlowLayout ) ) { add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.FlowLayout ) ) {
name: "panel2" name: "panel2"
"border": &LineBorder0 new javax.swing.border.LineBorder( sfield java.awt.Color magenta, 1, false ) "border": &LineBorder0 new javax.swing.border.LineBorder( sfield java.awt.Color magenta, 1, false )
add( new FormComponent( "javax.swing.JTextField" ) {
name: "textField5"
"text": "some text"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "button4"
"text": "..."
} )
}, new FormLayoutConstraints( null ) { }, new FormLayoutConstraints( null ) {
"title": "Tab 2" "title": "Tab 2"
} ) } )

View File

@@ -536,11 +536,13 @@ SplitPane.ancestorInputMap [lazy] 14 javax.swing.plaf.InputMapUIResource
#---- TabbedPane ---- #---- TabbedPane ----
TabbedPane.ancestorInputMap [lazy] 4 javax.swing.plaf.InputMapUIResource [UI] TabbedPane.ancestorInputMap [lazy] 6 javax.swing.plaf.InputMapUIResource [UI]
ctrl KP_UP requestFocus ctrl KP_UP requestFocus
ctrl PAGE_DOWN navigatePageDown ctrl PAGE_DOWN navigatePageDown
ctrl PAGE_UP navigatePageUp ctrl PAGE_UP navigatePageUp
ctrl TAB navigateNext
ctrl UP requestFocus ctrl UP requestFocus
shift ctrl TAB navigatePrevious
TabbedPane.focusInputMap [lazy] 10 javax.swing.plaf.InputMapUIResource [UI] TabbedPane.focusInputMap [lazy] 10 javax.swing.plaf.InputMapUIResource [UI]
ctrl DOWN requestFocusForVisibleComponent ctrl DOWN requestFocusForVisibleComponent
ctrl KP_DOWN requestFocusForVisibleComponent ctrl KP_DOWN requestFocusForVisibleComponent

View File

@@ -475,11 +475,13 @@ SplitPane.ancestorInputMap [lazy] 14 javax.swing.plaf.InputMapUIResource
#---- TabbedPane ---- #---- TabbedPane ----
TabbedPane.ancestorInputMap [lazy] 4 javax.swing.plaf.InputMapUIResource [UI] TabbedPane.ancestorInputMap [lazy] 6 javax.swing.plaf.InputMapUIResource [UI]
ctrl KP_UP requestFocus ctrl KP_UP requestFocus
ctrl PAGE_DOWN navigatePageDown ctrl PAGE_DOWN navigatePageDown
ctrl PAGE_UP navigatePageUp ctrl PAGE_UP navigatePageUp
ctrl TAB navigateNext
ctrl UP requestFocus ctrl UP requestFocus
shift ctrl TAB navigatePrevious
TabbedPane.focusInputMap [lazy] 10 javax.swing.plaf.InputMapUIResource [UI] TabbedPane.focusInputMap [lazy] 10 javax.swing.plaf.InputMapUIResource [UI]
ctrl DOWN requestFocusForVisibleComponent ctrl DOWN requestFocusForVisibleComponent
ctrl KP_DOWN requestFocusForVisibleComponent ctrl KP_DOWN requestFocusForVisibleComponent