capsule: keyboard open, query modes, sections, and search-state geometry (phase 6c)
Completes the spotlight capsule (theme 04), THEME_SYSTEM_PLAN.md §7: - Keyboard open (item A): a new launcher_command module subscribes to bread.command.box.open — only when the active theme's launcher is Embedded — and focuses launcher_entry on receipt, which the existing EventControllerFocus::connect_enter already turns into an open_fn() call, same path a mouse click into the entry takes. breadbox's own keybind-triggered launch now redirects to this same event under an embedded theme instead of stacking its overlay window on top (see the breadbox commit). - Click-away scrim (item B): open_fn/close_fn now show/hide panel.rs's dismiss surface via the new show_capsule_dismiss/hide_dismiss, and panels.set_on_dismiss(close_fn) wires a click-away into the capsule's own close path. The scrim's clickable region starts at a fixed offset (capsule row height + the drawer's own maximum content height, DRAWER_MAX_HEIGHT_PX) rather than the drawer's live height, so it can never geometrically overlap a rendered result row regardless of breadbar-dismiss's overlay layer always rendering above the bar's own. - Query modes (item C): connect_changed now runs bread_launcher::parse_query against the entry text and, when the parsed prefix is listed in [launcher].modes, swaps launcher_results.scroller for a new mode_list populated by populate_mode_list (calc result / filtered commands / an "open this URL" prompt). key_ctrl routes Up/Down/Return at whichever list is active; a `>`/`.` row's action runs through the new ModeAction enum (RunShell for a command's own fixed exec string, OpenUrl straight to xdg-open) so arbitrary typed text is never passed through a shell. - Sections (item D): ResultsList::new's new `sections` bool is threaded from [launcher].sections. - Search-state geometry (item E): root's own width now spring-animates between [launcher].width and .search_width (animate_capsule_width, same set_size_request-driven technique animate_drawer_height already uses), and a `.searching` class on root switches border-radius to .search_radius via a new CSS rule/transition in theme.rs. Also fixes a real bug this surfaced: drawer_target_height was measuring drawer_box itself, whose size_request is continuously overwritten by animate_drawer_height's own tick callback — GTK clamps a widget's measure() up to at least its own explicit size request, so switching from the (tall) app list to a one-row calc result measured the PREVIOUS frame's forced height instead of the new content's actual size. It now sums each visible child's own natural height directly, which also fixes the same latent issue for narrowing app-search results. screenshot.rs gains two new views (capsule-sections, capsule-calc) to exercise D and C's calc path.
This commit is contained in:
parent
c68ed0d7e9
commit
e52c507f32
4 changed files with 490 additions and 5 deletions
39
src/launcher_command.rs
Normal file
39
src/launcher_command.rs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
//! Subscribes to `bread.command.box.open` and focuses/opens the capsule —
|
||||
//! only under `[launcher] mode = "embedded"` (spotlight, THEME_SYSTEM_PLAN.md
|
||||
//! §7 phase 6c). See `breadbox/EVENTS.md` for the command's existing
|
||||
//! contract: it's honored today only while `breadbox listen` is running, and
|
||||
//! is a silent no-op at the bus with no subscriber. breadbar becomes a
|
||||
//! SECOND subscriber of the exact same verb here — under an embedded theme,
|
||||
//! breadbox's own `main` (see its doc comment on `dispatch_embedded_open`)
|
||||
//! redirects a direct launch to this same event instead of mapping its own
|
||||
//! overlay window, specifically so this module can pick it up. `breadbox
|
||||
//! listen`'s own handling of the same event is separately made a no-op
|
||||
//! under an embedded theme (see its `handle_open`) — there is exactly one
|
||||
//! real handler for this event at a time, whichever theme is active.
|
||||
//!
|
||||
//! Same connection pattern as `widgets::client` (this crate's other
|
||||
//! `BreadClient::subscribe` user): a fire-and-forget connect, a background
|
||||
//! subscription thread with its own reconnect/backoff, and the handle is
|
||||
//! leaked rather than threaded through `App` — there's no natural point to
|
||||
//! stop it before the process exits.
|
||||
|
||||
use crate::{App, AppInput};
|
||||
use bread_theme::shell::LauncherMode;
|
||||
use bread_utils::bread_client::BreadClient;
|
||||
use relm4::ComponentSender;
|
||||
|
||||
/// Starts the subscription iff the active shell theme's launcher is
|
||||
/// `Embedded`. A no-op call under every other theme — never connects to
|
||||
/// breadd at all, matching the "effectively a no-op under every other
|
||||
/// theme" pattern the rest of the capsule wiring already follows (main.rs's
|
||||
/// `open_fn`/`close_fn` doc comment).
|
||||
pub fn spawn(sender: ComponentSender<App>) {
|
||||
if crate::theme::shell_theme().launcher().mode != LauncherMode::Embedded {
|
||||
return;
|
||||
}
|
||||
let client = BreadClient::connect(crate::widgets::client::APP_ID);
|
||||
let subscription = client.subscribe("bread.command.box.open", move |_event| {
|
||||
sender.input(AppInput::OpenLauncher);
|
||||
});
|
||||
std::mem::forget(subscription);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue