Problem
For mouse handling, the routers are shared but the appliers are transcribed. In each case render.rs already decides what was hit; both backends then hand-write the same match over the verdict.
| Surface |
TUI |
GTK |
Shared router |
Duplicated apply |
| Modal overlay |
src/tui_main/mouse.rs:461-618 |
src/app.rs:3693-3755 + :3573-3680 |
route_modal_overlay_click |
7-arm match (Toast/Dialog/ContextMenu/TabSwitcher/Completion/Picker/FindReplace) — ~150 lines |
| Drag |
src/tui_main/mouse.rs:858-995 |
src/app.rs:4397-4585 |
route_mouse_drag |
11-arm match — ~120 lines |
| MouseUp |
src/tui_main/mouse.rs:996-1061 |
src/app.rs:4591-4673 |
none |
same 8-step sequence — ~50 lines |
| Chrome click |
src/tui_main/mouse.rs:2786-2910 |
src/app.rs:4134-4253 |
route_chrome_click |
StatusBand assembly + ChromeRoute apply — ~80 lines |
| Scroll (wheel + click) |
mouse.rs:1120-1245, :1737-1790 |
src/app.rs:1320-1352, :3788-3814 |
dispatch_scroll |
widget-id → action tables, 4 copies — ~100 lines |
TerminalPanelResize in the drag arm is the same clamp expressed in rows vs line_height multiples; compute_editor_layout already abstracts that unit.
render::apply_scroll_offset (src/render.rs:5376-5470) already exists as "the union, once" for the drag path — and all four scroll copies ignore it.
Fix
render::apply_modal_overlay_route(engine, route, &mut impl ModalHost) — host hooks: modal-stack push/pop, refresh_file_tree, explorer-context-action dispatch, exit.
render::apply_drag_route(...) for the 11-arm match.
- Share the MouseUp sequence (tab_drag release, drag_state end, divider clear, terminal resize/split finalize +
session.save, mouse_drag_active = false, terminal_autocopy_selection).
- Share the chrome apply. Root cause to fix while here:
src/tui_main/render_impl.rs:972-991 (render_window_status_line) throws away draw_status_bar's returned layout (let _ = ...) and re-derives zones at click time. GTK caches it. That violates feedback_cache_paint_layout and is why the two sides diverged.
- Add
render::apply_wheel_scroll, and route the click path's ScrollOffsetChanged through the existing apply_scroll_offset. Delete all four widget-id tables.
Also in scope — dead and unreachable routing
src/tui_main/mouse.rs:1844-1899 (activity-bar arm, 56 lines) appears unreachable: quadraui's ShellAdapter::handle (shell_adapter.rs:266-345) runs AppShell::handle first and returns on PanelChanged/SidebarHidden/BottomItemClicked, and src/tui_main/shell_app.rs:2740-2752 says so in its own comment. Only test callers reach it. It still carries a MenuSystem::new(build_menu_defs(..)) rebuild hack. Verify, then delete.
src/tui_main/shell_app.rs:1297-1330 (debug) and :1439-1500 (explorer) call dispatch_dap_sidebar_body_event / route_explorer_tree_event; src/tui_main/mouse.rs:1976-2010 and :2011-2043 call the same functions for the same rects, reachable only when the first declined. ~100 lines of shadowed routing.
Acceptance
- Black-box tests required, both backends, per surface: modal dismissal, divider drag, terminal panel resize, tab drag release, status-bar zone clicks, wheel scroll over each registered scroll surface.
- State in the PR that the new tests fail against unfixed
develop.
Problem
For mouse handling, the routers are shared but the appliers are transcribed. In each case
render.rsalready decides what was hit; both backends then hand-write the samematchover the verdict.src/tui_main/mouse.rs:461-618src/app.rs:3693-3755+:3573-3680route_modal_overlay_clicksrc/tui_main/mouse.rs:858-995src/app.rs:4397-4585route_mouse_dragsrc/tui_main/mouse.rs:996-1061src/app.rs:4591-4673src/tui_main/mouse.rs:2786-2910src/app.rs:4134-4253route_chrome_clickStatusBandassembly +ChromeRouteapply — ~80 linesmouse.rs:1120-1245,:1737-1790src/app.rs:1320-1352,:3788-3814dispatch_scrollTerminalPanelResizein the drag arm is the same clamp expressed in rows vsline_heightmultiples;compute_editor_layoutalready abstracts that unit.render::apply_scroll_offset(src/render.rs:5376-5470) already exists as "the union, once" for the drag path — and all four scroll copies ignore it.Fix
render::apply_modal_overlay_route(engine, route, &mut impl ModalHost)— host hooks: modal-stack push/pop,refresh_file_tree, explorer-context-action dispatch, exit.render::apply_drag_route(...)for the 11-arm match.session.save,mouse_drag_active = false,terminal_autocopy_selection).src/tui_main/render_impl.rs:972-991(render_window_status_line) throws awaydraw_status_bar's returned layout (let _ = ...) and re-derives zones at click time. GTK caches it. That violatesfeedback_cache_paint_layoutand is why the two sides diverged.render::apply_wheel_scroll, and route the click path'sScrollOffsetChangedthrough the existingapply_scroll_offset. Delete all four widget-id tables.Also in scope — dead and unreachable routing
src/tui_main/mouse.rs:1844-1899(activity-bar arm, 56 lines) appears unreachable: quadraui'sShellAdapter::handle(shell_adapter.rs:266-345) runsAppShell::handlefirst and returns onPanelChanged/SidebarHidden/BottomItemClicked, andsrc/tui_main/shell_app.rs:2740-2752says so in its own comment. Only test callers reach it. It still carries aMenuSystem::new(build_menu_defs(..))rebuild hack. Verify, then delete.src/tui_main/shell_app.rs:1297-1330(debug) and:1439-1500(explorer) calldispatch_dap_sidebar_body_event/route_explorer_tree_event;src/tui_main/mouse.rs:1976-2010and:2011-2043call the same functions for the same rects, reachable only when the first declined. ~100 lines of shadowed routing.Acceptance
develop.