mirror of
https://github.com/an-anime-team/an-anime-game-launcher.git
synced 2025-02-16 15:22:01 +03:00
feat(ui): added new pages to the first run window
Added new "ToS warning" and "Dependencies" working pages to the first run window
This commit is contained in:
parent
3f5ce430f9
commit
9d4ad8df34
5 changed files with 413 additions and 8 deletions
199
src/ui/first_run/dependencies.rs
Normal file
199
src/ui/first_run/dependencies.rs
Normal file
|
@ -0,0 +1,199 @@
|
|||
use relm4::prelude::*;
|
||||
use relm4::component::*;
|
||||
|
||||
use adw::prelude::*;
|
||||
|
||||
use anime_launcher_sdk::is_available;
|
||||
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
use super::main::FirstRunAppMsg;
|
||||
|
||||
pub struct DependenciesApp {
|
||||
show_arch: bool,
|
||||
show_debian: bool,
|
||||
show_fedora: bool
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum DependenciesAppMsg {
|
||||
Continue,
|
||||
Exit
|
||||
}
|
||||
|
||||
#[relm4::component(async, pub)]
|
||||
impl SimpleAsyncComponent for DependenciesApp {
|
||||
type Init = ();
|
||||
type Input = DependenciesAppMsg;
|
||||
type Output = FirstRunAppMsg;
|
||||
|
||||
view! {
|
||||
adw::PreferencesPage {
|
||||
set_hexpand: true,
|
||||
|
||||
add = &adw::PreferencesGroup {
|
||||
gtk::Label {
|
||||
set_label: "You're missing some dependencies!",
|
||||
set_margin_top: 32,
|
||||
|
||||
add_css_class: "title-1"
|
||||
},
|
||||
|
||||
gtk::Label {
|
||||
set_label: "You must install some packages to your system before continue installation process",
|
||||
|
||||
set_justify: gtk::Justification::Center,
|
||||
set_wrap: true,
|
||||
set_margin_top: 32
|
||||
}
|
||||
},
|
||||
|
||||
add = &adw::PreferencesGroup {
|
||||
set_valign: gtk::Align::Center,
|
||||
set_vexpand: true,
|
||||
|
||||
gtk::Box {
|
||||
set_orientation: gtk::Orientation::Vertical,
|
||||
set_spacing: 16,
|
||||
|
||||
gtk::Box {
|
||||
set_orientation: gtk::Orientation::Vertical,
|
||||
set_spacing: 16,
|
||||
|
||||
#[watch]
|
||||
set_visible: model.show_arch,
|
||||
|
||||
gtk::Label {
|
||||
set_label: "Arch (pacman)"
|
||||
},
|
||||
|
||||
gtk::Entry {
|
||||
set_text: "sudo pacman -Syu git xdelta3",
|
||||
set_editable: false
|
||||
}
|
||||
},
|
||||
|
||||
gtk::Box {
|
||||
set_orientation: gtk::Orientation::Vertical,
|
||||
set_spacing: 16,
|
||||
|
||||
#[watch]
|
||||
set_visible: model.show_debian,
|
||||
|
||||
gtk::Label {
|
||||
set_label: "Debian / Ubuntu (apt)"
|
||||
},
|
||||
|
||||
gtk::Entry {
|
||||
set_text: "sudo apt install git xdelta3",
|
||||
set_editable: false
|
||||
}
|
||||
},
|
||||
|
||||
gtk::Box {
|
||||
set_orientation: gtk::Orientation::Vertical,
|
||||
set_spacing: 16,
|
||||
|
||||
#[watch]
|
||||
set_visible: model.show_fedora,
|
||||
|
||||
gtk::Label {
|
||||
set_label: "Fedora (dnf)"
|
||||
},
|
||||
|
||||
gtk::Entry {
|
||||
set_text: "sudo dnf install git xdelta",
|
||||
set_editable: false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
add = &adw::PreferencesGroup {
|
||||
set_valign: gtk::Align::Center,
|
||||
set_vexpand: true,
|
||||
|
||||
gtk::Box {
|
||||
set_orientation: gtk::Orientation::Horizontal,
|
||||
set_halign: gtk::Align::Center,
|
||||
set_spacing: 8,
|
||||
|
||||
gtk::Button {
|
||||
set_label: "Check",
|
||||
add_css_class: "suggested-action",
|
||||
|
||||
connect_clicked => DependenciesAppMsg::Continue
|
||||
},
|
||||
|
||||
gtk::Button {
|
||||
set_label: "Exit",
|
||||
|
||||
connect_clicked => DependenciesAppMsg::Exit
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn init(
|
||||
_init: Self::Init,
|
||||
root: Self::Root,
|
||||
_sender: AsyncComponentSender<Self>,
|
||||
) -> AsyncComponentParts<Self> {
|
||||
let mut model = Self {
|
||||
show_arch: false,
|
||||
show_debian: false,
|
||||
show_fedora: false
|
||||
};
|
||||
|
||||
// Decide which packaging format used in system
|
||||
match Command::new("pacman").stdout(Stdio::null()).spawn() {
|
||||
Ok(_) => model.show_arch = true,
|
||||
|
||||
Err(_) => match Command::new("apt").stdout(Stdio::null()).spawn() {
|
||||
Ok(_) => model.show_debian = true,
|
||||
|
||||
Err(_) => match Command::new("dnf").stdout(Stdio::null()).spawn() {
|
||||
Ok(_) => model.show_fedora = true,
|
||||
|
||||
Err(_) => {
|
||||
model.show_arch = true;
|
||||
model.show_debian = true;
|
||||
model.show_fedora = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let widgets = view_output!();
|
||||
|
||||
AsyncComponentParts { model, widgets }
|
||||
}
|
||||
|
||||
async fn update(&mut self, msg: Self::Input, sender: AsyncComponentSender<Self>) {
|
||||
match msg {
|
||||
#[allow(unused_must_use)]
|
||||
DependenciesAppMsg::Continue => {
|
||||
let packages = ["git", "xdelta3"];
|
||||
|
||||
for package in packages {
|
||||
if !is_available(package) {
|
||||
sender.output(Self::Output::Toast {
|
||||
title: format!("Package is not available: {package}"),
|
||||
description: None
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
sender.output(Self::Output::ScrollToDefaultPaths);
|
||||
}
|
||||
|
||||
DependenciesAppMsg::Exit => {
|
||||
// TODO: relm4 has some function for it
|
||||
std::process::exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,19 +7,30 @@ use adw::prelude::*;
|
|||
use crate::i18n::tr;
|
||||
|
||||
use super::welcome::*;
|
||||
use super::tos_warning::*;
|
||||
use super::dependencies::*;
|
||||
|
||||
static mut MAIN_WINDOW: Option<adw::Window> = None;
|
||||
|
||||
pub struct FirstRunApp {
|
||||
welcome: AsyncController<WelcomeApp>,
|
||||
tos_warning: AsyncController<TosWarningApp>,
|
||||
dependencies: AsyncController<DependenciesApp>,
|
||||
|
||||
toast_overlay: adw::ToastOverlay,
|
||||
carousel: adw::Carousel
|
||||
carousel: adw::Carousel,
|
||||
|
||||
title: String
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum FirstRunAppMsg {
|
||||
ScrollToTosWarning,
|
||||
ScrollToDependencies,
|
||||
ScrollToDefaultPaths,
|
||||
ScrollToDownloadComponents,
|
||||
ScrollToChooseVoiceovers,
|
||||
ScrollToFinish,
|
||||
|
||||
Toast {
|
||||
title: String,
|
||||
|
@ -35,9 +46,11 @@ impl SimpleComponent for FirstRunApp {
|
|||
|
||||
view! {
|
||||
window = adw::Window {
|
||||
set_title: Some("Welcome"), // TODO: update this based on currently open page
|
||||
set_default_size: (780, 560),
|
||||
|
||||
#[watch]
|
||||
set_title: Some(&model.title),
|
||||
|
||||
#[local_ref]
|
||||
toast_overlay -> adw::ToastOverlay {
|
||||
gtk::Box {
|
||||
|
@ -52,10 +65,12 @@ impl SimpleComponent for FirstRunApp {
|
|||
set_allow_mouse_drag: false,
|
||||
|
||||
append = model.welcome.widget(),
|
||||
append = model.tos_warning.widget(),
|
||||
append = model.dependencies.widget(),
|
||||
},
|
||||
|
||||
adw::CarouselIndicatorDots {
|
||||
set_carousel: Some(&carousel),
|
||||
set_carousel: Some(carousel),
|
||||
set_height_request: 32
|
||||
}
|
||||
}
|
||||
|
@ -66,7 +81,7 @@ impl SimpleComponent for FirstRunApp {
|
|||
fn init(
|
||||
_parent: Self::Init,
|
||||
root: &Self::Root,
|
||||
_sender: ComponentSender<Self>,
|
||||
sender: ComponentSender<Self>,
|
||||
) -> ComponentParts<Self> {
|
||||
tracing::info!("Initializing first run window");
|
||||
|
||||
|
@ -76,10 +91,20 @@ impl SimpleComponent for FirstRunApp {
|
|||
let model = Self {
|
||||
welcome: WelcomeApp::builder()
|
||||
.launch(())
|
||||
.detach(),
|
||||
.forward(sender.input_sender(), std::convert::identity),
|
||||
|
||||
tos_warning: TosWarningApp::builder()
|
||||
.launch(())
|
||||
.forward(sender.input_sender(), std::convert::identity),
|
||||
|
||||
dependencies: DependenciesApp::builder()
|
||||
.launch(())
|
||||
.forward(sender.input_sender(), std::convert::identity),
|
||||
|
||||
toast_overlay,
|
||||
carousel
|
||||
carousel,
|
||||
|
||||
title: String::from("Welcome")
|
||||
};
|
||||
|
||||
let toast_overlay = &model.toast_overlay;
|
||||
|
@ -94,11 +119,43 @@ impl SimpleComponent for FirstRunApp {
|
|||
ComponentParts { model, widgets } // will return soon
|
||||
}
|
||||
|
||||
fn update(&mut self, msg: Self::Input, sender: ComponentSender<Self>) {
|
||||
fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {
|
||||
tracing::debug!("Called first run window event: {:?}", msg);
|
||||
|
||||
match msg {
|
||||
FirstRunAppMsg::ScrollToTosWarning => {
|
||||
self.title = String::from("ToS Warning");
|
||||
|
||||
self.carousel.scroll_to(self.tos_warning.widget(), true);
|
||||
}
|
||||
|
||||
FirstRunAppMsg::ScrollToDependencies => {
|
||||
self.title = String::from("Dependencies");
|
||||
|
||||
self.carousel.scroll_to(self.dependencies.widget(), true);
|
||||
}
|
||||
|
||||
FirstRunAppMsg::ScrollToDefaultPaths => {
|
||||
self.title = String::from("Default paths");
|
||||
|
||||
self.carousel.scroll_to(self.welcome.widget(), true);
|
||||
}
|
||||
|
||||
FirstRunAppMsg::ScrollToDownloadComponents => {
|
||||
self.title = String::from("Download components");
|
||||
|
||||
self.carousel.scroll_to(self.welcome.widget(), true);
|
||||
}
|
||||
|
||||
FirstRunAppMsg::ScrollToChooseVoiceovers => {
|
||||
self.title = String::from("Select voiceovers");
|
||||
|
||||
self.carousel.scroll_to(self.welcome.widget(), true);
|
||||
}
|
||||
|
||||
FirstRunAppMsg::ScrollToFinish => {
|
||||
self.title = String::from("Finish");
|
||||
|
||||
self.carousel.scroll_to(self.welcome.widget(), true);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,2 +1,4 @@
|
|||
pub mod main;
|
||||
pub mod welcome;
|
||||
pub mod tos_warning;
|
||||
pub mod dependencies;
|
||||
|
|
148
src/ui/first_run/tos_warning.rs
Normal file
148
src/ui/first_run/tos_warning.rs
Normal file
|
@ -0,0 +1,148 @@
|
|||
use relm4::prelude::*;
|
||||
use relm4::component::*;
|
||||
|
||||
use adw::prelude::*;
|
||||
|
||||
use anime_launcher_sdk::is_available;
|
||||
|
||||
use super::main::FirstRunAppMsg;
|
||||
|
||||
pub struct TosWarningApp;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum TosWarningAppMsg {
|
||||
Continue,
|
||||
Exit
|
||||
}
|
||||
|
||||
#[relm4::component(async, pub)]
|
||||
impl SimpleAsyncComponent for TosWarningApp {
|
||||
type Init = ();
|
||||
type Input = TosWarningAppMsg;
|
||||
type Output = FirstRunAppMsg;
|
||||
|
||||
view! {
|
||||
adw::PreferencesPage {
|
||||
set_hexpand: true,
|
||||
|
||||
add = &adw::PreferencesGroup {
|
||||
gtk::Label {
|
||||
set_label: "ToS violation warning",
|
||||
set_margin_top: 8,
|
||||
add_css_class: "title-1"
|
||||
},
|
||||
|
||||
gtk::Box {
|
||||
set_orientation: gtk::Orientation::Vertical,
|
||||
set_margin_top: 32,
|
||||
set_spacing: 12,
|
||||
set_hexpand: true,
|
||||
|
||||
// TODO: use some kind of multiline text field
|
||||
|
||||
gtk::Label {
|
||||
set_label: "This launcher is an unofficial tool, in no way related to miHoYo nor COGNOSPHERE.",
|
||||
|
||||
set_wrap: true,
|
||||
set_halign: gtk::Align::Center
|
||||
},
|
||||
|
||||
gtk::Label {
|
||||
set_label: "This tool is designed to facilitate playing Genshin Impact on Linux, and was built with the sole purpose of installing and running the game with less hassle.",
|
||||
|
||||
set_wrap: true,
|
||||
set_halign: gtk::Align::Center
|
||||
},
|
||||
|
||||
gtk::Label {
|
||||
set_label: "It does so by using existing components and making the experience simple for the user.",
|
||||
|
||||
set_wrap: true,
|
||||
set_halign: gtk::Align::Center
|
||||
},
|
||||
|
||||
gtk::Label {
|
||||
set_label: "However, some components used here likely break the miHoYo Terms of Service for Genshin Impact.",
|
||||
|
||||
set_wrap: true,
|
||||
set_halign: gtk::Align::Center
|
||||
},
|
||||
|
||||
gtk::Label {
|
||||
set_label: "If you are using this launcher, your player account could become identified as TOS-non-compliant by miHoYo/COGNOSPHERE.",
|
||||
|
||||
set_wrap: true,
|
||||
set_halign: gtk::Align::Center
|
||||
},
|
||||
|
||||
gtk::Label {
|
||||
set_label: "If this happens, as your account would be disobeying TOS, miHoYo/COGNOSPHERE are free to do what they want. Including banning.",
|
||||
|
||||
set_wrap: true,
|
||||
set_halign: gtk::Align::Center
|
||||
},
|
||||
|
||||
gtk::Label {
|
||||
set_label: "If you understand the risk of trying to play the game in an unofficial capacity, press OK and let's go researching the world of Teyvat!",
|
||||
|
||||
set_wrap: true,
|
||||
set_halign: gtk::Align::Center
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
add = &adw::PreferencesGroup {
|
||||
set_valign: gtk::Align::Center,
|
||||
set_vexpand: true,
|
||||
|
||||
gtk::Box {
|
||||
set_orientation: gtk::Orientation::Horizontal,
|
||||
set_halign: gtk::Align::Center,
|
||||
set_spacing: 8,
|
||||
|
||||
gtk::Button {
|
||||
set_label: "Continue",
|
||||
add_css_class: "suggested-action",
|
||||
|
||||
connect_clicked => TosWarningAppMsg::Continue
|
||||
},
|
||||
|
||||
gtk::Button {
|
||||
set_label: "Exit",
|
||||
|
||||
connect_clicked => TosWarningAppMsg::Exit
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn init(
|
||||
_init: Self::Init,
|
||||
root: Self::Root,
|
||||
_sender: AsyncComponentSender<Self>,
|
||||
) -> AsyncComponentParts<Self> {
|
||||
let model = Self;
|
||||
let widgets = view_output!();
|
||||
|
||||
AsyncComponentParts { model, widgets }
|
||||
}
|
||||
|
||||
async fn update(&mut self, msg: Self::Input, sender: AsyncComponentSender<Self>) {
|
||||
match msg {
|
||||
#[allow(unused_must_use)]
|
||||
TosWarningAppMsg::Continue => {
|
||||
if is_available("git") && is_available("xdelta3") {
|
||||
sender.output(Self::Output::ScrollToDefaultPaths);
|
||||
} else {
|
||||
sender.output(Self::Output::ScrollToDependencies);
|
||||
}
|
||||
}
|
||||
|
||||
TosWarningAppMsg::Exit => {
|
||||
// TODO: relm4 has some function for it
|
||||
std::process::exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,7 +3,6 @@ use relm4::component::*;
|
|||
|
||||
use adw::prelude::*;
|
||||
|
||||
use crate::*;
|
||||
use super::main::FirstRunAppMsg;
|
||||
|
||||
pub struct WelcomeApp;
|
||||
|
|
Loading…
Add table
Reference in a new issue