diff --git a/src/screenshot.rs b/src/screenshot.rs index 5cfd9f7..95a79cf 100644 --- a/src/screenshot.rs +++ b/src/screenshot.rs @@ -7,6 +7,19 @@ //! see `ui::tabs`. The window is a plain top-level (not layer-shell), so a //! full known-size canvas capture is enough, same reasoning as breadpad's //! popup view. +//! +//! One more view isn't a tab: "troubleshoot-wizard" opens the modal wizard +//! window (`ui::troubleshoot_wizard`, normally reachable via Home's +//! "Something's wrong?" button) directly. +//! +//! Deliberately NOT covered here: the onboarding tour (`ui::tour`). It's a +//! live, multi-step walkthrough that overlays callouts on the *real* +//! desktop as the user does real things (opens real apps, switches real +//! workspaces) — there's no meaningful "screenshot" of a single step in an +//! isolated environment with no real desktop for it to point at. +//! `ui::guide_view` and `ui::keybind_viewer` also aren't separate views: +//! both render embedded *inside* the Learn/Home tabs respectively, so +//! they're already covered by those tabs' own captures. use gtk4::prelude::*; use std::path::PathBuf; @@ -17,7 +30,12 @@ use std::time::Duration; /// anything has been drawn into it. const SETTLE_DELAY: Duration = Duration::from_millis(300); -const KNOWN_VIEWS: &[&str] = &["home", "learn", "ask"]; +/// Delay before opening the wizard modal — same reasoning as every other +/// app's PRE_POPUP_DELAY: the parent window's own layout needs a beat to +/// settle first. +const PRE_POPUP_DELAY: Duration = Duration::from_millis(300); + +const KNOWN_VIEWS: &[&str] = &["home", "learn", "ask", "troubleshoot-wizard"]; #[derive(Clone)] pub struct ScreenshotRequest { @@ -39,20 +57,41 @@ pub fn dispatch(window: >k4::ApplicationWindow, stack: >k4::Stack, req: Scre ); std::process::exit(1); } - stack.set_visible_child_name(&req.view); - let output = req.output; let (width, height) = (req.width as i32, req.height as i32); + + if req.view == "troubleshoot-wizard" { + window.connect_map(move |win| { + let output = output.clone(); + let win = win.clone(); + gtk4::glib::timeout_add_local_once(PRE_POPUP_DELAY, move || { + crate::ui::troubleshoot_wizard::open(&win, move |dialog| { + let output = output.clone(); + dialog.connect_map(move |_| { + let output = output.clone(); + gtk4::glib::timeout_add_local_once(SETTLE_DELAY, move || { + finish(bread_screenshots::capture_region(0, 0, width, height, &output)); + }); + }); + }); + }); + }); + // The caller (`ui::window::present`) returns immediately after this + // for the screenshot path, skipping its own normal `window.present()` + // call — trigger it here instead, so `connect_map` above actually has + // something to fire for. + window.present(); + return; + } + + stack.set_visible_child_name(&req.view); + window.connect_map(move |_| { let output = output.clone(); gtk4::glib::timeout_add_local_once(SETTLE_DELAY, move || { finish(bread_screenshots::capture_region(0, 0, width, height, &output)); }); }); - // The caller (`ui::window::present`) returns immediately after this for - // the screenshot path, skipping its own normal `window.present()` call - // — trigger it here instead, so `connect_map` above actually has - // something to fire for. window.present(); } diff --git a/src/ui/home.rs b/src/ui/home.rs index f2c1152..74ac629 100644 --- a/src/ui/home.rs +++ b/src/ui/home.rs @@ -83,7 +83,7 @@ pub fn build( troubleshoot_btn.set_halign(Align::Start); { let parent = parent_window.clone(); - troubleshoot_btn.connect_clicked(move |_| troubleshoot_wizard::open(&parent)); + troubleshoot_btn.connect_clicked(move |_| troubleshoot_wizard::open(&parent, |_| {})); } vbox.append(&troubleshoot_btn); diff --git a/src/ui/troubleshoot_wizard.rs b/src/ui/troubleshoot_wizard.rs index 465c5ec..e152678 100644 --- a/src/ui/troubleshoot_wizard.rs +++ b/src/ui/troubleshoot_wizard.rs @@ -25,7 +25,11 @@ struct Wizard { trees: HashMap>, } -pub fn open(parent: &impl IsA) { +/// `on_build` runs on the freshly built window *before* it's presented — +/// screenshot mode's only hook point, since `connect_map` registered any +/// later would miss a map that already happened. The real call site +/// (`ui::home`) passes a no-op. +pub fn open(parent: &impl IsA, on_build: impl FnOnce(&Window)) { let window = Window::builder() .transient_for(parent) .modal(true) @@ -41,6 +45,7 @@ pub fn open(parent: &impl IsA) { wizard.render_picker(); window.set_child(Some(&content)); + on_build(&window); window.present(); }