UI: Made progress bar component; added it to the components list

This commit is contained in:
Observer KRypt0n_ 2023-01-22 14:37:19 +02:00
parent d9086186ff
commit 12cf7b3e25
No known key found for this signature in database
GPG key ID: 844DA47BA25FE1E2
5 changed files with 214 additions and 15 deletions

View file

@ -5,6 +5,10 @@ use anime_launcher_sdk::config;
pub mod i18n; pub mod i18n;
pub mod ui; pub mod ui;
mod prettify_bytes;
pub use prettify_bytes::prettify_bytes;
pub const APP_ID: &str = "moe.launcher.an-anime-game-launcher-gtk"; pub const APP_ID: &str = "moe.launcher.an-anime-game-launcher-gtk";
pub const APP_VERSION: &str = env!("CARGO_PKG_VERSION"); pub const APP_VERSION: &str = env!("CARGO_PKG_VERSION");
pub const APP_DEBUG: bool = cfg!(debug_assertions); pub const APP_DEBUG: bool = cfg!(debug_assertions);
@ -38,8 +42,16 @@ fn main() {
tracing::info!("Set UI language to {}", i18n::LANG); tracing::info!("Set UI language to {}", i18n::LANG);
} }
// Run the app // Create the app
let app = RelmApp::new("moe.launcher.an-anime-game-launcher"); let app = RelmApp::new("moe.launcher.an-anime-game-launcher");
// Set global css
relm4::set_global_css("
progressbar > text {
margin-bottom: 4px;
}
");
// Run the app
app.run::<ui::main::App>(()); app.run::<ui::main::App>(());
} }

17
src/prettify_bytes.rs Normal file
View file

@ -0,0 +1,17 @@
pub fn prettify_bytes(bytes: u64) -> String {
if bytes > 1024 * 1024 * 1024 {
format!("{:.2} GB", bytes as f64 / 1024.0 / 1024.0 / 1024.0)
}
else if bytes > 1024 * 1024 {
format!("{:.2} MB", bytes as f64 / 1024.0 / 1024.0)
}
else if bytes > 1024 {
format!("{:.2} KB", bytes as f64 / 1024.0)
}
else {
format!("{:.2} B", bytes)
}
}

View file

@ -1,10 +1,12 @@
pub mod list; pub mod list;
pub mod group; pub mod group;
pub mod version; pub mod version;
pub mod progress_bar;
pub use list::*; pub use list::*;
pub use group::*; pub use group::*;
pub use version::*; pub use version::*;
pub use progress_bar::*;
use anime_launcher_sdk::components::*; use anime_launcher_sdk::components::*;

View file

@ -0,0 +1,130 @@
use relm4::prelude::*;
use adw::prelude::*;
use anime_launcher_sdk::anime_game_core::installer::installer::Update as InstallerUpdate;
use crate::prettify_bytes;
pub struct ProgressBar {
pub fraction: f64,
pub caption: Option<String>,
/// e.g. (53.21 MB, 10 GB)
pub downloaded: Option<(String, String)>,
pub visible: bool
}
#[derive(Debug)]
pub enum AppMsg {
Reset,
UpdateCaption(Option<String>),
/// (current bytes, total bytes)
UpdateProgress(u64, u64),
UpdateFromState(InstallerUpdate),
SetVisible(bool)
}
#[relm4::component(pub)]
impl SimpleComponent for ProgressBar {
type Init = (Option<String>, bool);
type Input = AppMsg;
type Output = ();
view! {
progress_bar = gtk::ProgressBar {
set_valign: gtk::Align::Center,
#[watch]
set_visible: model.visible,
#[watch]
set_fraction: model.fraction,
#[watch]
set_show_text: model.caption.is_some(),
#[watch]
set_text: match model.caption.as_ref() {
Some(caption) => Some({
if let Some((curr, total)) = &model.downloaded {
// caption.push_str(&format!(": {:.2}% ({curr} of {total})", model.fraction));
}
caption
}),
None => None
}
}
}
fn init(
init: Self::Init,
root: &Self::Root,
_sender: ComponentSender<Self>,
) -> ComponentParts<Self> {
let model = ProgressBar {
fraction: 0.0,
caption: init.0,
downloaded: None,
visible: init.1
};
let widgets = view_output!();
ComponentParts { model, widgets }
}
fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {
tracing::debug!("Called components list event: {:?}", msg);
match msg {
AppMsg::Reset => {
self.fraction = 0.0;
self.downloaded = None;
self.caption = None;
}
AppMsg::UpdateCaption(caption) => self.caption = caption,
AppMsg::UpdateProgress(curr, total) => {
self.fraction = curr as f64 / total as f64;
self.downloaded = Some((
prettify_bytes(curr),
prettify_bytes(total)
));
}
// TODO: add translation
AppMsg::UpdateFromState(state) => {
match state {
InstallerUpdate::CheckingFreeSpace(_) => self.caption = Some(String::from("Checking free space...")),
InstallerUpdate::DownloadingStarted(_) => self.caption = Some(String::from("Downloading...")),
InstallerUpdate::UnpackingStarted(_) => self.caption = Some(String::from("Unpacking...")),
InstallerUpdate::DownloadingProgress(curr, total) |
InstallerUpdate::UnpackingProgress(curr, total) => {
self.fraction = curr as f64 / total as f64;
self.downloaded = Some((
prettify_bytes(curr),
prettify_bytes(total)
));
}
InstallerUpdate::DownloadingFinished => tracing::info!("Downloading finished"),
InstallerUpdate::UnpackingFinished => tracing::info!("Unpacking finished"),
InstallerUpdate::DownloadingError(err) => tracing::error!("Downloading error: {:?}", err),
InstallerUpdate::UnpackingError(err) => tracing::error!("Unpacking error: {:?}", err)
}
}
AppMsg::SetVisible(visible) => self.visible = visible
}
}
}

View file

@ -5,17 +5,17 @@ use adw::prelude::*;
use gtk::glib::clone; use gtk::glib::clone;
use super::progress_bar::AppMsg as ProgressBarMsg;
use anime_launcher_sdk::config; use anime_launcher_sdk::config;
use anime_launcher_sdk::anime_game_core::installer::installer::*; use anime_launcher_sdk::anime_game_core::installer::installer::*;
use std::path::PathBuf; use std::path::PathBuf;
#[derive(Debug)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum VersionState { pub enum VersionState {
Downloaded, Downloaded,
Loading, Downloading,
Downloading(u64, u64),
Unpacking(u64, u64),
NotDownloaded NotDownloaded
} }
@ -28,13 +28,16 @@ pub struct ComponentVersion {
pub download_folder: PathBuf, pub download_folder: PathBuf,
pub show_recommended_only: bool, pub show_recommended_only: bool,
pub state: VersionState pub state: VersionState,
pub progress_bar: Controller<super::ProgressBar>
} }
#[derive(Debug)] #[derive(Debug)]
pub enum AppMsg { pub enum AppMsg {
ShowRecommendedOnly(bool), ShowRecommendedOnly(bool),
PerformAction PerformAction,
SetState(VersionState)
} }
#[relm4::component(pub)] #[relm4::component(pub)]
@ -62,6 +65,9 @@ impl SimpleComponent for ComponentVersion {
add_css_class: "flat", add_css_class: "flat",
set_valign: gtk::Align::Center, set_valign: gtk::Align::Center,
#[watch]
set_visible: model.state != VersionState::Downloading,
connect_clicked => AppMsg::PerformAction connect_clicked => AppMsg::PerformAction
} }
} }
@ -81,7 +87,11 @@ impl SimpleComponent for ComponentVersion {
download_folder: init.1, download_folder: init.1,
show_recommended_only: true, show_recommended_only: true,
state: VersionState::NotDownloaded state: VersionState::NotDownloaded,
progress_bar: super::ProgressBar::builder()
.launch((None, false))
.detach()
}; };
// Set default component state // Set default component state
@ -91,12 +101,17 @@ impl SimpleComponent for ComponentVersion {
VersionState::NotDownloaded VersionState::NotDownloaded
}; };
// Set progress bar width
model.progress_bar.widgets().progress_bar.set_width_request(200);
let widgets = view_output!(); let widgets = view_output!();
widgets.row.add_suffix(model.progress_bar.widget());
ComponentParts { model, widgets } ComponentParts { model, widgets }
} }
fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) { fn update(&mut self, msg: Self::Input, sender: ComponentSender<Self>) {
tracing::debug!("Called component version [{}] event: {:?} (state = {:?})", self.title, msg, self.state); tracing::debug!("Called component version [{}] event: {:?} (state = {:?})", self.title, msg, self.state);
match msg { match msg {
@ -108,6 +123,9 @@ impl SimpleComponent for ComponentVersion {
let path = self.download_folder.join(&self.name); let path = self.download_folder.join(&self.name);
if path.exists() { if path.exists() {
// To hide main button while it's deleting compontent's folder
self.state = VersionState::Downloading;
// todo // todo
std::fs::remove_dir_all(path).expect("Failed to delete component"); std::fs::remove_dir_all(path).expect("Failed to delete component");
@ -124,15 +142,33 @@ impl SimpleComponent for ComponentVersion {
installer.set_temp_folder(temp); installer.set_temp_folder(temp);
} }
// self.state = VersionState::Loading; self.state = VersionState::Downloading;
// todo sus let progress_bar_sender = self.progress_bar.sender().clone();
#[allow(unused_must_use)]
std::thread::spawn(clone!(@strong self.download_folder as download_folder => move || { std::thread::spawn(clone!(@strong self.download_folder as download_folder => move || {
installer.install(download_folder, |status| { progress_bar_sender.send(ProgressBarMsg::Reset);
match status { progress_bar_sender.send(ProgressBarMsg::SetVisible(true));
Update::UnpackingFinished | Update::UnpackingError(_) => println!("sus"),
_ => (), installer.install(download_folder, move |state| {
match &state {
Update::UnpackingFinished |
Update::DownloadingError(_) |
Update::UnpackingError(_) => {
progress_bar_sender.send(ProgressBarMsg::SetVisible(false));
sender.input(AppMsg::SetState(if let Update::UnpackingFinished = &state {
VersionState::Downloaded
} else {
VersionState::NotDownloaded
}));
},
_ => ()
} }
progress_bar_sender.send(ProgressBarMsg::UpdateFromState(state));
}); });
})); }));
} }
@ -141,6 +177,8 @@ impl SimpleComponent for ComponentVersion {
_ => () _ => ()
} }
} }
AppMsg::SetState(state) => self.state = state
} }
} }
} }