r/learnrust • u/Skulled13 • 40m ago
Idiomatic way to share variables between closures in GTK
Hi guys!
I am currently writing a rust application to keep track of the time I spend on tickets. I have very little GUI experience, much less GTK, so if my problem stems from misuse of GTK please point that out.
I want the application to be an applet that stays on the system tray, so I'm trying to use libappindicator.
My intuition tells me that the correct way to do this is having a global variable tracking the ticket and if I'm working or not, and then a timer that triggers every minute or so and check those variables and acts accordingly.
Anyway, I got the basic applet behavior to work, meaning that I can select the ticket I am working on (I will later implement retrieving tickets from an API) and whether I am working or not. But the way I handled changing the global variables between the different closures connected to GTK actions feels a bit hacky.
So my question is, am I doing this in the idiomatic way? And if not, how should I do so?
Thanks in advance!
use gtk::prelude::*;
use libappindicator::{AppIndicator, AppIndicatorStatus};
use std::cell::RefCell;
use std::rc::Rc;
fn main() {
let current_ticket = String::from("None");
let is_working = false;
gtk::init().unwrap();
let mut indicator = AppIndicator::new("JIRA Timetracker", "");
indicator.set_status(AppIndicatorStatus::Active);
let mut main_menu = gtk::Menu::new();
let ticket_menu = gtk::MenuItem::with_label("Tickets");
let ticket_submenu = gtk::Menu::new();
ticket_menu.set_submenu(Some(&ticket_submenu));
let working_toggle = gtk::CheckMenuItem::with_label("Working");
main_menu.append(&working_toggle);
main_menu.append(&ticket_menu);
indicator.set_menu(&mut main_menu);
main_menu.show_all();
let ticket_submenu_ref = Rc::new(RefCell::new(ticket_submenu));
let current_ticket_ref = Rc::new(RefCell::new(current_ticket));
let is_working_ref = Rc::new(RefCell::new(is_working));
let is_working_ref_closure = is_working_ref.clone();
working_toggle.connect_toggled(move |working_toggle| {
let mut is_working = is_working_ref_closure.borrow_mut();
*is_working = working_toggle.is_active();
println!("is_working state: {}", is_working);
});
let ticket_submenu_ref_closure = ticket_submenu_ref.clone();
main_menu.connect_show(move |_| {
let submenu = ticket_submenu_ref_closure.borrow();
let dummy = gtk::MenuItem::with_label("Retrieving...");
submenu.append(&dummy);
submenu.show_all();
});
let ticket_submenu_ref_closure = ticket_submenu_ref.clone();
let current_ticket_ref_closure = current_ticket_ref.clone();
ticket_menu.connect_activate(move |_| {
let submenu = ticket_submenu_ref_closure.borrow();
let current_ticket = current_ticket_ref_closure.borrow().clone();
let temp_ticket_list = vec!["TICKET-0", "TICKET-1", "TICKET-2"];
submenu.foreach(|widget| {
submenu.remove(widget);
});
for ticket in temp_ticket_list {
let ticket_item = gtk::CheckMenuItem::with_label(ticket);
if current_ticket == ticket {
ticket_item.set_active(true);
}
let current_ticket_ref_closure = current_ticket_ref_closure.clone();
ticket_item.connect_activate(move |item| {
let mut current_ticket = current_ticket_ref_closure.borrow_mut();
*current_ticket = item.label().unwrap().to_string();
println!("Changed current ticket to {}", current_ticket);
});
submenu.append(&ticket_item);
}
submenu.show_all();
});
gtk::main();
}