Skip to content

Add Preferences search (fix #5353)#5507

Merged
dacap merged 1 commit into
aseprite:betafrom
ckaiser:preferences-search
Feb 24, 2026
Merged

Add Preferences search (fix #5353)#5507
dacap merged 1 commit into
aseprite:betafrom
ckaiser:preferences-search

Conversation

@ckaiser

@ckaiser ckaiser commented Nov 4, 2025

Copy link
Copy Markdown
Member

Fixes #5353, adds a search to the preferences menu like so:

(Note: Outdated video)

pref-search.mp4

@ckaiser ckaiser requested a review from dacap as a code owner November 4, 2025 06:35

@aseprite-bot aseprite-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

dragAndDropFromEdges()->setSelected(m_pref.timeline.dragAndDropFromEdges.defaultValue());
}

void allWidgetsIn(Widget* root, WidgetsList& list)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: function 'allWidgetsIn' is within a recursive call chain [misc-no-recursion]

  void allWidgetsIn(Widget* root, WidgetsList& list)
       ^
Additional context

src/app/commands/cmd_options.cpp:2065: example recursive call chain, starting from function 'allWidgetsIn'

  void allWidgetsIn(Widget* root, WidgetsList& list)
       ^

src/app/commands/cmd_options.cpp:2070: Frame #1: function 'allWidgetsIn' calls function 'allWidgetsIn' here:

      allWidgetsIn(*i, list);
      ^

src/app/commands/cmd_options.cpp:2070: ... which was the starting point of the recursive call chain; there may be other cycles

      allWidgetsIn(*i, list);
      ^

@Gasparoken

Copy link
Copy Markdown
Member

Great, cool feature! Some comments:

  • The Dirty flag hack doesn't work in some cases (try searching for "p" and then go to the Timeline section; the "First Default Frame" label will be incorrectly enabled). It might be better to collect all Label widgets that have buddies during the "for" loop, then reprocess the labels collection to determine whether the label and its corresponding buddy should be disabled or not (I don't think the DIRTY flag will be needed. Just check if the label or its associated buddy is enabled; if so, both will be enabled).
  • Please rebase the PR to the latest commits.

@Gasparoken Gasparoken assigned ckaiser and unassigned Gasparoken Nov 12, 2025
@ckaiser

ckaiser commented Nov 17, 2025

Copy link
Copy Markdown
Member Author

Removed the use of the DIRTY flag and fixed some corner cases.

@ckaiser ckaiser assigned Gasparoken and unassigned ckaiser Nov 17, 2025
@Gasparoken

Copy link
Copy Markdown
Member

Great!! I didn't mention before but Slider and ColorButton widgets should be disabled too. I found that disable all Slider and ColorButton widget before to process the labels will be enough (similar case thatuiWindows()->setEnabled(false);

        // Always disable the Slider and ColorButton widgets; they will be enabled through
        // their label.
        if (widget && widget->type() == kSliderWidget || widget->type() == ColorButton::colorbutton_type())
          widget->setEnabled(false);

ColorButton::colorbutton_type() isn't accesible, I moved colorbutton_type() to the header file inside ColorButton class. This would require approval from @dacap.

@Gasparoken Gasparoken assigned ckaiser and unassigned Gasparoken Nov 19, 2025
@dacap

dacap commented Nov 19, 2025

Copy link
Copy Markdown
Member

In case we need access to colorbutton_type(), you can refactor it to static WidgetType ColorButton::Type() as a public member of ColorButton.

@aseprite-bot aseprite-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

dragAndDropFromEdges()->setSelected(m_pref.timeline.dragAndDropFromEdges.defaultValue());
}

void allWidgetsIn(Widget* root, WidgetsList& list)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: function 'allWidgetsIn' is within a recursive call chain [misc-no-recursion]

  void allWidgetsIn(Widget* root, WidgetsList& list)
       ^
Additional context

src/app/commands/cmd_options.cpp:2106: example recursive call chain, starting from function 'allWidgetsIn'

  void allWidgetsIn(Widget* root, WidgetsList& list)
       ^

src/app/commands/cmd_options.cpp:2111: Frame #1: function 'allWidgetsIn' calls function 'allWidgetsIn' here:

      allWidgetsIn(*i, list);
      ^

src/app/commands/cmd_options.cpp:2111: ... which was the starting point of the recursive call chain; there may be other cycles

      allWidgetsIn(*i, list);
      ^

Comment thread src/app/commands/cmd_options.cpp
@ckaiser

ckaiser commented Nov 21, 2025

Copy link
Copy Markdown
Member Author

Fixed some issues with the disabling/re-enabling and added a disabled state to the ColorButton basically making it greyscale, since it had no disabled feedback.

@dacap Made a change to Slider so it's also setting the text() to the value, I think it makes sense semantically but I could also just work around that in my code and not change a core class.

@ckaiser ckaiser assigned Gasparoken and unassigned ckaiser Nov 21, 2025
@Gasparoken Gasparoken assigned ckaiser and unassigned Gasparoken Nov 21, 2025
@ckaiser

ckaiser commented Nov 22, 2025

Copy link
Copy Markdown
Member Author

Fixed accidentally breaking everything (oops) and added two new things:

  • Made the search a focus magnet like in keyboard shortcuts
  • LinkLabel now inherits the regular label style which means they have a "disabled" color
  • Added the closeOnEsc property to SearchEntry, which came from me trying to naturally "clear" the search using the escape key. Made it optional by default and I've added it for big windows with a search like this, keyboard shortcuts and the command runner.

@ckaiser ckaiser assigned Gasparoken and unassigned ckaiser Nov 22, 2025
@Gasparoken

Copy link
Copy Markdown
Member

Almost perfect, but...:

          if (label->isEnabled()) {
            label->buddy()->setEnabled(true);

            // Special case to handle buttonsets
            if (label->buddy()->type() == kGridWidget) {
              for (auto* child : label->buddy()->children())
                child->setEnabled(true);
            }
          }
          else if (label->buddy()->isEnabled() && !label->buddy()->text().empty()) {
            label->setEnabled(true);
          }

I found that the ComboBox labels are disabled regardless, even though the string "pn" belongs to one of the available options ("png") in the default file association ComboBox under "Files" section.

The problem lies in the following "else if" statement:

  else if (label->buddy()->isEnabled() && !label->buddy()->text().empty())

In the mentioned ComboBox label->buddy()->isEnabled() is "true", however, !label->buddy()->text().empty() is "false" since !label->buddy() is a ComboBox type with no text. Finally, in the UI, the ComboBox label will incorrectly change to a disabled state.

@Gasparoken Gasparoken assigned ckaiser and unassigned Gasparoken Nov 25, 2025
@ckaiser ckaiser assigned Gasparoken and unassigned ckaiser Dec 4, 2025

@aseprite-bot aseprite-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

Comment thread src/app/commands/cmd_options.cpp
Comment thread src/ui/tooltips.cpp Outdated
@Gasparoken Gasparoken assigned ckaiser and unassigned Gasparoken Dec 5, 2025
@ckaiser ckaiser force-pushed the preferences-search branch from 5bc4583 to 18bfed3 Compare December 5, 2025 18:53
@ckaiser ckaiser assigned Gasparoken and unassigned ckaiser Dec 6, 2025
@ckaiser ckaiser force-pushed the preferences-search branch 2 times, most recently from 3823405 to 73d8ab3 Compare December 8, 2025 06:26
@ckaiser

ckaiser commented Dec 8, 2025

Copy link
Copy Markdown
Member Author

Added a cache of the lower case searchable text for the widgets, this speeds up the processing on each keystroke by around 40%~, I've also added debouncing to SearchEntry which further minimizes the load at the expense of a bit of latency, but it should be helpful on lower-end systems and not particularly noticeable otherwise. Also renamed closeOnEsc to clearOnEsc since that's what it's doing.

@ckaiser ckaiser assigned Gasparoken and unassigned Gasparoken Dec 8, 2025

@aseprite-bot aseprite-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

Comment thread src/app/commands/cmd_options.cpp

@Gasparoken Gasparoken left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! 🙌

@Gasparoken Gasparoken assigned dacap and unassigned Gasparoken Dec 9, 2025
@dacap

dacap commented Feb 18, 2026

Copy link
Copy Markdown
Member

I've rebased this to latest beta to avoid conflicts with the merged PR #5503

You can find the rebased version here https://github.com/dacap/aseprite/tree/preferences-search in case you want to move your own branch. Anyway there are some comments to make.

Comment thread data/widgets/keyboard_shortcuts.xml Outdated
Comment thread data/widgets/run_command.xml Outdated
@dacap

dacap commented Feb 18, 2026

Copy link
Copy Markdown
Member

@dacap Made a change to Slider so it's also setting the text() to the value, I think it makes sense semantically but I could also just work around that in my code and not change a core class.

I'm not sure why this change was required. Is it necessary for this feature?

Comment thread src/ui/tooltips.cpp Outdated

@aseprite-bot aseprite-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

Comment thread src/app/commands/cmd_options.cpp
@ckaiser ckaiser requested a review from dacap February 19, 2026 22:35
@dacap

dacap commented Feb 24, 2026

Copy link
Copy Markdown
Member

Looks great @ckaiser 👍 I'll merge it right now, and make a little change in the Grid section that it looks something has changed.

@dacap dacap merged commit a2459af into aseprite:beta Feb 24, 2026
12 checks passed
@dacap dacap linked an issue Feb 24, 2026 that may be closed by this pull request
@dacap dacap added this to the v1.3.18-beta1 milestone Feb 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a search field in Preferences dialog

4 participants