1use std::cell::Ref;
4use std::path::Path;
5
6use cosmic_text::{Buffer, LayoutRun};
7use taffy::TraversePartialTree;
8
9use crate::geometry::{Affine, Point, Rect, Size};
10use crate::screencopy::CaptureFrame;
11use crate::style::{Color, Style};
12use crate::text::TextNode;
13use crate::tree::{Node, NodeId, NodeKind, Tree, centered, icon_rect};
14use crate::widgets::{Fit, IconSource};
15
16const DEFAULT_TEXT_COLOR: Color = Color::rgb(0, 0, 0);
18
19pub struct WindowView<'w> {
22 tree: Ref<'w, Tree>,
23}
24
25impl<'w> WindowView<'w> {
26 pub(crate) fn new(tree: Ref<'w, Tree>) -> WindowView<'w> {
27 WindowView { tree }
28 }
29
30 pub fn size(&self) -> Size {
32 self.tree.size
33 }
34
35 pub fn root(&self) -> NodeView<'_> {
37 NodeView::new(&self.tree, self.tree.root)
38 .expect("invariant: the root node exists while the tree does")
39 }
40}
41
42#[derive(Clone, Copy)]
45pub struct NodeView<'a> {
46 tree: &'a Tree,
47 id: NodeId,
48 node: &'a Node,
49}
50
51impl<'a> NodeView<'a> {
52 fn new(tree: &'a Tree, id: NodeId) -> Option<NodeView<'a>> {
53 let node = tree.node(id)?;
54 Some(NodeView { tree, id, node })
55 }
56
57 pub fn name(&self) -> &'static str {
59 self.node.name
60 }
61
62 pub fn path(&self) -> &'a str {
64 &self.node.path
65 }
66
67 pub fn rect(&self) -> Rect {
70 self.node.rect.unwrap_or_default()
71 }
72
73 pub fn bounds(&self) -> Rect {
79 self.node.bounds
80 }
81
82 pub fn inner_bounds(&self) -> Rect {
86 self.node.inner_bounds
87 }
88
89 pub fn transform(&self) -> Option<Affine> {
94 self.node.transform
95 }
96
97 pub fn style(&self) -> Style {
101 self.node.shown_style()
102 }
103
104 pub fn children(&self) -> impl Iterator<Item = NodeView<'a>> + 'a {
106 let tree = self.tree;
107 tree
108 .taffy
109 .child_ids(self.id)
110 .filter_map(move |id| NodeView::new(tree, id))
111 }
112
113 pub fn clips(&self) -> bool {
116 self.node.clips()
117 }
118
119 pub fn content(&self) -> Content<'a> {
121 match &self.node.kind {
122 NodeKind::Text(text) => Content::Text(TextView {
123 text,
124 color: self.inherited_color(),
125 }),
126 NodeKind::Icon(icon) => match &icon.source {
127 Some(source) => Content::Icon(IconView {
128 source,
129 rect: icon_rect(self.node.content, icon.size),
130 }),
131 None => Content::None,
132 },
133 NodeKind::Input(input) => {
134 let color = self.inherited_color();
135 let color = if input.showing_placeholder() {
136 Color::rgba(color.r, color.g, color.b, color.a / 2)
137 } else {
138 color
139 };
140 Content::Input(InputView {
141 text: TextView {
142 text: &input.text,
143 color,
144 },
145 caret: (input.focused
147 && !matches!(&input.preedit, Some(preedit) if preedit.cursor.is_none()))
148 .then_some(input.caret),
149 selection: if input.focused { &input.selection } else { &[] },
150 area: input.area,
151 underline: &input.underline,
152 })
153 }
154 NodeKind::Image(image) => match &image.source {
155 Some(path) => Content::Image(ImageView {
156 path,
157 rect: centered(self.node.content, image.width, image.height),
158 fit: image.fit,
159 }),
160 None => Content::None,
161 },
162 NodeKind::Capture(capture) => match &capture.frame {
163 Some(frame) => Content::Capture(CaptureView {
164 id: capture.id,
165 frame,
166 rect: self.node.content,
167 fit: capture.fit,
168 }),
169 None => Content::None,
170 },
171 _ => Content::None,
172 }
173 }
174
175 fn inherited_color(&self) -> Color {
178 let mut id = Some(self.id);
179 while let Some(current) = id {
180 if let Some(color) = self
181 .tree
182 .node(current)
183 .and_then(|node| node.shown_style().color)
184 {
185 return color;
186 }
187 id = self.tree.taffy.parent(current);
188 }
189 DEFAULT_TEXT_COLOR
190 }
191}
192
193pub enum Content<'a> {
195 None,
197 Text(TextView<'a>),
199 Icon(IconView<'a>),
201 Image(ImageView<'a>),
203 Capture(CaptureView<'a>),
205 Input(InputView<'a>),
207}
208
209pub struct InputView<'a> {
212 pub text: TextView<'a>,
214 pub caret: Option<Rect>,
216 pub selection: &'a [Rect],
219 pub area: Rect,
221 pub underline: &'a [Rect],
223}
224
225pub struct TextView<'a> {
229 text: &'a TextNode,
230 color: Color,
231}
232
233impl<'a> TextView<'a> {
234 pub fn content(&self) -> &'a str {
236 &self.text.content
237 }
238
239 pub fn buffer(&self) -> &'a Buffer {
241 &self.text.buffer
242 }
243
244 pub fn runs(&self) -> impl Iterator<Item = LayoutRun<'a>> + 'a {
247 self.text.runs()
248 }
249
250 pub fn origin(&self) -> Point {
252 self.text.origin
253 }
254
255 pub fn color(&self) -> Color {
258 self.color
259 }
260}
261
262pub struct IconView<'a> {
264 pub source: &'a IconSource,
266 pub rect: Rect,
268}
269
270pub struct ImageView<'a> {
272 pub path: &'a Path,
274 pub rect: Rect,
276 pub fit: Fit,
278}
279
280pub struct CaptureView<'a> {
282 pub id: u64,
284 pub frame: &'a CaptureFrame,
286 pub rect: Rect,
288 pub fit: Fit,
290}