Skip to content

Phase B.5b — GTK runtime migration onto quadraui::Backend trait #249

Description

@JDonaghy

Context

Phase B.5 (commits 2c8fe7f2d8ef54, Apr 27 2026) shipped the trait surface and infrastructure for the GTK backend: GtkBackend struct, Backend trait impl, GDK→UiEvent translation helpers, accelerator registry, is_modal_open(), clipboard write + URL open. What B.5 did NOT do is migrate the actual GTK app runtime onto that infrastructure. Today the running GTK editor still drives:

  • Mouse/key dispatch through Relm4 Msg::* flow (NOT through wait_events).
  • Click hit-testing through inline engine.<modal>.is_some() checks (22 sites; modal stack only used by picker).
  • Keybindings through 16 inline matches_gtk_key arms (NOT through UiEvent::Accelerator dispatch).
  • Drawing through 24 direct quadraui_gtk::draw_* shim calls (only the quickfix panel goes through the trait).
  • Clipboard via engine-level callbacks (NOT through PlatformServices).

src/gtk/events.rs carries #![allow(dead_code)] because nothing produces yet. events_handle() is unused. The accelerator registry is populated but no dispatch path consults it. The trait is real but largely inert at runtime.

This issue is the master tracking doc for migrating the GTK runtime onto the trait so it actually consumes the infrastructure B.5 built. After this lands, generic paint::<B> and wait_events-driven event flow drive the GTK app the same way they drive the TUI.

Scope

Each sub-stage merges to develop independently (Path A). vimcode keeps booting and rendering at every commit. The total estimate is ~5–8 sessions of work, larger items first.

Stage map

# Goal Notes / dependencies
B5b.1 Wire mouse/key/scroll/resize signal callbacks to push translated UiEvents into backend.events_handle(). Add a glib::idle_add_local drain hook that calls wait_events periodically. At end of stage: the queue has producers + a consumer. No behavior change yet — Relm4 Msg flow stays authoritative; queue events are dispatched alongside (dual-write). None. Foundation for everything else.
B5b.2 Migrate the 16 matches_gtk_key arms to UiEvent::Accelerator(id, mods) dispatch. Mirrors TUI Stage 6's dispatch_panel_accelerator helper. Removes the inline string-binding parsing in the hot key path. Depends on B5b.1 (queue must consume Accelerator events).
B5b.3 Migrate dialog modals onto ModalStack. engine.dialog push on open, pop on close, click routing through dispatch_mouse_down. Replaces ~9 inline if engine.dialog.is_some() gates. Depends on B5b.1.
B5b.4 Migrate context menu onto ModalStack. ~3 gating sites. Closes #236 along the way (border visibility — the renderer change is a separate PR but easier to ship alongside). Depends on B5b.3 (pattern established).
B5b.5 Migrate completion popup onto ModalStack. Depends on B5b.3.
B5b.6 Migrate hover popup onto ModalStack. Add is_modal_open() gate to the LSP hover trigger so #247 closes (modal-hover bug + Pango font swap). Depends on B5b.3, B5b.5 (multi-modal stacking).
B5b.7 Migrate tab switcher onto ModalStack. Depends on B5b.3.
B5b.8 Migrate remaining 24 quadraui_gtk::draw_* direct call sites onto Backend::draw_* trait dispatch (palette, tree, form). Each gets the same enter_frame_scope wrapper the quickfix pilot uses. Depends on B5b.1 (frame_scope pattern is established by Stage 3 of B.5 plumbing — extending here).
B5b.9 Quadraui trait extension — add &Layout parameters to the deferred draw_* methods (status_bar, tab_bar, activity_bar, terminal, text_display) per BACKEND_TRAIT_PROPOSAL.md §6.2. Lands as a quadraui-side PR with corresponding TUI + GTK trait method updates. Independent of other B5b stages but blocks B5b.10.
B5b.10 Migrate the 5 layout-passthrough draw sites onto the trait. Depends on B5b.9.
B5b.11 Drop alias fields: App.modal_stack, App.drag_state removed (callers use App.backend.borrow().modal_stack_handle() directly). Depends on all click migrations (B5b.3–B5b.7) being done.
B5b.12 Drop dead quadraui_gtk::draw_* shims. Remove #![allow(dead_code)] from events.rs. Depends on B5b.8 + B5b.10.
B5b.13 Smoke-test parity sweep + final cleanup. Confirm GTK app behaves identically to pre-migration. Final stage.

Out of scope (deferred to later phases)

  • Replacing the Relm4 Msg::* enum entirely. Some GTK-specific concerns (file dialogs, async clipboard read, Pango font management) genuinely need GTK-specific message types that don't fit UiEvent. These stay on Relm4. The migration lets wait_events carry the portable events; Msg keeps the GTK-specific ones.
  • Editor primitive draw migration. draw.rs::draw_editor stays inherent (per BACKEND_TRAIT_PROPOSAL.md §6.2 — editor primitive is deferred until all backends ship).

Forward-compatibility hooks already in place

  • GtkBackend::events_handle() — Stage 4 plumbing. Use this clone in every signal callback.
  • GtkBackend::push_event() — convenience for callsites with direct &GtkBackend access.
  • GtkBackend::is_modal_open() — gate hover triggers / focus-stealing animations once modals are on the stack.
  • register_panel_accelerators() already runs at App init (B.5 Stage 6). Add the dispatch in B5b.2 to make it active.
  • events.rs translation helpers — already shipped with 10 unit tests. Just need callers.

Files to touch

  • src/gtk/mod.rs (~11k lines) — most signal callbacks, the App struct, the click handlers. Biggest lift; each B5b stage touches a focused subset.
  • src/gtk/click.rs (~563 lines) — hit-test helpers; affected by modal migration (B5b.3–B5b.7).
  • src/gtk/draw.rs (~6k lines) — touched only by B5b.8 + B5b.10 (draw migration).
  • src/gtk/backend.rs — receives the dispatch_panel_accelerator helper in B5b.2 (mirroring TUI's dispatch_panel_accelerator in tui_main/mod.rs).
  • quadraui/ — only B5b.9 (trait extension).

Related issues (will be resolved by sub-stages here)

How to pick this up in a fresh session

  1. Read PLAN.md "🎯 CURRENT FOCUS — Phase B.5b: GTK runtime migration" section.
  2. Read quadraui/docs/BACKEND.md for the trait shape.
  3. Read quadraui/docs/BACKEND_TRAIT_PROPOSAL.md §6.2 (layout-passthrough deferred).
  4. Read src/tui_main/backend.rs + src/tui_main/mod.rs::dispatch_panel_accelerator for the reference impl B5b.2 mirrors.
  5. Read src/gtk/backend.rs for what's already in place.
  6. Pick the lowest-numbered unchecked stage above and start.

Each stage is small enough to be a single focused PR. Quality gate per stage: cargo build --workspace --no-default-features ✔, cargo build --bin vimcode ✔, cargo clippy --bin vimcode -- -D warnings ✔, cargo test --no-default-features ✔ (5293 baseline).

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    infrastructureBuild, CI, distributionplatformPlatform-specific (macOS, Windows, Linux)

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions