diff --git a/CHANGELOG.md b/CHANGELOG.md index 277f3789..bcffb291 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ FlatLaf Change Log longer `null`. - Tree on macOS: Fixed Left and Right keys to collapse or expand nodes. +- ComboBox on macOS: Fixed keyboard navigation and show/hide popup. - Button and ToggleButton: Support per component minimum height (set client property `JComponent.minimumHeight` to an integer). (issue #44) - Button and ToggleButton: Do not apply minimum width if button border was diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatLaf.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatLaf.java index 0f1c7aa4..b02e76f4 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatLaf.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatLaf.java @@ -344,9 +344,26 @@ public abstract class FlatLaf private void initInputMaps( UIDefaults defaults ) { if( SystemInfo.IS_MAC ) { // AquaLookAndFeel (the base for UI defaults on macOS) uses special - // action keys (e.g. "aquaExpandNode") for its macOS typical tree node - // expanding/collapsing, which are not available in FlatLaf. - // --> map the keys back to default Java actions + // action keys (e.g. "aquaExpandNode") for some macOS specific behaviour. + // Those action keys are not available in FlatLaf, which makes it + // necessary to make some modifications. + + // combobox + defaults.put( "ComboBox.ancestorInputMap", new UIDefaults.LazyInputMap( new Object[] { + "ESCAPE", "hidePopup", + "PAGE_UP", "pageUpPassThrough", + "PAGE_DOWN", "pageDownPassThrough", + "HOME", "homePassThrough", + "END", "endPassThrough", + "DOWN", "selectNext", + "KP_DOWN", "selectNext", + "SPACE", "spacePopup", + "ENTER", "enterPressed", + "UP", "selectPrevious", + "KP_UP", "selectPrevious" + } ) ); + + // tree node expanding/collapsing modifyInputMap( defaults, "Tree.focusInputMap", "RIGHT", "selectChild", "KP_RIGHT", "selectChild", @@ -486,6 +503,9 @@ public abstract class FlatLaf //---- class LazyModifyInputMap ------------------------------------------- + /** + * Takes a (lazy) base input map and lazily applies modifications to it specified in bindings. + */ private static class LazyModifyInputMap implements LazyValue { @@ -515,6 +535,5 @@ public abstract class FlatLaf return inputMap; } - } } diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatComboBoxUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatComboBoxUI.java index acf56ced..166efa36 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatComboBoxUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatComboBoxUI.java @@ -28,6 +28,8 @@ import java.awt.Insets; import java.awt.LayoutManager; import java.awt.Rectangle; import java.awt.Shape; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.MouseListener; @@ -35,13 +37,16 @@ import java.awt.geom.Rectangle2D; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.lang.ref.WeakReference; +import javax.swing.AbstractAction; import javax.swing.BorderFactory; import javax.swing.DefaultListCellRenderer; +import javax.swing.InputMap; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JList; import javax.swing.JPanel; +import javax.swing.KeyStroke; import javax.swing.ListCellRenderer; import javax.swing.LookAndFeel; import javax.swing.SwingConstants; @@ -54,6 +59,7 @@ import javax.swing.plaf.basic.BasicComboPopup; import javax.swing.plaf.basic.ComboPopup; import javax.swing.text.JTextComponent; import com.formdev.flatlaf.FlatClientProperties; +import com.formdev.flatlaf.util.SystemInfo; import com.formdev.flatlaf.util.UIScale; /** @@ -271,6 +277,18 @@ public class FlatComboBoxUI editor.applyComponentOrientation( comboBox.getComponentOrientation() ); updateEditorColors(); + + // macOS + if( SystemInfo.IS_MAC && editor instanceof JTextComponent ) { + // delegate actions from editor text field to combobox, which is necessary + // because text field on macOS (based on Aqua LaF UI defaults) + // already handle those keys + InputMap inputMap = ((JTextComponent)editor).getInputMap(); + new EditorDelegateAction( inputMap, KeyStroke.getKeyStroke( "UP" ) ); + new EditorDelegateAction( inputMap, KeyStroke.getKeyStroke( "KP_UP" ) ); + new EditorDelegateAction( inputMap, KeyStroke.getKeyStroke( "DOWN" ) ); + new EditorDelegateAction( inputMap, KeyStroke.getKeyStroke( "KP_DOWN" ) ); + } } private void updateEditorColors() { @@ -608,4 +626,31 @@ public class FlatComboBoxUI rendererBorder.paintBorder( c, g, x, y, width, height ); } } + + //---- class EditorDelegateAction ----------------------------------------- + + /** + * Delegates actions from editor text field to combobox. + */ + private class EditorDelegateAction + extends AbstractAction + { + private final KeyStroke keyStroke; + + EditorDelegateAction( InputMap inputMap, KeyStroke keyStroke ) { + this.keyStroke = keyStroke; + + // add to input map + inputMap.put( keyStroke, this ); + } + + @Override + public void actionPerformed( ActionEvent e ) { + ActionListener action = comboBox.getActionForKeyStroke( keyStroke ); + if( action != null ) { + action.actionPerformed( new ActionEvent( comboBox, e.getID(), + e.getActionCommand(), e.getWhen(), e.getModifiers() ) ); + } + } + } }