mirror of
https://github.com/an-anime-team/an-anime-game-launcher.git
synced 2025-02-03 14:30:50 +03:00
Synced project structure with krypt0nn/gtk-example-app
This commit is contained in:
parent
8f65f6f3a8
commit
fd6e729cd4
5 changed files with 188 additions and 82 deletions
|
@ -79,7 +79,7 @@ Adw.ApplicationWindow window {
|
|||
spacing: 20;
|
||||
|
||||
Gtk.ProgressBar progress_bar {
|
||||
text: "Downloading: 37% (3.7 of 10 GB)\n";
|
||||
text: "Downloading: 37% (3.7 of 10 GB)";
|
||||
show-text: true;
|
||||
|
||||
width-request: 260;
|
||||
|
|
18
src/main.rs
18
src/main.rs
|
@ -1,6 +1,8 @@
|
|||
use gtk4::{self as gtk, prelude::*};
|
||||
use libadwaita::{self as adw, prelude::*};
|
||||
|
||||
use gtk::{CssProvider, StyleContext, gdk::Display, STYLE_PROVIDER_PRIORITY_APPLICATION};
|
||||
|
||||
pub mod ui;
|
||||
pub mod lib;
|
||||
|
||||
|
@ -19,9 +21,21 @@ fn main() {
|
|||
|
||||
// Init app window and show it
|
||||
application.connect_activate(|app| {
|
||||
let app = MainApp::new(app).expect("Failed to init MainApp");
|
||||
// Apply CSS styles to the application
|
||||
let provider = CssProvider::new();
|
||||
|
||||
app.show();
|
||||
provider.load_from_data(include_bytes!("styles.css"));
|
||||
|
||||
StyleContext::add_provider_for_display(
|
||||
&Display::default().expect("Could not connect to a display"),
|
||||
&provider,
|
||||
STYLE_PROVIDER_PRIORITY_APPLICATION
|
||||
);
|
||||
|
||||
// Load main window and show it
|
||||
let main = MainApp::new(app).expect("Failed to init MainApp");
|
||||
|
||||
main.show();
|
||||
});
|
||||
|
||||
// Run app
|
||||
|
|
3
src/styles.css
Normal file
3
src/styles.css
Normal file
|
@ -0,0 +1,3 @@
|
|||
progressbar > text {
|
||||
margin-bottom: 4px;
|
||||
}
|
105
src/ui/main.rs
105
src/ui/main.rs
|
@ -71,13 +71,21 @@ impl AppWidgets {
|
|||
/// This enum is used to describe an action inside of this application
|
||||
///
|
||||
/// 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)]
|
||||
#[derive(Debug, glib::Downgrade)]
|
||||
pub enum Actions {
|
||||
OpenPreferencesPage,
|
||||
PreferencesGoBack,
|
||||
LaunchGame
|
||||
}
|
||||
|
||||
impl Actions {
|
||||
pub fn into_fn<T: gtk::glib::IsA<gtk::Widget>>(&self, app: &App) -> Box<dyn Fn(&T)> {
|
||||
Box::new(clone!(@weak self as action, @strong app => move |_| {
|
||||
app.update(action);
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// This enum is used to store some of this application data
|
||||
///
|
||||
/// In this example we store a counter here to know what should we increment or decrement
|
||||
|
@ -100,7 +108,8 @@ pub struct Values;
|
|||
#[derive(Clone, glib::Downgrade)]
|
||||
pub struct App {
|
||||
widgets: AppWidgets,
|
||||
values: Rc<Cell<Values>>
|
||||
values: Rc<Cell<Values>>,
|
||||
actions: Rc<Cell<Option<glib::Sender<Actions>>>>
|
||||
}
|
||||
|
||||
impl App {
|
||||
|
@ -108,8 +117,9 @@ impl App {
|
|||
pub fn new(app: >k::Application) -> Result<Self, String> {
|
||||
let result = Self {
|
||||
widgets: AppWidgets::try_get()?,
|
||||
values: Default::default()
|
||||
}.init_events();
|
||||
values: Default::default(),
|
||||
actions: Default::default()
|
||||
}.init_events().init_actions();
|
||||
|
||||
// Bind app to the window
|
||||
result.widgets.window.set_application(Some(app));
|
||||
|
@ -120,51 +130,72 @@ impl App {
|
|||
/// Add default events and values to the widgets
|
||||
fn init_events(self) -> Self {
|
||||
// Open preferences page
|
||||
self.widgets.open_preferences.connect_clicked(clone!(@strong self as this => move |_| {
|
||||
this.update(Actions::OpenPreferencesPage);
|
||||
}));
|
||||
self.widgets.open_preferences.connect_clicked(Actions::OpenPreferencesPage.into_fn(&self));
|
||||
|
||||
// Go back button for preferences page
|
||||
self.widgets.preferences_stack.preferences_go_back.connect_clicked(clone!(@strong self as this => move |_| {
|
||||
this.update(Actions::PreferencesGoBack);
|
||||
}));
|
||||
self.widgets.preferences_stack.preferences_go_back.connect_clicked(Actions::PreferencesGoBack.into_fn(&self));
|
||||
|
||||
// Launch game
|
||||
self.widgets.launch_game.connect_clicked(clone!(@strong self as this => move |_| {
|
||||
this.update(Actions::LaunchGame);
|
||||
self.widgets.launch_game.connect_clicked(Actions::LaunchGame.into_fn(&self));
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Add actions processors
|
||||
///
|
||||
/// Changes will happen in the main thread so you can call `update` method from separate thread
|
||||
pub fn init_actions(self) -> Self {
|
||||
let (sender, receiver) = glib::MainContext::channel::<Actions>(glib::PRIORITY_DEFAULT);
|
||||
|
||||
receiver.attach(None, clone!(@strong self as this => move |action| {
|
||||
let values = this.values.take();
|
||||
|
||||
// Some debug output
|
||||
println!("[update] action: {:?}, values: {:?}", &action, &values);
|
||||
|
||||
match action {
|
||||
Actions::OpenPreferencesPage => {
|
||||
this.widgets.leaflet.set_visible_child_name("preferences_page");
|
||||
|
||||
if let Err(err) = this.widgets.preferences_stack.update() {
|
||||
this.toast_error("Failed to update preferences", err);
|
||||
}
|
||||
}
|
||||
|
||||
Actions::PreferencesGoBack => {
|
||||
this.widgets.leaflet.navigate(adw::NavigationDirection::Back);
|
||||
}
|
||||
|
||||
Actions::LaunchGame => {
|
||||
// Display toast message if the game is failed to run
|
||||
if let Err(err) = game::run(false) {
|
||||
this.toast_error("Failed to run game", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.values.set(values);
|
||||
|
||||
glib::Continue(true)
|
||||
}));
|
||||
|
||||
self.actions.set(Some(sender));
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Update widgets state by calling some action
|
||||
pub fn update(&self, action: Actions) {
|
||||
let values = self.values.take();
|
||||
pub fn update(&self, action: Actions) -> Result<(), std::sync::mpsc::SendError<Actions>> {
|
||||
let actions = self.actions.take();
|
||||
|
||||
let result = match &actions {
|
||||
Some(sender) => Ok(sender.send(action)?),
|
||||
None => Ok(())
|
||||
};
|
||||
|
||||
println!("[update] action: {:?}, counter: {:?}", &action, &values);
|
||||
self.actions.set(actions);
|
||||
|
||||
match action {
|
||||
Actions::OpenPreferencesPage => {
|
||||
self.widgets.leaflet.set_visible_child_name("preferences_page");
|
||||
|
||||
if let Err(err) = self.widgets.preferences_stack.update() {
|
||||
self.toast_error("Failed to update preferences", err);
|
||||
}
|
||||
}
|
||||
|
||||
Actions::PreferencesGoBack => {
|
||||
self.widgets.leaflet.navigate(adw::NavigationDirection::Back);
|
||||
}
|
||||
|
||||
Actions::LaunchGame => {
|
||||
// Display toast message if the game is failed to run
|
||||
if let Err(err) = game::run(false) {
|
||||
self.toast_error("Failed to run game", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.values.set(values);
|
||||
result
|
||||
}
|
||||
|
||||
/// Show application window
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use gtk4::{self as gtk, prelude::*};
|
||||
use libadwaita::{self as adw, prelude::*};
|
||||
|
||||
use gtk4::glib;
|
||||
use gtk4::glib::clone;
|
||||
use gtk::glib;
|
||||
use gtk::glib::clone;
|
||||
use gtk::Align;
|
||||
|
||||
use std::rc::Rc;
|
||||
use std::cell::Cell;
|
||||
|
@ -30,7 +31,7 @@ pub struct AppWidgets {
|
|||
pub dxvk_vanilla: adw::ExpanderRow,
|
||||
pub dxvk_async: adw::ExpanderRow,
|
||||
|
||||
pub dxvk_components: Rc<Vec<(adw::ActionRow, dxvk::Version)>>
|
||||
pub dxvk_components: Rc<Vec<(adw::ActionRow, gtk::Button, dxvk::Version)>>
|
||||
}
|
||||
|
||||
impl AppWidgets {
|
||||
|
@ -58,38 +59,28 @@ impl AppWidgets {
|
|||
|
||||
let mut components = Vec::new();
|
||||
|
||||
for version in list.vanilla {
|
||||
let row = adw::ActionRow::new();
|
||||
let button = gtk::Button::new();
|
||||
for (i, versions) in [list.vanilla, list.r#async].into_iter().enumerate() {
|
||||
for version in versions {
|
||||
let row = adw::ActionRow::new();
|
||||
let button = gtk::Button::new();
|
||||
|
||||
row.set_title(&version.version);
|
||||
row.set_visible(version.recommended);
|
||||
row.set_title(&version.version);
|
||||
row.set_visible(version.recommended);
|
||||
|
||||
button.set_icon_name("document-save-symbolic");
|
||||
button.set_valign(gtk::Align::Center);
|
||||
button.add_css_class("flat");
|
||||
button.set_icon_name("document-save-symbolic");
|
||||
button.set_valign(gtk::Align::Center);
|
||||
button.add_css_class("flat");
|
||||
|
||||
row.add_suffix(&button);
|
||||
row.add_suffix(&button);
|
||||
|
||||
result.dxvk_vanilla.add_row(&row);
|
||||
components.push((row, version));
|
||||
}
|
||||
match i {
|
||||
0 => result.dxvk_vanilla.add_row(&row),
|
||||
1 => result.dxvk_async.add_row(&row),
|
||||
_ => ()
|
||||
}
|
||||
|
||||
for version in list.r#async {
|
||||
let row = adw::ActionRow::new();
|
||||
let button = gtk::Button::new();
|
||||
|
||||
row.set_title(&version.version);
|
||||
row.set_visible(version.recommended);
|
||||
|
||||
button.set_icon_name("document-save-symbolic");
|
||||
button.set_valign(gtk::Align::Center);
|
||||
button.add_css_class("flat");
|
||||
|
||||
row.add_suffix(&button);
|
||||
|
||||
result.dxvk_async.add_row(&row);
|
||||
components.push((row, version));
|
||||
components.push((row, button, version));
|
||||
}
|
||||
}
|
||||
|
||||
result.dxvk_components = Rc::new(components);
|
||||
|
@ -101,9 +92,17 @@ impl AppWidgets {
|
|||
/// This enum is used to describe an action inside of this application
|
||||
///
|
||||
/// 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)]
|
||||
#[derive(Debug, glib::Downgrade)]
|
||||
pub enum Actions {
|
||||
DownloadDXVK(Rc<usize>)
|
||||
}
|
||||
|
||||
impl Actions {
|
||||
pub fn into_fn<T: gtk::glib::IsA<gtk::Widget>>(&self, app: &App) -> Box<dyn Fn(&T)> {
|
||||
Box::new(clone!(@weak self as action, @strong app => move |_| {
|
||||
app.update(action);
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// This enum is used to store some of this application data
|
||||
|
@ -128,7 +127,8 @@ pub struct Values;
|
|||
#[derive(Clone, glib::Downgrade)]
|
||||
pub struct App {
|
||||
widgets: AppWidgets,
|
||||
values: Rc<Cell<Values>>
|
||||
values: Rc<Cell<Values>>,
|
||||
actions: Rc<Cell<Option<glib::Sender<Actions>>>>
|
||||
}
|
||||
|
||||
impl App {
|
||||
|
@ -136,8 +136,9 @@ impl App {
|
|||
pub fn new() -> Result<Self, String> {
|
||||
let result = Self {
|
||||
widgets: AppWidgets::try_get()?,
|
||||
values: Default::default()
|
||||
}.init_events();
|
||||
values: Default::default(),
|
||||
actions: Default::default()
|
||||
}.init_events().init_actions();
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
@ -145,8 +146,8 @@ impl App {
|
|||
/// Add default events and values to the widgets
|
||||
fn init_events(self) -> Self {
|
||||
// Set DXVK recommended only switcher event
|
||||
self.widgets.dxvk_recommended_only.connect_state_notify(clone!(@weak self as this => move |switcher| {
|
||||
for (component, version) in &*this.widgets.dxvk_components {
|
||||
self.widgets.dxvk_recommended_only.connect_state_notify(clone!(@strong self as this => move |switcher| {
|
||||
for (component, _, version) in &*this.widgets.dxvk_components {
|
||||
component.set_visible(if switcher.state() {
|
||||
version.recommended
|
||||
} else {
|
||||
|
@ -155,18 +156,73 @@ impl App {
|
|||
}
|
||||
}));
|
||||
|
||||
// DXVK install/remove buttons
|
||||
let components = (*self.widgets.dxvk_components).clone();
|
||||
|
||||
for (i, (_, button, _)) in components.into_iter().enumerate() {
|
||||
button.connect_clicked(Actions::DownloadDXVK(Rc::new(i)).into_fn(&self));
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Add actions processors
|
||||
///
|
||||
/// Changes will happen in the main thread so you can call `update` method from separate thread
|
||||
pub fn init_actions(self) -> Self {
|
||||
let (sender, receiver) = glib::MainContext::channel::<Actions>(glib::PRIORITY_DEFAULT);
|
||||
|
||||
receiver.attach(None, clone!(@strong self as this => move |action| {
|
||||
let values = this.values.take();
|
||||
|
||||
// Some debug output
|
||||
println!("[update] action: {:?}, values: {:?}", &action, &values);
|
||||
|
||||
match action {
|
||||
Actions::DownloadDXVK(i) => {
|
||||
println!("123");
|
||||
|
||||
let (row, button, version) = &this.widgets.dxvk_components[*i];
|
||||
|
||||
let progress_bar = gtk::ProgressBar::new();
|
||||
|
||||
progress_bar.set_text(Some("Downloading: 0%"));
|
||||
progress_bar.set_show_text(true);
|
||||
|
||||
progress_bar.set_width_request(200);
|
||||
progress_bar.set_valign(Align::Center);
|
||||
|
||||
button.set_visible(false);
|
||||
|
||||
row.add_suffix(&progress_bar);
|
||||
|
||||
row.remove(&progress_bar);
|
||||
button.set_visible(true);
|
||||
}
|
||||
}
|
||||
|
||||
this.values.set(values);
|
||||
|
||||
glib::Continue(true)
|
||||
}));
|
||||
|
||||
self.actions.set(Some(sender));
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Update widgets state by calling some action
|
||||
pub fn update(&self, action: Actions) {
|
||||
/*let values = self.values.take();
|
||||
pub fn update(&self, action: Actions) -> Result<(), std::sync::mpsc::SendError<Actions>> {
|
||||
let actions = self.actions.take();
|
||||
|
||||
let result = match &actions {
|
||||
Some(sender) => Ok(sender.send(action)?),
|
||||
None => Ok(())
|
||||
};
|
||||
|
||||
match action {
|
||||
|
||||
}
|
||||
self.actions.set(actions);
|
||||
|
||||
self.values.set(values);*/
|
||||
result
|
||||
}
|
||||
|
||||
pub fn title() -> String {
|
||||
|
@ -247,3 +303,5 @@ impl App {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for App {}
|
||||
|
|
Loading…
Add table
Reference in a new issue