1

I'm trying to get the state of a gtk.CheckButton using Action since gtk docs says:

When the code must keep track of the state of a group of radio buttons, it is recommended to keep track of such state through a stateful GAction with a target for each button. Using the toggled signals to keep track of the group changes and state is discouraged.

use gtk::{Application, ApplicationWindow, Button, CheckButton};
use gtk::gio::SimpleAction;
use gtk::Orientation::Vertical;
use gtk::prelude::*;

fn main() {
    let application = Application::builder().application_id("a.b").build();
    application.connect_activate(|application| {
        let main_box = gtk::Box::builder().orientation(Vertical).build();
        let variant_a = "a".to_variant();
        let variant_b = "b".to_variant();
        let window = ApplicationWindow::builder().application(application).child(&main_box).build();
        let action_name = "win.me";
        let action = SimpleAction::new_stateful(action_name, Some(variant_a.type_()), &variant_a);
        window.add_action(&action);
        let button_a = CheckButton::builder().label("a").action_name(action_name)
            .action_target(&variant_a).build();
        main_box.append(&button_a);
        let button_b = CheckButton::builder().label("b").action_name(action_name)
            .action_target(&variant_b).group(&button_a).build();
        main_box.append(&button_b);
        let button = Button::builder().label("click").build();
        action.connect_change_state(|_, state| {
            println!("{:?}", state);
        });
        button.connect_clicked(move |_| {
            println!("{} {:?}", action.name(), action.state());
        });
        main_box.append(&button);
        window.present();
    });
    application.run();
}

However the CheckButtons are not sensitive. Setting sensitive to true makes them sensitive but clicking them won't make them checked.

In another try I used SimpleActionGroup and action-target-value but with the same results:

fn main() {
    let application = Application::builder().application_id("a.b").build();
    application.connect_activate(|application| {
        let main_box = gtk::Box::builder().orientation(Vertical).build();
        let variant_a = "a".to_variant();
        let variant_b = "b".to_variant();
        let window = ApplicationWindow::builder().application(application).child(&main_box).build();
        let action_name = "my.action";
        let action_group = SimpleActionGroup::new();
        window.insert_action_group("my.group", Some(&action_group));
        let action = SimpleAction::new_stateful(action_name, Some(variant_a.type_()), &variant_a);
        action_group.add_action(&action);
        let button_a = CheckButton::builder().label("a").action_name(action_name).build();
        button_a.set_action_target_value(Some(&variant_a));
        main_box.append(&button_a);
        let button_b = CheckButton::builder().label("b").action_name(action_name).group(&button_a).build();
        button_b.set_action_target_value(Some(&variant_b));
        main_box.append(&button_b);
        let button = Button::builder().label("click").build();
        action.connect_activate(|_, state| {
            println!("{:?}", state);
        });
        action.connect_change_state(|_, state| {
            println!("{:?}", state);
        });
        button.connect_clicked(move |_| {
            println!("{} {:?}", action.name(), action.state());
        });
        main_box.append(&button);
        window.present();
    });
    application.run();
}

How do I use Action to get the state of the grouped CheckButtons?

raven
  • 775
  • 6
  • 22

2 Answers2

0

Okay, I'm not a RUST programmer, but your code is missing a few things:

  1. Besides creating a SimplesAction, you also have to create a SimplesActionGroup to put the SimpleAction inside. You cannot add a SimpleAction directly to the window.

  2. You should not use .action_target(&variant...) , but .action_target_value(&variant...)

  3. In the Action's callback function, you have to manually call set_state() to set the "state" of the Action to the value received as a parameter in the callback when the "change_state" signal is triggered, which is the target_value of the clicked button .

The initial state defined in the Action will cause the checkbutton that has this target_value to be rendered as active. When you click on another button, the callback will receive the state of the button and set_state() will set this new state to the Action, which will automatically activate the clicked button, whose target_value corresponds to this new state.

Kripto
  • 46
  • 1
  • 3
  • I've updated the question with your suggestions; However the result is the same. I suspect either the variants or the `action-name` – raven Aug 28 '23 at 12:09
  • You are still not setting the action.state in the action callback. I don't know how to do it in Rust but you have to manually define the action.state in the callback, something like this: ` action.connect_change_state(|_, state| { --> action.set_state(state) <-- println!("{:?}", state); }); ` Otherwise your code looks ok. – Kripto Aug 29 '23 at 13:02
  • I forgot to mention nothing gets printed; meaning none of the callbacks fire. – raven Aug 30 '23 at 14:29
  • I "translated" your code to python and it works. I think the problem is the Action value and the CheckButton value, types and scope in Rust are more restricted. For the code to work, the CheckButton value and the Action value must match. This is how the group knows which CheckButton is active: comparing the value of the CheckButton with the current value of the Action, which is why the Action needs to have an initial value. – Kripto Aug 30 '23 at 15:10
0

you could either use an action group:

let action_group = SimpleActionGroup::new();
window.insert_action_group("gg", Some(&action_group));
action_group.add_action(&action);

and then set the action name: action_name("gg.aa") or directly add the action to the window:

    let application = Application::builder().application_id("a.b").build();
    application.connect_activate(|application| {
        let main_box = gtk::Box::builder().orientation(Vertical).build();
        let variant_a = "a".to_variant();
        let window = ApplicationWindow::builder().application(application).child(&main_box).build();
        let action = SimpleAction::new_stateful("aa", Some(variant_a.type_()), &variant_a);
        window.add_action(&action);
        let button_a = CheckButton::builder().label("a").action_name("win.aa")
            .action_target(&variant_a).build();
        main_box.append(&button_a);
        let button_b = CheckButton::builder().label("b").action_name("win.aa").group(&button_a)
            .action_target(&"b".to_variant()).build();
        main_box.append(&button_b);
        action.connect_activate(|action, state| {
            println!("{:?}", state);
            if let Some(state) = state {
                action.set_state(state);
            }
        });
        window.present();
    });
    application.run();

for more info see gtk4-rs book.

raven
  • 775
  • 6
  • 22