tawasuyu

tawasuyu/brahman

Rust 332TOML 127JSON 17Markdown 154710 Definitions

⎇ 8fdef818cc0524aa4bac39801cefd5960113dcdf crates/modules/ui_engine/widgets/theme-switcher/src/lib.rs Rust · 91 lines · 3.2 KB History Who touched what raw

feat(yahweh-widget-theme-switcher): control para ciclar themes en runtime 8fdef818c · Sergio ·

1//! `yahweh-widget-theme-switcher` — botón clickable para ciclar
2//! entre los presets de `Theme`.
3//!
4//! El botón muestra el nombre del theme actual; al click avanza al
5//! siguiente preset según [`Theme::next_after`] (rotación circular
6//! sobre [`Theme::all`]).
7//!
8//! El cambio se aplica con `Theme::set(cx, ...)` que invalida el
9//! global y dispara redraws en todos los widgets que observan el
10//! theme via `cx.observe_global::<Theme>()`. Para widgets que NO
11//! observan el theme (ej. los themed wrappers de banner/card en su
12//! versión actual, que leen el theme dentro de `render`), basta con
13//! que el render se vuelva a invocar — esto sucede automáticamente
14//! tras `cx.set_global` que marca todos los views como dirty.
15//!
16//! # Uso
17//!
18//! ```ignore
19//! use yahweh_widget_theme_switcher::theme_switcher;
20//!
21//! // Adentro de Render::render:
22//! let switcher = theme_switcher(cx);
23//! header.child(switcher)
24//! ```
25
26#![forbid(unsafe_code)]
27
28use gpui::{div, prelude::*, px, App, ClickEvent, IntoElement, SharedString, Window};
29use yahweh_theme::Theme;
30
31/// Construye el switcher: una `Div` clickable con el nombre del
32/// theme actual + flecha indicadora. Al click rota al siguiente
33/// preset.
34///
35/// Estilo: padding consistente con el resto de los chrome controls
36/// del repo (`px(8/4)`), `bg(theme.bg_panel_alt)`, `text_color(fg_text)`.
37/// Sin border, hover sutil con `bg_row_hover`.
38///
39/// El handler del click usa `cx.update_global::<Theme>` para
40/// reemplazar el theme global; los widgets que leen
41/// `Theme::global` en su próximo render verán el nuevo.
42pub fn theme_switcher(cx: &mut App) -> impl IntoElement {
43    let theme = Theme::global(cx).clone();
44    let label = format!("Tema: {} ▸", theme.name);
45
46    div()
47        .id("yahweh-theme-switcher")
48        .px(px(8.))
49        .py(px(4.))
50        .bg(theme.bg_panel_alt.clone())
51        .text_color(theme.fg_text)
52        .text_size(px(11.))
53        .rounded(px(3.))
54        .hover(move |d| d.bg(theme.bg_row_hover))
55        .child(SharedString::from(label))
56        .on_click(|_event: &ClickEvent, _window: &mut Window, cx: &mut App| {
57            let current_name = Theme::global(cx).name;
58            let next = Theme::next_after(current_name);
59            Theme::set(cx, next);
60        })
61}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66    use gpui::TestAppContext;
67
68    #[gpui::test]
69    fn switcher_constructs_with_theme_installed(cx: &mut TestAppContext) {
70        cx.update(|cx| {
71            Theme::install_default(cx);
72            let _div = theme_switcher(cx);
73            // Smoke: si llegamos aquí sin panic, el constructor lee
74            // el global, deriva colors, y construye un Div.
75        });
76    }
77
78    #[gpui::test]
79    fn theme_set_changes_global(cx: &mut TestAppContext) {
80        cx.update(|cx| {
81            Theme::install_default(cx);
82            let initial_name = Theme::global(cx).name;
83            // Ciclo manual sin pasar por el handler del click.
84            let next = Theme::next_after(initial_name);
85            Theme::set(cx, next.clone());
86            let after = Theme::global(cx).name;
87            assert_eq!(after, next.name);
88            assert_ne!(after, initial_name, "el ciclo debe cambiar el name");
89        });
90    }
91}