freya_components/
overflowed_content.rs1use std::time::Duration;
2
3use freya_animation::prelude::{
4 AnimDirection,
5 AnimNum,
6 Ease,
7 Function,
8 use_animation,
9};
10use freya_core::prelude::*;
11use torin::{
12 node::Node,
13 prelude::Area,
14 size::Size,
15};
16
17#[derive(Clone, PartialEq, Default)]
19pub enum OverflowedContentDirection {
20 #[default]
22 RightToLeft,
23 LeftToRight,
25}
26
27#[derive(Clone, PartialEq, Default)]
29pub enum OverflowedContentStart {
30 #[default]
32 Edge,
33 Visible,
35}
36
37#[derive(Clone, PartialEq)]
56pub struct OverflowedContent {
57 children: Vec<Element>,
58 layout: LayoutData,
59 duration: Duration,
60 direction: OverflowedContentDirection,
61 start: OverflowedContentStart,
62 key: DiffKey,
63}
64
65impl LayoutExt for OverflowedContent {
66 fn get_layout(&mut self) -> &mut LayoutData {
67 &mut self.layout
68 }
69}
70
71impl ContainerSizeExt for OverflowedContent {}
72
73impl Default for OverflowedContent {
74 fn default() -> Self {
75 Self::new()
76 }
77}
78
79impl ChildrenExt for OverflowedContent {
80 fn get_children(&mut self) -> &mut Vec<Element> {
81 &mut self.children
82 }
83}
84
85impl KeyExt for OverflowedContent {
86 fn write_key(&mut self) -> &mut DiffKey {
87 &mut self.key
88 }
89}
90
91impl OverflowedContent {
92 pub fn new() -> Self {
93 Self {
94 children: Vec::new(),
95 layout: Node {
96 width: Size::fill(),
97 height: Size::Inner,
98 ..Default::default()
99 }
100 .into(),
101 duration: Duration::from_secs(4),
102 direction: OverflowedContentDirection::default(),
103 start: OverflowedContentStart::default(),
104 key: DiffKey::None,
105 }
106 }
107
108 pub fn width(mut self, width: impl Into<Size>) -> Self {
109 self.layout.width = width.into();
110 self
111 }
112
113 pub fn height(mut self, height: impl Into<Size>) -> Self {
114 self.layout.height = height.into();
115 self
116 }
117
118 pub fn duration(mut self, duration: Duration) -> Self {
119 self.duration = duration;
120 self
121 }
122
123 pub fn direction(mut self, direction: OverflowedContentDirection) -> Self {
124 self.direction = direction;
125 self
126 }
127
128 pub fn right_to_left(self) -> Self {
129 self.direction(OverflowedContentDirection::RightToLeft)
130 }
131
132 pub fn left_to_right(self) -> Self {
133 self.direction(OverflowedContentDirection::LeftToRight)
134 }
135
136 pub fn start(mut self, start: OverflowedContentStart) -> Self {
137 self.start = start;
138 self
139 }
140
141 pub fn start_edge(self) -> Self {
142 self.start(OverflowedContentStart::Edge)
143 }
144
145 pub fn start_visible(self) -> Self {
146 self.start(OverflowedContentStart::Visible)
147 }
148}
149
150impl Component for OverflowedContent {
151 fn render(&self) -> impl IntoElement {
152 let mut content_area = use_state(Area::default);
153 let mut container_area = use_state(Area::default);
154
155 let container_width = container_area.read().width();
156 let content_width = content_area.read().width();
157 let does_overflow = content_width > container_width;
158
159 let duration = self.duration;
160
161 let animation = use_animation(move |_| {
162 AnimNum::new(0., 100.)
163 .duration(duration)
164 .ease(Ease::InOut)
165 .function(Function::Linear)
166 });
167
168 let is_running = *animation.is_running().read();
169 let has_run = *animation.has_run_yet().read();
170
171 use_side_effect_with_deps(
172 &(does_overflow, is_running, has_run),
173 move |&(does_overflow, is_running, has_run)| {
174 if does_overflow && (!has_run || !is_running) {
175 animation.run(AnimDirection::Forward);
176 }
177 },
178 );
179
180 let progress = animation.get().value();
181 let is_first_cycle =
182 *animation.runs().read() <= 1 && self.start == OverflowedContentStart::Visible;
183
184 let offset_x = if does_overflow {
185 match (&self.direction, is_first_cycle) {
186 (OverflowedContentDirection::RightToLeft, false) => {
187 container_width - (content_width + container_width) * progress / 100.
188 }
189 (OverflowedContentDirection::RightToLeft, true) => {
190 -(content_width * progress / 100.)
191 }
192 (OverflowedContentDirection::LeftToRight, false) => {
193 (content_width + container_width) * progress / 100. - content_width
194 }
195 (OverflowedContentDirection::LeftToRight, true) => {
196 container_width * progress / 100.
197 }
198 }
199 } else {
200 0.
201 };
202
203 rect()
204 .width(self.layout.width.clone())
205 .height(self.layout.height.clone())
206 .overflow(Overflow::Clip)
207 .on_sized(move |e: Event<SizedEventData>| container_area.set(e.area))
208 .child(
209 rect()
210 .offset_x(offset_x)
211 .on_sized(move |e: Event<SizedEventData>| content_area.set(e.area))
212 .children(self.children.clone()),
213 )
214 }
215
216 fn render_key(&self) -> DiffKey {
217 self.key.clone().or(self.default_key())
218 }
219}