gooey/widget/frame/
free.rs

1use std::sync::LazyLock;
2
3use nsys::math::Vector2;
4
5use crate::prelude::*;
6
7use super::set_layout;
8
9pub use self::builder::FreeBuilder as Builder;
10
11//
12//  controls
13//
14
15pub static CONTROLS : LazyLock <Controls> = LazyLock::new (||
16  controls::Builder::new()
17    .buttons ({
18      let mut v : Vec <controls::Button> = vec![
19        controls::button::Builtin::FrameFreeMoveRight,
20        controls::button::Builtin::FrameFreeMoveLeft,
21        controls::button::Builtin::FrameFreeMoveUp,
22        controls::button::Builtin::FrameFreeMoveDown,
23        controls::button::Builtin::FrameFreeGrowWidth,
24        controls::button::Builtin::FrameFreeShrinkWidth,
25        controls::button::Builtin::FrameFreeGrowHeight,
26        controls::button::Builtin::FrameFreeShrinkHeight
27      ].into_iter().map (Into::into).collect();
28      v.extend (super::CONTROLS.buttons.iter().cloned());
29      v.into()
30    })
31    .build()
32);
33
34/// Builtin button control `FrameFreeMoveRight`.
35///
36/// Frame must have free layout.
37pub fn move_right (
38  _             : &controls::button::Release,
39  elements      : &Tree <Element>,
40  node_id       : &NodeId,
41  action_buffer : &mut Vec <(NodeId, Action)>
42) {
43  log::trace!("move_right...");
44  let Widget (layout, _, canvas) = Frame::try_get (elements, node_id).unwrap();
45  let coord_kind_override = Some (canvas.coordinates.kind());
46  let layout  = {
47    use controller::component::Layout;
48    let (mut free, area) = layout.variant.clone().try_into().unwrap();
49    free.offset_move_right();
50    Layout { variant: (free, area).into(), .. layout.clone() }
51  };
52  set_layout (elements, node_id, layout, coord_kind_override, action_buffer);
53  log::trace!("...move_right");
54}
55
56/// Builtin button control `FrameFreeMoveLeft`.
57///
58/// Frame must have free layout.
59pub fn move_left (
60  _             : &controls::button::Release,
61  elements      : &Tree <Element>,
62  node_id       : &NodeId,
63  action_buffer : &mut Vec <(NodeId, Action)>
64) {
65  log::trace!("move_left...");
66  let Widget (layout, _, canvas) = Frame::try_get (elements, node_id).unwrap();
67  let coordinate_kind = Some (canvas.coordinates.kind());
68  let layout  = {
69    use controller::component::Layout;
70    let (mut free, area) = layout.variant.clone().try_into().unwrap();
71    free.offset_move_left();
72    Layout { variant: (free, area).into(), .. layout.clone() }
73  };
74  set_layout (elements, node_id, layout, coordinate_kind, action_buffer);
75  log::trace!("...move_left");
76}
77
78/// Builtin button control `FrameFreeMoveUp`.
79///
80/// Frame must have free layout.
81pub fn move_up (
82  _             : &controls::button::Release,
83  elements      : &Tree <Element>,
84  node_id       : &NodeId,
85  action_buffer : &mut Vec <(NodeId, Action)>
86) {
87  log::trace!("move_up...");
88  let Widget (layout, _, canvas) = Frame::try_get (elements, node_id).unwrap();
89  let coordinate_kind = Some (canvas.coordinates.kind());
90  let layout = {
91    use controller::component::Layout;
92    let (mut free, area) = layout.variant.clone().try_into().unwrap();
93    free.offset_move_up();
94    Layout { variant: (free, area).into(), .. layout.clone() }
95  };
96  set_layout (elements, node_id, layout, coordinate_kind, action_buffer);
97  log::trace!("...move_up");
98}
99
100/// Builtin button control `FrameFreeMoveDown`.
101///
102/// Frame must have free layout.
103pub fn move_down (
104  _             : &controls::button::Release,
105  elements      : &Tree <Element>,
106  node_id       : &NodeId,
107  action_buffer : &mut Vec <(NodeId, Action)>
108) {
109  log::trace!("move_down...");
110  let Widget (layout, _, canvas) = Frame::try_get (elements, node_id).unwrap();
111  let coordinate_kind = Some (canvas.coordinates.kind());
112  let layout = {
113    use controller::component::Layout;
114    let (mut free, area) = layout.variant.clone().try_into().unwrap();
115    free.offset_move_down();
116    Layout { variant: (free, area).into(), .. layout.clone() }
117  };
118  set_layout (elements, node_id, layout, coordinate_kind, action_buffer);
119  log::trace!("...move_down");
120}
121
122/// Builtin button control `FrameFreeGrowWidth`
123///
124/// Frame must have free layout.
125pub fn grow_width (
126  _             : &controls::button::Release,
127  elements      : &Tree <Element>,
128  node_id       : &NodeId,
129  action_buffer : &mut Vec <(NodeId, Action)>
130) {
131  log::trace!("grow_width...");
132  let Widget (layout, _, canvas) = Frame::try_get (elements, node_id).unwrap();
133  let coordinate_kind = Some (canvas.coordinates.kind());
134  let layout = {
135    use controller::component::Layout;
136    let (mut free, area) = layout.variant.clone().try_into().unwrap();
137    free.size_grow_width();
138    Layout { variant: (free, area).into(), .. layout.clone() }
139  };
140  set_layout (elements, node_id, layout, coordinate_kind, action_buffer);
141  log::trace!("...grow_width");
142}
143
144/// Builtin button control `FrameFreeShrinkWidth`
145///
146/// Frame must have free layout with absolute size.
147pub fn shrink_width (
148  _             : &controls::button::Release,
149  elements      : &Tree <Element>,
150  node_id       : &NodeId,
151  action_buffer : &mut Vec <(NodeId, Action)>
152) {
153  log::trace!("shrink_width...");
154  let Widget (layout, _, canvas) = Frame::try_get (elements, node_id).unwrap();
155  let coordinate_kind = Some (canvas.coordinates.kind());
156  let layout = {
157    use controller::component::Layout;
158    let (mut free, area) = layout.variant.clone().try_into().unwrap();
159    free.size_shrink_width();
160    Layout { variant: (free, area).into(), .. layout.clone() }
161  };
162  set_layout (elements, node_id, layout, coordinate_kind, action_buffer);
163  log::trace!("...shrink_width");
164}
165
166/// Builtin button control `FrameFreeGrowHeight`
167///
168/// Frame must have free layout with absolute size.
169pub fn grow_height (
170  _             : &controls::button::Release,
171  elements      : &Tree <Element>,
172  node_id       : &NodeId,
173  action_buffer : &mut Vec <(NodeId, Action)>
174) {
175  log::trace!("grow_height...");
176  let Widget (layout, _, canvas) = Frame::try_get (elements, node_id).unwrap();
177  let coordinate_kind = Some (canvas.coordinates.kind());
178  let layout = {
179    use controller::component::Layout;
180    let (mut free, area) = layout.variant.clone().try_into().unwrap();
181    free.size_grow_height();
182    Layout { variant: (free, area).into(), .. layout.clone() }
183  };
184  set_layout (elements, node_id, layout, coordinate_kind, action_buffer);
185  log::trace!("...grow_height");
186}
187
188/// Builtin button control `FrameFreeShrinkHeight`
189///
190/// Frame must have free layout with absolute size.
191pub fn shrink_height (
192  _             : &controls::button::Release,
193  elements      : &Tree <Element>,
194  node_id       : &NodeId,
195  action_buffer : &mut Vec <(NodeId, Action)>
196) {
197  log::trace!("shrink_height...");
198  let Widget (layout, _, canvas) = Frame::try_get (elements, node_id).unwrap();
199  let coordinate_kind = Some (canvas.coordinates.kind());
200  let layout = {
201    use controller::component::Layout;
202    let (mut free, area) = layout.variant.clone().try_into().unwrap();
203    free.size_shrink_height();
204    Layout { variant: (free, area).into(), .. layout.clone() }
205  };
206  set_layout (elements, node_id, layout, coordinate_kind, action_buffer);
207  log::trace!("...shrink_height");
208}
209
210//
211//  public
212//
213
214/// Rearrange child frames with free absolute layouts
215// TODO: make this a button control ???
216pub fn rearrange_absolute (
217  elements    : &Tree <Element>,
218  parent_id   : &NodeId,
219  anchor      : Alignment,
220  offset_hv   : (i32, i32),
221  orientation : Orientation,
222  spacing     : u32,
223  reverse     : bool
224) -> Vec <(NodeId, Action)> {
225  use controller::{offset, size, Offset, Orientation, Size};
226  use controller::component::{layout, Layout};
227  log::trace!("rearrange_absolute...");
228  let mut out = vec![];
229  let children_ids = elements.children_ids (parent_id).unwrap();
230  match orientation {
231    Orientation::Horizontal => {
232      let (mut horizontal, vertical) = offset_hv;
233      for child_id in children_ids {
234        if let Ok (Widget (layout, _, canvas)) = Frame::try_get (elements, child_id) {
235          match &layout.variant {
236            layout::Variant::Free (free@layout::Free {
237              offset: Offset { horizontal: offset::Signed::Absolute (_), .. },
238              size:   Size   { width:      size::Unsigned::Absolute (_), .. },
239              ..
240            }, area) => {
241              let coordinate_kind = Some (canvas.coordinates.kind());
242              let layout = {
243                let offset = {
244                  let horizontal = horizontal.into();
245                  let vertical   = vertical.into();
246                  Offset { horizontal, vertical }
247                };
248                let size = {
249                  let size = free.size;
250                  let w    = u32::try_from (size.width).unwrap();
251                  let next = (w + spacing) as i32;
252                  if reverse {
253                    horizontal -= next;
254                  } else {
255                    horizontal += next;
256                  }
257                  size
258                };
259                let free = layout::Free { anchor, offset, size };
260                Layout { variant: (free, area.clone()).into(), .. layout.clone() }
261              };
262              set_layout (elements, child_id, layout, coordinate_kind, &mut out);
263            }
264            _ => {}
265          }
266        }
267      }
268    }
269    Orientation::Vertical   => {
270      let (horizontal, mut vertical) = offset_hv;
271      for child_id in children_ids {
272        if let Ok (Widget (layout, _, canvas)) = Frame::try_get (elements, child_id) {
273          match &layout.variant {
274            layout::Variant::Free (free@layout::Free {
275              offset: Offset { vertical: offset::Signed::Absolute (_), .. },
276              size:   Size   { height:   size::Unsigned::Absolute (_), .. },
277              ..
278            }, area) => {
279              let coordinate_kind = Some (canvas.coordinates.kind());
280              let layout = {
281                let offset = {
282                  let horizontal = horizontal.into();
283                  let vertical   = vertical.into();
284                  Offset { horizontal, vertical }
285                };
286                let size   = {
287                  let size = free.size;
288                  let h = u32::try_from (size.height).unwrap();
289                  let next = (h + spacing) as i32;
290                  if reverse {
291                    vertical -= next;
292                  } else {
293                    vertical += next;
294                  }
295                  size
296                };
297                let free = layout::Free { anchor, offset, size };
298                Layout { variant: (free, area.clone()).into(), .. layout.clone() }
299              };
300              set_layout (elements, child_id, layout, coordinate_kind, &mut out);
301            }
302            _ => {}
303          }
304        }
305      }
306    }
307  }
308  log::trace!("...rearrange_absolute");
309  out
310}
311
312//
313//  crate
314//
315
316/// Compute the coordinates of a frame with the given free layout and parent
317/// coordinates
318pub (crate) fn coordinates (
319  parent_canvas       : &Canvas,
320  layout              : &layout::Free,
321  area                : &Area,
322  coord_kind_override : Option <coordinates::Kind>
323) -> Coordinates {
324  log::trace!("coordinates...");
325  let layout::Free { anchor, offset, size } = layout;
326  let parent_coordinates = match area {
327    Area::Exterior => parent_canvas.coordinates,
328    Area::Interior => parent_canvas.body_coordinates()
329  };
330  let dimensions = dimensions (&parent_coordinates.dimensions(), size,
331    coord_kind_override);
332  let position = position (&parent_coordinates, &dimensions, *anchor, offset,
333    coord_kind_override);
334  let out = (position, dimensions).try_into().unwrap();
335  log::trace!("...coordinates");
336  out
337}
338
339//
340//  private
341//
342
343/// Compute dimensions of a free frame with the given size in relation to the
344/// given parent dimensions.
345///
346/// Returned dimensions type (tile or pixel) will match that of the given parent
347/// dimensions unless a coordinate kind override is given.
348///
349/// Enforcees a minimum dimensions of 1.
350#[inline]
351fn dimensions (
352  parent_dimensions   : &Dimensions,
353  size                : &Size,
354  coord_kind_override : Option <coordinates::Kind>
355) -> Dimensions {
356  use controller::size;
357  use view::{coordinates, dimensions, Dimensions};
358  log::trace!("dimensions...");
359  let same_as_parent = || match parent_dimensions {
360    Dimensions::Tile  (dimensions) => {
361      let rows = match size.height {
362        size::Unsigned::Absolute (height) => height,
363        size::Unsigned::Relative (height) =>
364          std::cmp::max (1, (dimensions.rows() as f32 * *height) as u32)
365      };
366      let columns = match size.width {
367        size::Unsigned::Absolute (width) => width,
368        size::Unsigned::Relative (width) =>
369          std::cmp::max (1, (dimensions.columns() as f32 * *width)  as u32)
370      };
371      Dimensions::from (dimensions::Tile::from (Vector2::new (rows, columns)))
372    }
373    Dimensions::Pixel (dimensions) => {
374      let width = match size.width {
375        size::Unsigned::Absolute (width) => width,
376        size::Unsigned::Relative (width) =>
377          std::cmp::max (1, (dimensions.width() as f32 * *width)  as u32)
378      };
379      let height = match size.height {
380        size::Unsigned::Absolute (height) => height,
381        size::Unsigned::Relative (height) =>
382          std::cmp::max (1, (dimensions.height() as f32 * *height) as u32)
383      };
384      Dimensions::from (dimensions::Pixel::from (Vector2::new (width, height)))
385    }
386  };
387  let out = if let Some (kind) = coord_kind_override {
388    match parent_dimensions {
389      Dimensions::Tile  (_dimensions) =>
390        if kind == coordinates::Kind::Tile {
391          same_as_parent()
392        } else {
393          unimplemented!("TODO: pixel dimensions with tile dimension parent")
394        }
395      Dimensions::Pixel (dimensions) =>
396        if kind == coordinates::Kind::Pixel {
397          same_as_parent()
398        } else {
399          let [tile_width, tile_height] = *coordinates::TILE_WH;
400          let rows = match size.height {
401            size::Unsigned::Absolute (height) => height,
402            size::Unsigned::Relative (height) => {
403              let parent_rows = dimensions.height() / tile_height;
404              std::cmp::max (1, (parent_rows as f32 * *height) as u32)
405            }
406          };
407          let columns = match size.width {
408            size::Unsigned::Absolute (width) => width,
409            size::Unsigned::Relative (width) => {
410              let parent_columns = dimensions.width() / tile_width;
411              std::cmp::max (1, (parent_columns as f32 * *width)  as u32)
412            }
413          };
414          Dimensions::from (dimensions::Tile::from (Vector2::new (rows, columns)))
415        }
416    }
417  } else {
418    same_as_parent()
419  };
420  log::trace!("...dimensions");
421  out
422}
423
424/// Computes the position of a free frame with the given dimensions, anchor, and offset
425/// relative to the given parent coordinates.
426///
427/// Returned position type (tile or pixel) will match that of the given parent
428/// coordinates.
429fn position (
430  parent_coordinates  : &Coordinates,
431  dimensions          : &Dimensions,
432  anchor              : Alignment,
433  offset              : &Offset,
434  coord_kind_override : Option <coordinates::Kind>
435) -> Position {
436  use controller::{alignment, offset};
437  use view::{coordinates, dimensions, position, Coordinates};
438  log::trace!("position...");
439  let same_as_parent = || match parent_coordinates {
440    Coordinates::Tile (parent_position, parent_dimensions) => {
441      let dimensions = dimensions::Tile::try_from (*dimensions).unwrap();
442      let row    = match offset.vertical {
443        offset::Signed::Absolute (vertical) => match anchor.vertical {
444          alignment::Vertical::Top    => vertical,
445          alignment::Vertical::Middle =>
446            parent_dimensions.rows() as i32/2 - dimensions.rows() as i32/2
447              - vertical,
448          alignment::Vertical::Bottom =>
449            parent_dimensions.rows() as i32 - dimensions.rows() as i32
450              - vertical
451        }
452        offset::Signed::Relative (vertical) => {
453          let absolute_offset = (*vertical * parent_dimensions.rows() as f32)
454            as i32;
455          match anchor.vertical {
456            alignment::Vertical::Top    => absolute_offset,
457            alignment::Vertical::Middle =>
458              parent_dimensions.rows() as i32/2 - dimensions.rows() as i32/2
459                - absolute_offset,
460            alignment::Vertical::Bottom =>
461              parent_dimensions.rows() as i32 - dimensions.rows() as i32
462                - absolute_offset
463          }
464        }
465      };
466      let column = match offset.horizontal {
467        offset::Signed::Absolute (horizontal) => match anchor.horizontal {
468          alignment::Horizontal::Left   => horizontal,
469          alignment::Horizontal::Center =>
470            parent_dimensions.columns() as i32/2 - dimensions.columns() as i32/2
471              + horizontal,
472          alignment::Horizontal::Right  =>
473            parent_dimensions.columns() as i32 - dimensions.columns() as i32
474              - horizontal
475        }
476        offset::Signed::Relative (horizontal) => {
477          let absolute_offset =
478            (*horizontal * parent_dimensions.columns() as f32) as i32;
479          match anchor.horizontal {
480            alignment::Horizontal::Left   => absolute_offset,
481            alignment::Horizontal::Center =>
482              parent_dimensions.columns() as i32/2
483                - dimensions.columns() as i32/2 + absolute_offset,
484            alignment::Horizontal::Right  =>
485              parent_dimensions.columns() as i32 - dimensions.columns() as i32
486                - absolute_offset
487          }
488        }
489      };
490      position::Tile::from (
491        parent_position.position_rc + Vector2::new (row, column)
492      ).into()
493    }
494    Coordinates::Pixel (parent_position, parent_dimensions) => {
495      let dimensions = dimensions::Pixel::try_from (*dimensions).unwrap();
496      let y = match offset.vertical {
497        offset::Signed::Absolute (vertical) => match anchor.vertical {
498          alignment::Vertical::Bottom => vertical,
499          alignment::Vertical::Middle =>
500            parent_dimensions.height() as i32/2 - dimensions.height() as i32/2
501              + vertical,
502          alignment::Vertical::Top    =>
503            parent_dimensions.height() as i32 - dimensions.height() as i32
504              - vertical
505        }
506        offset::Signed::Relative (vertical) => {
507          let absolute_offset =
508            (*vertical * parent_dimensions.height() as f32) as i32;
509          match anchor.vertical {
510            alignment::Vertical::Bottom    =>
511              absolute_offset,
512            alignment::Vertical::Middle =>
513              parent_dimensions.height() as i32/2
514                - dimensions.height() as i32/2 + absolute_offset,
515            alignment::Vertical::Top =>
516              parent_dimensions.height() as i32 - dimensions.height() as i32
517                - absolute_offset
518          }
519        }
520      };
521      let x = match offset.horizontal {
522        offset::Signed::Absolute (horizontal) => match anchor.horizontal {
523          alignment::Horizontal::Left   => horizontal,
524          alignment::Horizontal::Center =>
525            parent_dimensions.width() as i32/2
526              - dimensions.width() as i32/2 + horizontal,
527          alignment::Horizontal::Right  =>
528            parent_dimensions.width() as i32 - dimensions.width() as i32
529              - horizontal
530        }
531        offset::Signed::Relative (horizontal) => {
532          let absolute_offset = (*horizontal * parent_dimensions.width() as f32)
533            as i32;
534          match anchor.horizontal {
535            alignment::Horizontal::Left   => absolute_offset,
536            alignment::Horizontal::Center =>
537              parent_dimensions.width() as i32/2
538                - dimensions.width() as i32/2 + absolute_offset,
539            alignment::Horizontal::Right  =>
540              parent_dimensions.width() as i32 - dimensions.width() as i32
541                - absolute_offset
542          }
543        }
544      };
545      position::Pixel::from (parent_position.position_xy + Vector2::new (x, y))
546        .into()
547    }
548  };
549  let out = if let Some (kind) = coord_kind_override {
550    match parent_coordinates.kind() {
551      coordinates::Kind::Tile  => if kind == coordinates::Kind::Tile {
552        same_as_parent()
553      } else {
554        unimplemented!("TODO: pixel position with tile position parent")
555      }
556      coordinates::Kind::Pixel => if kind == coordinates::Kind::Pixel {
557        same_as_parent()
558      } else {
559        // create a tile child attached to a pixel parent
560        let parent_aabb    = geometry::integer::Aabb2::from (*parent_coordinates);
561        let parent_aabb_tiles = coordinates::pixel_to_tile_aabb (parent_aabb);
562        let [parent_row, parent_column] = parent_aabb_tiles.min().0.into_array();
563        let parent_columns = parent_aabb_tiles.width();
564        let parent_rows    = parent_aabb_tiles.height();
565        let dimensions     = dimensions::Tile::try_from (*dimensions).unwrap();
566        #[expect(trivial_numeric_casts)]
567        let row            = match offset.vertical {
568          offset::Signed::Absolute (vertical) => match anchor.vertical {
569            alignment::Vertical::Top    => vertical,
570            alignment::Vertical::Middle =>
571              parent_rows as i32/2 - dimensions.rows() as i32/2 - vertical,
572            alignment::Vertical::Bottom =>
573              parent_rows as i32 - dimensions.rows() as i32 - vertical
574          }
575          offset::Signed::Relative (vertical) => {
576            let absolute_offset = (*vertical * parent_rows as f32)
577              as i32;
578            match anchor.vertical {
579              alignment::Vertical::Top    => absolute_offset,
580              alignment::Vertical::Middle =>
581                parent_rows as i32/2 - dimensions.rows() as i32/2 - absolute_offset,
582              alignment::Vertical::Bottom =>
583                parent_rows as i32 - dimensions.rows() as i32 - absolute_offset
584            }
585          }
586        };
587        #[expect(trivial_numeric_casts)]
588        let column     = match offset.horizontal {
589          offset::Signed::Absolute (horizontal) => match anchor.horizontal {
590            alignment::Horizontal::Left   => horizontal,
591            alignment::Horizontal::Center =>
592              parent_columns as i32/2 - dimensions.columns() as i32/2 + horizontal,
593            alignment::Horizontal::Right  =>
594              parent_columns as i32 - dimensions.columns() as i32 - horizontal
595          }
596          offset::Signed::Relative (horizontal) => {
597            let absolute_offset =
598              (*horizontal * parent_columns as f32) as i32;
599            match anchor.horizontal {
600              alignment::Horizontal::Left   => absolute_offset,
601              alignment::Horizontal::Center =>
602                parent_columns as i32/2 - dimensions.columns() as i32/2
603                  + absolute_offset,
604              alignment::Horizontal::Right  =>
605                parent_columns as i32 - dimensions.columns() as i32 - absolute_offset
606            }
607          }
608        };
609        position::Tile::new_rc (parent_row + row, parent_column + column).into()
610      }
611    }
612  } else {
613    same_as_parent()
614  };
615  log::trace!("...position");
616  out
617}
618
619//
620//  builder
621//
622
623mod builder {
624  use derive_builder::Builder;
625  use crate::prelude::*;
626
627  #[derive(Builder)]
628  #[builder(public, pattern="owned", build_fn(private), setter(strip_option))]
629  struct Free <'a, A : Application> {
630    elements            : &'a Tree <Element>,
631    parent_id           : &'a NodeId,
632    #[builder(default)]
633    appearances         : Appearances,
634    #[builder(default)]
635    area                : Area,
636    #[builder(default)]
637    bindings            : Option <&'a Bindings <A>>,
638    #[builder(default)]
639    border              : Option <Border>,
640    #[builder(default)]
641    clear_color         : canvas::ClearColor,
642    #[builder(default)]
643    coord_kind_override : Option <coordinates::Kind>,
644    #[builder(default)]
645    disabled            : bool,
646    #[builder(default)]
647    layout              : layout::Free,
648    #[builder(default)]
649    orientation         : (Orientation, Area)
650  }
651
652  impl <'a, A : Application> FreeBuilder <'a, A> {
653    pub const fn new (elements : &'a Tree <Element>, parent_id : &'a NodeId) -> Self {
654      FreeBuilder {
655        elements:            Some (elements),
656        parent_id:           Some (parent_id),
657        appearances:         None,
658        area:                None,
659        bindings:            None,
660        border:              None,
661        clear_color:         None,
662        coord_kind_override: None,
663        disabled:            None,
664        layout:              None,
665        orientation:         None
666      }
667    }
668  }
669
670  impl <A : Application> BuildElement for FreeBuilder <'_, A> {
671    /// Create a frame element with the given parent frame, free layout and optional
672    /// color.
673    ///
674    /// Controls for free frames will be taken from the given bindings.
675    ///
676    /// If not given an explicit coordinate kind, canvas coordinates type will match
677    /// that of the given parent dimensions (tile or pixel).
678    fn build_element (self) -> Element {
679      log::trace!("build element...");
680      let Free {
681        elements, parent_id, appearances, area, bindings, border, clear_color,
682        coord_kind_override, layout, orientation, disabled
683      } = self.build()
684        .map_err(|err| log::error!("frame free builder error: {err:?}"))
685        .unwrap();
686      let bindings_empty = Bindings::empty();
687      let bindings = bindings.unwrap_or(&bindings_empty).get_bindings (&super::CONTROLS);
688      let Widget (_, _, parent_canvas) = Frame::try_get (elements, parent_id).unwrap();
689      let parent_node = elements.get (parent_id).unwrap();
690      let focus_top = if let Some (first_child_id) = parent_node.children().first()
691        && Menu::try_get (elements, first_child_id).is_ok()
692      {
693        false
694      } else {
695        true
696      };
697      let controller = {
698        let mut controller      = Controller::with_bindings (&bindings);
699        controller.component    = Layout {
700          orientation, variant: (layout, area.clone()).into(),
701        }.into();
702        controller.focus_top    = focus_top;
703        controller.appearances  = appearances;
704        controller
705      };
706      let view = {
707        let coordinates =
708          super::coordinates (parent_canvas, &layout, &area, coord_kind_override);
709        let appearance  = controller.get_appearance().clone();
710        let component   = Canvas { coordinates, clear_color, border }.into();
711        View { component, appearance, .. View::default() }
712      };
713      let mut frame =
714        Element::new ("Frame".to_string(), controller, Model::default(), view);
715      if parent_node.data().controller.state == State::Disabled || disabled {
716        frame.disable()
717      }
718      log::trace!("...build element");
719      frame
720    }
721  }
722}