Add feature to disconnect a shortcut (#160)

This commit is contained in:
Philip Kristoffersen
2022-05-27 20:58:22 +02:00
committed by GitHub
parent 835c7ead8d
commit f16646f540
19 changed files with 271 additions and 122 deletions
+18 -4
View File
@@ -1,4 +1,4 @@
use std::path::{PathBuf, Path}; use std::path::{Path, PathBuf};
use steam_shortcuts_util::{shortcut::ShortcutOwned, Shortcut}; use steam_shortcuts_util::{shortcut::ShortcutOwned, Shortcut};
@@ -6,14 +6,28 @@ use steam_shortcuts_util::{shortcut::ShortcutOwned, Shortcut};
pub struct AmazonGame { pub struct AmazonGame {
pub title: String, pub title: String,
pub id: String, pub id: String,
pub launcher_path:PathBuf, pub launcher_path: PathBuf,
} }
impl From<AmazonGame> for ShortcutOwned { impl From<AmazonGame> for ShortcutOwned {
fn from(game: AmazonGame) -> Self { fn from(game: AmazonGame) -> Self {
let launch = format!("amazon-games://play/{}", game.id); let launch = format!("amazon-games://play/{}", game.id);
let exe = game.launcher_path.to_string_lossy().to_string(); let exe = game.launcher_path.to_string_lossy().to_string();
let start_dir= game.launcher_path.parent().unwrap_or_else(||Path::new("")).to_string_lossy().to_string(); let start_dir = game
Shortcut::new("0", game.title.as_str(), exe.as_str(), start_dir.as_str(), "", "", launch.as_str()).to_owned() .launcher_path
.parent()
.unwrap_or_else(|| Path::new(""))
.to_string_lossy()
.to_string();
Shortcut::new(
"0",
game.title.as_str(),
exe.as_str(),
start_dir.as_str(),
"",
"",
launch.as_str(),
)
.to_owned()
} }
} }
+6 -2
View File
@@ -41,7 +41,11 @@ impl Platform<AmazonGame, Box<dyn Error>> for AmazonPlatform {
let id = statement.read::<String>(0); let id = statement.read::<String>(0);
let title = statement.read::<String>(1); let title = statement.read::<String>(1);
if let (Ok(id), Ok(title)) = (id, title) { if let (Ok(id), Ok(title)) = (id, title) {
result.push(AmazonGame { title, id , launcher_path:launcher_path.clone()}); result.push(AmazonGame {
title,
id,
launcher_path: launcher_path.clone(),
});
} }
} }
Ok(result) Ok(result)
@@ -50,7 +54,7 @@ impl Platform<AmazonGame, Box<dyn Error>> for AmazonPlatform {
fn settings_valid(&self) -> crate::platform::SettingsValidity { fn settings_valid(&self) -> crate::platform::SettingsValidity {
let path = get_sqlite_path(); let path = get_sqlite_path();
let launcher = get_launcher_path(); let launcher = get_launcher_path();
if path.is_some() && launcher.is_some(){ if path.is_some() && launcher.is_some() {
crate::platform::SettingsValidity::Valid crate::platform::SettingsValidity::Valid
} else { } else {
crate::platform::SettingsValidity::Invalid { crate::platform::SettingsValidity::Invalid {
+1 -1
View File
@@ -3,5 +3,5 @@ use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize, Clone)] #[derive(Debug, Deserialize, Serialize, Clone)]
pub struct AmazonSettings { pub struct AmazonSettings {
pub enabled: bool, pub enabled: bool,
pub launcher_location: Option<String> pub launcher_location: Option<String>,
} }
+1 -1
View File
@@ -94,7 +94,7 @@ fn launcher_shortcut(manifest: ManifestItem) -> ShortcutOwned {
.as_ref() .as_ref()
.map(|p| { .map(|p| {
p.parent() p.parent()
.unwrap_or_else(||Path::new("")) .unwrap_or_else(|| Path::new(""))
.to_string_lossy() .to_string_lossy()
.to_string() .to_string()
}) })
+11 -8
View File
@@ -81,10 +81,13 @@ mod unix {
} }
} }
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
mod windows { mod windows {
use super::EpicPaths; use super::EpicPaths;
use std::{path::{Path, PathBuf}, env}; use std::{
env,
path::{Path, PathBuf},
};
fn manifest_location_from_registry() -> Option<PathBuf> { fn manifest_location_from_registry() -> Option<PathBuf> {
use winreg::enums::*; use winreg::enums::*;
@@ -157,10 +160,10 @@ mod unix {
pub fn get_locations() -> Option<EpicPaths> { pub fn get_locations() -> Option<EpicPaths> {
{ {
let manifest_folder_path = manifest_location_from_registry() let manifest_folder_path =
.unwrap_or_else(guess_default_manifest_location); manifest_location_from_registry().unwrap_or_else(guess_default_manifest_location);
let launcer_path = launcher_location_from_registry() let launcer_path =
.unwrap_or_else(guess_default_launcher_location); launcher_location_from_registry().unwrap_or_else(guess_default_launcher_location);
if launcer_path.exists() && manifest_folder_path.exists() { if launcer_path.exists() && manifest_folder_path.exists() {
Some(EpicPaths { Some(EpicPaths {
compat_folder_path: None, compat_folder_path: None,
@@ -172,4 +175,4 @@ mod unix {
} }
} }
} }
} }
+2 -4
View File
@@ -57,10 +57,8 @@ fn get_shortcuts_from_games(games: Vec<(GogGame, PathBuf)>) -> Vec<GogShortcut>
if let Some(primary_task) = tasks.iter().find(|t| { if let Some(primary_task) = tasks.iter().find(|t| {
t.is_primary.unwrap_or_default() t.is_primary.unwrap_or_default()
&& t.task_type == "FileTask" && t.task_type == "FileTask"
&& ( && (t.category.as_ref().unwrap_or(&String::from("")) == "launcher"
t.category.as_ref().unwrap_or(&String::from("")) == "launcher" || || t.category.as_ref().unwrap_or(&String::from("")) == "game")
t.category.as_ref().unwrap_or(&String::from("")) == "game"
)
}) { }) {
if let Some(task_path) = &primary_task.path { if let Some(task_path) = &primary_task.path {
let full_path = game_folder.join(&task_path); let full_path = game_folder.join(&task_path);
+4 -1
View File
@@ -33,7 +33,10 @@ impl From<HeroicGame> for ShortcutOwned {
let (exe, parameter) = match game.install_mode.unwrap() { let (exe, parameter) = match game.install_mode.unwrap() {
InstallationMode::FlatPak => ( InstallationMode::FlatPak => (
"flatpak", "flatpak",
format!("run com.heroicgameslauncher.hgl {} --no-gui", launch_parameter), format!(
"run com.heroicgameslauncher.hgl {} --no-gui",
launch_parameter
),
), ),
InstallationMode::UserBin => ("heroic", launch_parameter), InstallationMode::UserBin => ("heroic", launch_parameter),
}; };
+1 -1
View File
@@ -20,7 +20,7 @@ impl From<OriginGame> for ShortcutOwned {
"\"origin2://game/launch?offerIds={}&autoDownload=1&authCode=&cmdParams=\"", "\"origin2://game/launch?offerIds={}&autoDownload=1&authCode=&cmdParams=\"",
game.id) game.id)
}; };
let origin_location = format!("\"{}\"",game.origin_location.to_string_lossy()); let origin_location = format!("\"{}\"", game.origin_location.to_string_lossy());
let mut owned_shortcut = Shortcut::new( let mut owned_shortcut = Shortcut::new(
"0", "0",
game.title.as_str(), game.title.as_str(),
+11 -6
View File
@@ -128,13 +128,18 @@ pub fn get_default_location() -> Result<String, Box<dyn Error + Sync + Send>> {
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
let path_string = { let path_string = {
let home = std::env::var("HOME")?; let home = std::env::var("HOME")?;
let default_path = Path::new(&home) let default_path = Path::new(&home).join(".steam").join("steam");
.join(".steam") if default_path.exists() {
.join("steam");
if default_path.exists(){
default_path.to_string_lossy().to_string() default_path.to_string_lossy().to_string()
}else{ } else {
Path::new(&home).join(".var").join("app").join("com.valvesoftware.Steam").join(".steam").join("steam").to_string_lossy().to_string() Path::new(&home)
.join(".var")
.join("app")
.join("com.valvesoftware.Steam")
.join(".steam")
.join("steam")
.to_string_lossy()
.to_string()
} }
}; };
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
+1
View File
@@ -8,3 +8,4 @@ pub use synchronization::run_sync;
pub use synchronization::IsBoilRShortcut; pub use synchronization::IsBoilRShortcut;
pub use synchronization::SyncProgress; pub use synchronization::SyncProgress;
pub use synchronization::*;
+20 -1
View File
@@ -23,7 +23,7 @@ use std::error::Error;
use crate::{gog::GogPlatform, itch::ItchPlatform, origin::OriginPlatform}; use crate::{gog::GogPlatform, itch::ItchPlatform, origin::OriginPlatform};
use std::{fs::File, io::Write, path::Path}; use std::{fs::File, io::Write, path::Path};
const BOILR_TAG: &str = "boilr"; pub const BOILR_TAG: &str = "boilr";
pub enum SyncProgress { pub enum SyncProgress {
NotStarted, NotStarted,
@@ -34,6 +34,25 @@ pub enum SyncProgress {
Done, Done,
} }
pub fn disconnect_shortcut(settings: &Settings, app_id: u32) -> Result<(), String> {
let mut userinfo_shortcuts = get_shortcuts_paths(&settings.steam)
.map_err(|e| format!("Getting shortcut paths failed: {e}"))?;
for user in userinfo_shortcuts.iter_mut() {
let mut shortcut_info = get_shortcuts_for_user(user);
for shortcut in shortcut_info.shortcuts.iter_mut() {
if shortcut.app_id == app_id {
shortcut.dev_kit_game_id = "".to_string();
shortcut.tags.retain(|s| s != BOILR_TAG);
}
}
save_shortcuts(&shortcut_info.shortcuts, Path::new(&shortcut_info.path));
}
Ok(())
}
pub fn run_sync( pub fn run_sync(
settings: &Settings, settings: &Settings,
sender: &mut Option<Sender<SyncProgress>>, sender: &mut Option<Sender<SyncProgress>>,
+1
View File
@@ -0,0 +1 @@
+2
View File
@@ -1,5 +1,6 @@
mod defines; mod defines;
mod ui_backup; mod ui_backup;
mod ui_disconnect;
mod ui_image_download; mod ui_image_download;
mod ui_import_games; mod ui_import_games;
mod ui_settings; mod ui_settings;
@@ -7,6 +8,7 @@ mod uiapp;
pub use defines::*; pub use defines::*;
pub use ui_backup::*; pub use ui_backup::*;
pub use ui_disconnect::*;
pub use ui_image_download::*; pub use ui_image_download::*;
pub use ui_import_games::*; pub use ui_import_games::*;
pub use ui_settings::*; pub use ui_settings::*;
+89
View File
@@ -0,0 +1,89 @@
use egui::ScrollArea;
use super::ui_colors::*;
use super::MyEguiApp;
use crate::steam::get_shortcuts_for_user;
use crate::steam::get_shortcuts_paths;
use crate::steam::ShortcutInfo;
use crate::sync::disconnect_shortcut;
use crate::sync::IsBoilRShortcut;
#[derive(Default)]
pub struct DiconnectState {
pub connected_shortcuts: Option<Result<Vec<ShortcutInfo>, String>>,
}
impl MyEguiApp {
pub fn render_disconnect(&mut self, ui: &mut egui::Ui) {
let steam_settings = self.settings.steam.clone();
let users_info = self
.disconect_state
.connected_shortcuts
.get_or_insert_with(|| {
let users = get_shortcuts_paths(&steam_settings)
.map_err(|e| format!("Getting shortcut paths failed: {e}"));
users.map(|users| {
let mut user_info = vec![];
for user in users {
let shortcut_info = get_shortcuts_for_user(&user);
user_info.push(shortcut_info);
}
user_info
})
});
ui.heading("Add a disconnected Shortcuts");
ui.label("In this section you can add a shortcut that BoilR is not in control of.");
ui.label("This prevents BoilR from deleting or updating a shortcut it orignally added.");
ui.label(
"This is useful if you want to manully edit a shortcut after BoilR has imported it.",
);
ui.add_space(super::SECTION_SPACING);
match users_info.as_mut() {
Ok(users) => {
let has_multiple_users = users.len() > 1;
let mut redraw = 0;
set_scroll_style(ui);
ScrollArea::vertical()
.stick_to_right()
.auto_shrink([false, true])
.show(ui, |ui| {
ui.reset_style();
for user in users.iter_mut() {
if has_multiple_users {
ui.heading(&user.path.to_string_lossy().to_string());
}
for shortcut in user.shortcuts.iter() {
if shortcut.is_boilr_shortcut()
&& ui.button(&shortcut.app_name).clicked()
{
if disconnect_shortcut(&self.settings, shortcut.app_id).is_ok()
{
redraw = shortcut.app_id;
}
}
}
}
});
if redraw != 0 {
self.disconect_state.connected_shortcuts = None;
self.settings.blacklisted_games.push(redraw);
}
}
Err(msg) => {
ui.label(&*msg);
}
}
}
}
fn set_scroll_style(ui: &mut egui::Ui) {
let mut scroll_style = ui.style_mut();
scroll_style.visuals.extreme_bg_color = BACKGROUND_COLOR;
scroll_style.visuals.widgets.inactive.bg_fill = EXTRA_BACKGROUND_COLOR;
scroll_style.visuals.widgets.active.bg_fill = EXTRA_BACKGROUND_COLOR;
scroll_style.visuals.widgets.hovered.bg_fill = EXTRA_BACKGROUND_COLOR;
}
+1 -6
View File
@@ -155,7 +155,6 @@ impl MyEguiApp {
} else if let Some(action) = render_shortcut_images(ui, state) { } else if let Some(action) = render_shortcut_images(ui, state) {
return action; return action;
} }
} else { } else {
let is_shortcut = state.game_mode.is_shortcuts(); let is_shortcut = state.game_mode.is_shortcuts();
if ui if ui
@@ -178,7 +177,6 @@ impl MyEguiApp {
} else if let Some(action) = render_steam_game_select(ui, state) { } else if let Some(action) = render_steam_game_select(ui, state) {
return action; return action;
} }
} }
UserAction::NoAction UserAction::NoAction
} }
@@ -660,10 +658,7 @@ fn render_shortcut_images(ui: &mut egui::Ui, state: &ImageSelectState) -> Option
for image_type in ImageType::all() { for image_type in ImageType::all() {
ui.label(image_type.name()); ui.label(image_type.name());
let (_path, key) = shortcut.key(image_type, Path::new(&user_path)); let (_path, key) = shortcut.key(image_type, Path::new(&user_path));
let texture = state let texture = state.image_handles.get(&key).and_then(|k| match k.value() {
.image_handles
.get(&key)
.and_then(|k| match k.value() {
TextureState::Loaded(texture) => Some(texture.clone()), TextureState::Loaded(texture) => Some(texture.clone()),
_ => None, _ => None,
}); });
+1 -4
View File
@@ -9,7 +9,7 @@ use crate::sync;
use crate::sync::{download_images, SyncProgress}; use crate::sync::{download_images, SyncProgress};
use super::{ImageSelectState, backup_shortcuts}; use super::{backup_shortcuts, ImageSelectState};
use super::{ use super::{
ui_colors::{BACKGROUND_COLOR, EXTRA_BACKGROUND_COLOR}, ui_colors::{BACKGROUND_COLOR, EXTRA_BACKGROUND_COLOR},
MyEguiApp, MyEguiApp,
@@ -42,9 +42,6 @@ impl<T> FetcStatus<T> {
} }
impl MyEguiApp { impl MyEguiApp {
pub(crate) fn render_import_games(&mut self, ui: &mut egui::Ui) { pub(crate) fn render_import_games(&mut self, ui: &mut egui::Ui) {
ui.heading("Import Games"); ui.heading("Import Games");
+4 -1
View File
@@ -8,7 +8,7 @@ use super::{
ui_colors::{BACKGROUND_COLOR, EXTRA_BACKGROUND_COLOR}, ui_colors::{BACKGROUND_COLOR, EXTRA_BACKGROUND_COLOR},
MyEguiApp, MyEguiApp,
}; };
const SECTION_SPACING: f32 = 25.0; pub const SECTION_SPACING: f32 = 25.0;
const VERSION: &str = env!("CARGO_PKG_VERSION"); const VERSION: &str = env!("CARGO_PKG_VERSION");
impl MyEguiApp { impl MyEguiApp {
@@ -49,6 +49,9 @@ impl MyEguiApp {
{ {
self.render_amazon_settings(ui); self.render_amazon_settings(ui);
} }
self.render_disconnect(ui);
ui.add_space(SECTION_SPACING); ui.add_space(SECTION_SPACING);
ui.label(format!("Version: {}", VERSION)); ui.label(format!("Version: {}", VERSION));
}); });
+12 -1
View File
@@ -17,7 +17,7 @@ use super::{
}, },
ui_images::{get_import_image, get_logo, get_logo_icon}, ui_images::{get_import_image, get_logo, get_logo_icon},
ui_import_games::FetcStatus, ui_import_games::FetcStatus,
BackupState, ImageSelectState, BackupState, DiconnectState, ImageSelectState,
}; };
const SECTION_SPACING: f32 = 25.0; const SECTION_SPACING: f32 = 25.0;
@@ -39,6 +39,7 @@ pub struct MyEguiApp {
pub(crate) heroic_games: Option<Vec<HeroicGame>>, pub(crate) heroic_games: Option<Vec<HeroicGame>>,
pub(crate) image_selected_state: ImageSelectState, pub(crate) image_selected_state: ImageSelectState,
pub(crate) backup_state: BackupState, pub(crate) backup_state: BackupState,
pub(crate) disconect_state: DiconnectState,
} }
impl MyEguiApp { impl MyEguiApp {
@@ -55,6 +56,7 @@ impl MyEguiApp {
heroic_games: None, heroic_games: None,
image_selected_state: ImageSelectState::default(), image_selected_state: ImageSelectState::default(),
backup_state: BackupState::default(), backup_state: BackupState::default(),
disconect_state: DiconnectState::default(),
} }
} }
} }
@@ -65,6 +67,7 @@ enum Menues {
Settings, Settings,
Images, Images,
Backup, Backup,
Disconnect,
} }
impl Default for Menues { impl Default for Menues {
@@ -106,6 +109,11 @@ impl App for MyEguiApp {
.selectable_value(&mut self.selected_menu, Menues::Backup, "Backup") .selectable_value(&mut self.selected_menu, Menues::Backup, "Backup")
.changed(); .changed();
changed = changed
|| ui
.selectable_value(&mut self.selected_menu, Menues::Disconnect, "Disconnect")
.changed();
if changed { if changed {
self.backup_state.available_backups = None; self.backup_state.available_backups = None;
} }
@@ -172,6 +180,9 @@ impl App for MyEguiApp {
Menues::Backup => { Menues::Backup => {
self.render_backup(ui); self.render_backup(ui);
} }
Menues::Disconnect => {
self.render_disconnect(ui);
}
}; };
}); });
} }
+8 -4
View File
@@ -1,4 +1,4 @@
use std::path::{PathBuf, Path}; use std::path::{Path, PathBuf};
use steam_shortcuts_util::shortcut::{Shortcut, ShortcutOwned}; use steam_shortcuts_util::shortcut::{Shortcut, ShortcutOwned};
@@ -7,14 +7,18 @@ pub(crate) struct Game {
pub(crate) name: String, pub(crate) name: String,
pub(crate) icon: String, pub(crate) icon: String,
pub(crate) id: String, pub(crate) id: String,
pub(crate) launcher: PathBuf pub(crate) launcher: PathBuf,
} }
impl From<Game> for ShortcutOwned { impl From<Game> for ShortcutOwned {
fn from(game: Game) -> Self { fn from(game: Game) -> Self {
let launch = format!("\"uplay://launch/{}/0\"", game.id); let launch = format!("\"uplay://launch/{}/0\"", game.id);
let start_dir = game.launcher.parent().unwrap_or_else(|| {Path::new("")}).to_string_lossy(); let start_dir = game
let exe = format!("\"{}\"",game.launcher.to_string_lossy()); .launcher
.parent()
.unwrap_or_else(|| Path::new(""))
.to_string_lossy();
let exe = format!("\"{}\"", game.launcher.to_string_lossy());
Shortcut::new("0", &game.name, &exe, &start_dir, &game.icon, "", &launch).to_owned() Shortcut::new("0", &game.name, &exe, &start_dir, &game.icon, "", &launch).to_owned()
} }
} }