use std::cell::Ref;
use std::path::Path;
use cosmic_text::{Buffer, LayoutRun};
use taffy::TraversePartialTree;
use crate::geometry::{Point, Rect, Size};
use crate::screencopy::CaptureFrame;
use crate::style::{Color, Style};
use crate::text::TextNode;
use crate::tree::{Node, NodeId, NodeKind, Tree, centered, icon_rect};
use crate::widgets::{Fit, IconSource};
const DEFAULT_TEXT_COLOR: Color = Color::rgb(0, 0, 0);
pub struct WindowView<'w> {
tree: Ref<'w, Tree>,
}
impl<'w> WindowView<'w> {
pub(crate) fn new(tree: Ref<'w, Tree>) -> WindowView<'w> {
WindowView { tree }
}
pub fn size(&self) -> Size {
self.tree.size
}
pub fn root(&self) -> NodeView<'_> {
NodeView::new(&self.tree, self.tree.root)
.expect("invariant: the root node exists while the tree does")
}
}
#[derive(Clone, Copy)]
pub struct NodeView<'a> {
tree: &'a Tree,
id: NodeId,
node: &'a Node,
}
impl<'a> NodeView<'a> {
fn new(tree: &'a Tree, id: NodeId) -> Option<NodeView<'a>> {
let node = tree.node(id)?;
Some(NodeView { tree, id, node })
}
pub fn name(&self) -> &'static str {
self.node.name
}
pub fn path(&self) -> &'a str {
&self.node.path
}
pub fn rect(&self) -> Rect {
self.node.rect.unwrap_or_default()
}
pub fn bounds(&self) -> Rect {
self.node.bounds
}
pub fn style(&self) -> Style {
self.node.shown_style()
}
pub fn children(&self) -> impl Iterator<Item = NodeView<'a>> + 'a {
let tree = self.tree;
tree
.taffy
.child_ids(self.id)
.filter_map(move |id| NodeView::new(tree, id))
}
pub fn clips(&self) -> bool {
self.node.clips()
}
pub fn content(&self) -> Content<'a> {
match &self.node.kind {
NodeKind::Text(text) => Content::Text(TextView {
text,
color: self.inherited_color(),
}),
NodeKind::Icon(icon) => match &icon.source {
Some(source) => Content::Icon(IconView {
source,
rect: icon_rect(self.node.content, icon.size),
}),
None => Content::None,
},
NodeKind::Input(input) => {
let color = self.inherited_color();
let color = if input.showing_placeholder() {
Color::rgba(color.r, color.g, color.b, color.a / 2)
} else {
color
};
Content::Input(InputView {
text: TextView {
text: &input.text,
color,
},
caret: (input.focused
&& !matches!(&input.preedit, Some(preedit) if preedit.cursor.is_none()))
.then_some(input.caret),
selection: if input.focused { &input.selection } else { &[] },
area: input.area,
underline: &input.underline,
})
}
NodeKind::Image(image) => match &image.source {
Some(path) => Content::Image(ImageView {
path,
rect: centered(self.node.content, image.width, image.height),
fit: image.fit,
}),
None => Content::None,
},
NodeKind::Capture(capture) => match &capture.frame {
Some(frame) => Content::Capture(CaptureView {
id: capture.id,
frame,
rect: self.node.content,
fit: capture.fit,
}),
None => Content::None,
},
_ => Content::None,
}
}
fn inherited_color(&self) -> Color {
let mut id = Some(self.id);
while let Some(current) = id {
if let Some(color) = self
.tree
.node(current)
.and_then(|node| node.shown_style().color)
{
return color;
}
id = self.tree.taffy.parent(current);
}
DEFAULT_TEXT_COLOR
}
}
pub enum Content<'a> {
None,
Text(TextView<'a>),
Icon(IconView<'a>),
Image(ImageView<'a>),
Capture(CaptureView<'a>),
Input(InputView<'a>),
}
pub struct InputView<'a> {
pub text: TextView<'a>,
pub caret: Option<Rect>,
pub selection: &'a [Rect],
pub area: Rect,
pub underline: &'a [Rect],
}
pub struct TextView<'a> {
text: &'a TextNode,
color: Color,
}
impl<'a> TextView<'a> {
pub fn content(&self) -> &'a str {
&self.text.content
}
pub fn buffer(&self) -> &'a Buffer {
&self.text.buffer
}
pub fn runs(&self) -> impl Iterator<Item = LayoutRun<'a>> + 'a {
self.text.runs()
}
pub fn origin(&self) -> Point {
self.text.origin
}
pub fn color(&self) -> Color {
self.color
}
}
pub struct IconView<'a> {
pub source: &'a IconSource,
pub rect: Rect,
}
pub struct ImageView<'a> {
pub path: &'a Path,
pub rect: Rect,
pub fit: Fit,
}
pub struct CaptureView<'a> {
pub id: u64,
pub frame: &'a CaptureFrame,
pub rect: Rect,
pub fit: Fit,
}