feather_ui/component/
paragraph.rs1use crate::color::sRGB;
5use crate::component::ChildOf;
6use crate::component::text::Text;
7use crate::layout::{Desc, Layout, base, flex, leaf};
8use crate::persist::{FnPersist, VectorMap};
9use crate::{SourceID, UNSIZED_AXIS, gen_id, layout};
10use core::f32;
11use derive_where::derive_where;
12use std::rc::Rc;
13use std::sync::Arc;
14
15#[derive(feather_macro::StateMachineChild)]
16#[derive_where(Clone)]
17pub struct Paragraph<T> {
18 pub id: Arc<SourceID>,
19 props: Rc<T>,
20 children: im::Vector<Option<Box<ChildOf<dyn flex::Prop>>>>,
21}
22
23#[derive(Clone, Copy, Default, PartialEq, PartialOrd)]
24struct MinimalFlexChild {
25 grow: f32,
26}
27
28impl flex::Child for MinimalFlexChild {
29 fn grow(&self) -> f32 {
30 self.grow
31 }
32
33 fn shrink(&self) -> f32 {
34 0.0
35 }
36
37 fn basis(&self) -> crate::DValue {
38 crate::DValue {
39 dp: 0.0,
40 px: 0.0,
41 rel: UNSIZED_AXIS,
42 }
43 }
44}
45
46impl base::Area for MinimalFlexChild {
47 fn area(&self) -> &crate::DRect {
48 &crate::AUTO_DRECT
49 }
50}
51
52impl base::Anchor for MinimalFlexChild {}
53impl base::Order for MinimalFlexChild {}
54impl base::Margin for MinimalFlexChild {}
55impl base::RLimits for MinimalFlexChild {}
56impl base::Limits for MinimalFlexChild {}
57impl base::Padding for MinimalFlexChild {}
58impl leaf::Prop for MinimalFlexChild {}
59impl leaf::Padded for MinimalFlexChild {}
60
61impl<T: flex::Prop + 'static> Paragraph<T> {
62 pub fn new(id: Arc<SourceID>, props: T) -> Self {
63 Self {
64 id,
65 props: props.into(),
66 children: im::Vector::new(),
67 }
68 }
69
70 pub fn append(&mut self, child: Box<ChildOf<dyn flex::Prop>>) {
71 self.children.push_back(Some(child));
72 }
73
74 pub fn prepend(&mut self, child: Box<ChildOf<dyn flex::Prop>>) {
75 self.children.push_front(Some(child));
76 }
77
78 #[allow(clippy::too_many_arguments)]
79 pub fn set_text(
80 &mut self,
81 text: &str,
82 font_size: f32,
83 line_height: f32,
84 font: cosmic_text::FamilyOwned,
85 color: sRGB,
86 weight: cosmic_text::Weight,
87 style: cosmic_text::Style,
88 fullwidth: bool,
89 ) {
90 self.children.clear();
91 for (i, word) in text.split_ascii_whitespace().enumerate() {
92 let text = Text::<MinimalFlexChild>::new(
93 gen_id!(gen_id!(self.id), i),
94 MinimalFlexChild {
95 grow: if fullwidth { 1.0 } else { 0.0 },
96 },
97 font_size,
98 line_height,
99 word.to_owned() + " ",
100 font.clone(),
101 color,
102 weight,
103 style,
104 cosmic_text::Wrap::None,
105 None, );
107 self.children.push_back(Some(Box::new(text)));
108 }
109 }
110}
111
112impl<T: flex::Prop + 'static> super::Component for Paragraph<T> {
113 type Props = T;
114
115 fn layout(
116 &self,
117 manager: &mut crate::StateManager,
118 driver: &crate::graphics::Driver,
119 window: &Arc<SourceID>,
120 ) -> Box<dyn Layout<T>> {
121 let mut map = VectorMap::new(crate::persist::Persist::new(
122 |child: &Option<Box<ChildOf<dyn flex::Prop>>>| -> Option<Box<dyn Layout<<dyn flex::Prop as Desc>::Child>>> {
123 Some(child.as_ref()?.layout(manager, driver, window))
124 })
125 );
126
127 let (_, children) = map.call(Default::default(), &self.children);
128 Box::new(layout::Node::<T, dyn flex::Prop> {
129 props: self.props.clone(),
130 children,
131 id: Arc::downgrade(&self.id),
132 renderable: None,
133 layer: None,
134 })
135 }
136}