1

Тема: Рецепт створення простого меню з Checkbox і Radiobutton для gtk4

Вирішив оприлюднити цей рецепт тут, бо, як виявилося,
створити простеньке класичне меню в gtk4 ще той головняк.
Можливо, комусь буде корисне.
main.rs

mod main_window;
pub use crate::main_window::{activate_main_window, build_main_window};
use gtk::prelude::*;

fn main() {
    // Створення нового екземпляра програми
    let application = gtk::Application::builder()
        .application_id("org.gtk-rs.Movar")
        .build();
    application.connect_startup(build_main_window);
    application.connect_activate(activate_main_window);
    application.run();
}

main_window.rs

use gtk::{gio, prelude::*};

pub fn build_main_window(app: &gtk::Application) {
    //Побудова віджетів головного вікна
    create_actions_entries(&app);
    create_accelerators(&app);
    let menubar = create_menubar();
    app.set_menubar(Some(&menubar));
}

fn create_actions_entries(app: &gtk::Application){
    //Створення дій, що будуть прив'язані до натискання кнопок

    //Дія для кнопки Quit
    let quit = gio::ActionEntry::builder("quit")
        .activate(|app: &gtk::Application, _, _| {app.quit()})
        .build();

    //Дія для кнопки About
    let about = gio::ActionEntry::builder("about")
        .activate(|_, _, _| println!("About was pressed"))
        .build();

    //Дія для кнопки Manual
    let manual = gio::ActionEntry::builder("manual")
        .activate(|_, _, _| println!("Manual is pressed"))
        .build();

    //Дія для кнопки Website
    let website = gio::ActionEntry::builder("website")
        .activate(|_ ,_ ,_| println!("Website is pressed"))
        .build();

    //Дія для кнопки Dictionary settings
    let dict_settings = gio::ActionEntry::builder("dict_settings")
        .activate(|_ ,_, _| println!("Dictionary settings is pressed"))
        .build();

    //Дія для кнопки Clean search history
    let clean_search_history = gio::ActionEntry::builder("clean_search_history")
        .activate(|_, _, _| println!("Clean search history is pressed"))
        .build();

    //Дія для кнопки Show search history
    let show_history_default_state: bool = true;
    let show_search_history = gio::ActionEntry::builder("show_search_history")
        //Створення стану(state) необхідне для того, щоб з'явилася галочка Checkbox
        .state(show_history_default_state.to_variant())
        //Дія прив'язується до змінної action, відстежується за її допомогою
        //і перезаписується
        .activate(|_, action, _| {
            let state = action.state().unwrap();
            let action_state: bool = state.get().unwrap();
            let new_state = !action_state; // Натискання на Checkbox
            action.set_state(&new_state.to_variant());
            println!("Показати історію натиснуто")
            })
        .build();

    //Комплексна дія для групи з трьома Radiobutton в розділі Interface language
    let default_language_state: String = "Ukrainian".to_string();
    let interface_languages = gio::ActionEntry::builder("interface_languages")
        //Тип параметру треба вказати для подальшої його обробки
        .parameter_type(Some(&String::static_variant_type()))
        //Стан вказується по аналогії з Checkbox,
        //Radiobutton є, по суті, групою зв'язаних між собою Checkbox
        .state(default_language_state.to_variant())
        //В активації відбувається відстежування зміни параметра
        //та перезапис стану відповідного Radiobutton
        .activate(move |_, action, parameter| {
            // Get parameter
            let parameter = parameter
                .expect("Could not get parameter.")
                .get::<String>()
                .expect("The value needs to be of type `String`.");

            let interface_language = match parameter.as_str() {
                "Ukrainian" => "uk_Ua",
                "English" => "en_Uk",
                "Japanese" => "jp_Ja",
                _ => unreachable!(),
                };

            //Обробка зміненого параметру та перезапис стану Radiobutton
            println!("Натиснуто {}", interface_language);
            action.set_state(&parameter.to_variant());
        })
        .build();

    //Додавання дій до екземпляра програми,
    //щоб вони були доступні в інших функціях
    app.add_action_entries([
        quit,
        about,
        manual,
        website,
        dict_settings,
        clean_search_history,
        show_search_history,
        interface_languages,
    ]);
}

fn create_accelerators(app: &gtk::Application){
    //Створення гарячих клавіш для певних елементів меню
    app.set_accels_for_action("app.quit", &["<Ctrl>Q"] );
    app.set_accels_for_action("app.manual", &["F1"] );
    app.set_accels_for_action("app.dict_settings", &["F2"] );
}

fn create_menubar() -> gio::Menu {
    //Основна функція побудови головного меню
    let file_menu = create_file_menu();
    let help_menu = create_help_menu();
    let settings_menu = create_settings_menu();
    let menubar = gio::Menu::new();
    menubar.append_submenu(Some("File"), &file_menu);
    menubar.append_submenu(Some("Settings"), &settings_menu );
    menubar.append_submenu(Some("Help"), &help_menu);
    menubar
}

fn create_file_menu() ->gio::Menu{
    //Побудова підменю File
    let quit_menu_item = gio::MenuItem::new(Some("Quit"),  Some("app.quit"));
    let file_menu = gio::Menu::new();
    file_menu.append_item(&quit_menu_item);
    file_menu
}

fn create_settings_menu() ->gio::Menu{
    //Побудова підменю Settings

    //Створення кнопки Dictionary settings
    let settings_menu = gio::Menu::new();
    let dict_settings_menu_item = gio::MenuItem::new(
        Some("Dictionary settings"), Some("app.dict_settings") );

    //Створення підменю Search history
    let search_history_menu = gio::Menu::new();
    let clean_search_history_menu_item = gio::MenuItem::new(
        Some("Clean search history"), Some("app.clean_search_history") );
    let show_search_history_menu_item = gio::MenuItem::new(
        Some("Show search history"), Some("app.show_search_history") );
    search_history_menu.append_item(&clean_search_history_menu_item);
    search_history_menu.append_item(&show_search_history_menu_item);

    //Створення підменю Interface language
    let interface_language_menu = gio::Menu::new();
    //Спочатку створюємо кнопку з назвою мови
    let ukr_language_menu_item = gio::MenuItem::new(
        Some("Ukrainian"), Some("app.interface_languages"));
    let eng_language_menu_item = gio::MenuItem::new(
        Some("English"), Some("app.interface_languages") );
    let jap_language_menu_item = gio::MenuItem::new(
        Some("Japanese"), Some("app.interface_languages") );
    //Тепер прив'язуємо дію та параметр до відповідної кнопки з назвою мови
    //Без цього буде лише неактивна кнопка з назвою
    ukr_language_menu_item.set_action_and_target_value(
        Some("app.interface_languages"), Some(&"Ukrainian".to_variant()));
    eng_language_menu_item.set_action_and_target_value(
        Some("app.interface_languages"), Some(&"English".to_variant()) );
    jap_language_menu_item.set_action_and_target_value(
        Some("app.interface_languages"), Some(&"Japanese".to_variant()));
    //Додаємо відповідні кнопки до підменю вибору мов
    interface_language_menu.append_item(&ukr_language_menu_item);
    interface_language_menu.append_item(&eng_language_menu_item);
    interface_language_menu.append_item(&jap_language_menu_item);

    //Тепер додаємо усі створені кнопки й підменю до підменю вищого рівня
    settings_menu.append_submenu(Some("Interface language"), &interface_language_menu);
    settings_menu.append_item(&dict_settings_menu_item);
    settings_menu.append_submenu(Some("Search history"), &search_history_menu );
    settings_menu
}

fn create_help_menu() -> gio::Menu{
    //Створення підменю Help
    let about_menu_item =  gio::MenuItem::new(Some("About"),Some("app.about"));
    let manual_menu_item = gio::MenuItem::new(Some("Manual"), Some("app.manual") );
    let website_menu_item = gio::MenuItem::new(Some("Website"), Some("app.website") );

    let help_menu = gio::Menu::new();
    help_menu.append_item(&about_menu_item);
    help_menu.append_item(&manual_menu_item);
    help_menu.append_item(&website_menu_item);

    help_menu
}

pub fn activate_main_window(application: &gtk::Application) {
    //Створення головного вікна програми та його відображення
    let main_window = gtk::ApplicationWindow::builder()
        .application(application)
        .title("Movar")
        .default_width(800)
        .default_height(600)
        .show_menubar(true)
        .build();

    main_window.present();
}
Подякували: leofun011