cranpose_ui/modifier/
offset.rs1use std::{
2 hash::{Hash, Hasher},
3 rc::Rc,
4};
5
6use cranpose_foundation::{
7 DelegatableNode, ModifierNode, ModifierNodeContext, ModifierNodeElement, NodeCapabilities,
8 NodeState,
9};
10
11use super::{GraphicsLayer, Modifier, Point, inspector_metadata};
12use crate::{
13 LayoutDirection,
14 modifier_nodes::{FractionalOffsetElement, GraphicsLayerNode, OffsetElement},
15};
16
17impl Modifier {
18 pub fn offset(self, x: f32, y: f32) -> Self {
27 let modifier = Self::with_element(OffsetElement::new(x, y, true)).with_inspector_metadata(
28 inspector_metadata("offset", move |info| {
29 info.add_offset_components("offsetX", "offsetY", Point { x, y });
30 }),
31 );
32 self.then(modifier)
33 }
34
35 pub fn absolute_offset(self, x: f32, y: f32) -> Self {
43 let modifier = Self::with_element(OffsetElement::new(x, y, false)).with_inspector_metadata(
44 inspector_metadata("absoluteOffset", move |info| {
45 info.add_offset_components("absoluteOffsetX", "absoluteOffsetY", Point { x, y });
46 }),
47 );
48 self.then(modifier)
49 }
50
51 pub fn offset_fraction(self, x_fraction: f32, y_fraction: f32) -> Self {
65 let modifier = Self::with_element(FractionalOffsetElement::new(x_fraction, y_fraction))
66 .with_inspector_metadata(inspector_metadata("offsetFraction", move |info| {
67 info.add_property("offsetFractionX", x_fraction.to_string());
68 info.add_property("offsetFractionY", y_fraction.to_string());
69 }));
70 self.then(modifier)
71 }
72
73 pub fn offset_with(self, offset: impl Fn() -> Point + 'static) -> Self {
81 self.then_offset_px(OffsetPxElement::new(Rc::new(offset), true), "offset")
82 }
83
84 pub fn absolute_offset_with(self, offset: impl Fn() -> Point + 'static) -> Self {
88 self.then_offset_px(
89 OffsetPxElement::new(Rc::new(offset), false),
90 "absoluteOffset",
91 )
92 }
93
94 fn then_offset_px(self, element: OffsetPxElement, name: &'static str) -> Self {
95 let modifier =
96 Self::with_element(element).with_inspector_metadata(inspector_metadata(name, |info| {
97 info.add_property("lazy", "true");
98 }));
99 self.then(modifier)
100 }
101}
102
103#[derive(Clone)]
106pub(crate) struct OffsetPxElement {
107 offset: Rc<dyn Fn() -> Point>,
108 rtl_aware: bool,
109}
110
111impl OffsetPxElement {
112 fn new(offset: Rc<dyn Fn() -> Point>, rtl_aware: bool) -> Self {
113 Self { offset, rtl_aware }
114 }
115}
116
117impl std::fmt::Debug for OffsetPxElement {
118 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119 f.debug_struct("OffsetPxElement")
120 .field("rtl_aware", &self.rtl_aware)
121 .finish_non_exhaustive()
122 }
123}
124
125impl PartialEq for OffsetPxElement {
126 fn eq(&self, other: &Self) -> bool {
127 Rc::ptr_eq(&self.offset, &other.offset) && self.rtl_aware == other.rtl_aware
128 }
129}
130
131impl Eq for OffsetPxElement {}
132
133impl Hash for OffsetPxElement {
134 fn hash<H: Hasher>(&self, state: &mut H) {
135 Rc::as_ptr(&self.offset).cast::<()>().hash(state);
136 self.rtl_aware.hash(state);
137 }
138}
139
140impl ModifierNodeElement for OffsetPxElement {
141 type Node = OffsetPxNode;
142
143 fn create(&self) -> Self::Node {
144 OffsetPxNode {
145 layer: GraphicsLayerNode::new_lazy(translation(Rc::clone(&self.offset), false)),
146 offset: Rc::clone(&self.offset),
147 rtl_aware: self.rtl_aware,
148 layout_direction: LayoutDirection::Ltr,
149 }
150 }
151
152 fn update(&self, node: &mut Self::Node) {
153 if !Rc::ptr_eq(&node.offset, &self.offset) || node.rtl_aware != self.rtl_aware {
154 node.offset = Rc::clone(&self.offset);
155 node.rtl_aware = self.rtl_aware;
156 node.resolve_translation();
157 }
158 }
159
160 fn capabilities(&self) -> NodeCapabilities {
161 NodeCapabilities::DRAW
162 }
163
164 fn always_update(&self) -> bool {
165 true
166 }
167
168 fn auto_invalidate_on_update(&self) -> bool {
169 false
170 }
171}
172
173pub(crate) struct OffsetPxNode {
177 layer: GraphicsLayerNode,
178 offset: Rc<dyn Fn() -> Point>,
179 rtl_aware: bool,
180 layout_direction: LayoutDirection,
181}
182
183impl OffsetPxNode {
184 pub(crate) fn layer(&self) -> &GraphicsLayerNode {
185 &self.layer
186 }
187
188 pub(crate) fn set_layout_direction(&mut self, direction: LayoutDirection) {
189 if self.layout_direction != direction {
190 self.layout_direction = direction;
191 self.resolve_translation();
192 }
193 }
194
195 fn resolve_translation(&mut self) {
198 let mirror = self.rtl_aware && self.layout_direction.is_rtl();
199 self.layer
200 .set_lazy(translation(Rc::clone(&self.offset), mirror));
201 }
202}
203
204fn translation(offset: Rc<dyn Fn() -> Point>, mirror: bool) -> Rc<dyn Fn() -> GraphicsLayer> {
205 Rc::new(move || {
206 let offset = offset();
207 GraphicsLayer {
208 translation_x: if mirror { -offset.x } else { offset.x },
209 translation_y: offset.y,
210 ..GraphicsLayer::default()
211 }
212 })
213}
214
215impl DelegatableNode for OffsetPxNode {
216 fn node_state(&self) -> &NodeState {
217 self.layer.node_state()
218 }
219}
220
221impl ModifierNode for OffsetPxNode {
222 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
223 self.layer.on_attach(context);
224 }
225
226 fn on_detach(&mut self) {
227 self.layer.on_detach();
228 }
229}