mirror of
https://github.com/an-anime-team/sleepy-launcher.git
synced 2025-03-15 06:28:27 +03:00
0.5.0
- removed "Settings" main menu item because I said so - made `VoiceoverRow` component; added dynamic voiceovers loading Now you can download new voiceovers, but can't delete them (WIP) From previous commits: - probably fixed startup gtk errors
This commit is contained in:
parent
cb1125dfbc
commit
2f0862946f
10 changed files with 169 additions and 80 deletions
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "anime-game-launcher"
|
||||
version = "0.4.0"
|
||||
version = "0.5.0"
|
||||
description = "Anime Game launcher"
|
||||
authors = ["Nikita Podvirnyy <suimin.tu.mu.ga.mi@gmail.com>"]
|
||||
license = "GPL-3.0"
|
||||
|
|
|
@ -125,6 +125,5 @@ Gtk.AboutDialog about {
|
|||
|
||||
menu app_menu {
|
||||
item ("Check for updates")
|
||||
item ("Settings", "open-settings.open-settings")
|
||||
item ("About", "show-about-dialog.show-about-dialog")
|
||||
}
|
||||
|
|
|
@ -19,49 +19,9 @@ Adw.PreferencesPage general_page {
|
|||
};
|
||||
}
|
||||
|
||||
Adw.ExpanderRow {
|
||||
Adw.ExpanderRow voiceovers_row {
|
||||
title: "Game voiceovers";
|
||||
subtitle: "Select voice packages used in game";
|
||||
|
||||
Adw.ActionRow {
|
||||
title: "English";
|
||||
|
||||
Gtk.Button {
|
||||
icon-name: "document-save-symbolic";
|
||||
valign: center;
|
||||
styles ["flat"]
|
||||
}
|
||||
}
|
||||
|
||||
Adw.ActionRow {
|
||||
title: "Japanese";
|
||||
|
||||
Gtk.Button {
|
||||
icon-name: "user-trash-symbolic";
|
||||
valign: center;
|
||||
styles ["flat"]
|
||||
}
|
||||
}
|
||||
|
||||
Adw.ActionRow {
|
||||
title: "Korean";
|
||||
|
||||
Gtk.Button {
|
||||
icon-name: "document-save-symbolic";
|
||||
valign: center;
|
||||
styles ["flat"]
|
||||
}
|
||||
}
|
||||
|
||||
Adw.ActionRow {
|
||||
title: "Chinese";
|
||||
|
||||
Gtk.Button {
|
||||
icon-name: "document-save-symbolic";
|
||||
valign: center;
|
||||
styles ["flat"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
use gtk4::{self as gtk, prelude::*};
|
||||
use libadwaita::{self as adw, prelude::*};
|
||||
|
||||
use gtk::Align;
|
||||
|
||||
use crate::lib::dxvk::Version;
|
||||
use crate::ui::traits::download_component::*;
|
||||
|
||||
|
@ -45,7 +43,7 @@ impl DxvkRow {
|
|||
progress_bar.set_show_text(true);
|
||||
|
||||
progress_bar.set_width_request(200);
|
||||
progress_bar.set_valign(Align::Center);
|
||||
progress_bar.set_valign(gtk::Align::Center);
|
||||
progress_bar.hide();
|
||||
|
||||
row.add_suffix(&progress_bar);
|
||||
|
|
|
@ -2,3 +2,4 @@ pub mod dxvk_row;
|
|||
pub mod wine_group;
|
||||
pub mod wine_row;
|
||||
pub mod progress_bar;
|
||||
pub mod voiceover_row;
|
||||
|
|
50
src/ui/components/voiceover_row.rs
Normal file
50
src/ui/components/voiceover_row.rs
Normal file
|
@ -0,0 +1,50 @@
|
|||
use gtk4::{self as gtk, prelude::*};
|
||||
use libadwaita::{self as adw, prelude::*};
|
||||
|
||||
use anime_game_core::prelude::*;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct VoiceoverRow {
|
||||
pub package: VoicePackage,
|
||||
|
||||
pub row: adw::ActionRow,
|
||||
pub button: gtk::Button
|
||||
}
|
||||
|
||||
impl VoiceoverRow {
|
||||
pub fn new(package: VoicePackage) -> Self {
|
||||
let row = adw::ActionRow::new();
|
||||
let button = gtk::Button::new();
|
||||
|
||||
row.set_title(package.locale().to_name());
|
||||
|
||||
button.set_icon_name("document-save-symbolic");
|
||||
button.set_valign(gtk::Align::Center);
|
||||
button.add_css_class("flat");
|
||||
|
||||
row.add_suffix(&button);
|
||||
|
||||
Self {
|
||||
package,
|
||||
row,
|
||||
button
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_state<T: ToString>(&self, game_path: T) {
|
||||
if self.is_downloaded(game_path) {
|
||||
self.button.set_icon_name("user-trash-symbolic");
|
||||
}
|
||||
|
||||
else {
|
||||
self.button.set_icon_name("document-save-symbolic");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_downloaded<T: ToString>(&self, game_path: T) -> bool {
|
||||
self.package.is_installed_in(game_path)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for VoiceoverRow {}
|
||||
unsafe impl Sync for VoiceoverRow {}
|
|
@ -1,8 +1,6 @@
|
|||
use gtk4::{self as gtk, prelude::*};
|
||||
use libadwaita::{self as adw, prelude::*};
|
||||
|
||||
use gtk::Align;
|
||||
|
||||
use crate::lib::wine::Version;
|
||||
use crate::ui::traits::download_component::*;
|
||||
|
||||
|
@ -35,7 +33,7 @@ impl WineRow {
|
|||
progress_bar.set_show_text(true);
|
||||
|
||||
progress_bar.set_width_request(200);
|
||||
progress_bar.set_valign(Align::Center);
|
||||
progress_bar.set_valign(gtk::Align::Center);
|
||||
progress_bar.set_visible(false);
|
||||
|
||||
row.add_suffix(&progress_bar);
|
||||
|
|
|
@ -79,7 +79,7 @@ impl AppWidgets {
|
|||
get_object(&builder, "progress_bar_group")?
|
||||
),
|
||||
|
||||
preferences_stack: PreferencesStack::new(window, toast_overlay)?
|
||||
preferences_stack: PreferencesStack::new()?
|
||||
};
|
||||
|
||||
// Set devel style to ApplicationWindow if it's debug mode
|
||||
|
@ -175,12 +175,15 @@ pub struct App {
|
|||
impl App {
|
||||
/// Create new application
|
||||
pub fn new(app: >k::Application) -> Result<Self, String> {
|
||||
let result = Self {
|
||||
let mut result = Self {
|
||||
widgets: AppWidgets::try_get()?,
|
||||
values: Default::default(),
|
||||
actions: Default::default()
|
||||
}.init_events().init_actions();
|
||||
|
||||
// Set app reference
|
||||
result.widgets.preferences_stack.set_app(result.clone());
|
||||
|
||||
// Bind app to the window
|
||||
result.widgets.window.set_application(Some(app));
|
||||
|
||||
|
@ -197,10 +200,6 @@ impl App {
|
|||
about.show();
|
||||
}));
|
||||
|
||||
add_action(&self.widgets.menu, "open-settings", clone!(@strong self as this => move || {
|
||||
this.update(Actions::OpenPreferencesPage).unwrap();
|
||||
}));
|
||||
|
||||
// Open preferences page
|
||||
self.widgets.open_preferences.connect_clicked(Actions::OpenPreferencesPage.into_fn(&self));
|
||||
|
||||
|
|
|
@ -13,9 +13,11 @@ use anime_game_core::prelude::*;
|
|||
use crate::lib::config;
|
||||
use crate::lib::dxvk;
|
||||
use crate::lib::wine;
|
||||
use crate::lib::launcher::states::LauncherState;
|
||||
|
||||
use crate::ui::*;
|
||||
use crate::ui::traits::prelude::*;
|
||||
use crate::ui::components::voiceover_row::VoiceoverRow;
|
||||
use crate::ui::components::dxvk_row::DxvkRow;
|
||||
use crate::ui::components::wine_group::WineGroup;
|
||||
|
||||
|
@ -26,11 +28,11 @@ use crate::ui::components::wine_group::WineGroup;
|
|||
/// This function does not implement events
|
||||
#[derive(Clone, glib::Downgrade)]
|
||||
pub struct AppWidgets {
|
||||
pub window: adw::ApplicationWindow,
|
||||
pub toast_overlay: adw::ToastOverlay,
|
||||
|
||||
pub page: adw::PreferencesPage,
|
||||
|
||||
pub voiceovers_row: adw::ExpanderRow,
|
||||
pub voieover_components: Rc<Vec<VoiceoverRow>>,
|
||||
|
||||
pub game_version: gtk::Label,
|
||||
pub patch_version: gtk::Label,
|
||||
|
||||
|
@ -51,15 +53,15 @@ pub struct AppWidgets {
|
|||
}
|
||||
|
||||
impl AppWidgets {
|
||||
pub fn try_get(window: adw::ApplicationWindow, toast_overlay: adw::ToastOverlay) -> Result<Self, String> {
|
||||
pub fn try_get() -> Result<Self, String> {
|
||||
let builder = gtk::Builder::from_string(include_str!("../../../assets/ui/.dist/preferences/general.ui"));
|
||||
|
||||
let mut result = Self {
|
||||
window,
|
||||
toast_overlay,
|
||||
|
||||
page: get_object(&builder, "general_page")?,
|
||||
|
||||
voiceovers_row: get_object(&builder, "voiceovers_row")?,
|
||||
voieover_components: Default::default(),
|
||||
|
||||
game_version: get_object(&builder, "game_version")?,
|
||||
patch_version: get_object(&builder, "patch_version")?,
|
||||
|
||||
|
@ -84,6 +86,24 @@ impl AppWidgets {
|
|||
Err(err) => return Err(err.to_string())
|
||||
};
|
||||
|
||||
// Update voiceovers list
|
||||
let voice_packages = match VoicePackage::list_latest() {
|
||||
Ok(voice_packages) => voice_packages,
|
||||
Err(err) => return Err(err.to_string())
|
||||
};
|
||||
|
||||
let mut components = Vec::new();
|
||||
|
||||
for package in voice_packages {
|
||||
let row = VoiceoverRow::new(package);
|
||||
|
||||
result.voiceovers_row.add_row(&row.row);
|
||||
|
||||
components.push(row);
|
||||
}
|
||||
|
||||
result.voieover_components = Rc::new(components);
|
||||
|
||||
// Update wine versions lists
|
||||
let groups = match wine::List::get() {
|
||||
Ok(list) => list,
|
||||
|
@ -139,6 +159,7 @@ impl AppWidgets {
|
|||
/// It may be helpful if you want to add the same event for several widgets, or call an action inside of another action
|
||||
#[derive(Debug, Clone, glib::Downgrade)]
|
||||
pub enum Actions {
|
||||
VoiceoverPerformAction(Rc<usize>),
|
||||
DxvkPerformAction(Rc<usize>),
|
||||
WinePerformAction(Rc<(usize, usize)>),
|
||||
UpdateDxvkComboRow,
|
||||
|
@ -180,6 +201,7 @@ pub struct Values {
|
|||
/// That's what we need and what we use in `App::update` method
|
||||
#[derive(Clone, glib::Downgrade)]
|
||||
pub struct App {
|
||||
app: Rc<Cell<Option<super::MainApp>>>,
|
||||
widgets: AppWidgets,
|
||||
values: Rc<Cell<Values>>,
|
||||
actions: Rc<Cell<Option<glib::Sender<Actions>>>>
|
||||
|
@ -187,9 +209,10 @@ pub struct App {
|
|||
|
||||
impl App {
|
||||
/// Create new application
|
||||
pub fn new(window: adw::ApplicationWindow, toast_overlay: adw::ToastOverlay) -> Result<Self, String> {
|
||||
pub fn new() -> Result<Self, String> {
|
||||
let result = Self {
|
||||
widgets: AppWidgets::try_get(window, toast_overlay)?,
|
||||
app: Default::default(),
|
||||
widgets: AppWidgets::try_get()?,
|
||||
values: Default::default(),
|
||||
actions: Default::default()
|
||||
}.init_events().init_actions();
|
||||
|
@ -197,8 +220,20 @@ impl App {
|
|||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn set_app(&mut self, app: super::MainApp) {
|
||||
self.app.set(Some(app));
|
||||
}
|
||||
|
||||
/// Add default events and values to the widgets
|
||||
fn init_events(self) -> Self {
|
||||
// Voiceover download/delete button event
|
||||
for (i, row) in (&*self.widgets.voieover_components).into_iter().enumerate() {
|
||||
row.button.connect_clicked(clone!(@weak self as this => move |_| {
|
||||
this.update(Actions::VoiceoverPerformAction(Rc::new(i))).unwrap();
|
||||
}));
|
||||
}
|
||||
|
||||
// Selecting wine version event
|
||||
self.widgets.wine_selected.connect_selected_notify(clone!(@weak self as this => move |combo_row| {
|
||||
if let Some(model) = combo_row.model() {
|
||||
if model.n_items() > 0 {
|
||||
|
@ -207,6 +242,7 @@ impl App {
|
|||
}
|
||||
}));
|
||||
|
||||
// Selecting dxvk version event
|
||||
self.widgets.dxvk_selected.connect_selected_notify(clone!(@weak self as this => move |combo_row| {
|
||||
if let Some(model) = combo_row.model() {
|
||||
if model.n_items() > 0 {
|
||||
|
@ -289,6 +325,35 @@ impl App {
|
|||
println!("[general page] [update] action: {:?}", &action);
|
||||
|
||||
match action {
|
||||
Actions::VoiceoverPerformAction(i) => {
|
||||
let component = this.widgets.voieover_components[*i].clone();
|
||||
|
||||
if component.is_downloaded(&config.game.path) {
|
||||
// TODO: VoicePackage::delete()
|
||||
todo!();
|
||||
}
|
||||
|
||||
else {
|
||||
let option = (&*this.app).take();
|
||||
this.app.set(option.clone());
|
||||
|
||||
let app = option.unwrap();
|
||||
|
||||
// Add voiceover to config
|
||||
config.game.voices.push(component.package.locale().to_code().to_string());
|
||||
|
||||
config::update(config);
|
||||
|
||||
// Return back, update state and press "download" button if needed
|
||||
app.update(super::main::Actions::PreferencesGoBack).unwrap();
|
||||
app.update_state().then(move |state| {
|
||||
if let Ok(LauncherState::VoiceNotInstalled(_)) = state {
|
||||
app.update(super::main::Actions::PerformButtonEvent).unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Actions::DxvkPerformAction(i) => {
|
||||
let component = this.widgets.dxvk_components[*i].clone();
|
||||
|
||||
|
@ -511,14 +576,21 @@ impl App {
|
|||
/// This method is being called by the `PreferencesStack::update`
|
||||
pub fn prepare(&self, status_page: &adw::StatusPage) -> Result<(), Error> {
|
||||
let config = config::get()?;
|
||||
let game = Game::new(config.game.path);
|
||||
let game = Game::new(&config.game.path);
|
||||
|
||||
self.widgets.game_version.set_tooltip_text(None);
|
||||
self.widgets.patch_version.set_tooltip_text(None);
|
||||
// Update voiceovers states
|
||||
status_page.set_description(Some("Updating voiceovers info..."));
|
||||
|
||||
for package in &*self.widgets.voieover_components {
|
||||
package.update_state(&config.game.path);
|
||||
}
|
||||
|
||||
// Update game version
|
||||
status_page.set_description(Some("Updating game info..."));
|
||||
|
||||
self.widgets.game_version.set_tooltip_text(None);
|
||||
self.widgets.patch_version.set_tooltip_text(None);
|
||||
|
||||
match game.try_get_diff()? {
|
||||
VersionDiff::Latest(version) => {
|
||||
self.widgets.game_version.set_label(&version.to_string());
|
||||
|
@ -587,7 +659,10 @@ impl App {
|
|||
|
||||
impl ToastError for App {
|
||||
fn get_toast_widgets(&self) -> (adw::ApplicationWindow, adw::ToastOverlay) {
|
||||
(self.widgets.window.clone(), self.widgets.toast_overlay.clone())
|
||||
let app = (&*self.app).take();
|
||||
self.app.set(app.clone());
|
||||
|
||||
app.unwrap().get_toast_widgets()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@ use libadwaita as adw;
|
|||
|
||||
use gtk::glib;
|
||||
|
||||
use std::rc::Rc;
|
||||
use std::cell::Cell;
|
||||
use std::io::Error;
|
||||
|
||||
use crate::ui::*;
|
||||
|
@ -18,8 +20,7 @@ pub mod pages {
|
|||
|
||||
#[derive(Clone, glib::Downgrade)]
|
||||
pub struct PreferencesStack {
|
||||
pub window: adw::ApplicationWindow,
|
||||
pub toast_overlay: adw::ToastOverlay,
|
||||
pub app: Rc<Cell<Option<super::MainApp>>>,
|
||||
|
||||
pub preferences: gtk::Box,
|
||||
pub preferences_go_back: gtk::Button,
|
||||
|
@ -34,12 +35,11 @@ pub struct PreferencesStack {
|
|||
}
|
||||
|
||||
impl PreferencesStack {
|
||||
pub fn new(window: adw::ApplicationWindow, toast_overlay: adw::ToastOverlay) -> Result<Self, String> {
|
||||
pub fn new() -> Result<Self, String> {
|
||||
let builder = gtk::Builder::from_string(include_str!("../../../assets/ui/.dist/preferences.ui"));
|
||||
|
||||
let result = Self {
|
||||
window: window.clone(),
|
||||
toast_overlay: toast_overlay.clone(),
|
||||
app: Default::default(),
|
||||
|
||||
preferences: get_object(&builder, "preferences")?,
|
||||
preferences_go_back: get_object(&builder, "preferences_go_back")?,
|
||||
|
@ -48,8 +48,8 @@ impl PreferencesStack {
|
|||
flap: get_object(&builder, "flap")?,
|
||||
|
||||
stack: get_object(&builder, "stack")?,
|
||||
|
||||
general_page: pages::GeneralPage::new(window, toast_overlay)?,
|
||||
|
||||
general_page: pages::GeneralPage::new()?,
|
||||
enhancements_page: pages::EnhancementsPage::new()?
|
||||
};
|
||||
|
||||
|
@ -59,19 +59,25 @@ impl PreferencesStack {
|
|||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn set_app(&mut self, app: super::MainApp) {
|
||||
self.app.set(Some(app.clone()));
|
||||
|
||||
self.general_page.set_app(app);
|
||||
}
|
||||
|
||||
/// Update page info before opening it
|
||||
///
|
||||
/// Being called from the `MainApp` struct
|
||||
pub fn update(&self) -> Result<(), Error> {
|
||||
self.status_page.set_visible(true);
|
||||
self.status_page.show();
|
||||
self.status_page.set_description(None);
|
||||
self.flap.set_visible(false);
|
||||
self.flap.hide();
|
||||
|
||||
self.general_page.prepare(&self.status_page)?;
|
||||
self.enhancements_page.prepare(&self.status_page)?;
|
||||
|
||||
self.status_page.set_visible(false);
|
||||
self.flap.set_visible(true);
|
||||
self.status_page.hide();
|
||||
self.flap.show();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -79,7 +85,10 @@ impl PreferencesStack {
|
|||
|
||||
impl ToastError for PreferencesStack {
|
||||
fn get_toast_widgets(&self) -> (adw::ApplicationWindow, adw::ToastOverlay) {
|
||||
(self.window.clone(), self.toast_overlay.clone())
|
||||
let app = (&*self.app).take();
|
||||
self.app.set(app.clone());
|
||||
|
||||
app.unwrap().get_toast_widgets()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue