Skip to content

Rich text popup (editor hover): Surface::RichTextPopup migration breaks click hit-test #469

Description

@JDonaghy

Scope

Migrate `draw_editor_hover_popup` in `src/gtk/draw.rs` from a direct `quadraui_gtk::draw_rich_text_popup` shim call to `Surface::RichTextPopup` push via `enter_frame_scope` + `frame.draw(b)`. Last chunk of #463 (chunk D); the other 7 popup surfaces landed in PR (TBD link).

What happens when migrated

The popup paints correctly — same visual position, same border, same content. But three click interactions fall through to whatever's underneath:

  • Link clicks → fall through (e.g. lands on editor scrollbar if popup is wide enough to overlap it)
  • Scrollbar track click (click-to-page) → fall through
  • Scrollbar thumb drag → fall through

Scroll wheel inside the popup still works (different dispatch path).

What's confusing

`popup_layout` is computed once and used for both:

  1. `frame.push(Surface::RichTextPopup { popup, layout: &popup_layout })` → painted via trait
  2. `popup_rect` = `Some((popup_layout.bounds.x, ..., popup_layout.bounds.width, ...))`
  3. `link_rects` = `popup_layout.link_hit_regions` mapped to `(f64, f64, f64, f64, url)` tuples
  4. `scrollbar_hit` = `popup_layout.scrollbar` mapped to `PopupScrollbarHit`

All three caches (`editor_hover_popup_rect` / `editor_hover_link_rects` / `editor_hover_scrollbar`) draw from the same `popup_layout` instance. Paint also draws from it. So paint and click geometry should agree by construction — yet clicks miss.

`Backend::draw_rich_text_popup` in quadraui calls the same `crate::gtk::draw_rich_text_popup` rasteriser the legacy shim called, with the same `(cr, pango_layout, ui_font_desc, popup, layout, theme)`. Painting should be identical.

What worked around it

Reverted to the legacy `quadraui_gtk::draw_rich_text_popup` shim (kept on the branch). Now click hit-test works. The shim and the trait method literally call the same free function — the only difference is the `ui_font_desc` source (shim uses `super::draw::UI_FONT()`, trait uses `self.ui_font` set at startup). These should produce identical strings unless the user changes `settings.ui_font_size` mid-session.

Hypotheses to investigate

  1. `self.ui_font` vs `UI_FONT()` divergence — the shim reads the atomic-synced `UI_FONT()` each call; the trait method reads `self.ui_font` which was set once at startup. If the user resized their UI font, the trait paints at one size while the layout was computed for another. But this would only affect paint position, not click hit-test (which uses the layout, not the paint). Still worth ruling out.
  2. Surface::RichTextPopup z-order issue — `frame.draw(b)` registers the popup in `FrameHitMap` (which vimcode discards). But the existing modal-stack registration via `reconcile_editor_hover_modal` is separate. Maybe `frame.draw` is triggering some side effect on modal arbitration that confuses dispatch.
  3. Frame-scope state pollution — the trait method's `enter_frame_scope` mutates backend state (current_theme, pango pointers, etc.). When the popup paints inside `enter_frame_scope` and the outer scope (editor cr context) is restored, maybe some state survives that confuses the next click frame.

Next steps

  • Add a debug print in `reconcile_editor_hover_modal` to log `(visible, rect)` after migrated paint and confirm the modal entry is being pushed with the correct bounds.
  • If modal is correct but clicks still fall through, check whether `dispatch_click` is actually consulting the modal stack — could be a regression in quadraui where modal arbitration doesn't fire for non-scroll-surface modals.
  • If the migration is restored, leave it behind a temporary feature flag for parallel testing against the shim path.

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workinginfrastructureBuild, CI, distributionuiUI/rendering

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions