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