1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
use crate::{color::TRANSPARENT, paint::Stroke, widgets::*, *};
#[derive(Clone, Copy, Debug, Default)]
pub(crate) struct BarState {
open_menu: Option<Id>,
}
impl BarState {
fn load(ctx: &Context, bar_id: &Id) -> Self {
ctx.memory()
.menu_bar
.get(bar_id)
.cloned()
.unwrap_or_default()
}
fn save(self, ctx: &Context, bar_id: Id) {
ctx.memory().menu_bar.insert(bar_id, self);
}
}
pub fn bar<R>(ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R) -> (R, Response) {
ui.horizontal(|ui| {
let mut style = ui.style().clone();
style.spacing.button_padding = vec2(2.0, 0.0);
style.visuals.widgets.active.bg_stroke = Stroke::none();
style.visuals.widgets.hovered.bg_stroke = Stroke::none();
style.visuals.widgets.inactive.bg_fill = TRANSPARENT;
style.visuals.widgets.inactive.bg_stroke = Stroke::none();
ui.set_style(style);
let height = ui.style().spacing.interact_size.y;
ui.set_min_size(vec2(ui.available().width(), height));
add_contents(ui)
})
}
pub fn menu(ui: &mut Ui, title: impl Into<String>, add_contents: impl FnOnce(&mut Ui)) {
menu_impl(ui, title, Box::new(add_contents))
}
fn menu_impl<'c>(
ui: &mut Ui,
title: impl Into<String>,
add_contents: Box<dyn FnOnce(&mut Ui) + 'c>,
) {
let title = title.into();
let bar_id = ui.id();
let menu_id = bar_id.with(&title);
let mut bar_state = BarState::load(ui.ctx(), &bar_id);
let mut button = Button::new(title);
if bar_state.open_menu == Some(menu_id) {
button = button.fill(Some(ui.style().visuals.widgets.active.fg_fill));
}
let button_response = ui.add(button);
if button_response.clicked {
if bar_state.open_menu == Some(menu_id) {
bar_state.open_menu = None;
} else {
bar_state.open_menu = Some(menu_id);
}
} else if button_response.hovered && bar_state.open_menu.is_some() {
bar_state.open_menu = Some(menu_id);
}
if bar_state.open_menu == Some(menu_id) || ui.memory().all_menues_are_open {
let area = Area::new(menu_id)
.order(Order::Foreground)
.fixed_pos(button_response.rect.left_bottom());
let frame = Frame::menu(ui.style());
area.show(ui.ctx(), |ui| {
frame.show(ui, |ui| {
let mut style = ui.style().clone();
style.spacing.button_padding = vec2(2.0, 0.0);
style.visuals.widgets.active.bg_stroke = Stroke::none();
style.visuals.widgets.hovered.bg_stroke = Stroke::none();
style.visuals.widgets.inactive.bg_fill = TRANSPARENT;
style.visuals.widgets.inactive.bg_stroke = Stroke::none();
ui.set_style(style);
ui.with_layout(Layout::justified(Direction::Vertical), add_contents);
})
});
if ui.input().key_pressed(Key::Escape) || ui.input().mouse.click && !button_response.clicked
{
bar_state.open_menu = None;
}
}
bar_state.save(ui.ctx(), bar_id);
}