Skip to content

Retype App::backend to dyn Backend — remove the concrete GtkBackend coupling that gates src/app.rs (unblocks #47) #813

Description

@JDonaghy

Problem

src/app.rs:1 describes itself as "struct App — the backend-neutral editor shell application". It is not neutral: src/app.rs:592 types the backend field as the concrete GTK struct.

pub(crate) backend: Rc<RefCell<backend::GtkBackend>>,

That single line is what keeps src/lib.rs:45-46 gating the whole 7,131-line module behind #[cfg(feature = "gui")], and it is the stated Stage-1 blocker on #47 (native macOS GUI).

The blocker is gone upstream. quadraui#699 (filed 2026-09-03 16:38Z, closed 17:11Z, PR#700 / 88345fb) put both handle accessors on the Backend trait, and that commit is already inside vimcode's pinned rev:

Location
Backend trait quadraui/src/backend.rs:745 fn modal_stack_handle(&self) -> Rc<RefCell<ModalStack>>, :750 fn drag_state_handle(&self) -> Rc<RefCell<DragState>>
GtkBackend gtk/backend.rs:1439, :1447
TuiBackend tui/backend.rs:967, :971
MacBackend macos/backend.rs:695, :699
WinBackend win/backend.rs:762, :766

App's 19 calls (modal_stack_handle ×12 at src/app.rs:1144, 1315, 2166, 2214, 2226, 3594, 3621, 3639, 3774, 4319, 4342, 4606; drag_state_handle ×7 at :2115, 3605, 3775, 3788, 3972, 4341, 4603) currently resolve to GtkBackend's inherent copies (gtk/backend.rs:560, :582) only because the field names the concrete type. Retype the field and all 19 compile unchanged.

Correction to the record: PLAN.md and GOALS.md both state "44 call sites" for this blocker. That number came from grep -n 'self\.backend\.' src/gtk/mod.rs — every use of the field, not these two methods. Today src/app.rs has 17 self.backend. lines. Do not size this work from the 44.

Scope

Retype App::backend and remove the GTK-concrete coupling that follows from it. Out of scope: any macOS code. #47's stages 2+ cannot be built or verified on any machine in this fleet (no Apple SDK / objc2 toolchain) — this issue is the buildable, testable part.

The residual GTK coupling in src/app.rs is 35 code lines (python3 scripts/native_lines.py gtk src/app.rs reports 64, but ~39 of those are doc-comments that merely mention GTK). Beyond the backend field:

Platform-typed fields:281 settings_monitor: Option<gio::FileMonitor>, :425 window: Option<gtk4::Window>, :580 css_provider: Option<gtk4::CssProvider>. Also in the new_headless signature at :896-900.

Hook sites, with upstream status:

Hook Site Status
Exit :1481, :1544 glib::idle_add_local_once(process::exit) quadraui already has Reaction::Exit (quadraui/src/runtime.rs; GTK's ReactionSink::request_exit at gtk/run.rs:1074). app.rs uses it 0 times; TUI returns it at 7 sites. Pure adoption.
200 ms yank highlight :1921 glib::timeout_add_local_once No toolkit needed. TUI does it portably with yank_hl_deadline: Cell<Option<Instant>> polled in tick (src/tui_main/shell_app.rs:486, :2938).
Colorscheme / dark mode :1934-1940, :5836-5839 Trait has set_theme (already called once by App).
Window maximize :5521-5546 Trait has toggle_window_maximize — and App already calls it while also driving gtk4::Window::maximize directly at :5530-5536. Drop the direct call.
Window title / minimize / close :2043-2044, :5521-5546 Small genuine gap — no set_title/minimize/close on the trait. File upstream if needed.
CSD capture :4999-5027 (list_toplevels, set_decorated(false)) Needs a window-chrome flag in ShellConfig, not a toolkit call.
Settings-file monitor :856-868 Needs a file watcher, not GTK (notify crate or mtime poll in tick). Parity note: the TUI has no settings hot-reload at all — zero hits.
Icon theme path / bundled icon font :821-825 Only matters if native icon rendering remains.
Folder picker :5592-5613 gtk4::FileDialog::select_folder Genuine gap in PlatformServices (quadraui/src/backend.rs:2136 has file open/save only) — handled by the FolderPickerController adoption issue, not here.
Text measurement :5971-5972 click::build_editor_click_contextset_pango_context Genuinely toolkit-bound. Leave it; this plus folder-select are the only two real native needs in the file.

crate::gtk::{click, css, util} is less coupled than the module doc claims: of 19 helpers used, only click::build_editor_click_context (Pango), css::load_css (returns CssProvider), and util::install_bundled_icon_font/app_icon_image are toolkit-bound. pixel_to_click_target (11 uses), handle_mouse_click/double/drag, resolve_tab_right_click, make_theme_css, open_url and all 10 gtk/mod.rs helpers have no GTK types in their signatures.

Fix

  1. Retype App::backend to a dyn Backend handle. Verify the 19 handle calls compile untouched.
  2. Adopt Reaction::Exit; delete the two glib::idle_add_local_once exits.
  3. Port the yank-highlight timer to the TUI's deadline-in-tick pattern.
  4. Drop the direct gtk4::Window::maximize call in favour of the trait method already being called beside it.
  5. Isolate what genuinely remains (Pango measurement context, window title/minimize/close, CSD, icon theme, settings watcher) behind a narrow trait or PlatformServices extension, so the gui gate covers only that.
  6. Update the src/app.rs module doc — it currently lists blockers that no longer exist.

Do not close #47; it stays open for the macOS stages. Note in the PR which of the module doc's blockers are now retired.

Acceptance

  • cargo test (GUI lane) green; cargo clippy -- -D warnings, cargo fmt pass.
  • python3 scripts/native_lines.py gtk src/app.rs drops materially from 64.
  • Behaviour-preserving refactor: exit, yank highlight, maximize and theme switching must be covered by existing or new GtkDriver black-box tests. If you add one, state in the PR that it fails against unfixed develop per CLAUDE.md.

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

    coordTracked by coord-tui pipelineplatformPlatform-specific (macOS, Windows, Linux)status:readyRefined and ready to enter the work pipeline

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions