feat: alert module for simple ok dialogs

This commit is contained in:
Gabriele Musco 2023-08-01 22:36:09 +02:00
commit 4980876622
No known key found for this signature in database
GPG key ID: 1068D795C80E51DE
7 changed files with 72 additions and 85 deletions

17
src/ui/alert.rs Normal file
View file

@ -0,0 +1,17 @@
use gtk::traits::GtkWindowExt;
use relm4::{adw::traits::MessageDialogExt, prelude::*};
pub fn alert(title: &str, msg: Option<&str>, parent: Option<&gtk::Window>) {
let d = adw::MessageDialog::builder()
.modal(true)
.heading(title)
.build();
if msg.is_some() {
d.set_body(msg.unwrap());
}
if parent.is_some() {
d.set_transient_for(parent);
}
d.add_response("ok", "_Ok");
d.present();
}

View file

@ -1,4 +1,5 @@
use super::about_dialog::AboutDialog; use super::about_dialog::AboutDialog;
use super::alert::alert;
use super::build_window::{BuildStatus, BuildWindow}; use super::build_window::{BuildStatus, BuildWindow};
use super::debug_view::{DebugView, DebugViewMsg}; use super::debug_view::{DebugView, DebugViewMsg};
use super::libsurvive_setup_window::LibsurviveSetupWindow; use super::libsurvive_setup_window::LibsurviveSetupWindow;
@ -67,13 +68,9 @@ pub struct App {
#[tracker::do_not_track] #[tracker::do_not_track]
build_window: Controller<BuildWindow>, build_window: Controller<BuildWindow>,
#[tracker::do_not_track] #[tracker::do_not_track]
dependencies_dialog: adw::MessageDialog,
#[tracker::do_not_track]
setcap_confirm_dialog: adw::MessageDialog, setcap_confirm_dialog: adw::MessageDialog,
#[tracker::do_not_track] #[tracker::do_not_track]
libsurvive_setup_window: Controller<LibsurviveSetupWindow>, libsurvive_setup_window: Controller<LibsurviveSetupWindow>,
#[tracker::do_not_track]
profile_runner_failed_dialog: adw::MessageDialog,
#[tracker::do_not_track] #[tracker::do_not_track]
config: Config, config: Config,
@ -150,7 +147,14 @@ impl App {
)); ));
} }
Err(_) => { Err(_) => {
self.profile_runner_failed_dialog.present(); alert(
"Failed to start profile",
Some(concat!(
"You need to build the current profile before starting it.",
"\n\nYou can do this from the menu."
)),
Some(&self.app_win.clone().upcast::<gtk::Window>()),
);
} }
}; };
} }
@ -395,15 +399,18 @@ impl SimpleComponent for App {
if !missing_deps.is_empty() { if !missing_deps.is_empty() {
missing_deps.sort_unstable(); missing_deps.sort_unstable();
missing_deps.dedup(); // dedup only works if sorted, hence the above missing_deps.dedup(); // dedup only works if sorted, hence the above
self.dependencies_dialog.set_body( alert(
"Missing dependencies:",
Some(
missing_deps missing_deps
.iter() .iter()
.map(|dep| dep.name.clone()) .map(|dep| dep.name.clone())
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join(", ") .join(", ")
.as_str(), .as_str(),
),
Some(&self.app_win.clone().upcast::<gtk::Window>()),
); );
self.dependencies_dialog.present();
return; return;
} }
self.build_window self.build_window
@ -497,7 +504,10 @@ impl SimpleComponent for App {
self.config.save(); self.config.save();
} }
Msg::Quit => { Msg::Quit => {
sender.input(Msg::SaveWinSize(self.app_win.width(), self.app_win.height())); sender.input(Msg::SaveWinSize(
self.app_win.width(),
self.app_win.height(),
));
self.application.quit(); self.application.quit();
} }
} }
@ -511,13 +521,6 @@ impl SimpleComponent for App {
let config = Config::get_config(); let config = Config::get_config();
let win_size = config.win_size.clone(); let win_size = config.win_size.clone();
let profiles = Self::profiles_list(&config); let profiles = Self::profiles_list(&config);
let dependencies_dialog = adw::MessageDialog::builder()
.modal(true)
.transient_for(root)
.heading("Missing dependencies:")
.hide_on_close(true)
.build();
dependencies_dialog.add_response("ok", "_Ok");
let setcap_confirm_dialog = adw::MessageDialog::builder() let setcap_confirm_dialog = adw::MessageDialog::builder()
.modal(true) .modal(true)
.transient_for(root) .transient_for(root)
@ -543,18 +546,6 @@ impl SimpleComponent for App {
}); });
} }
let profile_runner_failed_dialog = adw::MessageDialog::builder()
.modal(true)
.transient_for(root)
.heading("Failed to start profile")
.body(concat!(
"You need to build the current profile before starting it.",
"\n\nYou can do this from the menu."
))
.hide_on_close(true)
.build();
profile_runner_failed_dialog.add_response("ok", "_Ok");
let model = App { let model = App {
application: init.application, application: init.application,
app_win: root.clone(), app_win: root.clone(),
@ -592,9 +583,7 @@ impl SimpleComponent for App {
.transient_for(root) .transient_for(root)
.launch(()) .launch(())
.detach(), .detach(),
dependencies_dialog,
setcap_confirm_dialog, setcap_confirm_dialog,
profile_runner_failed_dialog,
enable_debug_view: config.debug_view_enabled, enable_debug_view: config.debug_view_enabled,
config, config,
tracker: 0, tracker: 0,

View file

@ -10,12 +10,13 @@ use crate::{
use gtk::prelude::*; use gtk::prelude::*;
use relm4::{ use relm4::{
actions::{ActionGroupName, RelmAction, RelmActionGroup}, actions::{ActionGroupName, RelmAction, RelmActionGroup},
adw::traits::MessageDialogExt,
new_action_group, new_stateless_action, new_action_group, new_stateless_action,
prelude::*, prelude::*,
}; };
use std::thread::JoinHandle; use std::thread::JoinHandle;
use super::alert::alert;
#[derive(PartialEq, Eq, Debug, Clone)] #[derive(PartialEq, Eq, Debug, Clone)]
pub enum InstallWivrnStatus { pub enum InstallWivrnStatus {
Success, Success,
@ -32,7 +33,7 @@ pub struct InstallWivrnBox {
#[tracker::do_not_track] #[tracker::do_not_track]
install_runner: Option<Runner>, install_runner: Option<Runner>,
#[tracker::do_not_track] #[tracker::do_not_track]
adb_missing_dialog: adw::MessageDialog, root_win: gtk::Window,
} }
#[derive(Debug)] #[derive(Debug)]
@ -188,7 +189,7 @@ impl SimpleComponent for InstallWivrnBox {
} }
Self::Input::DownloadWivrn(link) => { Self::Input::DownloadWivrn(link) => {
if !check_dependency(adb_dep()) { if !check_dependency(adb_dep()) {
self.adb_missing_dialog.present(); alert("ADB is not installed", Some("Please install ADB on your computer to install WiVRn on your Android headset"), Some(&self.root_win));
} else { } else {
self.set_install_wivrn_status(InstallWivrnStatus::InProgress); self.set_install_wivrn_status(InstallWivrnStatus::InProgress);
self.download_thread = Some(download_file(link, wivrn_apk_download_path())); self.download_thread = Some(download_file(link, wivrn_apk_download_path()));
@ -210,21 +211,12 @@ impl SimpleComponent for InstallWivrnBox {
root: &Self::Root, root: &Self::Root,
sender: ComponentSender<Self>, sender: ComponentSender<Self>,
) -> ComponentParts<Self> { ) -> ComponentParts<Self> {
let adb_missing_dialog = adw::MessageDialog::builder()
.modal(true)
.transient_for(&init.root_win)
.heading("ADB is not installed")
.body("Please install ADB on your computer to install WiVRn on your Android headset")
.hide_on_close(true)
.build();
adb_missing_dialog.add_response("ok", "_Ok");
let model = Self { let model = Self {
selected_profile: init.selected_profile, selected_profile: init.selected_profile,
install_wivrn_status: InstallWivrnStatus::Done(None), install_wivrn_status: InstallWivrnStatus::Done(None),
download_thread: None, download_thread: None,
install_runner: None, install_runner: None,
adb_missing_dialog, root_win: init.root_win,
tracker: 0, tracker: 0,
}; };

View file

@ -1,9 +1,11 @@
use crate::{profile::Profile, runner::Runner}; use crate::{profile::Profile, runner::Runner};
use gtk::{glib, prelude::*};
use relm4::prelude::*;
use adw::prelude::*; use adw::prelude::*;
use gtk::glib;
use relm4::prelude::*;
use std::{cell::Cell, collections::HashMap, path::Path, rc::Rc, time::Duration}; use std::{cell::Cell, collections::HashMap, path::Path, rc::Rc, time::Duration};
use super::alert::alert;
const NO_FILE_MSG: &str = "(No file selected)"; const NO_FILE_MSG: &str = "(No file selected)";
const CALIBRATION_RUN_TIME_SECONDS: f64 = 30.0; const CALIBRATION_RUN_TIME_SECONDS: f64 = 30.0;
@ -28,9 +30,6 @@ pub struct LibsurviveSetupWindow {
#[tracker::do_not_track] #[tracker::do_not_track]
filefilter_listmodel: gtk::gio::ListStore, filefilter_listmodel: gtk::gio::ListStore,
#[tracker::do_not_track]
survive_cli_not_found_dialog: adw::MessageDialog,
#[tracker::do_not_track] #[tracker::do_not_track]
calibration_running: Rc<Cell<bool>>, calibration_running: Rc<Cell<bool>>,
#[tracker::do_not_track] #[tracker::do_not_track]
@ -388,7 +387,18 @@ impl SimpleComponent for LibsurviveSetupWindow {
.unwrap() .unwrap()
.scroll_to(self.loading_page.as_ref().unwrap(), true); .scroll_to(self.loading_page.as_ref().unwrap(), true);
} else { } else {
self.survive_cli_not_found_dialog.present(); let parent = match self.win.as_ref() {
None => None,
Some(w) => Some(w.clone().upcast::<gtk::Window>()),
};
alert(
"Survive CLI not found",
Some(concat!(
"You might need to build this profile first, ",
"or maybe Libsurvive isn't enabled for this profile"
)),
parent.as_ref(),
);
} }
} }
} }
@ -434,7 +444,11 @@ impl SimpleComponent for LibsurviveSetupWindow {
let runner = self.calibration_runner.as_mut().unwrap(); let runner = self.calibration_runner.as_mut().unwrap();
runner.terminate(); runner.terminate();
let mut n_runner = self.create_calibration_runner( let mut n_runner = self.create_calibration_runner(
self.profile.as_ref().unwrap().get_survive_cli_path().unwrap(), self.profile
.as_ref()
.unwrap()
.get_survive_cli_path()
.unwrap(),
); );
n_runner.start(); n_runner.start();
self.calibration_runner = Some(n_runner); self.calibration_runner = Some(n_runner);
@ -478,17 +492,6 @@ impl SimpleComponent for LibsurviveSetupWindow {
let json_filter = gtk::FileFilter::new(); let json_filter = gtk::FileFilter::new();
json_filter.add_mime_type("application/json"); json_filter.add_mime_type("application/json");
let survive_cli_not_found_dialog = adw::MessageDialog::builder()
.modal(true)
.hide_on_close(true)
.heading("Survive CLI not found")
.body(concat!(
"You might need to build this profile first, ",
"or maybe Libsurvive isn't enabled for this profile"
))
.build();
survive_cli_not_found_dialog.add_response("ok", "_Ok");
let mut model = LibsurviveSetupWindow { let mut model = LibsurviveSetupWindow {
win: None, win: None,
progressbar: None, progressbar: None,
@ -506,7 +509,6 @@ impl SimpleComponent for LibsurviveSetupWindow {
calibration_running: Rc::new(Cell::new(false)), calibration_running: Rc::new(Cell::new(false)),
first_run_done: false, first_run_done: false,
calibration_seconds_elapsed: 0.0, calibration_seconds_elapsed: 0.0,
survive_cli_not_found_dialog,
tracker: 0, tracker: 0,
}; };
@ -515,9 +517,6 @@ impl SimpleComponent for LibsurviveSetupWindow {
let widgets = view_output!(); let widgets = view_output!();
model.win = Some(widgets.win.clone()); model.win = Some(widgets.win.clone());
model
.survive_cli_not_found_dialog
.set_transient_for(Some(model.win.as_ref().unwrap()));
model.progressbar = Some(widgets.progressbar.clone()); model.progressbar = Some(widgets.progressbar.clone());
model.carousel = Some(widgets.carousel.clone()); model.carousel = Some(widgets.carousel.clone());
model.loading_page = Some(widgets.loading_page.clone()); model.loading_page = Some(widgets.loading_page.clone());

View file

@ -1,10 +1,11 @@
use super::alert::alert;
use super::devices_box::{DevicesBox, DevicesBoxMsg}; use super::devices_box::{DevicesBox, DevicesBoxMsg};
use super::install_wivrn_box::{InstallWivrnBox, InstallWivrnBoxInit, InstallWivrnBoxMsg}; use super::install_wivrn_box::{InstallWivrnBox, InstallWivrnBoxInit, InstallWivrnBoxMsg};
use super::profile_editor::{ProfileEditor, ProfileEditorMsg, ProfileEditorOutMsg}; use super::profile_editor::{ProfileEditor, ProfileEditorMsg, ProfileEditorOutMsg};
use super::steam_launch_options_box::{SteamLaunchOptionsBox, SteamLaunchOptionsBoxMsg}; use super::steam_launch_options_box::{SteamLaunchOptionsBox, SteamLaunchOptionsBoxMsg};
use crate::config::Config; use crate::config::Config;
use crate::constants::APP_NAME; use crate::constants::APP_NAME;
use crate::profile::{Profile, XRServiceType}; use crate::profile::Profile;
use crate::ui::app::{ use crate::ui::app::{
AboutAction, BuildProfileAction, BuildProfileCleanAction, DebugViewToggleAction, AboutAction, BuildProfileAction, BuildProfileCleanAction, DebugViewToggleAction,
LibsurviveSetupAction, LibsurviveSetupAction,
@ -37,8 +38,6 @@ pub struct MainView {
#[tracker::do_not_track] #[tracker::do_not_track]
profile_delete_confirm_dialog: adw::MessageDialog, profile_delete_confirm_dialog: adw::MessageDialog,
#[tracker::do_not_track] #[tracker::do_not_track]
cannot_duplicate_profile_dialog: adw::MessageDialog,
#[tracker::do_not_track]
profile_editor: Option<Controller<ProfileEditor>>, profile_editor: Option<Controller<ProfileEditor>>,
#[tracker::do_not_track] #[tracker::do_not_track]
root_win: gtk::Window, root_win: gtk::Window,
@ -368,7 +367,7 @@ impl SimpleComponent for MainView {
.sender() .sender()
.emit(ProfileEditorMsg::Present); .emit(ProfileEditorMsg::Present);
} else { } else {
self.cannot_duplicate_profile_dialog.present(); alert("This profile cannot be duplicated", None, Some(&self.root_win));
} }
} }
Self::Input::UpdateDevices(devs) => self Self::Input::UpdateDevices(devs) => self
@ -428,14 +427,6 @@ impl SimpleComponent for MainView {
}); });
} }
let cannot_duplicate_profile_dialog = adw::MessageDialog::builder()
.modal(true)
.transient_for(&init.root_win)
.hide_on_close(true)
.heading("This profile cannot be duplicated")
.build();
cannot_duplicate_profile_dialog.add_response("ok", "_Ok");
let mut model = Self { let mut model = Self {
xrservice_active: false, xrservice_active: false,
enable_debug_view: init.config.debug_view_enabled, enable_debug_view: init.config.debug_view_enabled,
@ -452,7 +443,6 @@ impl SimpleComponent for MainView {
selected_profile: init.selected_profile.clone(), selected_profile: init.selected_profile.clone(),
profile_not_editable_dialog, profile_not_editable_dialog,
profile_delete_confirm_dialog, profile_delete_confirm_dialog,
cannot_duplicate_profile_dialog,
root_win: init.root_win.clone(), root_win: init.root_win.clone(),
profile_editor: None, profile_editor: None,
tracker: 0, tracker: 0,

View file

@ -13,3 +13,4 @@ pub mod devices_box;
pub mod preference_rows; pub mod preference_rows;
pub mod macros; pub mod macros;
pub mod util; pub mod util;
pub mod alert;

View file

@ -9,7 +9,6 @@ use crate::{
withclones, withclones,
}; };
use adw::prelude::*; use adw::prelude::*;
use gtk::prelude::*;
use relm4::{factory::FactoryVecDeque, prelude::*}; use relm4::{factory::FactoryVecDeque, prelude::*};
use std::{cell::RefCell, rc::Rc}; use std::{cell::RefCell, rc::Rc};