1use std::sync::LazyLock;
14
15use crate::{Application, Presentation, TreeHelper};
16use crate::tree::{Tree, NodeId};
17use crate::interface::{
18 model, view, Action, Controller, Element, Interface, Model, View
19};
20use crate::interface::controller::{self, controls, Controls};
21use super::{Widget, Frame, build_and_get_node_ids, frame, set_option};
22
23mod make_table;
24
25pub use self::builder::{TextboxBuilder as Builder, FitBuilder, WithFrameBuilder};
26pub use self::make_table::{box_vec, make_table, mk_table};
27
28pub type Textbox <'element> = Widget <'element,
29 controller::component::Scroll, String, view::component::Body>;
30
31pub static CONTROLS : LazyLock <Controls> = LazyLock::new (||
36 controls::Builder::new()
37 .buttons (vec![
38 controls::button::Builtin::TextboxScrollDown,
39 controls::button::Builtin::TextboxScrollUp,
40 controls::button::Builtin::TextboxScrollRight,
41 controls::button::Builtin::TextboxScrollLeft,
42 controls::button::Builtin::TextboxPageDown,
43 controls::button::Builtin::TextboxPageUp,
44 controls::button::Builtin::TextboxPageRight,
45 controls::button::Builtin::TextboxPageLeft
46 ].into_iter().map (Into::into).collect::<Vec <_>>().into())
47 .build()
48);
49
50pub fn scroll_right (
52 _ : &controls::button::Release,
53 elements : &Tree <Element>,
54 textbox_id : &NodeId,
55 action_buffer : &mut Vec <(NodeId, Action)>
56) {
57 log::trace!("scroll_right...");
58 scroll_helper (elements, textbox_id, action_buffer,
59 controller::component::Scroll::offset_move_right, 1);
60 log::trace!("...scroll_right");
61}
62
63pub fn scroll_left (
65 _ : &controls::button::Release,
66 elements : &Tree <Element>,
67 textbox_id : &NodeId,
68 action_buffer : &mut Vec <(NodeId, Action)>
69) {
70 log::trace!("scroll_left...");
71 scroll_helper (elements, textbox_id, action_buffer,
72 controller::component::Scroll::offset_move_left, 1);
73 log::trace!("...scroll_left");
74}
75
76pub fn scroll_up (
78 _ : &controls::button::Release,
79 elements : &Tree <Element>,
80 textbox_id : &NodeId,
81 action_buffer : &mut Vec <(NodeId, Action)>
82) {
83 log::trace!("scroll_up...");
84 scroll_helper (elements, textbox_id, action_buffer,
85 controller::component::Scroll::offset_move_up, 1);
86 log::trace!("...scroll_up");
87}
88
89pub fn scroll_down (
91 _ : &controls::button::Release,
92 elements : &Tree <Element>,
93 textbox_id : &NodeId,
94 action_buffer : &mut Vec <(NodeId, Action)>
95) {
96 log::trace!("scroll_down...");
97 scroll_helper (elements, textbox_id, action_buffer,
98 controller::component::Scroll::offset_move_down, 1);
99 log::trace!("...scroll_down");
100}
101
102pub fn page_right (
104 _ : &controls::button::Release,
105 elements : &Tree <Element>,
106 textbox_id : &NodeId,
107 action_buffer : &mut Vec <(NodeId, Action)>
108) {
109 log::trace!("page_right...");
110 let (width, _) = {
112 let Widget (_, _, canvas) =
113 Frame::try_get (elements, elements.get_parent_id (textbox_id)).unwrap();
114 canvas.body_wh()
115 };
116 scroll_helper (elements, textbox_id, action_buffer,
117 controller::component::Scroll::offset_move_right, width-1);
118 log::trace!("...page_right");
119}
120
121pub fn page_left (
123 _ : &controls::button::Release,
124 elements : &Tree <Element>,
125 textbox_id : &NodeId,
126 action_buffer : &mut Vec <(NodeId, Action)>
127) {
128 log::trace!("page_left...");
129 let (width, _) = {
131 let Widget (_, _, canvas) =
132 Frame::try_get (elements, elements.get_parent_id (textbox_id)).unwrap();
133 canvas.body_wh()
134 };
135 scroll_helper (elements, textbox_id, action_buffer,
136 controller::component::Scroll::offset_move_left, width-1);
137 log::trace!("...page_left");
138}
139
140pub fn page_up (
142 _ : &controls::button::Release,
143 elements : &Tree <Element>,
144 textbox_id : &NodeId,
145 action_buffer : &mut Vec <(NodeId, Action)>
146) {
147 log::trace!("page_up...");
148 let (_, height) = {
150 let Widget (_, _, canvas) =
151 Frame::try_get (elements, elements.get_parent_id (textbox_id)).unwrap();
152 canvas.body_wh()
153 };
154 scroll_helper (elements, textbox_id, action_buffer,
155 controller::component::Scroll::offset_move_up, height-1);
156 log::trace!("...page_up");
157}
158
159pub fn page_down (
161 _ : &controls::button::Release,
162 elements : &Tree <Element>,
163 textbox_id : &NodeId,
164 action_buffer : &mut Vec <(NodeId, Action)>
165) {
166 log::trace!("page_down...");
167 let (_, height) = {
169 let Widget (_, _, canvas) =
170 Frame::try_get (elements, elements.get_parent_id (textbox_id)).unwrap();
171 canvas.body_wh()
172 };
173 scroll_helper (elements, textbox_id, action_buffer,
174 controller::component::Scroll::offset_move_down, height-1);
175 log::trace!("...page_down");
176}
177
178pub fn popup <A, P> (
184 interface : &mut Interface <A, P>,
185 parent_id : NodeId,
186 text : String,
187 appearances : Option <controller::Appearances>,
188 border : Option <view::Border>
189) -> [NodeId; 2] where
190 A : Application,
191 P : Presentation
192{
193 let mut builder = FitBuilder::<A>::new (interface.elements(), &parent_id)
194 .frame_anchor (controller::Alignment::middle_center())
195 .text (text);
196 set_option!(builder, frame_appearances, appearances);
197 set_option!(builder, frame_border, border);
198 let [frame_id, textbox_id] =
199 build_and_get_node_ids!(interface, builder, ["Frame", "Textbox"]);
200 let _ = interface.action (&frame_id, Action::Focus);
201 [frame_id, textbox_id]
202}
203
204pub fn get_contents (elements : &Tree <Element>, textbox_id : &NodeId)
206 -> String
207{
208 let Widget (_, contents, _) = Textbox::try_get (elements, textbox_id).unwrap();
209 contents.clone()
210}
211
212pub fn set_contents (
214 elements : &Tree <Element>,
215 textbox_id : &NodeId,
216 contents : String,
217 action_buffer : &mut Vec <(NodeId, Action)>
218) {
219 let new_body = {
220 let Widget (scroll, _, _) = Textbox::try_get (elements, textbox_id).unwrap();
222 let Widget (_, _, canvas) =
223 Frame::try_get (elements, elements.get_parent_id (textbox_id)).unwrap();
224 body (canvas, scroll, &contents)
225 };
226 { let update_text =
228 Box::new (|model : &mut Model| model.component = contents.into());
229 action_buffer.push ((textbox_id.clone(), Action::ModifyModel (update_text)));
230 }
231 { let update_body =
233 Box::new (|view : &mut View| view.component = new_body.into());
234 action_buffer.push ((textbox_id.clone(), Action::ModifyView (update_body)));
235 }
236}
237
238pub fn fit_to_contents (
240 elements : &Tree <Element>,
241 textbox_id : &NodeId,
242 action_buffer : &mut Vec <(NodeId, Action)>
243) {
244 use controller::component::layout;
245 let (width, height) = {
246 let contents = get_contents (elements, textbox_id);
247 let lines = contents.lines().map (str::len).collect::<Vec <_>>();
248 let height = lines.len() as u32;
249 let width = (*lines.iter().max().unwrap_or (&0)) as u32;
250 (width, height)
251 };
252 let frame_id = elements.get_parent_id (textbox_id);
253 let mut layout = {
254 let Widget (layout, _, _) = Frame::try_get (elements, frame_id).unwrap();
255 layout.clone()
256 };
257 match layout.variant {
258 layout::Variant::Free (ref mut free, _) => {
259 let old_size = free.size;
260 free.size.width = width.into();
261 free.size.height = height.into();
262 if old_size != free.size {
263 frame::set_layout (elements, frame_id, layout, None, action_buffer);
264 }
265 }
266 layout::Variant::Tiled (_) =>
267 log::warn!("fit to contents called on tiled textbox frame")
268 }
269}
270
271pub fn push_line (
274 elements : &Tree <Element>,
275 textbox_id : &NodeId,
276 line : String,
277 action_buffer : &mut Vec <(NodeId, Action)>
278) {
279 let new_body = {
280 let Widget (scroll, contents, _) = Textbox::try_get (elements, textbox_id).unwrap();
282 let mut contents = contents.clone();
283 contents.push ('\n');
284 contents.push_str (&line);
285 let Widget (_, _, canvas) =
286 Frame::try_get (elements, elements.get_parent_id (textbox_id)).unwrap();
287 body (canvas, scroll, &contents)
288 };
289 { let update_text = Box::new (move |model : &mut Model| match model.component {
291 model::Component::Text (ref mut text) => {
292 text.push ('\n');
293 text.push_str (&line);
294 }
295 _ => unreachable!()
296 });
297 action_buffer.push ((textbox_id.clone(), Action::ModifyModel (update_text)));
298 }
299 { let update_body = Box::new (|view : &mut View| view.component = new_body.into());
301 action_buffer.push ((textbox_id.clone(), Action::ModifyView (update_body)));
302 }
303}
304
305pub (crate) fn body (
310 canvas : &view::component::Canvas,
311 scroll : &controller::component::Scroll,
312 text : &str
313) -> view::component::Body {
314 use controller::{alignment, offset};
315 log::trace!("body...");
316 let (body_width, body_height) = canvas.body_wh();
317 let lines = text.lines().count() as u32;
318 let longest = text.lines().map (str::len).max().unwrap_or_default() as u32;
319 let scroll_horizontal = match scroll.offset.horizontal {
320 offset::Signed::Absolute (horizontal) => horizontal,
321 offset::Signed::Relative (horizontal) => {
322 let horizontal_max = longest.saturating_sub (body_width) as f32;
323 (*horizontal * horizontal_max).round() as i32
324 }
325 };
326 let scroll_vertical = match scroll.offset.vertical {
327 offset::Signed::Absolute (vertical) => vertical,
328 offset::Signed::Relative (vertical) => {
329 let vertical_max = lines.saturating_sub (body_height) as f32;
330 (*vertical * vertical_max).round() as i32
331 }
332 };
333 let mut body = String::new();
334 let (skip, body_top) = match scroll.alignment.vertical {
335 alignment::Vertical::Top => (scroll_vertical as usize, 0),
336 alignment::Vertical::Bottom => {
337 for _ in 0..body_height.saturating_sub (lines) {
338 body.push ('\n');
339 }
340 ( (lines.saturating_sub (body_height) as i32 - scroll_vertical).max (0)
341 as usize,
342 0
343 )
344 }
345 alignment::Vertical::Middle => {
346 let center = lines as i32 / 2;
347 let body_top = center - body_height as i32 / 2 - scroll_vertical;
348 for _ in body_top..0 {
349 body.push ('\n');
350 }
351 (body_top.max (0) as usize, body_top)
352 }
353 };
354 let take = (body_height as i32 + body_top.min (0)).max (0) as usize;
357 for line in text.lines().skip (skip).take (take) {
358 let len = line.len();
359 let (space, start, end) = match scroll.alignment.horizontal {
360 alignment::Horizontal::Left => (
361 0,
362 scroll_horizontal as usize,
363 (scroll_horizontal as usize + body_width as usize).min (len)
364 ),
365 alignment::Horizontal::Center => {
366 let center = longest as i32 / 2;
367 let body_left = center - body_width as i32 / 2 + scroll_horizontal;
368 let body_right = center + body_width as i32 / 2 + scroll_horizontal;
369 let line_start = center - len as i32 / 2;
370 let line_end = center + len as i32 / 2 - (len as i32 + 1) % 2;
371 let space = (line_start - body_left).max (0) as usize;
372 let start = (body_left - line_start).max (0) as usize;
373 let end = len - (line_end - body_right).max (0) as usize;
374 (space, start, end)
375 }
376 alignment::Horizontal::Right =>
377 ( (body_width as usize + scroll_horizontal as usize)
378 .saturating_sub (len),
379 len.saturating_sub (body_width as usize)
380 .saturating_sub (scroll_horizontal as usize),
381 len
382 )
383 };
384 if start < end {
385 for _ in 0..space {
386 body.push ('\0');
387 }
388 for ch in line.chars().skip (start).take (end - start) {
391 body.push (ch);
392 }
393 body.push ('\n');
394 } else if start == end {
395 body.push ('\n');
396 }
397 }
398 log::trace!("...body");
399 view::component::Body (body)
400}
401
402fn scroll_helper (
407 elements : &Tree <Element>,
408 textbox_id : &NodeId,
409 action_buffer : &mut Vec <(NodeId, Action)>,
410 scroll_fun : fn (&mut controller::component::Scroll, u32),
411 count : u32
412) {
413 use controller::{alignment, offset};
414 let Widget (scroll, text, _) = Textbox::try_get (elements, textbox_id)
415 .unwrap();
416 let Widget (_, _, canvas) =
417 Frame::try_get (elements, elements.get_parent_id (textbox_id)).unwrap();
418 let lines = text.lines().count() as u32;
419 let longest = text.lines().map (str::len).max().unwrap() as u32;
420 let (body_width, body_height) = canvas.body_wh();
421 let horizontal_max = longest.saturating_sub (body_width) as i32;
422 let vertical_max = lines.saturating_sub (body_height) as i32;
423 let new_scroll = {
424 let mut scroll = scroll.clone();
425 scroll_fun (&mut scroll, count);
426 match &mut scroll.offset.horizontal {
427 offset::Signed::Absolute (horizontal) => {
428 match scroll.alignment.horizontal {
429 alignment::Horizontal::Left | alignment::Horizontal::Right => {
430 *horizontal = (*horizontal).max (0);
431 *horizontal = (*horizontal).min (horizontal_max);
432 }
433 alignment::Horizontal::Center => {
434 *horizontal = (*horizontal)
435 .min (horizontal_max / 2 + (body_width+1) as i32 % 2)
436 .max (-horizontal_max / 2);
437 }
438 }
439 }
440 offset::Signed::Relative (_) => {} }
442 match &mut scroll.offset.vertical {
443 offset::Signed::Absolute (vertical) => {
444 match scroll.alignment.vertical {
445 alignment::Vertical::Top | alignment::Vertical::Bottom => {
446 *vertical = (*vertical).max (0);
447 *vertical = (*vertical).min (vertical_max);
448 }
449 alignment::Vertical::Middle => {
450 if vertical_max == 0 {
451 *vertical = 0;
452 } else {
453 *vertical = (*vertical)
454 .min (vertical_max / 2 + body_height as i32 % 2)
455 .max (-vertical_max / 2);
456 }
457 }
458 }
459 }
460 offset::Signed::Relative (_) => {} }
462 scroll
463 };
464 if &new_scroll != scroll {
465 let new_body = body (canvas, &new_scroll, text);
466 { let update_scroll = Box::new (move |controller : &mut Controller|
468 controller.component = new_scroll.into());
469 action_buffer.push (
470 (textbox_id.clone(), Action::ModifyController (update_scroll)));
471 }
472 { let update_body = Box::new (move |view : &mut View|
474 view.component = new_body.into());
475 action_buffer.push ((textbox_id.clone(), Action::ModifyView (update_body)));
476 }
477 }
478}
479
480mod builder {
485 use derive_builder::Builder;
486 use crate::prelude::*;
487 #[derive(Builder)]
496 #[builder(public, pattern="owned", build_fn(private), setter(strip_option))]
497 struct Textbox <'a, A : Application> {
498 elements : &'a Tree <Element>,
499 parent_id : &'a NodeId,
500 #[builder(default)]
501 appearances : Appearances,
502 #[builder(default)]
503 bindings : Option <&'a Bindings <A>>,
504 #[builder(default)]
505 frame_appearances : Appearances,
506 #[builder(default)]
507 frame_border : Option <Border>,
508 #[builder(default)]
509 frame_clear_color : Option <canvas::ClearColor>,
510 #[builder(default)]
511 frame_disabled : bool,
512 #[builder(default)]
513 frame_layout : Layout,
514 #[builder(default)]
515 text : String,
516 #[builder(default)]
517 scroll : Option <Scroll>
518 }
519
520 #[derive(Builder)]
521 #[builder(public, pattern="owned", build_fn(private), setter(strip_option))]
522 struct Fit <'a, A : Application> {
523 elements : &'a Tree <Element>,
524 parent_id : &'a NodeId,
525 #[builder(default)]
526 appearances : Appearances,
527 #[builder(default)]
528 bindings : Option <&'a Bindings <A>>,
529 #[builder(default)]
530 frame_anchor : Alignment,
531 #[builder(default)]
532 frame_appearances : Appearances,
533 #[builder(default)]
534 frame_area : Area,
535 #[builder(default)]
536 frame_border : Option <Border>,
537 #[builder(default)]
538 frame_clear_color : Option <canvas::ClearColor>,
539 #[builder(default)]
540 frame_disabled : bool,
541 #[builder(default)]
542 frame_offset : Offset,
543 #[builder(default)]
544 scroll : Option <Scroll>,
545 #[builder(default)]
546 text : String
547 }
548
549 #[derive(Builder)]
550 #[builder(public, pattern="owned", build_fn(private), setter(strip_option))]
551 struct WithFrame <'a, A : Application> {
552 elements : &'a Tree <Element>,
553 frame_id : &'a NodeId,
554 #[builder(default)]
555 appearances : Appearances,
556 #[builder(default)]
557 bindings : Option <&'a Bindings <A>>,
558 #[builder(default)]
559 scroll : Option <Scroll>,
560 #[builder(default)]
561 text : String
562 }
563
564 impl <'a, A : Application> TextboxBuilder <'a, A> {
565 pub const fn new (elements : &'a Tree <Element>, parent_id : &'a NodeId) -> Self {
566 TextboxBuilder {
567 elements: Some (elements),
568 parent_id: Some (parent_id),
569 appearances: None,
570 bindings: None,
571 frame_appearances: None,
572 frame_border: None,
573 frame_clear_color: None,
574 frame_disabled: None,
575 frame_layout: None,
576 scroll: None,
577 text: None
578 }
579 }
580 }
581
582 impl <A : Application> BuildActions for TextboxBuilder <'_, A> {
583 fn build_actions (self) -> Vec<(NodeId, Action)> {
584 use std::convert::TryInto;
585 use crate::tree::{InsertBehavior, Node};
586 use view::coordinates;
587 log::trace!("build actions...");
588 let Textbox {
589 elements, parent_id, appearances, bindings, frame_appearances,
590 frame_border, frame_clear_color, frame_disabled, frame_layout, text,
591 scroll
592 } = self.build()
593 .map_err(|err| log::error!("frame builder error: {err:?}"))
594 .unwrap();
595 let bindings_empty = Bindings::empty();
596 let bindings = bindings.unwrap_or (&bindings_empty);
597 { let Widget (_, _, canvas) = Frame::try_get (elements, parent_id)
599 .unwrap();
600 assert!(canvas.coordinates.kind() == coordinates::Kind::Tile);
601 }
602 let mut out = vec![];
603 let (mut subtree, order) = {
604 let mut actions = {
605 let mut frame = frame::Builder::new (elements, parent_id)
606 .appearances (frame_appearances)
607 .bindings (bindings)
608 .disabled (frame_disabled)
609 .layout (frame_layout);
610 set_option!(frame, border, frame_border);
611 set_option!(frame, clear_color, frame_clear_color);
612 frame.build_actions()
613 };
614 out.extend (actions.drain (1..));
615 debug_assert_eq!(actions.len(), 1);
616 actions.pop().unwrap().1.try_into().unwrap()
617 };
618 let frame_id = subtree.root_node_id().unwrap().clone();
619 let textbox = {
620 let Widget (_, _, canvas) = Frame::try_get (&subtree, &frame_id)
621 .unwrap();
622 let scroll = scroll.unwrap_or (Scroll::default_tile_absolute());
623 let body = super::body (canvas, &scroll, &text);
624 let controller = {
625 let mut controller = Controller::with_bindings (
626 &bindings.get_bindings (&super::CONTROLS));
627 controller.appearances = appearances;
628 controller.component = scroll.into();
629 controller
630 };
631 let model = Model { component: text.into(), .. Model::default() };
632 let view = view::Component::from (body).into();
633 Element::new ("Textbox".to_string(), controller, model, view)
634 };
635 let _ = subtree
636 .insert (Node::new (textbox), InsertBehavior::UnderNode (&frame_id))
637 .unwrap();
638 out.push ((parent_id.clone(), Action::Create (subtree, order)));
639 log::trace!("...build actions");
640 out
641 }
642 }
643
644 impl <'a, A : Application> FitBuilder <'a, A> {
645 pub const fn new (elements : &'a Tree <Element>, parent_id : &'a NodeId) -> Self {
646 FitBuilder {
647 elements: Some (elements),
648 parent_id: Some (parent_id),
649 appearances: None,
650 bindings: None,
651 frame_anchor: None,
652 frame_appearances: None,
653 frame_area: None,
654 frame_border: None,
655 frame_clear_color: None,
656 frame_disabled: None,
657 frame_offset: None,
658 text: None,
659 scroll: None
660 }
661 }
662 }
663
664 impl <A : Application> BuildActions for FitBuilder <'_, A> {
665 fn build_actions (self) -> Vec<(NodeId, Action)> {
667 use std::convert::TryInto;
668 use crate::tree::InsertBehavior;
669 log::trace!("fit build actions...");
670 let Fit {
671 elements, parent_id, appearances, bindings, frame_anchor,
672 frame_appearances, frame_area, frame_border, frame_clear_color,
673 frame_disabled, frame_offset, scroll, text
674 } = self.build()
675 .map_err (|err| log::error!("frame builder error: {err:?}"))
676 .unwrap();
677 let bindings_empty = Bindings::empty();
678 let bindings = bindings.unwrap_or (&bindings_empty);
679 { let Widget (_, _, canvas) = Frame::try_get (elements, parent_id).unwrap();
681 assert!(canvas.coordinates.kind() == coordinates::Kind::Tile);
682 }
683 let mut out = vec![];
684 let (mut subtree, order) = {
685 let size = {
686 let longest = text.lines().map (str::len).max().unwrap() as u32;
687 let lines = text.lines().count() as u32;
688 let (border_w, border_h) = frame_border.as_ref()
689 .map_or ((0, 0), Border::total_wh);
690 let width = (longest + border_w as u32).into();
691 let height = (lines + border_h as u32).into();
692 Size { width, height }
693 };
694 let layout = layout::Variant::from ((
695 layout::Free { anchor: frame_anchor, offset: frame_offset, size },
696 frame_area
697 ));
698 let mut actions = {
699 let mut frame = frame::Builder::new (elements, parent_id)
700 .appearances (frame_appearances)
701 .bindings (bindings)
702 .disabled (frame_disabled)
703 .layout (layout.into());
704 set_option!(frame, border, frame_border);
705 set_option!(frame, clear_color, frame_clear_color);
706 frame.build_actions()
707 };
708 out.extend (actions.drain (1..));
709 debug_assert_eq!(actions.len(), 1);
710 actions.pop().unwrap().1.try_into().unwrap()
711 };
712 let frame_id = subtree.root_node_id().unwrap().clone();
713 let textbox = {
714 let Widget (_, _, canvas) = Frame::try_get (&subtree, &frame_id)
715 .unwrap();
716 let scroll = scroll.unwrap_or (Scroll::default_tile_absolute());
717 let body = super::body (canvas, &scroll, &text);
718 let controller = {
719 let mut controller = Controller::with_bindings (bindings);
720 controller.appearances = appearances;
721 controller.component = scroll.into();
722 controller
723 };
724 let model = Model {
725 component: text.into(), .. Model::default()
726 };
727 let view = view::Component::from (body).into();
728 Element::new ("Textbox".to_string(), controller, model, view)
729 };
730 let _ = subtree
731 .insert (Node::new (textbox), InsertBehavior::UnderNode (&frame_id))
732 .unwrap();
733 out.push ((parent_id.clone(), Action::Create (subtree, order)));
734 log::trace!("...fit build actions");
735 out
736 }
737 }
738
739 impl <'a, A : Application> WithFrameBuilder <'a, A> {
740 pub const fn new (elements : &'a Tree <Element>, frame_id : &'a NodeId) -> Self {
741 WithFrameBuilder {
742 elements: Some (elements),
743 frame_id: Some (frame_id),
744 bindings: None,
745 appearances: None,
746 text: None,
747 scroll: None
748 }
749 }
750 }
751
752 impl <A : Application> BuildElement for WithFrameBuilder <'_, A> {
753 fn build_element (self) -> Element {
756 use view::coordinates;
757 log::trace!("with frame build element...");
758 let WithFrame {
759 elements, frame_id, bindings, appearances, text, scroll
760 } = self.build()
761 .map_err(|err| log::error!("frame builder error: {err:?}")).unwrap();
762 let bindings_empty = Bindings::empty();
763 let bindings = bindings.unwrap_or (&bindings_empty)
764 .get_bindings (&super::CONTROLS);
765 let Widget (_, _, canvas) = Frame::try_get (elements, frame_id)
766 .unwrap();
767 assert!(canvas.coordinates.kind() == coordinates::Kind::Tile);
768 let scroll = scroll.unwrap_or (Scroll::default_tile_absolute());
769 let body = super::body (canvas, &scroll, &text);
770 let controller = {
771 let mut controller = Controller::with_bindings (&bindings);
772 controller.appearances = appearances;
773 controller.component = scroll.into();
774 controller
775 };
776 let model = Model { component: text.into(), .. Model::default() };
777 let view = view::Component::from (body).into();
778 log::trace!("...with frame build element");
779 Element::new ("Textbox".to_string(), controller, model, view)
780 }
781 }
782}