freya_components/
overflowed_content.rs

1use std::time::Duration;
2
3use freya_animation::prelude::{
4    AnimDirection,
5    AnimNum,
6    Ease,
7    Function,
8    OnFinish,
9    use_animation,
10};
11use freya_core::prelude::*;
12use torin::{
13    node::Node,
14    prelude::Area,
15    size::Size,
16};
17
18/// Animate the content of a container when the content overflows.
19///
20/// This is primarily targeted to text that can't be fully shown in small layouts.
21///
22/// # Example
23///
24/// ```rust
25/// # use freya::prelude::*;
26/// fn app() -> impl IntoElement {
27///     Button::new().child(
28///         OverflowedContent::new().width(Size::px(100.)).child(
29///             label()
30///                 .text("Freya is a cross-platform GUI library for Rust")
31///                 .max_lines(1),
32///         ),
33///     )
34/// }
35/// ```
36#[derive(Clone, PartialEq)]
37pub struct OverflowedContent {
38    children: Vec<Element>,
39    layout: LayoutData,
40    duration: Duration,
41    key: DiffKey,
42}
43
44impl LayoutExt for OverflowedContent {
45    fn get_layout(&mut self) -> &mut LayoutData {
46        &mut self.layout
47    }
48}
49
50impl ContainerSizeExt for OverflowedContent {}
51
52impl Default for OverflowedContent {
53    fn default() -> Self {
54        Self::new()
55    }
56}
57
58impl ChildrenExt for OverflowedContent {
59    fn get_children(&mut self) -> &mut Vec<Element> {
60        &mut self.children
61    }
62}
63
64impl KeyExt for OverflowedContent {
65    fn write_key(&mut self) -> &mut DiffKey {
66        &mut self.key
67    }
68}
69
70impl OverflowedContent {
71    pub fn new() -> Self {
72        Self {
73            children: Vec::new(),
74            layout: Node {
75                width: Size::fill(),
76                height: Size::fill(),
77                ..Default::default()
78            }
79            .into(),
80            duration: Duration::from_secs(4),
81            key: DiffKey::None,
82        }
83    }
84
85    pub fn width(mut self, width: impl Into<Size>) -> Self {
86        self.layout.width = width.into();
87        self
88    }
89
90    pub fn height(mut self, height: impl Into<Size>) -> Self {
91        self.layout.height = height.into();
92        self
93    }
94
95    pub fn duration(mut self, duration: Duration) -> Self {
96        self.duration = duration;
97        self
98    }
99}
100
101impl Component for OverflowedContent {
102    fn render(&self) -> impl IntoElement {
103        let mut label_size = use_state(Area::default);
104        let mut rect_size = use_state(Area::default);
105
106        let rect_width = rect_size.read().width();
107        let label_width = label_size.read().width();
108        let does_overflow = label_width > rect_width;
109
110        let duration = self.duration;
111        let animation = use_animation(move |conf| {
112            conf.on_finish(OnFinish::restart());
113
114            AnimNum::new(0., 100.)
115                .duration(duration)
116                .ease(Ease::InOut)
117                .function(Function::Linear)
118        });
119
120        use_side_effect_with_deps(&does_overflow, move |does_overflow| {
121            if *does_overflow {
122                animation.run(AnimDirection::Forward);
123            }
124        });
125
126        let progress = animation.get().value();
127        let offset_x = if does_overflow {
128            ((label_width + rect_width) * progress / 100.) - rect_width
129        } else {
130            0.
131        };
132
133        rect()
134            .width(self.layout.width.clone())
135            .height(self.layout.height.clone())
136            .offset_x(-offset_x)
137            .overflow(Overflow::Clip)
138            .on_sized(move |e: Event<SizedEventData>| rect_size.set(e.area))
139            .child(
140                rect()
141                    .on_sized(move |e: Event<SizedEventData>| label_size.set(e.area))
142                    .children(self.children.clone()),
143            )
144    }
145
146    fn render_key(&self) -> DiffKey {
147        self.key.clone().or(self.default_key())
148    }
149}