use std::cell::{Cell, RefCell};
use std::rc::Rc;
use std::sync::atomic::{AtomicU64, Ordering};
use taffy::{AlignContent, AlignItems, AlignSelf, FlexDirection, FlexWrap, JustifyContent};
use crate::animation::AnimationSpec;
use crate::indication::IndicationNodeFactory;
use crate::{Brush, Color, PointerEvent, Size, Transform, Vec2};
#[derive(Clone, Copy, Debug)]
pub struct StateColors {
pub default: Color,
pub hovered: Color,
pub pressed: Color,
pub disabled: Color,
}
#[derive(Clone, Copy, Debug)]
pub struct StateElevation {
pub default: f32,
pub hovered: f32,
pub pressed: f32,
pub disabled: f32,
}
macro_rules! merge_opts {
($dst:ident, $src:ident; $($f:ident),+ $(,)?) => {
$( $dst.$f = $src.$f.or($dst.$f); )+
};
}
macro_rules! merge_flags {
($dst:ident, $src:ident; $($f:ident),+ $(,)?) => {
$( $dst.$f |= $src.$f; )+
};
}
macro_rules! impl_option_fields {
($ty:ty, $fn:ident) => {
impl $ty {
$fn!(replace);
}
};
($ty:ident) => {
impl $ty {
pub fn then(mut self, other: Self) -> Self {
merge_opts!(self, other;
key, size, width, height, required_size,
padding, padding_values,
min_width, min_height, max_width, max_height,
required_min_width, required_max_width,
required_min_height, required_max_height,
default_min_width, default_min_height,
fill_max, fill_max_w, fill_max_h,
background, state_colors, state_elevation, border,
flex_grow, flex_shrink, flex_basis, flex_wrap, flex_dir,
gap, row_gap, column_gap,
align_self, justify_content, align_items_container, align_content,
clip_rounded, render_z_index,
on_scroll,
on_pointer_down, on_pointer_move, on_pointer_up,
on_pointer_enter, on_pointer_leave,
on_double_click, on_long_click,
semantics, alpha, transform,
grid, grid_col_span, grid_row_span,
position_type,
offset_left, offset_right, offset_top, offset_bottom,
margin_left, margin_right, margin_top, margin_bottom,
aspect_ratio, intrinsic_width, intrinsic_height,
painter,
on_drag_start, on_drag_end, on_drag_enter, on_drag_over, on_drag_leave, on_drop,
on_action, cursor, animate_content_size, focus_requester, on_focus_changed,
interaction_source, text_input,
);
merge_flags!(self, other;
hit_passthrough, input_blocker, repaint_boundary, click, disabled,
propagate_min, focus_group,
);
if let Some(f) = other.focusable {
self.focusable = Some(f);
}
if other.indication.is_some() {
self.indication = other.indication;
}
if other.z_index != 0.0 {
self.z_index = other.z_index;
}
self
}
}
};
}
#[derive(Clone, Debug)]
pub struct Border {
pub width: f32,
pub color: Color,
pub radius: [f32; 4],
}
#[derive(Clone, Copy, Debug, Default)]
pub struct PaddingValues {
pub left: f32,
pub right: f32,
pub top: f32,
pub bottom: f32,
}
#[derive(Clone, Debug)]
pub struct GridConfig {
pub columns: usize,
pub row_gap: f32,
pub column_gap: f32,
}
#[derive(Clone, Copy, Debug)]
pub struct ShadowSpec {
pub blur_radius: f32,
pub offset_y: f32,
pub color: Color,
}
#[derive(Clone, Copy, Debug)]
#[non_exhaustive]
pub enum PositionType {
Relative,
Absolute,
}
#[derive(Clone)]
pub struct TextInputConfig {
pub hint: String,
pub multiline: bool,
pub on_change: Option<Rc<dyn Fn(String)>>,
pub on_submit: Option<Rc<dyn Fn(String)>>,
pub focus_tracker: Option<Rc<Cell<bool>>>,
pub value: String,
pub visual_transformation: Option<Rc<dyn crate::text::VisualTransformation>>,
pub keyboard_type: Option<crate::text::KeyboardType>,
pub ime_action: Option<crate::text::ImeAction>,
pub enabled: bool,
pub read_only: bool,
pub max_lines: Option<usize>,
pub min_lines: Option<usize>,
pub cursor_color: Option<Color>,
pub on_text_layout: Option<Rc<dyn Fn(&crate::text::TextLayoutResult)>>,
}
impl std::fmt::Debug for TextInputConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut s = f.debug_struct("TextInputConfig");
s.field("hint", &self.hint);
s.field("multiline", &self.multiline);
if self.on_change.is_some() {
s.field("on_change", &"…");
}
if self.on_submit.is_some() {
s.field("on_submit", &"…");
}
if self.focus_tracker.is_some() {
s.field("focus_tracker", &"…");
}
s.field("value", &self.value);
if self.visual_transformation.is_some() {
s.field("visual_transformation", &"…");
}
s.field("keyboard_type", &self.keyboard_type);
s.field("ime_action", &self.ime_action);
s.field("enabled", &self.enabled);
s.field("read_only", &self.read_only);
s.field("max_lines", &self.max_lines);
s.field("min_lines", &self.min_lines);
s.field("cursor_color", &self.cursor_color);
if self.on_text_layout.is_some() {
s.field("on_text_layout", &"…");
}
s.finish()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum IntrinsicSize {
Min,
Max,
}
static PRESS_COUNTER: AtomicU64 = AtomicU64::new(1);
pub type PressId = u64;
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Interaction {
Press(PressId, Vec2),
Release(PressId),
Cancel(PressId),
HoverEnter,
HoverLeave,
Focus,
Unfocus,
DragStart,
DragStop,
DragCancel,
}
impl Interaction {
#[inline]
pub fn new_press(position: Vec2) -> Self {
Interaction::Press(PRESS_COUNTER.fetch_add(1, Ordering::Relaxed), position)
}
}
#[derive(Clone)]
pub struct InteractionSource {
pub(crate) state: Rc<RefCell<InteractionState>>,
}
impl InteractionSource {
pub fn collect_is_pressed(&self) -> bool {
self.state.borrow().pressed > 0
}
pub fn collect_is_hovered(&self) -> bool {
self.state.borrow().hovered
}
pub fn collect_is_focused(&self) -> bool {
self.state.borrow().focused
}
pub fn collect_is_dragged(&self) -> bool {
self.state.borrow().dragged > 0
}
pub fn collect_last_press_position(&self) -> Option<Vec2> {
self.state.borrow().last_press_position
}
pub fn collect_last_press_id(&self) -> Option<PressId> {
self.state.borrow().last_press_id
}
pub fn stable_id(&self) -> *const () {
Rc::as_ptr(&self.state) as *const ()
}
pub fn to_mutable(&self) -> MutableInteractionSource {
MutableInteractionSource {
state: self.state.clone(),
}
}
}
#[derive(Clone)]
pub struct MutableInteractionSource {
pub(crate) state: Rc<RefCell<InteractionState>>,
}
impl MutableInteractionSource {
pub fn new() -> Self {
Self {
state: Rc::new(RefCell::new(InteractionState::default())),
}
}
pub fn emit(&self, interaction: Interaction) {
let mut s = self.state.borrow_mut();
match interaction {
Interaction::Press(id, pos) => {
s.pressed = s.pressed.saturating_add(1);
s.last_press_id = Some(id);
s.last_press_position = Some(pos);
}
Interaction::Release(_) | Interaction::Cancel(_) => {
s.pressed = s.pressed.saturating_sub(1);
}
Interaction::HoverEnter => s.hovered = true,
Interaction::HoverLeave => s.hovered = false,
Interaction::Focus => s.focused = true,
Interaction::Unfocus => s.focused = false,
Interaction::DragStart => s.dragged = s.dragged.saturating_add(1),
Interaction::DragStop | Interaction::DragCancel => {
s.dragged = s.dragged.saturating_sub(1);
}
}
}
pub fn source(&self) -> InteractionSource {
InteractionSource {
state: self.state.clone(),
}
}
}
impl Default for MutableInteractionSource {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Default)]
pub(crate) struct InteractionState {
pressed: u32,
hovered: bool,
focused: bool,
dragged: u32,
pub(crate) last_press_position: Option<Vec2>,
pub(crate) last_press_id: Option<PressId>,
}
#[derive(Clone, Default)]
pub struct Modifier {
pub key: Option<u64>,
pub size: Option<Size>,
pub width: Option<f32>,
pub height: Option<f32>,
pub required_size: Option<Size>,
pub fill_max: Option<f32>,
pub fill_max_w: Option<f32>,
pub fill_max_h: Option<f32>,
pub padding: Option<f32>,
pub padding_values: Option<PaddingValues>,
pub min_width: Option<f32>,
pub min_height: Option<f32>,
pub max_width: Option<f32>,
pub max_height: Option<f32>,
pub required_min_width: Option<f32>,
pub required_max_width: Option<f32>,
pub required_min_height: Option<f32>,
pub required_max_height: Option<f32>,
pub default_min_width: Option<f32>,
pub default_min_height: Option<f32>,
pub background: Option<Brush>,
pub state_colors: Option<StateColors>,
pub state_elevation: Option<StateElevation>,
pub border: Option<Border>,
pub flex_grow: Option<f32>,
pub flex_shrink: Option<f32>,
pub flex_basis: Option<f32>,
pub flex_wrap: Option<FlexWrap>,
pub flex_dir: Option<FlexDirection>,
pub gap: Option<f32>,
pub row_gap: Option<f32>,
pub column_gap: Option<f32>,
pub align_self: Option<AlignSelf>,
pub justify_content: Option<JustifyContent>,
pub align_items_container: Option<AlignItems>,
pub align_content: Option<AlignContent>,
pub clip_rounded: Option<[f32; 4]>,
pub z_index: f32,
pub render_z_index: Option<f32>,
pub hit_passthrough: bool,
pub input_blocker: bool,
pub repaint_boundary: bool,
pub click: bool,
pub disabled: bool,
pub focusable: Option<bool>,
pub propagate_min: bool,
pub focus_group: bool,
pub on_scroll: Option<Rc<dyn Fn(Vec2) -> Vec2>>,
pub on_pointer_down: Option<Rc<dyn Fn(PointerEvent)>>,
pub on_pointer_move: Option<Rc<dyn Fn(PointerEvent)>>,
pub on_pointer_up: Option<Rc<dyn Fn(PointerEvent)>>,
pub on_pointer_cancel: Option<Rc<dyn Fn(PointerEvent)>>,
pub on_pointer_enter: Option<Rc<dyn Fn(PointerEvent)>>,
pub on_pointer_leave: Option<Rc<dyn Fn(PointerEvent)>>,
pub on_double_click: Option<Rc<dyn Fn()>>,
pub on_long_click: Option<Rc<dyn Fn()>>,
pub semantics: Option<crate::Semantics>,
pub alpha: Option<f32>,
pub graphics_layer: Option<f32>,
pub shadow: Option<ShadowSpec>,
pub transform: Option<Transform>,
pub grid: Option<GridConfig>,
pub grid_col_span: Option<u16>,
pub grid_row_span: Option<u16>,
pub position_type: Option<PositionType>,
pub offset_left: Option<f32>,
pub offset_right: Option<f32>,
pub offset_top: Option<f32>,
pub offset_bottom: Option<f32>,
pub margin_left: Option<f32>,
pub margin_right: Option<f32>,
pub margin_top: Option<f32>,
pub margin_bottom: Option<f32>,
pub aspect_ratio: Option<f32>,
pub intrinsic_width: Option<IntrinsicSize>,
pub intrinsic_height: Option<IntrinsicSize>,
pub painter: Option<Rc<dyn Fn(&mut crate::Scene, crate::Rect, f32)>>,
pub on_drag_start: Option<Rc<dyn Fn(crate::dnd::DragStart) -> Option<crate::dnd::DragPayload>>>,
pub on_drag_end: Option<Rc<dyn Fn(crate::dnd::DragEnd)>>,
pub on_drag_enter: Option<Rc<dyn Fn(crate::dnd::DragOver)>>,
pub on_drag_over: Option<Rc<dyn Fn(crate::dnd::DragOver)>>,
pub on_drag_leave: Option<Rc<dyn Fn(crate::dnd::DragOver)>>,
pub on_drop: Option<Rc<dyn Fn(crate::dnd::DropEvent) -> bool>>,
pub on_action: Option<Rc<dyn Fn(crate::shortcuts::Action) -> bool>>,
pub cursor: Option<crate::CursorIcon>,
pub animate_content_size: Option<AnimationSpec>,
pub focus_requester: Option<crate::runtime::FocusRequester>,
pub on_focus_changed: Option<Rc<dyn Fn(bool)>>,
pub interaction_source: Option<InteractionSource>,
pub text_input: Option<TextInputConfig>,
pub indication: Option<Rc<dyn IndicationNodeFactory>>,
}
impl std::fmt::Debug for Modifier {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut s = f.debug_struct("Modifier");
macro_rules! opt_val {
($($name:ident),+ $(,)?) => {
$( if self.$name.is_some() { s.field(stringify!($name), &self.$name); } )+
};
}
if self.indication.is_some() {
s.field("indication", &"…");
}
opt_val!(
key,
size,
width,
height,
required_size,
padding,
padding_values,
min_width,
min_height,
max_width,
max_height,
required_min_width,
required_max_width,
required_min_height,
required_max_height,
default_min_width,
default_min_height,
fill_max,
fill_max_w,
fill_max_h,
background,
state_colors,
state_elevation,
border,
flex_grow,
flex_shrink,
flex_basis,
flex_wrap,
flex_dir,
gap,
row_gap,
column_gap,
align_self,
justify_content,
align_items_container,
align_content,
clip_rounded,
render_z_index,
semantics,
alpha,
transform,
grid,
grid_col_span,
grid_row_span,
position_type,
offset_left,
offset_right,
offset_top,
offset_bottom,
margin_left,
margin_right,
margin_top,
margin_bottom,
aspect_ratio,
intrinsic_width,
intrinsic_height,
cursor,
animate_content_size,
);
macro_rules! opt_cb {
($($name:ident),+ $(,)?) => {
$( if self.$name.is_some() { s.field(stringify!($name), &"…"); } )+
};
}
opt_cb!(
on_scroll,
on_pointer_down,
on_pointer_move,
on_pointer_up,
on_pointer_cancel,
on_pointer_enter,
on_pointer_leave,
on_double_click,
on_long_click,
painter,
on_drag_start,
on_drag_end,
on_drag_enter,
on_drag_over,
on_drag_leave,
on_drop,
on_action,
on_focus_changed,
interaction_source,
text_input,
);
macro_rules! flag {
($($name:ident),+ $(,)?) => {
$( if self.$name { s.field(stringify!($name), &true); } )+
};
}
flag!(
hit_passthrough,
input_blocker,
repaint_boundary,
click,
disabled,
propagate_min,
focus_group,
);
if let Some(f) = self.focusable {
s.field("focusable", &f);
}
if self.z_index != 0.0 {
s.field("z_index", &self.z_index);
}
s.finish()
}
}
impl_option_fields!(Modifier);
impl Modifier {
pub fn new() -> Self {
Self::default()
}
pub fn key(mut self, key: u64) -> Self {
self.key = Some(key);
self
}
pub fn size(mut self, w: f32, h: f32) -> Self {
self.size = Some(Size {
width: w,
height: h,
});
self
}
pub fn width(mut self, w: f32) -> Self {
self.width = Some(w);
self
}
pub fn height(mut self, h: f32) -> Self {
self.height = Some(h);
self
}
pub fn required_size(mut self, w: f32, h: f32) -> Self {
self.required_size = Some(Size {
width: w,
height: h,
});
self
}
pub fn required_width_in(mut self, min: f32, max: f32) -> Self {
self.required_min_width = Some(min.max(0.0));
self.required_max_width = Some(max.max(0.0));
self
}
pub fn required_height_in(mut self, min: f32, max: f32) -> Self {
self.required_min_height = Some(min.max(0.0));
self.required_max_height = Some(max.max(0.0));
self
}
pub fn required_min_width(mut self, w: f32) -> Self {
self.required_min_width = Some(w.max(0.0));
self
}
pub fn required_max_width(mut self, w: f32) -> Self {
self.required_max_width = Some(w.max(0.0));
self
}
pub fn required_min_height(mut self, h: f32) -> Self {
self.required_min_height = Some(h.max(0.0));
self
}
pub fn required_max_height(mut self, h: f32) -> Self {
self.required_max_height = Some(h.max(0.0));
self
}
pub fn default_min_size(mut self, w: f32, h: f32) -> Self {
self.default_min_width = Some(w.max(0.0));
self.default_min_height = Some(h.max(0.0));
self
}
pub fn fill_max_size(mut self) -> Self {
self.fill_max = Some(1.0);
self
}
pub fn fill_max_size_frac(mut self, fraction: f32) -> Self {
self.fill_max = Some(fraction.clamp(0.0, 1.0));
self
}
pub fn fill_max_width(mut self) -> Self {
self.fill_max_w = Some(1.0);
self
}
pub fn fill_max_width_frac(mut self, fraction: f32) -> Self {
self.fill_max_w = Some(fraction.clamp(0.0, 1.0));
self
}
pub fn fill_max_height(mut self) -> Self {
self.fill_max_h = Some(1.0);
self
}
pub fn fill_max_height_frac(mut self, fraction: f32) -> Self {
self.fill_max_h = Some(fraction.clamp(0.0, 1.0));
self
}
pub fn padding(mut self, v: f32) -> Self {
self.padding = Some(v);
self
}
pub fn padding_values(mut self, padding: PaddingValues) -> Self {
self.padding_values = Some(padding);
self
}
pub fn ime_padding(mut self) -> Self {
let insets = crate::locals::window_insets();
let scale = crate::locals::density().scale * crate::locals::ui_scale().0;
let mut p = self.padding_values.unwrap_or_default();
p.bottom += insets.ime_bottom / scale;
self.padding_values = Some(p);
self
}
pub fn system_bars_padding(mut self) -> Self {
let insets = crate::locals::window_insets();
let scale = crate::locals::density().scale * crate::locals::ui_scale().0;
let mut p = self.padding_values.unwrap_or_default();
p.top += insets.top / scale;
p.bottom += insets.bottom / scale;
self.padding_values = Some(p);
self
}
pub fn status_bars_padding(mut self) -> Self {
let insets = crate::locals::window_insets();
let scale = crate::locals::density().scale * crate::locals::ui_scale().0;
let mut p = self.padding_values.unwrap_or_default();
p.top += insets.top / scale;
self.padding_values = Some(p);
self
}
pub fn navigation_bars_padding(mut self) -> Self {
let insets = crate::locals::window_insets();
let scale = crate::locals::density().scale * crate::locals::ui_scale().0;
let mut p = self.padding_values.unwrap_or_default();
p.bottom += insets.bottom / scale;
self.padding_values = Some(p);
self
}
pub fn min_size(mut self, w: f32, h: f32) -> Self {
self.min_width = Some(w);
self.min_height = Some(h);
self
}
pub fn max_size(mut self, w: f32, h: f32) -> Self {
self.max_width = Some(w);
self.max_height = Some(h);
self
}
pub fn min_width(mut self, w: f32) -> Self {
self.min_width = Some(w);
self
}
pub fn min_height(mut self, h: f32) -> Self {
self.min_height = Some(h);
self
}
pub fn max_width(mut self, w: f32) -> Self {
self.max_width = Some(w);
self
}
pub fn max_height(mut self, h: f32) -> Self {
self.max_height = Some(h);
self
}
pub fn background(mut self, color: Color) -> Self {
self.background = Some(Brush::Solid(color));
self
}
pub fn background_brush(mut self, brush: Brush) -> Self {
self.background = Some(brush);
self
}
pub fn border(mut self, width: f32, color: Color, radius: f32) -> Self {
self.border = Some(Border {
width,
color,
radius: [radius; 4],
});
self
}
pub fn border_radii(mut self, width: f32, color: Color, radii: [f32; 4]) -> Self {
self.border = Some(Border {
width,
color,
radius: radii,
});
self
}
pub fn flex_grow(mut self, v: f32) -> Self {
self.flex_grow = Some(v);
self
}
pub fn flex_shrink(mut self, v: f32) -> Self {
self.flex_shrink = Some(v);
self
}
pub fn flex_basis(mut self, v: f32) -> Self {
self.flex_basis = Some(v);
self
}
pub fn flex_wrap(mut self, w: FlexWrap) -> Self {
self.flex_wrap = Some(w);
self
}
pub fn flex_dir(mut self, d: FlexDirection) -> Self {
self.flex_dir = Some(d);
self
}
pub fn gap(mut self, v: f32) -> Self {
let v = v.max(0.0);
self.gap = Some(v);
self.row_gap = Some(v);
self.column_gap = Some(v);
self
}
pub fn row_gap(mut self, v: f32) -> Self {
self.row_gap = Some(v.max(0.0));
self
}
pub fn column_gap(mut self, v: f32) -> Self {
self.column_gap = Some(v.max(0.0));
self
}
pub fn align_self(mut self, a: AlignSelf) -> Self {
self.align_self = Some(a);
self
}
pub fn align_self_center(mut self) -> Self {
self.align_self = Some(AlignSelf::Center);
self
}
pub fn justify_content(mut self, j: JustifyContent) -> Self {
self.justify_content = Some(j);
self
}
pub fn align_items(mut self, a: AlignItems) -> Self {
self.align_items_container = Some(a);
self
}
pub fn align_content(mut self, a: AlignContent) -> Self {
self.align_content = Some(a);
self
}
pub fn clip_rounded(mut self, radius: f32) -> Self {
self.clip_rounded = Some([radius; 4]);
self
}
pub fn clip_rounded_radii(mut self, radii: [f32; 4]) -> Self {
self.clip_rounded = Some(radii);
self
}
pub fn z_index(mut self, z: f32) -> Self {
self.z_index = z;
self
}
pub fn render_z_index(mut self, z: f32) -> Self {
self.render_z_index = Some(z);
self
}
pub fn input_blocker(mut self) -> Self {
self.input_blocker = true;
self
}
pub fn hit_passthrough(mut self) -> Self {
self.hit_passthrough = true;
self
}
pub fn clickable(mut self) -> Self {
self.click = true;
self
}
pub fn clickable_with_source(mut self, source: &MutableInteractionSource) -> Self {
self.click = true;
self.interaction_source = Some(source.source());
self
}
pub fn state_colors(mut self, colors: StateColors) -> Self {
self.state_colors = Some(colors);
self
}
pub fn state_elevation(mut self, elev: StateElevation) -> Self {
self.state_elevation = Some(elev);
self
}
pub fn disabled(mut self) -> Self {
self.disabled = true;
self
}
pub fn enabled(mut self, enabled: bool) -> Self {
self.disabled = !enabled;
self
}
pub fn focusable(mut self, focusable: bool) -> Self {
self.focusable = Some(focusable);
self
}
pub fn focus_group(mut self) -> Self {
self.focus_group = true;
self
}
pub fn interaction_source(mut self, source: &MutableInteractionSource) -> Self {
self.interaction_source = Some(source.source());
self
}
pub fn hoverable(
mut self,
on_enter: impl Fn() + 'static,
on_leave: impl Fn() + 'static,
) -> Self {
self.on_pointer_enter = Some(Rc::new(move |_| on_enter()));
self.on_pointer_leave = Some(Rc::new(move |_| on_leave()));
self
}
pub fn hoverable_with_source(mut self, source: &MutableInteractionSource) -> Self {
self.interaction_source = Some(source.source());
self
}
pub fn propagate_min_constraints(mut self, propagate: bool) -> Self {
self.propagate_min = propagate;
self
}
pub fn on_scroll(mut self, f: impl Fn(Vec2) -> Vec2 + 'static) -> Self {
self.on_scroll = Some(Rc::new(f));
self
}
pub fn on_pointer_down(mut self, f: impl Fn(PointerEvent) + 'static) -> Self {
self.on_pointer_down = Some(Rc::new(f));
self
}
pub fn on_pointer_move(mut self, f: impl Fn(PointerEvent) + 'static) -> Self {
self.on_pointer_move = Some(Rc::new(f));
self
}
pub fn on_pointer_up(mut self, f: impl Fn(PointerEvent) + 'static) -> Self {
self.on_pointer_up = Some(Rc::new(f));
self
}
pub fn on_pointer_cancel(mut self, f: impl Fn(PointerEvent) + 'static) -> Self {
self.on_pointer_cancel = Some(Rc::new(f));
self
}
pub fn on_pointer_enter(mut self, f: impl Fn(PointerEvent) + 'static) -> Self {
self.on_pointer_enter = Some(Rc::new(f));
self
}
pub fn on_pointer_leave(mut self, f: impl Fn(PointerEvent) + 'static) -> Self {
self.on_pointer_leave = Some(Rc::new(f));
self
}
pub fn on_double_click(mut self, f: impl Fn() + 'static) -> Self {
self.on_double_click = Some(Rc::new(f));
self
}
pub fn on_long_click(mut self, f: impl Fn() + 'static) -> Self {
self.on_long_click = Some(Rc::new(f));
self
}
pub fn semantics(mut self, s: crate::Semantics) -> Self {
self.semantics = Some(s);
self
}
pub fn alpha(mut self, a: f32) -> Self {
self.alpha = Some(a);
self
}
pub fn graphics_layer(mut self, alpha: f32) -> Self {
self.graphics_layer = Some(alpha.clamp(0.0, 1.0));
self
}
pub fn shadow(mut self, blur_radius: f32, offset_y: f32) -> Self {
self.shadow = Some(ShadowSpec {
blur_radius: blur_radius.max(0.0),
offset_y,
color: Color(0, 0, 0, 64),
});
self
}
pub fn shadow_with_color(mut self, blur_radius: f32, offset_y: f32, color: Color) -> Self {
self.shadow = Some(ShadowSpec {
blur_radius: blur_radius.max(0.0),
offset_y,
color,
});
self
}
pub fn elevation(mut self, level: f32) -> Self {
if level <= 0.0 {
self.shadow = None;
return self;
}
self.shadow = Some(ShadowSpec {
blur_radius: level * 2.0,
offset_y: level * 0.5,
color: Color(0, 0, 0, (level * 8.0).clamp(8.0, 80.0) as u8),
});
self
}
pub fn transform(mut self, t: Transform) -> Self {
self.transform = Some(t);
self
}
pub fn grid(mut self, columns: usize, row_gap: f32, column_gap: f32) -> Self {
self.grid = Some(GridConfig {
columns,
row_gap,
column_gap,
});
self
}
pub fn grid_span(mut self, col_span: u16, row_span: u16) -> Self {
self.grid_col_span = Some(col_span);
self.grid_row_span = Some(row_span);
self
}
pub fn absolute(mut self) -> Self {
self.position_type = Some(PositionType::Absolute);
self
}
pub fn offset(
mut self,
left: Option<f32>,
top: Option<f32>,
right: Option<f32>,
bottom: Option<f32>,
) -> Self {
self.offset_left = left;
self.offset_top = top;
self.offset_right = right;
self.offset_bottom = bottom;
self
}
pub fn offset_left(mut self, v: f32) -> Self {
self.offset_left = Some(v);
self
}
pub fn offset_right(mut self, v: f32) -> Self {
self.offset_right = Some(v);
self
}
pub fn offset_top(mut self, v: f32) -> Self {
self.offset_top = Some(v);
self
}
pub fn offset_bottom(mut self, v: f32) -> Self {
self.offset_bottom = Some(v);
self
}
pub fn margin(mut self, v: f32) -> Self {
self.margin_left = Some(v);
self.margin_right = Some(v);
self.margin_top = Some(v);
self.margin_bottom = Some(v);
self
}
pub fn margin_horizontal(mut self, v: f32) -> Self {
self.margin_left = Some(v);
self.margin_right = Some(v);
self
}
pub fn margin_vertical(mut self, v: f32) -> Self {
self.margin_top = Some(v);
self.margin_bottom = Some(v);
self
}
pub fn aspect_ratio(mut self, ratio: f32) -> Self {
self.aspect_ratio = Some(ratio);
self
}
pub fn intrinsic_width(mut self, mode: IntrinsicSize) -> Self {
self.intrinsic_width = Some(mode);
self
}
pub fn intrinsic_height(mut self, mode: IntrinsicSize) -> Self {
self.intrinsic_height = Some(mode);
self
}
pub fn painter(mut self, f: impl Fn(&mut crate::Scene, crate::Rect, f32) + 'static) -> Self {
self.painter = Some(Rc::new(f));
self
}
pub fn scale(self, s: f32) -> Self {
self.scale2(s, s)
}
pub fn scale2(mut self, sx: f32, sy: f32) -> Self {
let mut t = self.transform.unwrap_or_else(Transform::identity);
t.scale_x *= sx;
t.scale_y *= sy;
self.transform = Some(t);
self
}
pub fn translate(mut self, x: f32, y: f32) -> Self {
let t = self.transform.unwrap_or_else(Transform::identity);
self.transform = Some(t.combine(&Transform::translate(x, y)));
self
}
pub fn translate_vec2(self, v: Vec2) -> Self {
self.translate(v.x, v.y)
}
pub fn rotate(mut self, radians: f32) -> Self {
let mut t = self.transform.unwrap_or_else(Transform::identity);
t.rotate += radians;
self.transform = Some(t);
self
}
pub fn weight(mut self, w: f32) -> Self {
let w = w.max(0.0);
self.flex_grow = Some(w);
self.flex_shrink = Some(1.0);
self.flex_basis = Some(0.0);
self
}
pub fn repaint_boundary(mut self) -> Self {
self.repaint_boundary = true;
self
}
pub fn on_action(mut self, f: impl Fn(crate::shortcuts::Action) -> bool + 'static) -> Self {
self.on_action = Some(Rc::new(f));
self
}
pub fn on_drag_start(
mut self,
f: impl Fn(crate::dnd::DragStart) -> Option<crate::dnd::DragPayload> + 'static,
) -> Self {
self.on_drag_start = Some(Rc::new(f));
self
}
pub fn on_drag_end(mut self, f: impl Fn(crate::dnd::DragEnd) + 'static) -> Self {
self.on_drag_end = Some(Rc::new(f));
self
}
pub fn on_drag_enter(mut self, f: impl Fn(crate::dnd::DragOver) + 'static) -> Self {
self.on_drag_enter = Some(Rc::new(f));
self
}
pub fn on_drag_over(mut self, f: impl Fn(crate::dnd::DragOver) + 'static) -> Self {
self.on_drag_over = Some(Rc::new(f));
self
}
pub fn on_drag_leave(mut self, f: impl Fn(crate::dnd::DragOver) + 'static) -> Self {
self.on_drag_leave = Some(Rc::new(f));
self
}
pub fn on_drop(mut self, f: impl Fn(crate::dnd::DropEvent) -> bool + 'static) -> Self {
self.on_drop = Some(Rc::new(f));
self
}
pub fn cursor(mut self, c: crate::CursorIcon) -> Self {
self.cursor = Some(c);
self
}
pub fn animate_content_size(mut self, spec: AnimationSpec) -> Self {
self.animate_content_size = Some(spec);
self
}
pub fn focus_requester(mut self, fr: crate::runtime::FocusRequester) -> Self {
self.focus_requester = Some(fr);
self
}
pub fn on_focus_changed(mut self, f: impl Fn(bool) + 'static) -> Self {
self.on_focus_changed = Some(Rc::new(f));
self
}
pub fn text_input(mut self, config: TextInputConfig) -> Self {
self.text_input = Some(config);
self
}
pub fn indication(mut self, factory: Rc<dyn IndicationNodeFactory>) -> Self {
self.indication = Some(factory);
self
}
}