1use crate::{
2 border::BorderBuilder,
3 brush::{Brush, GradientPoint},
4 button::{ButtonBuilder, ButtonMessage},
5 core::{algebra::Vector2, color::Color, math::Rect, pool::Handle},
6 decorator::DecoratorBuilder,
7 define_constructor,
8 grid::{Column, GridBuilder, Row},
9 message::{CursorIcon, MessageDirection, UiMessage},
10 text::{Text, TextBuilder, TextMessage},
11 vector_image::{Primitive, VectorImageBuilder},
12 widget::{Widget, WidgetBuilder, WidgetMessage},
13 BuildContext, Control, HorizontalAlignment, NodeHandleMapping, RestrictionEntry, Thickness,
14 UiNode, UserInterface, VerticalAlignment, BRUSH_BRIGHT, BRUSH_LIGHT, BRUSH_LIGHTER,
15 BRUSH_LIGHTEST, COLOR_DARK, COLOR_DARKEST,
16};
17use std::{
18 any::{Any, TypeId},
19 cell::RefCell,
20 ops::{Deref, DerefMut},
21};
22
23#[derive(Debug, Clone, PartialEq)]
24pub enum WindowMessage {
25 Open { center: bool },
27
28 OpenModal { center: bool },
32
33 Close,
35
36 Minimize(bool),
39
40 CanMinimize(bool),
42
43 CanClose(bool),
45
46 CanResize(bool),
48
49 MoveStart,
51
52 Move(Vector2<f32>),
54
55 MoveEnd,
57
58 Title(WindowTitle),
60}
61
62impl WindowMessage {
63 define_constructor!(WindowMessage:Open => fn open(center: bool), layout: false);
64 define_constructor!(WindowMessage:OpenModal => fn open_modal(center: bool), layout: false);
65 define_constructor!(WindowMessage:Close => fn close(), layout: false);
66 define_constructor!(WindowMessage:Minimize => fn minimize(bool), layout: false);
67 define_constructor!(WindowMessage:CanMinimize => fn can_minimize(bool), layout: false);
68 define_constructor!(WindowMessage:CanClose => fn can_close(bool), layout: false);
69 define_constructor!(WindowMessage:CanResize => fn can_resize(bool), layout: false);
70 define_constructor!(WindowMessage:MoveStart => fn move_start(), layout: false);
71 define_constructor!(WindowMessage:Move => fn move_to(Vector2<f32>), layout: false);
72 define_constructor!(WindowMessage:MoveEnd => fn move_end(), layout: false);
73 define_constructor!(WindowMessage:Title => fn title(WindowTitle), layout: false);
74}
75
76#[derive(Clone)]
80pub struct Window {
81 widget: Widget,
82 mouse_click_pos: Vector2<f32>,
83 initial_position: Vector2<f32>,
84 initial_size: Vector2<f32>,
85 is_dragging: bool,
86 minimized: bool,
87 can_minimize: bool,
88 can_close: bool,
89 can_resize: bool,
90 header: Handle<UiNode>,
91 minimize_button: Handle<UiNode>,
92 close_button: Handle<UiNode>,
93 drag_delta: Vector2<f32>,
94 content: Handle<UiNode>,
95 grips: RefCell<[Grip; 8]>,
96 title: Handle<UiNode>,
97 title_grid: Handle<UiNode>,
98}
99
100const GRIP_SIZE: f32 = 6.0;
101const CORNER_GRIP_SIZE: f32 = GRIP_SIZE * 2.0;
102
103#[derive(Copy, Clone, Debug)]
104enum GripKind {
105 LeftTopCorner = 0,
106 RightTopCorner = 1,
107 RightBottomCorner = 2,
108 LeftBottomCorner = 3,
109 Left = 4,
110 Top = 5,
111 Right = 6,
112 Bottom = 7,
113}
114
115#[derive(Clone)]
116struct Grip {
117 kind: GripKind,
118 bounds: Rect<f32>,
119 is_dragging: bool,
120 cursor: CursorIcon,
121}
122
123impl Grip {
124 fn new(kind: GripKind, cursor: CursorIcon) -> Self {
125 Self {
126 kind,
127 bounds: Default::default(),
128 is_dragging: false,
129 cursor,
130 }
131 }
132}
133
134crate::define_widget_deref!(Window);
135
136impl Control for Window {
137 fn query_component(&self, type_id: TypeId) -> Option<&dyn Any> {
138 if type_id == TypeId::of::<Self>() {
139 Some(self)
140 } else {
141 None
142 }
143 }
144
145 fn resolve(&mut self, node_map: &NodeHandleMapping) {
146 node_map.resolve(&mut self.header);
147 node_map.resolve(&mut self.minimize_button);
148 node_map.resolve(&mut self.close_button);
149 node_map.resolve(&mut self.title);
150 node_map.resolve(&mut self.title_grid);
151 node_map.resolve(&mut self.content);
152 }
153
154 fn arrange_override(&self, ui: &UserInterface, final_size: Vector2<f32>) -> Vector2<f32> {
155 let size = self.widget.arrange_override(ui, final_size);
156
157 let mut grips = self.grips.borrow_mut();
158
159 grips[GripKind::Left as usize].bounds =
161 Rect::new(0.0, GRIP_SIZE, GRIP_SIZE, final_size.y - GRIP_SIZE * 2.0);
162 grips[GripKind::Top as usize].bounds =
163 Rect::new(GRIP_SIZE, 0.0, final_size.x - GRIP_SIZE * 2.0, GRIP_SIZE);
164 grips[GripKind::Right as usize].bounds = Rect::new(
165 final_size.x - GRIP_SIZE,
166 GRIP_SIZE,
167 GRIP_SIZE,
168 final_size.y - GRIP_SIZE * 2.0,
169 );
170 grips[GripKind::Bottom as usize].bounds = Rect::new(
171 GRIP_SIZE,
172 final_size.y - GRIP_SIZE,
173 final_size.x - GRIP_SIZE * 2.0,
174 GRIP_SIZE,
175 );
176
177 grips[GripKind::LeftTopCorner as usize].bounds =
179 Rect::new(0.0, 0.0, CORNER_GRIP_SIZE, CORNER_GRIP_SIZE);
180 grips[GripKind::RightTopCorner as usize].bounds = Rect::new(
181 final_size.x - GRIP_SIZE,
182 0.0,
183 CORNER_GRIP_SIZE,
184 CORNER_GRIP_SIZE,
185 );
186 grips[GripKind::RightBottomCorner as usize].bounds = Rect::new(
187 final_size.x - CORNER_GRIP_SIZE,
188 final_size.y - CORNER_GRIP_SIZE,
189 CORNER_GRIP_SIZE,
190 CORNER_GRIP_SIZE,
191 );
192 grips[GripKind::LeftBottomCorner as usize].bounds = Rect::new(
193 0.0,
194 final_size.y - CORNER_GRIP_SIZE,
195 CORNER_GRIP_SIZE,
196 CORNER_GRIP_SIZE,
197 );
198
199 size
200 }
201
202 fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
203 self.widget.handle_routed_message(ui, message);
204
205 if let Some(msg) = message.data::<WidgetMessage>() {
206 if self.can_resize {
208 match msg {
209 &WidgetMessage::MouseDown { pos, .. } => {
210 ui.send_message(WidgetMessage::topmost(
211 self.handle(),
212 MessageDirection::ToWidget,
213 ));
214
215 for grip in self.grips.borrow_mut().iter_mut() {
217 let offset = self.screen_position;
218 let screen_bounds = grip.bounds.translate(offset);
219 if screen_bounds.contains(pos) {
220 grip.is_dragging = true;
221 self.initial_position = self.actual_local_position();
222 self.initial_size = self.actual_size();
223 self.mouse_click_pos = pos;
224 ui.capture_mouse(self.handle());
225 break;
226 }
227 }
228 }
229 WidgetMessage::MouseUp { .. } => {
230 for grip in self.grips.borrow_mut().iter_mut() {
231 if grip.is_dragging {
232 ui.release_mouse_capture();
233 grip.is_dragging = false;
234 break;
235 }
236 }
237 }
238 &WidgetMessage::MouseMove { pos, .. } => {
239 let mut new_cursor = None;
240
241 for grip in self.grips.borrow().iter() {
242 let offset = self.screen_position;
243 let screen_bounds = grip.bounds.translate(offset);
244 if screen_bounds.contains(pos) {
245 new_cursor = Some(grip.cursor);
246 }
247
248 if grip.is_dragging {
249 let delta = self.mouse_click_pos - pos;
250 let (dx, dy, dw, dh) = match grip.kind {
251 GripKind::Left => (-1.0, 0.0, 1.0, 0.0),
252 GripKind::Top => (0.0, -1.0, 0.0, 1.0),
253 GripKind::Right => (0.0, 0.0, -1.0, 0.0),
254 GripKind::Bottom => (0.0, 0.0, 0.0, -1.0),
255 GripKind::LeftTopCorner => (-1.0, -1.0, 1.0, 1.0),
256 GripKind::RightTopCorner => (0.0, -1.0, -1.0, 1.0),
257 GripKind::RightBottomCorner => (0.0, 0.0, -1.0, -1.0),
258 GripKind::LeftBottomCorner => (-1.0, 0.0, 1.0, -1.0),
259 };
260
261 let new_pos = self.initial_position
262 + Vector2::new(delta.x * dx, delta.y * dy);
263 let new_size =
264 self.initial_size + Vector2::new(delta.x * dw, delta.y * dh);
265
266 if new_size.x > self.min_width()
267 && new_size.x < self.max_width()
268 && new_size.y > self.min_height()
269 && new_size.y < self.max_height()
270 {
271 ui.send_message(WidgetMessage::desired_position(
272 self.handle(),
273 MessageDirection::ToWidget,
274 new_pos,
275 ));
276 ui.send_message(WidgetMessage::width(
277 self.handle(),
278 MessageDirection::ToWidget,
279 new_size.x,
280 ));
281 ui.send_message(WidgetMessage::height(
282 self.handle(),
283 MessageDirection::ToWidget,
284 new_size.y,
285 ));
286 }
287
288 break;
289 }
290 }
291
292 self.set_cursor(new_cursor);
293 }
294 _ => {}
295 }
296 }
297
298 if (message.destination() == self.header
299 || ui
300 .node(self.header)
301 .has_descendant(message.destination(), ui))
302 && !message.handled()
303 && !self.has_active_grip()
304 {
305 match msg {
306 WidgetMessage::MouseDown { pos, .. } => {
307 self.mouse_click_pos = *pos;
308 ui.send_message(WindowMessage::move_start(
309 self.handle,
310 MessageDirection::ToWidget,
311 ));
312 message.set_handled(true);
313 }
314 WidgetMessage::MouseUp { .. } => {
315 ui.send_message(WindowMessage::move_end(
316 self.handle,
317 MessageDirection::ToWidget,
318 ));
319 message.set_handled(true);
320 }
321 WidgetMessage::MouseMove { pos, .. } => {
322 if self.is_dragging {
323 self.drag_delta = *pos - self.mouse_click_pos;
324 let new_pos = self.initial_position + self.drag_delta;
325 ui.send_message(WindowMessage::move_to(
326 self.handle(),
327 MessageDirection::ToWidget,
328 new_pos,
329 ));
330 }
331 message.set_handled(true);
332 }
333 _ => (),
334 }
335 }
336 if let WidgetMessage::Unlink = msg {
337 if message.destination() == self.handle() {
338 self.initial_position = self.screen_position;
339 }
340 }
341 } else if let Some(ButtonMessage::Click) = message.data::<ButtonMessage>() {
342 if message.destination() == self.minimize_button {
343 ui.send_message(WindowMessage::minimize(
344 self.handle(),
345 MessageDirection::ToWidget,
346 !self.minimized,
347 ));
348 } else if message.destination() == self.close_button {
349 ui.send_message(WindowMessage::close(
350 self.handle(),
351 MessageDirection::ToWidget,
352 ));
353 }
354 } else if let Some(msg) = message.data::<WindowMessage>() {
355 if message.destination() == self.handle()
356 && message.direction() == MessageDirection::ToWidget
357 {
358 match msg {
359 &WindowMessage::Open { center } => {
360 if !self.visibility() {
361 ui.send_message(WidgetMessage::visibility(
362 self.handle(),
363 MessageDirection::ToWidget,
364 true,
365 ));
366 ui.send_message(WidgetMessage::topmost(
367 self.handle(),
368 MessageDirection::ToWidget,
369 ));
370 if center {
371 ui.send_message(WidgetMessage::center(
372 self.handle(),
373 MessageDirection::ToWidget,
374 ));
375 }
376 }
377 }
378 &WindowMessage::OpenModal { center } => {
379 if !self.visibility() {
380 ui.send_message(WidgetMessage::visibility(
381 self.handle(),
382 MessageDirection::ToWidget,
383 true,
384 ));
385 ui.send_message(WidgetMessage::topmost(
386 self.handle(),
387 MessageDirection::ToWidget,
388 ));
389 if center {
390 ui.send_message(WidgetMessage::center(
391 self.handle(),
392 MessageDirection::ToWidget,
393 ));
394 }
395 ui.push_picking_restriction(RestrictionEntry {
396 handle: self.handle(),
397 stop: true,
398 });
399 }
400 }
401 WindowMessage::Close => {
402 if self.visibility() {
403 ui.send_message(WidgetMessage::visibility(
404 self.handle(),
405 MessageDirection::ToWidget,
406 false,
407 ));
408 ui.remove_picking_restriction(self.handle());
409 }
410 }
411 &WindowMessage::Minimize(minimized) => {
412 if self.minimized != minimized {
413 self.minimized = minimized;
414 if self.content.is_some() {
415 ui.send_message(WidgetMessage::visibility(
416 self.content,
417 MessageDirection::ToWidget,
418 !minimized,
419 ));
420 }
421 }
422 }
423 &WindowMessage::CanMinimize(value) => {
424 if self.can_minimize != value {
425 self.can_minimize = value;
426 if self.minimize_button.is_some() {
427 ui.send_message(WidgetMessage::visibility(
428 self.minimize_button,
429 MessageDirection::ToWidget,
430 value,
431 ));
432 }
433 }
434 }
435 &WindowMessage::CanClose(value) => {
436 if self.can_close != value {
437 self.can_close = value;
438 if self.close_button.is_some() {
439 ui.send_message(WidgetMessage::visibility(
440 self.close_button,
441 MessageDirection::ToWidget,
442 value,
443 ));
444 }
445 }
446 }
447 &WindowMessage::CanResize(value) => {
448 if self.can_resize != value {
449 self.can_resize = value;
450 ui.send_message(message.reverse());
451 }
452 }
453 &WindowMessage::Move(new_pos) => {
454 if self.desired_local_position() != new_pos {
455 ui.send_message(WidgetMessage::desired_position(
456 self.handle(),
457 MessageDirection::ToWidget,
458 new_pos,
459 ));
460
461 ui.send_message(message.reverse());
462 }
463 }
464 WindowMessage::MoveStart => {
465 if !self.is_dragging {
466 ui.capture_mouse(self.header);
467 let initial_position = self.actual_local_position();
468 self.initial_position = initial_position;
469 self.is_dragging = true;
470
471 ui.send_message(message.reverse());
472 }
473 }
474 WindowMessage::MoveEnd => {
475 if self.is_dragging {
476 ui.release_mouse_capture();
477 self.is_dragging = false;
478
479 ui.send_message(message.reverse());
480 }
481 }
482 WindowMessage::Title(title) => {
483 match title {
484 WindowTitle::Text(text) => {
485 if ui.node(self.title).cast::<Text>().is_some() {
486 ui.send_message(TextMessage::text(
489 self.title,
490 MessageDirection::ToWidget,
491 text.clone(),
492 ));
493 } else {
494 ui.send_message(WidgetMessage::remove(
495 self.title,
496 MessageDirection::ToWidget,
497 ));
498 self.title = make_text_title(&mut ui.build_ctx(), text);
499 }
500 }
501 WindowTitle::Node(node) => {
502 if self.title.is_some() {
503 ui.send_message(WidgetMessage::remove(
505 self.title,
506 MessageDirection::ToWidget,
507 ));
508 }
509
510 if node.is_some() {
511 self.title = *node;
512
513 ui.send_message(WidgetMessage::link(
515 self.title,
516 MessageDirection::ToWidget,
517 self.title_grid,
518 ));
519 }
520 }
521 }
522 }
523 }
524 }
525 }
526 }
527}
528
529impl Window {
530 pub fn is_dragging(&self) -> bool {
531 self.is_dragging
532 }
533
534 pub fn drag_delta(&self) -> Vector2<f32> {
535 self.drag_delta
536 }
537
538 pub fn has_active_grip(&self) -> bool {
539 for grip in self.grips.borrow().iter() {
540 if grip.is_dragging {
541 return true;
542 }
543 }
544 false
545 }
546
547 pub fn set_can_resize(&mut self, value: bool) {
548 self.can_resize = value;
549 }
550
551 pub fn can_resize(&self) -> bool {
552 self.can_resize
553 }
554}
555
556pub struct WindowBuilder {
557 pub widget_builder: WidgetBuilder,
558 pub content: Handle<UiNode>,
559 pub title: Option<WindowTitle>,
560 pub can_close: bool,
561 pub can_minimize: bool,
562 pub open: bool,
563 pub close_button: Option<Handle<UiNode>>,
564 pub minimize_button: Option<Handle<UiNode>>,
565 pub modal: bool,
567 pub can_resize: bool,
568}
569
570#[derive(Debug, Clone, PartialEq)]
578pub enum WindowTitle {
579 Text(String),
580 Node(Handle<UiNode>),
581}
582
583impl WindowTitle {
584 pub fn text<P: AsRef<str>>(text: P) -> Self {
585 WindowTitle::Text(text.as_ref().to_owned())
586 }
587
588 pub fn node(node: Handle<UiNode>) -> Self {
589 Self::Node(node)
590 }
591}
592
593fn make_text_title(ctx: &mut BuildContext, text: &str) -> Handle<UiNode> {
594 TextBuilder::new(
595 WidgetBuilder::new()
596 .with_margin(Thickness::uniform(5.0))
597 .on_row(0)
598 .on_column(0),
599 )
600 .with_text(text)
601 .build(ctx)
602}
603
604enum HeaderButton {
605 Close,
606 Minimize,
607}
608
609fn make_mark(ctx: &mut BuildContext, button: HeaderButton) -> Handle<UiNode> {
610 VectorImageBuilder::new(
611 WidgetBuilder::new()
612 .with_horizontal_alignment(HorizontalAlignment::Center)
613 .with_vertical_alignment(match button {
614 HeaderButton::Close => VerticalAlignment::Center,
615 HeaderButton::Minimize => VerticalAlignment::Bottom,
616 })
617 .with_margin(match button {
618 HeaderButton::Close => Thickness::uniform(0.0),
619 HeaderButton::Minimize => Thickness::bottom(3.0),
620 })
621 .with_foreground(BRUSH_BRIGHT),
622 )
623 .with_primitives(match button {
624 HeaderButton::Close => {
625 vec![
626 Primitive::Line {
627 begin: Vector2::new(0.0, 0.0),
628 end: Vector2::new(12.0, 12.0),
629 thickness: 3.0,
630 },
631 Primitive::Line {
632 begin: Vector2::new(12.0, 0.0),
633 end: Vector2::new(0.0, 12.0),
634 thickness: 3.0,
635 },
636 ]
637 }
638 HeaderButton::Minimize => {
639 vec![Primitive::Line {
640 begin: Vector2::new(0.0, 0.0),
641 end: Vector2::new(12.0, 0.0),
642 thickness: 3.0,
643 }]
644 }
645 })
646 .build(ctx)
647}
648
649fn make_header_button(ctx: &mut BuildContext, button: HeaderButton) -> Handle<UiNode> {
650 ButtonBuilder::new(WidgetBuilder::new().with_margin(Thickness::uniform(2.0)))
651 .with_back(
652 DecoratorBuilder::new(
653 BorderBuilder::new(WidgetBuilder::new())
654 .with_stroke_thickness(Thickness::uniform(0.0)),
655 )
656 .with_normal_brush(Brush::Solid(Color::TRANSPARENT))
657 .with_hover_brush(BRUSH_LIGHT)
658 .with_pressed_brush(BRUSH_LIGHTEST)
659 .build(ctx),
660 )
661 .with_content(make_mark(ctx, button))
662 .build(ctx)
663}
664
665impl<'a> WindowBuilder {
666 pub fn new(widget_builder: WidgetBuilder) -> Self {
667 Self {
668 widget_builder,
669 content: Handle::NONE,
670 title: None,
671 can_close: true,
672 can_minimize: true,
673 open: true,
674 close_button: None,
675 minimize_button: None,
676 modal: false,
677 can_resize: true,
678 }
679 }
680
681 pub fn with_content(mut self, content: Handle<UiNode>) -> Self {
682 self.content = content;
683 self
684 }
685
686 pub fn with_title(mut self, title: WindowTitle) -> Self {
687 self.title = Some(title);
688 self
689 }
690
691 pub fn with_minimize_button(mut self, button: Handle<UiNode>) -> Self {
692 self.minimize_button = Some(button);
693 self
694 }
695
696 pub fn with_close_button(mut self, button: Handle<UiNode>) -> Self {
697 self.close_button = Some(button);
698 self
699 }
700
701 pub fn can_close(mut self, can_close: bool) -> Self {
702 self.can_close = can_close;
703 self
704 }
705
706 pub fn can_minimize(mut self, can_minimize: bool) -> Self {
707 self.can_minimize = can_minimize;
708 self
709 }
710
711 pub fn open(mut self, open: bool) -> Self {
712 self.open = open;
713 self
714 }
715
716 pub fn modal(mut self, modal: bool) -> Self {
717 self.modal = modal;
718 self
719 }
720
721 pub fn can_resize(mut self, can_resize: bool) -> Self {
722 self.can_resize = can_resize;
723 self
724 }
725
726 pub fn build_window(self, ctx: &mut BuildContext) -> Window {
727 let minimize_button;
728 let close_button;
729
730 let title;
731 let title_grid;
732 let header = BorderBuilder::new(
733 WidgetBuilder::new()
734 .with_horizontal_alignment(HorizontalAlignment::Stretch)
735 .with_height(30.0)
736 .with_background(Brush::LinearGradient {
737 from: Vector2::new(0.5, 0.0),
738 to: Vector2::new(0.5, 1.0),
739 stops: vec![
740 GradientPoint {
741 stop: 0.0,
742 color: COLOR_DARK,
743 },
744 GradientPoint {
745 stop: 0.85,
746 color: COLOR_DARK,
747 },
748 GradientPoint {
749 stop: 1.0,
750 color: COLOR_DARKEST,
751 },
752 ],
753 })
754 .with_child({
755 title_grid = GridBuilder::new(
756 WidgetBuilder::new()
757 .with_child({
758 title = match self.title {
759 None => Handle::NONE,
760 Some(window_title) => match window_title {
761 WindowTitle::Node(node) => node,
762 WindowTitle::Text(text) => make_text_title(ctx, &text),
763 },
764 };
765 title
766 })
767 .with_child({
768 minimize_button = self.minimize_button.unwrap_or_else(|| {
769 make_header_button(ctx, HeaderButton::Minimize)
770 });
771 ctx[minimize_button]
772 .set_visibility(self.can_minimize)
773 .set_width(30.0)
774 .set_row(0)
775 .set_column(1);
776 minimize_button
777 })
778 .with_child({
779 close_button = self.close_button.unwrap_or_else(|| {
780 make_header_button(ctx, HeaderButton::Close)
781 });
782 ctx[close_button]
783 .set_width(30.0)
784 .set_visibility(self.can_close)
785 .set_row(0)
786 .set_column(2);
787 close_button
788 }),
789 )
790 .add_column(Column::stretch())
791 .add_column(Column::auto())
792 .add_column(Column::auto())
793 .add_row(Row::stretch())
794 .build(ctx);
795 title_grid
796 })
797 .on_row(0),
798 )
799 .build(ctx);
800
801 if self.content.is_some() {
802 ctx[self.content].set_row(1);
803 }
804 Window {
805 widget: self
806 .widget_builder
807 .with_visibility(self.open)
808 .with_child(
809 BorderBuilder::new(
810 WidgetBuilder::new()
811 .with_foreground(BRUSH_LIGHTER)
812 .with_child(
813 GridBuilder::new(
814 WidgetBuilder::new()
815 .with_child(self.content)
816 .with_child(header),
817 )
818 .add_column(Column::stretch())
819 .add_row(Row::auto())
820 .add_row(Row::stretch())
821 .build(ctx),
822 ),
823 )
824 .with_stroke_thickness(Thickness::uniform(1.0))
825 .build(ctx),
826 )
827 .build(),
828 mouse_click_pos: Vector2::default(),
829 initial_position: Vector2::default(),
830 initial_size: Default::default(),
831 is_dragging: false,
832 minimized: false,
833 can_minimize: self.can_minimize,
834 can_close: self.can_close,
835 can_resize: self.can_resize,
836 header,
837 minimize_button,
838 close_button,
839 drag_delta: Default::default(),
840 content: self.content,
841 grips: RefCell::new([
842 Grip::new(GripKind::LeftTopCorner, CursorIcon::NwResize),
844 Grip::new(GripKind::RightTopCorner, CursorIcon::NeResize),
845 Grip::new(GripKind::RightBottomCorner, CursorIcon::SeResize),
846 Grip::new(GripKind::LeftBottomCorner, CursorIcon::SwResize),
847 Grip::new(GripKind::Left, CursorIcon::WResize),
848 Grip::new(GripKind::Top, CursorIcon::NResize),
849 Grip::new(GripKind::Right, CursorIcon::EResize),
850 Grip::new(GripKind::Bottom, CursorIcon::SResize),
851 ]),
852 title,
853 title_grid,
854 }
855 }
856
857 pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
858 let modal = self.modal;
859 let open = self.open;
860
861 let node = self.build_window(ctx);
862 let handle = ctx.add_node(UiNode::new(node));
863
864 if modal && open {
865 ctx.ui
866 .push_picking_restriction(RestrictionEntry { handle, stop: true });
867 }
868
869 handle
870 }
871}