Skip to main content

crabshell_ui/
view.rs

1//! A read-only view of a laid-out window, for drawing it.
2
3use 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
16/// The color text uses when no ancestor sets one.
17const DEFAULT_TEXT_COLOR: Color = Color::rgb(0, 0, 0);
18
19/// A window's laid-out tree, borrowed for reading. Get one from
20/// [`Window::view`](crate::Window::view).
21pub 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  /// The window's size in logical pixels.
31  pub fn size(&self) -> Size {
32    self.tree.size
33  }
34
35  /// The window's root node, which has the window's style and its child.
36  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/// One node of a laid-out window: a widget, or the window itself at the
43/// root. Draw it, then its children in order, so later ones are on top.
44#[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  /// The widget type, such as `"Row"`, or `"PanelWindow"` at the root.
58  pub fn name(&self) -> &'static str {
59    self.node.name
60  }
61
62  /// The widget path, such as `PanelWindow[DP-1]/Row/Button[2]`.
63  pub fn path(&self) -> &'a str {
64    &self.node.path
65  }
66
67  /// The rectangle from the last layout, in window coordinates. Empty
68  /// before the first layout.
69  pub fn rect(&self) -> Rect {
70    self.node.rect.unwrap_or_default()
71  }
72
73  /// The area the node and everything inside it painted at the last
74  /// layout: its rectangle, text and icons that overflow it, and its
75  /// descendants' bounds. Nothing outside it needs drawing for this node.
76  pub fn bounds(&self) -> Rect {
77    self.node.bounds
78  }
79
80  /// The style in effect, with a button's hover and pressed styles laid
81  /// over its own. Inherited fields aren't filled in;
82  /// [`TextView::color`] has the color text is drawn in.
83  pub fn style(&self) -> Style {
84    self.node.shown_style()
85  }
86
87  /// The children, in drawing order.
88  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  /// Whether the node cuts its children to the inside of its border: a
97  /// clipping style or a scroll view.
98  pub fn clips(&self) -> bool {
99    self.node.clips()
100  }
101
102  /// What the node draws besides its background and border.
103  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          // An input method may ask for no caret while it composes.
129          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  /// The color of the nearest node, starting with this one, whose style
159  /// sets one.
160  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
176/// What a node draws besides its background and border.
177pub enum Content<'a> {
178  /// Nothing.
179  None,
180  /// Text.
181  Text(TextView<'a>),
182  /// An icon.
183  Icon(IconView<'a>),
184  /// An image.
185  Image(ImageView<'a>),
186  /// A screencopy view's frame.
187  Capture(CaptureView<'a>),
188  /// A text field.
189  Input(InputView<'a>),
190}
191
192/// A text field, ready to draw: the selection behind the text, then the
193/// text, then the caret.
194pub struct InputView<'a> {
195  /// What the field shows; a placeholder comes in half its color.
196  pub text: TextView<'a>,
197  /// The caret, while the field has the keyboard.
198  pub caret: Option<Rect>,
199  /// The selection, while the field has the keyboard: one span, or more
200  /// where right-to-left and left-to-right text mix.
201  pub selection: &'a [Rect],
202  /// The content box everything is cut to.
203  pub area: Rect,
204  /// The line under text an input method is composing.
205  pub underline: &'a [Rect],
206}
207
208/// Laid-out text, ready to draw. Lines can stick out of the node's
209/// rectangle when it is too small for them; draw them unclipped, as CSS
210/// does. The window's damage covers them.
211pub struct TextView<'a> {
212  text: &'a TextNode,
213  color: Color,
214}
215
216impl<'a> TextView<'a> {
217  /// The text itself.
218  pub fn content(&self) -> &'a str {
219    &self.text.content
220  }
221
222  /// The cosmic-text buffer layout measured, for drawing glyphs from.
223  pub fn buffer(&self) -> &'a Buffer {
224    &self.text.buffer
225  }
226
227  /// The lines to draw: the buffer's lines, at most `max_lines`. Glyph
228  /// positions are relative to [`origin`](Self::origin).
229  pub fn runs(&self) -> impl Iterator<Item = LayoutRun<'a>> + 'a {
230    self.text.runs()
231  }
232
233  /// The top-left corner of the first line, in window coordinates.
234  pub fn origin(&self) -> Point {
235    self.text.origin
236  }
237
238  /// The color to draw in, inherited if the text's own style doesn't set
239  /// one.
240  pub fn color(&self) -> Color {
241    self.color
242  }
243}
244
245/// An icon, ready to draw.
246pub struct IconView<'a> {
247  /// What to show.
248  pub source: &'a IconSource,
249  /// Where: a square of the icon's size, centered in the node.
250  pub rect: Rect,
251}
252
253/// An image, ready to draw.
254pub struct ImageView<'a> {
255  /// The file to show.
256  pub path: &'a Path,
257  /// Where: a box of the image's size, centered in the node.
258  pub rect: Rect,
259  /// How the picture fills the box.
260  pub fit: Fit,
261}
262
263/// A screencopy view's frame, ready to draw.
264pub struct CaptureView<'a> {
265  /// Names the view, so a cache can keep its frame scaled to its box.
266  pub id: u64,
267  /// The frame last captured.
268  pub frame: &'a CaptureFrame,
269  /// Where: the view's content box.
270  pub rect: Rect,
271  /// How the frame fills the box.
272  pub fit: Fit,
273}