Fix styled-text note hyperlink click detection#2448
Open
eduralph wants to merge 1 commit into
Open
Conversation
169d5a9 to
88a10b5
Compare
When a user clicks in empty space beside or below a hyperlink in a Note's styled-text view, GTK's get_iter_at_location() snaps the click to the nearest character position. The existing code treats that snapped position as a real link match, so the link opens even though the user never clicked on it. Add a new _pointer_over_iter() predicate to verify that the pointer is actually within the rendered glyph of a character, not merely adjacent to it. Because get_iter_at_location() advances past a glyph when the click lands in its trailing (right) half, the predicate checks the bounding rectangle of the character at the snapped position and, on a miss, the one immediately before it. Gate both URL regexp matches and internal link tags on this check, so clicks in the empty area beside/below a link no longer register as hits, while a click anywhere on the link glyph -- left or right half -- still opens it. This fixes the invariant that link activation requires the pointer to be over the link's rendered glyph, not over the nearest position when a click falls outside the text. Fixes #8841 Signed-off-by: Eduard Ralph <eduard@ralphovi.net>
88a10b5 to
c4bcc48
Compare
This was referenced Jul 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
User impact: When you view a Note that contains a web link, clicking in the empty space beside the link or below the last line of text unexpectedly opens the link in your browser — even though you never clicked on the link itself. Any click near a link that ends a line or the note can launch the browser by mistake.
This makes Gramps confirm the pointer is really over the link's text before treating a click as a link.
Reported in Mantis #8841.
What to look at
The Note viewer decides whether a click hit a link by asking GTK for the "nearest" text position, which snaps empty-area clicks onto the end of the text and its trailing link. The fix adds a check that the pointer is genuinely inside a character's rendered rectangle before counting it as a link hit. To reproduce: on
maintenance/gramps61, create a Note, paste a URL on its own line, save, and reopen in read-only view. Click in the empty area below or to the right of the URL — before the fix the browser opens; after the fix the empty-area click does nothing, while clicking on the URL still opens it.Root cause
Gtk.TextView.get_iter_at_location(x, y)is a GTK3 API that returns the "nearest" text iterator for any point, including a click in the empty area beside/below text — it never returns "no iter". Instyledtexteditor.py,on_motion_notify_event()(lines 424–456) setsiter_at_locationfrom the click at line 435, then uses that position directly to look up URLs (viamatch_check()) and internal link tags. An empty-area click snaps to the end of the text and matches the link if one ends there;on_button_press_event()(lines 501–516) then opens the link whenself.url_matchis set, anddo_match_changed()(line 404) keepsself.url_matchin sync.Fix
The fix (in
gramps/gui/widgets/styledtexteditor.py) adds a new private method_pointer_over_iter(x, y, text_iter)that usesget_iter_location()to confirm the point(x, y)is genuinely inside a character's bounding rectangle. Becauseget_iter_at_location()advances past a glyph when the click lands in that glyph's trailing (right) half (it adds Pango's trailing count), the character under the pointer may be the one beforetext_iter; the helper therefore checks the rectangle of the char at the iter and, on a miss, the char immediately before it. Both the URL regexp match path (match_check()) and the internal link-tag loop are guarded by this check:This ensures that a click to the right of the URL on its line is excluded (past the last glyph's rectangle); a click below the last line is excluded (below every line's rectangle); and a click anywhere on the URL glyph, left or right half, still matches (point inside the char's or its predecessor's rectangle → link still opens). Both URL matches and internal link tags are guarded, so neither hyperlink flavour can open on an empty-area click, and neither is lost on a real on-glyph click.
Verification
get_iter_at_location()snaps even out-of-bounds clicks; the fix gates both URL matches and internal link tags on a real glyph hit (checking the snapped char and its predecessor), clears the match otherwise, and leaves the match→open path intact.maintenance/gramps61—gramps/gui/widgets/styledtexteditor.py:435setsiter_at_locationfrom the user click (nearest-iter semantics per GTK3 docs /match_checkbehaviour). Patched: the URLmatch_check()and the link-tag loop now sit inside anif self._pointer_over_iter()block, with anelseclause settingself.match = Nonefor out-of-bounds clicks;_pointer_over_iter()usesget_iter_location()to confirm the point is inside the char at the iter or, on a miss, the char before it (covering the trailing-half advance so a real click on a link glyph's right half still opens). The signal path is unbroken:styledtexteditor.py:450–451still emits "match-changed" whenself.matchchanges,do_match_changed()still updatesself.url_match(line 404, preimage), andon_button_press_event()(lines 511–516, preimage) consumesself.url_matchand calls_open_url_cb()only when set.engine/interface/test_bug_0008841_note_link_hittest.py, red without the fix and green with it: before an empty-area click snaps to the URL end,match_check()includes that offset,self.matchis set, and the non-editable view calls_open_url_cb()so the "does not open" assertion fails; after,_pointer_over_iter()returns False,self.matchstays None, no browser launch occurs, and on-glyph clicks still match so the positive assertion passes both legs.Fixes #8841