1use std::convert::{TryFrom, TryInto};
14use std::sync::LazyLock;
15
16use crate::geometry;
17use crate::prelude::*;
18
19pub use self::builder::FrameBuilder as Builder;
20
21pub mod free;
22pub mod screen;
23pub mod tiled;
24
25pub type Frame <'element> = Widget <'element, Layout, model::Component, Canvas>;
26
27pub static CONTROLS : LazyLock <Controls> = LazyLock::new (||
32 controls::Builder::new()
33 .buttons (vec![
34 controls::button::Builtin::ActivatePointer,
35 controls::button::Builtin::FrameClose,
36 controls::button::Builtin::FrameToggleOrientation
37 ].into_iter().map (Into::into).collect::<Vec <_>>().into())
38 .build()
39);
40
41pub fn close (
47 _ : &controls::button::Release,
48 elements : &Tree <Element>,
49 node_id : &NodeId,
50 action_buffer : &mut Vec <(NodeId, Action)>
51) {
52 use controller::component::layout;
53 log::trace!("close...");
54 let Widget (layout, _, _) = Frame::try_get (elements, node_id).unwrap();
55 match layout.variant {
56 layout::Variant::Tiled (_) => for (sibling_id, coordinates) in
57 tiled::destroy_coordinates (elements, node_id)
58 {
59 set_coordinates (elements, &sibling_id, &coordinates, None,
60 action_buffer);
61 }
62 layout::Variant::Free (..) => {}
63 }
64 action_buffer.push ((node_id.clone(), Action::Destroy));
65 log::trace!("...close");
66}
67
68pub fn refresh (
73 _ : &controls::button::Release,
74 elements : &Tree <Element>,
75 node_id : &NodeId,
76 action_buffer : &mut Vec <(NodeId, Action)>
77) {
78 log::trace!("refresh...");
79 let Widget (layout, _, canvas) = Frame::try_get (elements, node_id).unwrap();
80 let coordinate_kind = Some (canvas.coordinates.kind());
81 set_layout (elements, node_id, layout.clone(), coordinate_kind, action_buffer);
82 log::trace!("...refresh");
83}
84
85pub fn toggle_orientation (
90 _ : &controls::button::Release,
91 elements : &Tree <Element>,
92 node_id : &NodeId,
93 action_buffer : &mut Vec <(NodeId, Action)>
94) {
95 log::trace!("toggle_orientation...");
96 let Widget (layout, _, canvas) = Frame::try_get (elements, node_id).unwrap();
97 let (mut orientation, area) = layout.orientation.clone();
98 orientation = orientation.toggle();
99 let layout = Layout {
100 orientation: (orientation, area),
101 .. layout.clone()
102 };
103 let coordinate_kind = Some (canvas.coordinates.kind());
104 set_layout (elements, node_id, layout, coordinate_kind, action_buffer);
105 log::trace!("...toggle_orientation");
106}
107
108pub fn activate_pointer (
117 release : &controls::button::Release,
118 elements : &Tree <Element>,
119 node_id : &NodeId,
120 action_buffer : &mut Vec <(NodeId, Action)>
121) {
122 log::trace!("activate pointer...");
123 let pointer_position = {
124 let pointer = POINTER.read().unwrap();
125 [pointer.position_horizontal, pointer.position_vertical].into()
126 };
127 let mut last = None;
128 for element_id in elements.traverse_pre_order_ids (node_id).unwrap() {
129 let element = elements.get (&element_id).unwrap().data();
130 if let Ok (Widget (_, _, canvas)) = Frame::try_from (element) {
131 let canvas_aabb = {
132 let aabb = geometry::integer::Aabb2::from (canvas.coordinates);
133 let pixel_aabb =
134 if canvas.coordinates.kind() == coordinates::Kind::Tile {
135 coordinates::tile_to_pixel_aabb (aabb)
136 } else {
137 aabb
138 };
139 geometry::Aabb2::with_minmax (
140 pixel_aabb.min().numcast().unwrap(),
141 pixel_aabb.max().numcast().unwrap())
142 };
143 if canvas_aabb.contains (&pointer_position) {
144 last = Some (element_id);
145 }
146 }
147 }
148 if let Some (element_id) = last {
149 let element = elements.get (&element_id).unwrap().data();
150 if element.controller.state == State::Enabled {
151 action_buffer.push ((element_id.clone(), Action::Focus));
152 }
153 if element.controller.state != State::Disabled &&
154 let Some (child_id) = elements.children_ids (&element_id).unwrap().next()
155 {
156 let child = elements.get (child_id).unwrap().data();
157 if let Ok (Widget (switch, _, _)) = Button::try_from (child) {
158 if switch.toggle {
159 match switch.state {
160 switch::State::On =>
161 button::release (&None, elements, &element_id, action_buffer),
162 switch::State::Off =>
163 button::push (release, elements, &element_id, action_buffer)
164 }
165 } else {
166 button::push (release, elements, &element_id, action_buffer)
167 }
168 }
169 }
170 }
171 log::trace!("...activate pointer");
172}
173
174pub fn hot_pointer (
183 pointer : &input::Pointer,
184 elements : &Tree <Element>,
185 node_id : &NodeId,
186 action_buffer : &mut Vec <(NodeId, Action)>
187) {
188 log::trace!("hot pointer...");
189 debug_assert_eq!(&*POINTER.read().unwrap(), pointer);
190 let pointer_position =
191 [pointer.position_horizontal, pointer.position_vertical].into();
192 let mut last = None;
193 for element_id in elements.traverse_pre_order_ids (node_id).unwrap() {
194 let element = elements.get (&element_id).unwrap().data();
195 if let Ok (Widget (_, _, canvas)) = Frame::try_from (element) {
196 let canvas_aabb = {
197 let aabb = geometry::integer::Aabb2::from (canvas.coordinates);
198 let pixel_aabb =
199 if canvas.coordinates.kind() == coordinates::Kind::Tile {
200 coordinates::tile_to_pixel_aabb (aabb)
201 } else {
202 aabb
203 };
204 geometry::Aabb2::with_minmax (
205 pixel_aabb.min().numcast().unwrap(),
206 pixel_aabb.max().numcast().unwrap())
207 };
208 if canvas_aabb.contains (&pointer_position) {
209 last = Some (element_id);
210 }
211 }
212 }
213 if let Some (element_id) = last {
214 let element = elements.get (&element_id).unwrap().data();
215 if element.controller.state == State::Enabled {
216 action_buffer.push ((element_id.clone(), Action::Focus));
217 }
218 }
219 log::trace!("...hot pointer");
220}
221
222pub (crate) fn set_layout (
229 elements : &Tree <Element>,
230 frame_id : &NodeId,
231 layout : Layout,
232 coord_kind_override : Option <coordinates::Kind>,
233 action_buffer : &mut Vec <(NodeId, Action)>
234) {
235 log::trace!("set_layout...");
236 { let new_layout = layout.clone();
238 let update_layout = Box::new (move |controller : &mut Controller|
239 controller.component = new_layout.into());
240 action_buffer.push (
241 (frame_id.clone(), Action::ModifyController (update_layout)));
242 }
243 for (frame_id, coordinates) in
245 new_coordinates (elements, frame_id, &layout, coord_kind_override)
246 {
247 set_coordinates (elements, &frame_id, &coordinates,
248 Some (layout.orientation.clone()), action_buffer);
249 }
250 log::trace!("...set_layout");
251}
252
253fn new_coordinates (
273 elements : &Tree <Element>,
274 frame_id : &NodeId,
275 layout : &Layout,
276 coord_kind_override : Option <coordinates::Kind>
277) -> Vec <(NodeId, Coordinates)> {
278 use controller::size;
279 use controller::component::layout;
280 use view::coordinates::dimensions;
281 use view::component::Canvas;
282 log::trace!("new_coordinates...");
283 let out = match &layout.variant {
284 layout::Variant::Free (free, area) => {
285 let parent_canvas = if elements.root_node_id().unwrap() == frame_id {
286 let Widget (_, _, canvas) = Frame::try_get (elements, frame_id).unwrap();
288 let (width, height) = {
289 let (free, _) = layout.variant.clone().try_into().unwrap();
291 let width = match free.size.width {
292 size::Unsigned::Absolute (w) => w,
293 size::Unsigned::Relative (_) => 1
294 };
295 let height = match free.size.height {
296 size::Unsigned::Absolute (h) => h,
297 size::Unsigned::Relative (_) => 1
298 };
299 (width, height)
300 };
301 let (mut parent, dimensions) =
302 if canvas.coordinates.kind() == coordinates::Kind::Tile {
303 ( Canvas::default_tile(),
304 dimensions::Tile::new_rc (height, width).into()
305 )
306 } else {
307 debug_assert!(canvas.coordinates.kind() == coordinates::Kind::Pixel);
308 ( Canvas::default_pixel(),
309 dimensions::Pixel::new_wh (width, height).into()
310 )
311 };
312 parent.coordinates.set_dimensions (dimensions);
313 parent
314 } else {
315 let Widget (_, _, parent_canvas) =
316 Frame::try_get (elements, elements.get_parent_id (frame_id)).unwrap();
317 parent_canvas.clone()
318 };
319 vec![(
320 frame_id.clone(),
321 free::coordinates (&parent_canvas, free, area, coord_kind_override)
322 )]
323 }
324 layout::Variant::Tiled (tiled) => {
325 debug_assert!(elements.root_node_id().unwrap() != frame_id,
326 "tiled root frame is unsupported");
327 tiled::new_coordinates (elements, frame_id, tiled)
328 }
329 };
330 log::trace!("...new_coordinates");
331 out
332}
333
334fn set_coordinates (
337 elements : &Tree <Element>,
338 frame_id : &NodeId,
339 coordinates : &Coordinates,
340 orientation : Option <(Orientation, Area)>,
341 action_buffer : &mut Vec <(NodeId, Action)>
342) {
343 log::trace!("set_coordinates...");
344 { let new_coordinates = *coordinates;
346 let update_canvas = Box::new (move |view : &mut View| {
347 use view::component::{Canvas, Kind};
348 let canvas = Canvas::try_ref_mut (&mut view.component).unwrap();
349 canvas.coordinates = new_coordinates;
350 });
351 action_buffer.push ((frame_id.clone(), Action::ModifyView (update_canvas)));
352 }
353 for (child_id, coordinates) in update_children (
355 elements, frame_id, coordinates, orientation, action_buffer
356 ) {
357 set_coordinates (elements, &child_id, &coordinates, None, action_buffer);
358 }
359 log::trace!("...set_coordinates");
360}
361
362fn update_children (
366 elements : &Tree <Element>,
367 frame_id : &NodeId,
368 coordinates : &Coordinates,
369 orientation : Option <(Orientation, Area)>,
370 action_buffer : &mut Vec <(NodeId, Action)>
371) -> Vec <(NodeId, Coordinates)> {
372 use view::component::{Body, Canvas, Image, Kind};
373 log::trace!("update_children...");
374 let mut child_coordinates = vec![];
375 let (mut tiled_ids, mut tiled_layouts) = (vec![], vec![]);
376 let node = elements.get (frame_id).unwrap();
377 let Widget (layout, _, canvas) = Frame::try_from (node.data()).unwrap();
378 let new_canvas = {
379 let coordinates = *coordinates;
380 Canvas { coordinates, .. canvas.clone() }
381 };
382 let orientation = orientation.unwrap_or (layout.orientation.clone());
383 for child_id in node.children().iter() {
384 if let Ok (Widget (layout, _, canvas)) = Frame::try_get (elements, child_id) {
385 use controller::component::layout;
387 match &layout.variant {
388 layout::Variant::Free (free, area) => {
389 let coordinate_kind = Some (canvas.coordinates.kind());
390 child_coordinates.push ((
391 child_id.clone(),
392 free::coordinates (&new_canvas, free, area, coordinate_kind)
393 ))
394 }
395 layout::Variant::Tiled (tiled) => {
396 tiled_ids.push (child_id.clone());
397 tiled_layouts.push (tiled.clone());
398 }
399 }
400 } else if let Ok (Widget (scroll, text, _)) =
401 Textbox::try_get (elements, child_id)
402 {
403 let new_body = textbox::body (&new_canvas, scroll, text);
405 let update_body = Box::new (|view : &mut View|{
406 let body = Body::try_ref_mut (&mut view.component).unwrap();
407 *body = new_body;
408 });
409 action_buffer.push ((child_id.clone(), Action::ModifyView (update_body)));
410 } else if let Ok (Widget (scroll, pixmap, _)) =
411 Picture::try_get (elements, child_id)
412 {
413 let new_image = picture::image (&new_canvas, scroll, pixmap);
414 let update_image = Box::new (|view : &mut View|{
415 let image = Image::try_ref_mut (&mut view.component).unwrap();
416 *image = new_image;
417 });
418 action_buffer.push ((child_id.clone(), Action::ModifyView (update_image)));
419 }
420 }
421 child_coordinates.extend (
422 tiled_ids.into_iter().zip (
423 tiled::child_coordinates (&orientation, &new_canvas, tiled_layouts)));
424 log::trace!("...update_children");
425 child_coordinates
426}
427
428mod builder {
433 use derive_builder::Builder;
434 use crate::prelude::*;
435
436 #[derive(Builder)]
437 #[builder(public, pattern="owned", build_fn(private), setter(strip_option))]
438 struct Frame <'a, A : Application> {
439 elements : &'a Tree <Element>,
440 parent_id : &'a NodeId,
441 #[builder(default)]
442 appearances : Appearances,
443 #[builder(default)]
444 bindings : Option <&'a Bindings <A>>,
445 #[builder(default)]
446 border : Option <Border>,
447 #[builder(default)]
448 clear_color : canvas::ClearColor,
449 #[builder(default)]
450 disabled : bool,
451 #[builder(default)]
452 layout : Layout
453 }
454
455 impl <'a, A : Application> FrameBuilder <'a, A> {
456 pub const fn new (elements : &'a Tree <Element>, parent_id : &'a NodeId) -> Self {
457 FrameBuilder {
458 elements: Some (elements),
459 parent_id: Some (parent_id),
460 appearances: None,
461 bindings: None,
462 border: None,
463 clear_color: None,
464 disabled: None,
465 layout: None
466 }
467 }
468 }
469
470 impl <A : Application> BuildActions for FrameBuilder <'_, A> {
471 fn build_actions (self) -> Vec<(NodeId, Action)> {
474 use controller::component::layout;
475 use crate::widget::{set_option, BuildElement};
476 log::trace!("build actions...");
477 let Frame {
478 elements, parent_id, appearances, bindings, border, clear_color,
479 disabled, layout
480 } = self.build()
481 .map_err(|err| log::error!("frame builder error: {err:?}")).unwrap();
482 let out = match layout.variant {
483 layout::Variant::Free (free, area) => {
484 let frame = {
485 let mut free = super::free::Builder::new (elements, parent_id)
486 .appearances (appearances)
487 .area (area)
488 .clear_color (clear_color)
489 .disabled (disabled)
490 .layout (free)
491 .orientation (layout.orientation);
492 set_option!(free, bindings, bindings);
493 set_option!(free, border, border);
494 free.build_element()
495 };
496 vec![(
497 parent_id.clone(),
498 Action::create_singleton (frame, CreateOrder::Append)
499 )]
500 }
501 layout::Variant::Tiled (tiled) => {
502 let mut tiled =
503 super::tiled::Builder::new (elements, parent_id)
504 .appearances (appearances)
505 .clear_color (clear_color)
506 .disabled (disabled)
507 .layout (tiled)
508 .orientation (layout.orientation);
509 set_option!(tiled, bindings, bindings);
510 set_option!(tiled, border, border);
511 tiled.build_actions()
512 }
513 };
514 log::trace!("...build actions");
515 out
516 }
517 }
518}