mirror of
https://github.com/an-anime-team/an-anime-game-launcher.git
synced 2025-02-16 15:22: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 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
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 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::*;
|
||||||
|
|
||||||
|
|
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 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue