diff --git a/src/tui_main/mod.rs b/src/tui_main/mod.rs index 61166cd0..7db7b029 100644 --- a/src/tui_main/mod.rs +++ b/src/tui_main/mod.rs @@ -784,6 +784,46 @@ fn restore_terminal(terminal: &mut Terminal>, keyboard_ // ─── Event loop ─────────────────────────────────────────────────────────────── +/// Enter `backend`'s frame scope exactly once for the whole `draw_frame` +/// call, while still handing `draw_frame` (and everything it calls) a +/// genuine `&mut ratatui::Frame` for the handful of raw buffer writes +/// (separators, cursor placement, ...) that have no `Backend::draw_*` +/// trait equivalent and are interleaved with trait calls in a +/// z-order-sensitive sequence (#600 Stage 1 — collapsing the ~30 +/// `enter_frame_scope` sites `draw_frame`/`panels.rs` used to open +/// individually down to the one this function makes). +/// +/// Rust's borrow checker won't let a single closure passed to +/// `TuiBackend::enter_frame_scope(frame, |b| ...)` also capture the +/// outer `frame` binding — `frame` is already consumed as +/// `enter_frame_scope`'s own argument, so referencing it again inside +/// the closure is E0382 (use of moved value). Relaying it through a raw +/// pointer sidesteps that: it's the same type-erasure technique +/// `TuiBackend::enter_frame_scope` already uses internally to smuggle +/// `&mut Frame<'_>` past its own `Cell<*mut ()>` field, just applied one +/// layer higher so `f` can reach both `backend` and `frame` at once. +fn with_frame_scope( + backend: &mut backend::TuiBackend, + frame: &mut ratatui::Frame<'_>, + f: impl FnOnce(&mut backend::TuiBackend, &mut ratatui::Frame<'_>) -> R, +) -> R { + // Reborrow (not move) so `frame` is still available to pass into + // `enter_frame_scope` below; the raw pointer itself carries no + // borrow-checker-tracked lifetime. + let frame_ptr: *mut ratatui::Frame<'_> = &mut *frame as *mut ratatui::Frame<'_>; + backend.enter_frame_scope(frame, |b| { + // SAFETY: `frame_ptr` aliases the exact `Frame` `frame` refers + // to. The outer `frame` binding above is not read again until + // this closure returns (it was moved into the `enter_frame_scope` + // call and `enter_frame_scope` itself only touches it through + // its own type-erased pointer, never dereferencing it while `f` + // runs — see that function's doc comment), so this is the only + // live `&mut Frame` in play for the duration of `f`. + let frame: &mut ratatui::Frame<'_> = unsafe { &mut *frame_ptr }; + f(b, frame) + }) +} + fn event_loop( terminal: &mut Terminal>, engine: &mut Engine, @@ -1038,32 +1078,34 @@ fn event_loop( .draw(|frame| { if let Some(s) = &screen { let drop_target = explorer_drag_active.as_ref().and_then(|&(_, t)| t); - draw_frame( - frame, - s, - &theme, - &mut sidebar, - engine, - sidebar_width, - quickfix_scroll_top, - folder_picker.as_ref(), - cmd_sel, - drop_target, - &mut hover_link_rects, - &mut hover_popup_rect, - &mut editor_hover_popup_rect, - &mut editor_hover_link_rects, - &mut editor_hover_scrollbar, - &mut tab_visible_counts, - &mut debug_toolbar_rect, - &mut completion_layout, - &mut context_menu_layout, - &mut dialog_layout, - &mut backend, - tui_drag_source, - tui_drag_cursor, - &tui_tab_drop_zone, - ); + with_frame_scope(&mut backend, frame, |backend, frame| { + draw_frame( + frame, + s, + &theme, + &mut sidebar, + engine, + sidebar_width, + quickfix_scroll_top, + folder_picker.as_ref(), + cmd_sel, + drop_target, + &mut hover_link_rects, + &mut hover_popup_rect, + &mut editor_hover_popup_rect, + &mut editor_hover_link_rects, + &mut editor_hover_scrollbar, + &mut tab_visible_counts, + &mut debug_toolbar_rect, + &mut completion_layout, + &mut context_menu_layout, + &mut dialog_layout, + backend, + tui_drag_source, + tui_drag_cursor, + &tui_tab_drop_zone, + ); + }); } }) .expect("draw frame"); @@ -1093,32 +1135,34 @@ fn event_loop( if let Some(s) = last_layout.as_ref() { let drop_target = explorer_drag_active.as_ref().and_then(|&(_, t)| t); - draw_frame( - frame, - s, - &theme, - &mut sidebar, - engine, - sidebar_width, - quickfix_scroll_top, - folder_picker.as_ref(), - cmd_sel, - drop_target, - &mut hover_link_rects, - &mut hover_popup_rect, - &mut editor_hover_popup_rect, - &mut editor_hover_link_rects, - &mut editor_hover_scrollbar, - &mut tab_visible_counts2, - &mut debug_toolbar_rect, - &mut completion_layout, - &mut context_menu_layout, - &mut dialog_layout, - &mut backend, - tui_drag_source, - tui_drag_cursor, - &tui_tab_drop_zone, - ); + with_frame_scope(&mut backend, frame, |backend, frame| { + draw_frame( + frame, + s, + &theme, + &mut sidebar, + engine, + sidebar_width, + quickfix_scroll_top, + folder_picker.as_ref(), + cmd_sel, + drop_target, + &mut hover_link_rects, + &mut hover_popup_rect, + &mut editor_hover_popup_rect, + &mut editor_hover_link_rects, + &mut editor_hover_scrollbar, + &mut tab_visible_counts2, + &mut debug_toolbar_rect, + &mut completion_layout, + &mut context_menu_layout, + &mut dialog_layout, + backend, + tui_drag_source, + tui_drag_cursor, + &tui_tab_drop_zone, + ); + }); } }) .expect("draw frame"); diff --git a/src/tui_main/panels.rs b/src/tui_main/panels.rs index ca3dd44a..bfe3cf55 100644 --- a/src/tui_main/panels.rs +++ b/src/tui_main/panels.rs @@ -47,11 +47,11 @@ pub(super) fn render_sidebar( return; } Some(PANEL_SEARCH) => { - render_search_panel(backend, frame, area, engine, theme); + render_search_panel(backend, area, engine, theme); return; } Some(PANEL_DEBUG) => { - render_debug_sidebar(backend, frame, area, engine, theme); + render_debug_sidebar(backend, area, engine, theme); return; } Some(PANEL_GIT) => { @@ -88,10 +88,13 @@ pub(super) fn render_sidebar( engine.explorer_tree_rect.set(q_rect); engine.explorer_viewport_rows.set(area.height as usize); render::populate_explorer_tree_controller(engine, theme); + // Do NOT open a nested `enter_frame_scope` here — `render_sidebar` is + // called from `draw_frame`, which already runs inside the caller's + // single `with_frame_scope` (see mod.rs's `terminal.draw` closures). + // Re-entering would just be a no-op round trip on `current_frame_ptr`, + // but it contradicts the "entered once per draw closure" invariant. backend.set_current_theme(super::quadraui_tui::q_theme(theme)); - backend.enter_frame_scope(frame, |b| { - engine.explorer_tree.borrow().render(b, q_rect); - }); + engine.explorer_tree.borrow().render(backend, q_rect); // TreeController.render() draws the scrollbar internally. // Register a ScrollSurface for scroll-wheel dispatch only. @@ -147,6 +150,9 @@ pub(super) fn render_settings_panel( width: area.width, height: chrome_h, }; + // Stage 1 scope note: `draw_settings_chrome` is a free rasteriser with no + // `Backend::draw_*` trait equivalent (checked against quadraui's Backend + // trait), so calling it directly on `buf` is correct and out of scope here. quadraui::tui::draw_settings_chrome( buf, chrome_area, @@ -176,18 +182,15 @@ pub(super) fn render_settings_panel( content_height as f32, ); backend.set_current_theme(super::quadraui_tui::q_theme(theme)); - backend.enter_frame_scope(frame, |b| { - engine - .settings_form_controller - .borrow_mut() - .render_and_cache(b, q_rect); - }); + engine + .settings_form_controller + .borrow_mut() + .render_and_cache(backend, q_rect); } /// Render the project search panel via SidebarSystem (Form + TreeView). pub(super) fn render_search_panel( backend: &mut super::backend::TuiBackend, - frame: &mut ratatui::Frame, area: Rect, engine: &Engine, theme: &Theme, @@ -215,9 +218,10 @@ pub(super) fn render_search_panel( engine.search_sidebar_body_rect.set(q_rect); backend.set_current_theme(super::quadraui_tui::q_theme(theme)); - backend.enter_frame_scope(frame, |b| { - engine.search_sidebar_system.borrow().render(b, q_rect); - }); + engine + .search_sidebar_system + .borrow() + .render(backend, q_rect); } // ─── Status / command line ──────────────────────────────────────────────────── @@ -355,11 +359,9 @@ pub(super) fn render_source_control( area.width as f32, paint_h as f32, ); + use quadraui::Backend; backend.set_current_theme(super::quadraui_tui::q_theme(theme)); - backend.enter_frame_scope(frame, |b| { - use quadraui::Backend; - b.draw_text_input(ti_rect, &ti); - }); + backend.draw_text_input(ti_rect, &ti); } if area.height < 1 + commit_box_h { @@ -382,9 +384,7 @@ pub(super) fn render_source_control( slab_h as f32, ); backend.set_current_theme(super::quadraui_tui::q_theme(theme)); - backend.enter_frame_scope(frame, |b| { - render::draw_sc_sidebar_panel(b, engine, sc, slab_rect); - }); + render::draw_sc_sidebar_panel(backend, engine, sc, slab_rect); } // Read section-area origin from the cached layout. @@ -414,9 +414,7 @@ pub(super) fn render_source_control( engine.sc_sidebar_body_rect.set(q_rect); render::populate_sc_sidebar_system(engine, theme); backend.set_current_theme(super::quadraui_tui::q_theme(theme)); - backend.enter_frame_scope(frame, |b| { - engine.sc_sidebar_system.borrow().render(b, q_rect); - }); + engine.sc_sidebar_system.borrow().render(backend, q_rect); // ── Branch picker / create popup (quadraui::Palette dual-mode, #480) ───── // Migrated from a hand-rolled popup to the dual-mode `Palette` primitive // shipped in quadraui#224 (list mode = switch branch, input mode = @@ -439,11 +437,9 @@ pub(super) fn render_source_control( popup_w as f32, popup_h as f32, ); + use quadraui::Backend; backend.set_current_theme(super::quadraui_tui::q_theme(theme)); - backend.enter_frame_scope(frame, |b| { - use quadraui::Backend; - b.draw_palette(q_rect, &palette); - }); + backend.draw_palette(q_rect, &palette); } // ── Help dialog (quadraui::Dialog + DialogTable, #480) ─────────────────── @@ -451,6 +447,7 @@ pub(super) fn render_source_control( // shipped in quadraui#225. Bindings list lives once in // `render::sc_help_dialog` instead of being duplicated per backend. if sc.help_open { + use quadraui::Backend; let viewport = quadraui::Rect::new( area.x as f32, area.y as f32, @@ -458,7 +455,8 @@ pub(super) fn render_source_control( area.height as f32, ); let (dialog, layout) = render::sc_help_dialog_layout(viewport, 1.0, 1.0); - super::quadraui_tui::draw_dialog(frame.buffer_mut(), &dialog, &layout, theme); + backend.set_current_theme(super::quadraui_tui::q_theme(theme)); + let _ = backend.draw_dialog(&dialog, &layout); } } @@ -497,6 +495,9 @@ pub(super) fn render_ext_panel( width: area.width, height: chrome_h, }; + // Stage 1 scope note: `draw_settings_chrome` is a free rasteriser with no + // `Backend::draw_*` trait equivalent (checked against quadraui's Backend + // trait), so calling it directly on the buffer is correct and out of scope here. quadraui::tui::draw_settings_chrome( frame.buffer_mut(), chrome_area, @@ -518,11 +519,9 @@ pub(super) fn render_ext_panel( body_w as f32, body_h as f32, ); + use quadraui::Backend; backend.set_current_theme(super::quadraui_tui::q_theme(theme)); - backend.enter_frame_scope(frame, |b| { - use quadraui::Backend; - b.draw_tree(body_q_rect, &tree); - }); + backend.draw_tree(body_q_rect, &tree); // Manual scrollbar: `draw_tree` doesn't render scrollbars yet. // Total visible rows = tree.rows.len() (sections + their expanded @@ -662,9 +661,9 @@ pub(super) fn render_ext_panel( /// The popup displays rendered markdown content and appears to the right of /// the sidebar at the vertical position of the hovered item. /// Returns (link_rects, popup_rect) where popup_rect is (x, y, w, h). -#[allow(clippy::type_complexity)] +#[allow(clippy::type_complexity, clippy::too_many_arguments)] pub(super) fn render_panel_hover_popup( - frame: &mut ratatui::Frame, + backend: &mut super::backend::TuiBackend, screen: &render::ScreenLayout, theme: &Theme, sidebar_right_x: u16, @@ -753,7 +752,9 @@ pub(super) fn render_panel_hover_popup( }, ); - super::quadraui_tui::draw_rich_text_popup(frame.buffer_mut(), &popup, &layout, theme); + use quadraui::Backend; + backend.set_current_theme(super::quadraui_tui::q_theme(theme)); + backend.draw_rich_text_popup(&popup, &layout); let link_rects: Vec<(u16, u16, u16, u16, String)> = layout .link_hit_regions @@ -789,9 +790,9 @@ pub(super) fn render_panel_hover_popup( /// Render an editor hover popup via the `quadraui::RichTextPopup` /// primitive. Returns `(link_rects, popup_bounds, scrollbar_hit)` for /// mouse hit-testing — derived from the primitive's resolved layout. -#[allow(clippy::type_complexity)] +#[allow(clippy::type_complexity, clippy::too_many_arguments)] pub(super) fn render_editor_hover_popup( - frame: &mut ratatui::Frame, + backend: &mut super::backend::TuiBackend, eh: &render::EditorHoverPopupData, popup_x: u16, popup_y: u16, @@ -837,7 +838,9 @@ pub(super) fn render_editor_hover_popup( }, ); - super::quadraui_tui::draw_rich_text_popup(frame.buffer_mut(), &popup, &layout, theme); + use quadraui::Backend; + backend.set_current_theme(super::quadraui_tui::q_theme(theme)); + backend.draw_rich_text_popup(&popup, &layout); let link_rects: Vec<(u16, u16, u16, u16, String)> = layout .link_hit_regions @@ -965,9 +968,7 @@ pub(super) fn render_ext_sidebar( render::populate_ext_sidebar_system(engine); let q_theme = super::quadraui_tui::q_theme(theme); backend.set_current_theme(q_theme); - backend.enter_frame_scope(frame, |b| { - engine.ext_sidebar_system.borrow().render(b, msv_rect); - }); + engine.ext_sidebar_system.borrow().render(backend, msv_rect); } // ─── AI assistant sidebar panel ─────────────────────────────────────────────── @@ -1190,11 +1191,12 @@ pub(super) fn render_ai_sidebar( /// chrome; item rendering goes through `Backend::draw_tree`. pub(super) fn render_debug_sidebar( backend: &mut super::backend::TuiBackend, - frame: &mut ratatui::Frame, area: Rect, engine: &Engine, theme: &Theme, ) { + use quadraui::Backend; + if area.height == 0 { return; } @@ -1209,10 +1211,7 @@ pub(super) fn render_debug_sidebar( let title_rect = quadraui::Rect::new(area.x as f32, area.y as f32, area.width as f32, 1.0); backend.set_current_theme(q_theme); - backend.enter_frame_scope(frame, |b| { - use quadraui::Backend; - let _ = b.draw_status_bar(title_rect, &title_bar, None, None); - }); + let _ = backend.draw_status_bar(title_rect, &title_bar, None, None); if area.height < 2 { return; @@ -1221,10 +1220,7 @@ pub(super) fn render_debug_sidebar( let action_rect = quadraui::Rect::new(area.x as f32, (area.y + 1) as f32, area.width as f32, 1.0); backend.set_current_theme(q_theme); - let hits = backend.enter_frame_scope(frame, |b| { - use quadraui::Backend; - b.draw_status_bar(action_rect, &action_bar, None, None) - }); + let hits = backend.draw_status_bar(action_rect, &action_bar, None, None); engine.dap_sidebar_action_hits.replace(Some(hits)); // ── SidebarSystem body (the four sections). ── @@ -1240,9 +1236,7 @@ pub(super) fn render_debug_sidebar( engine.dap_sidebar_body_rect.set(msv_rect); render::populate_dap_sidebar_system(engine); backend.set_current_theme(q_theme); - backend.enter_frame_scope(frame, |b| { - engine.dap_sidebar_system.borrow().render(b, msv_rect); - }); + engine.dap_sidebar_system.borrow().render(backend, msv_rect); } /// Render the bottom panel tab bar (Terminal | Debug Output) via @@ -1250,13 +1244,13 @@ pub(super) fn render_debug_sidebar( /// click handler (caller caches on `engine.bottom_tab_bar_hits`). pub(super) fn render_bottom_panel_tabs( backend: &mut super::backend::TuiBackend, - frame: &mut ratatui::Frame, area: Rect, active: &render::BottomPanelKind, has_terminal: bool, has_debug_output: bool, theme: &Theme, ) -> quadraui::TabBarHits { + use quadraui::Backend; let bar = render::build_bottom_panel_tab_bar(active, has_terminal, has_debug_output); let q_rect = quadraui::Rect::new( area.x as f32, @@ -1265,22 +1259,19 @@ pub(super) fn render_bottom_panel_tabs( area.height as f32, ); backend.set_current_theme(super::quadraui_tui::q_theme(theme)); - backend.enter_frame_scope(frame, |b| { - use quadraui::Backend; - b.draw_tab_bar(q_rect, &bar, None) - }) + backend.draw_tab_bar(q_rect, &bar, None) } // ─── Quickfix panel ─────────────────────────────────────────────────────────── pub(super) fn render_quickfix_panel( - frame: &mut ratatui::Frame, area: Rect, qf: &render::QuickfixPanel, scroll_top: usize, theme: &Theme, backend: &mut super::backend::TuiBackend, ) { + use quadraui::Backend; if area.height == 0 { return; } @@ -1298,10 +1289,7 @@ pub(super) fn render_quickfix_panel( area.height as f32, ); backend.set_current_theme(super::quadraui_tui::q_theme(theme)); - backend.enter_frame_scope(frame, |b| { - use quadraui::Backend; - b.draw_list(q_rect, &list); - }); + backend.draw_list(q_rect, &list); } // ─── Terminal panel ─────────────────────────────────────────────────────────── @@ -1310,12 +1298,12 @@ pub(super) fn render_quickfix_panel( /// quadraui primitives. Returns cached hit data for click dispatch. pub(super) fn render_terminal_toolbar( backend: &mut super::backend::TuiBackend, - frame: &mut ratatui::Frame, area: Rect, panel: &render::TerminalPanel, theme: &Theme, ) -> crate::core::engine::TerminalToolbarHits { use crate::core::engine::TerminalToolbarHits; + use quadraui::Backend; let toolbar = render::build_terminal_toolbar(panel, theme); let q_rect = quadraui::Rect::new( @@ -1327,12 +1315,9 @@ pub(super) fn render_terminal_toolbar( backend.set_current_theme(super::quadraui_tui::q_theme(theme)); match toolbar { render::TerminalToolbar::FindBar(bar) => { - let layout = backend.enter_frame_scope(frame, |b| { - use quadraui::Backend; - let _regions = b.draw_status_bar(q_rect, &bar, None, None); - bar.layout(area.width as f32, 1.0, 2.0, |seg| { - quadraui::StatusSegmentMeasure::new(seg.text.chars().count() as f32) - }) + let _regions = backend.draw_status_bar(q_rect, &bar, None, None); + let layout = bar.layout(area.width as f32, 1.0, 2.0, |seg| { + quadraui::StatusSegmentMeasure::new(seg.text.chars().count() as f32) }); TerminalToolbarHits::FindBar { layout, @@ -1340,10 +1325,7 @@ pub(super) fn render_terminal_toolbar( } } render::TerminalToolbar::TabStrip(bar) => { - let hits = backend.enter_frame_scope(frame, |b| { - use quadraui::Backend; - b.draw_tab_bar(q_rect, &bar, None) - }); + let hits = backend.draw_tab_bar(q_rect, &bar, None); TerminalToolbarHits::TabStrip(hits) } } @@ -1382,27 +1364,27 @@ pub(super) fn render_terminal_panel( let td = render::build_terminal_draw_data(panel, q_area, 1.0, 1.0, content_rows, None); engine.terminal_split_layout.replace(td.split); backend.set_current_theme(q_theme); - if let Some(split) = &td.split { - let left = td.left.as_ref().unwrap(); - let right = td.right.as_ref().unwrap(); - let sl = *split; - backend.enter_frame_scope(frame, |b| { - use quadraui::Backend; - b.draw_terminal(sl.left, left); - b.draw_terminal(sl.right, right); - }); - quadraui::tui::draw_terminal_divider( - frame.buffer_mut(), - split.divider_x as u16, - area.y, - area.height, - &q_theme, - ); - } else if let Some(ref term) = td.single { - backend.enter_frame_scope(frame, |b| { - use quadraui::Backend; - b.draw_terminal(q_area, term); - }); + { + use quadraui::Backend; + if let Some(split) = &td.split { + let left = td.left.as_ref().unwrap(); + let right = td.right.as_ref().unwrap(); + backend.draw_terminal(split.left, left); + backend.draw_terminal(split.right, right); + // Stage 1 scope note: `draw_terminal_divider` is a free rasteriser + // with no `Backend::draw_*` trait equivalent (checked against + // quadraui's Backend trait), so calling it directly is correct + // and out of scope here. + quadraui::tui::draw_terminal_divider( + frame.buffer_mut(), + split.divider_x as u16, + area.y, + area.height, + &q_theme, + ); + } else if let Some(ref term) = td.single { + backend.draw_terminal(q_area, term); + } } } @@ -1452,7 +1434,13 @@ mod sc_panel_tests { }; terminal .draw(|frame| { - render_source_control(&mut tui_backend, frame, area, engine, &theme); + // #600: `render_source_control` calls `Backend::draw_*` trait + // methods directly now (no per-call `enter_frame_scope`), so + // this harness needs to open the scope itself — mirrors what + // `event_loop`'s two `terminal.draw` closures do in `mod.rs`. + super::with_frame_scope(&mut tui_backend, frame, |backend, frame| { + render_source_control(backend, frame, area, engine, &theme); + }); }) .unwrap(); let buf = terminal.backend().buffer(); diff --git a/src/tui_main/quadraui_tui.rs b/src/tui_main/quadraui_tui.rs index 00b1736b..b7844f4d 100644 --- a/src/tui_main/quadraui_tui.rs +++ b/src/tui_main/quadraui_tui.rs @@ -4,6 +4,18 @@ //! primitives into a ratatui `Buffer`. Over time this file will grow to //! cover every primitive; currently supports `TreeView` (A.1a), //! `Form` (A.3a), `Palette` (A.4), and `ListView` (A.5). +//! +//! #600 Stage 1: the wrappers that used to live here for `ContextMenu`, +//! `Completions`, `Dialog`, `Tooltip`, `FindReplacePanel`, and +//! `RichTextPopup` were removed — every call site now routes through the +//! equivalent `Backend::draw_*` trait method (which reaches the same +//! underlying `quadraui::tui::draw_*` rasteriser internally) instead of +//! calling these free functions directly on `frame.buffer_mut()`. +//! `draw_activity_bar` stays a free function: `Backend::draw_activity_bar` +//! has a side effect (`focused_activity_bar` bookkeeping for a keyboard- +//! routing feature TUI doesn't use yet) and a `hovered_idx` parameter this +//! call site has no value for, so swapping it in isn't a same-behavior +//! change like the others were. use super::*; use ratatui::buffer::Buffer; @@ -32,66 +44,6 @@ pub(super) fn q_theme(theme: &Theme) -> quadraui::Theme { /// Keyboard-selected items get a full-row selection-bg fill; active /// items get a left-edge accent bar (unless keyboard-selected, where /// the selection bg takes precedence). -/// Draw a `quadraui::ContextMenu` popup via its D6 `ContextMenuLayout`. -/// Matches the pre-migration chrome: thin box border, selected item -/// rendered inverted, separators as a horizontal dash line, disabled -/// items dimmed. Shortcut (from item.detail) is right-aligned. -pub(super) fn draw_context_menu( - buf: &mut Buffer, - menu: &quadraui::ContextMenu, - layout: &quadraui::ContextMenuLayout, - theme: &Theme, -) { - quadraui::tui::draw_context_menu(buf, menu, layout, &q_theme(theme)); -} - -/// Draw a `quadraui::Completions` popup via the lifted -/// `quadraui::tui::draw_completions` rasteriser (#266). Vimcode's shim -/// role is to map the rich `render::Theme` to the smaller -/// `quadraui::Theme` via `q_theme()` — the body of the rasteriser -/// lives in the quadraui crate. -pub(super) fn draw_completions( - buf: &mut Buffer, - completions: &quadraui::Completions, - layout: &quadraui::CompletionsLayout, - theme: &Theme, -) { - quadraui::tui::draw_completions(buf, completions, layout, &q_theme(theme)); -} - -/// Draw a `quadraui::Dialog` via its D6 `DialogLayout`. Handles the -/// rounded-border chrome the TUI has always drawn and respects -/// horizontal vs. vertical button layout. -/// -/// The body text may contain embedded `\n` for multi-line messages — -/// each line is drawn on its own row inside `layout.body_bounds`. -pub(super) fn draw_dialog( - buf: &mut Buffer, - dialog: &quadraui::Dialog, - layout: &quadraui::DialogLayout, - theme: &Theme, -) { - quadraui::tui::draw_dialog(buf, dialog, layout, &q_theme(theme)); -} - -/// Draw a `quadraui::Tooltip` into `layout.bounds` on `buf`. Renders a -/// text box with side-bar borders only (`│` on the first and last -/// columns, no top/bottom border) — matches the visual style used by -/// the LSP hover popup and signature help. -/// -/// If `tooltip.styled` is `Some`, a single line of styled spans is -/// rendered (signature help path). Otherwise `tooltip.text` is split -/// on `\n` and each line is rendered plain (hover popup path). Lines -/// that exceed the box width are truncated. -pub(super) fn draw_tooltip( - buf: &mut Buffer, - tooltip: &quadraui::Tooltip, - layout: &quadraui::TooltipLayout, - theme: &Theme, -) { - quadraui::tui::draw_tooltip(buf, tooltip, layout, &q_theme(theme)); -} - pub(super) fn draw_activity_bar( buf: &mut Buffer, area: Rect, @@ -142,54 +94,3 @@ pub(super) fn draw_activity_bar( } } } - -/// Draw one row of a `quadraui::Terminal` cell grid into a ratatui buffer. -/// -/// `start_x` / `screen_row` are the destination cell coordinates; -/// `max_cols` clips the row to the visible width. `theme` supplies -/// fallback colours for find-match overlays — the cell's own `fg` / `bg` -/// win for normal cells and cursor/selection (which use inverted colours). -/// Draw the find/replace overlay by walking `panel.hit_regions` (the -/// shared cross-backend layout source-of-truth from -/// `core::engine::compute_find_replace_hit_regions`). Painting and -/// hit-test then derive from the same `FrHitRegion` list, so column -/// drift bugs (the same class fixed for debug toolbar + breadcrumb) -/// can't recur on this overlay. -/// -/// `panel.group_bounds.x/y` is already absolute terminal-screen space -/// (#550 — it's derived from `window_rects`, which TUI now feeds in -/// absolute coordinates like GTK, rather than content-area-relative). The -/// underlying `quadraui::tui::draw_find_replace` rasteriser still takes an -/// `editor_left` translation param (it's TUI-only — GTK never calls this -/// path — and quadraui's signature can't be changed from here); this -/// wrapper always passes `0` so that internal translation is a no-op -/// instead of double-counting the origin already baked into -/// `group_bounds`. The click-hit-test mirroring this paint math is in -/// `mouse.rs`'s find/replace handler — keep the two in sync. -/// -/// Painting that the hit-region list doesn't directly cover — -/// borders, the match-count text (a non-clickable status string), and -/// the focused field's cursor + selection — is layered in around the -/// region-driven dispatch. -pub(super) fn draw_find_replace( - buf: &mut Buffer, - area: Rect, - panel: &crate::render::FindReplacePanel, - theme: &Theme, -) { - quadraui::tui::draw_find_replace(buf, area, panel, &q_theme(theme), 0); -} - -/// Draw a `quadraui::RichTextPopup` into the buffer via the lifted -/// `quadraui::tui::draw_rich_text_popup` rasteriser (#266). Vimcode's -/// shim role is to map the rich `render::Theme` to the smaller -/// `quadraui::Theme` via `q_theme()` — the body of the rasteriser -/// lives in the quadraui crate. -pub(super) fn draw_rich_text_popup( - buf: &mut Buffer, - popup: &quadraui::RichTextPopup, - layout: &quadraui::RichTextPopupLayout, - theme: &Theme, -) { - quadraui::tui::draw_rich_text_popup(buf, popup, layout, &q_theme(theme)); -} diff --git a/src/tui_main/render_impl.rs b/src/tui_main/render_impl.rs index 92a2be91..2c651537 100644 --- a/src/tui_main/render_impl.rs +++ b/src/tui_main/render_impl.rs @@ -132,6 +132,16 @@ pub(super) fn draw_frame( ) { let area = frame.area(); + // #600 Stage 1: `backend` is already inside `with_frame_scope`'s single + // frame-scope entry for the whole call (`mod.rs::event_loop`'s two + // `terminal.draw` closures each open exactly one), so every + // `Backend::draw_*` call below can be made directly — no per-call + // `enter_frame_scope`/`set_current_theme` needed. `q_theme` is computed + // once here and reused everywhere a quadraui primitive needs it. + use quadraui::Backend as _; + let q_theme = super::quadraui_tui::q_theme(theme); + backend.set_current_theme(q_theme); + engine.scroll_surfaces.borrow_mut().clear(); // ── Top-level: [menu] / [content_area] ── @@ -216,7 +226,6 @@ pub(super) fn draw_frame( // ── Render menu bar strip (if visible) ─────────────────────────────────── if screen.menu_bar_visible { - let q_theme = super::quadraui_tui::q_theme(theme); let bar = engine.menu_system.borrow().menu_bar(); let bar_rect = quadraui::Rect::new( menu_bar_area.x as f32, @@ -224,10 +233,7 @@ pub(super) fn draw_frame( menu_bar_area.width as f32, menu_bar_area.height as f32, ); - let mb_layout = backend.enter_frame_scope(frame, |b| { - use quadraui::Backend; - b.draw_menu_bar(bar_rect, &bar) - }); + let mb_layout = backend.draw_menu_bar(bar_rect, &bar); let menu_end: u16 = mb_layout .visible_items @@ -254,8 +260,13 @@ pub(super) fn draw_frame( .saturating_sub(menu_end - menu_bar_area.x), height: menu_bar_area.height, }; - let cc_layout = - quadraui::tui::draw_command_center(frame.buffer_mut(), cc_area, &cc, &q_theme); + let cc_q_rect = quadraui::Rect::new( + cc_area.x as f32, + cc_area.y as f32, + cc_area.width as f32, + cc_area.height as f32, + ); + let cc_layout = backend.draw_command_center(cc_q_rect, &cc); engine.command_center_layout.replace(Some(cc_layout)); // Note: dropdown is rendered LAST (after all content) so it draws on top. } else { @@ -360,7 +371,7 @@ pub(super) fn draw_frame( width: target.rect.width as u16, height: 1, }; - let vis = render_tab_bar(backend, frame, g_tab, target.bar, theme); + let vis = render_tab_bar(backend, g_tab, target.bar, theme); tab_visible_counts_out.push((target.group_id, vis)); } // Draw breadcrumb bars (below each group's tab bar). Hidden while the @@ -375,7 +386,7 @@ pub(super) fn draw_frame( width: t.rect.width as u16, height: 1, }; - let layout = draw_breadcrumb_bar(backend, frame, bc_rect, t.bar, theme); + let layout = draw_breadcrumb_bar(backend, bc_rect, t.bar, theme); *t.draw_layout.borrow_mut() = Some(layout); } // Draw divider lines (vertical only — horizontal splits use the tab bar as divider). @@ -423,7 +434,7 @@ pub(super) fn draw_frame( width: target.rect.width as u16, height: 1, }; - let vis = render_tab_bar(backend, frame, tab_rect, target.bar, theme); + let vis = render_tab_bar(backend, tab_rect, target.bar, theme); tab_visible_counts_out.push((target.group_id, vis)); } // Draw breadcrumb bar for the single group. Hidden while the terminal @@ -439,7 +450,7 @@ pub(super) fn draw_frame( width: t.rect.width as u16, height: 1, }; - let layout = draw_breadcrumb_bar(backend, frame, bc_rect, t.bar, theme); + let layout = draw_breadcrumb_bar(backend, bc_rect, t.bar, theme); *t.draw_layout.borrow_mut() = Some(layout); } render_all_windows(backend, frame, &screen.windows, theme); @@ -465,6 +476,7 @@ pub(super) fn draw_frame( if tab_drag_source.is_some() { render_tab_drag_overlay( frame, + backend, engine, editor_area, screen, @@ -531,12 +543,7 @@ pub(super) fn draw_frame( max_popup_height, |_| quadraui::CompletionItemMeasure::new(1.0), ); - super::quadraui_tui::draw_completions( - frame.buffer_mut(), - &completions, - &layout, - theme, - ); + backend.draw_completions(&completions, &layout); *completion_layout_out = Some(layout); } } @@ -566,7 +573,7 @@ pub(super) fn draw_frame( ); let (tooltip, layout) = render::hover_popup_to_quadraui_tooltip(hover, popup_x, popup_y, viewport); - super::quadraui_tui::draw_tooltip(frame.buffer_mut(), &tooltip, &layout, theme); + backend.draw_tooltip(&tooltip, &layout); } } @@ -588,7 +595,7 @@ pub(super) fn draw_frame( let popup_x = win_x + gutter_w + vis_col; let popup_y = win_y + anchor_view; let (eh_links, eh_rect, eh_sb) = - render_editor_hover_popup(frame, eh, popup_x, popup_y, frame.area(), theme); + render_editor_hover_popup(backend, eh, popup_x, popup_y, area, theme); *editor_hover_link_rects_out = eh_links; *editor_hover_popup_rect_out = eh_rect; *editor_hover_scrollbar_out = eh_sb; @@ -620,7 +627,7 @@ pub(super) fn draw_frame( ); let (tooltip, layout) = render::diff_peek_to_quadraui_tooltip(peek, popup_x, popup_y, viewport, theme); - super::quadraui_tui::draw_tooltip(frame.buffer_mut(), &tooltip, &layout, theme); + backend.draw_tooltip(&tooltip, &layout); } } @@ -648,27 +655,19 @@ pub(super) fn draw_frame( ); let (tooltip, layout) = render::signature_help_to_quadraui_tooltip(sig, popup_x, popup_y, viewport, theme); - super::quadraui_tui::draw_tooltip(frame.buffer_mut(), &tooltip, &layout, theme); + backend.draw_tooltip(&tooltip, &layout); } } // ── Quickfix panel (persistent bottom strip) ────────────────────────────── if let Some(ref qf) = screen.quickfix { - render_quickfix_panel( - frame, - quickfix_area, - qf, - quickfix_scroll_top, - theme, - backend, - ); + render_quickfix_panel(quickfix_area, qf, quickfix_scroll_top, theme, backend); } // ── Separated status line (above terminal, when status_line_above_terminal is active) ── if let Some(ref status) = screen.separated_status_line { render_window_status_line( backend, - frame, separated_status_area.x, separated_status_area.y, separated_status_area.width, @@ -703,7 +702,6 @@ pub(super) fn draw_frame( }; let hits = render_bottom_panel_tabs( backend, - frame, tab_bar_area, &engine.bottom_panel_kind, engine.terminal_open, @@ -720,7 +718,7 @@ pub(super) fn draw_frame( width: content_area.width, height: 1, }; - let hits = render_terminal_toolbar(backend, frame, toolbar_area, term, theme); + let hits = render_terminal_toolbar(backend, toolbar_area, term, theme); engine.terminal_toolbar_hits.replace(Some(hits)); let term_content = Rect { x: content_area.x, @@ -757,15 +755,8 @@ pub(super) fn draw_frame( content_area.width as f32, content_area.height as f32, ); - backend.set_current_theme(super::quadraui_tui::q_theme(theme)); - let td_layout = { - use quadraui::Backend; - backend.text_display_layout(q_rect, &td) - }; - backend.enter_frame_scope(frame, |b| { - use quadraui::Backend; - b.draw_text_display(q_rect, &td); - }); + let td_layout = backend.text_display_layout(q_rect, &td); + backend.draw_text_display(q_rect, &td); let scrollbar = td_layout .scrollbar_bounds @@ -817,10 +808,7 @@ pub(super) fn draw_frame( debug_toolbar_area.height as f32, ); *debug_toolbar_rect_out = q_rect; - backend.set_current_theme(super::quadraui_tui::q_theme(theme)); - backend.enter_frame_scope(frame, |b| { - render::draw_debug_toolbar(b, engine, q_rect); - }); + render::draw_debug_toolbar(backend, engine, q_rect); } // ── Wildmenu bar (command Tab completion) ───────────────────────────────── @@ -832,11 +820,7 @@ pub(super) fn draw_frame( wildmenu_area.width as f32, wildmenu_area.height as f32, ); - backend.set_current_theme(super::quadraui_tui::q_theme(theme)); - backend.enter_frame_scope(frame, |b| { - use quadraui::Backend; - b.draw_status_bar(q_rect, &bar, None, None); - }); + backend.draw_status_bar(q_rect, &bar, None, None); } // ── Status / command ────────────────────────────────────────────────────── @@ -847,11 +831,7 @@ pub(super) fn draw_frame( status_area.width as f32, status_area.height as f32, ); - backend.set_current_theme(super::quadraui_tui::q_theme(theme)); - backend.enter_frame_scope(frame, |b| { - use quadraui::Backend; - b.draw_status_bar(q_rect, bar, None, None); - }); + backend.draw_status_bar(q_rect, bar, None, None); } render_command_line(frame.buffer_mut(), cmd_area, &screen.command, theme); @@ -878,7 +858,7 @@ pub(super) fn draw_frame( let sep_x = sidebar_sep_area.x + sidebar_sep_area.width - 1; if sidebar.ext_panel_name.is_some() || engine.active_panel_is(PANEL_GIT) { let (rects, popup_rect) = render_panel_hover_popup( - frame, + backend, screen, theme, sep_x + 1, @@ -916,11 +896,7 @@ pub(super) fn draw_frame( popup_area.width as f32, popup_area.height as f32, ); - backend.set_current_theme(super::quadraui_tui::q_theme(theme)); - backend.enter_frame_scope(frame, |b| { - use quadraui::Backend; - b.draw_palette(q_rect, &palette); - }); + backend.draw_palette(q_rect, &palette); } // ── Find/replace overlay (top-right of active group) ─────────────────── @@ -934,12 +910,18 @@ pub(super) fn draw_frame( // absolute-input convention to lean on there); passing `0` here // keeps that internal translation a no-op instead of double- // counting the origin now baked into `group_bounds` itself. - super::quadraui_tui::draw_find_replace(frame.buffer_mut(), area, find_replace, theme); + let q_area = quadraui::Rect::new( + area.x as f32, + area.y as f32, + area.width as f32, + area.height as f32, + ); + backend.draw_find_replace(q_area, find_replace); } // ── Unified picker modal (above terminal/status so it's fully visible) ── if let Some(ref picker) = screen.picker { - render_picker_popup(frame, picker, area, theme, backend); + render_picker_popup(picker, area, theme, backend); } // ── Tab switcher popup ─────────────────────────────────────────────────── @@ -972,11 +954,7 @@ pub(super) fn draw_frame( popup_area.width as f32, popup_area.height as f32, ); - backend.set_current_theme(super::quadraui_tui::q_theme(theme)); - backend.enter_frame_scope(frame, |b| { - use quadraui::Backend; - b.draw_list(q_rect, &list); - }); + backend.draw_list(q_rect, &list); } } @@ -1002,7 +980,7 @@ pub(super) fn draw_frame( }; let (menu, layout) = render::context_menu_generic_layout(&inset_panel, inner_viewport, 1.0, 1.0, 1.0); - super::quadraui_tui::draw_context_menu(frame.buffer_mut(), &menu, &layout, theme); + let _ = backend.draw_context_menu(&menu, &layout); *context_menu_layout_out = Some(layout); } @@ -1015,7 +993,7 @@ pub(super) fn draw_frame( area.height as f32, ); let (q_dialog, layout) = render::dialog_generic_layout(dialog, viewport, 1.0, 1.0); - super::quadraui_tui::draw_dialog(frame.buffer_mut(), &q_dialog, &layout, theme); + let _ = backend.draw_dialog(&q_dialog, &layout); *dialog_layout_out = Some(layout); } else { *dialog_layout_out = None; @@ -1029,9 +1007,7 @@ pub(super) fn draw_frame( menu_bar_area.width as f32, menu_bar_area.height as f32, ); - backend.enter_frame_scope(frame, |b| { - engine.menu_system.borrow().render(b, bar_rect); - }); + engine.menu_system.borrow().render(backend, bar_rect); } // Toast overlay (#450) — drawn LAST so it sits on top of every other @@ -1040,10 +1016,14 @@ pub(super) fn draw_frame( // returned layout is cached on the engine so click handlers can run // hit_test → handle_toast_hit (× close, action buttons). if let Some(stack) = render::build_toast_stack(engine) { - let q_theme = super::quadraui_tui::q_theme(theme); let toast_area = frame.area(); - let layout = - quadraui::tui::draw_toast_stack(frame.buffer_mut(), toast_area, &stack, &q_theme); + let q_toast_area = quadraui::Rect::new( + toast_area.x as f32, + toast_area.y as f32, + toast_area.width as f32, + toast_area.height as f32, + ); + let layout = backend.draw_toast_stack(q_toast_area, &stack); engine.toast_layout.replace(Some(layout)); } else { engine.toast_layout.replace(None); @@ -1253,6 +1233,7 @@ fn build_tui_tab_slots( #[allow(clippy::too_many_arguments)] pub(super) fn render_tab_drag_overlay( frame: &mut ratatui::Frame, + backend: &mut super::backend::TuiBackend, engine: &Engine, editor_area: Rect, screen: &render::ScreenLayout, @@ -1296,13 +1277,14 @@ pub(super) fn render_tab_drag_overlay( }; { - let q_theme = super::quadraui_tui::q_theme(theme); + use quadraui::Backend; let q_overlay = quadraui::DropOverlay { highlight: overlay.highlight, insertion_bar: overlay.insertion_bar, ghost_position: Some(overlay.ghost_position), }; - quadraui::tui::draw_drop_overlay(frame.buffer_mut(), &q_overlay, &q_theme); + backend.set_current_theme(super::quadraui_tui::q_theme(theme)); + backend.draw_drop_overlay(&q_overlay); } // Look up the tab label from engine using the captured drag source. @@ -1396,11 +1378,11 @@ pub(super) fn compute_tui_tab_drop_zone( /// (built by `render::build_screen_layout`). pub(super) fn render_tab_bar( backend: &mut super::backend::TuiBackend, - frame: &mut ratatui::Frame, area: Rect, bar: &quadraui::TabBar, theme: &Theme, ) -> usize { + use quadraui::Backend; let q_rect = quadraui::Rect::new( area.x as f32, area.y as f32, @@ -1408,10 +1390,7 @@ pub(super) fn render_tab_bar( area.height as f32, ); backend.set_current_theme(super::quadraui_tui::q_theme(theme)); - let hits = backend.enter_frame_scope(frame, |b| { - use quadraui::Backend; - b.draw_tab_bar(q_rect, bar, None) - }); + let hits = backend.draw_tab_bar(q_rect, bar, None); hits.available_cols } @@ -1422,11 +1401,11 @@ pub(super) fn render_tab_bar( /// Returns the `StatusBarLayout` for click-time hit testing. pub(super) fn draw_breadcrumb_bar( backend: &mut super::backend::TuiBackend, - frame: &mut ratatui::Frame, area: Rect, bar: &quadraui::StatusBar, theme: &Theme, ) -> quadraui::StatusBarLayout { + use quadraui::Backend; let q_rect = quadraui::Rect::new( area.x as f32, area.y as f32, @@ -1434,10 +1413,7 @@ pub(super) fn draw_breadcrumb_bar( area.height as f32, ); backend.set_current_theme(super::quadraui_tui::q_theme(theme)); - backend.enter_frame_scope(frame, |b| { - use quadraui::Backend; - b.draw_status_bar(q_rect, bar, None, None) - }) + backend.draw_status_bar(q_rect, bar, None, None) } // ─── Editor windows ─────────────────────────────────────────────────────────── @@ -1464,12 +1440,12 @@ pub(super) fn render_all_windows( /// Render the unified picker popup. Supports single-pane (no preview) and /// two-pane (with preview) layouts, fuzzy match highlighting, and scrollbar. pub(super) fn render_picker_popup( - frame: &mut ratatui::Frame, picker: &render::PickerPanel, term_area: Rect, theme: &Theme, backend: &mut super::backend::TuiBackend, ) { + use quadraui::Backend; let has_preview = picker.preview.is_some(); let geo = render::PickerGeometry::compute( term_area.width as f32, @@ -1480,10 +1456,7 @@ pub(super) fn render_picker_popup( let palette = render::picker_panel_to_palette(picker); let q_rect = quadraui::Rect::new(geo.popup_x, geo.popup_y, geo.popup_w, geo.popup_h); backend.set_current_theme(super::quadraui_tui::q_theme(theme)); - backend.enter_frame_scope(frame, |b| { - use quadraui::Backend; - b.draw_palette(q_rect, &palette); - }); + backend.draw_palette(q_rect, &palette); } /// Render one editor window (pane) into `frame`. @@ -1521,23 +1494,24 @@ pub(super) fn render_window( }; let editor = render::to_q_editor(window); - let q_theme = super::quadraui_tui::q_theme(theme); - let result = quadraui::tui::draw_editor(frame.buffer_mut(), editor_area, &editor, &q_theme); + let editor_q_rect = quadraui::Rect::new( + editor_area.x as f32, + editor_area.y as f32, + editor_area.width as f32, + editor_area.height as f32, + ); + backend.set_current_theme(super::quadraui_tui::q_theme(theme)); + let result = { + use quadraui::Backend; + backend.draw_editor(editor_q_rect, &editor) + }; if let Some(pos) = result.cursor_position { frame.set_cursor_position(pos); } if let (Some(status), Some(sy)) = (&window.status_line, status_bar_row) { - render_window_status_line( - backend, - frame, - editor_area.x, - sy, - editor_area.width, - status, - theme, - ); + render_window_status_line(backend, editor_area.x, sy, editor_area.width, status, theme); } } @@ -1555,23 +1529,20 @@ pub(super) fn render_window( /// click handler runs the layout on demand against current bar width. fn render_window_status_line( backend: &mut super::backend::TuiBackend, - frame: &mut ratatui::Frame, x: u16, y: u16, width: u16, status: &crate::render::WindowStatusLine, theme: &crate::render::Theme, ) { + use quadraui::Backend; let bar = crate::render::window_status_line_to_status_bar( status, quadraui::WidgetId::new("status:window"), ); let q_rect = quadraui::Rect::new(x as f32, y as f32, width as f32, 1.0); backend.set_current_theme(super::quadraui_tui::q_theme(theme)); - backend.enter_frame_scope(frame, |b| { - use quadraui::Backend; - let _ = b.draw_status_bar(q_rect, &bar, None, None); - }); + let _ = backend.draw_status_bar(q_rect, &bar, None, None); } /// Convert a character-index column to a visual column, expanding tabs. @@ -1755,32 +1726,38 @@ mod tests { terminal .draw(|frame| { - draw_frame( - frame, - &screen, - &theme, - &mut sidebar, - engine, - sidebar_width, - 0, // quickfix_scroll_top - None, // folder_picker - None, // cmd_sel - None, // explorer_drop_target - &mut hover_link_rects, - &mut hover_popup_rect, - &mut editor_hover_popup_rect, - &mut editor_hover_link_rects, - &mut editor_hover_scrollbar, - &mut tab_visible_counts, - &mut dbg_toolbar_rect, - &mut completion_layout, - &mut context_menu_layout, - &mut dialog_layout, - &mut backend, - None, // tab_drag_source - None, // tab_drag_cursor - &crate::core::window::DropZone::None, // tab_drop_zone - ); + // #600: `draw_frame` calls `Backend::draw_*` trait methods + // directly (no per-call `enter_frame_scope`), so this + // harness opens the one frame-scope entry itself — mirrors + // `event_loop`'s two `terminal.draw` closures in `mod.rs`. + super::with_frame_scope(&mut backend, frame, |backend, frame| { + draw_frame( + frame, + &screen, + &theme, + &mut sidebar, + engine, + sidebar_width, + 0, // quickfix_scroll_top + None, // folder_picker + None, // cmd_sel + None, // explorer_drop_target + &mut hover_link_rects, + &mut hover_popup_rect, + &mut editor_hover_popup_rect, + &mut editor_hover_link_rects, + &mut editor_hover_scrollbar, + &mut tab_visible_counts, + &mut dbg_toolbar_rect, + &mut completion_layout, + &mut context_menu_layout, + &mut dialog_layout, + backend, + None, // tab_drag_source + None, // tab_drag_cursor + &crate::core::window::DropZone::None, // tab_drop_zone + ); + }); }) .unwrap(); @@ -1962,32 +1939,34 @@ mod tests { let mut backend2 = super::backend::TuiBackend::new(); terminal .draw(|frame| { - draw_frame( - frame, - &screen, - &theme, - &mut sidebar, - engine, - 0, - 0, - None, - None, - None, - &mut hlr, - &mut hpr, - &mut ehpr, - &mut ehlr, - &mut ehs, - &mut tvc, - &mut dtr, - &mut cl, - &mut cml, - &mut dl, - &mut backend2, - None, - None, - &crate::core::window::DropZone::None, - ); + super::with_frame_scope(&mut backend2, frame, |backend2, frame| { + draw_frame( + frame, + &screen, + &theme, + &mut sidebar, + engine, + 0, + 0, + None, + None, + None, + &mut hlr, + &mut hpr, + &mut ehpr, + &mut ehlr, + &mut ehs, + &mut tvc, + &mut dtr, + &mut cl, + &mut cml, + &mut dl, + backend2, + None, + None, + &crate::core::window::DropZone::None, + ); + }); }) .unwrap(); terminal.backend().buffer().clone()