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(¶m.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();
}