1use crate::{
9 border::BorderBuilder,
10 brush::Brush,
11 core::{algebra::Vector2, color::Color, math::Rect, pool::Handle},
12 define_constructor,
13 grid::{Column, GridBuilder, Row},
14 message::{CursorIcon, MessageDirection, UiMessage},
15 widget::{Widget, WidgetBuilder, WidgetMessage},
16 window::{Window, WindowMessage},
17 BuildContext, Control, NodeHandleMapping, Thickness, UiNode, UserInterface,
18};
19use std::{
20 any::{Any, TypeId},
21 cell::{Cell, RefCell},
22 ops::{Deref, DerefMut},
23};
24
25#[derive(Debug, Clone, PartialEq)]
26pub enum TileMessage {
27 Content(TileContent),
28 Split {
30 window: Handle<UiNode>,
31 direction: SplitDirection,
32 first: bool,
33 },
34}
35
36impl TileMessage {
37 define_constructor!(TileMessage:Content => fn content(TileContent), layout: false);
38 define_constructor!(TileMessage:Split => fn split(window: Handle<UiNode>,
39 direction: SplitDirection,
40 first: bool), layout: false);
41}
42
43#[derive(Debug, PartialEq, Clone)]
44pub enum TileContent {
45 Empty,
46 Window(Handle<UiNode>),
47 VerticalTiles {
48 splitter: f32,
49 tiles: [Handle<UiNode>; 2],
53 },
54 HorizontalTiles {
55 splitter: f32,
56 tiles: [Handle<UiNode>; 2],
60 },
61}
62
63impl TileContent {
64 pub fn is_empty(&self) -> bool {
65 matches!(self, TileContent::Empty)
66 }
67}
68
69#[derive(Clone)]
70pub struct Tile {
71 widget: Widget,
72 left_anchor: Handle<UiNode>,
73 right_anchor: Handle<UiNode>,
74 top_anchor: Handle<UiNode>,
75 bottom_anchor: Handle<UiNode>,
76 center_anchor: Handle<UiNode>,
77 content: TileContent,
78 splitter: Handle<UiNode>,
79 dragging_splitter: bool,
80 drop_anchor: Cell<Handle<UiNode>>,
81}
82
83crate::define_widget_deref!(Tile);
84
85impl Control for Tile {
86 fn query_component(&self, type_id: TypeId) -> Option<&dyn Any> {
87 if type_id == TypeId::of::<Self>() {
88 Some(self)
89 } else {
90 None
91 }
92 }
93
94 fn resolve(&mut self, node_map: &NodeHandleMapping) {
95 node_map.resolve_cell(&mut self.drop_anchor);
96 node_map.resolve(&mut self.splitter);
97 node_map.resolve(&mut self.center_anchor);
98 node_map.resolve(&mut self.bottom_anchor);
99 node_map.resolve(&mut self.top_anchor);
100 node_map.resolve(&mut self.right_anchor);
101 node_map.resolve(&mut self.left_anchor);
102 match &mut self.content {
103 TileContent::Empty => {}
104 TileContent::Window(window) => node_map.resolve(window),
105 TileContent::VerticalTiles { tiles, .. }
106 | TileContent::HorizontalTiles { tiles, .. } => {
107 for tile in tiles {
108 node_map.resolve(tile);
109 }
110 }
111 }
112 }
113
114 fn measure_override(&self, ui: &UserInterface, available_size: Vector2<f32>) -> Vector2<f32> {
115 for &child_handle in self.children() {
116 let available_size = match self.content {
121 TileContent::VerticalTiles {
122 splitter,
123 ref tiles,
124 } => {
125 if tiles[0] == child_handle {
126 Vector2::new(available_size.x, available_size.y * splitter)
127 } else if tiles[1] == child_handle {
128 Vector2::new(available_size.x, available_size.y * (1.0 - splitter))
129 } else {
130 available_size
131 }
132 }
133 TileContent::HorizontalTiles {
134 splitter,
135 ref tiles,
136 } => {
137 if tiles[0] == child_handle {
138 Vector2::new(available_size.x * splitter, available_size.y)
139 } else if tiles[1] == child_handle {
140 Vector2::new(available_size.x * (1.0 - splitter), available_size.y)
141 } else {
142 available_size
143 }
144 }
145 _ => available_size,
146 };
147
148 ui.measure_node(child_handle, available_size);
149 }
150
151 available_size
152 }
153
154 fn arrange_override(&self, ui: &UserInterface, final_size: Vector2<f32>) -> Vector2<f32> {
155 let splitter_size = ui.node(self.splitter).desired_size();
156
157 for &child_handle in self.children() {
158 let full_bounds = Rect::new(0.0, 0.0, final_size.x, final_size.y);
159
160 let bounds = match self.content {
161 TileContent::VerticalTiles {
162 splitter,
163 ref tiles,
164 } => {
165 if tiles[0] == child_handle {
166 Rect::new(
167 0.0,
168 0.0,
169 final_size.x,
170 final_size.y * splitter - splitter_size.y * 0.5,
171 )
172 } else if tiles[1] == child_handle {
173 Rect::new(
174 0.0,
175 final_size.y * splitter + splitter_size.y * 0.5,
176 final_size.x,
177 final_size.y * (1.0 - splitter) - splitter_size.y,
178 )
179 } else if self.splitter == child_handle {
180 Rect::new(
181 0.0,
182 final_size.y * splitter - splitter_size.y * 0.5,
183 final_size.x,
184 splitter_size.y,
185 )
186 } else {
187 full_bounds
188 }
189 }
190 TileContent::HorizontalTiles {
191 splitter,
192 ref tiles,
193 } => {
194 if tiles[0] == child_handle {
195 Rect::new(
196 0.0,
197 0.0,
198 final_size.x * splitter - splitter_size.x * 0.5,
199 final_size.y,
200 )
201 } else if tiles[1] == child_handle {
202 Rect::new(
203 final_size.x * splitter + splitter_size.x * 0.5,
204 0.0,
205 final_size.x * (1.0 - splitter) - splitter_size.x * 0.5,
206 final_size.y,
207 )
208 } else if self.splitter == child_handle {
209 Rect::new(
210 final_size.x * splitter - splitter_size.x * 0.5,
211 0.0,
212 splitter_size.x,
213 final_size.y,
214 )
215 } else {
216 full_bounds
217 }
218 }
219 _ => full_bounds,
220 };
221
222 ui.arrange_node(child_handle, &bounds);
223
224 if child_handle != self.splitter {
228 ui.send_message(WidgetMessage::width(
229 child_handle,
230 MessageDirection::ToWidget,
231 bounds.w(),
232 ));
233 ui.send_message(WidgetMessage::height(
234 child_handle,
235 MessageDirection::ToWidget,
236 bounds.h(),
237 ));
238 }
239 }
240
241 final_size
242 }
243
244 fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
245 self.widget.handle_routed_message(ui, message);
246
247 if let Some(msg) = message.data::<TileMessage>() {
248 if message.destination() == self.handle() {
249 match msg {
250 TileMessage::Content(content) => {
251 self.content = content.clone();
252
253 match content {
254 TileContent::Empty => {
255 ui.send_message(WidgetMessage::visibility(
256 self.splitter,
257 MessageDirection::ToWidget,
258 false,
259 ));
260 }
261 &TileContent::Window(window) => {
262 ui.send_message(WidgetMessage::link(
263 window,
264 MessageDirection::ToWidget,
265 self.handle(),
266 ));
267
268 ui.send_message(WidgetMessage::visibility(
269 self.splitter,
270 MessageDirection::ToWidget,
271 false,
272 ));
273
274 ui.send_message(WindowMessage::can_resize(
275 window,
276 MessageDirection::ToWidget,
277 false,
278 ));
279 }
280 TileContent::VerticalTiles { tiles, .. }
281 | TileContent::HorizontalTiles { tiles, .. } => {
282 for &tile in tiles {
283 ui.send_message(WidgetMessage::link(
284 tile,
285 MessageDirection::ToWidget,
286 self.handle(),
287 ));
288 }
289
290 ui.send_message(WidgetMessage::visibility(
291 self.splitter,
292 MessageDirection::ToWidget,
293 true,
294 ));
295
296 match content {
297 TileContent::HorizontalTiles { .. } => {
298 ui.send_message(WidgetMessage::width(
299 self.splitter,
300 MessageDirection::ToWidget,
301 DEFAULT_SPLITTER_SIZE,
302 ));
303 ui.send_message(WidgetMessage::height(
304 self.splitter,
305 MessageDirection::ToWidget,
306 f32::INFINITY,
307 ));
308 }
309 TileContent::VerticalTiles { .. } => {
310 ui.send_message(WidgetMessage::width(
311 self.splitter,
312 MessageDirection::ToWidget,
313 f32::INFINITY,
314 ));
315 ui.send_message(WidgetMessage::height(
316 self.splitter,
317 MessageDirection::ToWidget,
318 DEFAULT_SPLITTER_SIZE,
319 ));
320 }
321 _ => (),
322 }
323 }
324 }
325 }
326 &TileMessage::Split {
327 window,
328 direction,
329 first,
330 } => {
331 if matches!(self.content, TileContent::Window(_)) {
332 self.split(ui, window, direction, first);
333 }
334 }
335 }
336 }
337 } else if let Some(msg) = message.data::<WidgetMessage>() {
338 match msg {
339 &WidgetMessage::MouseDown { .. } => {
340 if !message.handled() && message.destination() == self.splitter {
341 message.set_handled(true);
342 self.dragging_splitter = true;
343 ui.capture_mouse(self.splitter);
344 }
345 }
346 &WidgetMessage::MouseUp { .. } => {
347 if !message.handled() && message.destination() == self.splitter {
348 message.set_handled(true);
349 self.dragging_splitter = false;
350 ui.release_mouse_capture();
351 }
352 }
353 &WidgetMessage::MouseMove { pos, .. } => {
354 if self.dragging_splitter {
355 let bounds = self.screen_bounds();
356 match self.content {
357 TileContent::VerticalTiles {
358 ref mut splitter, ..
359 } => {
360 *splitter = ((pos.y - bounds.y()) / bounds.h()).max(0.0).min(1.0);
361 self.invalidate_layout();
362 }
363 TileContent::HorizontalTiles {
364 ref mut splitter, ..
365 } => {
366 *splitter = ((pos.x - bounds.x()) / bounds.w()).max(0.0).min(1.0);
367 self.invalidate_layout();
368 }
369 _ => (),
370 }
371 }
372 }
373 WidgetMessage::Unlink => {
374 match self.content {
376 TileContent::VerticalTiles { tiles, .. }
377 | TileContent::HorizontalTiles { tiles, .. } => {
378 let mut has_empty_sub_tile = false;
379 for &tile in &tiles {
380 if let Some(sub_tile) = ui.node(tile).cast::<Tile>() {
381 if let TileContent::Empty = sub_tile.content {
382 has_empty_sub_tile = true;
383 break;
384 }
385 }
386 }
387 if has_empty_sub_tile {
388 for &tile in &tiles {
389 if let Some(sub_tile) = ui.node(tile).cast::<Tile>() {
390 match sub_tile.content {
391 TileContent::Window(sub_tile_wnd) => {
392 ui.send_message(WidgetMessage::unlink(
395 sub_tile_wnd,
396 MessageDirection::ToWidget,
397 ));
398
399 ui.send_message(TileMessage::content(
400 self.handle,
401 MessageDirection::ToWidget,
402 TileContent::Window(sub_tile_wnd),
403 ));
404 ui.send_message(WidgetMessage::visibility(
406 self.splitter,
407 MessageDirection::ToWidget,
408 false,
409 ));
410 }
411 TileContent::VerticalTiles {
414 splitter,
415 tiles: sub_tiles,
416 } => {
417 for &sub_tile in &sub_tiles {
418 ui.send_message(WidgetMessage::unlink(
419 sub_tile,
420 MessageDirection::ToWidget,
421 ));
422 }
423 ui.send_message(TileMessage::content(
425 self.handle,
426 MessageDirection::ToWidget,
427 TileContent::VerticalTiles {
428 splitter,
429 tiles: sub_tiles,
430 },
431 ));
432 }
433 TileContent::HorizontalTiles {
434 splitter,
435 tiles: sub_tiles,
436 } => {
437 for &sub_tile in &sub_tiles {
438 ui.send_message(WidgetMessage::unlink(
439 sub_tile,
440 MessageDirection::ToWidget,
441 ));
442 }
443 ui.send_message(TileMessage::content(
445 self.handle,
446 MessageDirection::ToWidget,
447 TileContent::HorizontalTiles {
448 splitter,
449 tiles: sub_tiles,
450 },
451 ));
452 }
453 _ => {}
454 }
455 }
456 }
457
458 for &tile in &tiles {
460 ui.send_message(WidgetMessage::remove(
461 tile,
462 MessageDirection::ToWidget,
463 ));
464 }
465 }
466 }
467 _ => (),
468 }
469 }
470 _ => {}
471 }
472 } else if let Some(WindowMessage::Move(_)) = message.data::<WindowMessage>() {
474 let content_moved = match self.content {
476 TileContent::Window(window) => window == message.destination(),
477 _ => false,
478 };
479
480 if content_moved {
481 if let Some(window) = ui.node(message.destination()).cast::<Window>() {
482 if window.drag_delta().norm() > 20.0 {
483 ui.send_message(TileMessage::content(
484 self.handle,
485 MessageDirection::ToWidget,
486 TileContent::Empty,
487 ));
488
489 ui.send_message(WidgetMessage::unlink(
490 message.destination(),
491 MessageDirection::ToWidget,
492 ));
493
494 ui.send_message(WindowMessage::can_resize(
495 message.destination(),
496 MessageDirection::ToWidget,
497 true,
498 ));
499
500 if let Some((_, docking_manager)) =
501 ui.try_borrow_by_type_up::<DockingManager>(self.parent())
502 {
503 docking_manager
504 .floating_windows
505 .borrow_mut()
506 .push(message.destination());
507 }
508 }
509 }
510 }
511 }
512 }
513
514 fn preview_message(&self, ui: &UserInterface, message: &mut UiMessage) {
517 if let Some(WidgetMessage::Unlink) = message.data::<WidgetMessage>() {
518 if let TileContent::Empty | TileContent::Window(_) = self.content {
519 for &anchor in &self.anchors() {
521 ui.send_message(WidgetMessage::visibility(
522 anchor,
523 MessageDirection::ToWidget,
524 true,
525 ));
526 }
527 }
528 } else if let Some(msg) = message.data::<WindowMessage>() {
529 if let Some((_, docking_manager)) =
530 ui.try_borrow_by_type_up::<DockingManager>(self.parent())
531 {
532 if docking_manager
534 .floating_windows
535 .borrow_mut()
536 .contains(&message.destination())
537 {
538 match msg {
539 &WindowMessage::Move(_) => {
540 if let TileContent::Empty | TileContent::Window(_) = self.content {
542 let pos = ui.cursor_position;
544 for &anchor in &self.anchors() {
545 ui.send_message(WidgetMessage::background(
546 anchor,
547 MessageDirection::ToWidget,
548 Brush::Solid(DEFAULT_ANCHOR_COLOR),
549 ))
550 }
551 if ui.node(self.left_anchor).screen_bounds().contains(pos) {
552 ui.send_message(WidgetMessage::background(
553 self.left_anchor,
554 MessageDirection::ToWidget,
555 Brush::Solid(Color::WHITE),
556 ));
557 self.drop_anchor.set(self.left_anchor);
558 } else if ui.node(self.right_anchor).screen_bounds().contains(pos) {
559 ui.send_message(WidgetMessage::background(
560 self.right_anchor,
561 MessageDirection::ToWidget,
562 Brush::Solid(Color::WHITE),
563 ));
564 self.drop_anchor.set(self.right_anchor);
565 } else if ui.node(self.top_anchor).screen_bounds().contains(pos) {
566 ui.send_message(WidgetMessage::background(
567 self.top_anchor,
568 MessageDirection::ToWidget,
569 Brush::Solid(Color::WHITE),
570 ));
571 self.drop_anchor.set(self.top_anchor);
572 } else if ui.node(self.bottom_anchor).screen_bounds().contains(pos)
573 {
574 ui.send_message(WidgetMessage::background(
575 self.bottom_anchor,
576 MessageDirection::ToWidget,
577 Brush::Solid(Color::WHITE),
578 ));
579 self.drop_anchor.set(self.bottom_anchor);
580 } else if ui.node(self.center_anchor).screen_bounds().contains(pos)
581 {
582 ui.send_message(WidgetMessage::background(
583 self.center_anchor,
584 MessageDirection::ToWidget,
585 Brush::Solid(Color::WHITE),
586 ));
587 self.drop_anchor.set(self.center_anchor);
588 } else {
589 self.drop_anchor.set(Handle::NONE);
590 }
591 }
592 }
593 WindowMessage::MoveStart => {
594 if let TileContent::Empty | TileContent::Window(_) = self.content {
595 for &anchor in &self.anchors() {
597 ui.send_message(WidgetMessage::visibility(
598 anchor,
599 MessageDirection::ToWidget,
600 true,
601 ));
602 }
603 }
604 }
605 WindowMessage::MoveEnd => {
606 for &anchor in &self.anchors() {
608 ui.send_message(WidgetMessage::visibility(
609 anchor,
610 MessageDirection::ToWidget,
611 false,
612 ));
613 }
614
615 if self.drop_anchor.get().is_some() {
617 match self.content {
618 TileContent::Empty => {
619 if self.drop_anchor.get() == self.center_anchor {
620 ui.send_message(TileMessage::content(
621 self.handle,
622 MessageDirection::ToWidget,
623 TileContent::Window(message.destination()),
624 ));
625 ui.send_message(WidgetMessage::link(
626 message.destination(),
627 MessageDirection::ToWidget,
628 self.handle,
629 ));
630 }
631 }
632 TileContent::Window(_) => {
633 if self.drop_anchor.get() == self.left_anchor {
634 ui.send_message(TileMessage::split(
636 self.handle,
637 MessageDirection::ToWidget,
638 message.destination(),
639 SplitDirection::Horizontal,
640 true,
641 ));
642 } else if self.drop_anchor.get() == self.right_anchor {
643 ui.send_message(TileMessage::split(
645 self.handle,
646 MessageDirection::ToWidget,
647 message.destination(),
648 SplitDirection::Horizontal,
649 false,
650 ));
651 } else if self.drop_anchor.get() == self.top_anchor {
652 ui.send_message(TileMessage::split(
654 self.handle,
655 MessageDirection::ToWidget,
656 message.destination(),
657 SplitDirection::Vertical,
658 true,
659 ));
660 } else if self.drop_anchor.get() == self.bottom_anchor {
661 ui.send_message(TileMessage::split(
663 self.handle,
664 MessageDirection::ToWidget,
665 message.destination(),
666 SplitDirection::Vertical,
667 false,
668 ));
669 }
670 }
671 _ => (),
673 }
674 }
675 }
676 _ => (),
677 }
678 }
679 }
680 }
681 }
682}
683
684#[derive(Debug, Clone, Copy, Eq, PartialEq)]
685pub enum SplitDirection {
686 Horizontal,
687 Vertical,
688}
689
690impl Tile {
691 pub fn anchors(&self) -> [Handle<UiNode>; 5] {
692 [
693 self.left_anchor,
694 self.right_anchor,
695 self.top_anchor,
696 self.bottom_anchor,
697 self.center_anchor,
698 ]
699 }
700
701 fn split(
702 &mut self,
703 ui: &mut UserInterface,
704 window: Handle<UiNode>,
705 direction: SplitDirection,
706 first: bool,
707 ) {
708 let existing_content = match self.content {
709 TileContent::Window(existing_window) => existing_window,
710 _ => Handle::NONE,
711 };
712
713 let first_tile = TileBuilder::new(WidgetBuilder::new())
714 .with_content({
715 if first {
716 TileContent::Window(window)
717 } else {
718 TileContent::Empty
719 }
720 })
721 .build(&mut ui.build_ctx());
722
723 let second_tile = TileBuilder::new(WidgetBuilder::new())
724 .with_content({
725 if first {
726 TileContent::Empty
727 } else {
728 TileContent::Window(window)
729 }
730 })
731 .build(&mut ui.build_ctx());
732
733 if existing_content.is_some() {
734 ui.send_message(TileMessage::content(
735 if first { second_tile } else { first_tile },
736 MessageDirection::ToWidget,
737 TileContent::Window(existing_content),
738 ));
739 }
740
741 ui.send_message(TileMessage::content(
742 self.handle,
743 MessageDirection::ToWidget,
744 match direction {
745 SplitDirection::Horizontal => TileContent::HorizontalTiles {
746 tiles: [first_tile, second_tile],
747 splitter: 0.5,
748 },
749 SplitDirection::Vertical => TileContent::VerticalTiles {
750 tiles: [first_tile, second_tile],
751 splitter: 0.5,
752 },
753 },
754 ));
755 }
756}
757
758#[derive(Clone)]
759pub struct DockingManager {
760 widget: Widget,
761 floating_windows: RefCell<Vec<Handle<UiNode>>>,
762}
763
764crate::define_widget_deref!(DockingManager);
765
766impl Control for DockingManager {
767 fn query_component(&self, type_id: TypeId) -> Option<&dyn Any> {
768 if type_id == TypeId::of::<Self>() {
769 Some(self)
770 } else {
771 None
772 }
773 }
774
775 fn resolve(&mut self, node_map: &NodeHandleMapping) {
776 node_map.resolve_slice(&mut self.floating_windows.borrow_mut());
777 }
778
779 fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
780 self.widget.handle_routed_message(ui, message);
781 }
782
783 fn preview_message(&self, _ui: &UserInterface, message: &mut UiMessage) {
784 if let Some(WidgetMessage::LinkWith(_)) = message.data::<WidgetMessage>() {
785 let pos = self
786 .floating_windows
787 .borrow()
788 .iter()
789 .position(|&i| i == message.destination());
790 if let Some(pos) = pos {
791 self.floating_windows.borrow_mut().remove(pos);
792 }
793 }
794 }
795}
796
797pub struct DockingManagerBuilder {
798 widget_builder: WidgetBuilder,
799 floating_windows: Vec<Handle<UiNode>>,
800}
801
802impl DockingManagerBuilder {
803 pub fn new(widget_builder: WidgetBuilder) -> Self {
804 Self {
805 widget_builder,
806 floating_windows: Default::default(),
807 }
808 }
809
810 pub fn with_floating_windows(mut self, windows: Vec<Handle<UiNode>>) -> Self {
811 self.floating_windows = windows;
812 self
813 }
814
815 pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
816 let docking_manager = DockingManager {
817 widget: self.widget_builder.with_preview_messages(true).build(),
818 floating_windows: RefCell::new(self.floating_windows),
819 };
820
821 ctx.add_node(UiNode::new(docking_manager))
822 }
823}
824
825pub struct TileBuilder {
826 widget_builder: WidgetBuilder,
827 content: TileContent,
828}
829
830pub const DEFAULT_SPLITTER_SIZE: f32 = 4.0;
831pub const DEFAULT_ANCHOR_COLOR: Color = Color::opaque(150, 150, 150);
832
833pub fn make_default_anchor(ctx: &mut BuildContext, row: usize, column: usize) -> Handle<UiNode> {
834 let default_anchor_size = 30.0;
835 BorderBuilder::new(
836 WidgetBuilder::new()
837 .with_width(default_anchor_size)
838 .with_height(default_anchor_size)
839 .with_visibility(false)
840 .on_row(row)
841 .on_column(column)
842 .with_draw_on_top(true)
843 .with_background(Brush::Solid(DEFAULT_ANCHOR_COLOR)),
844 )
845 .build(ctx)
846}
847
848impl TileBuilder {
849 pub fn new(widget_builder: WidgetBuilder) -> Self {
850 Self {
851 widget_builder,
852 content: TileContent::Empty,
853 }
854 }
855
856 pub fn with_content(mut self, content: TileContent) -> Self {
857 self.content = content;
858 self
859 }
860
861 pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
862 let left_anchor = make_default_anchor(ctx, 2, 1);
863 let right_anchor = make_default_anchor(ctx, 2, 3);
864 let dock_anchor = make_default_anchor(ctx, 2, 2);
865 let top_anchor = make_default_anchor(ctx, 1, 2);
866 let bottom_anchor = make_default_anchor(ctx, 3, 2);
867
868 let grid = GridBuilder::new(
869 WidgetBuilder::new()
870 .with_child(left_anchor)
871 .with_child(dock_anchor)
872 .with_child(right_anchor)
873 .with_child(top_anchor)
874 .with_child(bottom_anchor),
875 )
876 .add_row(Row::stretch())
877 .add_row(Row::auto())
878 .add_row(Row::auto())
879 .add_row(Row::auto())
880 .add_row(Row::stretch())
881 .add_column(Column::stretch())
882 .add_column(Column::auto())
883 .add_column(Column::auto())
884 .add_column(Column::auto())
885 .add_column(Column::stretch())
886 .build(ctx);
887
888 let splitter = BorderBuilder::new(
889 WidgetBuilder::new()
890 .with_width({
891 if let TileContent::HorizontalTiles { .. } = self.content {
892 DEFAULT_SPLITTER_SIZE
893 } else {
894 f32::INFINITY
895 }
896 })
897 .with_height({
898 if let TileContent::VerticalTiles { .. } = self.content {
899 DEFAULT_SPLITTER_SIZE
900 } else {
901 f32::INFINITY
902 }
903 })
904 .with_visibility(matches!(
905 self.content,
906 TileContent::VerticalTiles { .. } | TileContent::HorizontalTiles { .. }
907 ))
908 .with_cursor(match self.content {
909 TileContent::HorizontalTiles { .. } => Some(CursorIcon::WResize),
910 TileContent::VerticalTiles { .. } => Some(CursorIcon::NResize),
911 _ => None,
912 })
913 .with_margin(Thickness::uniform(1.0)),
914 )
915 .build(ctx);
916
917 if let TileContent::Window(window) = self.content {
918 if let Some(window) = ctx[window].cast_mut::<Window>() {
919 window.set_can_resize(false);
922 }
923 }
924
925 let children = match self.content {
926 TileContent::Window(window) => vec![window],
927 TileContent::VerticalTiles { tiles, .. } => vec![tiles[0], tiles[1]],
928 TileContent::HorizontalTiles { tiles, .. } => vec![tiles[0], tiles[1]],
929 _ => vec![],
930 };
931
932 let tile = Tile {
933 widget: self
934 .widget_builder
935 .with_preview_messages(true)
936 .with_child(grid)
937 .with_child(splitter)
938 .with_children(children)
939 .build(),
940 left_anchor,
941 right_anchor,
942 top_anchor,
943 bottom_anchor,
944 center_anchor: dock_anchor,
945 content: self.content,
946 splitter,
947 dragging_splitter: false,
948 drop_anchor: Default::default(),
949 };
950
951 ctx.add_node(UiNode::new(tile))
952 }
953}