1#![allow(non_snake_case)]
2pub mod anim;
106pub mod anim_ext;
107pub mod gestures;
108pub mod layout;
109pub mod lazy;
110pub mod navigation;
111pub mod scroll;
112
113use rustc_hash::{FxHashMap, FxHashSet};
114use std::collections::{HashMap, HashSet};
115use std::rc::Rc;
116use std::{cell::RefCell, cmp::Ordering};
117
118use repose_core::*;
119use taffy::style::FlexDirection;
120use taffy::{Overflow, Point};
121
122pub mod textfield;
123use crate::textfield::{TF_FONT_DP, TF_PADDING_X_DP, byte_to_char_index, measure_text};
124use repose_core::locals;
125pub use textfield::{TextArea, TextField, TextFieldState};
126
127thread_local! {
128 static LAYOUT_ENGINE: RefCell<layout::LayoutEngine> =
129 RefCell::new(layout::LayoutEngine::new());
130}
131
132#[derive(Default)]
133pub struct Interactions {
134 pub hover: Option<u64>,
135 pub pressed: HashSet<u64>,
136}
137
138pub fn Surface(modifier: Modifier, child: View) -> View {
139 let mut v = View::new(0, ViewKind::Surface).modifier(modifier);
140 v.children = vec![child];
141 v
142}
143
144pub fn Box(modifier: Modifier) -> View {
145 View::new(0, ViewKind::Box).modifier(modifier)
146}
147
148pub fn Row(modifier: Modifier) -> View {
149 View::new(0, ViewKind::Row).modifier(modifier)
150}
151
152pub fn Column(modifier: Modifier) -> View {
153 View::new(0, ViewKind::Column).modifier(modifier)
154}
155
156pub fn Stack(modifier: Modifier) -> View {
157 View::new(0, ViewKind::Stack).modifier(modifier)
158}
159
160#[deprecated = "Use ScollArea instead"]
161pub fn Scroll(modifier: Modifier) -> View {
162 View::new(
163 0,
164 ViewKind::ScrollV {
165 on_scroll: None,
166 set_viewport_height: None,
167 set_content_height: None,
168 get_scroll_offset: None,
169 set_scroll_offset: None,
170 },
171 )
172 .modifier(modifier)
173}
174
175pub fn Text(text: impl Into<String>) -> View {
176 View::new(
177 0,
178 ViewKind::Text {
179 text: text.into(),
180 color: Color::WHITE,
181 font_size: 16.0, soft_wrap: true,
183 max_lines: None,
184 overflow: TextOverflow::Visible,
185 },
186 )
187}
188
189pub fn Spacer() -> View {
190 Box(Modifier::new().flex_grow(1.0))
191}
192
193pub fn Space(modifier: Modifier) -> View {
194 Box(modifier)
195}
196
197pub fn Grid(
198 columns: usize,
199 modifier: Modifier,
200 children: Vec<View>,
201 row_gap: f32,
202 column_gap: f32,
203) -> View {
204 Column(modifier.grid(columns, row_gap, column_gap)).with_children(children)
205}
206
207pub fn Button(content: impl IntoChildren, on_click: impl Fn() + 'static) -> View {
208 View::new(
209 0,
210 ViewKind::Button {
211 on_click: Some(Rc::new(on_click)),
212 },
213 )
214 .with_children(content.into_children())
215 .semantics(Semantics {
216 role: Role::Button,
217 label: None, focused: false,
219 enabled: true,
220 })
221}
222
223pub fn Checkbox(checked: bool, on_change: impl Fn(bool) + 'static) -> View {
224 View::new(
225 0,
226 ViewKind::Checkbox {
227 checked,
228 on_change: Some(Rc::new(on_change)),
229 },
230 )
231 .semantics(Semantics {
232 role: Role::Checkbox,
233 label: None,
234 focused: false,
235 enabled: true,
236 })
237}
238
239pub fn RadioButton(selected: bool, on_select: impl Fn() + 'static) -> View {
240 View::new(
241 0,
242 ViewKind::RadioButton {
243 selected,
244 on_select: Some(Rc::new(on_select)),
245 },
246 )
247 .semantics(Semantics {
248 role: Role::RadioButton,
249 label: None,
250 focused: false,
251 enabled: true,
252 })
253}
254
255pub fn Switch(checked: bool, on_change: impl Fn(bool) + 'static) -> View {
256 View::new(
257 0,
258 ViewKind::Switch {
259 checked,
260 on_change: Some(Rc::new(on_change)),
261 },
262 )
263 .semantics(Semantics {
264 role: Role::Switch,
265 label: None,
266 focused: false,
267 enabled: true,
268 })
269}
270pub fn Slider(
271 value: f32,
272 range: (f32, f32),
273 step: Option<f32>,
274 on_change: impl Fn(f32) + 'static,
275) -> View {
276 View::new(
277 0,
278 ViewKind::Slider {
279 value,
280 min: range.0,
281 max: range.1,
282 step,
283 on_change: Some(Rc::new(on_change)),
284 },
285 )
286 .semantics(Semantics {
287 role: Role::Slider,
288 label: None,
289 focused: false,
290 enabled: true,
291 })
292}
293
294pub fn RangeSlider(
295 start: f32,
296 end: f32,
297 range: (f32, f32),
298 step: Option<f32>,
299 on_change: impl Fn(f32, f32) + 'static,
300) -> View {
301 View::new(
302 0,
303 ViewKind::RangeSlider {
304 start,
305 end,
306 min: range.0,
307 max: range.1,
308 step,
309 on_change: Some(Rc::new(on_change)),
310 },
311 )
312 .semantics(Semantics {
313 role: Role::Slider,
314 label: None,
315 focused: false,
316 enabled: true,
317 })
318}
319
320pub fn LinearProgress(value: Option<f32>) -> View {
321 View::new(
322 0,
323 ViewKind::ProgressBar {
324 value: value.unwrap_or(0.0),
325 min: 0.0,
326 max: 1.0,
327 circular: false,
328 },
329 )
330 .semantics(Semantics {
331 role: Role::ProgressBar,
332 label: None,
333 focused: false,
334 enabled: true,
335 })
336}
337
338pub fn ProgressBar(value: f32, range: (f32, f32)) -> View {
339 View::new(
340 0,
341 ViewKind::ProgressBar {
342 value,
343 min: range.0,
344 max: range.1,
345 circular: false,
346 },
347 )
348 .semantics(Semantics {
349 role: Role::ProgressBar,
350 label: None,
351 focused: false,
352 enabled: true,
353 })
354}
355
356pub fn Image(modifier: Modifier, handle: ImageHandle) -> View {
357 View::new(
358 0,
359 ViewKind::Image {
360 handle,
361 tint: Color::WHITE,
362 fit: ImageFit::Contain,
363 },
364 )
365 .modifier(modifier)
366}
367
368pub trait ImageExt {
369 fn image_tint(self, c: Color) -> View;
370 fn image_fit(self, fit: ImageFit) -> View;
371}
372impl ImageExt for View {
373 fn image_tint(mut self, c: Color) -> View {
374 if let ViewKind::Image { tint, .. } = &mut self.kind {
375 *tint = c;
376 }
377 self
378 }
379 fn image_fit(mut self, fit: ImageFit) -> View {
380 if let ViewKind::Image { fit: f, .. } = &mut self.kind {
381 *f = fit;
382 }
383 self
384 }
385}
386
387fn flex_dir_for(kind: &ViewKind) -> Option<FlexDirection> {
388 match kind {
389 ViewKind::Row => {
390 if repose_core::locals::text_direction() == repose_core::locals::TextDirection::Rtl {
391 Some(FlexDirection::RowReverse)
392 } else {
393 Some(FlexDirection::Row)
394 }
395 }
396 ViewKind::Column | ViewKind::Surface | ViewKind::ScrollV { .. } => {
397 Some(FlexDirection::Column)
398 }
399 _ => None,
400 }
401}
402
403pub trait ViewExt: Sized {
405 fn child(self, children: impl IntoChildren) -> Self;
406}
407
408impl ViewExt for View {
409 fn child(self, children: impl IntoChildren) -> Self {
410 self.with_children(children.into_children())
411 }
412}
413
414pub trait IntoChildren {
415 fn into_children(self) -> Vec<View>;
416}
417
418impl IntoChildren for View {
419 fn into_children(self) -> Vec<View> {
420 vec![self]
421 }
422}
423
424impl IntoChildren for Vec<View> {
425 fn into_children(self) -> Vec<View> {
426 self
427 }
428}
429
430impl<const N: usize> IntoChildren for [View; N] {
431 fn into_children(self) -> Vec<View> {
432 self.into()
433 }
434}
435
436macro_rules! impl_into_children_tuple {
438 ($($idx:tt $t:ident),+) => {
439 impl<$($t: IntoChildren),+> IntoChildren for ($($t,)+) {
440 fn into_children(self) -> Vec<View> {
441 let mut v = Vec::new();
442 $(v.extend(self.$idx.into_children());)+
443 v
444 }
445 }
446 };
447}
448
449impl_into_children_tuple!(0 A);
450impl_into_children_tuple!(0 A, 1 B);
451impl_into_children_tuple!(0 A, 1 B, 2 C);
452impl_into_children_tuple!(0 A, 1 B, 2 C, 3 D);
453impl_into_children_tuple!(0 A, 1 B, 2 C, 3 D, 4 E);
454impl_into_children_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F);
455impl_into_children_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G);
456impl_into_children_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H);
457
458pub fn layout_and_paint(
460 root: &View,
461 size_px_u32: (u32, u32),
462 textfield_states: &HashMap<u64, Rc<RefCell<TextFieldState>>>,
463 interactions: &Interactions,
464 focused: Option<u64>,
465) -> (Scene, Vec<HitRegion>, Vec<SemNode>) {
466 LAYOUT_ENGINE.with(|engine| {
467 engine
468 .borrow_mut()
469 .layout_frame(root, size_px_u32, textfield_states, interactions, focused)
470 })
471}
472
473pub trait TextStyle {
475 fn color(self, c: Color) -> View;
476 fn size(self, px: f32) -> View;
477 fn max_lines(self, n: usize) -> View;
478 fn single_line(self) -> View;
479 fn overflow_ellipsize(self) -> View;
480 fn overflow_clip(self) -> View;
481 fn overflow_visible(self) -> View;
482}
483impl TextStyle for View {
484 fn color(mut self, c: Color) -> View {
485 if let ViewKind::Text {
486 color: text_color, ..
487 } = &mut self.kind
488 {
489 *text_color = c;
490 }
491 self
492 }
493 fn size(mut self, dp_font: f32) -> View {
494 if let ViewKind::Text {
495 font_size: text_size_dp,
496 ..
497 } = &mut self.kind
498 {
499 *text_size_dp = dp_font;
500 }
501 self
502 }
503 fn max_lines(mut self, n: usize) -> View {
504 if let ViewKind::Text {
505 max_lines,
506 soft_wrap,
507 ..
508 } = &mut self.kind
509 {
510 *max_lines = Some(n);
511 *soft_wrap = true;
512 }
513 self
514 }
515 fn single_line(mut self) -> View {
516 if let ViewKind::Text {
517 soft_wrap,
518 max_lines,
519 ..
520 } = &mut self.kind
521 {
522 *soft_wrap = false;
523 *max_lines = Some(1);
524 }
525 self
526 }
527 fn overflow_ellipsize(mut self) -> View {
528 if let ViewKind::Text { overflow, .. } = &mut self.kind {
529 *overflow = TextOverflow::Ellipsis;
530 }
531 self
532 }
533 fn overflow_clip(mut self) -> View {
534 if let ViewKind::Text { overflow, .. } = &mut self.kind {
535 *overflow = TextOverflow::Clip;
536 }
537 self
538 }
539 fn overflow_visible(mut self) -> View {
540 if let ViewKind::Text { overflow, .. } = &mut self.kind {
541 *overflow = TextOverflow::Visible;
542 }
543 self
544 }
545}