Added DxvkRow component, made example downloading progress

This commit is contained in:
Observer KRypt0n_ 2022-07-10 11:55:49 +02:00
parent fd6e729cd4
commit 9817cbd989
No known key found for this signature in database
GPG key ID: 844DA47BA25FE1E2
10 changed files with 133 additions and 51 deletions

View file

@ -24,7 +24,7 @@ fn main() {
// Apply CSS styles to the application // Apply CSS styles to the application
let provider = CssProvider::new(); 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( StyleContext::add_provider_for_display(
&Display::default().expect("Could not connect to a display"), &Display::default().expect("Could not connect to a display"),

View file

@ -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::<i32>(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);
}
});
}
}

1
src/ui/components/mod.rs Normal file
View file

@ -0,0 +1 @@
pub mod dxvk_row;

View file

@ -147,7 +147,10 @@ impl App {
pub fn init_actions(self) -> Self { pub fn init_actions(self) -> Self {
let (sender, receiver) = glib::MainContext::channel::<Actions>(glib::PRIORITY_DEFAULT); let (sender, receiver) = glib::MainContext::channel::<Actions>(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(); let values = this.values.take();
// Some debug output // Some debug output
@ -160,6 +163,12 @@ impl App {
if let Err(err) = this.widgets.preferences_stack.update() { if let Err(err) = this.widgets.preferences_stack.update() {
this.toast_error("Failed to update preferences", err); 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 => { Actions::PreferencesGoBack => {
@ -177,7 +186,7 @@ impl App {
this.values.set(values); this.values.set(values);
glib::Continue(true) glib::Continue(true)
})); });
self.actions.set(Some(sender)); 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 { pub enum AppState {
Launch, Launch,

View file

@ -5,6 +5,8 @@ mod main;
mod preferences; mod preferences;
mod toast_error; mod toast_error;
pub mod components;
pub use main::{ pub use main::{
App as MainApp, App as MainApp,
// AppState as MainAppState, // AppState as MainAppState,

View file

@ -198,3 +198,6 @@ impl App {
Ok(()) Ok(())
} }
} }
unsafe impl Send for App {}
unsafe impl Sync for App {}

View file

@ -15,6 +15,8 @@ use crate::ui::get_object;
use crate::lib::config; use crate::lib::config;
use crate::lib::dxvk; use crate::lib::dxvk;
use crate::ui::components::dxvk_row::DxvkRow;
/// This structure is used to describe widgets used in application /// 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 /// `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_vanilla: adw::ExpanderRow,
pub dxvk_async: adw::ExpanderRow, pub dxvk_async: adw::ExpanderRow,
pub dxvk_components: Rc<Vec<(adw::ActionRow, gtk::Button, dxvk::Version)>> pub dxvk_components: Rc<Vec<DxvkRow>>
} }
impl AppWidgets { impl AppWidgets {
@ -61,25 +63,15 @@ impl AppWidgets {
for (i, versions) in [list.vanilla, list.r#async].into_iter().enumerate() { for (i, versions) in [list.vanilla, list.r#async].into_iter().enumerate() {
for version in versions { for version in versions {
let row = adw::ActionRow::new(); let row = DxvkRow::new(version);
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);
match i { match i {
0 => result.dxvk_vanilla.add_row(&row), 0 => result.dxvk_vanilla.add_row(&row.row),
1 => result.dxvk_async.add_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 /// 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 /// 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 { pub enum Actions {
DownloadDXVK(Rc<usize>) DownloadDXVK(Rc<usize>)
} }
impl Actions { impl Actions {
pub fn into_fn<T: gtk::glib::IsA<gtk::Widget>>(&self, app: &App) -> Box<dyn Fn(&T)> { pub fn into_fn<T: gtk::glib::IsA<gtk::Widget>>(&self, app: &App) -> Box<dyn Fn(&T)> {
Box::new(clone!(@weak self as action, @strong app => move |_| { Box::new(clone!(@strong self as action, @strong app => move |_| {
app.update(action); app.update(action.clone());
})) }))
} }
} }
@ -147,9 +139,9 @@ impl App {
fn init_events(self) -> Self { fn init_events(self) -> Self {
// Set DXVK recommended only switcher event // Set DXVK recommended only switcher event
self.widgets.dxvk_recommended_only.connect_state_notify(clone!(@strong self as this => move |switcher| { self.widgets.dxvk_recommended_only.connect_state_notify(clone!(@strong self as this => move |switcher| {
for (component, _, version) in &*this.widgets.dxvk_components { for component in &*this.widgets.dxvk_components {
component.set_visible(if switcher.state() { component.row.set_visible(if switcher.state() {
version.recommended component.version.recommended
} else { } else {
true true
}); });
@ -157,10 +149,10 @@ impl App {
})); }));
// DXVK install/remove buttons // 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() { for (i, component) in components.into_iter().enumerate() {
button.connect_clicked(Actions::DownloadDXVK(Rc::new(i)).into_fn(&self)); component.button.connect_clicked(Actions::DownloadDXVK(Rc::new(i)).into_fn(&self));
} }
self self
@ -172,7 +164,10 @@ impl App {
pub fn init_actions(self) -> Self { pub fn init_actions(self) -> Self {
let (sender, receiver) = glib::MainContext::channel::<Actions>(glib::PRIORITY_DEFAULT); let (sender, receiver) = glib::MainContext::channel::<Actions>(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(); let values = this.values.take();
// Some debug output // Some debug output
@ -180,31 +175,18 @@ impl App {
match action { match action {
Actions::DownloadDXVK(i) => { Actions::DownloadDXVK(i) => {
println!("123"); let component = &this.widgets.dxvk_components[*i];
let (row, button, version) = &this.widgets.dxvk_components[*i]; println!("Download DXVK: {:?}", &component.version);
let progress_bar = gtk::ProgressBar::new(); component.download();
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);
} }
} }
this.values.set(values); this.values.set(values);
glib::Continue(true) glib::Continue(true)
})); });
self.actions.set(Some(sender)); self.actions.set(Some(sender));
@ -305,3 +287,4 @@ impl App {
} }
unsafe impl Send for App {} unsafe impl Send for App {}
unsafe impl Sync for App {}

View file

@ -1,7 +1,7 @@
use gtk4::{self as gtk, prelude::*}; use gtk4::{self as gtk, prelude::*};
use libadwaita::{self as adw, prelude::*}; use libadwaita::{self as adw, prelude::*};
use gtk4::glib; use gtk::glib;
use std::io::Error; use std::io::Error;
@ -84,3 +84,6 @@ impl ToastError for PreferencesStack {
(self.window.clone(), self.toast_overlay.clone()) (self.window.clone(), self.toast_overlay.clone())
} }
} }
unsafe impl Send for PreferencesStack {}
unsafe impl Sync for PreferencesStack {}

View file

@ -9,8 +9,8 @@ pub trait ToastError {
/// Show toast with `toast` title and `See message` button /// Show toast with `toast` title and `See message` button
/// ///
/// This button will show message dialog with error message /// This button will show message dialog with error message
fn toast_error<T: ToString + 'static>(&self, toast: &str, err: T) { fn toast_error<T: ToString, F: std::fmt::Display + 'static>(&self, toast: T, err: F) {
let toast = adw::Toast::new(toast); let toast = adw::Toast::new(toast.to_string().as_str());
toast.set_button_label(Some("See message")); toast.set_button_label(Some("See message"));
toast.set_action_name(Some("see-message.see-message")); toast.set_action_name(Some("see-message.see-message"));
@ -25,7 +25,7 @@ pub trait ToastError {
gtk::DialogFlags::all(), gtk::DialogFlags::all(),
gtk::MessageType::Info, gtk::MessageType::Info,
gtk::ButtonsType::Close, gtk::ButtonsType::Close,
&err.to_string() &format!("{}", err)
); );
dialog.connect_response(move |dialog, _| { dialog.connect_response(move |dialog, _| {