This commit is contained in:
Philip Kristoffersen
2022-04-09 21:49:11 +02:00
committed by GitHub
parent 2a1523470f
commit a0c6fe0c8f
22 changed files with 2349 additions and 572 deletions
+20 -8
View File
@@ -67,6 +67,19 @@ jobs:
strip: false strip: false
ui: false ui: false
target: aarch64-apple-darwin target: aarch64-apple-darwin
- os: macos-latest
file: target/release/boilr
asset_name: macos_x86_64_ui_BoilR
args: -9
strip: false
ui: true
- os: macos-latest
file: target/aarch64-apple-darwin/release/boilr
asset_name: macos_darwin_ui_BoilR
args: -9
strip: false
ui: true
target: aarch64-apple-darwin
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v1
- name: Rust Cache - name: Rust Cache
@@ -76,26 +89,25 @@ jobs:
if: ${{ matrix.os == 'ubuntu-latest' && matrix.ui}} if: ${{ matrix.os == 'ubuntu-latest' && matrix.ui}}
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install -y libx11-dev libxext-dev libxft-dev libxinerama-dev libxcursor-dev libxrender-dev libxfixes-dev libpango1.0-dev libpng-dev libgl1-mesa-dev libglu1-mesa-dev sudo apt-get install -y libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev libspeechd-dev libxkbcommon-dev libssl-dev
- name: Build Release Version UI - name: Build Release Version UI
if: ${{ matrix.ui}} if: ${{ matrix.target != 'aarch64-apple-darwin' && matrix.ui == true}}
run: cargo build --release --features ui run: cargo build --release --features ui
- name: Build Release Version CLI - name: Build Release Version CLI
if: ${{ matrix.ui == false && matrix.target != 'aarch64-apple-darwin'}} if: ${{ matrix.target != 'aarch64-apple-darwin' && matrix.ui == false}}
run: cargo build --release run: cargo build --release
- name: Build aarch64 Release Version CLI - name: Build aarch64 Release Version CLI
if: ${{ matrix.ui == false && matrix.target == 'aarch64-apple-darwin'}} if: ${{matrix.target == 'aarch64-apple-darwin' && matrix.ui == false}}
run: rustup target add aarch64-apple-darwin && cargo build --release --target aarch64-apple-darwin run: rustup target add aarch64-apple-darwin && cargo build --release --target aarch64-apple-darwin
- name: Build aarch64 Release Version UI
if: ${{matrix.target == 'aarch64-apple-darwin' && matrix.ui == true}}
run: rustup target add aarch64-apple-darwin && cargo build --release --target aarch64-apple-darwin --features ui
- name: Compress binaries - name: Compress binaries
if: ${{ (matrix.os == 'windows-latest' || matrix.ui == false) && matrix.os != 'macos-latest' }}
uses: svenstaro/upx-action@v2 uses: svenstaro/upx-action@v2
with: with:
file: ${{ matrix.file }} file: ${{ matrix.file }}
args: ${{ matrix.args }} args: ${{ matrix.args }}
strip: ${{ matrix.strip }} strip: ${{ matrix.strip }}
- name: Strip binaries
if: ${{ matrix.os == 'ubuntu-latest' && matrix.ui == true}}
run: strip ${{ matrix.file }}
- name: Upload Release Asset - name: Upload Release Asset
id: upload-release-asset id: upload-release-asset
uses: actions/upload-release-asset@v1 uses: actions/upload-release-asset@v1
Generated
+1891 -158
View File
File diff suppressed because it is too large Load Diff
+6 -10
View File
@@ -5,7 +5,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
steam_shortcuts_util = {git="https://github.com/PhilipK/steam_shortcuts_util"} steam_shortcuts_util = { git = "https://github.com/PhilipK/steam_shortcuts_util",rev="c8953ecfc9c612be49c537fb3add683cd83f9379" }
steamgriddb_api = "^0.2,0" steamgriddb_api = "^0.2,0"
serde = { version = "^1.0.136", features = ["derive"] } serde = { version = "^1.0.136", features = ["derive"] }
serde_json = "^1.0.79" serde_json = "^1.0.79"
@@ -13,25 +13,21 @@ tokio = { version = "^1.17.0", features = ["full"] }
reqwest = { version = "^0.11.10", default_features = false } reqwest = { version = "^0.11.10", default_features = false }
config = "^0.11.0" config = "^0.11.0"
failure = "^0.1.8" failure = "^0.1.8"
#https://fltk-rs.github.io/fltk-rs/
fltk = { version = "^1.3.2", features = ["fltk-bundled"], optional = true }
nom = "^7.1.1" nom = "^7.1.1"
nom_locate = "^4.0.0"
flate2 = "^1.0.22" flate2 = "^1.0.22"
toml = { version = "^0.5.8", optional = true }
futures = { version = "^0.3.21" } futures = { version = "^0.3.21" }
dashmap = { version = "^5.2.0", features = ["serde"] } dashmap = { version = "^5.2.0", features = ["serde"] }
fltk-theme = {version ="^0.4.2" , optional = true }
is_executable = "^1.0.1" is_executable = "^1.0.1"
rusty-leveldb = "^0.3.6" rusty-leveldb = "^0.3.6"
base64 = "^0.13.0" base64 = "^0.13.0"
eframe = { version = "0.17.0", optional = true }
[build-dependencies] egui = { version = "0.17.0", optional = true }
fl2rust = { version = "^0.4", optional = true } image = { version = "0.24.1", optional = true, features = ["png"] }
toml = { version = "^0.5.8", optional = true }
[features] [features]
#default = ["ui"] #default = ["ui"]
ui = ["fltk", "fl2rust", "toml", "fltk-theme"] ui = ["eframe", "egui","toml","image"]
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
+6 -5
View File
@@ -2,7 +2,9 @@
## Description ## Description
This little tool will show games from other games platforms in your Steam library. BoilR automatically adds (almost) all your games to your Steam library.
![BoilR Screenshot](screenshot_1.png)
It uses the Steam 3rd party shortcuts feature and does not require you to set up anything. It uses the Steam 3rd party shortcuts feature and does not require you to set up anything.
The goal is that you do not have to leave your Steam library to launch games from other launchers/stores, so that you can find all the games that you have available. The goal is that you do not have to leave your Steam library to launch games from other launchers/stores, so that you can find all the games that you have available.
@@ -14,8 +16,8 @@ Optionally you can set BoilR up to automatically download artwork from [SteamGri
- [x] Automatically download art from [SteamGridDB](https://www.steamgriddb.com/) - [x] Automatically download art from [SteamGridDB](https://www.steamgriddb.com/)
- [x] Cross Platform (Windows, Linux, Mac, Steam Deck) - [x] Cross Platform (Windows, Linux, Mac, Steam Deck)
- [x] Standalone / No install needed - [x] Standalone / No install needed
- [x] Small (~1.5mb on disk) - [x] Small (~3mb on disk)
- [x] Lightweight (~2mb ram) - [x] Lightweight (~30mb ram)
- [x] Fast synchronization (~1 second) - [x] Fast synchronization (~1 second)
## Integrations ## Integrations
@@ -44,8 +46,7 @@ Optionally you can set BoilR up to automatically download artwork from [SteamGri
## Getting cover art for your shortcuts ## Getting cover art for your shortcuts
- Get a [SteamGridDB API key](https://www.steamgriddb.com/profile/preferences/api) - Get a [SteamGridDB API key](https://www.steamgriddb.com/profile/preferences/api)
- Run BoilR put the auth key in the input. - Run BoilR put the auth key in the input (in the settings section).
- Click Save
- Click Syncrhonize - Click Syncrhonize
-18
View File
@@ -1,18 +0,0 @@
#[cfg(feature = "ui")]
use std::env;
#[cfg(feature = "ui")]
use std::path::PathBuf;
fn main() {
#[cfg(feature = "ui")]
{
println!("cargo:rerun-if-changed=gui/mainview.fl");
let g = fl2rust::Generator::default();
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
g.in_out(
"gui/mainview.fl",
out_path.join("mainview.rs").to_str().unwrap(),
)
.expect("Failed to generate rust from fl file!");
}
}
-129
View File
@@ -1,129 +0,0 @@
# data file for the Fltk User Interface Designer (fluid)
version 1.0302
header_name {.h}
code_name {.cxx}
class UserInterface {open
} {
Function {make_window()} {open
} {
Fl_Window win {
label {BoilR} open
xywh {1 1 500 575} type Double visible
} {
# ----- Steam -----
Fl_Check_Button steam_create_collections {
label {Create Collections}
tooltip {Create Collections for each platform}
xywh {25 25 155 25} down_box DOWN_BOX
}
Fl_Input steam_location_input {
label {Steam Location}
xywh {175 25 300 25} align 5
}
# ----- Art download -----
Fl_Check_Button enable_steamgrid_db_checkbox {
label {Download Images}
tooltip {Download coverart for games from www.steamgriddb.com}
xywh {25 75 155 25} down_box DOWN_BOX
}
Fl_Input steamgrid_db_auth_key_input {
label {SteamGridDb API Key}
tooltip {See how to get your key here https://github.com/PhilipK/BoilR#getting-cover-art-for-your-shortcuts}
xywh {175 75 300 25} align 5
}
# ----- Epic games -----
Fl_Check_Button enable_egs_checkbox {
label {Epic Games}
xywh {25 125 155 25} down_box DOWN_BOX
}
Fl_Input epic_location_input {
label {Epic Folder}
xywh {175 125 300 25} align 5
}
# ----- Legendary/Rare -----
Fl_Check_Button enable_legendary_checkbox {
label {Legendary / Rare}
xywh {25 175 155 25} down_box DOWN_BOX
}
Fl_Input legendary_executable_input {
label {Legendary Executable}
xywh {175 175 300 25} align 5
}
# ----- Itch -----
Fl_Check_Button enable_itch_checkbox {
label {Itch.io}
xywh {25 225 155 25} down_box DOWN_BOX
}
Fl_Input itch_locatoin_input {
label {Itch Folder}
xywh {175 225 300 25} align 5
}
# ----- Origin -----
Fl_Group origin_group {
xywh {25 275 375 50} align 5
} {
Fl_Check_Button enable_origin_checkbox {
label Origin
xywh {25 275 155 25} down_box DOWN_BOX
}
}
# ----- GOG -----
Fl_Check_Button enable_gog_checkbox {
label {GOG Galaxy}
xywh {25 325 155 25} down_box DOWN_BOX
}
Fl_Input gog_folder_input {
label {GOG Data Folder}
xywh {175 325 300 25} align 5
}
Fl_Input gog_winedrive_input {
label {GOG Wine C Drive}
xywh {175 375 300 25} align 5
}
# ----- Uplay -----
Fl_Check_Button enable_uplay_checkbox {
label Uplay
xywh {25 375 155 25} down_box DOWN_BOX
}
# ----- Lutris -----
Fl_Check_Button enable_lutris_checkbox {
label Lutris
xywh {25 425 155 25} down_box DOWN_BOX
}
Fl_Input lutris_executable_input {
label {Lutris Executable}
xywh {175 425 300 25} align 5
}
# ----- Heroic -----
Fl_Check_Button enable_heroic_checkbox {
label Heroic
xywh {25 475 155 25} down_box DOWN_BOX
}
# ----- Buttons -----
Fl_Button save_settings_button {
label {Save Settings}
xywh {25 525 125 25}
}
Fl_Button synchronize_button {
label {Synchronize Games}
xywh {175 525 300 25}
}
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 941 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 866 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 584 B

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

+3 -1
View File
@@ -1,4 +1,5 @@
debug= false debug= false
blacklisted_games = []
[steamgrid_db] [steamgrid_db]
auth_key = "Write your authentication key between these quotes" auth_key = "Write your authentication key between these quotes"
@@ -27,7 +28,7 @@ enabled = true
create_symlinks = true create_symlinks = true
[lutris] [lutris]
enabled = true enabled = false
[heroic] [heroic]
enabled = true enabled = true
@@ -35,3 +36,4 @@ enabled = true
[steam] [steam]
create_collections = false create_collections = false
optimize_for_big_picture = false optimize_for_big_picture = false
+10 -9
View File
@@ -1,8 +1,9 @@
mod egs; mod egs;
mod gog; mod gog;
mod heroic;
mod itch; mod itch;
mod lutris;
mod legendary; mod legendary;
mod lutris;
mod origin; mod origin;
mod platform; mod platform;
mod settings; mod settings;
@@ -10,23 +11,23 @@ mod steam;
mod steamgriddb; mod steamgriddb;
mod sync; mod sync;
mod uplay; mod uplay;
mod heroic;
#[cfg(feature = "ui")] #[cfg(feature = "ui")]
mod ui; mod ui;
use std::error::Error; use std::error::Error;
#[cfg(not(feature = "ui"))]
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> { async fn main() -> Result<(), Box<dyn Error>> {
#[cfg(feature = "ui")]
{
ui::run_ui().await
}
#[cfg(not(feature = "ui"))]
{
let settings = settings::Settings::new()?; let settings = settings::Settings::new()?;
settings::Settings::write_config_if_missing(); settings::Settings::write_config_if_missing();
sync::run_sync(&settings).await let usersinfo = sync::run_sync(&settings).unwrap();
sync::download_images(&settings,&usersinfo).await;
Ok(())
} }
#[cfg(feature = "ui")]
fn main() -> Result<(), Box<dyn Error>> {
ui::run_ui()
} }
+1
View File
@@ -11,6 +11,7 @@ use std::env;
#[derive(Debug, Deserialize, Serialize, Clone)] #[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Settings { pub struct Settings {
pub debug: bool, pub debug: bool,
pub blacklisted_games: Vec<u32>,
pub epic_games: EpicGamesLauncherSettings, pub epic_games: EpicGamesLauncherSettings,
pub legendary: LegendarySettings, pub legendary: LegendarySettings,
pub itch: ItchSettings, pub itch: ItchSettings,
+2 -2
View File
@@ -52,7 +52,7 @@ pub struct SteamUsersInfo {
/// Get the paths to the steam users shortcuts (one for each user) /// Get the paths to the steam users shortcuts (one for each user)
pub fn get_shortcuts_paths( pub fn get_shortcuts_paths(
settings: &SteamSettings, settings: &SteamSettings,
) -> Result<Vec<SteamUsersInfo>, Box<dyn Error>> { ) -> Result<Vec<SteamUsersInfo>, Box<dyn Error + Sync + Send>> {
let user_location = settings.location.clone(); let user_location = settings.location.clone();
let steam_path_str = match user_location { let steam_path_str = match user_location {
Some(location) => location, Some(location) => location,
@@ -107,7 +107,7 @@ pub fn get_shortcuts_paths(
Ok(users_info) Ok(users_info)
} }
pub fn get_default_location() -> Result<String, Box<dyn Error>> { pub fn get_default_location() -> Result<String, Box<dyn Error + Sync + Send>> {
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
let path_string = { let path_string = {
let key = "PROGRAMFILES(X86)"; let key = "PROGRAMFILES(X86)";
+2 -2
View File
@@ -230,7 +230,7 @@ async fn get_images_for_ids(
download_animated: bool, download_animated: bool,
) -> Result< ) -> Result<
Vec<steamgriddb_api::response::SteamGridDbResult<steamgriddb_api::images::Image>>, Vec<steamgriddb_api::response::SteamGridDbResult<steamgriddb_api::images::Image>>,
Box<dyn std::error::Error>, String,
> { > {
use steamgriddb_api::query_parameters::AnimtionType; use steamgriddb_api::query_parameters::AnimtionType;
use steamgriddb_api::query_parameters::QueryType::*; use steamgriddb_api::query_parameters::QueryType::*;
@@ -276,7 +276,7 @@ async fn get_images_for_ids(
let image_search_result = client.get_images_for_ids(image_ids, &query_type).await; let image_search_result = client.get_images_for_ids(image_ids, &query_type).await;
image_search_result image_search_result.map_err(|e| format!("Image search failed {:?}",e))
} }
async fn get_steam_image_url(game_id: usize, image_type: &ImageType) -> Option<String> { async fn get_steam_image_url(game_id: usize, image_type: &ImageType) -> Option<String> {
+2
View File
@@ -3,3 +3,5 @@ mod symlinks;
mod synchronization; mod synchronization;
pub use synchronization::run_sync; pub use synchronization::run_sync;
pub use synchronization::download_images;
pub use synchronization::get_platform_shortcuts;
+11 -7
View File
@@ -24,13 +24,15 @@ use std::{fs::File, io::Write, path::Path};
const BOILR_TAG: &str = "boilr"; const BOILR_TAG: &str = "boilr";
pub async fn run_sync(settings: &Settings) -> Result<(), Box<dyn Error>> { pub fn run_sync(settings: &Settings) -> Result<Vec<SteamUsersInfo>, String> {
let mut userinfo_shortcuts = get_shortcuts_paths(&settings.steam)?; let mut userinfo_shortcuts = get_shortcuts_paths(&settings.steam)
.map_err(|e| format!("Getting shortcut paths failed: {e}"))?;
let platform_shortcuts = get_platform_shortcuts(settings); let platform_shortcuts = get_platform_shortcuts(settings);
let all_shortcuts: Vec<ShortcutOwned> = platform_shortcuts let all_shortcuts: Vec<ShortcutOwned> = platform_shortcuts
.iter() .iter()
.flat_map(|s| s.1.clone()) .flat_map(|s| s.1.clone())
.filter(|s| !settings.blacklisted_games.contains(&s.app_id))
.collect(); .collect();
for shortcut in &all_shortcuts { for shortcut in &all_shortcuts {
println!("Appid: {} name: {}", shortcut.app_id, shortcut.app_name); println!("Appid: {} name: {}", shortcut.app_id, shortcut.app_name);
@@ -69,15 +71,17 @@ pub async fn run_sync(settings: &Settings) -> Result<(), Box<dyn Error>> {
println!("Finished synchronizing games in: {:?}", duration); println!("Finished synchronizing games in: {:?}", duration);
} }
Ok(userinfo_shortcuts)
}
pub async fn download_images(settings: &Settings, userinfo_shortcuts: &Vec<SteamUsersInfo>) {
if settings.steamgrid_db.enabled { if settings.steamgrid_db.enabled {
if settings.steamgrid_db.prefer_animated { if settings.steamgrid_db.prefer_animated {
println!("downloading animated images"); println!("downloading animated images");
download_images_for_users(settings, &userinfo_shortcuts, true).await; download_images_for_users(settings, userinfo_shortcuts, true).await;
} }
download_images_for_users(settings, &userinfo_shortcuts, false).await; download_images_for_users(settings, userinfo_shortcuts, false).await;
} }
Ok(())
} }
fn remove_old_shortcuts(shortcut_info: &mut ShortcutInfo) { fn remove_old_shortcuts(shortcut_info: &mut ShortcutInfo) {
@@ -138,7 +142,7 @@ fn write_shortcut_collections<S: AsRef<str>>(
Ok(()) Ok(())
} }
fn get_platform_shortcuts(settings: &Settings) -> Vec<(String, Vec<ShortcutOwned>)> { pub fn get_platform_shortcuts(settings: &Settings) -> Vec<(String, Vec<ShortcutOwned>)> {
let mut platform_results = vec![ let mut platform_results = vec![
update_platform_shortcuts(&EpicPlatform::new(settings.epic_games.clone())), update_platform_shortcuts(&EpicPlatform::new(settings.epic_games.clone())),
update_platform_shortcuts(&LegendaryPlatform::new(settings.legendary.clone())), update_platform_shortcuts(&LegendaryPlatform::new(settings.legendary.clone())),
+48
View File
@@ -0,0 +1,48 @@
pub mod ui_colors {
use egui::Color32;
pub const TEXT_COLOR: Color32 = Color32::from_rgb(255, 212, 163);
pub const BACKGROUND_COLOR: Color32 = Color32::from_rgb(13, 43, 69);
pub const BG_STROKE_COLOR: Color32 = Color32::from_rgb(32, 60, 86);
// pub const WHITE:Color32 = Color32::from_rgb(255, 236, 214);
pub const LIGHT_ORANGE: Color32 = Color32::from_rgb(255, 212, 163);
pub const ORANGE: Color32 = Color32::from_rgb(255, 170, 94);
pub const PURLPLE: Color32 = Color32::from_rgb(84, 78, 104);
}
pub mod ui_images {
use eframe::epi::IconData;
use egui::{ColorImage, ImageData};
pub const IMPORT_GAMES_IMAGE: &[u8] = include_bytes!("../../resources/import_games_button.png");
pub const LOGO_32: &[u8] = include_bytes!("../../resources/logo32.png");
pub const LOGO_ICON: &[u8] = include_bytes!("../../resources/logo_small.png");
pub fn get_import_image() -> ImageData {
ImageData::Color(load_image_from_memory(IMPORT_GAMES_IMAGE).unwrap())
}
pub fn get_logo() -> ImageData {
ImageData::Color(load_image_from_memory(LOGO_32).unwrap())
}
pub fn get_logo_icon() -> IconData {
let image = image::load_from_memory(LOGO_ICON).unwrap();
let image_buffer = image.to_rgba8();
let pixels = image_buffer.as_flat_samples();
IconData {
height: image.height() as u32,
width: image.width() as u32,
rgba: pixels.as_slice().to_vec(),
}
}
fn load_image_from_memory(image_data: &[u8]) -> Result<ColorImage, image::ImageError> {
let image = image::load_from_memory(image_data)?;
let size = [image.width() as _, image.height() as _];
let image_buffer = image.to_rgba8();
let pixels = image_buffer.as_flat_samples();
Ok(ColorImage::from_rgba_unmultiplied(size, pixels.as_slice()))
}
}
-4
View File
@@ -1,4 +0,0 @@
#![allow(unused_variables)]
#![allow(unused_mut)]
#![allow(unused_imports)]
include!(concat!(env!("OUT_DIR"), "/mainview.rs"));
+4 -5
View File
@@ -1,5 +1,4 @@
mod mainview; mod uiapp;
mod ui; mod defines;
pub use uiapp::*;
pub(crate) use mainview::UserInterface; pub use defines::*;
pub use ui::run_ui;
-208
View File
@@ -1,208 +0,0 @@
use crate::sync::run_sync;
use crate::ui::UserInterface;
use crate::{egs::get_default_location, settings::Settings};
use fltk::{app, prelude::*};
use fltk_theme::{WidgetTheme, ThemeType};
use futures::executor::block_on;
use std::error::Error;
use std::{cell::RefCell, rc::Rc};
pub async fn run_ui() -> Result<(), Box<dyn Error>> {
let app = app::App::default().with_scheme(app::Scheme::Gtk);
let widget_theme = WidgetTheme::new(ThemeType::Greybird);
widget_theme.apply();
let settings = Rc::new(RefCell::new(Settings::new()?));
let mut ui = UserInterface::make_window();
update_ui_with_settings(&mut ui, &settings.clone().borrow());
{
let save_ui = ui.clone();
let mut save_button = ui.save_settings_button.clone();
let save_settings = settings.clone();
save_button.set_callback(move |cb| {
update_settings_with_ui_values(&mut save_settings.borrow_mut(), &save_ui);
save_settings_to_file(&save_settings.borrow());
cb.set_label("Saved");
});
}
{
let mut synch_bytton = ui.synchronize_button.clone();
let synch_ui = ui.clone();
let sync_settings = settings.clone();
synch_bytton.set_callback(move |cb| {
let mut settings_clone = sync_settings.borrow().clone();
update_settings_with_ui_values(&mut settings_clone, &synch_ui);
use tokio::task;
let task = task::spawn_blocking(move || {
let setting_ref = settings_clone.clone();
let synchro_task = run_sync(&setting_ref);
match block_on(synchro_task) {
Ok(_) => println!("Finished sync"),
Err(e) => println!("{}", e),
}
});
match block_on(task){
Ok(_) => cb.set_label("Synched"),
Err(_) => cb.set_label("Error Synching"),
}
});
}
app.run().unwrap();
Ok(())
}
fn empty_or_whitespace(input: String) -> Option<String> {
if input.trim().is_empty() {
None
} else {
Some(input)
}
}
fn update_settings_with_ui_values(settings: &mut Settings, ui: &UserInterface) {
// Steam location
settings.steam.location = empty_or_whitespace(ui.steam_location_input.value());
settings.steam.create_collections = ui.steam_create_collections.value();
// Steamgrid db
settings.steamgrid_db.enabled = ui.enable_steamgrid_db_checkbox.value();
settings.steamgrid_db.auth_key = empty_or_whitespace(ui.steamgrid_db_auth_key_input.value());
// Legendary
settings.legendary.enabled = ui.enable_legendary_checkbox.value();
settings.legendary.executable = empty_or_whitespace(ui.legendary_executable_input.value());
// Origin
settings.origin.enabled = ui.enable_origin_checkbox.value();
// Epic
settings.epic_games.enabled = ui.enable_egs_checkbox.value();
settings.epic_games.location = empty_or_whitespace(ui.epic_location_input.value());
// Itch
settings.itch.enabled = ui.enable_itch_checkbox.value();
settings.itch.location = empty_or_whitespace(ui.itch_locatoin_input.value());
// Gog
settings.gog.enabled = ui.enable_gog_checkbox.value();
settings.gog.location = empty_or_whitespace(ui.gog_folder_input.value());
settings.gog.wine_c_drive = empty_or_whitespace(ui.gog_winedrive_input.value());
// Uplay
settings.uplay.enabled = ui.enable_uplay_checkbox.value();
// Lutris
settings.lutris.enabled = ui.enable_lutris_checkbox.value();
settings.lutris.executable = empty_or_whitespace(ui.lutris_executable_input.value());
// Heroic
settings.heroic.enabled = ui.enable_heroic_checkbox.value();
}
fn save_settings_to_file(settings: &Settings) {
let toml = toml::to_string(&settings).unwrap();
std::fs::write("config.toml", toml).unwrap();
}
fn update_ui_with_settings(ui: &mut UserInterface, settings: &Settings) {
use std::path::Path;
use crate::{gog, itch, steam};
ui.steam_location_input.set_value(
&settings
.steam
.location
.clone()
.unwrap_or(steam::get_default_location().unwrap_or(String::from(""))),
);
ui.steam_create_collections
.set_value(settings.steam.create_collections);
ui.enable_steamgrid_db_checkbox
.set_value(settings.steamgrid_db.enabled);
ui.steamgrid_db_auth_key_input.set_value(
&settings
.steamgrid_db
.auth_key
.clone()
.unwrap_or(String::from("")),
);
ui.enable_legendary_checkbox
.set_value(settings.legendary.enabled);
ui.legendary_executable_input.set_value(
settings
.legendary
.executable
.clone()
.unwrap_or(String::from("legendary"))
.as_str(),
);
ui.enable_egs_checkbox
.set_value(settings.epic_games.enabled);
let default_egs = get_default_location();
let default_egs = default_egs
.map(|p| p.to_str().unwrap().to_owned())
.unwrap_or(String::from(""));
let egs_location = settings
.epic_games
.location
.clone()
.unwrap_or(String::from(default_egs));
ui.epic_location_input.set_value(&egs_location);
ui.enable_egs_checkbox
.set_value(settings.epic_games.enabled);
ui.enable_itch_checkbox.set_value(settings.itch.enabled);
let mut default_itch_location = itch::get_default_location();
if !Path::new(&default_itch_location).exists() {
default_itch_location = String::from("");
}
ui.enable_itch_checkbox.set_value(settings.itch.enabled);
ui.itch_locatoin_input.set_value(
&settings
.itch
.location
.clone()
.unwrap_or(default_itch_location),
);
ui.enable_origin_checkbox.set_value(settings.origin.enabled);
ui.enable_gog_checkbox.set_value(settings.gog.enabled);
let default_gog_location = gog::default_location();
let default_gog_location = if !default_gog_location.exists() {
String::from("")
} else {
default_gog_location.to_string_lossy().to_string()
};
ui.gog_folder_input.set_value(
&settings
.gog
.location
.clone()
.unwrap_or(default_gog_location),
);
#[cfg(target_family = "unix")]
{
ui.gog_winedrive_input
.set_value(&settings.gog.wine_c_drive.clone().unwrap_or("".to_string()));
}
#[cfg(not(target_family = "unix"))]
{
ui.gog_winedrive_input.hide();
ui.enable_heroic_checkbox.hide();
}
ui.enable_uplay_checkbox.set_value(settings.uplay.enabled);
ui.enable_lutris_checkbox.set_value(settings.lutris.enabled);
ui.lutris_executable_input.set_value(
settings
.lutris
.executable
.clone()
.unwrap_or(String::from("lutris"))
.as_str(),
);
ui.enable_heroic_checkbox.set_value(settings.heroic.enabled);
}
+337
View File
@@ -0,0 +1,337 @@
use eframe::{egui, epi::{self, IconData},};
use egui::{ScrollArea, TextureHandle, Stroke, Rounding, Image};
use futures::executor::block_on;
use steam_shortcuts_util::shortcut::ShortcutOwned;
use std::error::Error;
use tokio::runtime::Runtime;
use crate::{settings::Settings, sync::{download_images, self}, sync::run_sync};
use super::{ui_images::{get_import_image, get_logo, get_logo_icon}, ui_colors::{TEXT_COLOR, BACKGROUND_COLOR, BG_STROKE_COLOR, ORANGE, PURLPLE, LIGHT_ORANGE}};
#[derive(Default)]
struct UiImages{
import_button: Option<egui::TextureHandle>,
logo_32: Option<egui::TextureHandle>,
}
struct MyEguiApp {
selected_menu: Menues,
settings: Settings,
rt: Runtime,
ui_images: UiImages,
games_to_sync:Option<Vec<(String, Vec<ShortcutOwned>)>>
}
impl MyEguiApp {
pub fn new() -> Self {
let runtime = Runtime::new().unwrap();
Self {
selected_menu: Menues::Import,
settings: Settings::new().expect("We must be able to load our settings"),
rt: runtime,
games_to_sync:None,
ui_images: UiImages::default(),
}
}
pub fn run_sync(&self) {
let settings = self.settings.clone();
self.rt.spawn_blocking(move || {
MyEguiApp::save_settings_to_file(&settings);
//TODO get status back to ui
let usersinfo = run_sync(&settings).unwrap();
let task = download_images(&settings, &usersinfo);
block_on(task);
});
}
fn save_settings_to_file(settings: &Settings) {
let toml = toml::to_string(&settings).unwrap();
std::fs::write("config.toml", toml).unwrap();
}
}
#[derive(PartialEq)]
enum Menues {
Import,
Settings,
}
impl Default for Menues {
fn default() -> Menues {
Menues::Import
}
}
impl epi::App for MyEguiApp {
fn name(&self) -> &str {
"BoilR"
}
fn update(&mut self, ctx: &egui::Context, _frame: &epi::Frame) {
let mut style: egui::Style = (*ctx.style()).clone();
create_style(&mut style);
ctx.set_style(style);
egui::SidePanel::new(egui::panel::Side::Left, "Side Panel")
.default_width(40.0)
.show(ctx, |ui| {
let texture = self.get_logo_image(ui);
let size = texture.size_vec2();
ui.image(texture, size);
ui.separator();
ui.selectable_value(&mut self.selected_menu, Menues::Import, "Import Games");
// ui.selectable_value(&mut self.selected_menu, Menues::Art, "Art");
ui.selectable_value(&mut self.selected_menu, Menues::Settings, "Settings");
});
if self.games_to_sync.is_some(){
egui::TopBottomPanel::new(egui::panel::TopBottomSide::Bottom, "Bottom Panel")
.show(ctx,|ui|{
let texture = self.get_import_image(ui);
let size = texture.size_vec2();
let image_button = Image::new(texture, size);
if ui.add(image_button).on_hover_text("Import your games into steam").clicked() {
self.run_sync();
}
});
}
egui::CentralPanel::default()
.show(ctx, |ui| {
if let Menues::Import = self.selected_menu{
}else{
self.games_to_sync = None;
}
match self.selected_menu {
Menues::Import => {
self.render_import_games(ui);
},
Menues::Settings => {
self.render_settings(ui);
},
};
});
}
}
fn create_style(style: &mut egui::Style) {
style.spacing.item_spacing = egui::vec2(15.0, 15.0);
style.visuals.button_frame = false;
style.visuals.dark_mode = true;
style.visuals.override_text_color = Some(TEXT_COLOR);
style.visuals.widgets.noninteractive.rounding = Rounding{
ne:0.0,
nw:0.0,
se:0.0,
sw:0.0
};
style.visuals.faint_bg_color = PURLPLE;
style.visuals.extreme_bg_color = BACKGROUND_COLOR;
style.visuals.widgets.active.bg_fill = BACKGROUND_COLOR;
style.visuals.widgets.active.bg_stroke = Stroke::new(2.0,BG_STROKE_COLOR);
style.visuals.widgets.active.fg_stroke = Stroke::new(2.0,LIGHT_ORANGE);
style.visuals.widgets.open.bg_fill = BACKGROUND_COLOR;
style.visuals.widgets.open.bg_stroke = Stroke::new(2.0,BG_STROKE_COLOR);
style.visuals.widgets.open.fg_stroke = Stroke::new(2.0,LIGHT_ORANGE);
style.visuals.widgets.noninteractive.bg_fill = BACKGROUND_COLOR;
style.visuals.widgets.noninteractive.bg_stroke = Stroke::new(2.0,BG_STROKE_COLOR);
style.visuals.widgets.noninteractive.fg_stroke = Stroke::new(2.0,ORANGE);
style.visuals.widgets.inactive.bg_fill = BACKGROUND_COLOR;
style.visuals.widgets.inactive.bg_stroke = Stroke::new(2.0,BG_STROKE_COLOR);
style.visuals.widgets.inactive.fg_stroke = Stroke::new(2.0,ORANGE);
style.visuals.widgets.hovered.bg_fill = BACKGROUND_COLOR;
style.visuals.widgets.hovered.bg_stroke = Stroke::new(2.0,BG_STROKE_COLOR);
style.visuals.widgets.hovered.fg_stroke = Stroke::new(2.0,LIGHT_ORANGE);
}
impl MyEguiApp{
fn get_import_image(&mut self, ui:&mut egui::Ui) -> &mut TextureHandle {
self.ui_images.import_button.get_or_insert_with(|| {
// Load the texture only once.
ui.ctx().load_texture("import_image", get_import_image())
})
}
fn get_logo_image(&mut self, ui:&mut egui::Ui) -> &mut TextureHandle {
self.ui_images.logo_32.get_or_insert_with(|| {
// Load the texture only once.
ui.ctx().load_texture("logo32", get_logo())
})
}
fn render_settings(&mut self, ui: &mut egui::Ui){
ui.heading("Settings");
ui.label("Here you can change your settings");
ScrollArea::vertical().show(ui,|ui| {
ui.heading("SteamGridDB");
ui.checkbox(&mut self.settings.steamgrid_db.enabled, "Download images");
ui.horizontal(|ui| {
let mut empty_string ="".to_string();
let auth_key = self.settings.steamgrid_db.auth_key.as_mut().unwrap_or(&mut empty_string);
ui.label("Authentication key: ");
if ui.text_edit_singleline(auth_key).changed(){
self.settings.steamgrid_db.auth_key = Some(auth_key.to_string());
}
});
ui.horizontal(|ui| {
ui.label("To download images you need an API Key from SteamGridDB, you can find yours");
ui.hyperlink_to("here", "https://www.steamgriddb.com/profile/preferences/api")
});
ui.checkbox(&mut self.settings.steamgrid_db.prefer_animated, "Prefer animated images").on_hover_text("Prefer downloading animated images over static images (this can slow Steam down but looks neat)");
ui.separator();
ui.heading("Steam");
ui.horizontal(|ui| {
let mut empty_string ="".to_string();
let steam_location = self.settings.steam.location.as_mut().unwrap_or(&mut empty_string);
ui.label("Steam Location: ");
if ui.text_edit_singleline(steam_location).changed(){
self.settings.steam.location = Some(steam_location.to_string());
}
});
ui.checkbox(&mut self.settings.steam.create_collections, "Create collections").on_hover_text("Tries to create a games collection for each platform");
ui.checkbox(&mut self.settings.steam.optimize_for_big_picture, "Optimize for big picture").on_hover_text("Set icons to be larger horizontal images, this looks nice in steam big picture mode, but a bit off in desktop mode");
ui.separator();
ui.heading("Epic Games");
ui.checkbox(&mut self.settings.epic_games.enabled, "Import form Epic Games");
ui.horizontal(|ui| {
let mut empty_string ="".to_string();
let epic_location = self.settings.epic_games.location.as_mut().unwrap_or(&mut empty_string);
ui.label("Epic Manifests Location: ").on_hover_text("The location where Epic stores its manifest files that BoilR needs to read");
if ui.text_edit_singleline(epic_location).changed(){
self.settings.epic_games.location = Some(epic_location.to_string());
}
});
ui.separator();
#[cfg(target_family = "unix")]
{
ui.heading("Heroic");
ui.checkbox(&mut self.settings.heroic.enabled, "Import form Heroic");
ui.separator();
}
ui.heading("Legendary & Rare");
ui.checkbox(&mut self.settings.legendary.enabled, "Import form Legendary & Rare");
ui.horizontal(|ui| {
let mut empty_string ="".to_string();
let legendary_location = self.settings.legendary.executable.as_mut().unwrap_or(&mut empty_string);
ui.label("Legendary Executable: ").on_hover_text("The location of the legendary executable to use");
if ui.text_edit_singleline(legendary_location).changed(){
self.settings.legendary.executable = Some(legendary_location.to_string());
}
});
ui.separator();
ui.heading("Itch.io");
ui.checkbox(&mut self.settings.itch.enabled, "Import form Itch.io");
ui.horizontal(|ui| {
let mut empty_string ="".to_string();
let itch_location = self.settings.itch.location.as_mut().unwrap_or(&mut empty_string);
ui.label("Itch.io Folder: ");
if ui.text_edit_singleline(itch_location).changed(){
self.settings.itch.location = Some(itch_location.to_string());
}
});
ui.separator();
ui.heading("Origin");
ui.checkbox(&mut self.settings.origin.enabled, "Import from Origin");
ui.separator();
ui.heading("GoG Galaxy");
ui.checkbox(&mut self.settings.gog.enabled, "Import form GoG Galaxy");
ui.horizontal(|ui| {
let mut empty_string ="".to_string();
let itch_location = self.settings.gog.location.as_mut().unwrap_or(&mut empty_string);
ui.label("GoG Galaxy Folder: ");
if ui.text_edit_singleline(itch_location).changed(){
self.settings.gog.location = Some(itch_location.to_string());
}
});
ui.separator();
ui.heading("Uplay");
ui.checkbox(&mut self.settings.uplay.enabled, "Import form Uplay");
ui.separator();
ui.heading("Lutris");
ui.checkbox(&mut self.settings.lutris.enabled, "Import form Uplay");
ui.horizontal(|ui| {
let mut empty_string ="".to_string();
let lutris_location = self.settings.lutris.executable.as_mut().unwrap_or(&mut empty_string);
ui.label("Lutris Location: ");
if ui.text_edit_singleline(lutris_location).changed(){
self.settings.lutris.executable = Some(lutris_location.to_string());
}
});
});
}
fn render_import_games(&mut self, ui: &mut egui::Ui){
ui.heading("Import Games");
ui.label("Select the games you want to import into steam");
ScrollArea::vertical().show(ui,|ui| {
match &self.games_to_sync{
Some(games_to_sync) => {
for (platform_name, shortcuts) in games_to_sync{
ui.heading(platform_name);
for shortcut in shortcuts {
let mut import_game = !self.settings.blacklisted_games.contains(&shortcut.app_id);
let checkbox = egui::Checkbox::new(&mut import_game,&shortcut.app_name);
let response = ui.add(checkbox);
if response.clicked(){
if !self.settings.blacklisted_games.contains(&shortcut.app_id){
self.settings.blacklisted_games.push(shortcut.app_id);
}else{
self.settings.blacklisted_games.retain(|id| *id != shortcut.app_id);
}
}
}
}
},
None => {
self.games_to_sync = Some(sync::get_platform_shortcuts(&self.settings));
},
};
ui.label("Check the settings if BoilR didn't find the game you where looking for");
});
}
}
pub fn run_ui() -> Result<(), Box<dyn Error>> {
let app = MyEguiApp::new();
let mut native_options = eframe::NativeOptions::default();
native_options.initial_window_size = Some(egui::Vec2{
x:500.,
y:500.
});
native_options.icon_data = Some(get_logo_icon());
eframe::run_native(Box::new(app), native_options);
}