breadpad/breadman/src/screenshot.rs
Breadway c929517b49
Some checks failed
check / check (push) Failing after 1s
dev release / build (push) Failing after 1s
Adopt bread_utils::screenshot_cli for --screenshot flags
Use the crate for pair validation, settle delay, and canvas defaults
in breadpad and breadman. breadman now depends on bread-utils v0.7.2
for screenshot_cli; existing hand-rolled argv parsing stays.
2026-08-16 00:26:07 +08:00

132 lines
5.4 KiB
Rust

//! `--screenshot` CLI mode: render the named view, capture it via
//! `bread-screenshots`, then exit — driven by `bread-ecosystem`'s
//! `bread-capture` orchestrator, or run standalone for one-off captures.
//!
//! No clap here, same reasoning as breadpad: extends breadman's own
//! hand-rolled `mod args` instead of bolting on a second parser that would
//! reject its real flags (`--view`, `done`, `upcoming --plain`).
//!
//! `--screenshot <view>` doubles as the view selector for every named stack
//! page ("all", "upcoming", "todo", ...) — it's passed through as
//! `initial_view` (the same field `--view` already sets) rather than
//! needing a separate mechanism, since breadman already supports opening
//! directly to a named stack page.
//!
//! One view isn't a stack page at all: "editor" opens the per-note editor
//! dialog (`editor::open_editor`), normally only reachable by clicking a
//! real note row's edit button. Screenshot mode calls the same builder
//! function directly against the first real note in the store (bypassing
//! the button/click-handler entirely), with no-op save/delete/error
//! callbacks since nothing here should actually persist a change.
use bread_utils::screenshot_cli::SETTLE_DELAY;
use gtk4::prelude::*;
use libadwaita::prelude::*;
use std::path::PathBuf;
use std::rc::Rc;
use std::sync::Arc;
use std::time::Duration;
/// Delay before popping the editor popover open — 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 = SETTLE_DELAY;
#[derive(Clone)]
pub struct ScreenshotRequest {
pub view: String,
pub output: PathBuf,
pub width: u32,
pub height: u32,
}
/// Wire up the given view's screenshot sequence against an already-built,
/// not-yet-presented window. Every path here ends by exiting the process —
/// it never returns control to the normal note-manager UI.
///
/// Unlike the other apps' `dispatch`, this doesn't validate `req.view`
/// against a known-views list for the stack-page case — an invalid name
/// just falls through to breadman's own `unwrap_or("all")` default (see
/// `build_app_window`), same as `--view` already behaves for a normal run.
pub fn dispatch(
window: &gtk4::ApplicationWindow,
req: ScreenshotRequest,
state: crate::AppState,
) {
let output = req.output;
let (width, height) = (req.width as i32, req.height as i32);
if req.view == "new-note" {
window.connect_map(move |root| {
let output = output.clone();
let root = root.clone();
let state = state.clone();
gtk4::glib::timeout_add_local_once(PRE_POPUP_DELAY, move || {
crate::show_add_note_window(&root, state, breadpad_shared::types::NoteType::Note, 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));
});
});
});
});
});
return;
}
if req.view == "editor" {
window.connect_map(move |root| {
let output = output.clone();
let state = state.clone();
let root = root.clone();
gtk4::glib::timeout_add_local_once(PRE_POPUP_DELAY, move || {
let Some(note) = state.notes.borrow().first().cloned() else {
eprintln!("breadman: no notes in the store to build the editor view from");
std::process::exit(1);
};
let morning = state.cfg.borrow().reminders.default_morning.clone();
let store = Arc::new(state.write_store());
// AdwDialog handles its own presentation/centering - no more
// manual popover anchor/position/autohide juggling. Must
// connect `map` BEFORE presenting, or the signal (which can
// fire synchronously inside `present`) is missed entirely.
let dialog = crate::editor::open_editor(
&note,
store,
morning,
Rc::new(|_| {}),
Rc::new(|| {}),
Rc::new(|_| {}),
);
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));
});
});
dialog.present(Some(root.upcast_ref::<gtk4::Widget>()));
});
});
return;
}
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));
});
});
}
fn finish(result: anyhow::Result<()>) {
match result {
Ok(()) => std::process::exit(0),
Err(e) => {
eprintln!("breadman: screenshot capture failed: {e}");
std::process::exit(1);
}
}
}