1use crate::{
2 types::{Rect, Vector2},
3 ui::{Drag, DragState},
4 Id, Layout, Ui,
5};
6
7#[derive(Debug, Clone)]
8pub struct Group {
9 id: Id,
10 position: Option<Vector2>,
11 layout: Layout,
12 size: Vector2,
13 draggable: bool,
14 highlight: bool,
15 hoverable: bool,
16}
17
18impl Group {
19 pub fn new(id: Id, size: Vector2) -> Group {
20 Group {
21 id,
22 size,
23 position: None,
24 layout: Layout::Horizontal,
25 draggable: false,
26 highlight: false,
27 hoverable: false,
28 }
29 }
30
31 pub fn position(self, position: Vector2) -> Group {
32 Group {
33 position: Some(position),
34 ..self
35 }
36 }
37
38 pub fn layout(self, layout: Layout) -> Group {
39 Group { layout, ..self }
40 }
41
42 pub fn draggable(self, draggable: bool) -> Group {
43 Group { draggable, ..self }
44 }
45
46 pub fn hoverable(self, hoverable: bool) -> Group {
47 Group { hoverable, ..self }
48 }
49
50 pub fn highlight(self, highlight: bool) -> Group {
51 Group { highlight, ..self }
52 }
53
54 pub fn ui<F: FnOnce(&mut Ui)>(self, ui: &mut Ui, f: F) -> Drag {
55 let token = self.begin(ui);
56 f(ui);
57 token.end(ui)
58 }
59
60 pub fn begin(self, ui: &mut Ui) -> GroupToken {
61 let mut drag = Drag::No;
62
63 let parent = ui.get_active_window_context();
64
65 let parent_rect = parent.window.content_rect();
66
67 parent.window.childs.push(self.id);
68
69 let pos = parent.window.cursor.fit(
70 self.size,
71 self.position.map_or(self.layout, Layout::Free),
72 );
73 let rect = Rect::new(pos.x, pos.y, self.size.x, self.size.y);
74 let parent_id = Some(parent.window.id);
75
76 let mut context = ui.begin_window(self.id, parent_id, pos, self.size, 0., true);
77
78 let hovered =
79 (self.hoverable || self.draggable) && rect.contains(context.input.mouse_position);
80
81 if self.draggable && context.dragging.is_none() && hovered && context.input.click_down {
82 *context.dragging = Some((self.id, DragState::Clicked(context.input.mouse_position)));
83 }
84
85 if let Some((id, DragState::Clicked(orig))) = context.dragging {
86 if *id == self.id
87 && context.input.is_mouse_down
88 && context.input.mouse_position.distance(*orig) > 5.
89 {
90 *context.dragging = Some((self.id, DragState::Dragging(*orig)));
91 }
92 if context.input.is_mouse_down == false {
93 *context.dragging = None;
94 }
95 }
96
97 if let Some((id, DragState::Dragging(_))) = context.dragging {
98 let id = *id;
99
100 if id == self.id {
101 drag = Drag::Dragging(context.input.mouse_position, *context.drag_hovered_previous_frame);
102
103 if context.input.is_mouse_down == false {
104 *context.dragging = None;
105 drag = Drag::Dropped(context.input.mouse_position, *context.drag_hovered_previous_frame);
106 }
107 }
108
109 if id != self.id && hovered {
110 *context.drag_hovered = Some(self.id);
111 }
112 }
113
114 context.window.draw_commands.clip(parent_rect);
115
116 context.scroll_area();
117
118 let clip_rect = context.window.content_rect();
119 context.window.draw_commands.clip(clip_rect);
120 context.window.draw_commands.draw_rect(
121 rect,
122 context
123 .global_style
124 .drag_border(context.focused, hovered, self.highlight),
125 None,
126 );
127
128 GroupToken {
129 draggable: self.draggable,
130 drag,
131 pos,
132 size: self.size,
133 }
134 }
135}
136
137#[must_use = "Must call `.end()` to finish Group"]
138pub struct GroupToken {
139 draggable: bool,
140 drag: Drag,
141 pos: Vector2,
142 size: Vector2,
143}
144
145impl GroupToken {
146 pub fn end(self, ui: &mut Ui) -> Drag {
147 let context = ui.get_active_window_context();
148
149 context.window.draw_commands.clip(None);
150
151 if context.focused && self.draggable {
152 if
153 context.input.is_mouse_down
155 && Rect::new(self.pos.x, self.pos.y, self.size.x, self.size.y)
156 .contains(context.input.mouse_position)
157 {
158 }
163 }
164
165 ui.end_window();
166
167 self.drag
168 }
169}
170
171impl Ui {
172 pub fn group<F: FnOnce(&mut Ui)>(&mut self, id: Id, size: Vector2, f: F) -> Drag {
173 Group::new(id, size).ui(self, f)
174 }
175}