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 window_v2;
113pub mod windowing;
114
115use std::cell::RefCell;
116use std::collections::{HashMap, HashSet};
117use std::rc::Rc;
118use std::sync::atomic::{AtomicU64, Ordering};
119
120use repose_core::*;
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 hover_ancestors: std::collections::HashSet<u64>,
138 pub pressed: HashSet<u64>,
139}
140
141pub fn Box(modifier: Modifier) -> View {
142 View::new(0, ViewKind::Box).modifier(modifier)
143}
144
145pub fn Row(modifier: Modifier) -> View {
146 View::new(0, ViewKind::Row).modifier(modifier)
147}
148
149pub struct RowScope {
163 _private: (),
164}
165
166impl RowScope {
167 pub fn align_by_baseline(&self, view: View) -> View {
171 self.align_by(view, BaselineAlign::FirstBaseline)
172 }
173
174 pub fn align_by_last_baseline(&self, view: View) -> View {
177 self.align_by(view, BaselineAlign::LastBaseline)
178 }
179
180 pub fn align_by(&self, mut view: View, line: BaselineAlign) -> View {
182 view.modifier.baseline_align = Some(line);
183 view
184 }
185}
186
187pub fn row_scope<R>(content: impl FnOnce(&RowScope) -> R) -> R {
191 content(&RowScope { _private: () })
192}
193
194pub fn Column(modifier: Modifier) -> View {
195 View::new(0, ViewKind::Column).modifier(modifier)
196}
197
198#[derive(Clone, Copy, Debug, PartialEq)]
201pub struct FlowRowConfig {
202 pub balanced: bool,
205 pub min_lines: u16,
208}
209
210impl Default for FlowRowConfig {
211 fn default() -> Self {
212 Self {
213 balanced: false,
214 min_lines: 1,
215 }
216 }
217}
218
219#[derive(Clone, Copy, Debug, PartialEq)]
221pub struct FlowColumnConfig {
222 pub balanced: bool,
224 pub min_lines: u16,
226}
227
228impl Default for FlowColumnConfig {
229 fn default() -> Self {
230 Self {
231 balanced: false,
232 min_lines: 1,
233 }
234 }
235}
236
237pub fn FlowRow(modifier: Modifier, config: FlowRowConfig) -> View {
241 let mut modifier = modifier.flex_wrap(if config.balanced {
242 FlexWrap::Balance
243 } else {
244 FlexWrap::Wrap
245 });
246 modifier.flex_line_count = Some(config.min_lines.max(1));
247 Row(modifier)
248}
249
250#[deprecated = "Use Column instead (identical behavior)"]
253pub fn Stack(modifier: Modifier) -> View {
254 Column(modifier)
255}
256
257pub fn FlowColumn(modifier: Modifier, config: FlowColumnConfig) -> View {
261 let mut modifier = modifier.flex_wrap(if config.balanced {
262 FlexWrap::Balance
263 } else {
264 FlexWrap::Wrap
265 });
266 modifier.flex_line_count = Some(config.min_lines.max(1));
267 Column(modifier)
268}
269
270pub fn Center(modifier: Modifier) -> View {
275 Box(modifier.content_alignment_safe(Alignment::Center))
276}
277
278pub fn ZStack(modifier: Modifier) -> View {
279 View::new(0, ViewKind::ZStack).modifier(modifier)
280}
281
282pub fn OverlayHost(modifier: Modifier) -> View {
283 View::new(0, ViewKind::OverlayHost).modifier(modifier)
284}
285
286#[deprecated = "Use Modifier::vertical_scroll instead"]
287pub fn Scroll(modifier: Modifier) -> View {
288 View::new(0, ViewKind::Box).modifier(modifier.vertical_scroll(ScrollAxisBinding {
289 show_scrollbar: true,
290 ..Default::default()
291 }))
292}
293
294pub fn Text(text: impl Into<String>) -> View {
295 View::new(
296 0,
297 ViewKind::Text {
298 text: text.into(),
299 color: locals::content_color(),
300 font_size: locals::text_size().unwrap_or(Sp(16.0)),
302 soft_wrap: true,
303 max_lines: None,
304 overflow: TextOverflow::Clip,
305 font_family: Some("sans-serif"),
306 annotations: None,
307 text_align: TextAlign::Start,
308 font_weight: FontWeight::NORMAL,
309 font_style: FontStyle::Normal,
310 text_decoration: TextDecoration::default(),
311 letter_spacing: Sp::ZERO,
312 line_height: Sp::ZERO,
313 url: None,
314 font_variation_settings: None,
315 draw_style: DrawStyle::Fill,
316 },
317 )
318}
319
320pub fn AnnotatedText(annotated: AnnotatedString) -> View {
324 let annotations: Option<std::sync::Arc<[TextSpan]>> = if annotated.spans.is_empty() {
325 None
326 } else {
327 Some(annotated.spans.clone())
328 };
329 View::new(
330 0,
331 ViewKind::Text {
332 text: annotated.text,
333 color: locals::content_color(),
334 font_size: locals::text_size().unwrap_or(Sp(16.0)),
335 soft_wrap: true,
336 max_lines: None,
337 overflow: TextOverflow::Clip,
338 font_family: Some("sans-serif"),
339 annotations,
340 text_align: TextAlign::Start,
341 font_weight: FontWeight::NORMAL,
342 font_style: FontStyle::Normal,
343 text_decoration: TextDecoration::default(),
344 letter_spacing: Sp::ZERO,
345 line_height: Sp::ZERO,
346 url: None,
347 font_variation_settings: None,
348 draw_style: DrawStyle::Fill,
349 },
350 )
351}
352
353pub fn Spacer() -> View {
354 Box(Modifier::new().flex_grow(1.0))
355}
356
357pub fn Space(modifier: Modifier) -> View {
358 Box(modifier)
359}
360
361pub fn Grid(
362 columns: usize,
363 modifier: Modifier,
364 children: Vec<View>,
365 row_gap: Dp,
366 column_gap: Dp,
367) -> View {
368 Column(modifier.grid(columns, row_gap, column_gap)).with_children(children)
369}
370
371static DRAGVALUE_COUNTER: AtomicU64 = AtomicU64::new(0);
372
373pub fn DragValue(
378 value: f32,
379 range: (f32, f32),
380 speed: f32,
381 on_change: impl Fn(f32) + 'static,
382) -> View {
383 let id = DRAGVALUE_COUNTER.fetch_add(1, Ordering::Relaxed);
384 let drag_start_x = remember_mutable_with_key(format!("dv_dsx_{}", id), || 0.0f32);
385 let drag_start_val = remember_mutable_with_key(format!("dv_dsv_{}", id), || 0.0f32);
386 let is_dragging = remember_mutable_with_key(format!("dv_drg_{}", id), || false);
387
388 let oc = Rc::new(on_change);
389 let min = range.0;
390 let max = range.1;
391 let cur = value;
392
393 let th = locals::theme();
394
395 Box(Modifier::new()
396 .min_width(Dp(48.0))
397 .height(Dp(28.0))
398 .background(th.surface_container)
399 .border(Dp(1.0), th.outline, Dp(4.0))
400 .clip_rounded(Dp(4.0))
401 .padding_values(PaddingValues {
402 left: Dp(4.0),
403 right: Dp(4.0),
404 top: Dp::ZERO,
405 bottom: Dp::ZERO,
406 })
407 .on_pointer_down({
408 let dsx = drag_start_x.clone();
409 let dsv = drag_start_val.clone();
410 let drg = is_dragging.clone();
411 move |pe: PointerEvent| {
412 drg.set(true);
413 dsx.set(pe.position_in_window().x);
414 dsv.set(cur);
415 }
416 })
417 .on_pointer_move({
418 let dsx = drag_start_x.clone();
419 let dsv = drag_start_val.clone();
420 let drg = is_dragging.clone();
421 let oc = oc.clone();
422 move |pe: PointerEvent| {
423 if !drg.with(|v| *v) {
424 return;
425 }
426 let start_x = dsx.with(|v| *v);
427 let start_val = dsv.with(|v| *v);
428 let new_val =
429 (start_val + (pe.position_in_window().x - start_x) * speed).clamp(min, max);
430 (oc)(new_val);
431 }
432 })
433 .on_pointer_up({
434 let drg = is_dragging.clone();
435 move |_pe: PointerEvent| {
436 drg.set(false);
437 }
438 })
439 .cursor(CursorIcon::EwResize))
440 .child(
441 Text(format_value(value))
442 .size(Sp(13.0))
443 .color(th.on_surface)
444 .single_line()
445 .overflow_ellipsize(),
446 )
447}
448
449fn format_value(v: f32) -> String {
450 if (v - v.round()).abs() < 1e-6 {
451 format!("{}", v.round() as i64)
452 } else if (v * 10.0 - (v * 10.0).round()).abs() < 1e-6 {
453 format!("{:.1}", v)
454 } else {
455 format!("{:.2}", v)
456 }
457}
458
459pub fn Image(modifier: Modifier, handle: ImageHandle) -> View {
460 View::new(
461 0,
462 ViewKind::Image {
463 handle,
464 tint: Color::WHITE,
465 fit: ImageFit::Contain,
466 },
467 )
468 .modifier(modifier)
469}
470
471pub fn Embedded(modifier: Modifier, payload: PaintCallbackPayload) -> View {
473 let mut m = modifier.paint_callback(payload);
474 let has_size = m.size.is_some()
475 || m.width.is_some()
476 || m.height.is_some()
477 || m.fill_max.is_some()
478 || m.fill_max_w.is_some()
479 || m.fill_max_h.is_some();
480 if !has_size {
481 m = m.size(Dp(100.0), Dp(100.0));
482 }
483 Box(m)
484}
485
486pub trait ImageExt {
487 fn image_tint(self, c: Color) -> View;
488 fn image_fit(self, fit: ImageFit) -> View;
489}
490impl ImageExt for View {
491 fn image_tint(mut self, c: Color) -> View {
492 if let ViewKind::Image { tint, .. } = &mut self.kind {
493 *tint = c;
494 }
495 self
496 }
497 fn image_fit(mut self, fit: ImageFit) -> View {
498 if let ViewKind::Image { fit: f, .. } = &mut self.kind {
499 *f = fit;
500 }
501 self
502 }
503}
504
505pub trait ViewExt: Sized {
507 fn child(self, children: impl IntoChildren) -> Self;
508}
509
510impl ViewExt for View {
511 fn child(mut self, children: impl IntoChildren) -> Self {
512 self.children.extend(children.into_children());
513 self
514 }
515}
516
517pub trait IntoChildren {
518 fn into_children(self) -> Vec<View>;
519}
520
521impl IntoChildren for View {
522 fn into_children(self) -> Vec<View> {
523 vec![self]
524 }
525}
526
527impl IntoChildren for Vec<View> {
528 fn into_children(self) -> Vec<View> {
529 self
530 }
531}
532
533impl<const N: usize> IntoChildren for [View; N] {
534 fn into_children(self) -> Vec<View> {
535 self.into()
536 }
537}
538
539macro_rules! impl_into_children_tuple {
541 ($($idx:tt $t:ident),+) => {
542 impl<$($t: IntoChildren),+> IntoChildren for ($($t,)+) {
543 fn into_children(self) -> Vec<View> {
544 let mut v = Vec::new();
545 $(v.extend(self.$idx.into_children());)+
546 v
547 }
548 }
549 };
550}
551
552impl_into_children_tuple!(0 A);
553impl_into_children_tuple!(0 A, 1 B);
554impl_into_children_tuple!(0 A, 1 B, 2 C);
555impl_into_children_tuple!(0 A, 1 B, 2 C, 3 D);
556impl_into_children_tuple!(0 A, 1 B, 2 C, 3 D, 4 E);
557impl_into_children_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F);
558impl_into_children_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G);
559impl_into_children_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H);
560
561pub fn layout_and_paint(
564 root: &View,
565 size_px_u32: (u32, u32),
566 textfield_states: &HashMap<u64, Rc<RefCell<TextFieldState>>>,
567 interactions: &Interactions,
568 focused: Option<u64>,
569) -> (Scene, Vec<HitRegion>, Vec<SemNode>) {
570 LAYOUT_ENGINE.with(|engine| {
571 engine
572 .borrow_mut()
573 .layout_frame(root, size_px_u32, textfield_states, interactions, focused)
574 })
575}
576
577pub fn last_layout_stats() -> layout::LayoutStats {
581 LAYOUT_ENGINE.with(|engine| engine.borrow().stats.clone())
582}
583
584pub use layout::LayoutStats;
585
586pub trait TextStyle {
588 fn color(self, c: Color) -> View;
589 fn size(self, size: Sp) -> View;
590 fn max_lines(self, n: usize) -> View;
591 fn single_line(self) -> View;
592 fn overflow_ellipsize(self) -> View;
593 fn overflow_clip(self) -> View;
594 fn overflow_visible(self) -> View;
595 fn font_family(self, family: &'static str) -> View;
596 fn text_align(self, align: TextAlign) -> View;
597 fn font_weight(self, weight: FontWeight) -> View;
598 fn font_style(self, style: FontStyle) -> View;
599 fn text_decoration(self, decoration: TextDecoration) -> View;
600 fn letter_spacing(self, spacing: Sp) -> View;
601 fn line_height(self, height: Sp) -> View;
602 fn url(self, url: impl Into<std::sync::Arc<str>>) -> View;
603 fn font_variation_settings(self, settings: &str) -> View;
604 fn draw_style(self, style: DrawStyle) -> View;
605 fn fill_and_stroke(self, width: f32) -> View;
608}
609impl TextStyle for View {
610 fn color(mut self, c: Color) -> View {
611 if let ViewKind::Text {
612 color: text_color, ..
613 } = &mut self.kind
614 {
615 *text_color = c;
616 }
617 self
618 }
619 fn size(mut self, size: Sp) -> View {
620 if let ViewKind::Text {
621 font_size: text_size_sp,
622 ..
623 } = &mut self.kind
624 {
625 *text_size_sp = size;
626 }
627 self
628 }
629 fn max_lines(mut self, n: usize) -> View {
630 if let ViewKind::Text {
631 max_lines,
632 soft_wrap,
633 ..
634 } = &mut self.kind
635 {
636 *max_lines = Some(n);
637 *soft_wrap = true;
638 }
639 self
640 }
641 fn single_line(mut self) -> View {
642 if let ViewKind::Text {
643 soft_wrap,
644 max_lines,
645 ..
646 } = &mut self.kind
647 {
648 *soft_wrap = false;
649 *max_lines = Some(1);
650 }
651 self
652 }
653 fn overflow_ellipsize(mut self) -> View {
654 if let ViewKind::Text { overflow, .. } = &mut self.kind {
655 *overflow = TextOverflow::Ellipsis;
656 }
657 self
658 }
659 fn overflow_clip(mut self) -> View {
660 if let ViewKind::Text { overflow, .. } = &mut self.kind {
661 *overflow = TextOverflow::Clip;
662 }
663 self
664 }
665 fn overflow_visible(mut self) -> View {
666 if let ViewKind::Text { overflow, .. } = &mut self.kind {
667 *overflow = TextOverflow::Visible;
668 }
669 self
670 }
671 fn font_family(mut self, family: &'static str) -> View {
672 if let ViewKind::Text {
673 font_family: ff, ..
674 } = &mut self.kind
675 {
676 *ff = Some(family);
677 }
678 self
679 }
680 fn text_align(mut self, align: TextAlign) -> View {
681 if let ViewKind::Text { text_align, .. } = &mut self.kind {
682 *text_align = align;
683 }
684 self
685 }
686 fn font_weight(mut self, weight: FontWeight) -> View {
687 if let ViewKind::Text { font_weight, .. } = &mut self.kind {
688 *font_weight = weight;
689 }
690 self
691 }
692 fn font_style(mut self, style: FontStyle) -> View {
693 if let ViewKind::Text { font_style, .. } = &mut self.kind {
694 *font_style = style;
695 }
696 self
697 }
698 fn text_decoration(mut self, decoration: TextDecoration) -> View {
699 if let ViewKind::Text {
700 text_decoration, ..
701 } = &mut self.kind
702 {
703 *text_decoration = decoration;
704 }
705 self
706 }
707 fn letter_spacing(mut self, spacing: Sp) -> View {
708 if let ViewKind::Text { letter_spacing, .. } = &mut self.kind {
709 *letter_spacing = spacing;
710 }
711 self
712 }
713 fn line_height(mut self, height: Sp) -> View {
714 if let ViewKind::Text { line_height, .. } = &mut self.kind {
715 *line_height = height;
716 }
717 self
718 }
719 fn url(mut self, url: impl Into<std::sync::Arc<str>>) -> View {
720 if let ViewKind::Text {
721 url: u,
722 text_decoration,
723 ..
724 } = &mut self.kind
725 {
726 *u = Some(url.into());
727 if !text_decoration.underline && !text_decoration.strikethrough {
728 *text_decoration = TextDecoration::UNDERLINE;
729 }
730 }
731 self
732 }
733 fn font_variation_settings(mut self, settings: &str) -> View {
734 if let ViewKind::Text {
735 font_variation_settings,
736 ..
737 } = &mut self.kind
738 {
739 *font_variation_settings = Some(settings.into());
740 }
741 self
742 }
743 fn draw_style(mut self, style: DrawStyle) -> View {
744 if let ViewKind::Text { draw_style, .. } = &mut self.kind {
745 *draw_style = style;
746 }
747 self
748 }
749 fn fill_and_stroke(mut self, width: f32) -> View {
750 if let ViewKind::Text { draw_style, .. } = &mut self.kind {
751 *draw_style = DrawStyle::fill_and_stroke(width);
752 }
753 self
754 }
755}