1use std::convert::TryFrom;
2use std::sync::LazyLock;
3use log;
4
5use crate::prelude::*;
6
7use super::set_layout;
8
9pub use self::builder::TiledBuilder as Builder;
10
11pub static CONTROLS : LazyLock <Controls> = LazyLock::new (||
16 controls::Builder::new()
17 .buttons ({
18 let mut v : Vec <controls::Button> = vec![
19 controls::button::Builtin::FrameTiledIncreaseSize,
20 controls::button::Builtin::FrameTiledDecreaseSize
21 ].into_iter().map (Into::into).collect();
22 v.extend (super::CONTROLS.buttons.iter().copied());
23 v.into()
24 })
25 .build()
26);
27
28pub fn increase_size (
32 _ : &controls::button::Release,
33 elements : &Tree <Element>,
34 node_id : &NodeId,
35 action_buffer : &mut Vec <(NodeId, Action)>
36) {
37 log::trace!("increase_size...");
38 let Widget (layout, _, canvas) = Frame::try_get (elements, node_id).unwrap();
39 let coordinate_kind = Some (canvas.coordinates.kind());
40 let layout = {
41 use controller::component::{layout, Layout};
42 let mut tiled = layout::Tiled::try_from (layout.variant.clone()).unwrap();
43 tiled.modify_size (1);
44 Layout { variant: tiled.into(), .. layout.clone() }
45 };
46 set_layout (elements, node_id, layout, coordinate_kind, action_buffer);
47 log::trace!("...increase_size");
48}
49
50pub fn decrease_size (
54 _ : &controls::button::Release,
55 elements : &Tree <Element>,
56 node_id : &NodeId,
57 action_buffer : &mut Vec <(NodeId, Action)>
58) {
59 log::trace!("decrease_size...");
60 let Widget (layout, _, canvas) = Frame::try_get (elements, node_id).unwrap();
61 let coordinate_kind = Some (canvas.coordinates.kind());
62 let layout = {
63 use controller::component::{layout, Layout};
64 let mut tiled = layout::Tiled::try_from (layout.variant.clone()).unwrap();
65 tiled.modify_size (-1);
66 Layout { variant: tiled.into(), .. layout.clone() }
67 };
68 set_layout (elements, node_id, layout, coordinate_kind, action_buffer);
69 log::trace!("...decrease_size");
70}
71
72pub (crate) fn new_coordinates (
79 elements : &Tree <Element>,
80 frame_id : &NodeId,
81 layout : &layout::Tiled
82) -> Vec <(NodeId, Coordinates)> {
83 use controller::component::layout;
84 log::trace!("coordinates...");
85 let parent_id = elements.get_parent_id (frame_id);
86 let Widget (parent_layout, _, parent_canvas) = Frame::try_get (elements, parent_id)
87 .unwrap();
88 let parent_node = elements.get (parent_id).unwrap();
89 let mut total_weights = 0;
90 let mut total_absolute = 0;
91 let mut add_layout = |layout : &layout::Tiled| match layout {
92 layout::Tiled::Weighted (weight) => total_weights += weight.get(),
93 layout::Tiled::Absolute (size) => total_absolute += size.get()
94 };
95 let tiled_children = parent_node.children().iter().filter_map (|child_id| {
96 let Widget (child_layout, _, _) = Frame::try_get (elements, child_id).ok()?;
97 if child_id == frame_id {
98 add_layout (layout);
100 Some ((child_id.clone(), layout))
101 } else if let controller::component::layout::Variant::Tiled (tiled) =
102 &child_layout.variant
103 {
104 add_layout (tiled);
105 Some ((child_id.clone(), tiled))
106 } else {
107 None
108 }
109 }).collect::<Vec<_>>();
110 let mut out = vec![];
111 let mut start_weight = 0;
112 let mut start_absolute = 0;
113 for (id, layout) in tiled_children.into_iter() {
114 let coordinates = coordinates (
115 &parent_layout.orientation, parent_canvas, layout, &mut start_weight,
116 &mut start_absolute, total_weights, total_absolute);
117 out.push ((id, coordinates));
118 }
119 log::trace!("...coordinates");
120 out
121}
122
123pub (crate) fn destroy_coordinates (
126 elements : &Tree <Element>,
127 frame_id : &NodeId
128) -> Vec <(NodeId, Coordinates)> {
129 use controller::component::layout;
130 log::trace!("destroy_coordinates...");
131 let parent_id = elements.get_parent_id (frame_id);
132 let Widget (parent_layout, _, parent_canvas) = Frame::try_get (elements, parent_id)
133 .unwrap();
134 let parent_node = elements.get (parent_id).unwrap();
135 let mut total_weights = 0;
136 let mut total_absolute = 0;
137 let mut add_layout = |layout : &layout::Tiled| match layout {
138 layout::Tiled::Weighted (weight) => total_weights += weight.get(),
139 layout::Tiled::Absolute (size) => total_absolute += size.get()
140 };
141 let tiled_children = parent_node.children().iter().filter_map (|child_id| {
142 if child_id == frame_id {
143 None
144 } else {
145 let Widget (child_layout, _, _) = Frame::try_get (elements, child_id).ok()?;
146 if let controller::component::layout::Variant::Tiled (tiled) =
147 &child_layout.variant
148 {
149 add_layout (tiled);
150 Some ((child_id.clone(), tiled))
151 } else {
152 None
153 }
154 }
155 }).collect::<Vec <_>>();
156 let mut out = vec![];
157 let mut start_weight = 0;
158 let mut start_absolute = 0;
159 for (id, layout) in tiled_children.into_iter() {
160 let coordinates = coordinates (
161 &parent_layout.orientation, parent_canvas, layout, &mut start_weight,
162 &mut start_absolute, total_weights, total_absolute);
163 out.push ((id, coordinates));
164 }
165 log::trace!("...destroy_coordinates");
166 out
167}
168
169pub (crate) fn child_coordinates (
172 orientation : &(Orientation, Area),
173 canvas : &Canvas,
174 layouts : Vec <layout::Tiled>
175) -> Vec <Coordinates> {
176 use controller::component::layout;
177 let mut child_coordinates = vec![];
178 let mut total_weights = 0;
179 let mut total_absolute = 0;
180 let add_layout = |layout : &layout::Tiled| match layout {
181 layout::Tiled::Weighted (weight) => total_weights += weight.get(),
182 layout::Tiled::Absolute (size) => total_absolute += size.get()
183 };
184 layouts.iter().for_each (add_layout);
185 let mut start_weight = 0;
186 let mut start_absolute = 0;
187 for layout in layouts {
188 child_coordinates.push (
189 coordinates (orientation, canvas, &layout, &mut start_weight,
190 &mut start_absolute, total_weights, total_absolute));
191 }
192 child_coordinates
193}
194
195fn coordinates (
200 (parent_orientation, parent_area) : &(Orientation, Area),
201 parent_canvas : &Canvas,
202 child_layout : &layout::Tiled,
203 start_weight : &mut u32,
204 start_absolute : &mut u32,
205 total_weights : u32,
206 total_absolute : u32
207) -> Coordinates {
208 use controller::component::layout;
209 use view::{coordinates, dimensions, position};
210 log::trace!("coordinates...");
211 let parent_coordinates = match parent_area {
212 Area::Exterior => parent_canvas.coordinates,
213 Area::Interior => parent_canvas.body_coordinates()
214 };
215 let (position_horizontal, position_vertical, width, height) =
216 match parent_orientation {
217 Orientation::Horizontal => {
218 let height = parent_coordinates.dimensions_vertical();
219 let parent_width = parent_coordinates.dimensions_horizontal();
220 let position_vertical = parent_coordinates.position_vertical();
221 let parent_position_horizontal = parent_coordinates.position_horizontal();
222 let position_horizontal = parent_position_horizontal + *start_absolute as i32 +
223 ( (*start_weight as f64 / total_weights as f64) *
224 parent_width.saturating_sub (total_absolute) as f64
225 ) as i32;
226 let width = match child_layout {
227 layout::Tiled::Weighted (weight) => {
228 let weight = weight.get();
229 *start_weight += weight;
230 let next_position = parent_position_horizontal + *start_absolute as i32 +
231 ( (*start_weight as f64 / total_weights as f64) *
232 parent_width.saturating_sub (total_absolute) as f64
233 ) as i32;
234 (next_position - position_horizontal) as u32
235 }
236 layout::Tiled::Absolute (size) => {
237 let size = size.get();
238 *start_absolute += size;
239 size
240 }
241 };
242 (position_horizontal, position_vertical, width, height)
243 }
244 Orientation::Vertical => {
245 let width = parent_coordinates.dimensions_horizontal();
246 let parent_height = parent_coordinates.dimensions_vertical();
247 let position_horizontal = parent_coordinates.position_horizontal();
248 let parent_position_vertical = parent_coordinates.position_vertical();
249 let position_vertical = parent_position_vertical + *start_absolute as i32 +
250 ( (*start_weight as f64 / total_weights as f64) *
251 parent_height.saturating_sub (total_absolute) as f64
252 ) as i32;
253 let height = match child_layout {
254 layout::Tiled::Weighted (weight) => {
255 let weight = weight.get();
256 *start_weight += weight;
257 let next_position = parent_position_vertical + *start_absolute as i32 +
258 ( (*start_weight as f64 / total_weights as f64) *
259 parent_height.saturating_sub (total_absolute) as f64
260 ) as i32;
261 (next_position - position_vertical) as u32
262 }
263 layout::Tiled::Absolute (size) => {
264 let size = size.get();
265 *start_absolute += size;
266 size
267 }
268 };
269 (position_horizontal, position_vertical, width, height)
270 }
271 };
272 let out = if parent_coordinates.kind() == coordinates::Kind::Tile {
273 ( position::Tile::new_rc (position_vertical, position_horizontal),
274 dimensions::Tile::new_rc (height, width)
275 ).into()
276 } else {
277 debug_assert!(parent_coordinates.kind() == coordinates::Kind::Pixel);
278 ( position::Pixel::new_xy (position_horizontal, position_vertical),
279 dimensions::Pixel::new_wh (width, height)
280 ).into()
281 };
282 log::trace!("...coordinates");
283 out
284}
285
286mod builder {
291 use derive_builder::Builder;
292 use crate::prelude::*;
293 use frame::set_coordinates;
294
295 #[derive(Builder)]
296 #[builder(public, pattern="owned", build_fn(private), setter(strip_option))]
297 struct Tiled <'a, A : Application> {
298 elements : &'a Tree <Element>,
299 parent_id : &'a NodeId,
300 #[builder(default)]
301 appearances : Appearances,
302 #[builder(default)]
303 bindings : Option <&'a Bindings <A>>,
304 #[builder(default)]
305 border : Option <Border>,
306 #[builder(default)]
307 clear_color : canvas::ClearColor,
308 #[builder(default)]
309 create_order : CreateOrder,
310 #[builder(default)]
311 disabled : bool,
312 #[builder(default)]
313 layout : layout::Tiled,
314 #[builder(default)]
315 name : Option <String>,
316 #[builder(default)]
317 orientation : (Orientation, Area),
318 }
319
320 impl <'a, A : Application> TiledBuilder <'a, A> {
321 pub const fn new (elements : &'a Tree <Element>, parent_id : &'a NodeId) -> Self {
322 TiledBuilder {
323 elements: Some (elements),
324 parent_id: Some (parent_id),
325 appearances: None,
326 bindings: None,
327 border: None,
328 clear_color: None,
329 create_order: None,
330 disabled: None,
331 layout: None,
332 name: None,
333 orientation: None
334 }
335 }
336 }
337
338 impl <A : Application> BuildActions for TiledBuilder <'_, A> {
339 fn build_actions (self) -> Vec <(NodeId, Action)> {
344 use view::component::Canvas;
345 log::trace!("build actions...");
346 let Tiled {
347 elements, parent_id, appearances, bindings, border, clear_color,
348 create_order, disabled, layout, name, orientation
349 } = self.build()
350 .map_err(|err| log::error!("frame free builder error: {err:?}"))
351 .unwrap();
352 let bindings_empty = Bindings::empty();
353 let bindings = bindings.unwrap_or (&bindings_empty)
354 .get_bindings (&super::CONTROLS);
355 let (new_coordinates, sibling_coordinates) =
356 create_coordinates (elements, parent_id, &layout, create_order);
357 let frame = {
358 use controller::component::Layout;
359 let controller = {
360 let mut controller = Controller::with_bindings (&bindings);
361 controller.component = Layout {
362 orientation, variant: layout.into()
363 }.into();
364 controller.appearances = appearances;
365 controller
366 };
367 let view = {
368 let coordinates = new_coordinates;
369 let appearance = controller.get_appearance().clone();
370 let component = Canvas { coordinates, clear_color, border }.into();
371 View { component, appearance, .. View::default() }
372 };
373 let name = name.unwrap_or ("Frame".to_string());
374 let mut frame = Element::new (name, controller, Model::default(), view);
375 let parent_node = elements.get (parent_id).unwrap();
376 if parent_node.data().controller.state == State::Disabled || disabled {
377 frame.disable()
378 }
379 frame
380 };
381 let mut out = vec![
382 (parent_id.clone(), Action::create_singleton (frame, create_order))
383 ];
384 for (id, coordinates) in sibling_coordinates.into_iter() {
385 set_coordinates (elements, &id, &coordinates, None, &mut out);
386 }
387 log::trace!("...build actions");
388 out
389 }
390 }
391
392 fn create_coordinates (
395 elements : &Tree <Element>,
396 parent_frame_id : &NodeId,
397 layout : &layout::Tiled,
398 order : CreateOrder
399 ) -> (Coordinates, Vec <(NodeId, Coordinates)>) {
400 use controller::component::layout;
401 log::trace!("create_coordinates...");
402 let Widget (parent_layout, _, parent_canvas) =
403 Frame::try_get (elements, parent_frame_id).unwrap();
404 let parent_node = elements.get (parent_frame_id).unwrap();
405 let mut total_weights = 0;
406 let mut total_absolute = 0;
407 let mut add_layout = |layout : &layout::Tiled| match layout {
408 layout::Tiled::Weighted (weight) => total_weights += weight.get(),
409 layout::Tiled::Absolute (size) => total_absolute += size.get()
410 };
411 add_layout (layout);
412 let sibling_layouts = parent_node.children().iter().map (|child_id| {
413 let Widget (child_layout, _, _) = Frame::try_get (elements, child_id).ok()?;
414 if let layout::Variant::Tiled (tiled) = &child_layout.variant {
415 add_layout (tiled);
416 Some ((child_id, tiled))
417 } else {
418 None
419 }
420 }).collect::<Vec <_>>();
421 let order_index = match order {
422 CreateOrder::Prepend => Some (0),
423 CreateOrder::NthSibling (n) => Some (n),
424 CreateOrder::Append => None
425 };
426 let mut sibling_coordinates = vec![];
427 let mut start_weight = 0;
428 let mut start_absolute = 0;
429 let mut new_coordinates = None;
430 let mut index = 0;
431 for (i, maybe_tiled) in sibling_layouts.into_iter().enumerate() {
433 if order_index == Some (i as u32) {
434 new_coordinates = Some (super::coordinates (
435 &parent_layout.orientation, parent_canvas, layout, &mut start_weight,
436 &mut start_absolute, total_weights, total_absolute
437 ));
438 }
439 if let Some ((id, tiled)) = maybe_tiled {
440 let coordinates = super::coordinates (
441 &parent_layout.orientation, parent_canvas, tiled, &mut start_weight,
442 &mut start_absolute, total_weights, total_absolute);
443 sibling_coordinates.push ((id.clone(), coordinates));
444 }
445 index = i + 1;
446 }
447 if new_coordinates.is_none() {
448 if cfg!(debug_assertions) && let Some (n) = order_index {
449 debug_assert_eq!(n, index as u32);
450 }
451 new_coordinates = Some (super::coordinates (
452 &parent_layout.orientation, parent_canvas, layout,
453 &mut start_weight, &mut start_absolute, total_weights, total_absolute
454 ));
455 }
456 log::trace!("...create_coordinates");
457 (new_coordinates.unwrap(), sibling_coordinates)
458 }
459}