#![allow(non_snake_case)]
pub mod adaptive;
pub mod anim;
pub mod anim_ext;
pub mod color_picker;
pub mod gestures;
pub mod layout;
pub use layout::IntrinsicSizeMode;
pub mod lazy;
pub mod selection;
pub mod subcompose;
pub use lazy::{
LazyColumn, LazyHorizontalGrid, LazyRow, LazyVerticalGrid, LazyVerticalStaggeredGrid,
SimpleList,
};
pub mod lazy_states;
pub use lazy_states::{
ItemHeight, LazyColumnConfig, LazyColumnState, LazyGridConfig, LazyGridState, LazyRowConfig,
LazyRowState, LazyVerticalStaggeredGridConfig, LazyVerticalStaggeredGridState,
};
pub use subcompose::{
BoxWithConstraints, SubcomposeLayout, box_with_constraints_with_key, subcompose_hash_key,
subcompose_layout_with_slots, subcompose_with_key, subcompose_with_key_slots,
};
pub mod overlay;
pub mod pager;
pub mod scroll;
pub mod windowing;
use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::rc::Rc;
use std::sync::atomic::{AtomicU64, Ordering};
use repose_core::*;
use taffy::style::FlexDirection;
pub mod textfield;
use repose_core::locals;
pub use selection::SelectableText;
pub use textfield::{
BasicSecureTextField, BasicTextField, KeyboardOptions, TextFieldConfig, TextFieldState,
};
thread_local! {
static LAYOUT_ENGINE: RefCell<layout::LayoutEngine> =
RefCell::new(layout::LayoutEngine::new());
}
#[derive(Default)]
pub struct Interactions {
pub hover: Option<u64>,
pub pressed: HashSet<u64>,
}
pub fn Box(modifier: Modifier) -> View {
View::new(0, ViewKind::Box).modifier(modifier)
}
pub fn Row(modifier: Modifier) -> View {
View::new(0, ViewKind::Row).modifier(modifier)
}
pub fn Column(modifier: Modifier) -> View {
View::new(0, ViewKind::Column).modifier(modifier)
}
pub fn FlowRow(modifier: Modifier) -> View {
Row(modifier.flex_wrap(FlexWrap::Wrap))
}
pub fn FlowColumn(modifier: Modifier) -> View {
Column(modifier.flex_wrap(FlexWrap::Wrap))
}
pub fn Center(modifier: Modifier) -> View {
Box(modifier.align_self(AlignSelf::CENTER))
}
pub fn Stack(modifier: Modifier) -> View {
View::new(0, ViewKind::Stack).modifier(modifier)
}
pub fn ZStack(modifier: Modifier) -> View {
View::new(0, ViewKind::ZStack).modifier(modifier)
}
pub fn OverlayHost(modifier: Modifier) -> View {
View::new(0, ViewKind::OverlayHost).modifier(modifier)
}
#[deprecated = "Use Modifier::vertical_scroll instead"]
pub fn Scroll(modifier: Modifier) -> View {
View::new(0, ViewKind::Box)
.modifier(modifier.vertical_scroll(ScrollAxisBinding {
show_scrollbar: true,
..Default::default()
}))
}
pub fn Text(text: impl Into<String>) -> View {
View::new(
0,
ViewKind::Text {
text: text.into(),
color: locals::content_color(),
font_size: 16.0, soft_wrap: true,
max_lines: None,
overflow: TextOverflow::Visible,
font_family: None,
annotations: None,
text_align: TextAlign::Start,
font_weight: FontWeight::NORMAL,
font_style: FontStyle::Normal,
text_decoration: TextDecoration::default(),
letter_spacing: 0.0,
line_height: 0.0,
},
)
}
pub fn AnnotatedText(annotated: AnnotatedString) -> View {
let annotations: Option<std::sync::Arc<[TextSpan]>> = if annotated.spans.is_empty() {
None
} else {
Some(annotated.spans.clone())
};
View::new(
0,
ViewKind::Text {
text: annotated.text,
color: locals::content_color(),
font_size: 16.0,
soft_wrap: true,
max_lines: None,
overflow: TextOverflow::Visible,
font_family: None,
annotations,
text_align: TextAlign::Start,
font_weight: FontWeight::NORMAL,
font_style: FontStyle::Normal,
text_decoration: TextDecoration::default(),
letter_spacing: 0.0,
line_height: 0.0,
},
)
}
pub fn Spacer() -> View {
Box(Modifier::new().flex_grow(1.0))
}
pub fn Space(modifier: Modifier) -> View {
Box(modifier)
}
pub fn Grid(
columns: usize,
modifier: Modifier,
children: Vec<View>,
row_gap: f32,
column_gap: f32,
) -> View {
Column(modifier.grid(columns, row_gap, column_gap)).with_children(children)
}
pub fn Expander(modifier: Modifier, expanded: bool, on_toggle: impl Fn() + 'static) -> View {
View::new(
0,
ViewKind::Expander {
expanded,
on_toggle: Some(Rc::new(on_toggle)),
},
)
.modifier(modifier)
}
pub fn TreeRow(
modifier: Modifier,
depth: usize,
has_children: bool,
is_expanded: bool,
is_selected: bool,
on_toggle: impl Fn() + 'static,
on_select: impl Fn() + 'static,
) -> View {
View::new(
0,
ViewKind::TreeRow {
depth,
has_children,
is_expanded,
is_selected,
on_toggle: Some(Rc::new(on_toggle)),
on_select: Some(Rc::new(on_select)),
},
)
.modifier(modifier)
}
static DRAGVALUE_COUNTER: AtomicU64 = AtomicU64::new(0);
pub fn DragValue(
value: f32,
range: (f32, f32),
speed: f32,
on_change: impl Fn(f32) + 'static,
) -> View {
let id = DRAGVALUE_COUNTER.fetch_add(1, Ordering::Relaxed);
let drag_start_x = remember_state_with_key(format!("dv_dsx_{}", id), || 0.0f32);
let drag_start_val = remember_state_with_key(format!("dv_dsv_{}", id), || 0.0f32);
let is_dragging = remember_state_with_key(format!("dv_drg_{}", id), || false);
let oc = Rc::new(on_change);
let min = range.0;
let max = range.1;
let cur = value;
let th = locals::theme();
Box(Modifier::new()
.min_width(48.0)
.height(28.0)
.background(th.surface_container)
.border(1.0, th.outline, 4.0)
.clip_rounded(4.0)
.padding_values(PaddingValues {
left: 4.0,
right: 4.0,
top: 0.0,
bottom: 0.0,
})
.on_pointer_down({
let dsx = drag_start_x.clone();
let dsv = drag_start_val.clone();
let drg = is_dragging.clone();
move |pe: PointerEvent| {
*drg.borrow_mut() = true;
*dsx.borrow_mut() = pe.position.x;
*dsv.borrow_mut() = cur;
}
})
.on_pointer_move({
let dsx = drag_start_x.clone();
let dsv = drag_start_val.clone();
let drg = is_dragging.clone();
let oc = oc.clone();
move |pe: PointerEvent| {
if !*drg.borrow() {
return;
}
let dx = pe.position.x - *dsx.borrow();
let new_val = (*dsv.borrow() + dx * speed).clamp(min, max);
(oc)(new_val);
}
})
.on_pointer_up({
let drg = is_dragging.clone();
move |_pe: PointerEvent| {
*drg.borrow_mut() = false;
}
})
.cursor(CursorIcon::EwResize))
.child(
Text(format_value(value))
.size(13.0)
.color(th.on_surface)
.single_line()
.overflow_ellipsize(),
)
}
fn format_value(v: f32) -> String {
if (v - v.round()).abs() < 1e-6 {
format!("{}", v.round() as i64)
} else if (v * 10.0 - (v * 10.0).round()).abs() < 1e-6 {
format!("{:.1}", v)
} else {
format!("{:.2}", v)
}
}
pub fn Image(modifier: Modifier, handle: ImageHandle) -> View {
View::new(
0,
ViewKind::Image {
handle,
tint: Color::WHITE,
fit: ImageFit::Contain,
},
)
.modifier(modifier)
}
pub trait ImageExt {
fn image_tint(self, c: Color) -> View;
fn image_fit(self, fit: ImageFit) -> View;
}
impl ImageExt for View {
fn image_tint(mut self, c: Color) -> View {
if let ViewKind::Image { tint, .. } = &mut self.kind {
*tint = c;
}
self
}
fn image_fit(mut self, fit: ImageFit) -> View {
if let ViewKind::Image { fit: f, .. } = &mut self.kind {
*f = fit;
}
self
}
}
fn flex_dir_for(kind: &ViewKind, modifier: &Modifier) -> Option<FlexDirection> {
if let Some(ref scroll) = modifier.scroll {
return Some(match scroll.axis() {
ScrollAxis::Vertical => FlexDirection::Column,
ScrollAxis::Horizontal => FlexDirection::Row,
ScrollAxis::Both => FlexDirection::Column,
});
}
match kind {
ViewKind::Row => {
if repose_core::locals::text_direction() == repose_core::locals::TextDirection::Rtl {
Some(FlexDirection::RowReverse)
} else {
Some(FlexDirection::Row)
}
}
ViewKind::Column => Some(FlexDirection::Column),
_ => None,
}
}
pub trait ViewExt: Sized {
fn child(self, children: impl IntoChildren) -> Self;
}
impl ViewExt for View {
fn child(self, children: impl IntoChildren) -> Self {
self.with_children(children.into_children())
}
}
pub trait IntoChildren {
fn into_children(self) -> Vec<View>;
}
impl IntoChildren for View {
fn into_children(self) -> Vec<View> {
vec![self]
}
}
impl IntoChildren for Vec<View> {
fn into_children(self) -> Vec<View> {
self
}
}
impl<const N: usize> IntoChildren for [View; N] {
fn into_children(self) -> Vec<View> {
self.into()
}
}
macro_rules! impl_into_children_tuple {
($($idx:tt $t:ident),+) => {
impl<$($t: IntoChildren),+> IntoChildren for ($($t,)+) {
fn into_children(self) -> Vec<View> {
let mut v = Vec::new();
$(v.extend(self.$idx.into_children());)+
v
}
}
};
}
impl_into_children_tuple!(0 A);
impl_into_children_tuple!(0 A, 1 B);
impl_into_children_tuple!(0 A, 1 B, 2 C);
impl_into_children_tuple!(0 A, 1 B, 2 C, 3 D);
impl_into_children_tuple!(0 A, 1 B, 2 C, 3 D, 4 E);
impl_into_children_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F);
impl_into_children_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G);
impl_into_children_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H);
pub fn layout_and_paint(
root: &View,
size_px_u32: (u32, u32),
textfield_states: &HashMap<u64, Rc<RefCell<TextFieldState>>>,
interactions: &Interactions,
focused: Option<u64>,
) -> (Scene, Vec<HitRegion>, Vec<SemNode>) {
LAYOUT_ENGINE.with(|engine| {
engine
.borrow_mut()
.layout_frame(root, size_px_u32, textfield_states, interactions, focused)
})
}
pub trait TextStyle {
fn color(self, c: Color) -> View;
fn size(self, px: f32) -> View;
fn max_lines(self, n: usize) -> View;
fn single_line(self) -> View;
fn overflow_ellipsize(self) -> View;
fn overflow_clip(self) -> View;
fn overflow_visible(self) -> View;
fn font_family(self, family: &'static str) -> View;
fn text_align(self, align: TextAlign) -> View;
fn font_weight(self, weight: FontWeight) -> View;
fn font_style(self, style: FontStyle) -> View;
fn text_decoration(self, decoration: TextDecoration) -> View;
fn letter_spacing(self, spacing: f32) -> View;
fn line_height(self, height: f32) -> View;
}
impl TextStyle for View {
fn color(mut self, c: Color) -> View {
if let ViewKind::Text {
color: text_color, ..
} = &mut self.kind
{
*text_color = c;
}
self
}
fn size(mut self, dp_font: f32) -> View {
if let ViewKind::Text {
font_size: text_size_dp,
..
} = &mut self.kind
{
*text_size_dp = dp_font;
}
self
}
fn max_lines(mut self, n: usize) -> View {
if let ViewKind::Text {
max_lines,
soft_wrap,
..
} = &mut self.kind
{
*max_lines = Some(n);
*soft_wrap = true;
}
self
}
fn single_line(mut self) -> View {
if let ViewKind::Text {
soft_wrap,
max_lines,
..
} = &mut self.kind
{
*soft_wrap = false;
*max_lines = Some(1);
}
self
}
fn overflow_ellipsize(mut self) -> View {
if let ViewKind::Text { overflow, .. } = &mut self.kind {
*overflow = TextOverflow::Ellipsis;
}
self
}
fn overflow_clip(mut self) -> View {
if let ViewKind::Text { overflow, .. } = &mut self.kind {
*overflow = TextOverflow::Clip;
}
self
}
fn overflow_visible(mut self) -> View {
if let ViewKind::Text { overflow, .. } = &mut self.kind {
*overflow = TextOverflow::Visible;
}
self
}
fn font_family(mut self, family: &'static str) -> View {
if let ViewKind::Text {
font_family: ff, ..
} = &mut self.kind
{
*ff = Some(family);
}
self
}
fn text_align(mut self, align: TextAlign) -> View {
if let ViewKind::Text { text_align, .. } = &mut self.kind {
*text_align = align;
}
self
}
fn font_weight(mut self, weight: FontWeight) -> View {
if let ViewKind::Text { font_weight, .. } = &mut self.kind {
*font_weight = weight;
}
self
}
fn font_style(mut self, style: FontStyle) -> View {
if let ViewKind::Text { font_style, .. } = &mut self.kind {
*font_style = style;
}
self
}
fn text_decoration(mut self, decoration: TextDecoration) -> View {
if let ViewKind::Text {
text_decoration, ..
} = &mut self.kind
{
*text_decoration = decoration;
}
self
}
fn letter_spacing(mut self, spacing: f32) -> View {
if let ViewKind::Text { letter_spacing, .. } = &mut self.kind {
*letter_spacing = spacing;
}
self
}
fn line_height(mut self, height: f32) -> View {
if let ViewKind::Text { line_height, .. } = &mut self.kind {
*line_height = height;
}
self
}
}