- 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:
Observer KRypt0n_ 2022-07-30 11:54:11 +02:00
parent cb1125dfbc
commit 2f0862946f
No known key found for this signature in database
GPG key ID: 844DA47BA25FE1E2
10 changed files with 169 additions and 80 deletions

View file

@ -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"

View file

@ -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")
}

View file

@ -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"]
}
}
}
}

View file

@ -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);

View file

@ -2,3 +2,4 @@ pub mod dxvk_row;
pub mod wine_group;
pub mod wine_row;
pub mod progress_bar;
pub mod voiceover_row;

View 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 {}

View file

@ -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);

View file

@ -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: &gtk::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));

View file

@ -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()
}
}

View file

@ -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()
}
}