macroquad/ui/widgets/
group.rs

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