1#![allow(non_snake_case)]
2pub mod adaptive;
106pub mod anim;
107pub mod anim_ext;
108pub mod color_picker;
109pub mod gestures;
110pub mod layout;
111pub use layout::IntrinsicSizeMode;
112pub mod lazy;
113pub mod selection;
114pub mod subcompose;
115pub use lazy::{
116 LazyColumn, LazyHorizontalGrid, LazyRow, LazyVerticalGrid, LazyVerticalStaggeredGrid,
117 SimpleList,
118};
119pub mod lazy_states;
120pub use lazy_states::{
121 ItemHeight, LazyColumnConfig, LazyColumnState, LazyGridConfig, LazyGridState, LazyRowConfig,
122 LazyRowState, LazyVerticalStaggeredGridConfig, LazyVerticalStaggeredGridState,
123};
124pub use subcompose::{
125 BoxWithConstraints, SubcomposeLayout, box_with_constraints_with_key, subcompose_hash_key,
126 subcompose_layout_with_slots, subcompose_with_key, subcompose_with_key_slots,
127};
128pub mod overlay;
129pub mod pager;
130pub mod scroll;
131pub mod windowing;
132
133use std::cell::RefCell;
134use std::collections::{HashMap, HashSet};
135use std::rc::Rc;
136use std::sync::atomic::{AtomicU64, Ordering};
137
138use repose_core::*;
139use taffy::style::FlexDirection;
140
141pub mod textfield;
142use repose_core::locals;
143pub use selection::SelectableText;
144pub use textfield::{
145 BasicSecureTextField, BasicTextField, KeyboardOptions, TextFieldConfig, TextFieldState,
146};
147
148thread_local! {
149 static LAYOUT_ENGINE: RefCell<layout::LayoutEngine> =
150 RefCell::new(layout::LayoutEngine::new());
151}
152
153#[derive(Default)]
154pub struct Interactions {
155 pub hover: Option<u64>,
156 pub pressed: HashSet<u64>,
157}
158
159pub fn Box(modifier: Modifier) -> View {
160 View::new(0, ViewKind::Box).modifier(modifier)
161}
162
163pub fn Row(modifier: Modifier) -> View {
164 View::new(0, ViewKind::Row).modifier(modifier)
165}
166
167pub fn Column(modifier: Modifier) -> View {
168 View::new(0, ViewKind::Column).modifier(modifier)
169}
170
171pub fn FlowRow(modifier: Modifier) -> View {
174 Row(modifier.flex_wrap(FlexWrap::Wrap))
175}
176
177pub fn FlowColumn(modifier: Modifier) -> View {
180 Column(modifier.flex_wrap(FlexWrap::Wrap))
181}
182
183pub fn Center(modifier: Modifier) -> View {
185 Box(modifier.align_self(AlignSelf::Center))
186}
187
188pub fn Stack(modifier: Modifier) -> View {
189 View::new(0, ViewKind::Stack).modifier(modifier)
190}
191
192pub fn ZStack(modifier: Modifier) -> View {
193 View::new(0, ViewKind::ZStack).modifier(modifier)
194}
195
196pub fn OverlayHost(modifier: Modifier) -> View {
197 View::new(0, ViewKind::OverlayHost).modifier(modifier)
198}
199
200#[deprecated = "Use ScollArea instead"]
201pub fn Scroll(modifier: Modifier) -> View {
202 View::new(
203 0,
204 ViewKind::ScrollV {
205 on_scroll: None,
206 set_viewport_height: None,
207 set_content_height: None,
208 get_scroll_offset: None,
209 set_scroll_offset: None,
210 show_scrollbar: true,
211 tick_scroll: None,
212 },
213 )
214 .modifier(modifier)
215}
216
217pub fn Text(text: impl Into<String>) -> View {
218 View::new(
219 0,
220 ViewKind::Text {
221 text: text.into(),
222 color: locals::content_color(),
223 font_size: 16.0, soft_wrap: true,
225 max_lines: None,
226 overflow: TextOverflow::Visible,
227 font_family: None,
228 annotations: None,
229 text_align: TextAlign::Start,
230 font_weight: FontWeight::NORMAL,
231 font_style: FontStyle::Normal,
232 text_decoration: TextDecoration::default(),
233 letter_spacing: 0.0,
234 line_height: 0.0,
235 },
236 )
237}
238
239pub fn AnnotatedText(annotated: AnnotatedString) -> View {
243 let annotations: Option<std::sync::Arc<[TextSpan]>> = if annotated.spans.is_empty() {
244 None
245 } else {
246 Some(annotated.spans.clone())
247 };
248 View::new(
249 0,
250 ViewKind::Text {
251 text: annotated.text,
252 color: locals::content_color(),
253 font_size: 16.0,
254 soft_wrap: true,
255 max_lines: None,
256 overflow: TextOverflow::Visible,
257 font_family: None,
258 annotations,
259 text_align: TextAlign::Start,
260 font_weight: FontWeight::NORMAL,
261 font_style: FontStyle::Normal,
262 text_decoration: TextDecoration::default(),
263 letter_spacing: 0.0,
264 line_height: 0.0,
265 },
266 )
267}
268
269pub fn Spacer() -> View {
270 Box(Modifier::new().flex_grow(1.0))
271}
272
273pub fn Space(modifier: Modifier) -> View {
274 Box(modifier)
275}
276
277pub fn Grid(
278 columns: usize,
279 modifier: Modifier,
280 children: Vec<View>,
281 row_gap: f32,
282 column_gap: f32,
283) -> View {
284 Column(modifier.grid(columns, row_gap, column_gap)).with_children(children)
285}
286
287pub fn Expander(modifier: Modifier, expanded: bool, on_toggle: impl Fn() + 'static) -> View {
288 View::new(
289 0,
290 ViewKind::Expander {
291 expanded,
292 on_toggle: Some(Rc::new(on_toggle)),
293 },
294 )
295 .modifier(modifier)
296}
297
298pub fn TreeRow(
304 modifier: Modifier,
305 depth: usize,
306 has_children: bool,
307 is_expanded: bool,
308 is_selected: bool,
309 on_toggle: impl Fn() + 'static,
310 on_select: impl Fn() + 'static,
311) -> View {
312 View::new(
313 0,
314 ViewKind::TreeRow {
315 depth,
316 has_children,
317 is_expanded,
318 is_selected,
319 on_toggle: Some(Rc::new(on_toggle)),
320 on_select: Some(Rc::new(on_select)),
321 },
322 )
323 .modifier(modifier)
324}
325
326static DRAGVALUE_COUNTER: AtomicU64 = AtomicU64::new(0);
327
328pub fn DragValue(
333 value: f32,
334 range: (f32, f32),
335 speed: f32,
336 on_change: impl Fn(f32) + 'static,
337) -> View {
338 let id = DRAGVALUE_COUNTER.fetch_add(1, Ordering::Relaxed);
339 let drag_start_x = remember_state_with_key(format!("dv_dsx_{}", id), || 0.0f32);
340 let drag_start_val = remember_state_with_key(format!("dv_dsv_{}", id), || 0.0f32);
341 let is_dragging = remember_state_with_key(format!("dv_drg_{}", id), || false);
342
343 let oc = Rc::new(on_change);
344 let min = range.0;
345 let max = range.1;
346 let cur = value;
347
348 let th = locals::theme();
349
350 Box(Modifier::new()
351 .min_width(48.0)
352 .height(28.0)
353 .background(th.surface_container)
354 .border(1.0, th.outline, 4.0)
355 .clip_rounded(4.0)
356 .padding_values(PaddingValues {
357 left: 4.0,
358 right: 4.0,
359 top: 0.0,
360 bottom: 0.0,
361 })
362 .on_pointer_down({
363 let dsx = drag_start_x.clone();
364 let dsv = drag_start_val.clone();
365 let drg = is_dragging.clone();
366 move |pe: PointerEvent| {
367 *drg.borrow_mut() = true;
368 *dsx.borrow_mut() = pe.position.x;
369 *dsv.borrow_mut() = cur;
370 }
371 })
372 .on_pointer_move({
373 let dsx = drag_start_x.clone();
374 let dsv = drag_start_val.clone();
375 let drg = is_dragging.clone();
376 let oc = oc.clone();
377 move |pe: PointerEvent| {
378 if !*drg.borrow() {
379 return;
380 }
381 let dx = pe.position.x - *dsx.borrow();
382 let new_val = (*dsv.borrow() + dx * speed).clamp(min, max);
383 (oc)(new_val);
384 }
385 })
386 .on_pointer_up({
387 let drg = is_dragging.clone();
388 move |_pe: PointerEvent| {
389 *drg.borrow_mut() = false;
390 }
391 })
392 .cursor(CursorIcon::EwResize))
393 .child(
394 Text(format_value(value))
395 .size(13.0)
396 .color(th.on_surface)
397 .single_line()
398 .overflow_ellipsize(),
399 )
400}
401
402fn format_value(v: f32) -> String {
403 if (v - v.round()).abs() < 1e-6 {
404 format!("{}", v.round() as i64)
405 } else if (v * 10.0 - (v * 10.0).round()).abs() < 1e-6 {
406 format!("{:.1}", v)
407 } else {
408 format!("{:.2}", v)
409 }
410}
411
412pub fn Image(modifier: Modifier, handle: ImageHandle) -> View {
413 View::new(
414 0,
415 ViewKind::Image {
416 handle,
417 tint: Color::WHITE,
418 fit: ImageFit::Contain,
419 },
420 )
421 .modifier(modifier)
422}
423
424pub trait ImageExt {
425 fn image_tint(self, c: Color) -> View;
426 fn image_fit(self, fit: ImageFit) -> View;
427}
428impl ImageExt for View {
429 fn image_tint(mut self, c: Color) -> View {
430 if let ViewKind::Image { tint, .. } = &mut self.kind {
431 *tint = c;
432 }
433 self
434 }
435 fn image_fit(mut self, fit: ImageFit) -> View {
436 if let ViewKind::Image { fit: f, .. } = &mut self.kind {
437 *f = fit;
438 }
439 self
440 }
441}
442
443fn flex_dir_for(kind: &ViewKind) -> Option<FlexDirection> {
444 match kind {
445 ViewKind::Row => {
446 if repose_core::locals::text_direction() == repose_core::locals::TextDirection::Rtl {
447 Some(FlexDirection::RowReverse)
448 } else {
449 Some(FlexDirection::Row)
450 }
451 }
452 ViewKind::Column | ViewKind::ScrollV { .. } => Some(FlexDirection::Column),
453 _ => None,
454 }
455}
456
457pub trait ViewExt: Sized {
459 fn child(self, children: impl IntoChildren) -> Self;
460}
461
462impl ViewExt for View {
463 fn child(self, children: impl IntoChildren) -> Self {
464 self.with_children(children.into_children())
465 }
466}
467
468pub trait IntoChildren {
469 fn into_children(self) -> Vec<View>;
470}
471
472impl IntoChildren for View {
473 fn into_children(self) -> Vec<View> {
474 vec![self]
475 }
476}
477
478impl IntoChildren for Vec<View> {
479 fn into_children(self) -> Vec<View> {
480 self
481 }
482}
483
484impl<const N: usize> IntoChildren for [View; N] {
485 fn into_children(self) -> Vec<View> {
486 self.into()
487 }
488}
489
490macro_rules! impl_into_children_tuple {
492 ($($idx:tt $t:ident),+) => {
493 impl<$($t: IntoChildren),+> IntoChildren for ($($t,)+) {
494 fn into_children(self) -> Vec<View> {
495 let mut v = Vec::new();
496 $(v.extend(self.$idx.into_children());)+
497 v
498 }
499 }
500 };
501}
502
503impl_into_children_tuple!(0 A);
504impl_into_children_tuple!(0 A, 1 B);
505impl_into_children_tuple!(0 A, 1 B, 2 C);
506impl_into_children_tuple!(0 A, 1 B, 2 C, 3 D);
507impl_into_children_tuple!(0 A, 1 B, 2 C, 3 D, 4 E);
508impl_into_children_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F);
509impl_into_children_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G);
510impl_into_children_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H);
511
512pub fn layout_and_paint(
514 root: &View,
515 size_px_u32: (u32, u32),
516 textfield_states: &HashMap<u64, Rc<RefCell<TextFieldState>>>,
517 interactions: &Interactions,
518 focused: Option<u64>,
519) -> (Scene, Vec<HitRegion>, Vec<SemNode>) {
520 LAYOUT_ENGINE.with(|engine| {
521 engine
522 .borrow_mut()
523 .layout_frame(root, size_px_u32, textfield_states, interactions, focused)
524 })
525}
526
527pub trait TextStyle {
529 fn color(self, c: Color) -> View;
530 fn size(self, px: f32) -> View;
531 fn max_lines(self, n: usize) -> View;
532 fn single_line(self) -> View;
533 fn overflow_ellipsize(self) -> View;
534 fn overflow_clip(self) -> View;
535 fn overflow_visible(self) -> View;
536 fn font_family(self, family: &'static str) -> View;
537 fn text_align(self, align: TextAlign) -> View;
538 fn font_weight(self, weight: FontWeight) -> View;
539 fn font_style(self, style: FontStyle) -> View;
540 fn text_decoration(self, decoration: TextDecoration) -> View;
541 fn letter_spacing(self, spacing: f32) -> View;
542 fn line_height(self, height: f32) -> 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}