Skip to content
55 changes: 28 additions & 27 deletions examples/gtk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,37 @@ use gtk4::prelude::*;
#[cfg(target_os = "linux")]
use keyboard_types::{Code, Modifiers};
#[cfg(target_os = "linux")]
use muda::{accelerator::Accelerator, MenuEvent};
use muda::{accelerator::Accelerator, ContextMenu, MenuEvent};

#[cfg(target_os = "linux")]
fn main() {
// Create a new application
let application = gtk4::Application::builder()
.application_id("com.github.gtk4-rs.examples.menubar")
.application_id("com.github.muda.example.gtk")
.build();
application.connect_startup(on_startup);
application.connect_activate(on_activate);
application.run();
}

#[cfg(target_os = "linux")]
fn on_startup(_: &gtk4::Application) {
MenuEvent::set_event_handler(Some(|event| {
println!("{event:?}");
}));
}
application.connect_startup(|_| {
MenuEvent::set_event_handler(Some(|event| {
println!("{event:?}");
}))
});

#[cfg(target_os = "linux")]
fn on_activate(application: &gtk4::Application) {
use muda::ContextMenu;

let window = gtk4::ApplicationWindow::builder()
.application(application)
.title("Menubar Example")
.default_width(350)
.default_height(350)
.show_menubar(true)
.build();
application.connect_activate(move |application| {
let window = gtk4::ApplicationWindow::builder()
.application(application)
.title("GTK Menubar Example")
.default_width(350)
.default_height(350)
.show_menubar(true)
.build();

window.present();
setup_ui(&window);
});

application.run();
}

#[cfg(target_os = "linux")]
fn setup_ui(window: &gtk4::ApplicationWindow) {
let about_menu_item = muda::MenuItem::new("About", true, None);

let check = muda::CheckMenuItem::new(
Expand Down Expand Up @@ -67,21 +64,25 @@ fn on_activate(application: &gtk4::Application) {
menubar.append(&file_menu).unwrap();

let vbox = gtk4::Box::new(gtk4::Orientation::Vertical, 0);
menubar.init_for_gtk_window(&window, Some(&vbox)).unwrap();
menubar.init_for_gtk_window(window, Some(&vbox)).unwrap();

let btn = gtk4::Button::with_label("ASdasd");
let w = window.clone();

btn.connect_clicked(move |_| {
file_menu.show_context_menu_for_gtk_window(w.dynamic_cast_ref().unwrap(), None);
});

vbox.append(&btn);

window.set_child(Some(&vbox));
window.present();
}

#[cfg(not(target_os = "linux"))]
fn main() {}
fn main() {
eprintln!("This example is only available on Linux");
}

fn load_icon(path: &std::path::Path) -> muda::Icon {
let (icon_rgba, icon_width, icon_height) = {
Expand Down
287 changes: 287 additions & 0 deletions examples/gtk_widgets.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,287 @@
#![cfg(target_os = "linux")]

use gtk4::{
PopoverMenuFlags, gio::{self, File, FileIcon, ThemedIcon, prelude::*}, prelude::*
};
use muda::gtk_widgets::{self, MenuBar};

const SPACING: i32 = 8;
const SPACING_LARGE: i32 = SPACING * 2;
const SPACING_XLARGE: i32 = SPACING * 4;

fn main() {
let application = gtk4::Application::builder()
.application_id("com.github.muda.example.gtk")
.build();

application.connect_activate(move |application| {
let window = gtk4::ApplicationWindow::builder()
.application(application)
.title("GTK Menubar Example")
.default_width(350)
.default_height(350)
.show_menubar(true)
.build();

let icon = FileIcon::new(&File::for_path(std::path::Path::new(concat!(
env!("CARGO_MANIFEST_DIR"),
"/examples/icon.png"
))));

let menu = {
let file_menu = {
let icon_section = gio::Menu::new();
let icon_with_label_menu_item =
gio::MenuItem::new(Some("Icon With Label"), Some("muda.icon"));

icon_with_label_menu_item.set_icon(&icon);

icon_section.append_item(&icon_with_label_menu_item);

let submenu_item = gio::MenuItem::new(Some("Icon Submenu"), None);

let icon_submenu = gio::Menu::new();
let icon_menu_item =
gio::MenuItem::new(Some("Icon Accelerator"), Some("muda.icon-accel"));

icon_submenu.append_item(&icon_menu_item);

submenu_item.set_submenu(Some(&icon_submenu));

icon_section.append_item(&submenu_item);

let cool_section = gtk4::gio::Menu::new();
let cool_menu_item = gtk4::gio::MenuItem::new(Some("Be Cool"), Some("muda.cool"));
let cool_menu_shortcut_item =
gtk4::gio::MenuItem::new(Some("Shortcut"), Some("muda.cool-shortcut"));

cool_section.append_item(&cool_menu_item);
cool_section.append_item(&cool_menu_shortcut_item);

let program_section = gio::Menu::new();
let about_menu_item = gio::MenuItem::new(Some("About"), Some("muda.about"));
let quit_menu_item = gio::MenuItem::new(Some("Quit"), Some("muda.quit"));

program_section.append_item(&about_menu_item);
program_section.append_item(&quit_menu_item);

let group = gio::SimpleActionGroup::new();

group.add_action(&gio::SimpleAction::new_stateful(
"cool",
None,
&true.to_variant(),
));

group.add_action(&gio::SimpleAction::new_stateful(
"cool-shortcut",
None,
&true.to_variant(),
));

group.add_action(&gio::SimpleAction::new("quit", None));
group.add_action(&gio::SimpleAction::new("about", None));
group.add_action(&gio::SimpleAction::new("icon", None));
group.add_action(&gio::SimpleAction::new("icon-accel", None));

window.insert_action_group("muda", Some(&group));

let file_menu = gtk4::gio::Menu::new();

file_menu.append_section(None, &icon_section);
file_menu.append_section(Some("Cool Header"), &cool_section);
file_menu.append_section(None, &program_section);

file_menu
};

let window_menu_item = {
let window_menu = gio::Menu::new();

let nothing_item =
gio::MenuItem::new(Some("Absolutely Nothing..."), Some("muda.nothing"));

window_menu.append_item(&nothing_item);

let window_menu_item = gio::MenuItem::new(Some("Window"), None);

window_menu_item.set_icon(&icon);
window_menu_item.set_submenu(Some(&window_menu));

window_menu_item
};

let evil_menu = gio::Menu::new();
let evil_menu_item = gio::MenuItem::new(Some("Please Put Text On Your Menus :("), None);

evil_menu.append_item(&evil_menu_item);

let annoyed_menu_item = gio::MenuItem::new(None, None);
annoyed_menu_item.set_icon(&ThemedIcon::from_names(&[
"face-plain-symbolic",
"face-plain",
]));

let annoyed_menu = gio::Menu::new();
let warning_menu_item =
gio::MenuItem::new(Some("Seriously Put Text On Your Menus >:("), None);

annoyed_menu.append_item(&warning_menu_item);
annoyed_menu_item.set_submenu(Some(&annoyed_menu));

let menu = gtk4::gio::Menu::new();
menu.append_submenu(Some("File"), &file_menu);
menu.append_item(&window_menu_item);
menu.append_submenu(None, &evil_menu);
menu.append_item(&annoyed_menu_item);
menu
};

application.set_accels_for_action("muda.about", &["<Control>C"]);
application.set_accels_for_action("muda.quit", &["<Control>Q"]);
application.set_accels_for_action("muda.cool-shortcut", &["<Control>W"]);
application.set_accels_for_action("muda.icon-accel", &["<Control>I"]);

setup_ui(&window, &icon, &menu);

window.present();
});

application.run();
}

fn setup_ui(window: &gtk4::ApplicationWindow, icon: &FileIcon, menu: &gio::Menu) {
let vbox = gtk4::Box::builder()
.orientation(gtk4::Orientation::Vertical)
.spacing(SPACING)
.margin_top(SPACING_LARGE)
.margin_bottom(SPACING_LARGE)
.hexpand(true)
.halign(gtk4::Align::Fill)
.build();

let hbox = gtk4::Box::builder()
.orientation(gtk4::Orientation::Horizontal)
.spacing(SPACING)
.margin_start(SPACING)
.margin_end(SPACING)
.hexpand(true)
.halign(gtk4::Align::Fill)
.build();

setup_muda_menu(&hbox, menu);
setup_gtk_popover_menu(&hbox, menu);

let image = gtk4::Image::builder()
.gicon(icon)
.icon_size(gtk4::IconSize::Large)
.build();

vbox.append(&hbox);
vbox.append(&image);
vbox.append(&gtk4::Label::new(Some(
"Some menu items should have this icon",
)));

window.set_child(Some(&vbox));
}

// TODO: Replace with muda components
fn setup_muda_menu(container: &gtk4::Box, menu: &gio::Menu) {
let vbox = gtk4::Box::new(gtk4::Orientation::Vertical, 0);
let frame = gtk4::Frame::builder().hexpand(true).vexpand(true).build();
let menu_bar = MenuBar::new(menu.clone());

let inner_vbox = gtk4::Box::new(gtk4::Orientation::Vertical, SPACING);

let gesture = gtk4::GestureClick::new();
gesture.set_button(3);

gesture.connect_pressed(move |gesture, _, x, y| {
println!(
"Pressed, x: {x}, y: {y}, button: {}",
gesture.current_button()
);
});

inner_vbox.add_controller(gesture);

inner_vbox.append(
&gtk4::Label::builder()
.label("Muda Widgets\nRight Click to See Context Menu")
.justify(gtk4::Justification::Center)
.margin_top(SPACING_LARGE)
.margin_bottom(SPACING_LARGE)
.margin_start(SPACING_XLARGE)
.margin_end(SPACING_XLARGE)
.valign(gtk4::Align::Center)
.xalign(0.5)
.vexpand(true)
.build(),
);

vbox.append(&menu_bar);
vbox.append(&inner_vbox);

frame.set_child(Some(&vbox));

container.append(&frame);
}

fn setup_gtk_popover_menu(container: &gtk4::Box, menu: &gio::Menu) {
use gtk4::{PopoverMenu, PopoverMenuBar};

let vbox = gtk4::Box::new(gtk4::Orientation::Vertical, 0);
let frame = gtk4::Frame::builder().hexpand(true).vexpand(true).build();
let menu_bar = PopoverMenuBar::builder()
.menu_model(menu)
.valign(gtk4::Align::Start)
.build();

let inner_vbox = gtk4::Box::new(gtk4::Orientation::Vertical, SPACING);

let gesture = gtk4::GestureClick::new();
gesture.set_button(3);

let popover = PopoverMenu::from_model_full(&menu.item_link(0, "submenu").unwrap(), PopoverMenuFlags::NESTED);

// TODO: Fix position offset of mouse
popover.set_position(gtk4::PositionType::Right);
popover.set_parent(&frame);
popover.present();

gesture.connect_pressed(move |gesture, _, x, y| {
use gtk4::gdk::Rectangle;

println!(
"Pressed, x: {x}, y: {y}, button: {}",
gesture.current_button()
);

popover.set_pointing_to(Some(&Rectangle::new(x as i32, y as i32, 0, 0)));
popover.popup();
});

inner_vbox.add_controller(gesture);

inner_vbox.append(
&gtk4::Label::builder()
.label("GTK Widgets\nRight Click to See Context Menu")
.justify(gtk4::Justification::Center)
.margin_top(SPACING_LARGE)
.margin_bottom(SPACING_LARGE)
.margin_start(SPACING_XLARGE)
.margin_end(SPACING_XLARGE)
.valign(gtk4::Align::Center)
.xalign(0.5)
.vexpand(true)
.build(),
);

vbox.append(&menu_bar);
vbox.append(&inner_vbox);

frame.set_child(Some(&vbox));

container.append(&frame);
}
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,9 @@ mod menu_id;
mod platform_impl;
mod util;

#[cfg(target_os = "linux")]
pub use platform_impl::widgets as gtk_widgets;

pub use about_metadata::AboutMetadata;
pub use builders::*;
pub use dpi;
Expand Down
Loading