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