Obtain currently highlighted item from JComboBox popup (not selected item)
highlight, java, jcombobox, selection, swing
Solution
Good question and good solution - except that there seems to be bug in accessibleCombo which doesn't update its internal wiring on updateUI, that is, when toggling the LAF:
- the accessible selection changes of the list are fired by an internal ListSelectionListener registered to the comboPopup list
- the comboPopup is controlled by the ui-delegate and re/created in installUI
- accessibleCombo doesn't update its internal list to the newly created and installed
Not much you can do about it. So I would listen directly to the list selection, then you have full control about re-wiring on LAF changes:
public static class XComboBox extends JComboBox {
private ListSelectionListener listener;
public XComboBox() {
uninstall();
install();
}
@Override
public void updateUI() {
uninstall();
super.updateUI();
install();
}
private void uninstall() {
if (listener == null) return;
getPopupList().removeListSelectionListener(listener);
listener = null;
}
protected void install() {
listener = new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
if (e.getValueIsAdjusting()) return;
JList list = getPopupList();
System.out.println("--> " + String.valueOf(list.getSelectedValue()));
}
};
getPopupList().addListSelectionListener(listener);
}
private JList getPopupList() {
ComboPopup popup = (ComboPopup) getUI().getAccessibleChild(this, 0);
return popup.getList();
}
}
Problem
I'd like to be able to react when currently highlighted item in a `JComboBox` drop down list changes. Note that I'm not looking for a way to get the currently selected item, but the highlighted one. When mouse hovers over this popup it highlights the item at mouse position, but this does not affect currently selected item, so I cannot simply listen via an `ItemListener` or `ActionListener` to achieve what I want. I'm trying to create a component which consists of a `JComboBox` and a coupled tooltip which displays additional information (documentation) for the currently highlighted item. As my first attempt I'm adding some code to my constructor (extended `JComboBox`): ``` import java.awt.BorderLayout; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.accessibility.AccessibleContext; import javax.accessibility.AccessibleState; import javax.swing.DefaultComboBoxModel; import javax.swing.JComboBox; import javax.swing.JFrame; import javax.swing.JList; import javax.swing.SwingUtilities; import javax.swing.plaf.basic.ComboPopup; public class SomeFrame extends JFrame { private MyComboBox combo; public SomeFrame() { setDefaultCloseOperation(DISPOSE_ON_CLOSE); setSize(100,20); setLocationRelativeTo(null); setLayout(new BorderLayout()); combo = new MyComboBox(); combo.setModel(new DefaultComboBoxModel(new String[]{"one", "two", "three", "four"})); add(combo); pack(); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { SomeFrame frame = new SomeFrame(); frame.setVisible(true); } }); } // this is the important part private static class MyComboBox extends JComboBox { public MyComboBox() { getAccessibleContext().addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { if (AccessibleContext.ACCESSIBLE_STATE_PROPERTY.equals(evt.getPropertyName()) && AccessibleState.FOCUSED.equals(evt.getNewValue()) && getAccessibleContext().getAccessibleChild(0) instanceof ComboPopup) { ComboPopup popup = (ComboPopup) getAccessibleContext().getAccessibleChild(0); JList list = popup.getList(); System.out.println("--> " + String.valueOf(list.getSelectedValue())); } } }); } } } ``` It seems to work, but I got to this code through some shady channels and trial/error, so I'm thinking there has to be a better way of doing it. Any ideas? Is above code even production safe?