Skip to content

GTK: 22 Relm4-era widget handles are permanently None — the second orphan pocket #672 could not see (and why #723's fix cannot run) #731

Description

@JDonaghy

Summary

src/gtk/draw.rs was not the only thing #540's Relm4 cutover orphaned. 22 widget
handles on App are initialised None at construction and assigned nowhere in the
crate.
They guard ~103 if let Some(…) / match arms that can never execute,
and — unlike draw.rs — there is no file-level #![allow(dead_code)] marking them,
so #672's "no file-level allow" acceptance criterion could not have found them.

This is the same sweep #672 did for draw.rs, applied to the widget-handle
pocket that survived it.

Why the compiler is silent

These fields are read, just never written. Rust warns on a field that is never
read; it does not warn on a field that is never assigned a non-default value. So
every one of these compiles clean, reads as live code to a human, and is unreachable
at runtime. Two are already documented as dead in-file:

  • src/gtk/mod.rs:534-542"Always None under the ShellApp runner … nothing in
    this file assigns this field. Every if let Some(da) = self.drawing_area… arm is
    therefore dead code inherited from the Relm4 build."
  • src/gtk/mod.rs:2123"self.drawing_area is never assigned under the ShellApp
    runner … so the old if let Some(da) arm never ran and this was unconditionally
    None."

The other 20 carry no such note.

Inventory

All declared in struct App and initialised Rc::new(RefCell::new(None)); grep for
borrow_mut() = Some / .replace(Some( across src/gtk/ returns zero hits for any
of them.

Field decl init refs guarded arms
drawing_area mod.rs:543 :1627 39 29
overlay :575 :1646 56 7
ext_dyn_panel_da_ref :555 :1632 14 12
settings_da_ref :571 :1643 12 6
git_sidebar_da_ref :552 :1630 11 8
explorer_sidebar_da_ref :517 :1622 11 8
debug_sidebar_da_ref :544 :1628 10 7
ext_sidebar_da_ref :553 :1631 7 4
ai_sidebar_da_ref :558 :1634 7 4
activity_bar_da_ref :520 :1623 6 4
ctx_menu_overlay_da :528 :1625 6 4
panel_hover_da :814 :1701 6 4
search_sidebar_da_ref :564 :1638 5 2
menu_dropdown_da :812 :1700 5 1
sidebar_inner_sw :559 :1635 4 2
ext_dyn_panel_box :557 :1633 3 1
explorer_panel_box :563 :1637 3 0
debug_panel_box :565 :1639 3 0
git_panel_box :566 :1640 3 0
ext_panel_box :567 :1641 3 0
settings_panel_box :568 :1642 3 0
ai_panel_box_ref :572 :1644 3 0

Plus window_scrollbars (:574, init :1645), which is a HashMap rather than an
Option but is populated only inside a block gated on two of the above.

The live consequence: #723's GTK half cannot run

This is not a hypothetical cleanup. e02a824 (2026-09-01, one week after #672)
landed the GTK half of #723 — inset the native gtk4::Scrollbar past the minimap strip
so the two stop fighting over the pane's right edge. The geometry is correct. It is also
unreachable:

create_window_scrollbars (mod.rs:2825)      <- only constructor of gtk4::Scrollbar
  called from mod.rs:2760, inside...
sync_scrollbar (mod.rs:2657)                <- only caller
  :2672  let overlay = match self.overlay.borrow().as_ref() { Some(o) => …, None => return };
  :2677  let drawing_area = match self.drawing_area.borrow().as_ref() { Some(da) => …, None => return };

Both guards are permanently None, so sync_scrollbar returns before reaching either
one. window_scrollbars is populated nowhere else. No gtk4::Scrollbar is ever
constructed under the runner
, so native_scrollbar_margin_start (:1438) and
sync_scrollbar_positions (:1459) have no live caller, and #723's fix cannot execute.

The commit message states the premise without drawing the conclusion:

GTK widget visibility/geometry has no headless coverage (App::new_headless never
constructs a real gtk4::Scrollbar; GtkDriver only sees Cairo paint, not overlay
widgets) — hence the pure-function test plus a SMOKE_TESTS item.

Its four native_scrollbar_placement_tests all exercise native_scrollbar_margin_start
in isolation: a pure function with no caller on any live path. They pass; they prove
nothing about the screen.

So #723 needs re-verifying on a display before it is closed, and whatever is actually
drawing the scrollbar the user sees is not this code. Two candidates worth checking
first: quadraui's own gtk::draw_editor painting a scrollbar into the single DA (the
TUI-equivalent path), or the runner's ScrolledWindow policy.

Scope

  1. Delete the 22 fields, their initialisers, and every arm they guard. Where an arm
    contained the only implementation of a behaviour, that behaviour is already absent
    at runtime — deleting it changes nothing on screen. Where deletion would remove the
    last reference to a helper (sync_scrollbar, create_window_scrollbars,
    sync_scrollbar_positions, native_scrollbar_margin_start, struct WindowScrollbars),
    delete the helper too.
  2. Re-derive No scrollbar when the minimap is on: quadraui's MinimapLayout.scrollbar is computed and discarded, and sync_scrollbar_positions doesn't know the strip exists #723 against reality. Determine what actually paints GTK's editor
    scrollbar, then either re-fix No scrollbar when the minimap is on: quadraui's MinimapLayout.scrollbar is computed and discarded, and sync_scrollbar_positions doesn't know the strip exists #723 there or reopen it with the corrected diagnosis.
    Do not delete native_scrollbar_margin_start's logic without recording where the
    inset decision has to move to.
  3. Audit the fields the deletion exposes. grep -rn '\.ai_panel\|\.ext_panel\|\.ext_sidebar\|\.diff_toolbar\|\.tab_scroll_offset' src/gtk/ returns zero — these
    ScreenLayout fields have no reader anywhere in the GTK backend. screen.ai_panel
    was named in #592-B: GTK live path — paint the panel surfaces (quickfix, bottom_tabs, debug_toolbar, panel_hover, ai_panel) #670's scope but its commit paints only
    quickfix/bottom_tabs/debug_toolbar/panel_hover. Confirm with a GtkDriver test
    whether the AI panel paints at all; if not, that is a GTK live render path silently drops 13 populated ScreenLayout fields (rest of #587's blast radius) #592-class gap that survived
    the epic.

Out of scope

Acceptance criteria

  • No field in struct App is Option-typed, initialised None, and never assigned.
    Add a comment to any deliberate exception saying who assigns it.
  • grep -rn 'gtk4::\(Box\|Overlay\|ScrolledWindow\)' src/gtk/ returns only live uses.
  • cargo build && cargo test && cargo clippy -- -D warnings && cargo fmt --check all
    EXIT=0.
  • A GtkDriver black-box test covering whichever surface turns out to be the real
    scrollbar painter, stated in the PR as verified RED against unfixed develop
    (CLAUDE.md, "Black-box coverage is the acceptance bar", rule 2).
  • The PR says explicitly which of the deleted arms, if any, removed a behaviour that a
    user could observe. Expected answer: none — but it must be checked, not assumed.

Files

  • src/gtk/mod.rs (all of the above)
  • src/gtk/testing.rs (new driver coverage)

Milestone

#7 Platform-Neutral


⚠️ Line numbers in this body drift — locate by symbol

Every line number above was measured on develop @ 4872ab9 (2026-09-01). They are
already wrong.
#727 landed src/gtk/mod.rs +435/−44 within hours and moved
enum Msg 1096→1131, fn dispatch 1799→1896, sync_scrollbar 2657→2754. Seven more
issues sit ahead of this one in the chain, each deleting or moving thousands of lines.

Treat the numbers as provenance — proof the claim was verified once — never as
coordinates. Locate by these anchors instead:

grep -nE "^\s+(explorer_sidebar_da_ref|activity_bar_da_ref|ctx_menu_overlay_da|drawing_area|debug_sidebar_da_ref|git_sidebar_da_ref|ext_sidebar_da_ref|ext_dyn_panel_da_ref|ext_dyn_panel_box|ai_sidebar_da_ref|sidebar_inner_sw|explorer_panel_box|search_sidebar_da_ref|debug_panel_box|git_panel_box|ext_panel_box|settings_panel_box|settings_da_ref|ai_panel_box_ref|overlay|menu_dropdown_da|panel_hover_da|window_scrollbars):" src/gtk/mod.rs
grep -nE "fn (sync_scrollbar|create_window_scrollbars|sync_scrollbar_positions|native_scrollbar_margin_start)|struct WindowScrollbars" src/gtk/mod.rs
# the inventory regenerates: for each field above, grep -c for reads, and confirm
# ZERO hits for:  grep -rn "borrow_mut() = Some\|\.replace(Some(" src/gtk/

This is the same defect #734 exists to fix: src/tui_main/ carries 19
mirrors mod.rs:NNNN comments whose targets all drifted after #540, leaving the only
record of a cross-backend contract pointing at unrelated code. Do not re-point the
numbers when they rot — the anchors are the durable form.

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

    bugSomething isn't workingcoordTracked by coord-tui pipelinestatus:readyRefined and ready to enter the work pipelineuiUI/rendering

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions