ToolBar: support arrow-keys-only navigation within focusable buttons of toolbar:

- arrow keys move focus within toolbar (provided by `BasicToolBarUI`)
- tab-key moves focus out of toolbar
- if moving focus into the toolbar, focus recently focused toolbar button

(issue #346)
This commit is contained in:
Karl Tauber
2021-10-05 16:36:50 +02:00
parent 1e93deab2a
commit 69042e42b7
7 changed files with 103 additions and 3 deletions

View File

@@ -20,6 +20,11 @@ FlatLaf Change Log
- Skip components with empty input map (e.g. `JLabel`) when using arrow keys
to navigate in focusable buttons (if UI value `ToolBar.focusableButtons` is
`true`).
- Support arrow-keys-only navigation within focusable buttons of toolbar (if
UI value `ToolBar.focusableButtons` is `true`):
- arrow keys move focus within toolbar
- tab-key moves focus out of toolbar
- if moving focus into the toolbar, focus recently focused toolbar button
- Added more color functions to class `ColorFunctions` for easy use in
applications: `lighten()`, `darken()`, `saturate()`, `desaturate()`, `spin()`,
`tint()`, `shade()` and `luma()`.

View File

@@ -18,6 +18,8 @@ package com.formdev.flatlaf.ui;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.FocusTraversalPolicy;
import java.awt.Insets;
import java.awt.event.ContainerEvent;
import java.awt.event.ContainerListener;
@@ -26,6 +28,7 @@ import java.util.Map;
import javax.swing.AbstractButton;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.LayoutFocusTraversalPolicy;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.plaf.ComponentUI;
@@ -52,6 +55,7 @@ import com.formdev.flatlaf.util.LoggingFacade;
* <!-- FlatToolBarUI -->
*
* @uiDefault ToolBar.focusableButtons boolean
* @uiDefault ToolBar.arrowKeysOnlyNavigation boolean
* @uiDefault ToolBar.floatable boolean
*
* <!-- FlatToolBarBorder -->
@@ -65,13 +69,14 @@ public class FlatToolBarUI
extends BasicToolBarUI
implements StyleableUI
{
/** @since 1.4 */
@Styleable protected boolean focusableButtons;
/** @since 1.4 */ @Styleable protected boolean focusableButtons;
/** @since 2 */ @Styleable protected boolean arrowKeysOnlyNavigation;
// for FlatToolBarBorder
@Styleable protected Insets borderMargins;
@Styleable protected Color gripColor;
private FocusTraversalPolicy focusTraversalPolicy;
private Boolean oldFloatable;
private Map<String, Object> oldStyleValues;
@@ -83,6 +88,8 @@ public class FlatToolBarUI
public void installUI( JComponent c ) {
super.installUI( c );
installFocusTraversalPolicy();
installStyle();
// disable focusable state of buttons (when switching from another Laf)
@@ -100,6 +107,8 @@ public class FlatToolBarUI
if( !focusableButtons )
setButtonsFocusable( true );
uninstallFocusTraversalPolicy();
oldStyleValues = null;
}
@@ -108,6 +117,7 @@ public class FlatToolBarUI
super.installDefaults();
focusableButtons = UIManager.getBoolean( "ToolBar.focusableButtons" );
arrowKeysOnlyNavigation = UIManager.getBoolean( "ToolBar.arrowKeysOnlyNavigation" );
// floatable
if( !UIManager.getBoolean( "ToolBar.floatable" ) ) {
@@ -165,11 +175,18 @@ public class FlatToolBarUI
/** @since 2 */
protected void applyStyle( Object style ) {
boolean oldFocusableButtons = focusableButtons;
boolean oldArrowKeysOnlyNavigation = arrowKeysOnlyNavigation;
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
if( focusableButtons != oldFocusableButtons )
setButtonsFocusable( focusableButtons );
if( arrowKeysOnlyNavigation != oldArrowKeysOnlyNavigation || focusableButtons != oldFocusableButtons ) {
if( arrowKeysOnlyNavigation )
installFocusTraversalPolicy();
else
uninstallFocusTraversalPolicy();
}
}
/** @since 2 */
@@ -194,6 +211,32 @@ public class FlatToolBarUI
c.setFocusable( focusable );
}
/** @since 2 */
protected void installFocusTraversalPolicy() {
if( !arrowKeysOnlyNavigation || !focusableButtons || toolBar.getFocusTraversalPolicy() != null )
return;
focusTraversalPolicy = createFocusTraversalPolicy();
if( focusTraversalPolicy != null ) {
toolBar.setFocusTraversalPolicy( focusTraversalPolicy );
toolBar.setFocusTraversalPolicyProvider( true );
}
}
/** @since 2 */
protected void uninstallFocusTraversalPolicy() {
if( focusTraversalPolicy != null && toolBar.getFocusTraversalPolicy() == focusTraversalPolicy ) {
toolBar.setFocusTraversalPolicy( null );
toolBar.setFocusTraversalPolicyProvider( false );
}
focusTraversalPolicy = null;
}
/** @since 2 */
protected FocusTraversalPolicy createFocusTraversalPolicy() {
return new FlatToolBarFocusTraversalPolicy();
}
/**
* Does the same as super.navigateFocusedComp() with the exception that components
* with empty input map (e.g. JLabel) are skipped.
@@ -262,4 +305,51 @@ public class FlatToolBarUI
super.setOrientation( orientation );
}
//---- class FlatToolBarFocusTraversalPolicy ------------------------------
/**
* Focus traversal policy used for toolbar to modify traversal behaviour:
* <ul>
* <li>Tab-key moves focus out of toolbar.</li>
* <li>If moving focus into the toolbar, focus recently focused toolbar button.</li>
* </ul>
*
* @since 2
*/
protected class FlatToolBarFocusTraversalPolicy
extends LayoutFocusTraversalPolicy
{
@Override
public Component getComponentAfter( Container aContainer, Component aComponent ) {
// move focus out of toolbar
return null;
}
@Override
public Component getComponentBefore( Container aContainer, Component aComponent ) {
// move focus out of toolbar
return null;
}
@Override
public Component getFirstComponent( Container aContainer ) {
return getRecentComponent( aContainer, true );
}
@Override
public Component getLastComponent( Container aContainer ) {
return getRecentComponent( aContainer, false );
}
private Component getRecentComponent( Container aContainer, boolean first ) {
// if moving focus into the toolbar, focus recently focused toolbar button
if( focusedCompIndex >= 0 && focusedCompIndex < toolBar.getComponentCount() )
return toolBar.getComponent( focusedCompIndex );
return first
? super.getFirstComponent( aContainer )
: super.getLastComponent( aContainer );
}
}
}

View File

@@ -760,8 +760,9 @@ ToggleButton.tab.focusBackground = $TabbedPane.focusColor
ToolBar.border = com.formdev.flatlaf.ui.FlatToolBarBorder
ToolBar.borderMargins = 2,2,2,2
ToolBar.isRollover = true
ToolBar.floatable = false
ToolBar.focusableButtons = false
ToolBar.arrowKeysOnlyNavigation = true
ToolBar.floatable = false
ToolBar.gripColor = @icon
ToolBar.dockingBackground = darken($ToolBar.background,5%)
ToolBar.dockingForeground = $Component.borderColor

View File

@@ -1262,6 +1262,7 @@ ToggleButtonUI com.formdev.flatlaf.ui.FlatToggleButtonUI
#---- ToolBar ----
ToolBar.arrowKeysOnlyNavigation true
ToolBar.background #3c3f41 HSL 204 4 25 javax.swing.plaf.ColorUIResource [UI]
ToolBar.border [lazy] 2,2,2,2 false com.formdev.flatlaf.ui.FlatToolBarBorder [UI]
ToolBar.borderMargins 2,2,2,2 javax.swing.plaf.InsetsUIResource [UI]

View File

@@ -1267,6 +1267,7 @@ ToggleButtonUI com.formdev.flatlaf.ui.FlatToggleButtonUI
#---- ToolBar ----
ToolBar.arrowKeysOnlyNavigation true
ToolBar.background #f2f2f2 HSL 0 0 95 javax.swing.plaf.ColorUIResource [UI]
ToolBar.border [lazy] 2,2,2,2 false com.formdev.flatlaf.ui.FlatToolBarBorder [UI]
ToolBar.borderMargins 2,2,2,2 javax.swing.plaf.InsetsUIResource [UI]

View File

@@ -1275,6 +1275,7 @@ ToggleButtonUI com.formdev.flatlaf.ui.FlatToggleButtonUI
#---- ToolBar ----
ToolBar.arrowKeysOnlyNavigation true
ToolBar.background #ccffcc HSL 120 100 90 javax.swing.plaf.ColorUIResource [UI]
ToolBar.border [lazy] 2,2,2,2 false com.formdev.flatlaf.ui.FlatToolBarBorder [UI]
ToolBar.borderMargins 2,2,2,2 javax.swing.plaf.InsetsUIResource [UI]

View File

@@ -983,6 +983,7 @@ ToggleButton.toolbar.pressedBackground
ToggleButton.toolbar.selectedBackground
ToggleButtonUI
ToolBar.ancestorInputMap
ToolBar.arrowKeysOnlyNavigation
ToolBar.background
ToolBar.border
ToolBar.borderMargins