1#![allow(non_snake_case)]
2pub mod adaptive;
87pub mod anim;
88pub mod anim_ext;
89pub mod color_picker;
90pub mod gestures;
91pub mod layout;
92pub use layout::IntrinsicSizeMode;
93pub mod lazy;
94pub mod selection;
95pub mod subcompose;
96pub use lazy::{
97 LazyColumn, LazyHorizontalGrid, LazyRow, LazyVerticalGrid, LazyVerticalStaggeredGrid,
98 SimpleList,
99};
100pub mod lazy_states;
101pub use lazy_states::{
102 ItemHeight, LazyColumnConfig, LazyColumnState, LazyGridConfig, LazyGridState, LazyRowConfig,
103 LazyRowState, LazyVerticalStaggeredGridConfig, LazyVerticalStaggeredGridState,
104};
105pub use subcompose::{
106 BoxWithConstraints, SubcomposeLayout, box_with_constraints_with_key, subcompose_hash_key,
107 subcompose_layout_with_slots, subcompose_with_key, subcompose_with_key_slots,
108};
109pub mod overlay;
110pub mod pager;
111pub mod scroll;
112pub mod windowing;
113
114use std::cell::RefCell;
115use std::collections::{HashMap, HashSet};
116use std::rc::Rc;
117use std::sync::atomic::{AtomicU64, Ordering};
118
119use repose_core::*;
120use taffy::style::FlexDirection;
121
122pub mod textfield;
123use repose_core::locals;
124pub use selection::{SelectableText, SelectableTextExt};
125pub use textfield::{
126 BasicSecureTextField, BasicTextField, KeyboardOptions, TextFieldConfig, TextFieldState,
127};
128
129thread_local! {
130 static LAYOUT_ENGINE: RefCell<layout::LayoutEngine> =
131 RefCell::new(layout::LayoutEngine::new());
132}
133
134#[derive(Default)]
135pub struct Interactions {
136 pub hover: Option<u64>,
137 pub pressed: HashSet<u64>,
138}
139
140pub fn Box(modifier: Modifier) -> View {
141 View::new(0, ViewKind::Box).modifier(modifier)
142}
143
144pub fn Row(modifier: Modifier) -> View {
145 View::new(0, ViewKind::Row).modifier(modifier)
146}
147
148pub fn Column(modifier: Modifier) -> View {
149 View::new(0, ViewKind::Column).modifier(modifier)
150}
151
152pub fn FlowRow(modifier: Modifier) -> View {
155 Row(modifier.flex_wrap(FlexWrap::Wrap))
156}
157
158#[deprecated = "Use Column instead (identical behavior)"]
161pub fn Stack(modifier: Modifier) -> View {
162 Column(modifier)
163}
164
165pub fn FlowColumn(modifier: Modifier) -> View {
168 Column(modifier.flex_wrap(FlexWrap::Wrap))
169}
170
171pub fn Center(modifier: Modifier) -> View {
173 Box(modifier.align_self(AlignSelf::CENTER))
174}
175
176pub fn ZStack(modifier: Modifier) -> View {
177 View::new(0, ViewKind::ZStack).modifier(modifier)
178}
179
180pub fn OverlayHost(modifier: Modifier) -> View {
181 View::new(0, ViewKind::OverlayHost).modifier(modifier)
182}
183
184#[deprecated = "Use Modifier::vertical_scroll instead"]
185pub fn Scroll(modifier: Modifier) -> View {
186 View::new(0, ViewKind::Box).modifier(modifier.vertical_scroll(ScrollAxisBinding {
187 show_scrollbar: true,
188 ..Default::default()
189 }))
190}
191
192pub fn Text(text: impl Into<String>) -> View {
193 View::new(
194 0,
195 ViewKind::Text {
196 text: text.into(),
197 color: locals::content_color(),
198 font_size: 16.0, soft_wrap: true,
200 max_lines: None,
201 overflow: TextOverflow::Visible,
202 font_family: Some("sans-serif"),
203 annotations: None,
204 text_align: TextAlign::Start,
205 font_weight: FontWeight::NORMAL,
206 font_style: FontStyle::Normal,
207 text_decoration: TextDecoration::default(),
208 letter_spacing: 0.0,
209 line_height: 0.0,
210 url: None,
211 font_variation_settings: None,
212 },
213 )
214}
215
216pub fn AnnotatedText(annotated: AnnotatedString) -> View {
220 let annotations: Option<std::sync::Arc<[TextSpan]>> = if annotated.spans.is_empty() {
221 None
222 } else {
223 Some(annotated.spans.clone())
224 };
225 View::new(
226 0,
227 ViewKind::Text {
228 text: annotated.text,
229 color: locals::content_color(),
230 font_size: 16.0,
231 soft_wrap: true,
232 max_lines: None,
233 overflow: TextOverflow::Visible,
234 font_family: Some("sans-serif"),
235 annotations,
236 text_align: TextAlign::Start,
237 font_weight: FontWeight::NORMAL,
238 font_style: FontStyle::Normal,
239 text_decoration: TextDecoration::default(),
240 letter_spacing: 0.0,
241 line_height: 0.0,
242 url: None,
243 font_variation_settings: None,
244 },
245 )
246}
247
248pub fn Spacer() -> View {
249 Box(Modifier::new().flex_grow(1.0))
250}
251
252pub fn Space(modifier: Modifier) -> View {
253 Box(modifier)
254}
255
256pub fn Grid(
257 columns: usize,
258 modifier: Modifier,
259 children: Vec<View>,
260 row_gap: f32,
261 column_gap: f32,
262) -> View {
263 Column(modifier.grid(columns, row_gap, column_gap)).with_children(children)
264}
265
266pub fn Expander(modifier: Modifier, expanded: bool, on_toggle: impl Fn() + 'static) -> View {
267 View::new(
268 0,
269 ViewKind::Expander {
270 expanded,
271 on_toggle: Some(Rc::new(on_toggle)),
272 },
273 )
274 .modifier(modifier)
275}
276
277pub fn TreeRow(
283 modifier: Modifier,
284 depth: usize,
285 has_children: bool,
286 is_expanded: bool,
287 is_selected: bool,
288 on_toggle: impl Fn() + 'static,
289 on_select: impl Fn() + 'static,
290) -> View {
291 View::new(
292 0,
293 ViewKind::TreeRow {
294 depth,
295 has_children,
296 is_expanded,
297 is_selected,
298 on_toggle: Some(Rc::new(on_toggle)),
299 on_select: Some(Rc::new(on_select)),
300 },
301 )
302 .modifier(modifier)
303}
304
305static DRAGVALUE_COUNTER: AtomicU64 = AtomicU64::new(0);
306
307pub fn DragValue(
312 value: f32,
313 range: (f32, f32),
314 speed: f32,
315 on_change: impl Fn(f32) + 'static,
316) -> View {
317 let id = DRAGVALUE_COUNTER.fetch_add(1, Ordering::Relaxed);
318 let drag_start_x = remember_mutable_with_key(format!("dv_dsx_{}", id), || 0.0f32);
319 let drag_start_val = remember_mutable_with_key(format!("dv_dsv_{}", id), || 0.0f32);
320 let is_dragging = remember_mutable_with_key(format!("dv_drg_{}", id), || false);
321
322 let oc = Rc::new(on_change);
323 let min = range.0;
324 let max = range.1;
325 let cur = value;
326
327 let th = locals::theme();
328
329 Box(Modifier::new()
330 .min_width(48.0)
331 .height(28.0)
332 .background(th.surface_container)
333 .border(1.0, th.outline, 4.0)
334 .clip_rounded(4.0)
335 .padding_values(PaddingValues {
336 left: 4.0,
337 right: 4.0,
338 top: 0.0,
339 bottom: 0.0,
340 })
341 .on_pointer_down({
342 let dsx = drag_start_x.clone();
343 let dsv = drag_start_val.clone();
344 let drg = is_dragging.clone();
345 move |pe: PointerEvent| {
346 drg.set(true);
347 dsx.set(pe.position.x);
348 dsv.set(cur);
349 }
350 })
351 .on_pointer_move({
352 let dsx = drag_start_x.clone();
353 let dsv = drag_start_val.clone();
354 let drg = is_dragging.clone();
355 let oc = oc.clone();
356 move |pe: PointerEvent| {
357 if !drg.with(|v| *v) {
358 return;
359 }
360 let start_x = dsx.with(|v| *v);
361 let start_val = dsv.with(|v| *v);
362 let new_val = (start_val + (pe.position.x - start_x) * speed).clamp(min, max);
363 (oc)(new_val);
364 }
365 })
366 .on_pointer_up({
367 let drg = is_dragging.clone();
368 move |_pe: PointerEvent| {
369 drg.set(false);
370 }
371 })
372 .cursor(CursorIcon::EwResize))
373 .child(
374 Text(format_value(value))
375 .size(13.0)
376 .color(th.on_surface)
377 .single_line()
378 .overflow_ellipsize(),
379 )
380}
381
382fn format_value(v: f32) -> String {
383 if (v - v.round()).abs() < 1e-6 {
384 format!("{}", v.round() as i64)
385 } else if (v * 10.0 - (v * 10.0).round()).abs() < 1e-6 {
386 format!("{:.1}", v)
387 } else {
388 format!("{:.2}", v)
389 }
390}
391
392pub fn Image(modifier: Modifier, handle: ImageHandle) -> View {
393 View::new(
394 0,
395 ViewKind::Image {
396 handle,
397 tint: Color::WHITE,
398 fit: ImageFit::Contain,
399 },
400 )
401 .modifier(modifier)
402}
403
404pub trait ImageExt {
405 fn image_tint(self, c: Color) -> View;
406 fn image_fit(self, fit: ImageFit) -> View;
407}
408impl ImageExt for View {
409 fn image_tint(mut self, c: Color) -> View {
410 if let ViewKind::Image { tint, .. } = &mut self.kind {
411 *tint = c;
412 }
413 self
414 }
415 fn image_fit(mut self, fit: ImageFit) -> View {
416 if let ViewKind::Image { fit: f, .. } = &mut self.kind {
417 *f = fit;
418 }
419 self
420 }
421}
422
423fn flex_dir_for(kind: &ViewKind, modifier: &Modifier) -> Option<FlexDirection> {
424 if let Some(ref scroll) = modifier.scroll {
425 return Some(match scroll.axis() {
426 ScrollAxis::Vertical => FlexDirection::Column,
427 ScrollAxis::Horizontal => FlexDirection::Row,
428 ScrollAxis::Both => FlexDirection::Column,
429 });
430 }
431 match kind {
432 ViewKind::Row => {
433 if repose_core::locals::text_direction() == repose_core::locals::TextDirection::Rtl {
434 Some(FlexDirection::RowReverse)
435 } else {
436 Some(FlexDirection::Row)
437 }
438 }
439 ViewKind::Column => Some(FlexDirection::Column),
440 _ => None,
441 }
442}
443
444pub trait ViewExt: Sized {
446 fn child(self, children: impl IntoChildren) -> Self;
447}
448
449impl ViewExt for View {
450 fn child(mut self, children: impl IntoChildren) -> Self {
451 self.children.extend(children.into_children());
452 self
453 }
454}
455
456pub trait IntoChildren {
457 fn into_children(self) -> Vec<View>;
458}
459
460impl IntoChildren for View {
461 fn into_children(self) -> Vec<View> {
462 vec![self]
463 }
464}
465
466impl IntoChildren for Vec<View> {
467 fn into_children(self) -> Vec<View> {
468 self
469 }
470}
471
472impl<const N: usize> IntoChildren for [View; N] {
473 fn into_children(self) -> Vec<View> {
474 self.into()
475 }
476}
477
478macro_rules! impl_into_children_tuple {
480 ($($idx:tt $t:ident),+) => {
481 impl<$($t: IntoChildren),+> IntoChildren for ($($t,)+) {
482 fn into_children(self) -> Vec<View> {
483 let mut v = Vec::new();
484 $(v.extend(self.$idx.into_children());)+
485 v
486 }
487 }
488 };
489}
490
491impl_into_children_tuple!(0 A);
492impl_into_children_tuple!(0 A, 1 B);
493impl_into_children_tuple!(0 A, 1 B, 2 C);
494impl_into_children_tuple!(0 A, 1 B, 2 C, 3 D);
495impl_into_children_tuple!(0 A, 1 B, 2 C, 3 D, 4 E);
496impl_into_children_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F);
497impl_into_children_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G);
498impl_into_children_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H);
499
500pub fn layout_and_paint(
503 root: &View,
504 size_px_u32: (u32, u32),
505 textfield_states: &HashMap<u64, Rc<RefCell<TextFieldState>>>,
506 interactions: &Interactions,
507 focused: Option<u64>,
508) -> (Scene, Vec<HitRegion>, Vec<SemNode>) {
509 LAYOUT_ENGINE.with(|engine| {
510 engine
511 .borrow_mut()
512 .layout_frame(root, size_px_u32, textfield_states, interactions, focused)
513 })
514}
515
516pub fn last_layout_stats() -> layout::LayoutStats {
520 LAYOUT_ENGINE.with(|engine| engine.borrow().stats.clone())
521}
522
523pub use layout::LayoutStats;
524
525pub trait TextStyle {
527 fn color(self, c: Color) -> View;
528 fn size(self, px: f32) -> View;
529 fn max_lines(self, n: usize) -> View;
530 fn single_line(self) -> View;
531 fn overflow_ellipsize(self) -> View;
532 fn overflow_clip(self) -> View;
533 fn overflow_visible(self) -> View;
534 fn font_family(self, family: &'static str) -> View;
535 fn text_align(self, align: TextAlign) -> View;
536 fn font_weight(self, weight: FontWeight) -> View;
537 fn font_style(self, style: FontStyle) -> View;
538 fn text_decoration(self, decoration: TextDecoration) -> View;
539 fn letter_spacing(self, spacing: f32) -> View;
540 fn line_height(self, height: f32) -> View;
541 fn url(self, url: impl Into<std::sync::Arc<str>>) -> View;
542 fn font_variation_settings(self, settings: &str) -> View;
543}
544impl TextStyle for View {
545 fn color(mut self, c: Color) -> View {
546 if let ViewKind::Text {
547 color: text_color, ..
548 } = &mut self.kind
549 {
550 *text_color = c;
551 }
552 self
553 }
554 fn size(mut self, dp_font: f32) -> View {
555 if let ViewKind::Text {
556 font_size: text_size_dp,
557 ..
558 } = &mut self.kind
559 {
560 *text_size_dp = dp_font;
561 }
562 self
563 }
564 fn max_lines(mut self, n: usize) -> View {
565 if let ViewKind::Text {
566 max_lines,
567 soft_wrap,
568 ..
569 } = &mut self.kind
570 {
571 *max_lines = Some(n);
572 *soft_wrap = true;
573 }
574 self
575 }
576 fn single_line(mut self) -> View {
577 if let ViewKind::Text {
578 soft_wrap,
579 max_lines,
580 ..
581 } = &mut self.kind
582 {
583 *soft_wrap = false;
584 *max_lines = Some(1);
585 }
586 self
587 }
588 fn overflow_ellipsize(mut self) -> View {
589 if let ViewKind::Text { overflow, .. } = &mut self.kind {
590 *overflow = TextOverflow::Ellipsis;
591 }
592 self
593 }
594 fn overflow_clip(mut self) -> View {
595 if let ViewKind::Text { overflow, .. } = &mut self.kind {
596 *overflow = TextOverflow::Clip;
597 }
598 self
599 }
600 fn overflow_visible(mut self) -> View {
601 if let ViewKind::Text { overflow, .. } = &mut self.kind {
602 *overflow = TextOverflow::Visible;
603 }
604 self
605 }
606 fn font_family(mut self, family: &'static str) -> View {
607 if let ViewKind::Text {
608 font_family: ff, ..
609 } = &mut self.kind
610 {
611 *ff = Some(family);
612 }
613 self
614 }
615 fn text_align(mut self, align: TextAlign) -> View {
616 if let ViewKind::Text { text_align, .. } = &mut self.kind {
617 *text_align = align;
618 }
619 self
620 }
621 fn font_weight(mut self, weight: FontWeight) -> View {
622 if let ViewKind::Text { font_weight, .. } = &mut self.kind {
623 *font_weight = weight;
624 }
625 self
626 }
627 fn font_style(mut self, style: FontStyle) -> View {
628 if let ViewKind::Text { font_style, .. } = &mut self.kind {
629 *font_style = style;
630 }
631 self
632 }
633 fn text_decoration(mut self, decoration: TextDecoration) -> View {
634 if let ViewKind::Text {
635 text_decoration, ..
636 } = &mut self.kind
637 {
638 *text_decoration = decoration;
639 }
640 self
641 }
642 fn letter_spacing(mut self, spacing: f32) -> View {
643 if let ViewKind::Text { letter_spacing, .. } = &mut self.kind {
644 *letter_spacing = spacing;
645 }
646 self
647 }
648 fn line_height(mut self, height: f32) -> View {
649 if let ViewKind::Text { line_height, .. } = &mut self.kind {
650 *line_height = height;
651 }
652 self
653 }
654 fn url(mut self, url: impl Into<std::sync::Arc<str>>) -> View {
655 if let ViewKind::Text {
656 url: u,
657 text_decoration,
658 ..
659 } = &mut self.kind
660 {
661 *u = Some(url.into());
662 if !text_decoration.underline && !text_decoration.strikethrough {
663 *text_decoration = TextDecoration::UNDERLINE;
664 }
665 }
666 self
667 }
668 fn font_variation_settings(mut self, settings: &str) -> View {
669 if let ViewKind::Text {
670 font_variation_settings,
671 ..
672 } = &mut self.kind
673 {
674 *font_variation_settings = Some(settings.into());
675 }
676 self
677 }
678}