From d1415a8c534134836f8f2bdeac044bab0b1cf011 Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Tue, 25 Feb 2020 17:25:48 +0100 Subject: [PATCH] TabbedPane: support Ctrl+TAB/Ctrl+Shift+TAB to switch to next/previous tab if a child of tabbedpane has focus --- CHANGELOG.md | 2 + .../com/formdev/flatlaf/FlatInputMaps.java | 7 ++++ .../formdev/flatlaf/ui/FlatTabbedPaneUI.java | 25 ++++++++++++ .../flatlaf/testing/FlatContainerTest.java | 40 +++++++++++++++++++ .../flatlaf/testing/FlatContainerTest.jfd | 34 +++++++++++++++- .../FlatLightLaf_InputMap_1.8.0_202-mac.txt | 4 +- .../FlatLightLaf_InputMap_1.8.0_202.txt | 4 +- 7 files changed, 113 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70f0e688..cbef6961 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ FlatLaf Change Log ## Unreleased - PasswordField: Warn about enabled Caps Lock. +- TabbedPane: Support Ctrl+TAB / Ctrl+Shift+TAB to switch + to next / previous tab. - TextField, FormattedTextField and PasswordField: Support round borders (see UI default value `TextComponent.arc`). (issue #65) - IntelliJ Themes: Added Gradianto themes to demo. diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatInputMaps.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatInputMaps.java index 57c5822e..55450875 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatInputMaps.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatInputMaps.java @@ -69,7 +69,14 @@ class FlatInputMaps "ctrl PAGE_DOWN", "negativeBlockIncrement", "ctrl PAGE_UP", "positiveBlockIncrement" ); + } + modifyInputMap( defaults, "TabbedPane.ancestorInputMap", + "ctrl TAB", "navigateNext", + "shift ctrl TAB", "navigatePrevious" + ); + + if( !SystemInfo.IS_MAC ) { modifyInputMap( defaults, "Tree.focusInputMap", "ADD", "expand", "SUBTRACT", "collapse" diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTabbedPaneUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTabbedPaneUI.java index 5466dc0a..b4861f2b 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTabbedPaneUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTabbedPaneUI.java @@ -26,15 +26,21 @@ import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Insets; +import java.awt.KeyboardFocusManager; import java.awt.Rectangle; import java.awt.Shape; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; import java.awt.geom.Path2D; import java.awt.geom.Rectangle2D; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.util.Collections; +import java.util.Set; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JTabbedPane; +import javax.swing.KeyStroke; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.UIResource; @@ -90,6 +96,9 @@ import com.formdev.flatlaf.util.UIScale; public class FlatTabbedPaneUI extends BasicTabbedPaneUI { + private static Set focusForwardTraversalKeys; + private static Set focusBackwardTraversalKeys; + protected Color disabledForeground; protected Color selectedBackground; protected Color selectedForeground; @@ -142,11 +151,27 @@ public class FlatTabbedPaneUI tabHeight = scale( tabHeight ); 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 ); } @Override 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(); disabledForeground = null; diff --git a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatContainerTest.java b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatContainerTest.java index 49d208cf..daf5779e 100644 --- a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatContainerTest.java +++ b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatContainerTest.java @@ -81,7 +81,11 @@ public class FlatContainerTest JSplitPane splitPane3 = new JSplitPane(); JSplitPane splitPane1 = new JSplitPane(); JPanel panel10 = new JPanel(); + JTextField textField2 = new JTextField(); + JButton button1 = new JButton(); JPanel panel11 = new JPanel(); + JTextField textField3 = new JTextField(); + JButton button2 = new JButton(); JSplitPane splitPane2 = new JSplitPane(); JPanel panel12 = new JPanel(); JPanel panel13 = new JPanel(); @@ -89,7 +93,11 @@ public class FlatContainerTest tabbedPane1 = new JTabbedPane(); JPanel panel1 = new JPanel(); JLabel label1 = new JLabel(); + JTextField textField4 = new JTextField(); + JButton button3 = new JButton(); JPanel panel2 = new JPanel(); + JTextField textField5 = new JTextField(); + JButton button4 = new JButton(); JLabel label2 = new JLabel(); tabbedPane3 = new JTabbedPane(); JPanel panel5 = new JPanel(); @@ -145,6 +153,14 @@ public class FlatContainerTest { panel10.setBackground(Color.orange); panel10.setLayout(new FlowLayout()); + + //---- textField2 ---- + textField2.setText("some text"); + panel10.add(textField2); + + //---- button1 ---- + button1.setText("..."); + panel10.add(button1); } splitPane1.setLeftComponent(panel10); @@ -152,6 +168,14 @@ public class FlatContainerTest { panel11.setBackground(Color.magenta); panel11.setLayout(new FlowLayout()); + + //---- textField3 ---- + textField3.setText("some text"); + panel11.add(textField3); + + //---- button2 ---- + button2.setText("..."); + panel11.add(button2); } splitPane1.setRightComponent(panel11); } @@ -195,6 +219,14 @@ public class FlatContainerTest //---- label1 ---- label1.setText("TOP"); panel1.add(label1); + + //---- textField4 ---- + textField4.setText("some text"); + panel1.add(textField4); + + //---- button3 ---- + button3.setText("..."); + panel1.add(button3); } tabbedPane1.addTab("Tab 1", panel1); @@ -202,6 +234,14 @@ public class FlatContainerTest { panel2.setBorder(new LineBorder(Color.magenta)); panel2.setLayout(new FlowLayout()); + + //---- textField5 ---- + textField5.setText("some text"); + panel2.add(textField5); + + //---- button4 ---- + button4.setText("..."); + panel2.add(button4); } tabbedPane1.addTab("Tab 2", panel2); diff --git a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatContainerTest.jfd b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatContainerTest.jfd index d850940b..8fc771a9 100644 --- a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatContainerTest.jfd +++ b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatContainerTest.jfd @@ -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 { contentType: "form/swing" @@ -32,12 +32,28 @@ new FormModel { add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.FlowLayout ) ) { name: "panel10" "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 ) { "value": "left" } ) add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.FlowLayout ) ) { name: "panel11" "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 ) { "value": "right" } ) @@ -86,12 +102,28 @@ new FormModel { name: "label1" "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 ) { "title": "Tab 1" } ) add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.FlowLayout ) ) { name: "panel2" "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 ) { "title": "Tab 2" } ) diff --git a/flatlaf-testing/src/main/resources/com/formdev/flatlaf/testing/uidefaults/FlatLightLaf_InputMap_1.8.0_202-mac.txt b/flatlaf-testing/src/main/resources/com/formdev/flatlaf/testing/uidefaults/FlatLightLaf_InputMap_1.8.0_202-mac.txt index ff924a50..f680e51e 100644 --- a/flatlaf-testing/src/main/resources/com/formdev/flatlaf/testing/uidefaults/FlatLightLaf_InputMap_1.8.0_202-mac.txt +++ b/flatlaf-testing/src/main/resources/com/formdev/flatlaf/testing/uidefaults/FlatLightLaf_InputMap_1.8.0_202-mac.txt @@ -536,11 +536,13 @@ SplitPane.ancestorInputMap [lazy] 14 javax.swing.plaf.InputMapUIResource #---- TabbedPane ---- -TabbedPane.ancestorInputMap [lazy] 4 javax.swing.plaf.InputMapUIResource [UI] +TabbedPane.ancestorInputMap [lazy] 6 javax.swing.plaf.InputMapUIResource [UI] ctrl KP_UP requestFocus ctrl PAGE_DOWN navigatePageDown ctrl PAGE_UP navigatePageUp + ctrl TAB navigateNext ctrl UP requestFocus + shift ctrl TAB navigatePrevious TabbedPane.focusInputMap [lazy] 10 javax.swing.plaf.InputMapUIResource [UI] ctrl DOWN requestFocusForVisibleComponent ctrl KP_DOWN requestFocusForVisibleComponent diff --git a/flatlaf-testing/src/main/resources/com/formdev/flatlaf/testing/uidefaults/FlatLightLaf_InputMap_1.8.0_202.txt b/flatlaf-testing/src/main/resources/com/formdev/flatlaf/testing/uidefaults/FlatLightLaf_InputMap_1.8.0_202.txt index dd37c8ab..16db08bf 100644 --- a/flatlaf-testing/src/main/resources/com/formdev/flatlaf/testing/uidefaults/FlatLightLaf_InputMap_1.8.0_202.txt +++ b/flatlaf-testing/src/main/resources/com/formdev/flatlaf/testing/uidefaults/FlatLightLaf_InputMap_1.8.0_202.txt @@ -475,11 +475,13 @@ SplitPane.ancestorInputMap [lazy] 14 javax.swing.plaf.InputMapUIResource #---- TabbedPane ---- -TabbedPane.ancestorInputMap [lazy] 4 javax.swing.plaf.InputMapUIResource [UI] +TabbedPane.ancestorInputMap [lazy] 6 javax.swing.plaf.InputMapUIResource [UI] ctrl KP_UP requestFocus ctrl PAGE_DOWN navigatePageDown ctrl PAGE_UP navigatePageUp + ctrl TAB navigateNext ctrl UP requestFocus + shift ctrl TAB navigatePrevious TabbedPane.focusInputMap [lazy] 10 javax.swing.plaf.InputMapUIResource [UI] ctrl DOWN requestFocusForVisibleComponent ctrl KP_DOWN requestFocusForVisibleComponent