Skip to content

Commit

Permalink
Highlight alignment column (DistributedProofreaders#525)
Browse files Browse the repository at this point in the history
Adds "Highlight Alignment Column" to the Search menu. This should work the same way the GG1 feature did: when activated, whatever column the cursor is on will be set as the alignment column and highlighted with a color, until turned off.

Fixes DistributedProofreaders#43
  • Loading branch information
tangledhelix authored Nov 17, 2024
1 parent 3d70a04 commit dc91312
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 11 deletions.
7 changes: 7 additions & 0 deletions src/guiguts/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
highlight_double_quotes,
remove_highlights,
highlight_quotbrac_callback,
highlight_aligncol_callback,
)
from guiguts.maintext import maintext
from guiguts.mainwindow import (
Expand Down Expand Up @@ -596,6 +597,12 @@ def init_search_menu(self) -> None:
"Highlight S~urrounding Quotes & Brackets",
root().highlight_quotbrac,
)
menu_search.add_checkbox(
"Highlight Al~ignment Column",
root().highlight_aligncol,
lambda: highlight_aligncol_callback(True),
lambda: highlight_aligncol_callback(False),
)
menu_search.add_separator()
self.init_bookmark_menu(menu_search)

Expand Down
77 changes: 66 additions & 11 deletions src/guiguts/highlight.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
"""Highlight functionality."""

from enum import auto, StrEnum
from tkinter import Text

from guiguts.maintext import maintext
from guiguts.preferences import preferences, PrefKey
from guiguts.root import root
from guiguts.utilities import IndexRange


Expand All @@ -19,6 +21,7 @@ class HighlightTag(StrEnum):
CURLY_DOUBLE_QUOTE = auto()
STRAIGHT_SINGLE_QUOTE = auto()
CURLY_SINGLE_QUOTE = auto()
ALIGNCOL = auto()


class HighlightColors:
Expand Down Expand Up @@ -88,6 +91,12 @@ class HighlightColors:
"Default": {"bg": "dodgerblue", "fg": "white"},
}

ALIGNCOL = {
"Light": {"bg": "greenyellow", "fg": "black"},
"Dark": {"bg": "#577a32", "fg": "white"},
"Default": {"bg": "greenyellow", "fg": "black"},
}


def highlight_selection(
pat: str,
Expand Down Expand Up @@ -196,7 +205,9 @@ def highlight_single_pair_bracketing_cursor(
_highlight_configure_tag(tag_name, tag_colors)
cursor = maintext().get_insert_index().index()

(top_index, bot_index) = get_screen_window_coordinates()
(top_index, bot_index) = get_screen_window_coordinates(
maintext().focus_widget(), 80
)

# search backward for the startchar
startindex = search_for_base_character_in_pair(
Expand All @@ -223,16 +234,19 @@ def highlight_single_pair_bracketing_cursor(
maintext().tag_add(tag_name, endindex, maintext().index(f"{endindex}+1c"))


def get_screen_window_coordinates() -> tuple[str, str]:
def get_screen_window_coordinates(
viewport: Text, offscreen_lines: int = 5
) -> tuple[str, str]:
"""
Find start and end coordinates for viewport (with a margin of
offscreen text added for padding).
"""
# A magic number cribbed from Tk.pm's TextEdit.pm.
# This is how many rows to explore beyond the visible viewport.
offscreen_rows = 80
Find start and end coordinates for a viewport (with a margin of offscreen
text added for padding).
(top_frac, bot_frac) = maintext().focus_widget().yview()
Args:
viewport: the viewport to inspect
offscreen_lines: optional count of offscreen lines to inspect (default: 5)
"""
(top_frac, bot_frac) = viewport.yview()
# use maintext() here, not view - there is no TextPeer.rowcol()
end_index = maintext().rowcol("end")

# Don't try to go beyond the boundaries of the document.
Expand All @@ -241,8 +255,8 @@ def get_screen_window_coordinates() -> tuple[str, str]:
# the document; do some math to calculate what the top or bottom row in the
# viewport should be, then use min/max to make sure that value isn't less
# than 0 or more than the total row count.
top_line = max(int((top_frac * end_index.row) - offscreen_rows), 0)
bot_line = min(int((bot_frac * end_index.row) + offscreen_rows), end_index.row)
top_line = max(int((top_frac * end_index.row) - offscreen_lines), 0)
bot_line = min(int((bot_frac * end_index.row) + offscreen_lines), end_index.row)

return (f"{top_line}.0", f"{bot_line}.0")

Expand Down Expand Up @@ -433,3 +447,44 @@ def remove_highlights_quotbrac() -> None:
HighlightTag.CURLY_SINGLE_QUOTE,
):
maintext().tag_delete(tag)


def highlight_aligncol_callback(value: bool) -> None:
"""Callback when highlight_aligncol active state is changed."""
if value:
root().aligncol = maintext().get_insert_index().col
highlight_aligncol()
else:
remove_highlights_aligncol()


def highlight_aligncol() -> None:
"""Add a highlight to all characters in the alignment column."""
if root().highlight_aligncol.get():
maintext().tag_delete(HighlightTag.ALIGNCOL)
_highlight_configure_tag(HighlightTag.ALIGNCOL, HighlightColors.ALIGNCOL)

highlight_aligncol_in_viewport(maintext())
if PrefKey.SPLIT_TEXT_WINDOW:
highlight_aligncol_in_viewport(maintext().peer)


def highlight_aligncol_in_viewport(viewport: Text):
"""Do highlighting of the alignment column in a single viewport."""
(top_index, bot_index) = get_screen_window_coordinates(viewport)

col = root().aligncol
row = int(top_index.split(".")[0])
end_row = int(bot_index.split(".")[0])

while row <= end_row:
# find length of row; don't highlight if row is too short to contain col
rowlen = int(maintext().index(f"{row}.0 lineend").split(".")[1])
if 0 < col < rowlen:
maintext().tag_add(HighlightTag.ALIGNCOL, f"{row}.{col}")
row += 1


def remove_highlights_aligncol() -> None:
"""Remove highlights for alignment column"""
maintext().tag_delete(HighlightTag.ALIGNCOL)
2 changes: 2 additions & 0 deletions src/guiguts/maintext.py
Original file line number Diff line number Diff line change
Expand Up @@ -523,13 +523,15 @@ def _on_change(self, *_args: Any) -> None:
# import here to avoid circular import problem.
from guiguts.highlight import ( # pylint: disable=import-outside-toplevel
highlight_quotbrac,
highlight_aligncol,
)

if not self.numbers_need_updating:
self.root.after_idle(self._do_linenumbers_redraw)
self.root.after_idle(self._call_config_callbacks)
self.root.after_idle(self.save_sash_coords)
self.root.after_idle(highlight_quotbrac)
self.root.after_idle(highlight_aligncol)
self.numbers_need_updating = True

def save_sash_coords(self) -> None:
Expand Down
2 changes: 2 additions & 0 deletions src/guiguts/root.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ def __init__(self, **kwargs: Any) -> None:
self.allow_config_saves = False
self.split_text_window = PersistentBoolean(PrefKey.SPLIT_TEXT_WINDOW)
self.highlight_quotbrac = PersistentBoolean(PrefKey.HIGHLIGHT_QUOTBRAC)
self.highlight_aligncol = tk.BooleanVar()
self.aligncol = -1

self.option_add("*tearOff", preferences.get(PrefKey.TEAROFF_MENUS))
self.rowconfigure(0, weight=1)
Expand Down

0 comments on commit dc91312

Please sign in to comment.