#![allow(non_snake_case)]
use std::fmt;
use std::hash::{Hash, Hasher};
use std::rc::Rc;
#[cfg(not(target_arch = "wasm32"))]
use std::sync::OnceLock;
use cranpose_core::hash::default;
mod alignment;
mod background;
mod blur;
mod chain;
mod clickable;
mod draw_cache;
mod fill;
mod focus;
mod graphics_layer;
mod local;
mod offset;
mod padding;
mod pointer_input;
mod scroll;
mod semantics;
mod shadow;
mod size;
mod slices;
mod weight;
pub use crate::draw::{DrawCacheBuilder, DrawCommand};
#[allow(unused_imports)]
pub use chain::{ModifierChainHandle, ModifierChainInspectorNode, ModifierLocalsHandle};
pub use cranpose_foundation::{
modifier_element, AnyModifierElement, DynModifierElement, FocusState, PointerEvent,
PointerEventKind, SemanticsConfiguration,
};
use cranpose_foundation::{ModifierNodeElement, NodeCapabilities};
#[allow(unused_imports)]
pub use cranpose_ui_graphics::{
BlendMode, BlurredEdgeTreatment, Brush, Color, ColorFilter, CompositingStrategy, CornerRadii,
CutDirection, Dp, DpOffset, EdgeInsets, GradientCutMaskSpec, GradientFadeMaskSpec,
GraphicsLayer, LayerShape, Point, Rect, RenderEffect, RoundedCornerShape, RuntimeShader,
Shadow, ShadowScope, Size, TransformOrigin,
};
use cranpose_ui_layout::{Alignment, HorizontalAlignment, IntrinsicSize, VerticalAlignment};
#[allow(unused_imports)]
pub use focus::{FocusDirection, FocusRequester};
pub(crate) use local::{
ModifierLocalAncestorResolver, ModifierLocalSource, ModifierLocalToken, ResolvedModifierLocal,
};
#[allow(unused_imports)]
pub use local::{ModifierLocalKey, ModifierLocalReadScope};
#[allow(unused_imports)]
pub use pointer_input::{AwaitPointerEventScope, PointerInputScope};
pub use semantics::{collect_semantics_from_chain, collect_semantics_from_modifier};
pub use slices::{
collect_modifier_slices, collect_modifier_slices_into, collect_slices_from_modifier,
ModifierNodeSlices, ModifierNodeSlicesDebugStats,
};
#[cfg(feature = "test-helpers")]
pub use scroll::{last_fling_velocity, reset_last_fling_velocity};
use crate::modifier_nodes::ClipToBoundsElement;
use focus::{FocusRequesterElement, FocusTargetElement};
use local::{ModifierLocalConsumerElement, ModifierLocalProviderElement};
use semantics::SemanticsElement;
#[derive(Clone, Debug, Default)]
pub struct InspectorInfo {
properties: Vec<InspectorProperty>,
}
impl InspectorInfo {
pub fn new() -> Self {
Self::default()
}
pub fn add_property<V: Into<String>>(&mut self, name: &'static str, value: V) {
self.properties.push(InspectorProperty {
name,
value: value.into(),
});
}
pub fn properties(&self) -> &[InspectorProperty] {
&self.properties
}
pub fn is_empty(&self) -> bool {
self.properties.is_empty()
}
pub fn add_dimension(&mut self, name: &'static str, constraint: DimensionConstraint) {
self.add_property(name, describe_dimension(constraint));
}
pub fn add_offset_components(
&mut self,
x_name: &'static str,
y_name: &'static str,
offset: Point,
) {
self.add_property(x_name, offset.x.to_string());
self.add_property(y_name, offset.y.to_string());
}
pub fn add_alignment<A>(&mut self, name: &'static str, alignment: A)
where
A: fmt::Debug,
{
self.add_property(name, format!("{alignment:?}"));
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct InspectorProperty {
pub name: &'static str,
pub value: String,
}
#[derive(Clone, Debug, PartialEq)]
pub struct ModifierInspectorRecord {
pub name: &'static str,
pub properties: Vec<InspectorProperty>,
}
#[derive(Clone, Debug)]
pub(crate) struct InspectorMetadata {
name: &'static str,
info: InspectorInfo,
}
impl InspectorMetadata {
pub(crate) fn new<F>(name: &'static str, recorder: F) -> Self
where
F: FnOnce(&mut InspectorInfo),
{
let mut info = InspectorInfo::new();
recorder(&mut info);
Self { name, info }
}
fn is_empty(&self) -> bool {
self.info.is_empty()
}
fn to_record(&self) -> ModifierInspectorRecord {
ModifierInspectorRecord {
name: self.name,
properties: self.info.properties().to_vec(),
}
}
}
fn describe_dimension(constraint: DimensionConstraint) -> String {
match constraint {
DimensionConstraint::Unspecified => "unspecified".to_string(),
DimensionConstraint::Points(value) => value.to_string(),
DimensionConstraint::Fraction(value) => format!("fraction({value})"),
DimensionConstraint::Intrinsic(size) => format!("intrinsic({size:?})"),
}
}
pub(crate) fn inspector_metadata<F>(name: &'static str, recorder: F) -> InspectorMetadata
where
F: FnOnce(&mut InspectorInfo),
{
if !inspector_metadata_enabled() {
return InspectorMetadata::new(name, |_| {});
}
InspectorMetadata::new(name, recorder)
}
pub(crate) fn modifier_debug_enabled() -> bool {
#[cfg(not(target_arch = "wasm32"))]
{
static ENV_DEBUG: OnceLock<bool> = OnceLock::new();
*ENV_DEBUG.get_or_init(|| std::env::var_os("COMPOSE_DEBUG_MODIFIERS").is_some())
}
#[cfg(target_arch = "wasm32")]
{
false
}
}
fn inspector_metadata_enabled() -> bool {
cfg!(test) || modifier_debug_enabled()
}
#[derive(Clone)]
enum ModifierKind {
Empty,
Single {
elements: Rc<Vec<DynModifierElement>>,
inspector: Rc<Vec<InspectorMetadata>>,
},
}
const FINGERPRINT_KIND_EMPTY: u8 = 0;
const FINGERPRINT_KIND_SINGLE: u8 = 1;
const FINGERPRINT_EMPTY_STRICT_SEED: u64 = 0x243f_6a88_85a3_08d3;
const FINGERPRINT_EMPTY_STRUCTURAL_SEED: u64 = 0x1319_8a2e_0370_7344;
const FINGERPRINT_SINGLE_STRICT_SEED: u64 = 0xa409_3822_299f_31d0;
const FINGERPRINT_SINGLE_STRUCTURAL_SEED: u64 = 0x082e_fa98_ec4e_6c89;
const FINGERPRINT_SEQUENCE_MUL: u64 = 0x9e37_79b1_85eb_ca87;
const FINGERPRINT_STRICT_UPDATE_TAG: u64 = 0xdbe6_d5d5_fe4c_ce2f;
const FINGERPRINT_STRUCTURAL_DRAW_ONLY_TAG: u64 = 0x94d0_49bb_1331_11eb;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct ModifierFingerprints {
strict: u64,
structural: u64,
}
#[inline]
fn mix_fingerprint_bits(mut value: u64) -> u64 {
value ^= value >> 33;
value = value.wrapping_mul(0xff51_afd7_ed55_8ccd);
value ^= value >> 33;
value = value.wrapping_mul(0xc4ce_b9fe_1a85_ec53);
value ^ (value >> 33)
}
#[inline]
fn fold_fingerprint(state: u64, value: u64) -> u64 {
mix_fingerprint_bits(state ^ value.wrapping_add(FINGERPRINT_SEQUENCE_MUL))
.wrapping_mul(FINGERPRINT_SEQUENCE_MUL)
}
#[inline]
fn empty_fingerprints() -> ModifierFingerprints {
ModifierFingerprints {
strict: fold_fingerprint(FINGERPRINT_EMPTY_STRICT_SEED, FINGERPRINT_KIND_EMPTY as u64),
structural: fold_fingerprint(
FINGERPRINT_EMPTY_STRUCTURAL_SEED,
FINGERPRINT_KIND_EMPTY as u64,
),
}
}
#[inline]
fn single_fingerprint_seed() -> ModifierFingerprints {
let strict = fold_fingerprint(
FINGERPRINT_SINGLE_STRICT_SEED,
FINGERPRINT_KIND_SINGLE as u64,
);
let structural = fold_fingerprint(
FINGERPRINT_SINGLE_STRUCTURAL_SEED,
FINGERPRINT_KIND_SINGLE as u64,
);
ModifierFingerprints { strict, structural }
}
#[inline]
fn element_common_fingerprint(element: &DynModifierElement) -> u64 {
let mut hasher = default::new();
element.element_type().hash(&mut hasher);
element.capabilities().bits().hash(&mut hasher);
hasher.finish()
}
#[inline]
fn element_fingerprints(element: &DynModifierElement) -> ModifierFingerprints {
let common = element_common_fingerprint(element);
let requires_update = element.requires_update();
let strict_payload = if requires_update {
let element_ptr = Rc::as_ptr(element) as *const () as usize as u64;
element_ptr ^ FINGERPRINT_STRICT_UPDATE_TAG
} else {
element.hash_code()
};
let strict = mix_fingerprint_bits(common ^ strict_payload);
let is_draw_only = element.capabilities() == NodeCapabilities::DRAW;
let structural_payload = if is_draw_only {
FINGERPRINT_STRUCTURAL_DRAW_ONLY_TAG
} else {
element.hash_code()
};
let structural = mix_fingerprint_bits(common ^ structural_payload);
ModifierFingerprints { strict, structural }
}
#[inline]
fn append_fingerprints(
mut fingerprints: ModifierFingerprints,
elements: &[DynModifierElement],
) -> ModifierFingerprints {
for element in elements {
let element_fingerprints = element_fingerprints(element);
fingerprints.strict = fold_fingerprint(fingerprints.strict, element_fingerprints.strict);
fingerprints.structural =
fold_fingerprint(fingerprints.structural, element_fingerprints.structural);
}
fingerprints
}
fn single_fingerprints(elements: &[DynModifierElement]) -> ModifierFingerprints {
append_fingerprints(single_fingerprint_seed(), elements)
}
pub struct ModifierElementIterator<'a> {
inner: std::slice::Iter<'a, DynModifierElement>,
}
impl<'a> Iterator for ModifierElementIterator<'a> {
type Item = &'a DynModifierElement;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl ExactSizeIterator for ModifierElementIterator<'_> {}
pub(crate) struct ModifierInspectorIterator<'a> {
inner: std::slice::Iter<'a, InspectorMetadata>,
}
impl<'a> Iterator for ModifierInspectorIterator<'a> {
type Item = &'a InspectorMetadata;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl ExactSizeIterator for ModifierInspectorIterator<'_> {}
#[derive(Clone)]
pub struct Modifier {
kind: ModifierKind,
strict_fingerprint: u64,
structural_fingerprint: u64,
element_count: usize,
}
impl Default for Modifier {
fn default() -> Self {
let fingerprints = empty_fingerprints();
Self {
kind: ModifierKind::Empty,
strict_fingerprint: fingerprints.strict,
structural_fingerprint: fingerprints.structural,
element_count: 0,
}
}
}
impl Modifier {
pub fn empty() -> Self {
Self::default()
}
pub fn clip_to_bounds(self) -> Self {
let modifier = Self::with_element(ClipToBoundsElement::new()).with_inspector_metadata(
inspector_metadata("clipToBounds", |info| {
info.add_property("clipToBounds", "true");
}),
);
self.then(modifier)
}
pub fn modifier_local_provider<T, F>(self, key: ModifierLocalKey<T>, value: F) -> Self
where
T: 'static,
F: Fn() -> T + 'static,
{
let element = ModifierLocalProviderElement::new(key, value);
let modifier = Modifier::from_parts(vec![modifier_element(element)]);
self.then(modifier)
}
pub fn modifier_local_consumer<F>(self, consumer: F) -> Self
where
F: for<'scope> Fn(&mut ModifierLocalReadScope<'scope>) + 'static,
{
let element = ModifierLocalConsumerElement::new(consumer);
let modifier = Modifier::from_parts(vec![modifier_element(element)]);
self.then(modifier)
}
pub fn semantics<F>(self, recorder: F) -> Self
where
F: Fn(&mut SemanticsConfiguration) + 'static,
{
let mut preview = SemanticsConfiguration::default();
recorder(&mut preview);
let description = preview.content_description.clone();
let is_button = preview.is_button;
let is_clickable = preview.is_clickable;
let metadata = inspector_metadata("semantics", move |info| {
if let Some(desc) = &description {
info.add_property("contentDescription", desc.clone());
}
if is_button {
info.add_property("isButton", "true");
}
if is_clickable {
info.add_property("isClickable", "true");
}
});
let element = SemanticsElement::new(recorder);
let modifier =
Modifier::from_parts(vec![modifier_element(element)]).with_inspector_metadata(metadata);
self.then(modifier)
}
pub fn focus_target(self) -> Self {
let element = FocusTargetElement::new();
let modifier = Modifier::from_parts(vec![modifier_element(element)]);
self.then(modifier)
}
pub fn on_focus_changed<F>(self, callback: F) -> Self
where
F: Fn(FocusState) + 'static,
{
let element = FocusTargetElement::with_callback(callback);
let modifier = Modifier::from_parts(vec![modifier_element(element)]);
self.then(modifier)
}
pub fn focus_requester(self, requester: &FocusRequester) -> Self {
let element = FocusRequesterElement::new(requester.id());
let modifier = Modifier::from_parts(vec![modifier_element(element)]);
self.then(modifier)
}
pub fn debug_chain(self, tag: &'static str) -> Self {
use cranpose_foundation::{ModifierNode, ModifierNodeContext, NodeCapabilities, NodeState};
#[derive(Clone)]
struct DebugChainElement {
tag: &'static str,
}
impl fmt::Debug for DebugChainElement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("DebugChainElement")
.field("tag", &self.tag)
.finish()
}
}
impl PartialEq for DebugChainElement {
fn eq(&self, other: &Self) -> bool {
self.tag == other.tag
}
}
impl Eq for DebugChainElement {}
impl std::hash::Hash for DebugChainElement {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.tag.hash(state);
}
}
impl ModifierNodeElement for DebugChainElement {
type Node = DebugChainNode;
fn create(&self) -> Self::Node {
DebugChainNode::new(self.tag)
}
fn update(&self, node: &mut Self::Node) {
node.tag = self.tag;
}
fn capabilities(&self) -> NodeCapabilities {
NodeCapabilities::empty()
}
}
struct DebugChainNode {
tag: &'static str,
state: NodeState,
}
impl DebugChainNode {
fn new(tag: &'static str) -> Self {
Self {
tag,
state: NodeState::new(),
}
}
}
impl ModifierNode for DebugChainNode {
fn on_attach(&mut self, _context: &mut dyn ModifierNodeContext) {
eprintln!("[debug_chain:{}] Modifier chain attached", self.tag);
}
fn on_detach(&mut self) {
eprintln!("[debug_chain:{}] Modifier chain detached", self.tag);
}
fn on_reset(&mut self) {
eprintln!("[debug_chain:{}] Modifier chain reset", self.tag);
}
}
impl cranpose_foundation::DelegatableNode for DebugChainNode {
fn node_state(&self) -> &NodeState {
&self.state
}
}
let element = DebugChainElement { tag };
let modifier = Modifier::from_parts(vec![modifier_element(element)]);
self.then(modifier)
.with_inspector_metadata(inspector_metadata("debugChain", move |info| {
info.add_property("tag", tag);
}))
}
pub fn then(&self, next: Modifier) -> Modifier {
if self.is_trivially_empty() {
return next;
}
if next.is_trivially_empty() {
return self.clone();
}
let (self_elements, self_inspector) = match &self.kind {
ModifierKind::Empty => unreachable!(),
ModifierKind::Single {
elements,
inspector,
..
} => (elements.as_ref(), inspector.as_ref()),
};
let (next_elements, next_inspector) = match &next.kind {
ModifierKind::Empty => unreachable!(),
ModifierKind::Single {
elements,
inspector,
..
} => (elements.as_ref(), inspector.as_ref()),
};
let mut merged_elements = Vec::with_capacity(self_elements.len() + next_elements.len());
merged_elements.extend_from_slice(self_elements);
merged_elements.extend_from_slice(next_elements);
let mut merged_inspector = Vec::with_capacity(self_inspector.len() + next_inspector.len());
merged_inspector.extend_from_slice(self_inspector);
merged_inspector.extend_from_slice(next_inspector);
let fingerprints = append_fingerprints(
ModifierFingerprints {
strict: self.strict_fingerprint,
structural: self.structural_fingerprint,
},
next_elements,
);
Modifier {
kind: ModifierKind::Single {
elements: Rc::new(merged_elements),
inspector: Rc::new(merged_inspector),
},
strict_fingerprint: fingerprints.strict,
structural_fingerprint: fingerprints.structural,
element_count: self.element_count + next.element_count,
}
}
pub(crate) fn iter_elements(&self) -> ModifierElementIterator<'_> {
match &self.kind {
ModifierKind::Empty => ModifierElementIterator { inner: [].iter() },
ModifierKind::Single { elements, .. } => ModifierElementIterator {
inner: elements.iter(),
},
}
}
pub(crate) fn iter_inspector_metadata(&self) -> ModifierInspectorIterator<'_> {
match &self.kind {
ModifierKind::Empty => ModifierInspectorIterator { inner: [].iter() },
ModifierKind::Single { inspector, .. } => ModifierInspectorIterator {
inner: inspector.iter(),
},
}
}
#[cfg(test)]
pub(crate) fn elements(&self) -> Vec<DynModifierElement> {
match &self.kind {
ModifierKind::Empty => Vec::new(),
ModifierKind::Single { elements, .. } => elements.as_ref().clone(),
}
}
pub(crate) fn inspector_metadata(&self) -> Vec<InspectorMetadata> {
match &self.kind {
ModifierKind::Empty => Vec::new(),
ModifierKind::Single { inspector, .. } => inspector.as_ref().clone(),
}
}
pub(crate) fn rehouse_for_live_compaction(&self) -> Self {
match &self.kind {
ModifierKind::Empty => Self::default(),
ModifierKind::Single {
elements,
inspector,
} => Self {
kind: ModifierKind::Single {
elements: Rc::new(elements.iter().cloned().collect()),
inspector: Rc::new(inspector.as_ref().clone()),
},
strict_fingerprint: self.strict_fingerprint,
structural_fingerprint: self.structural_fingerprint,
element_count: self.element_count,
},
}
}
pub fn total_padding(&self) -> f32 {
let padding = self.padding_values();
padding
.left
.max(padding.right)
.max(padding.top)
.max(padding.bottom)
}
pub fn explicit_size(&self) -> Option<Size> {
let props = self.layout_properties();
match (props.width, props.height) {
(DimensionConstraint::Points(width), DimensionConstraint::Points(height)) => {
Some(Size { width, height })
}
_ => None,
}
}
pub fn padding_values(&self) -> EdgeInsets {
self.resolved_modifiers().padding()
}
pub(crate) fn layout_properties(&self) -> LayoutProperties {
self.resolved_modifiers().layout_properties()
}
pub fn box_alignment(&self) -> Option<Alignment> {
self.layout_properties().box_alignment()
}
pub fn column_alignment(&self) -> Option<HorizontalAlignment> {
self.layout_properties().column_alignment()
}
pub fn row_alignment(&self) -> Option<VerticalAlignment> {
self.layout_properties().row_alignment()
}
pub fn draw_commands(&self) -> Vec<DrawCommand> {
collect_slices_from_modifier(self).draw_commands().to_vec()
}
pub fn clips_to_bounds(&self) -> bool {
collect_slices_from_modifier(self).clip_to_bounds()
}
pub fn collect_inspector_records(&self) -> Vec<ModifierInspectorRecord> {
self.inspector_metadata()
.iter()
.map(|metadata| metadata.to_record())
.collect()
}
pub fn resolved_modifiers(&self) -> ResolvedModifiers {
let mut handle = ModifierChainHandle::new();
let _ = handle.update(self);
handle.resolved_modifiers()
}
fn with_element<E>(element: E) -> Self
where
E: ModifierNodeElement,
{
let dyn_element = modifier_element(element);
Self::from_parts(vec![dyn_element])
}
pub(crate) fn from_parts(elements: Vec<DynModifierElement>) -> Self {
if elements.is_empty() {
Self::default()
} else {
let element_count = elements.len();
let fingerprints = single_fingerprints(elements.as_slice());
Self {
kind: ModifierKind::Single {
elements: Rc::new(elements),
inspector: Rc::new(Vec::new()),
},
strict_fingerprint: fingerprints.strict,
structural_fingerprint: fingerprints.structural,
element_count,
}
}
}
fn is_trivially_empty(&self) -> bool {
matches!(self.kind, ModifierKind::Empty)
}
pub(crate) fn with_inspector_metadata(self, metadata: InspectorMetadata) -> Self {
if metadata.is_empty() {
return self;
}
match self.kind {
ModifierKind::Empty => self,
ModifierKind::Single {
elements,
inspector,
} => {
let mut new_inspector = inspector.as_ref().clone();
new_inspector.push(metadata);
Self {
kind: ModifierKind::Single {
elements,
inspector: Rc::new(new_inspector),
},
strict_fingerprint: self.strict_fingerprint,
structural_fingerprint: self.structural_fingerprint,
element_count: self.element_count,
}
}
}
}
pub fn structural_eq(&self, other: &Self) -> bool {
self.eq_internal(other, false)
}
fn eq_internal(&self, other: &Self, consider_always_update: bool) -> bool {
if self.element_count != other.element_count {
return false;
}
if consider_always_update {
if self.strict_fingerprint != other.strict_fingerprint {
return false;
}
} else if self.structural_fingerprint != other.structural_fingerprint {
return false;
}
match (&self.kind, &other.kind) {
(ModifierKind::Empty, ModifierKind::Empty) => true,
(
ModifierKind::Single {
elements: e1,
inspector: _,
},
ModifierKind::Single {
elements: e2,
inspector: _,
},
) => {
if Rc::ptr_eq(e1, e2) {
return true;
}
if e1.len() != e2.len() {
return false;
}
for (a, b) in e1.iter().zip(e2.iter()) {
if !consider_always_update
&& a.element_type() == b.element_type()
&& a.capabilities() == NodeCapabilities::DRAW
&& b.capabilities() == NodeCapabilities::DRAW
{
continue;
}
if consider_always_update && (a.requires_update() || b.requires_update()) {
if !Rc::ptr_eq(a, b) {
return false;
}
continue;
}
if !a.equals_element(&**b) {
return false;
}
}
true
}
_ => false,
}
}
}
impl PartialEq for Modifier {
fn eq(&self, other: &Self) -> bool {
self.eq_internal(other, true)
}
}
impl Eq for Modifier {}
impl fmt::Display for Modifier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.kind {
ModifierKind::Empty => write!(f, "Modifier.empty"),
ModifierKind::Single { elements, .. } => {
if elements.is_empty() {
return write!(f, "Modifier.empty");
}
write!(f, "Modifier[")?;
for (index, element) in elements.iter().enumerate() {
if index > 0 {
write!(f, ", ")?;
}
let name = element.inspector_name();
let mut properties = Vec::new();
element.record_inspector_properties(&mut |prop, value| {
properties.push(format!("{prop}={value}"));
});
if properties.is_empty() {
write!(f, "{name}")?;
} else {
write!(f, "{name}({})", properties.join(", "))?;
}
}
write!(f, "]")
}
}
}
}
impl fmt::Debug for Modifier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ResolvedBackground {
color: Color,
shape: Option<RoundedCornerShape>,
}
impl ResolvedBackground {
pub fn new(color: Color, shape: Option<RoundedCornerShape>) -> Self {
Self { color, shape }
}
pub fn color(&self) -> Color {
self.color
}
pub fn shape(&self) -> Option<RoundedCornerShape> {
self.shape
}
pub fn set_shape(&mut self, shape: Option<RoundedCornerShape>) {
self.shape = shape;
}
}
#[derive(Clone, Copy, Debug, PartialEq, Default)]
pub struct ResolvedModifiers {
padding: EdgeInsets,
layout: LayoutProperties,
offset: Point,
}
impl ResolvedModifiers {
pub fn padding(&self) -> EdgeInsets {
self.padding
}
pub fn layout_properties(&self) -> LayoutProperties {
self.layout
}
pub fn offset(&self) -> Point {
self.offset
}
pub(crate) fn set_padding(&mut self, padding: EdgeInsets) {
self.padding = padding;
}
pub(crate) fn set_layout_properties(&mut self, layout: LayoutProperties) {
self.layout = layout;
}
pub(crate) fn set_offset(&mut self, offset: Point) {
self.offset = offset;
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub enum DimensionConstraint {
#[default]
Unspecified,
Points(f32),
Fraction(f32),
Intrinsic(IntrinsicSize),
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct LayoutWeight {
pub weight: f32,
pub fill: bool,
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct LayoutProperties {
padding: EdgeInsets,
width: DimensionConstraint,
height: DimensionConstraint,
min_width: Option<f32>,
min_height: Option<f32>,
max_width: Option<f32>,
max_height: Option<f32>,
weight: Option<LayoutWeight>,
box_alignment: Option<Alignment>,
column_alignment: Option<HorizontalAlignment>,
row_alignment: Option<VerticalAlignment>,
}
impl LayoutProperties {
pub fn padding(&self) -> EdgeInsets {
self.padding
}
pub fn width(&self) -> DimensionConstraint {
self.width
}
pub fn height(&self) -> DimensionConstraint {
self.height
}
pub fn min_width(&self) -> Option<f32> {
self.min_width
}
pub fn min_height(&self) -> Option<f32> {
self.min_height
}
pub fn max_width(&self) -> Option<f32> {
self.max_width
}
pub fn max_height(&self) -> Option<f32> {
self.max_height
}
pub fn weight(&self) -> Option<LayoutWeight> {
self.weight
}
pub fn box_alignment(&self) -> Option<Alignment> {
self.box_alignment
}
pub fn column_alignment(&self) -> Option<HorizontalAlignment> {
self.column_alignment
}
pub fn row_alignment(&self) -> Option<VerticalAlignment> {
self.row_alignment
}
}
#[cfg(test)]
#[path = "tests/modifier_tests.rs"]
mod tests;