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().cloned());
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) =
133 Frame::try_get (elements, parent_id).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)
146 .ok()?;
147 if let controller::component::layout::Variant::Tiled (tiled) =
148 &child_layout.variant
149 {
150 add_layout (tiled);
151 Some ((child_id.clone(), tiled))
152 } else {
153 None
154 }
155 }
156 }).collect::<Vec <_>>();
157 let mut out = vec![];
158 let mut start_weight = 0;
159 let mut start_absolute = 0;
160 for (id, layout) in tiled_children.into_iter() {
161 let coordinates = coordinates (
162 &parent_layout.orientation, parent_canvas, layout, &mut start_weight,
163 &mut start_absolute, total_weights, total_absolute);
164 out.push ((id, coordinates));
165 }
166 log::trace!("...destroy_coordinates");
167 out
168}
169
170pub (crate) fn child_coordinates (
173 orientation : &(Orientation, Area),
174 canvas : &Canvas,
175 layouts : Vec <layout::Tiled>
176) -> Vec <Coordinates> {
177 use controller::component::layout;
178 let mut child_coordinates = vec![];
179 let mut total_weights = 0;
180 let mut total_absolute = 0;
181 let add_layout = |layout : &layout::Tiled| match layout {
182 layout::Tiled::Weighted (weight) => total_weights += weight.get(),
183 layout::Tiled::Absolute (size) => total_absolute += size.get()
184 };
185 layouts.iter().for_each (add_layout);
186 let mut start_weight = 0;
187 let mut start_absolute = 0;
188 for layout in layouts {
189 child_coordinates.push (
190 coordinates (orientation, canvas, &layout, &mut start_weight,
191 &mut start_absolute, total_weights, total_absolute));
192 }
193 child_coordinates
194}
195
196fn coordinates (
201 (parent_orientation, parent_area) :
202 &(Orientation, Area),
203 parent_canvas : &Canvas,
204 child_layout : &layout::Tiled,
205 start_weight : &mut u32,
206 start_absolute : &mut u32,
207 total_weights : u32,
208 total_absolute : u32
209) -> Coordinates {
210 use controller::component::layout;
211 use view::{coordinates, dimensions, position};
212 log::trace!("coordinates...");
213 let parent_coordinates = match parent_area {
214 Area::Exterior => parent_canvas.coordinates,
215 Area::Interior => parent_canvas.body_coordinates()
216 };
217 let (position_horizontal, position_vertical, width, height) =
218 match parent_orientation {
219 Orientation::Horizontal => {
220 let height = parent_coordinates.dimensions_vertical();
221 let parent_width = parent_coordinates.dimensions_horizontal();
222 let position_vertical = parent_coordinates.position_vertical();
223 let parent_position_horizontal = parent_coordinates.position_horizontal();
224 let position_horizontal = parent_position_horizontal +
225 *start_absolute as i32 +
226 ((*start_weight as f64 / total_weights as f64) *
227 parent_width.saturating_sub (total_absolute) as f64)
228 as i32;
229 let width = match child_layout {
230 layout::Tiled::Weighted (weight) => {
231 let weight = weight.get();
232 *start_weight += weight;
233 let next_position = parent_position_horizontal +
234 *start_absolute as i32 +
235 ((*start_weight as f64 / total_weights as f64) *
236 parent_width.saturating_sub (total_absolute) as f64)
237 as i32;
238 (next_position - position_horizontal) as u32
239 }
240 layout::Tiled::Absolute (size) => {
241 let size = size.get();
242 *start_absolute += size;
243 size
244 }
245 };
246 (position_horizontal, position_vertical, width, height)
247 }
248 Orientation::Vertical => {
249 let width = parent_coordinates.dimensions_horizontal();
250 let parent_height = parent_coordinates.dimensions_vertical();
251 let position_horizontal = parent_coordinates.position_horizontal();
252 let parent_position_vertical = parent_coordinates.position_vertical();
253 let position_vertical = parent_position_vertical +
254 *start_absolute as i32 +
255 ((*start_weight as f64 / total_weights as f64) *
256 parent_height.saturating_sub (total_absolute) as f64)
257 as i32;
258 let height = match child_layout {
259 layout::Tiled::Weighted (weight) => {
260 let weight = weight.get();
261 *start_weight += weight;
262 let next_position = parent_position_vertical +
263 *start_absolute as i32 +
264 ((*start_weight as f64 / total_weights as f64) *
265 parent_height.saturating_sub (total_absolute) as f64)
266 as i32;
267 (next_position - position_vertical) as u32
268 }
269 layout::Tiled::Absolute (size) => {
270 let size = size.get();
271 *start_absolute += size;
272 size
273 }
274 };
275 (position_horizontal, position_vertical, width, height)
276 }
277 };
278 let out = if parent_coordinates.kind() == coordinates::Kind::Tile {
279 ( position::Tile::new_rc (position_vertical, position_horizontal),
280 dimensions::Tile::new_rc (height, width)
281 ).into()
282 } else {
283 debug_assert!(parent_coordinates.kind() == coordinates::Kind::Pixel);
284 ( position::Pixel::new_xy (position_horizontal, position_vertical),
285 dimensions::Pixel::new_wh (width, height)
286 ).into()
287 };
288 log::trace!("...coordinates");
289 out
290}
291
292mod builder {
297 use derive_builder::Builder;
298 use crate::prelude::*;
299 use frame::set_coordinates;
300
301 #[derive(Builder)]
302 #[builder(public, pattern="owned", build_fn(private), setter(strip_option))]
303 struct Tiled <'a, A : Application> {
304 elements : &'a Tree <Element>,
305 parent_id : &'a NodeId,
306 #[builder(default)]
307 appearances : Appearances,
308 #[builder(default)]
309 bindings : Option <&'a Bindings <A>>,
310 #[builder(default)]
311 border : Option <Border>,
312 #[builder(default)]
313 clear_color : canvas::ClearColor,
314 #[builder(default)]
315 create_order : CreateOrder,
316 #[builder(default)]
317 disabled : bool,
318 #[builder(default)]
319 layout : layout::Tiled,
320 #[builder(default)]
321 orientation : (Orientation, Area),
322 }
323
324 impl <'a, A : Application> TiledBuilder <'a, A> {
325 pub const fn new (elements : &'a Tree <Element>, parent_id : &'a NodeId) -> Self {
326 TiledBuilder {
327 elements: Some (elements),
328 parent_id: Some (parent_id),
329 appearances: None,
330 bindings: None,
331 border: None,
332 clear_color: None,
333 create_order: None,
334 disabled: None,
335 layout: None,
336 orientation: None
337 }
338 }
339 }
340
341 impl <A : Application> BuildActions for TiledBuilder <'_, A> {
342 fn build_actions (self) -> Vec <(NodeId, Action)> {
347 use view::component::Canvas;
348 log::trace!("build actions...");
349 let Tiled {
350 elements, parent_id, appearances, bindings, border, clear_color,
351 create_order, disabled, layout, orientation
352 } = self.build()
353 .map_err(|err| log::error!("frame free builder error: {err:?}"))
354 .unwrap();
355 let bindings_empty = Bindings::empty();
356 let bindings = bindings.unwrap_or (&bindings_empty)
357 .get_bindings (&super::CONTROLS);
358 let (new_coordinates, sibling_coordinates) =
359 create_coordinates (elements, parent_id, &layout, create_order);
360 let frame = {
361 use controller::component::Layout;
362 let controller = {
363 let mut controller = Controller::with_bindings (&bindings);
364 controller.component = Layout {
365 orientation, variant: layout.into()
366 }.into();
367 controller.appearances = appearances;
368 controller
369 };
370 let view = {
371 let coordinates = new_coordinates;
372 let appearance = controller.get_appearance().clone();
373 let component = Canvas { coordinates, clear_color, border }.into();
374 View { component, appearance, .. View::default() }
375 };
376 let mut frame =
377 Element::new ("Frame".to_string(), controller, Model::default(), view);
378 let parent_node = elements.get (parent_id).unwrap();
379 if parent_node.data().controller.state == State::Disabled ||
380 disabled
381 {
382 frame.disable()
383 }
384 frame
385 };
386 let mut out = vec![
387 (parent_id.clone(), Action::create_singleton (frame, create_order))
388 ];
389 for (id, coordinates) in sibling_coordinates.into_iter() {
390 set_coordinates (elements, &id, &coordinates, None, &mut out);
391 }
392 log::trace!("...build actions");
393 out
394 }
395 }
396
397 fn create_coordinates (
400 elements : &Tree <Element>,
401 parent_frame_id : &NodeId,
402 layout : &layout::Tiled,
403 order : CreateOrder
404 ) -> (Coordinates, Vec <(NodeId, Coordinates)>) {
405 use controller::component::layout;
406 log::trace!("create_coordinates...");
407 let Widget (parent_layout, _, parent_canvas) =
408 Frame::try_get (elements, parent_frame_id).unwrap();
409 let parent_node = elements.get (parent_frame_id).unwrap();
410 let mut total_weights = 0;
411 let mut total_absolute = 0;
412 let mut add_layout = |layout : &layout::Tiled| match layout {
413 layout::Tiled::Weighted (weight) => total_weights += weight.get(),
414 layout::Tiled::Absolute (size) => total_absolute += size.get()
415 };
416 add_layout (layout);
417 let sibling_layouts = parent_node.children().iter().map (|child_id| {
418 let Widget (child_layout, _, _) = Frame::try_get (elements, child_id).ok()?;
419 if let layout::Variant::Tiled (tiled) = &child_layout.variant {
420 add_layout (tiled);
421 Some ((child_id, tiled))
422 } else {
423 None
424 }
425 }).collect::<Vec <_>>();
426 let order_index = match order {
427 CreateOrder::Prepend => Some (0),
428 CreateOrder::NthSibling (n) => Some (n),
429 CreateOrder::Append => None
430 };
431 let mut sibling_coordinates = vec![];
432 let mut start_weight = 0;
433 let mut start_absolute = 0;
434 let mut new_coordinates = None;
435 let mut index = 0;
436 for (i, maybe_tiled) in sibling_layouts.into_iter().enumerate() {
438 if order_index == Some (i as u32) {
439 new_coordinates = Some (super::coordinates (
440 &parent_layout.orientation, parent_canvas, layout, &mut start_weight,
441 &mut start_absolute, total_weights, total_absolute
442 ));
443 }
444 if let Some ((id, tiled)) = maybe_tiled {
445 let coordinates = super::coordinates (
446 &parent_layout.orientation, parent_canvas, tiled, &mut start_weight,
447 &mut start_absolute, total_weights, total_absolute);
448 sibling_coordinates.push ((id.clone(), coordinates));
449 }
450 index = i + 1;
451 }
452 if new_coordinates.is_none() {
453 if cfg!(debug_assertions) && let Some (n) = order_index {
454 debug_assert_eq!(n, index as u32);
455 }
456 new_coordinates = Some (super::coordinates (
457 &parent_layout.orientation, parent_canvas, layout,
458 &mut start_weight, &mut start_absolute, total_weights, total_absolute
459 ));
460 }
461 log::trace!("...create_coordinates");
462 (new_coordinates.unwrap(), sibling_coordinates)
463 }
464}