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;
113pub mod window_v2;
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 fn Column(modifier: Modifier) -> View {
150 View::new(0, ViewKind::Column).modifier(modifier)
151}
152
153pub fn FlowRow(modifier: Modifier) -> View {
156 Row(modifier.flex_wrap(FlexWrap::Wrap))
157}
158
159#[deprecated = "Use Column instead (identical behavior)"]
162pub fn Stack(modifier: Modifier) -> View {
163 Column(modifier)
164}
165
166pub fn FlowColumn(modifier: Modifier) -> View {
169 Column(modifier.flex_wrap(FlexWrap::Wrap))
170}
171
172pub fn Center(modifier: Modifier) -> View {
175 Box(modifier.content_alignment(Alignment::Center))
176}
177
178pub fn ZStack(modifier: Modifier) -> View {
179 View::new(0, ViewKind::ZStack).modifier(modifier)
180}
181
182pub fn OverlayHost(modifier: Modifier) -> View {
183 View::new(0, ViewKind::OverlayHost).modifier(modifier)
184}
185
186#[deprecated = "Use Modifier::vertical_scroll instead"]
187pub fn Scroll(modifier: Modifier) -> View {
188 View::new(0, ViewKind::Box).modifier(modifier.vertical_scroll(ScrollAxisBinding {
189 show_scrollbar: true,
190 ..Default::default()
191 }))
192}
193
194pub fn Text(text: impl Into<String>) -> View {
195 View::new(
196 0,
197 ViewKind::Text {
198 text: text.into(),
199 color: locals::content_color(),
200 font_size: locals::text_size().unwrap_or(16.0), soft_wrap: true,
202 max_lines: None,
203 overflow: TextOverflow::Clip,
204 font_family: Some("sans-serif"),
205 annotations: None,
206 text_align: TextAlign::Start,
207 font_weight: FontWeight::NORMAL,
208 font_style: FontStyle::Normal,
209 text_decoration: TextDecoration::default(),
210 letter_spacing: 0.0,
211 line_height: 0.0,
212 url: None,
213 font_variation_settings: None,
214 },
215 )
216}
217
218pub fn AnnotatedText(annotated: AnnotatedString) -> View {
222 let annotations: Option<std::sync::Arc<[TextSpan]>> = if annotated.spans.is_empty() {
223 None
224 } else {
225 Some(annotated.spans.clone())
226 };
227 View::new(
228 0,
229 ViewKind::Text {
230 text: annotated.text,
231 color: locals::content_color(),
232 font_size: locals::text_size().unwrap_or(16.0),
233 soft_wrap: true,
234 max_lines: None,
235 overflow: TextOverflow::Clip,
236 font_family: Some("sans-serif"),
237 annotations,
238 text_align: TextAlign::Start,
239 font_weight: FontWeight::NORMAL,
240 font_style: FontStyle::Normal,
241 text_decoration: TextDecoration::default(),
242 letter_spacing: 0.0,
243 line_height: 0.0,
244 url: None,
245 font_variation_settings: None,
246 },
247 )
248}
249
250pub fn Spacer() -> View {
251 Box(Modifier::new().flex_grow(1.0))
252}
253
254pub fn Space(modifier: Modifier) -> View {
255 Box(modifier)
256}
257
258pub fn Grid(
259 columns: usize,
260 modifier: Modifier,
261 children: Vec<View>,
262 row_gap: f32,
263 column_gap: f32,
264) -> View {
265 Column(modifier.grid(columns, row_gap, column_gap)).with_children(children)
266}
267
268pub fn Expander(modifier: Modifier, expanded: bool, on_toggle: impl Fn() + 'static) -> View {
269 View::new(
270 0,
271 ViewKind::Expander {
272 expanded,
273 on_toggle: Some(Rc::new(on_toggle)),
274 },
275 )
276 .modifier(modifier)
277}
278
279pub fn TreeRow(
285 modifier: Modifier,
286 depth: usize,
287 has_children: bool,
288 is_expanded: bool,
289 is_selected: bool,
290 on_toggle: impl Fn() + 'static,
291 on_select: impl Fn() + 'static,
292) -> View {
293 View::new(
294 0,
295 ViewKind::TreeRow {
296 depth,
297 has_children,
298 is_expanded,
299 is_selected,
300 on_toggle: Some(Rc::new(on_toggle)),
301 on_select: Some(Rc::new(on_select)),
302 },
303 )
304 .modifier(modifier)
305}
306
307static DRAGVALUE_COUNTER: AtomicU64 = AtomicU64::new(0);
308
309pub fn DragValue(
314 value: f32,
315 range: (f32, f32),
316 speed: f32,
317 on_change: impl Fn(f32) + 'static,
318) -> View {
319 let id = DRAGVALUE_COUNTER.fetch_add(1, Ordering::Relaxed);
320 let drag_start_x = remember_mutable_with_key(format!("dv_dsx_{}", id), || 0.0f32);
321 let drag_start_val = remember_mutable_with_key(format!("dv_dsv_{}", id), || 0.0f32);
322 let is_dragging = remember_mutable_with_key(format!("dv_drg_{}", id), || false);
323
324 let oc = Rc::new(on_change);
325 let min = range.0;
326 let max = range.1;
327 let cur = value;
328
329 let th = locals::theme();
330
331 Box(Modifier::new()
332 .min_width(48.0)
333 .height(28.0)
334 .background(th.surface_container)
335 .border(1.0, th.outline, 4.0)
336 .clip_rounded(4.0)
337 .padding_values(PaddingValues {
338 left: 4.0,
339 right: 4.0,
340 top: 0.0,
341 bottom: 0.0,
342 })
343 .on_pointer_down({
344 let dsx = drag_start_x.clone();
345 let dsv = drag_start_val.clone();
346 let drg = is_dragging.clone();
347 move |pe: PointerEvent| {
348 drg.set(true);
349 dsx.set(pe.position_in_window().x);
350 dsv.set(cur);
351 }
352 })
353 .on_pointer_move({
354 let dsx = drag_start_x.clone();
355 let dsv = drag_start_val.clone();
356 let drg = is_dragging.clone();
357 let oc = oc.clone();
358 move |pe: PointerEvent| {
359 if !drg.with(|v| *v) {
360 return;
361 }
362 let start_x = dsx.with(|v| *v);
363 let start_val = dsv.with(|v| *v);
364 let new_val =
365 (start_val + (pe.position_in_window().x - start_x) * speed).clamp(min, max);
366 (oc)(new_val);
367 }
368 })
369 .on_pointer_up({
370 let drg = is_dragging.clone();
371 move |_pe: PointerEvent| {
372 drg.set(false);
373 }
374 })
375 .cursor(CursorIcon::EwResize))
376 .child(
377 Text(format_value(value))
378 .size(13.0)
379 .color(th.on_surface)
380 .single_line()
381 .overflow_ellipsize(),
382 )
383}
384
385fn format_value(v: f32) -> String {
386 if (v - v.round()).abs() < 1e-6 {
387 format!("{}", v.round() as i64)
388 } else if (v * 10.0 - (v * 10.0).round()).abs() < 1e-6 {
389 format!("{:.1}", v)
390 } else {
391 format!("{:.2}", v)
392 }
393}
394
395pub fn Image(modifier: Modifier, handle: ImageHandle) -> View {
396 View::new(
397 0,
398 ViewKind::Image {
399 handle,
400 tint: Color::WHITE,
401 fit: ImageFit::Contain,
402 },
403 )
404 .modifier(modifier)
405}
406
407pub trait ImageExt {
408 fn image_tint(self, c: Color) -> View;
409 fn image_fit(self, fit: ImageFit) -> View;
410}
411impl ImageExt for View {
412 fn image_tint(mut self, c: Color) -> View {
413 if let ViewKind::Image { tint, .. } = &mut self.kind {
414 *tint = c;
415 }
416 self
417 }
418 fn image_fit(mut self, fit: ImageFit) -> View {
419 if let ViewKind::Image { fit: f, .. } = &mut self.kind {
420 *f = fit;
421 }
422 self
423 }
424}
425
426pub trait ViewExt: Sized {
428 fn child(self, children: impl IntoChildren) -> Self;
429}
430
431impl ViewExt for View {
432 fn child(mut self, children: impl IntoChildren) -> Self {
433 self.children.extend(children.into_children());
434 self
435 }
436}
437
438pub trait IntoChildren {
439 fn into_children(self) -> Vec<View>;
440}
441
442impl IntoChildren for View {
443 fn into_children(self) -> Vec<View> {
444 vec![self]
445 }
446}
447
448impl IntoChildren for Vec<View> {
449 fn into_children(self) -> Vec<View> {
450 self
451 }
452}
453
454impl<const N: usize> IntoChildren for [View; N] {
455 fn into_children(self) -> Vec<View> {
456 self.into()
457 }
458}
459
460macro_rules! impl_into_children_tuple {
462 ($($idx:tt $t:ident),+) => {
463 impl<$($t: IntoChildren),+> IntoChildren for ($($t,)+) {
464 fn into_children(self) -> Vec<View> {
465 let mut v = Vec::new();
466 $(v.extend(self.$idx.into_children());)+
467 v
468 }
469 }
470 };
471}
472
473impl_into_children_tuple!(0 A);
474impl_into_children_tuple!(0 A, 1 B);
475impl_into_children_tuple!(0 A, 1 B, 2 C);
476impl_into_children_tuple!(0 A, 1 B, 2 C, 3 D);
477impl_into_children_tuple!(0 A, 1 B, 2 C, 3 D, 4 E);
478impl_into_children_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F);
479impl_into_children_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G);
480impl_into_children_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H);
481
482pub fn layout_and_paint(
485 root: &View,
486 size_px_u32: (u32, u32),
487 textfield_states: &HashMap<u64, Rc<RefCell<TextFieldState>>>,
488 interactions: &Interactions,
489 focused: Option<u64>,
490) -> (Scene, Vec<HitRegion>, Vec<SemNode>) {
491 LAYOUT_ENGINE.with(|engine| {
492 engine
493 .borrow_mut()
494 .layout_frame(root, size_px_u32, textfield_states, interactions, focused)
495 })
496}
497
498pub fn last_layout_stats() -> layout::LayoutStats {
502 LAYOUT_ENGINE.with(|engine| engine.borrow().stats.clone())
503}
504
505pub use layout::LayoutStats;
506
507pub trait TextStyle {
509 fn color(self, c: Color) -> View;
510 fn size(self, px: f32) -> View;
511 fn max_lines(self, n: usize) -> View;
512 fn single_line(self) -> View;
513 fn overflow_ellipsize(self) -> View;
514 fn overflow_clip(self) -> View;
515 fn overflow_visible(self) -> View;
516 fn font_family(self, family: &'static str) -> View;
517 fn text_align(self, align: TextAlign) -> View;
518 fn font_weight(self, weight: FontWeight) -> View;
519 fn font_style(self, style: FontStyle) -> View;
520 fn text_decoration(self, decoration: TextDecoration) -> View;
521 fn letter_spacing(self, spacing: f32) -> View;
522 fn line_height(self, height: f32) -> View;
523 fn url(self, url: impl Into<std::sync::Arc<str>>) -> View;
524 fn font_variation_settings(self, settings: &str) -> View;
525}
526impl TextStyle for View {
527 fn color(mut self, c: Color) -> View {
528 if let ViewKind::Text {
529 color: text_color, ..
530 } = &mut self.kind
531 {
532 *text_color = c;
533 }
534 self
535 }
536 fn size(mut self, dp_font: f32) -> View {
537 if let ViewKind::Text {
538 font_size: text_size_dp,
539 ..
540 } = &mut self.kind
541 {
542 *text_size_dp = dp_font;
543 }
544 self
545 }
546 fn max_lines(mut self, n: usize) -> View {
547 if let ViewKind::Text {
548 max_lines,
549 soft_wrap,
550 ..
551 } = &mut self.kind
552 {
553 *max_lines = Some(n);
554 *soft_wrap = true;
555 }
556 self
557 }
558 fn single_line(mut self) -> View {
559 if let ViewKind::Text {
560 soft_wrap,
561 max_lines,
562 ..
563 } = &mut self.kind
564 {
565 *soft_wrap = false;
566 *max_lines = Some(1);
567 }
568 self
569 }
570 fn overflow_ellipsize(mut self) -> View {
571 if let ViewKind::Text { overflow, .. } = &mut self.kind {
572 *overflow = TextOverflow::Ellipsis;
573 }
574 self
575 }
576 fn overflow_clip(mut self) -> View {
577 if let ViewKind::Text { overflow, .. } = &mut self.kind {
578 *overflow = TextOverflow::Clip;
579 }
580 self
581 }
582 fn overflow_visible(mut self) -> View {
583 if let ViewKind::Text { overflow, .. } = &mut self.kind {
584 *overflow = TextOverflow::Visible;
585 }
586 self
587 }
588 fn font_family(mut self, family: &'static str) -> View {
589 if let ViewKind::Text {
590 font_family: ff, ..
591 } = &mut self.kind
592 {
593 *ff = Some(family);
594 }
595 self
596 }
597 fn text_align(mut self, align: TextAlign) -> View {
598 if let ViewKind::Text { text_align, .. } = &mut self.kind {
599 *text_align = align;
600 }
601 self
602 }
603 fn font_weight(mut self, weight: FontWeight) -> View {
604 if let ViewKind::Text { font_weight, .. } = &mut self.kind {
605 *font_weight = weight;
606 }
607 self
608 }
609 fn font_style(mut self, style: FontStyle) -> View {
610 if let ViewKind::Text { font_style, .. } = &mut self.kind {
611 *font_style = style;
612 }
613 self
614 }
615 fn text_decoration(mut self, decoration: TextDecoration) -> View {
616 if let ViewKind::Text {
617 text_decoration, ..
618 } = &mut self.kind
619 {
620 *text_decoration = decoration;
621 }
622 self
623 }
624 fn letter_spacing(mut self, spacing: f32) -> View {
625 if let ViewKind::Text { letter_spacing, .. } = &mut self.kind {
626 *letter_spacing = spacing;
627 }
628 self
629 }
630 fn line_height(mut self, height: f32) -> View {
631 if let ViewKind::Text { line_height, .. } = &mut self.kind {
632 *line_height = height;
633 }
634 self
635 }
636 fn url(mut self, url: impl Into<std::sync::Arc<str>>) -> View {
637 if let ViewKind::Text {
638 url: u,
639 text_decoration,
640 ..
641 } = &mut self.kind
642 {
643 *u = Some(url.into());
644 if !text_decoration.underline && !text_decoration.strikethrough {
645 *text_decoration = TextDecoration::UNDERLINE;
646 }
647 }
648 self
649 }
650 fn font_variation_settings(mut self, settings: &str) -> View {
651 if let ViewKind::Text {
652 font_variation_settings,
653 ..
654 } = &mut self.kind
655 {
656 *font_variation_settings = Some(settings.into());
657 }
658 self
659 }
660}