mirror of
https://gitlab.com/gabmus/envision.git
synced 2025-09-11 20:15:44 +00:00
feat: alert module for simple ok dialogs
This commit is contained in:
parent
504e945ba5
commit
4980876622
7 changed files with 72 additions and 85 deletions
17
src/ui/alert.rs
Normal file
17
src/ui/alert.rs
Normal 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<>k::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();
|
||||||
|
}
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue