1#![allow(non_snake_case)]
2pub mod adaptive;
87pub mod anim;
88pub mod anim_ext;
89pub mod color_picker;
90pub(crate) mod hit_testing;
91pub use hit_testing::{
92 HitRegionSnapshot, dispatch_keyboard_action, hit_region_contains, hit_region_local_rect,
93 hit_region_pointer_coordinates, hit_region_to_local, hit_region_transformed_origin,
94 hit_region_world_rect, hit_test_enabled_frame, hit_test_frame, hit_test_frame_path,
95 hit_test_frame_regions,
96};
97pub mod layout;
98pub use layout::IntrinsicSizeMode;
99pub mod lazy;
100pub mod selection;
101pub mod subcompose;
102pub use lazy::{
103 LazyColumn, LazyHorizontalGrid, LazyRow, LazyVerticalGrid, LazyVerticalStaggeredGrid,
104 SimpleList,
105};
106pub mod lazy_states;
107pub use lazy_states::{
108 ItemHeight, LazyColumnConfig, LazyColumnState, LazyGridConfig, LazyGridState, LazyRowConfig,
109 LazyRowState, LazyVerticalStaggeredGridConfig, LazyVerticalStaggeredGridState,
110};
111pub use subcompose::{
112 BoxWithConstraints, SubcomposeLayout, box_with_constraints_with_key, subcompose_hash_key,
113 subcompose_layout_with_slots, subcompose_with_key, subcompose_with_key_slots,
114};
115pub mod overlay;
116pub mod pager;
117pub mod scroll;
118pub mod scrollbar_fade;
119pub mod scrollbar_repeat;
120pub mod window_v2;
121pub mod windowing;
122
123use std::cell::RefCell;
124use std::collections::{HashMap, HashSet};
125use std::rc::Rc;
126use std::sync::atomic::{AtomicU64, Ordering};
127
128use repose_core::*;
129
130pub(crate) mod text_selection;
131pub mod textfield;
132use repose_core::locals;
133pub use selection::{SelectableText, SelectableTextExt};
134pub use textfield::{
135 BasicSecureTextField, BasicTextField, KeyboardOptions, TextFieldConfig, TextFieldState,
136};
137
138thread_local! {
139 static LAYOUT_ENGINE: RefCell<layout::LayoutEngine> =
140 RefCell::new(layout::LayoutEngine::new());
141}
142
143#[derive(Default)]
144pub struct Interactions {
145 pub hover: Option<u64>,
146 pub hover_ancestors: std::collections::HashSet<u64>,
147 pub pressed: HashSet<u64>,
148}
149
150pub fn Box(modifier: Modifier) -> View {
151 View::new(0, ViewKind::Box).modifier(modifier)
152}
153
154pub fn Row(modifier: Modifier) -> View {
155 View::new(0, ViewKind::Row).modifier(modifier)
156}
157
158pub struct RowScope {
172 _private: (),
173}
174
175impl RowScope {
176 pub fn align_by_baseline(&self, view: View) -> View {
180 self.align_by(view, BaselineAlign::FirstBaseline)
181 }
182
183 pub fn align_by_last_baseline(&self, view: View) -> View {
186 self.align_by(view, BaselineAlign::LastBaseline)
187 }
188
189 pub fn align_by(&self, mut view: View, line: BaselineAlign) -> View {
191 view.modifier.baseline_align = Some(line);
192 view
193 }
194}
195
196pub fn row_scope<R>(content: impl FnOnce(&RowScope) -> R) -> R {
200 content(&RowScope { _private: () })
201}
202
203pub fn Column(modifier: Modifier) -> View {
204 View::new(0, ViewKind::Column).modifier(modifier)
205}
206
207#[derive(Clone, Copy, Debug, PartialEq)]
210pub struct FlowRowConfig {
211 pub balanced: bool,
214 pub min_lines: u16,
217}
218
219impl Default for FlowRowConfig {
220 fn default() -> Self {
221 Self {
222 balanced: false,
223 min_lines: 1,
224 }
225 }
226}
227
228#[derive(Clone, Copy, Debug, PartialEq)]
230pub struct FlowColumnConfig {
231 pub balanced: bool,
233 pub min_lines: u16,
235}
236
237impl Default for FlowColumnConfig {
238 fn default() -> Self {
239 Self {
240 balanced: false,
241 min_lines: 1,
242 }
243 }
244}
245
246pub fn FlowRow(modifier: Modifier, config: FlowRowConfig) -> View {
250 let mut modifier = modifier.flex_wrap(if config.balanced {
251 FlexWrap::Balance
252 } else {
253 FlexWrap::Wrap
254 });
255 modifier.flex_line_count = Some(config.min_lines.max(1));
256 Row(modifier)
257}
258
259#[deprecated = "Use Column instead (identical behavior)"]
262pub fn Stack(modifier: Modifier) -> View {
263 Column(modifier)
264}
265
266pub fn FlowColumn(modifier: Modifier, config: FlowColumnConfig) -> View {
270 let mut modifier = modifier.flex_wrap(if config.balanced {
271 FlexWrap::Balance
272 } else {
273 FlexWrap::Wrap
274 });
275 modifier.flex_line_count = Some(config.min_lines.max(1));
276 Column(modifier)
277}
278
279pub fn Center(modifier: Modifier) -> View {
284 Box(modifier.content_alignment_safe(Alignment::Center))
285}
286
287pub fn ZStack(modifier: Modifier) -> View {
288 View::new(0, ViewKind::ZStack).modifier(modifier)
289}
290
291pub fn OverlayHost(modifier: Modifier) -> View {
292 View::new(0, ViewKind::OverlayHost).modifier(modifier)
293}
294
295#[deprecated = "Use Modifier::vertical_scroll instead"]
296pub fn Scroll(modifier: Modifier) -> View {
297 View::new(0, ViewKind::Box).modifier(modifier.vertical_scroll(ScrollAxisBinding {
298 show_scrollbar: true,
299 ..Default::default()
300 }))
301}
302
303pub fn Text(text: impl Into<String>) -> View {
304 View::new(
305 0,
306 ViewKind::Text {
307 text: text.into(),
308 color: locals::content_color(),
309 font_size: locals::text_size().unwrap_or(Sp(16.0)),
311 soft_wrap: true,
312 max_lines: None,
313 overflow: TextOverflow::Clip,
314 font_family: Some("sans-serif"),
315 annotations: None,
316 text_align: TextAlign::Start,
317 font_weight: FontWeight::NORMAL,
318 font_style: FontStyle::Normal,
319 text_decoration: TextDecoration::default(),
320 letter_spacing: Sp::ZERO,
321 line_height: Sp::ZERO,
322 url: None,
323 font_variation_settings: None,
324 draw_style: DrawStyle::Fill,
325 },
326 )
327}
328
329pub fn AnnotatedText(annotated: AnnotatedString) -> View {
333 let annotations: Option<std::sync::Arc<[TextSpan]>> = if annotated.spans.is_empty() {
334 None
335 } else {
336 Some(annotated.spans.clone())
337 };
338 View::new(
339 0,
340 ViewKind::Text {
341 text: annotated.text,
342 color: locals::content_color(),
343 font_size: locals::text_size().unwrap_or(Sp(16.0)),
344 soft_wrap: true,
345 max_lines: None,
346 overflow: TextOverflow::Clip,
347 font_family: Some("sans-serif"),
348 annotations,
349 text_align: TextAlign::Start,
350 font_weight: FontWeight::NORMAL,
351 font_style: FontStyle::Normal,
352 text_decoration: TextDecoration::default(),
353 letter_spacing: Sp::ZERO,
354 line_height: Sp::ZERO,
355 url: None,
356 font_variation_settings: None,
357 draw_style: DrawStyle::Fill,
358 },
359 )
360}
361
362pub fn Spacer() -> View {
363 Box(Modifier::new().flex_grow(1.0))
364}
365
366pub fn Space(modifier: Modifier) -> View {
367 Box(modifier)
368}
369
370pub fn Grid(
371 columns: usize,
372 modifier: Modifier,
373 children: Vec<View>,
374 row_gap: Dp,
375 column_gap: Dp,
376) -> View {
377 Column(modifier.grid(columns, row_gap, column_gap)).with_children(children)
378}
379
380static DRAGVALUE_COUNTER: AtomicU64 = AtomicU64::new(0);
381
382pub fn DragValue(
387 value: f32,
388 range: (f32, f32),
389 speed: f32,
390 on_change: impl Fn(f32) + 'static,
391) -> View {
392 let id = DRAGVALUE_COUNTER.fetch_add(1, Ordering::Relaxed);
393 let drag_start_x = remember_mutable_with_key(format!("dv_dsx_{}", id), || 0.0f32);
394 let drag_start_val = remember_mutable_with_key(format!("dv_dsv_{}", id), || 0.0f32);
395 let is_dragging = remember_mutable_with_key(format!("dv_drg_{}", id), || false);
396
397 let oc = Rc::new(on_change);
398 let min = range.0;
399 let max = range.1;
400 let cur = value;
401
402 let th = locals::theme();
403
404 Box(Modifier::new()
405 .min_width(Dp(48.0))
406 .height(Dp(28.0))
407 .background(th.surface_container)
408 .border(Dp(1.0), th.outline, Dp(4.0))
409 .clip_rounded(Dp(4.0))
410 .padding_values(PaddingValues {
411 left: Dp(4.0),
412 right: Dp(4.0),
413 top: Dp::ZERO,
414 bottom: Dp::ZERO,
415 })
416 .on_pointer_down({
417 let dsx = drag_start_x.clone();
418 let dsv = drag_start_val.clone();
419 let drg = is_dragging.clone();
420 move |pe: PointerEvent| {
421 drg.set(true);
422 dsx.set(pe.position_in_window().x);
423 dsv.set(cur);
424 }
425 })
426 .on_pointer_move({
427 let dsx = drag_start_x.clone();
428 let dsv = drag_start_val.clone();
429 let drg = is_dragging.clone();
430 let oc = oc.clone();
431 move |pe: PointerEvent| {
432 if !drg.with(|v| *v) {
433 return;
434 }
435 let start_x = dsx.with(|v| *v);
436 let start_val = dsv.with(|v| *v);
437 let new_val =
438 (start_val + (pe.position_in_window().x - start_x) * speed).clamp(min, max);
439 (oc)(new_val);
440 }
441 })
442 .on_pointer_up({
443 let drg = is_dragging.clone();
444 move |_pe: PointerEvent| {
445 drg.set(false);
446 }
447 })
448 .on_pointer_cancel({
449 let drg = is_dragging.clone();
450 move |_pe: PointerEvent| {
451 drg.set(false);
452 }
453 })
454 .cursor(CursorIcon::EwResize))
455 .child(
456 Text(format_value(value))
457 .size(Sp(13.0))
458 .color(th.on_surface)
459 .single_line()
460 .overflow_ellipsize(),
461 )
462}
463
464fn format_value(v: f32) -> String {
465 if (v - v.round()).abs() < 1e-6 {
466 format!("{}", v.round() as i64)
467 } else if (v * 10.0 - (v * 10.0).round()).abs() < 1e-6 {
468 format!("{:.1}", v)
469 } else {
470 format!("{:.2}", v)
471 }
472}
473
474pub fn Image(modifier: Modifier, handle: ImageHandle) -> View {
475 View::new(
476 0,
477 ViewKind::Image {
478 handle,
479 tint: Color::WHITE,
480 fit: ImageFit::Contain,
481 filter: ImageFilter::Linear,
482 source_rect: None,
483 alignment: ImageAlignment::Center,
484 },
485 )
486 .modifier(modifier)
487}
488
489pub fn Embedded(modifier: Modifier, payload: PaintCallbackPayload) -> View {
491 let mut m = modifier.paint_callback(payload);
492 let has_size = m.size.is_some()
493 || m.width.is_some()
494 || m.height.is_some()
495 || m.fill_max.is_some()
496 || m.fill_max_w.is_some()
497 || m.fill_max_h.is_some();
498 if !has_size {
499 m = m.size(Dp(100.0), Dp(100.0));
500 }
501 Box(m)
502}
503
504pub trait ImageExt {
505 fn image_tint(self, c: Color) -> View;
506 fn image_fit(self, fit: ImageFit) -> View;
507 fn image_filter(self, filter: ImageFilter) -> View;
508 fn image_source_rect(self, source_rect: ImageSourceRect) -> View;
509 fn image_alignment(self, alignment: ImageAlignment) -> View;
510}
511impl ImageExt for View {
512 fn image_tint(mut self, c: Color) -> View {
513 if let ViewKind::Image { tint, .. } = &mut self.kind {
514 *tint = c;
515 }
516 self
517 }
518 fn image_fit(mut self, fit: ImageFit) -> View {
519 if let ViewKind::Image { fit: f, .. } = &mut self.kind {
520 *f = fit;
521 }
522 self
523 }
524 fn image_filter(mut self, filter: ImageFilter) -> View {
525 if let ViewKind::Image { filter: f, .. } = &mut self.kind {
526 *f = filter;
527 }
528 self
529 }
530 fn image_source_rect(mut self, source: ImageSourceRect) -> View {
531 if let ViewKind::Image { source_rect, .. } = &mut self.kind {
532 *source_rect = Some(source);
533 }
534 self
535 }
536 fn image_alignment(mut self, alignment: ImageAlignment) -> View {
537 if let ViewKind::Image {
538 alignment: current, ..
539 } = &mut self.kind
540 {
541 *current = alignment;
542 }
543 self
544 }
545}
546
547pub trait ViewExt: Sized {
549 fn child(self, children: impl IntoChildren) -> Self;
550}
551
552impl ViewExt for View {
553 fn child(mut self, children: impl IntoChildren) -> Self {
554 self.children.extend(children.into_children());
555 self
556 }
557}
558
559pub trait IntoChildren {
560 fn into_children(self) -> Vec<View>;
561}
562
563impl IntoChildren for View {
564 fn into_children(self) -> Vec<View> {
565 vec![self]
566 }
567}
568
569impl IntoChildren for Vec<View> {
570 fn into_children(self) -> Vec<View> {
571 self
572 }
573}
574
575impl<const N: usize> IntoChildren for [View; N] {
576 fn into_children(self) -> Vec<View> {
577 self.into()
578 }
579}
580
581macro_rules! impl_into_children_tuple {
583 ($($idx:tt $t:ident),+) => {
584 impl<$($t: IntoChildren),+> IntoChildren for ($($t,)+) {
585 fn into_children(self) -> Vec<View> {
586 let mut v = Vec::new();
587 $(v.extend(self.$idx.into_children());)+
588 v
589 }
590 }
591 };
592}
593
594impl_into_children_tuple!(0 A);
595impl_into_children_tuple!(0 A, 1 B);
596impl_into_children_tuple!(0 A, 1 B, 2 C);
597impl_into_children_tuple!(0 A, 1 B, 2 C, 3 D);
598impl_into_children_tuple!(0 A, 1 B, 2 C, 3 D, 4 E);
599impl_into_children_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F);
600impl_into_children_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G);
601impl_into_children_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H);
602
603pub fn layout_and_paint(
606 root: &View,
607 size_px_u32: (u32, u32),
608 textfield_states: &HashMap<u64, Rc<RefCell<TextFieldState>>>,
609 interactions: &Interactions,
610 focused: Option<u64>,
611) -> (Scene, Vec<HitRegion>, Vec<SemNode>) {
612 let result = LAYOUT_ENGINE.with(|engine| {
613 engine
614 .borrow_mut()
615 .layout_frame(root, size_px_u32, textfield_states, interactions, focused)
616 });
617 textfield::prune_textfield_registries();
618 result
619}
620
621pub fn last_layout_stats() -> layout::LayoutStats {
625 LAYOUT_ENGINE.with(|engine| engine.borrow().stats.clone())
626}
627
628pub use layout::LayoutStats;
629
630pub trait TextStyle {
632 fn color(self, c: Color) -> View;
633 fn size(self, size: Sp) -> View;
634 fn max_lines(self, n: usize) -> View;
635 fn single_line(self) -> View;
636 fn overflow_ellipsize(self) -> View;
637 fn overflow_clip(self) -> View;
638 fn overflow_visible(self) -> View;
639 fn font_family(self, family: &'static str) -> View;
640 fn text_align(self, align: TextAlign) -> View;
641 fn font_weight(self, weight: FontWeight) -> View;
642 fn font_style(self, style: FontStyle) -> View;
643 fn text_decoration(self, decoration: TextDecoration) -> View;
644 fn letter_spacing(self, spacing: Sp) -> View;
645 fn line_height(self, height: Sp) -> View;
646 fn url(self, url: impl Into<std::sync::Arc<str>>) -> View;
647 fn font_variation_settings(self, settings: &str) -> View;
648 fn draw_style(self, style: DrawStyle) -> View;
649 fn fill_and_stroke(self, width: f32) -> View;
652 fn outlined(self, width: f32, color: Color) -> View;
657}
658impl TextStyle for View {
659 fn color(mut self, c: Color) -> View {
660 if let ViewKind::Text {
661 color: text_color, ..
662 } = &mut self.kind
663 {
664 *text_color = c;
665 }
666 self
667 }
668 fn size(mut self, size: Sp) -> View {
669 if let ViewKind::Text {
670 font_size: text_size_sp,
671 ..
672 } = &mut self.kind
673 {
674 *text_size_sp = size;
675 }
676 self
677 }
678 fn max_lines(mut self, n: usize) -> View {
679 if let ViewKind::Text {
680 max_lines,
681 soft_wrap,
682 ..
683 } = &mut self.kind
684 {
685 *max_lines = Some(n);
686 *soft_wrap = true;
687 }
688 self
689 }
690 fn single_line(mut self) -> View {
691 if let ViewKind::Text {
692 soft_wrap,
693 max_lines,
694 ..
695 } = &mut self.kind
696 {
697 *soft_wrap = false;
698 *max_lines = Some(1);
699 }
700 self
701 }
702 fn overflow_ellipsize(mut self) -> View {
703 if let ViewKind::Text { overflow, .. } = &mut self.kind {
704 *overflow = TextOverflow::Ellipsis;
705 }
706 self
707 }
708 fn overflow_clip(mut self) -> View {
709 if let ViewKind::Text { overflow, .. } = &mut self.kind {
710 *overflow = TextOverflow::Clip;
711 }
712 self
713 }
714 fn overflow_visible(mut self) -> View {
715 if let ViewKind::Text { overflow, .. } = &mut self.kind {
716 *overflow = TextOverflow::Visible;
717 }
718 self
719 }
720 fn font_family(mut self, family: &'static str) -> View {
721 if let ViewKind::Text {
722 font_family: ff, ..
723 } = &mut self.kind
724 {
725 *ff = Some(family);
726 }
727 self
728 }
729 fn text_align(mut self, align: TextAlign) -> View {
730 if let ViewKind::Text { text_align, .. } = &mut self.kind {
731 *text_align = align;
732 }
733 self
734 }
735 fn font_weight(mut self, weight: FontWeight) -> View {
736 if let ViewKind::Text { font_weight, .. } = &mut self.kind {
737 *font_weight = weight;
738 }
739 self
740 }
741 fn font_style(mut self, style: FontStyle) -> View {
742 if let ViewKind::Text { font_style, .. } = &mut self.kind {
743 *font_style = style;
744 }
745 self
746 }
747 fn text_decoration(mut self, decoration: TextDecoration) -> View {
748 if let ViewKind::Text {
749 text_decoration, ..
750 } = &mut self.kind
751 {
752 *text_decoration = decoration;
753 }
754 self
755 }
756 fn letter_spacing(mut self, spacing: Sp) -> View {
757 if let ViewKind::Text { letter_spacing, .. } = &mut self.kind {
758 *letter_spacing = spacing;
759 }
760 self
761 }
762 fn line_height(mut self, height: Sp) -> View {
763 if let ViewKind::Text { line_height, .. } = &mut self.kind {
764 *line_height = height;
765 }
766 self
767 }
768 fn url(mut self, url: impl Into<std::sync::Arc<str>>) -> View {
769 if let ViewKind::Text {
770 url: u,
771 text_decoration,
772 ..
773 } = &mut self.kind
774 {
775 *u = Some(url.into());
776 if !text_decoration.underline && !text_decoration.strikethrough {
777 *text_decoration = TextDecoration::UNDERLINE;
778 }
779 }
780 self
781 }
782 fn font_variation_settings(mut self, settings: &str) -> View {
783 if let ViewKind::Text {
784 font_variation_settings,
785 ..
786 } = &mut self.kind
787 {
788 *font_variation_settings = Some(settings.into());
789 }
790 self
791 }
792 fn draw_style(mut self, style: DrawStyle) -> View {
793 if let ViewKind::Text { draw_style, .. } = &mut self.kind {
794 *draw_style = style;
795 }
796 self
797 }
798 fn fill_and_stroke(mut self, width: f32) -> View {
799 if let ViewKind::Text { draw_style, .. } = &mut self.kind {
800 *draw_style = DrawStyle::fill_and_stroke(width);
801 }
802 self
803 }
804 fn outlined(mut self, width: f32, color: Color) -> View {
805 if let ViewKind::Text { draw_style, .. } = &mut self.kind {
806 *draw_style = DrawStyle::fill_and_stroke_with_outline(width, color);
807 }
808 self
809 }
810}