mirror of
https://github.com/an-anime-team/an-anime-game-launcher.git
synced 2024-11-21 20:36:01 +03:00
UI: Made progress bar component; added it to the components list
This commit is contained in:
parent
d9086186ff
commit
12cf7b3e25
5 changed files with 214 additions and 15 deletions
14
src/main.rs
14
src/main.rs
|
@ -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
17
src/prettify_bytes.rs
Normal 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)
|
||||
}
|
||||
}
|
|
@ -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::*;
|
||||
|
||||
|
|
130
src/ui/components/progress_bar.rs
Normal file
130
src/ui/components/progress_bar.rs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue