Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Showing all matches during a search #579

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 39 additions & 3 deletions src/guiguts/maintext.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import tkinter as tk
from tkinter import ttk, Text
from tkinter import font as tk_font
from typing import Any, Callable, Optional, Literal, Generator
from typing import Any, Callable, Optional, Literal, Generator, cast
from enum import auto, StrEnum

import regex as re
Expand Down Expand Up @@ -275,6 +275,7 @@ class HighlightTag(StrEnum):
ALIGNCOL = auto()
CURSOR_LINE = auto()
COLUMN_RULER = auto()
SEARCH = auto()


class HighlightColors:
Expand Down Expand Up @@ -350,6 +351,21 @@ class HighlightColors:
"Dark": {"bg": "#324F78", "fg": "white"},
}

SEARCH = {
"Light": {
"bg": "#f0f0f0",
"fg": "#a8a8a8",
"relief": "ridge",
"borderwidth": "2",
},
"Dark": {
"bg": "#0f0f0f",
"fg": "#8a8a8a",
"relief": "ridge",
"borderwidth": "2",
},
}


class TextPeer(tk.Text):
"""A peer of maintext's text widget.
Expand Down Expand Up @@ -2875,6 +2891,16 @@ def _highlight_configure_tag(
background=tag_colors[theme]["bg"],
foreground=tag_colors[theme]["fg"],
)
if "borderwidth" in tag_colors[theme]:
self.tag_configure(tag_name, borderwidth=tag_colors[theme]["borderwidth"])
if "relief" in tag_colors[theme]:
self.tag_configure(
tag_name,
relief=cast(
Literal["raised", "sunken", "flat", "ridge", "solid", "groove"],
tag_colors[theme]["relief"],
),
)

def highlight_selection(
self,
Expand Down Expand Up @@ -2902,8 +2928,14 @@ def highlight_selection(
tag_name, match.rowcol.index(), match.rowcol.index() + "+1c"
)

def remove_search_highlights(self) -> None:
"""Remove highlights for search"""
if maintext().winfo_exists():
Copy link
Collaborator

Choose a reason for hiding this comment

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

Sorry @tangledhelix - I misled you. I think this fix would actually be better if it said

        if self.winfo_exists():

If you agree, please change it. If you think it doesn't matter, don't worry. I'm going to approve anyway now.

self.tag_remove(HighlightTag.SEARCH, "1.0", tk.END)

def remove_highlights(self) -> None:
"""Remove active highlights."""
self.remove_search_highlights()
self.tag_remove(HighlightTag.QUOTEMARK, "1.0", tk.END)

def highlight_quotemarks(self, pat: str) -> None:
Expand Down Expand Up @@ -3278,8 +3310,7 @@ def highlight_configure_tags(self, first_run: bool = False) -> None:
#
for tag, colors in (
(HighlightTag.QUOTEMARK, HighlightColors.QUOTEMARK),
# "sel" is for active selections - don't override the default color
("sel", None),
(HighlightTag.SEARCH, HighlightColors.SEARCH),
(HighlightTag.SPOTLIGHT, HighlightColors.SPOTLIGHT),
(HighlightTag.PAREN, HighlightColors.PAREN),
(HighlightTag.CURLY_BRACKET, HighlightColors.CURLY_BRACKET),
Expand All @@ -3296,6 +3327,11 @@ def highlight_configure_tags(self, first_run: bool = False) -> None:
if order_needs_update:
self.tag_lower(tag)

# Position the "sel" tag specially. This must be done for both widgets
# since they're independent.
self.tag_lower("sel", HighlightTag.QUOTEMARK)
self.peer.tag_lower("sel", HighlightTag.QUOTEMARK)

def _autoscroll_start(
self, event: tk.Event # pylint: disable=unused-argument
) -> None:
Expand Down
22 changes: 21 additions & 1 deletion src/guiguts/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import regex as re

from guiguts.checkers import CheckerDialog
from guiguts.maintext import maintext, TclRegexCompileError, FindMatch
from guiguts.maintext import maintext, TclRegexCompileError, FindMatch, HighlightTag
from guiguts.preferences import preferences, PersistentBoolean, PrefKey
from guiguts.utilities import sound_bell, IndexRowCol, IndexRange, sing_plur
from guiguts.widgets import (
Expand Down Expand Up @@ -292,6 +292,11 @@ def set_first_last() -> None:
self.config_width()
self.allow_geometry_save()

# Handle tag cleanup when search panel is closed
self.top_frame.bind(
"<Destroy>", lambda _event: maintext().remove_search_highlights()
)

def show_multi_replace(self, show: bool, resize: bool = True) -> None:
"""Show or hide the multi-replace buttons.

Expand Down Expand Up @@ -365,7 +370,22 @@ def search_clicked(
start_rowcol = get_search_start(backwards)
stop_rowcol = maintext().start() if backwards else maintext().end()
message = ""

# Find all matching occurrences, tag with the "SEARCH" highlight.
# Clean up any existing "SEARCH" highlights first.
maintext().remove_search_highlights()
try:
allmatches = self._find_all(
IndexRange(maintext().start(), maintext().end()), self.search_box.get()
)
for _match in allmatches:
maintext().tag_add(
HighlightTag.SEARCH,
_match.rowcol.index(),
f"{_match.rowcol.index()}+{_match.count}c",
)
# Now that "background" matches are highlighted, find the next match
# and jump there as the "active" match. Uses the "sel" highlight.
_do_find_next(
search_string, backwards, IndexRange(start_rowcol, stop_rowcol)
)
Expand Down
Loading