From f7437dc9786f12dee84e1cfd51c0cea05bb36b3e Mon Sep 17 00:00:00 2001 From: MostCromulent <201167372+MostCromulent@users.noreply.github.com> Date: Sun, 19 Apr 2026 11:27:22 +0930 Subject: [PATCH] Add search filter to mobile Settings Adds a persistent search field at the top of the Settings tab that filters visible items by label or description as the user types. Mirrors the search already present on the desktop preferences screen. FGroupList gains a setItemFilter(Predicate) method; its layout now skips items and groups that the filter hides. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../forge/screens/settings/SettingsPage.java | 20 +++++++++++++- .../src/forge/toolbox/FGroupList.java | 27 ++++++++++++++++++- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java b/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java index 7ff87e3c130..b7d2f952264 100644 --- a/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java +++ b/forge-gui-mobile/src/forge/screens/settings/SettingsPage.java @@ -31,12 +31,14 @@ import forge.toolbox.FList; import forge.toolbox.FOptionPane; import forge.toolbox.FScrollPane; +import forge.toolbox.FTextField; import forge.util.Lang; import forge.util.Utils; import java.util.*; public class SettingsPage extends TabPage { + private final FTextField txtSearch = add(new FTextField()); private final FGroupList lstSettings = add(new FGroupList<>()); private final CustomSelectSetting settingSkins; private final CustomSelectSetting settingCJKFonts; @@ -45,6 +47,9 @@ public SettingsPage() { super(Forge.getLocalizer().getMessage("lblSettings"), Forge.hdbuttons ? FSkinImage.HDPREFERENCE : FSkinImage.SETTINGS); lstSettings.setListItemRenderer(new SettingRenderer()); + txtSearch.setFont(FSkinFont.get(12)); + txtSearch.setGhostText(Forge.getLocalizer().getMessage("lblSearch")); + txtSearch.setChangedHandler(e -> applySearch()); lstSettings.addGroup(Forge.getLocalizer().getMessage("lblGeneralSettings")); lstSettings.addGroup(Forge.getLocalizer().getMessage("lblGameplayOptions")); @@ -731,9 +736,22 @@ public void refreshCJKFontsList() { settingCJKFonts.updateOptions(FSkinFont.getAllCJKFonts()); } + private void applySearch() { + final String query = txtSearch.getText().toLowerCase().trim(); + if (query.isEmpty()) { + lstSettings.setItemFilter(null); + return; + } + lstSettings.setItemFilter(setting -> + (setting.label != null && setting.label.toLowerCase().contains(query)) + || (setting.description != null && setting.description.toLowerCase().contains(query))); + } + @Override protected void doLayout(float width, float height) { - lstSettings.setBounds(0, 0, width, height); + float searchHeight = FTextField.getDefaultHeight(txtSearch.getFont()); + txtSearch.setBounds(0, 0, width, searchHeight); + lstSettings.setBounds(0, searchHeight, width, height - searchHeight); } private abstract class Setting { diff --git a/forge-gui-mobile/src/forge/toolbox/FGroupList.java b/forge-gui-mobile/src/forge/toolbox/FGroupList.java index 32a8b30f0cc..b90ee1c9829 100644 --- a/forge-gui-mobile/src/forge/toolbox/FGroupList.java +++ b/forge-gui-mobile/src/forge/toolbox/FGroupList.java @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.function.Predicate; import forge.Forge; import forge.Graphics; @@ -134,6 +135,19 @@ public void setListItemRenderer(ListItemRenderer renderer0) { renderer = renderer0; } + public void setItemFilter(Predicate filter) { + for (ListGroup group : groups) { + boolean anyVisible = false; + for (ListItem item : group.items) { + boolean visible = filter == null || filter.test(item.value); + item.setVisible(visible); + anyVisible |= visible; + } + group.setVisible(anyVisible); + } + revalidate(); + } + public FSkinFont getFont() { return font; } @@ -204,7 +218,15 @@ public float getPreferredHeight() { height += GROUP_HEADER_HEIGHT; } if (!isCollapsed) { - height += renderer.getItemHeight() * items.size() + 1; //+1 so bottom border not cut off + int visibleCount = 0; + for (ListItem item : items) { + if (item.isVisible()) { + visibleCount++; + } + } + if (visibleCount > 0) { + height += renderer.getItemHeight() * visibleCount + 1; //+1 so bottom border not cut off + } } return height; } @@ -220,6 +242,9 @@ protected void doLayout(float width, float height) { float itemHeight = renderer.getItemHeight(); for (ListItem item : items) { + if (!item.isVisible()) { + continue; + } item.setBounds(0, y, width, itemHeight); y += itemHeight; }