diff --git a/src/styles.css b/assets/styles.css similarity index 100% rename from src/styles.css rename to assets/styles.css diff --git a/src/main.rs b/src/main.rs index b0e8326..3cdc125 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,7 +24,7 @@ fn main() { // Apply CSS styles to the application let provider = CssProvider::new(); - provider.load_from_data(include_bytes!("styles.css")); + provider.load_from_data(include_bytes!("../assets/styles.css")); StyleContext::add_provider_for_display( &Display::default().expect("Could not connect to a display"), diff --git a/src/ui/components/dxvk_row.rs b/src/ui/components/dxvk_row.rs new file mode 100644 index 0000000..e0e2e11 --- /dev/null +++ b/src/ui/components/dxvk_row.rs @@ -0,0 +1,78 @@ +use gtk4::{self as gtk, prelude::*}; +use libadwaita::{self as adw, prelude::*}; + +use gtk::glib; +use gtk::Align; + +use crate::lib::dxvk::Version; + +#[derive(Debug, Clone)] +pub struct DxvkRow { + pub version: Version, + + pub row: adw::ActionRow, + pub button: gtk::Button, + pub progress_bar: gtk::ProgressBar +} + +impl DxvkRow { + pub fn new(version: Version) -> Self { + let row = adw::ActionRow::new(); + let button = gtk::Button::new(); + + row.set_title(&version.version); + row.set_visible(version.recommended); + + button.set_icon_name("document-save-symbolic"); + button.set_valign(gtk::Align::Center); + button.add_css_class("flat"); + + row.add_suffix(&button); + + let progress_bar = gtk::ProgressBar::new(); + + progress_bar.set_text(Some("Downloading: 0%")); + progress_bar.set_show_text(true); + + progress_bar.set_width_request(200); + progress_bar.set_valign(Align::Center); + progress_bar.set_visible(false); + + row.add_suffix(&progress_bar); + + Self { + version, + row, + button, + progress_bar + } + } + + pub fn download(&self) { + let (sender, receiver) = glib::MainContext::channel::(glib::PRIORITY_DEFAULT); + let this = self.clone(); + + this.progress_bar.set_visible(true); + this.button.set_visible(false); + + receiver.attach(None, move |fraction| { + this.progress_bar.set_fraction(fraction as f64 / 100f64); + this.progress_bar.set_text(Some(&format!("Downloading: {}%", fraction))); + + if fraction == 100 { + this.progress_bar.set_visible(false); + this.button.set_visible(true); + } + + glib::Continue(true) + }); + + std::thread::spawn(move || { + for i in 1..101 { + std::thread::sleep(std::time::Duration::from_millis(150)); + + sender.send(i); + } + }); + } +} diff --git a/src/ui/components/mod.rs b/src/ui/components/mod.rs new file mode 100644 index 0000000..6fbcd3c --- /dev/null +++ b/src/ui/components/mod.rs @@ -0,0 +1 @@ +pub mod dxvk_row; diff --git a/src/ui/main.rs b/src/ui/main.rs index 578cac6..d9e7eec 100644 --- a/src/ui/main.rs +++ b/src/ui/main.rs @@ -147,7 +147,10 @@ impl App { pub fn init_actions(self) -> Self { let (sender, receiver) = glib::MainContext::channel::(glib::PRIORITY_DEFAULT); - receiver.attach(None, clone!(@strong self as this => move |action| { + // I prefer to avoid using clone! here because it breaks my code autocompletion + let this = self.clone(); + + receiver.attach(None, move |action| { let values = this.values.take(); // Some debug output @@ -156,10 +159,16 @@ impl App { match action { Actions::OpenPreferencesPage => { this.widgets.leaflet.set_visible_child_name("preferences_page"); - + if let Err(err) = this.widgets.preferences_stack.update() { this.toast_error("Failed to update preferences", err); } + + /*tokio::task::spawn(async { + if let Err(err) = this.widgets.preferences_stack.update().await { + // this.update(Actions::ToastError(Rc::new((String::from("Failed to update preferences"), err)))); + } + });*/ } Actions::PreferencesGoBack => { @@ -177,7 +186,7 @@ impl App { this.values.set(values); glib::Continue(true) - })); + }); self.actions.set(Some(sender)); @@ -210,6 +219,9 @@ impl ToastError for App { } } +unsafe impl Send for App {} +unsafe impl Sync for App {} + /* pub enum AppState { Launch, diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 717f852..fc37435 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -5,6 +5,8 @@ mod main; mod preferences; mod toast_error; +pub mod components; + pub use main::{ App as MainApp, // AppState as MainAppState, diff --git a/src/ui/preferences/enhanced_page.rs b/src/ui/preferences/enhanced_page.rs index 1ea2528..ab6e73c 100644 --- a/src/ui/preferences/enhanced_page.rs +++ b/src/ui/preferences/enhanced_page.rs @@ -198,3 +198,6 @@ impl App { Ok(()) } } + +unsafe impl Send for App {} +unsafe impl Sync for App {} diff --git a/src/ui/preferences/general_page.rs b/src/ui/preferences/general_page.rs index 32015d0..edf0f33 100644 --- a/src/ui/preferences/general_page.rs +++ b/src/ui/preferences/general_page.rs @@ -15,6 +15,8 @@ use crate::ui::get_object; use crate::lib::config; use crate::lib::dxvk; +use crate::ui::components::dxvk_row::DxvkRow; + /// This structure is used to describe widgets used in application /// /// `AppWidgets::try_get` function loads UI file from `.assets/ui/.dist` folder and returns structure with references to its widgets @@ -31,7 +33,7 @@ pub struct AppWidgets { pub dxvk_vanilla: adw::ExpanderRow, pub dxvk_async: adw::ExpanderRow, - pub dxvk_components: Rc> + pub dxvk_components: Rc> } impl AppWidgets { @@ -61,25 +63,15 @@ impl AppWidgets { for (i, versions) in [list.vanilla, list.r#async].into_iter().enumerate() { for version in versions { - let row = adw::ActionRow::new(); - let button = gtk::Button::new(); - - row.set_title(&version.version); - row.set_visible(version.recommended); - - button.set_icon_name("document-save-symbolic"); - button.set_valign(gtk::Align::Center); - button.add_css_class("flat"); - - row.add_suffix(&button); + let row = DxvkRow::new(version); match i { - 0 => result.dxvk_vanilla.add_row(&row), - 1 => result.dxvk_async.add_row(&row), + 0 => result.dxvk_vanilla.add_row(&row.row), + 1 => result.dxvk_async.add_row(&row.row), _ => () } - components.push((row, button, version)); + components.push(row); } } @@ -92,15 +84,15 @@ impl AppWidgets { /// This enum is used to describe an action inside of this application /// /// It may be helpful if you want to add the same event for several widgets, or call an action inside of another action -#[derive(Debug, glib::Downgrade)] +#[derive(Debug, Clone, glib::Downgrade)] pub enum Actions { DownloadDXVK(Rc) } impl Actions { pub fn into_fn>(&self, app: &App) -> Box { - Box::new(clone!(@weak self as action, @strong app => move |_| { - app.update(action); + Box::new(clone!(@strong self as action, @strong app => move |_| { + app.update(action.clone()); })) } } @@ -147,9 +139,9 @@ impl App { fn init_events(self) -> Self { // Set DXVK recommended only switcher event self.widgets.dxvk_recommended_only.connect_state_notify(clone!(@strong self as this => move |switcher| { - for (component, _, version) in &*this.widgets.dxvk_components { - component.set_visible(if switcher.state() { - version.recommended + for component in &*this.widgets.dxvk_components { + component.row.set_visible(if switcher.state() { + component.version.recommended } else { true }); @@ -157,10 +149,10 @@ impl App { })); // DXVK install/remove buttons - let components = (*self.widgets.dxvk_components).clone(); + let components = &*self.widgets.dxvk_components; - for (i, (_, button, _)) in components.into_iter().enumerate() { - button.connect_clicked(Actions::DownloadDXVK(Rc::new(i)).into_fn(&self)); + for (i, component) in components.into_iter().enumerate() { + component.button.connect_clicked(Actions::DownloadDXVK(Rc::new(i)).into_fn(&self)); } self @@ -172,7 +164,10 @@ impl App { pub fn init_actions(self) -> Self { let (sender, receiver) = glib::MainContext::channel::(glib::PRIORITY_DEFAULT); - receiver.attach(None, clone!(@strong self as this => move |action| { + // I prefer to avoid using clone! here because it breaks my code autocompletion + let this = self.clone(); + + receiver.attach(None, move |action| { let values = this.values.take(); // Some debug output @@ -180,31 +175,18 @@ impl App { match action { Actions::DownloadDXVK(i) => { - println!("123"); + let component = &this.widgets.dxvk_components[*i]; - let (row, button, version) = &this.widgets.dxvk_components[*i]; - - let progress_bar = gtk::ProgressBar::new(); - - progress_bar.set_text(Some("Downloading: 0%")); - progress_bar.set_show_text(true); - - progress_bar.set_width_request(200); - progress_bar.set_valign(Align::Center); - - button.set_visible(false); - - row.add_suffix(&progress_bar); - - row.remove(&progress_bar); - button.set_visible(true); + println!("Download DXVK: {:?}", &component.version); + + component.download(); } } this.values.set(values); glib::Continue(true) - })); + }); self.actions.set(Some(sender)); @@ -305,3 +287,4 @@ impl App { } unsafe impl Send for App {} +unsafe impl Sync for App {} diff --git a/src/ui/preferences/mod.rs b/src/ui/preferences/mod.rs index 8c400cc..a81bc85 100644 --- a/src/ui/preferences/mod.rs +++ b/src/ui/preferences/mod.rs @@ -1,7 +1,7 @@ use gtk4::{self as gtk, prelude::*}; use libadwaita::{self as adw, prelude::*}; -use gtk4::glib; +use gtk::glib; use std::io::Error; @@ -84,3 +84,6 @@ impl ToastError for PreferencesStack { (self.window.clone(), self.toast_overlay.clone()) } } + +unsafe impl Send for PreferencesStack {} +unsafe impl Sync for PreferencesStack {} diff --git a/src/ui/toast_error.rs b/src/ui/toast_error.rs index a45a07a..eca0805 100644 --- a/src/ui/toast_error.rs +++ b/src/ui/toast_error.rs @@ -9,8 +9,8 @@ pub trait ToastError { /// Show toast with `toast` title and `See message` button /// /// This button will show message dialog with error message - fn toast_error(&self, toast: &str, err: T) { - let toast = adw::Toast::new(toast); + fn toast_error(&self, toast: T, err: F) { + let toast = adw::Toast::new(toast.to_string().as_str()); toast.set_button_label(Some("See message")); toast.set_action_name(Some("see-message.see-message")); @@ -25,7 +25,7 @@ pub trait ToastError { gtk::DialogFlags::all(), gtk::MessageType::Info, gtk::ButtonsType::Close, - &err.to_string() + &format!("{}", err) ); dialog.connect_response(move |dialog, _| {