general settings: added test launcher style switcher

- classic style loads its background from "config's_tmp_path/background"
  so from "launcher folder/background" by default
- icons will be changed in some future
This commit is contained in:
Observer KRypt0n_ 2023-02-21 17:32:57 +02:00
parent 949a0f37e9
commit 5870316724
No known key found for this signature in database
GPG key ID: 844DA47BA25FE1E2
8 changed files with 228 additions and 18 deletions

@ -1 +1 @@
Subproject commit 8a32066c07cd471fe2a33ecfa778edffb8796f19 Subproject commit a62aeef9cd9caa0e147fddcf05f4c412c942b736

12
assets/images/classic.svg Normal file
View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="90" height="60" viewBox="0 0 90 60" xml:space="preserve">
<defs>
</defs>
<g transform="matrix(1.58 0 0 0.39 62.57 51.18)" id="HUkKwqLQOm47Zs9qaMmTc" >
<path style="stroke: rgb(242,144,30); stroke-width: 0; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(53,132,228); fill-rule: nonzero; opacity: 1;" vector-effect="non-scaling-stroke" transform=" translate(0, 0)" d="M -8.25689 0.69081 C -8.25689 -4.25086 -7.26151 -8.256879999999999 -6.03365 -8.256879999999999 L 6.03365 -8.256879999999999 L 6.03365 -8.256879999999999 C 7.2615099999999995 -8.256879999999999 8.25689 -4.2508599999999985 8.25689 0.6908100000000008 L 8.25689 0.6908100000000008 L 8.25689 0.6908100000000008 C 8.25689 4.869440000000001 7.41521 8.256890000000002 6.37694 8.256890000000002 L -6.37694 8.256890000000002 L -6.37694 8.256890000000002 C -7.41521 8.256890000000002 -8.25689 4.869440000000003 -8.25689 0.6908100000000017 z" stroke-linecap="round" />
</g>
<g transform="matrix(0.39 0 0 0.39 79.98 51.2)" id="3u3imhfuSenPkHHelxk_U" >
<path style="stroke: rgb(242,144,30); stroke-width: 0; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(230,230,230); fill-rule: nonzero; opacity: 1;" vector-effect="non-scaling-stroke" transform=" translate(0, 0)" d="M -8.25689 0.72542 C -8.25689 -4.2353700000000005 -4.26412 -8.25688 0.6612200000000001 -8.25688 L 0.6612200000000001 -8.25688 L 0.6612200000000001 -8.25688 C 4.85619 -8.25688 8.25689 -4.8317000000000005 8.25689 -0.6065400000000007 L 8.25689 0.6615399999999994 L 8.25689 0.6615399999999994 C 8.25689 4.85633 4.88064 8.256889999999999 0.7158300000000004 8.256889999999999 L -0.7792399999999995 8.256889999999999 L -0.7792399999999995 8.256889999999999 C -4.90902 8.256889999999999 -8.256879999999999 4.8849399999999985 -8.256879999999999 0.7254299999999985 z" stroke-linecap="round" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

12
assets/images/modern.svg Normal file
View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="90" height="60" viewBox="0 0 90 60" xml:space="preserve">
<defs>
</defs>
<g transform="matrix(1.58 0 0 0.39 41.18 39.34)" id="HUkKwqLQOm47Zs9qaMmTc" >
<path style="stroke: rgb(242,144,30); stroke-width: 0; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(53,132,228); fill-rule: nonzero; opacity: 1;" vector-effect="non-scaling-stroke" transform=" translate(0, 0)" d="M -8.25689 0.69081 C -8.25689 -4.25086 -7.26151 -8.256879999999999 -6.03365 -8.256879999999999 L 6.03365 -8.256879999999999 L 6.03365 -8.256879999999999 C 7.2615099999999995 -8.256879999999999 8.25689 -4.2508599999999985 8.25689 0.6908100000000008 L 8.25689 0.6908100000000008 L 8.25689 0.6908100000000008 C 8.25689 4.869440000000001 7.41521 8.256890000000002 6.37694 8.256890000000002 L -6.37694 8.256890000000002 L -6.37694 8.256890000000002 C -7.41521 8.256890000000002 -8.25689 4.869440000000003 -8.25689 0.6908100000000017 z" stroke-linecap="round" />
</g>
<g transform="matrix(0.39 0 0 0.39 58.59 39.36)" id="3u3imhfuSenPkHHelxk_U" >
<path style="stroke: rgb(242,144,30); stroke-width: 0; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(230,230,230); fill-rule: nonzero; opacity: 1;" vector-effect="non-scaling-stroke" transform=" translate(0, 0)" d="M -8.25689 0.72542 C -8.25689 -4.2353700000000005 -4.26412 -8.25688 0.6612200000000001 -8.25688 L 0.6612200000000001 -8.25688 L 0.6612200000000001 -8.25688 C 4.85619 -8.25688 8.25689 -4.8317000000000005 8.25689 -0.6065400000000007 L 8.25689 0.6615399999999994 L 8.25689 0.6615399999999994 C 8.25689 4.85633 4.88064 8.256889999999999 0.7158300000000004 8.256889999999999 L -0.7792399999999995 8.256889999999999 L -0.7792399999999995 8.256889999999999 C -4.90902 8.256889999999999 -8.256879999999999 4.8849399999999985 -8.256879999999999 0.7254299999999985 z" stroke-linecap="round" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -3,4 +3,10 @@
<gresource prefix="/org/app"> <gresource prefix="/org/app">
<file compressed="true">images/icon.png</file> <file compressed="true">images/icon.png</file>
</gresource> </gresource>
<gresource prefix="/org/app">
<file compressed="true">images/modern.svg</file>
</gresource>
<gresource prefix="/org/app">
<file compressed="true">images/classic.svg</file>
</gresource>
</gresources> </gresources>

View file

@ -6,6 +6,8 @@ use anime_launcher_sdk::anime_game_core::genshin::prelude::*;
use tracing_subscriber::prelude::*; use tracing_subscriber::prelude::*;
use std::path::PathBuf;
pub mod i18n; pub mod i18n;
pub mod ui; pub mod ui;
@ -110,14 +112,22 @@ fn main() {
} }
// Create the app // Create the app
let app = RelmApp::new("moe.launcher.an-anime-game-launcher"); let app = RelmApp::new(APP_ID);
// Set global css // Set global css
relm4::set_global_css(" relm4::set_global_css(&format!("
progressbar > text { progressbar > text {{
margin-bottom: 4px; margin-bottom: 4px;
} }}
");
window.classic-style {{
background: url(\"file://{}/background\");
background-repeat: no-repeat;
background-size: cover;
}}
",
CONFIG.launcher.temp.as_ref().unwrap_or(&PathBuf::from("/tmp")).to_string_lossy()
));
// Run the app // Run the app
app.run::<ui::main::App>(()); app.run::<ui::main::App>(());

View file

@ -8,6 +8,9 @@ use relm4::{
use gtk::prelude::*; use gtk::prelude::*;
use adw::prelude::*; use adw::prelude::*;
use anime_launcher_sdk::config::launcher::LauncherStyle;
use crate::*;
use crate::i18n::tr; use crate::i18n::tr;
use super::preferences::main::App as PreferencesApp; use super::preferences::main::App as PreferencesApp;
@ -25,13 +28,16 @@ static mut PREFERENCES_WINDOW: Option<AsyncController<PreferencesApp>> = None;
static mut ABOUT_DIALOG: Option<Controller<AboutDialog>> = None; static mut ABOUT_DIALOG: Option<Controller<AboutDialog>> = None;
pub struct App { pub struct App {
loading: Option<Option<String>> loading: Option<Option<String>>,
style: LauncherStyle
} }
#[derive(Debug)] #[derive(Debug)]
pub enum AppMsg { pub enum AppMsg {
PerformAction,
OpenPreferences, OpenPreferences,
ClosePreferences ClosePreferences,
UpdateLauncherStyle(LauncherStyle)
} }
#[relm4::component(pub)] #[relm4::component(pub)]
@ -57,12 +63,47 @@ impl SimpleComponent for App {
view! { view! {
main_window = adw::Window { main_window = adw::Window {
set_title: Some("An Anime Game Launcher"), set_title: Some("An Anime Game Launcher"),
set_default_size: (900, 600),
#[watch]
set_default_size: (
match model.style {
LauncherStyle::Modern => 900,
LauncherStyle::Classic => 1094 // (w = 1280 / 730 * h, where 1280x730 is default background picture resolution)
},
match model.style {
LauncherStyle::Modern => 600,
LauncherStyle::Classic => 624
}
),
#[watch]
add_css_class: match model.style {
LauncherStyle::Modern => "",
LauncherStyle::Classic => "classic-style"
},
#[watch]
remove_css_class: match model.style {
LauncherStyle::Modern => "classic-style",
LauncherStyle::Classic => ""
},
gtk::Box { gtk::Box {
set_orientation: gtk::Orientation::Vertical, set_orientation: gtk::Orientation::Vertical,
adw::HeaderBar { adw::HeaderBar {
#[watch]
add_css_class: match model.style {
LauncherStyle::Modern => "",
LauncherStyle::Classic => "flat"
},
#[watch]
remove_css_class: match model.style {
LauncherStyle::Modern => "flat",
LauncherStyle::Classic => ""
},
pack_end = &gtk::MenuButton { pack_end = &gtk::MenuButton {
set_icon_name: "open-menu-symbolic", set_icon_name: "open-menu-symbolic",
set_menu_model: Some(&main_menu) set_menu_model: Some(&main_menu)
@ -89,6 +130,9 @@ impl SimpleComponent for App {
set_visible: model.loading.is_none(), set_visible: model.loading.is_none(),
add = &adw::PreferencesGroup { add = &adw::PreferencesGroup {
#[watch]
set_visible: model.style == LauncherStyle::Modern,
gtk::Image { gtk::Image {
set_resource: Some("/org/app/images/icon.png"), set_resource: Some("/org/app/images/icon.png"),
set_vexpand: true, set_vexpand: true,
@ -103,11 +147,33 @@ impl SimpleComponent for App {
}, },
add = &adw::PreferencesGroup { add = &adw::PreferencesGroup {
set_valign: gtk::Align::Center, #[watch]
set_valign: match model.style {
LauncherStyle::Modern => gtk::Align::Center,
LauncherStyle::Classic => gtk::Align::End
},
#[watch]
set_width_request: match model.style {
LauncherStyle::Modern => -1,
LauncherStyle::Classic => 800
},
set_vexpand: true, set_vexpand: true,
gtk::Box { gtk::Box {
set_halign: gtk::Align::Center, #[watch]
set_halign: match model.style {
LauncherStyle::Modern => gtk::Align::Center,
LauncherStyle::Classic => gtk::Align::End
},
#[watch]
set_height_request: match model.style {
LauncherStyle::Modern => -1,
LauncherStyle::Classic => 40
},
set_margin_top: 64, set_margin_top: 64,
set_spacing: 8, set_spacing: 8,
@ -117,12 +183,16 @@ impl SimpleComponent for App {
set_width_request: 200, set_width_request: 200,
add_css_class: "suggested-action", add_css_class: "suggested-action",
connect_clicked => |_| { connect_clicked => AppMsg::PerformAction
anime_launcher_sdk::game::run().expect("Failed to run the game");
}
}, },
gtk::Button { gtk::Button {
#[watch]
set_width_request: match model.style {
LauncherStyle::Modern => -1,
LauncherStyle::Classic => 40
},
set_icon_name: "emblem-system-symbolic", set_icon_name: "emblem-system-symbolic",
connect_clicked => AppMsg::OpenPreferences connect_clicked => AppMsg::OpenPreferences
@ -137,12 +207,13 @@ impl SimpleComponent for App {
fn init( fn init(
_counter: Self::Init, _counter: Self::Init,
root: &Self::Root, root: &Self::Root,
_sender: ComponentSender<Self>, sender: ComponentSender<Self>,
) -> ComponentParts<Self> { ) -> ComponentParts<Self> {
tracing::info!("Initializing main window"); tracing::info!("Initializing main window");
let model = App { let model = App {
loading: None loading: None,
style: CONFIG.launcher.style
}; };
let widgets = view_output!(); let widgets = view_output!();
@ -156,7 +227,7 @@ impl SimpleComponent for App {
unsafe { unsafe {
PREFERENCES_WINDOW = Some(PreferencesApp::builder() PREFERENCES_WINDOW = Some(PreferencesApp::builder()
.launch(widgets.main_window.clone().into()) .launch(widgets.main_window.clone().into())
.detach()); .forward(sender.input_sender(), std::convert::identity));
ABOUT_DIALOG = Some(AboutDialog::builder() ABOUT_DIALOG = Some(AboutDialog::builder()
.transient_for(widgets.main_window.clone()) .transient_for(widgets.main_window.clone())
@ -166,6 +237,7 @@ impl SimpleComponent for App {
let group = RelmActionGroup::<WindowActionGroup>::new(); let group = RelmActionGroup::<WindowActionGroup>::new();
// TODO
group.add_action::<LauncherFolder>(&RelmAction::new_stateless(move |_| { group.add_action::<LauncherFolder>(&RelmAction::new_stateless(move |_| {
println!("Open launcher folder!"); println!("Open launcher folder!");
})); }));
@ -197,6 +269,10 @@ impl SimpleComponent for App {
tracing::debug!("Called main window event: {:?}", msg); tracing::debug!("Called main window event: {:?}", msg);
match msg { match msg {
AppMsg::PerformAction => {
anime_launcher_sdk::game::run().expect("Failed to run the game");
}
AppMsg::OpenPreferences => unsafe { AppMsg::OpenPreferences => unsafe {
PREFERENCES_WINDOW.as_ref().unwrap_unchecked().widget().show(); PREFERENCES_WINDOW.as_ref().unwrap_unchecked().widget().show();
} }
@ -204,6 +280,10 @@ impl SimpleComponent for App {
AppMsg::ClosePreferences => unsafe { AppMsg::ClosePreferences => unsafe {
PREFERENCES_WINDOW.as_ref().unwrap_unchecked().widget().hide(); PREFERENCES_WINDOW.as_ref().unwrap_unchecked().widget().hide();
} }
AppMsg::UpdateLauncherStyle(style) => {
self.style = style;
}
} }
} }
} }

View file

@ -16,6 +16,61 @@ impl WidgetTemplate for General {
set_title: &tr("general"), set_title: &tr("general"),
set_icon_name: Some("applications-system-symbolic"), set_icon_name: Some("applications-system-symbolic"),
add = &adw::PreferencesGroup {
set_title: "Appearance",
gtk::Box {
set_orientation: gtk::Orientation::Horizontal,
set_halign: gtk::Align::Center,
set_spacing: 32,
gtk::Box {
set_orientation: gtk::Orientation::Vertical,
#[name(modern_style_button)]
gtk::ToggleButton {
add_css_class: "card",
set_width_request: 180,
set_height_request: 120,
gtk::Image {
set_from_resource: Some("/org/app/images/modern.svg")
}
},
gtk::Label {
set_text: "Modern",
set_margin_top: 16
}
},
gtk::Box {
set_orientation: gtk::Orientation::Vertical,
#[name(classic_style_button)]
gtk::ToggleButton {
add_css_class: "card",
set_width_request: 180,
set_height_request: 120,
gtk::Image {
set_from_resource: Some("/org/app/images/classic.svg")
}
},
gtk::Label {
set_text: "Classic",
set_margin_top: 16
}
}
}
},
add = &adw::PreferencesGroup { add = &adw::PreferencesGroup {
set_title: &tr("general"), set_title: &tr("general"),

View file

@ -5,6 +5,7 @@ use gtk::prelude::*;
use adw::prelude::*; use adw::prelude::*;
use anime_launcher_sdk::config; use anime_launcher_sdk::config;
use anime_launcher_sdk::config::launcher::LauncherStyle;
use anime_launcher_sdk::components::*; use anime_launcher_sdk::components::*;
use anime_launcher_sdk::wincompatlib::prelude::*; use anime_launcher_sdk::wincompatlib::prelude::*;
@ -20,6 +21,8 @@ pub struct App {
wine_components: AsyncController<ComponentsList>, wine_components: AsyncController<ComponentsList>,
dxvk_components: AsyncController<ComponentsList>, dxvk_components: AsyncController<ComponentsList>,
style: LauncherStyle,
downloaded_wine_versions: Vec<wine::Version>, downloaded_wine_versions: Vec<wine::Version>,
downloaded_dxvk_versions: Vec<dxvk::Version>, downloaded_dxvk_versions: Vec<dxvk::Version>,
@ -36,6 +39,7 @@ pub enum AppMsg {
title: String, title: String,
description: Option<String> description: Option<String>
}, },
UpdateLauncherStyle(LauncherStyle),
WineRecommendedOnly(bool), WineRecommendedOnly(bool),
DxvkRecommendedOnly(bool), DxvkRecommendedOnly(bool),
UpdateDownloadedWine, UpdateDownloadedWine,
@ -50,7 +54,7 @@ pub enum AppMsg {
impl SimpleAsyncComponent for App { impl SimpleAsyncComponent for App {
type Init = gtk::Window; type Init = gtk::Window;
type Input = AppMsg; type Input = AppMsg;
type Output = (); type Output = crate::ui::main::AppMsg;
view! { view! {
preferences_window = adw::PreferencesWindow { preferences_window = adw::PreferencesWindow {
@ -65,6 +69,22 @@ impl SimpleAsyncComponent for App {
// but I have no idea how to do it other way // but I have no idea how to do it other way
// There're no graphical glitches so don't care // There're no graphical glitches so don't care
#[template_child]
modern_style_button {
#[watch]
set_active: model.style == LauncherStyle::Modern,
connect_clicked => AppMsg::UpdateLauncherStyle(LauncherStyle::Modern)
},
#[template_child]
classic_style_button {
#[watch]
set_active: model.style == LauncherStyle::Classic,
connect_clicked => AppMsg::UpdateLauncherStyle(LauncherStyle::Classic)
},
#[template_child] #[template_child]
wine_versions { wine_versions {
add = model.wine_components.widget(), add = model.wine_components.widget(),
@ -191,6 +211,8 @@ impl SimpleAsyncComponent for App {
}) })
.forward(sender.input_sender(), std::convert::identity), .forward(sender.input_sender(), std::convert::identity),
style: CONFIG.launcher.style,
downloaded_wine_versions: vec![], downloaded_wine_versions: vec![],
downloaded_dxvk_versions: vec![], downloaded_dxvk_versions: vec![],
@ -253,6 +275,19 @@ impl SimpleAsyncComponent for App {
PREFERENCES_WINDOW.as_ref().unwrap_unchecked().add_toast(&toast); PREFERENCES_WINDOW.as_ref().unwrap_unchecked().add_toast(&toast);
} }
#[allow(unused_must_use)]
AppMsg::UpdateLauncherStyle(style) => {
if let Ok(mut config) = config::get() {
config.launcher.style = style;
config::update(config);
}
self.style = style;
sender.output(Self::Output::UpdateLauncherStyle(style));
}
AppMsg::WineRecommendedOnly(state) => { AppMsg::WineRecommendedOnly(state) => {
// todo // todo
self.wine_components.sender().send(components::list::AppMsg::ShowRecommendedOnly(state)).unwrap(); self.wine_components.sender().send(components::list::AppMsg::ShowRecommendedOnly(state)).unwrap();