diff --git a/Cargo.lock b/Cargo.lock index 0a7a6ae..763d352 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -232,6 +232,7 @@ version = "1.2.0" dependencies = [ "base64", "config", + "copypasta", "dashmap", "eframe", "egui", diff --git a/Cargo.toml b/Cargo.toml index 39cc181..2949a5f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,9 +26,10 @@ image = { version = "0.24.1", features = ["png"] } toml = { version = "^0.5.8" } sysinfo = "0.23.10" sqlite = "0.26.0" +copypasta = "0.7.1" [target.'cfg(windows)'.dependencies] winreg = "0.10.1" [target.'cfg(windows)'.build-dependencies] -winres = "0.1" \ No newline at end of file +winres = "0.1" diff --git a/src/ui/ui_image_download.rs b/src/ui/ui_image_download.rs index b5a9b1d..a6635d5 100644 --- a/src/ui/ui_image_download.rs +++ b/src/ui/ui_image_download.rs @@ -27,11 +27,14 @@ pub struct ImageSelectState { pub steam_user: Option, pub steam_users: Option>, + pub user_shortcuts: Option>, pub image_type_selected: Option, pub image_options: Receiver>>, pub image_handles: std::sync::Arc>, + + pub possible_names: Option>, } pub enum TextureState { @@ -69,7 +72,9 @@ impl Default for ImageSelectState { wide_image: Default::default(), steam_user: Default::default(), steam_users: Default::default(), + user_shortcuts: Default::default(), image_type_selected: Default::default(), + possible_names: None, image_options: watch::channel(FetcStatus::NeedsFetched).1, image_handles: Arc::new(DashMap::new()), } @@ -78,6 +83,7 @@ impl Default for ImageSelectState { #[derive(Debug)] enum UserAction { + CorrectGridId, UserSelected(SteamUsersInfo), ShortcutSelected(ShortcutOwned), ImageTypeSelected(ImageType), @@ -91,7 +97,7 @@ impl MyEguiApp { fn render_ui_image_action(&self, ui: &mut egui::Ui) -> UserAction { let state = &self.image_selected_state; 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() { return UserAction::BackButton; @@ -101,12 +107,19 @@ impl MyEguiApp { } if let Some(shortcut) = state.selected_shortcut.as_ref() { ui.heading(&shortcut.app_name); - 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(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(action) = self.render_possible_images(ui, image_type, state) { + return action; + } + } else if let Some(action) = render_shortcut_images(ui, state) { return action; } - } else if let Some(action) = render_shortcut_images(ui, state) { - return action; } } else if let Some(action) = self.render_shortcut_select(ui) { return action; @@ -115,20 +128,17 @@ impl MyEguiApp { } fn render_shortcut_select(&self, ui: &mut egui::Ui) -> Option { - match &*self.games_to_sync.borrow() { - FetcStatus::Fetched(shortcuts) => { - for (platform_name, shortcuts) in shortcuts { - ui.heading(platform_name); - for shortcut in shortcuts { - if ui.button(&shortcut.app_name).clicked() { - return Some(UserAction::ShortcutSelected(shortcut.clone())); - } + let shortcuts = &self.image_selected_state.user_shortcuts; + match shortcuts { + Some(shortcuts) => { + for shortcut in shortcuts { + if ui.button(&shortcut.app_name).clicked() { + return Some(UserAction::ShortcutSelected(shortcut.clone())); } } } - _ => { - ui.ctx().request_repaint(); - ui.label("Finding installed games"); + None => { + ui.label("Could not find any shortcuts"); } } None @@ -245,19 +255,40 @@ impl MyEguiApp { self.handle_image_selected(image); } UserAction::BackButton => { - let state = &mut self.image_selected_state; - handle_back_button_action(state); + self.handle_back_button_action(); } UserAction::GridIdChanged(grid_id) => { self.handle_grid_change(grid_id); } 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) { 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 { let client = steamgriddb_api::Client::new(auth_key); let mut cache = CachedSearch::new(&client); @@ -270,6 +301,7 @@ impl MyEguiApp { fn handle_user_selected(&mut self, user: SteamUsersInfo) { let state = &mut self.image_selected_state; + state.user_shortcuts = Some(crate::steam::get_shortcuts_for_user(&user).shortcuts); state.steam_user = Some(user); } @@ -332,29 +364,36 @@ impl MyEguiApp { app_name, image_type: *selected_image_type, }; - //TODO make this actually parallel self.rt.spawn_blocking(move || { let _ = block_on(crate::steamgriddb::download_to_download(&to_download)); }); - let image_ref = match selected_image_type { - ImageType::Hero => &mut self.image_selected_state.hero_image, - ImageType::Grid => &mut self.image_selected_state.grid_image, - ImageType::WideGrid => &mut self.image_selected_state.wide_image, - ImageType::Logo => &mut self.image_selected_state.logo_image, - ImageType::BigPicture => &mut self.image_selected_state.wide_image, - ImageType::Icon => &mut self.image_selected_state.icon_image, - }; - let image_key = image.thumbnail_path.as_path().to_string_lossy().to_string(); - let texture_handle = self - .image_selected_state - .image_handles - .get(&image_key) - .unwrap(); - if let TextureState::Loaded(texture_handle) = texture_handle.value() { - *image_ref = Some(texture_handle.clone()); + + { + let image_ref = match selected_image_type { + ImageType::Hero => &mut self.image_selected_state.hero_image, + ImageType::Grid => &mut self.image_selected_state.grid_image, + ImageType::WideGrid => &mut self.image_selected_state.wide_image, + ImageType::Logo => &mut self.image_selected_state.logo_image, + ImageType::BigPicture => &mut self.image_selected_state.wide_image, + ImageType::Icon => &mut self.image_selected_state.icon_image, + }; + let image_key = image.thumbnail_path.as_path().to_string_lossy().to_string(); + let texture_handle = self + .image_selected_state + .image_handles + .get(&image_key) + .unwrap(); + if let TextureState::Loaded(texture_handle) = texture_handle.value() { + *image_ref = Some(texture_handle.clone()); + } + self.image_selected_state.image_type_selected = None; + self.image_selected_state.image_options = watch::channel(FetcStatus::NeedsFetched).1; } - self.image_selected_state.image_type_selected = None; - 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) { @@ -364,7 +403,6 @@ impl MyEguiApp { if let Some(auth_key) = &self.settings.steamgrid_db.auth_key { let client = steamgriddb_api::Client::new(auth_key); let search = CachedSearch::new(&client); - //TODO make this multithreaded state.grid_id = self .rt .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) .join("config") .join("grid"); - //TODO put this in seperate thread state.hero_image = get_image(ui, &shortcut, &folder, &ImageType::Hero); state.grid_image = get_image(ui, &shortcut, &folder, &ImageType::Grid); 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.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, + ui: &mut egui::Ui, +) -> Option { + 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 { @@ -392,10 +461,16 @@ fn render_shortcut_images(ui: &mut egui::Ui, state: &ImageSelectState) -> Option 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() { ui.label(image_type.name()); 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)); } } @@ -415,29 +490,27 @@ fn render_user_select(state: &ImageSelectState, ui: &mut egui::Ui) -> UserAction 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.; -fn render_thumbnail(ui: &mut egui::Ui, image: &Option) -> bool { - match image { - Some(texture) => { - let mut size = texture.size_vec2(); - clamp_to_width(&mut size, MAX_WIDTH); - let image_button = ImageButton::new(texture, size); - ui.add(image_button) - .on_hover_text("Click to change image") - .clicked() +fn render_thumbnail(ui: &mut egui::Ui, image: &Option, image_type:&ImageType) -> bool { + if let Some(texture) = image { + let mut size = texture.size_vec2(); + clamp_to_width(&mut size, MAX_WIDTH); + let image_button = ImageButton::new(texture, size); + let added =ui.add(image_button); + match image_type{ + 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(), } } diff --git a/src/ui/ui_settings.rs b/src/ui/ui_settings.rs index 6c43d5d..31063fb 100644 --- a/src/ui/ui_settings.rs +++ b/src/ui/ui_settings.rs @@ -1,3 +1,4 @@ +use copypasta::ClipboardProvider; use eframe::egui; use egui::ScrollArea; @@ -181,10 +182,24 @@ impl MyEguiApp { .auth_key .as_mut() .unwrap_or(&mut empty_string); + let mut auth_key_clipboard = false; 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() { 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.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) +}