keyboard focus with GTK4 Rust does not work as expected and how to get state

16 views Asked by At

The following Rust program compiles and runs on Linux Mint, using GTK4 version 4.6. However it does not behave as expected and I have an additional question. It has 3 check buttons high, medium, low, that I want to activate either with the mouse or with the keyboard. It works fine with the mouse, but it does not work with keyboard. First when I click on a level button, I want the keyboard focus move to that button. Even though I grab focus, it does not do it! Second when I hit return on a button, it does not activate. Strange, when I click three times with the mouse, then the keyboard focus finally moves!

Next, I have another question. When I click on the Doit button I want to get the level. But I cannot access the state of the action, because an ActionEntry cannot be cloned... So what should I do?

use std::process::exit;
use gtk4 as gtk;
use gtk::prelude::{ToVariant, StaticVariantType, GtkWindowExt,
               WidgetExt, ActionExt, ButtonExt, BoxExt, CheckButtonExt,
               ActionMapExtManual, ApplicationExt, ApplicationExtManual};
//use gtk::gio::{SimpleAction, SimpleActionGroup};
use gtk::{gio,  Application, ApplicationWindow, Button, Orientation,
      ListBox, SelectionMode, CheckButton, ActionBar};
use gio::{ActionEntry};

const APP_ID: &str = "org.gtk_rs.my_guitest";

fn build_button(group: Option<&CheckButton>, lab: &str, param: i32) -> CheckButton {
let label = lab.to_owned();
let mut builder = CheckButton::builder().label(label.clone())
    .can_focus(true).focus_on_click(true);
if let Some(button) = group {
    builder = builder.group(button)
}
let button = builder.build();
button.connect_toggled(move |button| {
    if button.is_active() {
        button.activate_action("win.level", Some(&param.to_variant()))
            .expect("Action not found!");
        button.grab_focus();
    }
});
button
}


fn build_ui(app: &Application) {
let window=ApplicationWindow::builder()
    .application(app)
    .title("GTK Test")
    .default_width(400)
    .default_height(500)
    .build();
// Container to contain everthing
let container = gtk::Box::builder()
    .orientation(Orientation::Vertical)
    .build();
window.set_child(Some(&container));
// build listbox with three buttons for options
let t1 = build_button(None, "High", 1);
let t2 = build_button(Some(&t1), "Medium", 2);
let t3 = build_button(Some(&t1), "Low", 3);
let option_list = ListBox::builder().name("OptionList")
    .selection_mode(SelectionMode::Single)
    .margin_start(40).margin_end(40)
    .margin_top(12).margin_bottom(12)
    .build();

option_list.append(&t1);
option_list.append(&t2);
option_list.append(&t3);
container.append(&option_list);
// Build action
let select = ActionEntry::builder("level")
    .parameter_type(Some(&i32::static_variant_type()))
    .state(0.to_variant())
    .activate(move |_, action, param| {
       let current = action.state().expect("no state")
           .get::<i32>().expect("variant not i32");
        let new = param.expect("no parameter")
            .get::<i32>().expect("arg is not i32");
        action.set_state(&new.to_variant());
        println!("Level changed from {} to {}", current, new);
    })
    .build();
window.add_action_entries([select]);

// Build Action bar with 2 buttons Doit and Quit
let doit = Button::builder().label("Do Something")
    .margin_start(12).margin_end(12)
    .margin_top(12).margin_bottom(12)
    .build();
// How to get selected level ?
doit.connect_clicked(move |_| {
    let s =0;
    //This does not compile
    //    if let Some(s) = select.state() {println!("The action state is {}",s);}
    println!("The action state is {}",s);
}  );

let quit = Button::builder().label("Quit")
    .margin_start(12).margin_end(12)
    .margin_top(12).margin_bottom(12)
    .build();
quit.connect_clicked(move |_| {exit(0)});

let action_bar = ActionBar::builder()
    .margin_start(12).margin_end(12)
    .margin_top(12).margin_bottom(12)
    .name("Action")
    .build();
action_bar.pack_start(&doit);
action_bar.pack_end(&quit);
container.append(&action_bar);

// Present window
window.present();
}

fn main() {
let app = Application::builder().application_id(APP_ID).build();
app.connect_activate(build_ui);
app.run();
}
0

There are 0 answers