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 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_VERSION: &str = env!("CARGO_PKG_VERSION");
pub const APP_DEBUG: bool = cfg!(debug_assertions);
@ -38,8 +42,16 @@ fn main() {
tracing::info!("Set UI language to {}", i18n::LANG);
}
// Run the app
// Create the app
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>(());
}

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 group;
pub mod version;
pub mod progress_bar;
pub use list::*;
pub use group::*;
pub use version::*;
pub use progress_bar::*;
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 super::progress_bar::AppMsg as ProgressBarMsg;
use anime_launcher_sdk::config;
use anime_launcher_sdk::anime_game_core::installer::installer::*;
use std::path::PathBuf;
#[derive(Debug)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum VersionState {
Downloaded,
Loading,
Downloading(u64, u64),
Unpacking(u64, u64),
Downloading,
NotDownloaded
}
@ -28,13 +28,16 @@ pub struct ComponentVersion {
pub download_folder: PathBuf,
pub show_recommended_only: bool,
pub state: VersionState
pub state: VersionState,
pub progress_bar: Controller<super::ProgressBar>
}
#[derive(Debug)]
pub enum AppMsg {
ShowRecommendedOnly(bool),
PerformAction
PerformAction,
SetState(VersionState)
}
#[relm4::component(pub)]
@ -62,6 +65,9 @@ impl SimpleComponent for ComponentVersion {
add_css_class: "flat",
set_valign: gtk::Align::Center,
#[watch]
set_visible: model.state != VersionState::Downloading,
connect_clicked => AppMsg::PerformAction
}
}
@ -81,7 +87,11 @@ impl SimpleComponent for ComponentVersion {
download_folder: init.1,
show_recommended_only: true,
state: VersionState::NotDownloaded
state: VersionState::NotDownloaded,
progress_bar: super::ProgressBar::builder()
.launch((None, false))
.detach()
};
// Set default component state
@ -91,12 +101,17 @@ impl SimpleComponent for ComponentVersion {
VersionState::NotDownloaded
};
// Set progress bar width
model.progress_bar.widgets().progress_bar.set_width_request(200);
let widgets = view_output!();
widgets.row.add_suffix(model.progress_bar.widget());
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);
match msg {
@ -108,6 +123,9 @@ impl SimpleComponent for ComponentVersion {
let path = self.download_folder.join(&self.name);
if path.exists() {
// To hide main button while it's deleting compontent's folder
self.state = VersionState::Downloading;
// todo
std::fs::remove_dir_all(path).expect("Failed to delete component");
@ -124,15 +142,33 @@ impl SimpleComponent for ComponentVersion {
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 || {
installer.install(download_folder, |status| {
match status {
Update::UnpackingFinished | Update::UnpackingError(_) => println!("sus"),
_ => (),
progress_bar_sender.send(ProgressBarMsg::Reset);
progress_bar_sender.send(ProgressBarMsg::SetVisible(true));
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
}
}
}