rosace_widgets/tree/
app.rs1use rosace_core::types::{Point, Rect, Size};
2use rosace_layout::Constraints;
3use rosace_render::{Color, FontCache, PictureRecorder, SkiaCanvas};
4use rosace_theme::{ThemeData, built_in};
5use super::{Widget, LayoutCtx, PaintCtx};
6
7pub struct WidgetApp {
25 pub width: u32,
26 pub height: u32,
27 background: Option<Color>,
29 pub font: FontCache,
30 pub theme: ThemeData,
31}
32
33impl WidgetApp {
34 pub fn new(width: u32, height: u32) -> Self {
36 let font = FontCache::system_ui()
37 .or_else(FontCache::system_mono)
38 .expect("no system font found");
39 Self {
40 width,
41 height,
42 background: None,
43 font,
44 theme: built_in::dark_theme(),
45 }
46 }
47
48 pub fn background(mut self, c: Color) -> Self { self.background = Some(c); self }
50
51 pub fn with_font(mut self, f: FontCache) -> Self { self.font = f; self }
53
54 pub fn theme(mut self, t: ThemeData) -> Self { self.theme = t; self }
56
57 pub fn dark(mut self) -> Self { self.theme = built_in::dark_theme(); self }
59
60 pub fn light(mut self) -> Self { self.theme = built_in::light_theme(); self }
62
63 fn resolve_bg(&self) -> Color {
64 self.background.unwrap_or_else(|| {
65 let c = self.theme.colors.background;
66 Color::rgba(
67 (c.r * 255.0) as u8,
68 (c.g * 255.0) as u8,
69 (c.b * 255.0) as u8,
70 (c.a * 255.0) as u8,
71 )
72 })
73 }
74
75 pub fn render(&self, root: &dyn Widget) -> SkiaCanvas {
77 rosace_theme::set_theme(self.theme.clone());
78
79 let mut canvas = SkiaCanvas::new(self.width, self.height);
80 canvas.clear(self.resolve_bg());
81
82 let constraints = Constraints::tight(self.width as f32, self.height as f32);
83 let lctx = LayoutCtx::new(constraints, &self.font, &self.theme);
84 let size = root.layout(&lctx);
85
86 let mut recorder = PictureRecorder::new();
87 let mut ctx = PaintCtx::root(
88 &mut recorder,
89 Rect {
90 origin: Point { x: 0.0, y: 0.0 },
91 size: Size {
92 width: size.width.min(self.width as f32),
93 height: size.height.min(self.height as f32),
94 },
95 },
96 &self.font,
97 self.theme.clone(),
98 std::rc::Rc::new(std::cell::RefCell::new(super::RenderTree::new())),
99 );
100
101 root.paint(&mut ctx);
102 let picture = recorder.finish();
103 canvas.play_picture(&picture, &self.font);
104 canvas
105 }
106
107 pub fn render_into(&self, canvas: &mut SkiaCanvas, root: &dyn Widget) {
109 rosace_theme::set_theme(self.theme.clone());
110 canvas.clear(self.resolve_bg());
111
112 let w = canvas.width() as f32;
113 let h = canvas.height() as f32;
114 let constraints = Constraints::tight(w, h);
115 let lctx = LayoutCtx::new(constraints, &self.font, &self.theme);
116 let size = root.layout(&lctx);
117
118 let mut recorder = PictureRecorder::new();
119 let mut ctx = PaintCtx::root(
120 &mut recorder,
121 Rect {
122 origin: Point { x: 0.0, y: 0.0 },
123 size: Size {
124 width: size.width.min(w),
125 height: size.height.min(h),
126 },
127 },
128 &self.font,
129 self.theme.clone(),
130 std::rc::Rc::new(std::cell::RefCell::new(super::RenderTree::new())),
131 );
132 root.paint(&mut ctx);
133 let picture = recorder.finish();
134 canvas.play_picture(&picture, &self.font);
135 }
136
137 pub fn render_png(&self, root: &dyn Widget) -> Vec<u8> {
139 self.render(root)
140 .encode_png()
141 .expect("png encode failed")
142 }
143}