Add search UI feedback and hide button to search/replace plugin

- Add visual feedback styling for search states (searching, success, fail)
- Show match count in status label when searching
- Add hide (X) button to hide search panel
- Fix typo: stateus_lbl → status_lbl
- Add _set_find_options_lbl to display active search options
This commit is contained in:
2026-02-19 01:07:44 -06:00
parent 61b8bbc5fa
commit 3e920291a0
4 changed files with 66 additions and 13 deletions

View File

@@ -32,6 +32,8 @@ class SearchMixin:
def _find_all_matches(self, search_text, buffer): def _find_all_matches(self, search_text, buffer):
self.update_style(0)
self.matches.clear() self.matches.clear()
self.current_index = -1 self.current_index = -1

View File

@@ -37,11 +37,14 @@ class SearchReplaceMixin(SearchMixin, ReplaceMixin):
self.highlight_tag = buffer.get_tag_table().lookup("search-highlight") self.highlight_tag = buffer.get_tag_table().lookup("search-highlight")
if not search_text: if not search_text:
self.update_style(-1)
self.clear_highlight(buffer) self.clear_highlight(buffer)
self.status_lbl.set_label("Find in current buffer...")
return return
self._find_all_matches(search_text, buffer) self._find_all_matches(search_text, buffer)
self._highlight_all_matches(buffer) self._highlight_all_matches(buffer)
self._update_status_lbl(len(self.matches), search_text)
def _find_entry_activate(self, entry): def _find_entry_activate(self, entry):
self._find_entry_next_match(entry) self._find_entry_next_match(entry)

View File

@@ -40,6 +40,7 @@ class ModeButtons(Gtk.ButtonBox):
match_case_bttn = Gtk.ToggleButton(label = "Aa") match_case_bttn = Gtk.ToggleButton(label = "Aa")
in_selection_bttn = Gtk.ToggleButton() in_selection_bttn = Gtk.ToggleButton()
whole_word_bttn = Gtk.ToggleButton() whole_word_bttn = Gtk.ToggleButton()
hide_bttn = Gtk.Button(label = "X")
use_regex_bttn.set_sensitive(False) use_regex_bttn.set_sensitive(False)
@@ -53,6 +54,11 @@ class ModeButtons(Gtk.ButtonBox):
in_selection_bttn.connect("toggled", self._toggled_button, "in_selection") in_selection_bttn.connect("toggled", self._toggled_button, "in_selection")
whole_word_bttn.connect("toggled", self._toggled_button, "whole_word") whole_word_bttn.connect("toggled", self._toggled_button, "whole_word")
hide_bttn.connect(
"clicked",
lambda widget: self.get_parent().hide()
)
in_selection_bttn.set_image( in_selection_bttn.set_image(
Gtk.Image.new_from_file("images/only-in-selection.png") Gtk.Image.new_from_file("images/only-in-selection.png")
) )
@@ -64,6 +70,7 @@ class ModeButtons(Gtk.ButtonBox):
self.add(match_case_bttn) self.add(match_case_bttn)
self.add(in_selection_bttn) self.add(in_selection_bttn)
self.add(whole_word_bttn) self.add(whole_word_bttn)
self.add(hide_bttn)
def _toggled_button(self, toggle_button, mode: str): def _toggled_button(self, toggle_button, mode: str):
setattr(self, mode, not getattr(self, mode)) setattr(self, mode, not getattr(self, mode))

View File

@@ -50,12 +50,13 @@ class SearchReplace(Gtk.Grid, SearchReplaceMixin):
self.connect("hide", self._handle_hide) self.connect("hide", self._handle_hide)
def _load_widgets(self): def _load_widgets(self):
self.stateus_lbl = Gtk.Label(label = "Find in Current Buffer") self.status_lbl = Gtk.Label(label = "Find in Current Buffer")
self.find_options_lbl = Gtk.Label(label = "Finding with Options: Case Insensitive") self.find_options_lbl = Gtk.Label(label = "Finding with Options: Case Insensitive")
self.mode_bttn_box = ModeButtons() self.mode_bttn_box = ModeButtons()
self.find_entry = Gtk.SearchEntry() self.find_entry = Gtk.SearchEntry()
self.replace_entry = Gtk.SearchEntry() self.replace_entry = Gtk.SearchEntry()
find_bttn = Gtk.Button(label = "Find") find_bttn = Gtk.Button(label = "Find")
find_all_bttn = Gtk.Button(label = "Find All") find_all_bttn = Gtk.Button(label = "Find All")
replace_bttn = Gtk.Button(label = "Replace") replace_bttn = Gtk.Button(label = "Replace")
@@ -97,17 +98,18 @@ class SearchReplace(Gtk.Grid, SearchReplaceMixin):
lambda button: self._replace_all_activate(self.replace_entry) lambda button: self._replace_all_activate(self.replace_entry)
) )
self.attach(child = self.stateus_lbl, left = 0, top = 0, width = 2, height = 1) self.attach(child = self.status_lbl, left = 0, top = 0, width = 3, height = 1)
self.attach(child = self.find_options_lbl, left = 2, top = 0, width = 2, height = 1) self.attach(child = self.find_options_lbl, left = 3, top = 0, width = 2, height = 1)
self.attach(child = self.mode_bttn_box, left = 4, top = 0, width = 2, height = 1) self.attach(child = self.mode_bttn_box, left = 5, top = 0, width = 2, height = 1)
self.attach(child = self.find_entry, left = 0, top = 1, width = 4, height = 1) self.attach(child = self.find_entry, left = 0, top = 1, width = 5, height = 1)
self.attach(child = find_bttn, left = 4, top = 1, width = 1, height = 1) self.attach(child = find_bttn, left = 5, top = 1, width = 1, height = 1)
self.attach(child = find_all_bttn, left = 5, top = 1, width = 1, height = 1) self.attach(child = find_all_bttn, left = 6, top = 1, width = 1, height = 1)
self.attach(child = self.replace_entry, left = 0, top = 2, width = 5, height = 1)
self.attach(child = replace_bttn, left = 5, top = 2, width = 1, height = 1)
self.attach(child = replace_all_bttn, left = 6, top = 2, width = 1, height = 1)
self.attach(child = self.replace_entry, left = 0, top = 2, width = 4, height = 1)
self.attach(child = replace_bttn, left = 4, top = 2, width = 1, height = 1)
self.attach(child = replace_all_bttn, left = 5, top = 2, width = 1, height = 1)
def _handle_show(self, widget): def _handle_show(self, widget):
self.find_entry.set_text("") self.find_entry.set_text("")
@@ -120,9 +122,6 @@ class SearchReplace(Gtk.Grid, SearchReplaceMixin):
self.clear_highlight(buffer) self.clear_highlight(buffer)
self.active_view.grab_focus() self.active_view.grab_focus()
def request_update(self):
self._find_entry_search_change(self.find_entry)
def _find_entry_key_release_event(self, widget, event): def _find_entry_key_release_event(self, widget, event):
modifiers = Gdk.ModifierType(event.get_state() & ~Gdk.ModifierType.LOCK_MASK) modifiers = Gdk.ModifierType(event.get_state() & ~Gdk.ModifierType.LOCK_MASK)
is_control = True if modifiers & Gdk.ModifierType.CONTROL_MASK else False is_control = True if modifiers & Gdk.ModifierType.CONTROL_MASK else False
@@ -145,6 +144,48 @@ class SearchReplace(Gtk.Grid, SearchReplaceMixin):
elif is_control and keyname == "f": elif is_control and keyname == "f":
self.hide() self.hide()
def _set_find_options_lbl(self):
find_options = "Finding with Options: "
if self.mode_bttn_box.use_regex:
find_options += "Regex"
find_options += ", " if self.mode_bttn_box.use_regex else ""
find_options += "Case Sensitive" if self.mode_bttn_box.match_case else "Case Inensitive"
if self.mode_bttn_box.in_selection:
find_options += ", Within Current Selection"
if self.mode_bttn_box.whole_word:
find_options += ", Whole Word"
self.find_options_lbl.set_label(find_options)
def update_style(self, state):
self.find_entry.get_style_context().remove_class("searching")
self.find_entry.get_style_context().remove_class("search-success")
self.find_entry.get_style_context().remove_class("search-fail")
if state == 0:
self.find_entry.get_style_context().add_class("searching")
elif state == 1:
self.find_entry.get_style_context().add_class("search-success")
elif state == 2:
self.find_entry.get_style_context().add_class("search-fail")
def _update_status_lbl(self, total_count: int = 0, query: str = None):
if not query: return
count = total_count if total_count > 0 else "No"
plural = "s" if total_count > 1 else ""
self.update_style(2) if total_count == 0 else self.update_style(1)
self.status_lbl.set_label(f"{count} result{plural} found for:\n'{query}'")
def request_update(self):
self._set_find_options_lbl()
self._find_entry_search_change(self.find_entry)
def clear_highlight(self, buffer): def clear_highlight(self, buffer):
if not self.highlight_tag: return if not self.highlight_tag: return
start, end = buffer.get_bounds() start, end = buffer.get_bounds()