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() ) );
+ }
+ }
+ }
}