Construct itemGroup within SearchableAndCategorizableComboBox

This commit is contained in:
SiboVG 2024-08-07 21:22:59 +02:00
parent 354843deb9
commit b0feb33459
4 changed files with 51 additions and 84 deletions

View File

@ -28,11 +28,7 @@ import java.awt.Component;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* Panel for configuring a component's material and finish properties. * Panel for configuring a component's material and finish properties.
@ -63,11 +59,6 @@ public class MaterialPanel extends JPanel implements Invalidatable, Invalidating
@Override @Override
public void run() { public void run() {
mm.addCustomMaterial(); mm.addCustomMaterial();
if (MaterialPanel.this.materialCombo != null) {
MaterialComboBox.updateComboBoxItems(MaterialPanel.this.materialCombo, MaterialGroup.ALL_GROUPS,
mm.getAllMaterials());
MaterialPanel.this.materialCombo.setSelectedItem(mm.getSelectedItem());
}
} }
}); });
}); });
@ -89,8 +80,7 @@ public class MaterialPanel extends JPanel implements Invalidatable, Invalidating
}); });
// Material selection combo box // Material selection combo box
this.materialCombo = MaterialComboBox.createComboBox(mm, MaterialGroup.ALL_GROUPS, mm.getAllMaterials(), this.materialCombo = MaterialComboBox.createComboBox(mm, customMaterialButton, editMaterialsButton);
customMaterialButton, editMaterialsButton);
this.materialCombo.setSelectedItem(mm.getSelectedItem()); this.materialCombo.setSelectedItem(mm.getSelectedItem());
this.materialCombo.setToolTipText(trans.get("MaterialPanel.combo.ttip.ComponentMaterialAffects")); this.materialCombo.setToolTipText(trans.get("MaterialPanel.combo.ttip.ComponentMaterialAffects"));
this.add(this.materialCombo, "spanx 4, growx, wrap paragraph"); this.add(this.materialCombo, "spanx 4, growx, wrap paragraph");
@ -171,10 +161,8 @@ public class MaterialPanel extends JPanel implements Invalidatable, Invalidating
private static final Translator trans = Application.getTranslator(); private static final Translator trans = Application.getTranslator();
public static SearchableAndCategorizableComboBox<MaterialGroup, Material> createComboBox( public static SearchableAndCategorizableComboBox<MaterialGroup, Material> createComboBox(
MaterialModel mm, MaterialGroup[] allGroups, Material[] materials, Component... extraCategoryWidgets) { MaterialModel mm, Component... extraCategoryWidgets) {
final Map<MaterialGroup, List<Material>> materialGroupMap = return new SearchableAndCategorizableComboBox<>(mm,
createMaterialGroupMap(allGroups, materials);
return new SearchableAndCategorizableComboBox<>(mm, materialGroupMap,
trans.get("MaterialPanel.MaterialComboBox.placeholder"), extraCategoryWidgets) { trans.get("MaterialPanel.MaterialComboBox.placeholder"), extraCategoryWidgets) {
@Override @Override
public String getDisplayString(Material item) { public String getDisplayString(Material item) {
@ -186,47 +174,5 @@ public class MaterialPanel extends JPanel implements Invalidatable, Invalidating
} }
}; };
} }
public static void updateComboBoxItems(SearchableAndCategorizableComboBox<MaterialGroup, Material> comboBox,
MaterialGroup[] allGroups, Material[] materials) {
final Map<MaterialGroup, List<Material>> materialGroupMap = createMaterialGroupMap(allGroups, materials);
comboBox.updateItems(materialGroupMap);
comboBox.invalidate();
comboBox.repaint();
}
/**
* Create a map of material group and corresponding material.
* @param groups the groups
* @param materials the materials
* @return the map linking the materials to their groups
*/
private static Map<MaterialGroup, List<Material>> createMaterialGroupMap(
MaterialGroup[] groups, Material[] materials) {
// Sort the groups based on priority (lower number = higher priority)
MaterialGroup[] sortedGroups = groups.clone();
Arrays.sort(sortedGroups, Comparator.comparingInt(MaterialGroup::getPriority));
Map<MaterialGroup, List<Material>> map = new LinkedHashMap<>();
MaterialGroup materialGroup;
for (MaterialGroup group : sortedGroups) {
List<Material> itemsForGroup = new ArrayList<>();
for (Material material : materials) {
materialGroup = material.getGroup();
if (materialGroup.equals(group)) {
itemsForGroup.add(material);
}
}
// Sort the types within each group based on priority
itemsForGroup.sort(Comparator.comparingInt(Material::getGroupPriority));
map.put(group, itemsForGroup);
}
// Remove empty groups
map.entrySet().removeIf(entry -> entry.getValue().isEmpty());
return map;
}
} }
} }

View File

@ -17,9 +17,8 @@ import java.util.Map;
public class FlightDataComboBox extends JComboBox<FlightDataType> { public class FlightDataComboBox extends JComboBox<FlightDataType> {
private static final Translator trans = Application.getTranslator(); private static final Translator trans = Application.getTranslator();
public static SearchableAndCategorizableComboBox<FlightDataTypeGroup, FlightDataType> createComboBox(FlightDataTypeGroup[] allGroups, FlightDataType[] types) { public static SearchableAndCategorizableComboBox<FlightDataTypeGroup, FlightDataType> createComboBox(List<FlightDataType> types) {
final Map<FlightDataTypeGroup, List<FlightDataType>> typeGroupMap = createFlightDataGroupMap(allGroups, types); return new SearchableAndCategorizableComboBox<>(types, trans.get("FlightDataComboBox.placeholder"));
return new SearchableAndCategorizableComboBox<>(typeGroupMap, trans.get("FlightDataComboBox.placeholder"));
} }
/** /**

View File

@ -173,7 +173,7 @@ public class SimulationPlotPanel extends JPanel {
//// X axis type: //// X axis type:
this.add(new JLabel(trans.get("simplotpanel.lbl.Xaxistype")), "spanx, split"); this.add(new JLabel(trans.get("simplotpanel.lbl.Xaxistype")), "spanx, split");
domainTypeSelector = FlightDataComboBox.createComboBox(FlightDataTypeGroup.ALL_GROUPS, types); domainTypeSelector = FlightDataComboBox.createComboBox(Arrays.asList(types));
domainTypeSelector.setSelectedItem(configuration.getDomainAxisType()); domainTypeSelector.setSelectedItem(configuration.getDomainAxisType());
domainTypeSelector.addItemListener(new ItemListener() { domainTypeSelector.addItemListener(new ItemListener() {
@Override @Override
@ -497,7 +497,7 @@ public class SimulationPlotPanel extends JPanel {
this.index = plotIndex; this.index = plotIndex;
typeSelector = FlightDataComboBox.createComboBox(FlightDataTypeGroup.ALL_GROUPS, types); typeSelector = FlightDataComboBox.createComboBox(Arrays.asList(types));
typeSelector.setSelectedItem(type); typeSelector.setSelectedItem(type);
typeSelector.addItemListener(new ItemListener() { typeSelector.addItemListener(new ItemListener() {
@Override @Override

View File

@ -28,6 +28,8 @@ import java.awt.Component;
import java.awt.EventQueue; import java.awt.EventQueue;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Point; import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter; import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent; import java.awt.event.FocusEvent;
import java.awt.event.KeyAdapter; import java.awt.event.KeyAdapter;
@ -45,8 +47,10 @@ import java.util.Iterator;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.Vector; import java.util.Vector;
@ -63,12 +67,12 @@ public class SearchableAndCategorizableComboBox<G extends Group, T extends Group
private static final int CHECKMARK_X_OFFSET = 5; private static final int CHECKMARK_X_OFFSET = 5;
private static final int CHECKMARK_Y_OFFSET = 5; private static final int CHECKMARK_Y_OFFSET = 5;
private final String placeHolderText; private String placeHolderText;
private JPopupMenu categoryPopup; private JPopupMenu categoryPopup;
private JPopupMenu searchPopup; private JPopupMenu searchPopup;
private PlaceholderTextField searchFieldCategory; private PlaceholderTextField searchFieldCategory;
private PlaceholderTextField searchFieldSearch; private PlaceholderTextField searchFieldSearch;
private final Component[] extraCategoryWidgets; private Component[] extraCategoryWidgets;
private JList<T> filteredList; private JList<T> filteredList;
private Map<G, List<T>> itemGroupMap; private Map<G, List<T>> itemGroupMap;
@ -84,19 +88,34 @@ public class SearchableAndCategorizableComboBox<G extends Group, T extends Group
/** /**
* Create a searchable and categorizable combo box. * Create a searchable and categorizable combo box.
* @param itemGroupMap the map of items and their corresponding groups
* @param placeHolderText the placeholder text for the search field (when no text is entered) * @param placeHolderText the placeholder text for the search field (when no text is entered)
* @param extraCategoryWidgets extra widgets to add to the category popup. Each widget will be added as a separate menu item. * @param extraCategoryWidgets extra widgets to add to the category popup. Each widget will be added as a separate menu item.
*/ */
public SearchableAndCategorizableComboBox(ComboBoxModel<T> model, Map<G, List<T>> itemGroupMap, String placeHolderText, public SearchableAndCategorizableComboBox(ComboBoxModel<T> model, String placeHolderText,
Component... extraCategoryWidgets) { Component... extraCategoryWidgets) {
super(model != null ? model : new DefaultComboBoxModel<>()); super(model != null ? model : new DefaultComboBoxModel<>());
List<T> items = new ArrayList<>();
for (int i = 0; i < Objects.requireNonNull(model).getSize(); i++) {
items.add(model.getElementAt(i));
}
init(model, constructItemGroupMapFromList(items), placeHolderText, extraCategoryWidgets);
}
public SearchableAndCategorizableComboBox(List<T> allItems, String placeHolderText, Component... extraCategoryWidgets) {
super();
init(null, constructItemGroupMapFromList(allItems), placeHolderText, extraCategoryWidgets);
}
private void init(ComboBoxModel<T> model, Map<G, List<T>> itemGroupMap, String placeHolderText, Component... extraCategoryWidgets) {
setEditable(false); setEditable(false);
initColors(); initColors();
this.extraCategoryWidgets = extraCategoryWidgets; this.extraCategoryWidgets = extraCategoryWidgets;
this.placeHolderText = placeHolderText; this.placeHolderText = placeHolderText;
this.itemGroupMap = itemGroupMap;
updateItems(itemGroupMap); updateItems(itemGroupMap);
setupMainRenderer(); setupMainRenderer();
@ -105,10 +124,6 @@ public class SearchableAndCategorizableComboBox<G extends Group, T extends Group
addMouseListeners(); addMouseListeners();
} }
public SearchableAndCategorizableComboBox(Map<G, List<T>> itemGroupMap, String placeHolderText, Component... extraCategoryWidgets) {
this(null, itemGroupMap, placeHolderText, extraCategoryWidgets);
}
private static void initColors() { private static void initColors() {
updateColors(); updateColors();
UITheme.Theme.addUIThemeChangeListener(SearchableAndCategorizableComboBox::updateColors); UITheme.Theme.addUIThemeChangeListener(SearchableAndCategorizableComboBox::updateColors);
@ -118,6 +133,21 @@ public class SearchableAndCategorizableComboBox<G extends Group, T extends Group
textSelectionBackground = GUIUtil.getUITheme().getTextSelectionBackgroundColor(); textSelectionBackground = GUIUtil.getUITheme().getTextSelectionBackgroundColor();
} }
private Map<G, List<T>> constructItemGroupMapFromList(List<T> items) {
Map<G, List<T>> itemGroupMap = new TreeMap<>(new Comparator<G>() {
@Override
public int compare(G g1, G g2) {
return Integer.compare(g1.getPriority(), g2.getPriority());
}
});
for (T item : items) {
G group = item.getGroup();
itemGroupMap.computeIfAbsent(group, k -> new ArrayList<>()).add(item);
}
return itemGroupMap;
}
public void setupMainRenderer() { public void setupMainRenderer() {
setRenderer(new DefaultListCellRenderer() { setRenderer(new DefaultListCellRenderer() {
@Override @Override
@ -173,20 +203,6 @@ public class SearchableAndCategorizableComboBox<G extends Group, T extends Group
repaint(); repaint();
} }
private void updateItemsFromModel() {
ComboBoxModel<T> model = getModel();
Map<G, List<T>> newGroupMap = new HashMap<>();
for (int i = 0; i < model.getSize(); i++) {
T item = model.getElementAt(i);
G group = item.getGroup();
newGroupMap.computeIfAbsent(group, k -> new ArrayList<>()).add(item);
}
Map<G, List<T>> newItemGroupMap = new HashMap<>(newGroupMap);
updateItems(newItemGroupMap);
}
private List<T> extractItemsFromMap(Map<G, List<T>> itemGroupMap) { private List<T> extractItemsFromMap(Map<G, List<T>> itemGroupMap) {
Set<T> uniqueItems = new HashSet<>(); // Use a Set to ensure uniqueness Set<T> uniqueItems = new HashSet<>(); // Use a Set to ensure uniqueness
for (G group : itemGroupMap.keySet()) { for (G group : itemGroupMap.keySet()) {
@ -425,6 +441,12 @@ public class SearchableAndCategorizableComboBox<G extends Group, T extends Group
updateItemsFromModel(); updateItemsFromModel();
} }
}); });
addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
model.setSelectedItem(SearchableAndCategorizableComboBox.this.getSelectedItem());
}
});
} }
private void setupSearchFieldListeners() { private void setupSearchFieldListeners() {