Multiple improvements for steamdeck (#99)

Paste the SteamgridDB API key in from clipboard automatically if it looks like a API key. This is nice for steam deck so you don't have to do a right-click paste or worse manually write in the key.
BoilR guesses what game it should find images for based on the game name, but it might be wrong, it is now possible to correct this
No longer show a back button on the user select screen (it did nothing)
Now you can change the images for ALL your shortcuts, not just the BoilR ones.
This commit is contained in:
Philip Kristoffersen
2022-04-26 20:15:25 +02:00
committed by GitHub
parent d2ccef8b9b
commit 365757c5b5
4 changed files with 155 additions and 61 deletions
Generated
+1
View File
@@ -232,6 +232,7 @@ version = "1.2.0"
dependencies = [ dependencies = [
"base64", "base64",
"config", "config",
"copypasta",
"dashmap", "dashmap",
"eframe", "eframe",
"egui", "egui",
+1
View File
@@ -26,6 +26,7 @@ image = { version = "0.24.1", features = ["png"] }
toml = { version = "^0.5.8" } toml = { version = "^0.5.8" }
sysinfo = "0.23.10" sysinfo = "0.23.10"
sqlite = "0.26.0" sqlite = "0.26.0"
copypasta = "0.7.1"
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
winreg = "0.10.1" winreg = "0.10.1"
+105 -32
View File
@@ -27,11 +27,14 @@ pub struct ImageSelectState {
pub steam_user: Option<SteamUsersInfo>, pub steam_user: Option<SteamUsersInfo>,
pub steam_users: Option<Vec<SteamUsersInfo>>, pub steam_users: Option<Vec<SteamUsersInfo>>,
pub user_shortcuts: Option<Vec<ShortcutOwned>>,
pub image_type_selected: Option<ImageType>, pub image_type_selected: Option<ImageType>,
pub image_options: Receiver<FetcStatus<Vec<PossibleImage>>>, pub image_options: Receiver<FetcStatus<Vec<PossibleImage>>>,
pub image_handles: std::sync::Arc<DashMap<String, TextureState>>, pub image_handles: std::sync::Arc<DashMap<String, TextureState>>,
pub possible_names: Option<Vec<steamgriddb_api::search::SearchResult>>,
} }
pub enum TextureState { pub enum TextureState {
@@ -69,7 +72,9 @@ impl Default for ImageSelectState {
wide_image: Default::default(), wide_image: Default::default(),
steam_user: Default::default(), steam_user: Default::default(),
steam_users: Default::default(), steam_users: Default::default(),
user_shortcuts: Default::default(),
image_type_selected: Default::default(), image_type_selected: Default::default(),
possible_names: None,
image_options: watch::channel(FetcStatus::NeedsFetched).1, image_options: watch::channel(FetcStatus::NeedsFetched).1,
image_handles: Arc::new(DashMap::new()), image_handles: Arc::new(DashMap::new()),
} }
@@ -78,6 +83,7 @@ impl Default for ImageSelectState {
#[derive(Debug)] #[derive(Debug)]
enum UserAction { enum UserAction {
CorrectGridId,
UserSelected(SteamUsersInfo), UserSelected(SteamUsersInfo),
ShortcutSelected(ShortcutOwned), ShortcutSelected(ShortcutOwned),
ImageTypeSelected(ImageType), ImageTypeSelected(ImageType),
@@ -91,7 +97,7 @@ impl MyEguiApp {
fn render_ui_image_action(&self, ui: &mut egui::Ui) -> UserAction { fn render_ui_image_action(&self, ui: &mut egui::Ui) -> UserAction {
let state = &self.image_selected_state; let state = &self.image_selected_state;
ui.heading("Images"); ui.heading("Images");
if (state.selected_shortcut.is_some() || state.has_multiple_users()) if (state.selected_shortcut.is_some() || (state.has_multiple_users() && state.steam_user.is_some()))
&& ui.button("Back").clicked() && ui.button("Back").clicked()
{ {
return UserAction::BackButton; return UserAction::BackButton;
@@ -101,6 +107,12 @@ impl MyEguiApp {
} }
if let Some(shortcut) = state.selected_shortcut.as_ref() { if let Some(shortcut) = state.selected_shortcut.as_ref() {
ui.heading(&shortcut.app_name); ui.heading(&shortcut.app_name);
if let Some(possible_names) = state.possible_names.as_ref() {
if let Some(value) = render_possible_names(possible_names, ui) {
return value;
}
} else {
if let Some(image_type) = state.image_type_selected.as_ref() { if let Some(image_type) = state.image_type_selected.as_ref() {
if let Some(action) = self.render_possible_images(ui, image_type, state) { if let Some(action) = self.render_possible_images(ui, image_type, state) {
return action; return action;
@@ -108,6 +120,7 @@ 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 if let Some(action) = self.render_shortcut_select(ui) { } else if let Some(action) = self.render_shortcut_select(ui) {
return action; return action;
} }
@@ -115,20 +128,17 @@ impl MyEguiApp {
} }
fn render_shortcut_select(&self, ui: &mut egui::Ui) -> Option<UserAction> { fn render_shortcut_select(&self, ui: &mut egui::Ui) -> Option<UserAction> {
match &*self.games_to_sync.borrow() { let shortcuts = &self.image_selected_state.user_shortcuts;
FetcStatus::Fetched(shortcuts) => { match shortcuts {
for (platform_name, shortcuts) in shortcuts { Some(shortcuts) => {
ui.heading(platform_name);
for shortcut in shortcuts { for shortcut in shortcuts {
if ui.button(&shortcut.app_name).clicked() { if ui.button(&shortcut.app_name).clicked() {
return Some(UserAction::ShortcutSelected(shortcut.clone())); return Some(UserAction::ShortcutSelected(shortcut.clone()));
} }
} }
} }
} None => {
_ => { ui.label("Could not find any shortcuts");
ui.ctx().request_repaint();
ui.label("Finding installed games");
} }
} }
None None
@@ -245,19 +255,40 @@ impl MyEguiApp {
self.handle_image_selected(image); self.handle_image_selected(image);
} }
UserAction::BackButton => { UserAction::BackButton => {
let state = &mut self.image_selected_state; self.handle_back_button_action();
handle_back_button_action(state);
} }
UserAction::GridIdChanged(grid_id) => { UserAction::GridIdChanged(grid_id) => {
self.handle_grid_change(grid_id); self.handle_grid_change(grid_id);
} }
UserAction::NoAction => {} UserAction::NoAction => {}
UserAction::CorrectGridId => {
self.handle_correct_grid_request();
}
}; };
} }
fn handle_correct_grid_request(&mut self) {
let app_name = self
.image_selected_state
.selected_shortcut
.as_ref()
.map(|s| s.app_name.clone())
.unwrap_or_default();
let auth_key = self
.settings
.steamgrid_db
.auth_key
.clone()
.unwrap_or_default();
let client = steamgriddb_api::Client::new(&auth_key);
let search_results = self.rt.block_on(client.search(&app_name));
self.image_selected_state.possible_names = search_results.ok();
}
fn handle_grid_change(&mut self, grid_id: usize) { fn handle_grid_change(&mut self, grid_id: usize) {
self.image_selected_state.grid_id = Some(grid_id); self.image_selected_state.grid_id = Some(grid_id);
self.image_selected_state.possible_names = None;
if let Some(auth_key) = &self.settings.steamgrid_db.auth_key { if let Some(auth_key) = &self.settings.steamgrid_db.auth_key {
let client = steamgriddb_api::Client::new(auth_key); let client = steamgriddb_api::Client::new(auth_key);
let mut cache = CachedSearch::new(&client); let mut cache = CachedSearch::new(&client);
@@ -270,6 +301,7 @@ impl MyEguiApp {
fn handle_user_selected(&mut self, user: SteamUsersInfo) { fn handle_user_selected(&mut self, user: SteamUsersInfo) {
let state = &mut self.image_selected_state; let state = &mut self.image_selected_state;
state.user_shortcuts = Some(crate::steam::get_shortcuts_for_user(&user).shortcuts);
state.steam_user = Some(user); state.steam_user = Some(user);
} }
@@ -332,10 +364,11 @@ impl MyEguiApp {
app_name, app_name,
image_type: *selected_image_type, image_type: *selected_image_type,
}; };
//TODO make this actually parallel
self.rt.spawn_blocking(move || { self.rt.spawn_blocking(move || {
let _ = block_on(crate::steamgriddb::download_to_download(&to_download)); let _ = block_on(crate::steamgriddb::download_to_download(&to_download));
}); });
{
let image_ref = match selected_image_type { let image_ref = match selected_image_type {
ImageType::Hero => &mut self.image_selected_state.hero_image, ImageType::Hero => &mut self.image_selected_state.hero_image,
ImageType::Grid => &mut self.image_selected_state.grid_image, ImageType::Grid => &mut self.image_selected_state.grid_image,
@@ -356,6 +389,12 @@ impl MyEguiApp {
self.image_selected_state.image_type_selected = None; self.image_selected_state.image_type_selected = None;
self.image_selected_state.image_options = watch::channel(FetcStatus::NeedsFetched).1; self.image_selected_state.image_options = watch::channel(FetcStatus::NeedsFetched).1;
} }
self.clear_loaded_images();
}
fn clear_loaded_images(&mut self) {
self.image_selected_state.image_handles.clear();
}
fn handle_shortcut_selected(&mut self, shortcut: ShortcutOwned, ui: &mut egui::Ui) { fn handle_shortcut_selected(&mut self, shortcut: ShortcutOwned, ui: &mut egui::Ui) {
let state = &mut self.image_selected_state; let state = &mut self.image_selected_state;
@@ -364,7 +403,6 @@ impl MyEguiApp {
if let Some(auth_key) = &self.settings.steamgrid_db.auth_key { if let Some(auth_key) = &self.settings.steamgrid_db.auth_key {
let client = steamgriddb_api::Client::new(auth_key); let client = steamgriddb_api::Client::new(auth_key);
let search = CachedSearch::new(&client); let search = CachedSearch::new(&client);
//TODO make this multithreaded
state.grid_id = self state.grid_id = self
.rt .rt
.block_on(search.search(shortcut.app_id, &shortcut.app_name)) .block_on(search.search(shortcut.app_id, &shortcut.app_name))
@@ -375,7 +413,6 @@ impl MyEguiApp {
let folder = Path::new(&user.steam_user_data_folder) let folder = Path::new(&user.steam_user_data_folder)
.join("config") .join("config")
.join("grid"); .join("grid");
//TODO put this in seperate thread
state.hero_image = get_image(ui, &shortcut, &folder, &ImageType::Hero); state.hero_image = get_image(ui, &shortcut, &folder, &ImageType::Hero);
state.grid_image = get_image(ui, &shortcut, &folder, &ImageType::Grid); state.grid_image = get_image(ui, &shortcut, &folder, &ImageType::Grid);
state.icon_image = get_image(ui, &shortcut, &folder, &ImageType::Icon); state.icon_image = get_image(ui, &shortcut, &folder, &ImageType::Icon);
@@ -383,6 +420,38 @@ impl MyEguiApp {
state.wide_image = get_image(ui, &shortcut, &folder, &ImageType::WideGrid); state.wide_image = get_image(ui, &shortcut, &folder, &ImageType::WideGrid);
state.selected_shortcut = Some(shortcut); state.selected_shortcut = Some(shortcut);
} }
fn handle_back_button_action(&mut self) {
let state = &mut self.image_selected_state;
if state.possible_names.is_some() {
state.possible_names = None;
} else if state.image_type_selected.is_some() {
state.image_type_selected = None;
state.image_handles.clear();
} else if state.selected_shortcut.is_some() {
state.selected_shortcut = None;
state.hero_image = None;
state.grid_image = None;
state.icon_image = None;
state.logo_image = None;
state.wide_image = None;
} else {
state.user_shortcuts = None;
state.steam_user = None;
}
}
}
fn render_possible_names(
possible_names: &Vec<steamgriddb_api::search::SearchResult>,
ui: &mut egui::Ui,
) -> Option<UserAction> {
for possible in possible_names {
if ui.button(&possible.name).clicked() {
return Some(UserAction::GridIdChanged(possible.id));
}
}
None
} }
fn render_shortcut_images(ui: &mut egui::Ui, state: &ImageSelectState) -> Option<UserAction> { fn render_shortcut_images(ui: &mut egui::Ui, state: &ImageSelectState) -> Option<UserAction> {
@@ -392,10 +461,16 @@ fn render_shortcut_images(ui: &mut egui::Ui, state: &ImageSelectState) -> Option
return Some(UserAction::GridIdChanged(grid_id)); return Some(UserAction::GridIdChanged(grid_id));
} }
}; };
if ui
.button("Click here if the images are for a wrong game")
.clicked()
{
return Some(UserAction::CorrectGridId);
}
for image_type in ImageType::all() { for image_type in ImageType::all() {
ui.label(image_type.name()); ui.label(image_type.name());
let image_ref = get_image_ref(image_type, state); let image_ref = get_image_ref(image_type, state);
if render_thumbnail(ui, image_ref) { if render_thumbnail(ui, image_ref, &image_type) {
return Some(UserAction::ImageTypeSelected(*image_type)); return Some(UserAction::ImageTypeSelected(*image_type));
} }
} }
@@ -415,29 +490,27 @@ fn render_user_select(state: &ImageSelectState, ui: &mut egui::Ui) -> UserAction
UserAction::NoAction UserAction::NoAction
} }
fn handle_back_button_action(state: &mut ImageSelectState) {
if state.image_type_selected.is_some() {
state.image_type_selected = None;
} else if state.selected_shortcut.is_some() {
state.selected_shortcut = None;
} else {
state.steam_user = None;
}
}
const MAX_WIDTH: f32 = 300.; const MAX_WIDTH: f32 = 300.;
fn render_thumbnail(ui: &mut egui::Ui, image: &Option<egui::TextureHandle>) -> bool { fn render_thumbnail(ui: &mut egui::Ui, image: &Option<egui::TextureHandle>, image_type:&ImageType) -> bool {
match image { if let Some(texture) = image {
Some(texture) => {
let mut size = texture.size_vec2(); let mut size = texture.size_vec2();
clamp_to_width(&mut size, MAX_WIDTH); clamp_to_width(&mut size, MAX_WIDTH);
let image_button = ImageButton::new(texture, size); let image_button = ImageButton::new(texture, size);
ui.add(image_button) let added =ui.add(image_button);
.on_hover_text("Click to change image") match image_type{
.clicked() ImageType::Icon => false,
_=> added.on_hover_text("Click to change image").clicked()
}
} else {
match image_type{
ImageType::Icon => {
ui.label("No icon found");
false
},
_=>
ui.button("Pick an image").clicked(),
} }
None => ui.button("Pick an image").clicked(),
} }
} }
+19
View File
@@ -1,3 +1,4 @@
use copypasta::ClipboardProvider;
use eframe::egui; use eframe::egui;
use egui::ScrollArea; use egui::ScrollArea;
@@ -181,10 +182,24 @@ impl MyEguiApp {
.auth_key .auth_key
.as_mut() .as_mut()
.unwrap_or(&mut empty_string); .unwrap_or(&mut empty_string);
let mut auth_key_clipboard = false;
ui.label("Authentication key: "); ui.label("Authentication key: ");
if let Ok(mut clipboard_ctx) = copypasta::ClipboardContext::new(){
if let Ok(content) = clipboard_ctx.get_contents(){
if auth_key.is_empty() && looks_like_auth_key(&content) {
*auth_key = content.clone();
auth_key_clipboard = true;
}else if content.eq(auth_key){
auth_key_clipboard = true;
}
}
}
if ui.text_edit_singleline(auth_key).changed() { if ui.text_edit_singleline(auth_key).changed() {
self.settings.steamgrid_db.auth_key = Some(auth_key.to_string()); self.settings.steamgrid_db.auth_key = Some(auth_key.to_string());
} }
if auth_key_clipboard{
ui.label("Found API Key in clipboard");
}
}); });
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.label( ui.label(
@@ -275,3 +290,7 @@ impl MyEguiApp {
} }
} }
} }
fn looks_like_auth_key(content: &String) -> bool {
content.len() == 32 && content.is_ascii() && content.chars().all(char::is_alphanumeric) && content.chars().all(char::is_lowercase)
}