1use std::cell::Ref;
4use std::path::Path;
5
6use cosmic_text::{Buffer, LayoutRun};
7use taffy::TraversePartialTree;
8
9use crate::geometry::{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 {
77 self.node.bounds
78 }
79
80 pub fn style(&self) -> Style {
84 self.node.shown_style()
85 }
86
87 pub fn children(&self) -> impl Iterator<Item = NodeView<'a>> + 'a {
89 let tree = self.tree;
90 tree
91 .taffy
92 .child_ids(self.id)
93 .filter_map(move |id| NodeView::new(tree, id))
94 }
95
96 pub fn clips(&self) -> bool {
99 self.node.clips()
100 }
101
102 pub fn content(&self) -> Content<'a> {
104 match &self.node.kind {
105 NodeKind::Text(text) => Content::Text(TextView {
106 text,
107 color: self.inherited_color(),
108 }),
109 NodeKind::Icon(icon) => match &icon.source {
110 Some(source) => Content::Icon(IconView {
111 source,
112 rect: icon_rect(self.node.content, icon.size),
113 }),
114 None => Content::None,
115 },
116 NodeKind::Input(input) => {
117 let color = self.inherited_color();
118 let color = if input.showing_placeholder() {
119 Color::rgba(color.r, color.g, color.b, color.a / 2)
120 } else {
121 color
122 };
123 Content::Input(InputView {
124 text: TextView {
125 text: &input.text,
126 color,
127 },
128 caret: (input.focused
130 && !matches!(&input.preedit, Some(preedit) if preedit.cursor.is_none()))
131 .then_some(input.caret),
132 selection: if input.focused { &input.selection } else { &[] },
133 area: input.area,
134 underline: &input.underline,
135 })
136 }
137 NodeKind::Image(image) => match &image.source {
138 Some(path) => Content::Image(ImageView {
139 path,
140 rect: centered(self.node.content, image.width, image.height),
141 fit: image.fit,
142 }),
143 None => Content::None,
144 },
145 NodeKind::Capture(capture) => match &capture.frame {
146 Some(frame) => Content::Capture(CaptureView {
147 id: capture.id,
148 frame,
149 rect: self.node.content,
150 fit: capture.fit,
151 }),
152 None => Content::None,
153 },
154 _ => Content::None,
155 }
156 }
157
158 fn inherited_color(&self) -> Color {
161 let mut id = Some(self.id);
162 while let Some(current) = id {
163 if let Some(color) = self
164 .tree
165 .node(current)
166 .and_then(|node| node.shown_style().color)
167 {
168 return color;
169 }
170 id = self.tree.taffy.parent(current);
171 }
172 DEFAULT_TEXT_COLOR
173 }
174}
175
176pub enum Content<'a> {
178 None,
180 Text(TextView<'a>),
182 Icon(IconView<'a>),
184 Image(ImageView<'a>),
186 Capture(CaptureView<'a>),
188 Input(InputView<'a>),
190}
191
192pub struct InputView<'a> {
195 pub text: TextView<'a>,
197 pub caret: Option<Rect>,
199 pub selection: &'a [Rect],
202 pub area: Rect,
204 pub underline: &'a [Rect],
206}
207
208pub struct TextView<'a> {
212 text: &'a TextNode,
213 color: Color,
214}
215
216impl<'a> TextView<'a> {
217 pub fn content(&self) -> &'a str {
219 &self.text.content
220 }
221
222 pub fn buffer(&self) -> &'a Buffer {
224 &self.text.buffer
225 }
226
227 pub fn runs(&self) -> impl Iterator<Item = LayoutRun<'a>> + 'a {
230 self.text.runs()
231 }
232
233 pub fn origin(&self) -> Point {
235 self.text.origin
236 }
237
238 pub fn color(&self) -> Color {
241 self.color
242 }
243}
244
245pub struct IconView<'a> {
247 pub source: &'a IconSource,
249 pub rect: Rect,
251}
252
253pub struct ImageView<'a> {
255 pub path: &'a Path,
257 pub rect: Rect,
259 pub fit: Fit,
261}
262
263pub struct CaptureView<'a> {
265 pub id: u64,
267 pub frame: &'a CaptureFrame,
269 pub rect: Rect,
271 pub fit: Fit,
273}