layer_shika_composition/
popup_builder.rs1use crate::Result;
2use crate::popup::PopupShell;
3use layer_shika_domain::dimensions::LogicalRect;
4use layer_shika_domain::value_objects::handle::PopupHandle;
5use layer_shika_domain::value_objects::output_target::OutputTarget;
6use layer_shika_domain::value_objects::popup_behavior::ConstraintAdjustment;
7use layer_shika_domain::value_objects::popup_config::PopupConfig;
8use layer_shika_domain::value_objects::popup_position::{
9 Alignment, AnchorPoint, Offset, PopupPosition,
10};
11use layer_shika_domain::value_objects::popup_size::PopupSize;
12
13pub struct Unbound;
15
16pub struct Bound {
18 shell: PopupShell,
19}
20
21pub struct PopupBuilder<State = Unbound> {
38 state: State,
39 config: PopupConfig,
40}
41
42impl PopupBuilder<Unbound> {
43 #[must_use]
48 pub fn new(component: impl Into<String>) -> Self {
49 Self {
50 state: Unbound,
51 config: PopupConfig::new(component),
52 }
53 }
54
55 #[must_use]
56 pub(crate) fn with_shell(self, shell: PopupShell) -> PopupBuilder<Bound> {
57 PopupBuilder {
58 state: Bound { shell },
59 config: self.config,
60 }
61 }
62}
63
64impl<State> PopupBuilder<State> {
65 #[must_use]
66 pub fn position(mut self, position: PopupPosition) -> Self {
67 self.config.position = position;
68 self
69 }
70
71 #[must_use]
72 pub fn at_cursor(self) -> Self {
73 self.position(PopupPosition::Cursor {
74 offset: Offset::default(),
75 })
76 }
77
78 #[must_use]
79 pub fn at_position(self, x: f32, y: f32) -> Self {
80 self.position(PopupPosition::Absolute { x, y })
81 }
82
83 #[must_use]
84 pub fn centered(self) -> Self {
85 self.position(PopupPosition::Centered {
86 offset: Offset::default(),
87 })
88 }
89
90 #[must_use]
91 pub fn relative_to_rect(
92 self,
93 rect: LogicalRect,
94 anchor: AnchorPoint,
95 alignment: Alignment,
96 ) -> Self {
97 self.position(PopupPosition::Element {
98 rect,
99 anchor,
100 alignment,
101 })
102 }
103
104 #[must_use]
105 pub fn offset(mut self, x: f32, y: f32) -> Self {
106 match &mut self.config.position {
107 PopupPosition::Absolute { x: abs_x, y: abs_y } => {
108 *abs_x += x;
109 *abs_y += y;
110 }
111 PopupPosition::Cursor { offset }
112 | PopupPosition::Centered { offset }
113 | PopupPosition::RelativeToParent { offset, .. } => {
114 offset.x += x;
115 offset.y += y;
116 }
117 PopupPosition::Element { .. } => {
118 self.config.position = PopupPosition::Cursor {
119 offset: Offset { x, y },
120 };
121 }
122 }
123 self
124 }
125
126 #[must_use]
127 pub fn size(mut self, size: PopupSize) -> Self {
128 self.config.size = size;
129 self
130 }
131
132 #[must_use]
133 pub fn fixed_size(self, width: f32, height: f32) -> Self {
134 self.size(PopupSize::Fixed { width, height })
135 }
136
137 #[must_use]
138 pub fn min_size(self, width: f32, height: f32) -> Self {
139 self.size(PopupSize::Minimum { width, height })
140 }
141
142 #[must_use]
143 pub fn max_size(self, width: f32, height: f32) -> Self {
144 self.size(PopupSize::Maximum { width, height })
145 }
146
147 #[must_use]
148 pub fn content_sized(self) -> Self {
149 self.size(PopupSize::Content)
150 }
151
152 #[must_use]
153 pub fn grab(mut self, enable: bool) -> Self {
154 self.config.behavior.grab = enable;
155 self
156 }
157
158 #[must_use]
159 pub fn modal(mut self, enable: bool) -> Self {
160 self.config.behavior.modal = enable;
161 self
162 }
163
164 #[must_use]
165 pub fn close_on_click_outside(mut self) -> Self {
166 self.config.behavior.close_on_click_outside = true;
167 self
168 }
169
170 #[must_use]
171 pub fn close_on_escape(mut self) -> Self {
172 self.config.behavior.close_on_escape = true;
173 self
174 }
175
176 #[must_use]
177 pub fn constraint_adjustment(mut self, adjustment: ConstraintAdjustment) -> Self {
178 self.config.behavior.constraint_adjustment = adjustment;
179 self
180 }
181
182 #[must_use]
183 pub fn on_output(mut self, target: OutputTarget) -> Self {
184 self.config.output = target;
185 self
186 }
187
188 #[must_use]
189 pub fn on_primary(self) -> Self {
190 self.on_output(OutputTarget::Primary)
191 }
192
193 #[must_use]
194 pub fn on_active(self) -> Self {
195 self.on_output(OutputTarget::Active)
196 }
197
198 #[must_use]
199 pub fn parent(mut self, parent: PopupHandle) -> Self {
200 self.config.parent = Some(parent);
201 self
202 }
203
204 #[must_use]
205 pub const fn z_index(mut self, index: i32) -> Self {
206 self.config.z_index = index;
207 self
208 }
209
210 #[must_use]
211 pub fn close_on(mut self, callback_name: impl Into<String>) -> Self {
212 self.config.close_callback = Some(callback_name.into());
213 self
214 }
215
216 #[must_use]
217 pub fn resize_on(mut self, callback_name: impl Into<String>) -> Self {
218 self.config.resize_callback = Some(callback_name.into());
219 self
220 }
221
222 #[must_use]
226 pub fn build(self) -> PopupConfig {
227 self.config
228 }
229}
230
231impl PopupBuilder<Bound> {
232 pub fn show(self) -> Result<PopupHandle> {
237 self.state.shell.show(self.config)
238 }
239}