use telar::{
Color, Component, DrawCommand, EventResult, LayoutItem, LayoutStyle, LocalTree, RectStyle,
Rectangle, RenderNode, ShapeStyle, ThemeTokens, UiTree, box_item, reset_layout_runtime,
set_system_dark, use_theme_tokens,
};
struct Root(Box<dyn LayoutItem>);
impl Component for Root {
fn view(&self) -> RenderNode {
self.0.view()
}
fn on_event(&mut self, _: &platform_core::Event) -> EventResult {
EventResult::Ignored
}
fn debug_name(&self) -> &'static str {
"ThemeRoot"
}
}
#[derive(Clone)]
struct Day;
impl ThemeTokens for Day {
fn surface_alt(&self) -> Color {
Color::rgba(1.0, 0.0, 0.0, 1.0)
}
}
#[derive(Clone)]
struct Night;
impl ThemeTokens for Night {
fn surface_alt(&self) -> Color {
Color::rgba(0.0, 0.0, 1.0, 1.0)
}
}
fn drawn_fill(tree: &LocalTree) -> Color {
tree.frame()
.iter()
.find_map(|c| match c {
DrawCommand::Rect { style, .. } => style.fill.as_ref().map(|p| p.solid_color()),
_ => None,
})
.expect("the tree drew its box")
}
fn a_themed_box() -> LocalTree {
let boxed = Rectangle::new(LayoutStyle::new().width(100.0).height(40.0), || {
RectStyle::default().with_fill(use_theme_tokens().surface_alt())
})
.unwrap();
LocalTree::new(Box::new(Root(box_item(boxed))))
}
fn following_the_system() {
telar::register_mode("day", || telar::set_theme(Day));
telar::register_mode("night", || telar::set_theme(Night));
telar::follow_system("day", "night");
}
#[test]
fn a_theme_change_repaints_a_rasterised_tree() {
reset_layout_runtime();
set_system_dark(false);
following_the_system();
let tree = a_themed_box();
assert_eq!(
drawn_fill(&tree),
Day.surface_alt(),
"it opens in the light one"
);
set_system_dark(true);
telar::relayout_if_dirty();
assert_eq!(
drawn_fill(&tree),
Night.surface_alt(),
"and follows the system"
);
set_system_dark(false);
}
#[test]
fn a_theme_change_repaints_a_tree_of_elements() {
reset_layout_runtime();
let was = ui_tree::set_element_capture(true);
set_system_dark(false);
following_the_system();
let tree = a_themed_box();
assert_eq!(
drawn_fill(&tree),
Day.surface_alt(),
"it opens in the light one"
);
set_system_dark(true);
telar::relayout_if_dirty();
let after = drawn_fill(&tree);
ui_tree::set_element_capture(was);
set_system_dark(false);
assert_eq!(after, Night.surface_alt(), "and follows the system");
}
#[test]
fn a_tree_mounted_before_the_flush_still_takes_the_theme_the_flush_installs() {
reset_layout_runtime();
let was = ui_tree::set_element_capture(true);
set_system_dark(false);
following_the_system();
telar::begin_batch();
set_system_dark(true);
let tree = a_themed_box();
let during = drawn_fill(&tree);
telar::end_batch();
telar::relayout_if_dirty();
let after = drawn_fill(&tree);
ui_tree::set_element_capture(was);
set_system_dark(false);
assert_eq!(
during,
Day.surface_alt(),
"nothing flushed yet, so the tree is built in the theme that was in force"
);
assert_eq!(
after,
Night.surface_alt(),
"and the flush that installs the real one repaints it"
);
}
#[test]
fn an_application_hosting_other_trees_is_told_the_scheme_changed() {
use std::cell::Cell;
use std::rc::Rc;
use telar::{App, AppRuntime, LocalApp};
struct Host(Rc<Cell<Option<bool>>>);
impl App for Host {
fn root(&self) -> Box<dyn Component> {
Box::new(Root(box_item(
Rectangle::new(LayoutStyle::new().width(1.0).height(1.0), || {
RectStyle::default().with_fill(use_theme_tokens().surface_alt())
})
.unwrap(),
)))
}
fn on_color_scheme(&self, dark: bool) {
self.0.set(Some(dark));
}
}
reset_layout_runtime();
set_system_dark(false);
following_the_system();
let told = Rc::new(Cell::new(None));
let app = LocalApp(Host(Rc::clone(&told)));
app.set_system_dark(true);
let repainted = drawn_fill(&a_themed_box());
set_system_dark(false);
assert_eq!(
told.get(),
Some(true),
"the application was never told, so a host could not fan the change out to its plugins"
);
assert_eq!(
repainted,
Night.surface_alt(),
"and the default's own work still ran, so the host's tree follows too"
);
}