Skip to content

Commit 0e08f4e

Browse files
committed
use a separate action for check menu items
1 parent 1f1ddaf commit 0e08f4e

File tree

2 files changed

+83
-39
lines changed

2 files changed

+83
-39
lines changed

examples/gtk.rs

+9-2
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,22 @@ fn on_activate(application: &gtk4::Application) {
3838
let menubar = {
3939
let file_menu = {
4040
let about_menu_item = muda::MenuItem::new("About", true, None);
41+
let check = muda::CheckMenuItem::new(
42+
"Check",
43+
true,
44+
true,
45+
Some(Accelerator::new(Modifiers::CONTROL, Code::KeyQ)),
46+
);
4147
let quit_menu_item = muda::MenuItem::with_id(
4248
"quit",
43-
"Quit",
49+
"&Quit",
4450
true,
4551
Some(Accelerator::new(Modifiers::CONTROL, Code::KeyQ)),
4652
);
4753

48-
let file_menu = muda::Submenu::new("File", true);
54+
let file_menu = muda::Submenu::new("&File", true);
4955
file_menu.append(&about_menu_item).unwrap();
56+
file_menu.append(&check).unwrap();
5057
file_menu.append(&quit_menu_item).unwrap();
5158
file_menu
5259
};

src/platform_impl/gtk/mod.rs

+74-37
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ use std::{
1111
rc::Rc,
1212
};
1313

14+
use accelerator::to_gtk_mnemonic;
1415
use dpi::Position;
15-
use gtk4::{gio::SimpleActionGroup, glib::VariantTy, prelude::*};
16+
use gtk4::{gio, glib::VariantTy, prelude::*};
1617
pub(crate) use icon::PlatformIcon;
1718

1819
use crate::{
@@ -24,27 +25,31 @@ use crate::{
2425

2526
static COUNTER: Counter = Counter::new();
2627

27-
struct GtkMenuBar(gtk4::PopoverMenuBar, gtk4::gio::Menu);
28+
const DEFAULT_ACTION: &str = "_internal_sendEvent";
29+
const DEFAULT_ACTION_GROUP: &str = "muda";
30+
const DEFAULT_DETAILED_ACTION: &str = "muda._internal_sendEvent";
31+
32+
struct GtkMenuBar(gtk4::PopoverMenuBar, gio::Menu);
2833

2934
impl GtkMenuBar {
3035
fn new() -> Self {
31-
let menu = gtk4::gio::Menu::new();
36+
let menu = gio::Menu::new();
3237
Self(gtk4::PopoverMenuBar::from_model(Some(&menu)), menu)
3338
}
3439

3540
fn widget(&self) -> &gtk4::PopoverMenuBar {
3641
&self.0
3742
}
3843

39-
fn menu(&self) -> &gtk4::gio::Menu {
44+
fn menu(&self) -> &gio::Menu {
4045
&self.1
4146
}
4247
}
4348

4449
pub struct Menu {
4550
id: MenuId,
4651
instances: HashMap<u32, GtkMenuBar>,
47-
action_group: Option<SimpleActionGroup>,
52+
action_group: Option<gio::SimpleActionGroup>,
4853
children: Vec<Rc<RefCell<MenuChild>>>,
4954
}
5055

@@ -114,24 +119,15 @@ impl Menu {
114119
if self.action_group.is_none() {
115120
let action_group = gtk4::gio::SimpleActionGroup::new();
116121

117-
let action = gtk4::gio::SimpleAction::new("sendEvent", Some(&VariantTy::STRING));
118-
action_group.add_action(&action);
122+
let action = gtk4::gio::SimpleAction::new(DEFAULT_ACTION, Some(&VariantTy::STRING));
119123
action.connect_activate(|_, v| {
120124
if let Some(v) = v {
121125
MenuEvent::send(MenuEvent {
122126
id: MenuId(v.as_ref().to_string()),
123127
});
124128
}
125129
});
126-
127-
let action =
128-
gtk4::gio::SimpleAction::new_stateful("sendCheckEvent", None, &"".to_variant());
129130
action_group.add_action(&action);
130-
action.connect_activate(|_, _| {
131-
MenuEvent::send(MenuEvent {
132-
id: MenuId("0".to_string()),
133-
});
134-
});
135131

136132
self.action_group = Some(action_group);
137133
}
@@ -144,7 +140,7 @@ impl Menu {
144140
return Err(crate::Error::AlreadyInitialized);
145141
}
146142

147-
window.insert_action_group("muda", self.action_group.as_ref());
143+
window.insert_action_group(DEFAULT_ACTION_GROUP, self.action_group.as_ref());
148144

149145
for item in self.items() {
150146
self.add_menu_item_with_id(item.as_ref(), id)?;
@@ -221,11 +217,12 @@ impl Menu {
221217

222218
#[derive(Clone)]
223219
enum GtkChild {
224-
Item(gtk4::gio::MenuItem),
220+
Item(gio::MenuItem),
225221
Submenu {
226222
id: u32,
227-
item: gtk4::gio::MenuItem,
228-
menu: gtk4::gio::Menu,
223+
item: gio::MenuItem,
224+
menu: gio::Menu,
225+
action_group: Option<gio::SimpleActionGroup>,
229226
},
230227
}
231228

@@ -239,14 +236,21 @@ impl GtkChild {
239236
}
240237
}
241238

242-
fn menu(&self) -> &gtk4::gio::Menu {
239+
fn menu(&self) -> &gio::Menu {
243240
match self {
244241
GtkChild::Item(_) => {
245242
unreachable!("This is a bug report to https://github.com/tauri-apps/muda")
246243
}
247244
GtkChild::Submenu { menu, .. } => menu,
248245
}
249246
}
247+
248+
fn action_group(&self) -> Option<&gio::SimpleActionGroup> {
249+
match self {
250+
GtkChild::Item(_) => None,
251+
GtkChild::Submenu { action_group, .. } => action_group.as_ref(),
252+
}
253+
}
250254
}
251255

252256
pub struct MenuChild {
@@ -255,6 +259,8 @@ pub struct MenuChild {
255259
enabled: bool,
256260
accelerator: Option<Accelerator>,
257261

262+
checked: bool,
263+
258264
type_: MenuItemType,
259265

260266
instances: HashMap<u32, Vec<GtkChild>>,
@@ -267,6 +273,7 @@ impl MenuChild {
267273
id: id.unwrap_or_else(|| MenuId(COUNTER.next().to_string())),
268274
text: text.to_string(),
269275
enabled,
276+
checked: false,
270277
accelerator: None,
271278
type_: MenuItemType::Submenu,
272279
instances: HashMap::new(),
@@ -277,17 +284,18 @@ impl MenuChild {
277284
fn create_gtk_item_for_submenu(
278285
&mut self,
279286
menu_id: u32,
280-
action_group: Option<&gtk4::gio::SimpleActionGroup>,
281-
) -> crate::Result<gtk4::gio::MenuItem> {
282-
let menu = gtk4::gio::Menu::new();
283-
let item = gtk4::gio::MenuItem::new_submenu(Some(&self.text), &menu);
287+
action_group: Option<&gio::SimpleActionGroup>,
288+
) -> crate::Result<gio::MenuItem> {
289+
let menu = gio::Menu::new();
290+
let item = gio::MenuItem::new_submenu(Some(&to_gtk_mnemonic(&self.text)), &menu);
284291

285292
let id = COUNTER.next();
286293

287294
let child = GtkChild::Submenu {
288295
item: item.clone(),
289296
menu,
290297
id,
298+
action_group: action_group.cloned(),
291299
};
292300

293301
self.instances.entry(menu_id).or_default().push(child);
@@ -307,7 +315,7 @@ impl MenuChild {
307315

308316
for menus in self.instances.values() {
309317
for gtk_child in menus {
310-
let gtk_item = item.make_gtk_menu_item(gtk_child.id(), None /* TODO */)?;
318+
let gtk_item = item.make_gtk_menu_item(gtk_child.id(), gtk_child.action_group())?;
311319

312320
match op {
313321
AddOp::Append => gtk_child.menu().append_item(&gtk_item),
@@ -324,7 +332,7 @@ impl MenuChild {
324332
pub fn add_menu_item_with_id(&self, item: &dyn IsMenuItem, id: u32) -> crate::Result<()> {
325333
for menus in self.instances.values() {
326334
for gtk_child in menus.iter().filter(|m| m.id() == id) {
327-
let gtk_item = item.make_gtk_menu_item(gtk_child.id(), None /* TODO */)?;
335+
let gtk_item = item.make_gtk_menu_item(gtk_child.id(), gtk_child.action_group())?;
328336
gtk_child.menu().append_item(&gtk_item);
329337
}
330338
}
@@ -364,6 +372,7 @@ impl MenuChild {
364372
text: text.to_string(),
365373
enabled,
366374
accelerator,
375+
checked: false,
367376
type_: MenuItemType::MenuItem,
368377
instances: HashMap::new(),
369378
children: Vec::new(),
@@ -373,11 +382,11 @@ impl MenuChild {
373382
fn create_gtk_item_for_menu_item(
374383
&mut self,
375384
menu_id: u32,
376-
action_group: Option<&gtk4::gio::SimpleActionGroup>,
377-
) -> crate::Result<gtk4::gio::MenuItem> {
378-
let item = gtk4::gio::MenuItem::new(
379-
Some(&self.text),
380-
Some(&format!("muda.sendEvent::{}", self.id.as_ref())),
385+
_action_group: Option<&gio::SimpleActionGroup>,
386+
) -> crate::Result<gio::MenuItem> {
387+
let item = gio::MenuItem::new(
388+
Some(&to_gtk_mnemonic(&self.text)),
389+
Some(&format!("{DEFAULT_DETAILED_ACTION}::{}", self.id.as_ref())),
381390
);
382391

383392
let child = GtkChild::Item(item.clone());
@@ -422,6 +431,7 @@ impl MenuChild {
422431
text: text.unwrap_or_else(|| item_type.text().to_string()),
423432
enabled: true,
424433
accelerator: None,
434+
checked: false,
425435
type_: MenuItemType::Predefined,
426436
instances: HashMap::new(),
427437
children: Vec::new(),
@@ -442,12 +452,39 @@ impl MenuChild {
442452
text: text.to_string(),
443453
enabled,
444454
accelerator,
455+
checked,
445456
type_: MenuItemType::Check,
446457
instances: HashMap::new(),
447458
children: Vec::new(),
448459
}
449460
}
450461

462+
fn create_gtk_item_for_check_menu_item(
463+
&mut self,
464+
menu_id: u32,
465+
action_group: Option<&gio::SimpleActionGroup>,
466+
) -> crate::Result<gio::MenuItem> {
467+
let item = gio::MenuItem::new(
468+
Some(&to_gtk_mnemonic(&self.text)),
469+
Some(&format!("{DEFAULT_ACTION_GROUP}.{}", self.id.as_ref())),
470+
);
471+
472+
if let Some(action_group) = action_group {
473+
let state = &self.checked.to_variant();
474+
let action = gio::SimpleAction::new_stateful(self.id.as_ref(), None, state);
475+
let id = self.id.clone();
476+
action.connect_state_notify(move |_| {
477+
MenuEvent::send(MenuEvent { id: id.clone() });
478+
});
479+
action_group.add_action(&action);
480+
}
481+
482+
let child = GtkChild::Item(item.clone());
483+
self.instances.entry(menu_id).or_default().push(child);
484+
485+
Ok(item)
486+
}
487+
451488
pub fn is_checked(&self) -> bool {
452489
todo!()
453490
}
@@ -470,6 +507,7 @@ impl MenuChild {
470507
text: text.to_string(),
471508
enabled,
472509
accelerator,
510+
checked: false,
473511
type_: MenuItemType::Icon,
474512
instances: HashMap::new(),
475513
children: Vec::new(),
@@ -488,6 +526,7 @@ impl MenuChild {
488526
text: text.to_string(),
489527
enabled,
490528
accelerator,
529+
checked: false,
491530
type_: MenuItemType::Submenu,
492531
instances: HashMap::new(),
493532
children: Vec::new(),
@@ -501,19 +540,17 @@ impl dyn IsMenuItem + '_ {
501540
fn make_gtk_menu_item(
502541
&self,
503542
menu_id: u32,
504-
action_group: Option<&gtk4::gio::SimpleActionGroup>,
505-
) -> crate::Result<gtk4::gio::MenuItem> {
543+
action_group: Option<&gio::SimpleActionGroup>,
544+
) -> crate::Result<gio::MenuItem> {
506545
let kind = self.kind();
507546
let mut child = kind.child_mut();
508547
match child.item_type() {
509548
MenuItemType::Submenu => child.create_gtk_item_for_submenu(menu_id, action_group),
510549
MenuItemType::MenuItem => child.create_gtk_item_for_menu_item(menu_id, action_group),
550+
MenuItemType::Check => child.create_gtk_item_for_check_menu_item(menu_id, action_group),
511551
_ => todo!(),
512552
// MenuItemType::Predefined => {
513-
// child.create_gtk_item_for_predefined_menu_item(menu_id, action_group, add_to_store)
514-
// }
515-
// MenuItemType::Check => {
516-
// child.create_gtk_item_for_check_menu_item(menu_id, action_group, add_to_store)
553+
// child.create_gtk_item_for_predefined_menu_item(menu_id, action_group)
517554
// }
518555
// MenuItemType::Icon => child.create_gtk_item_for_icon_menu_item(
519556
// menu_id,

0 commit comments

Comments
 (0)