Dropdown menues and image grids (#260)

* Add dropdowns to ui image select

* More "wrong game" text to buttom

* Show a grid when selecting downloaded games

* Update to version 1.7.4
This commit is contained in:
Philip Kristoffersen
2022-10-21 09:24:17 +02:00
committed by GitHub
parent 6286387d72
commit 98318e0288
4 changed files with 246 additions and 167 deletions
Generated
+1 -1
View File
@@ -192,7 +192,7 @@ dependencies = [
[[package]] [[package]]
name = "boilr" name = "boilr"
version = "1.7.3" version = "1.7.4"
dependencies = [ dependencies = [
"base64", "base64",
"chrono", "chrono",
+1 -5
View File
@@ -1,7 +1,7 @@
[package] [package]
edition = "2021" edition = "2021"
name = "boilr" name = "boilr"
version = "1.7.3" version = "1.7.4"
[dependencies] [dependencies]
base64 = "^0.13.0" base64 = "^0.13.0"
@@ -68,7 +68,3 @@ winreg = "^0.10.1"
[features] [features]
# This feature is enabled when building for a flatpak environment # This feature is enabled when building for a flatpak environment
flatpak = [] flatpak = []
[profile.release]
strip = true # Automatically strip symbols from the binary.
lto = "thin"
@@ -25,6 +25,14 @@ https://hughsie.github.io/oars/index.html
--> -->
<content_rating type="oars-1.1" /> <content_rating type="oars-1.1" />
<releases> <releases>
<release version="1.7.4" date="2022-10-21">
<description>
<ul>
<li>Improve UI for selecting download images</li>
<li>Add dropdowns to user selection</li>
</ul>
</description>
</release>
<release version="1.7.3" date="2022-10-21"> <release version="1.7.3" date="2022-10-21">
<description> <description>
<ul> <ul>
+154 -79
View File
@@ -10,7 +10,7 @@ use crate::{
steamgriddb::{get_image_extension, get_query_type, CachedSearch, ImageType, ToDownload}, steamgriddb::{get_image_extension, get_query_type, CachedSearch, ImageType, ToDownload},
}; };
use dashmap::DashMap; use dashmap::DashMap;
use egui::{Button, ImageButton, ScrollArea}; use egui::{Button, Grid, ImageButton, ScrollArea};
use futures::executor::block_on; use futures::executor::block_on;
use steam_shortcuts_util::shortcut::ShortcutOwned; use steam_shortcuts_util::shortcut::ShortcutOwned;
use steamgriddb_api::images::MimeTypes; use steamgriddb_api::images::MimeTypes;
@@ -43,7 +43,7 @@ pub enum TextureState {
Failed, Failed,
} }
#[derive(Debug)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum GameMode { pub enum GameMode {
Shortcuts, Shortcuts,
SteamGames, SteamGames,
@@ -56,13 +56,11 @@ impl GameMode {
GameMode::SteamGames => false, GameMode::SteamGames => false,
} }
} }
}
impl ImageSelectState { pub fn label(&self) -> &str {
pub fn has_multiple_users(&self) -> bool { match self {
match &self.steam_users { GameMode::Shortcuts => "Images for shortcuts",
Some(users) => users.len() > 1, GameMode::SteamGames => "Images for steam games",
None => false,
} }
} }
} }
@@ -135,16 +133,27 @@ enum UserAction {
impl MyEguiApp { 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"); if let Some(action) = ui
if (state.selected_shortcut.is_some() .horizontal(|ui| {
|| (state.has_multiple_users() && state.steam_user.is_some())) let render_back =
&& ui.button("Back").clicked() state.selected_shortcut.is_some() || state.image_type_selected.is_some();
if render_back {
if ui.button("Back").clicked() {
return Some(UserAction::BackButton);
}
None
} else {
if let Some(value) = render_user_select(state, ui) {
return Some(value);
}
render_shortcut_mode_select(state, ui)
}
})
.inner
{ {
return UserAction::BackButton; return action;
}
if state.steam_user.is_none() {
return render_user_select(state, ui);
} }
if let Some(shortcut) = state.selected_shortcut.as_ref() { if let Some(shortcut) = state.selected_shortcut.as_ref() {
ui.heading(shortcut.name()); ui.heading(shortcut.name());
@@ -161,19 +170,6 @@ impl MyEguiApp {
} }
} else { } else {
let is_shortcut = state.game_mode.is_shortcuts(); let is_shortcut = state.game_mode.is_shortcuts();
if ui
.selectable_label(is_shortcut, "Images for shortcuts")
.clicked()
{
return UserAction::SetGamesMode(GameMode::Shortcuts);
}
if ui
.selectable_label(!is_shortcut, "Images for steam games")
.clicked()
{
return UserAction::SetGamesMode(GameMode::SteamGames);
}
if is_shortcut { if is_shortcut {
if let Some(action) = self.render_shortcut_select(ui) { if let Some(action) = self.render_shortcut_select(ui) {
return action; return action;
@@ -270,8 +266,10 @@ impl MyEguiApp {
image_type: &ImageType, image_type: &ImageType,
state: &ImageSelectState, state: &ImageSelectState,
) -> Option<UserAction> { ) -> Option<UserAction> {
ui.heading(image_type.name()); ui.label(image_type.name());
if let Some(action) = ui
.horizontal(|ui| {
if ui if ui
.small_button("Clear image?") .small_button("Clear image?")
.on_hover_text("Click here to clear the image") .on_hover_text("Click here to clear the image")
@@ -287,10 +285,24 @@ impl MyEguiApp {
{ {
return Some(UserAction::ImageTypeCleared(*image_type, true)); return Some(UserAction::ImageTypeCleared(*image_type, true));
} }
None
})
.inner
{
return Some(action);
}
let column_padding = 10.;
let column_width = MAX_WIDTH * 0.75;
let width = ui.available_width();
let columns = (width / (column_width + column_padding)).floor() as u32;
let mut column = 0;
match &*state.image_options.borrow() { match &*state.image_options.borrow() {
FetcStatus::Fetched(images) => { FetcStatus::Fetched(images) => {
let x = Grid::new("ImageThumbnailSelectGrid").spacing([column_padding,column_padding]).show(ui, |ui| {
for image in images { for image in images {
let image_key = image.thumbnail_path.as_path().to_string_lossy().to_string();
let image_key =
image.thumbnail_path.as_path().to_string_lossy().to_string();
match state.image_handles.get_mut(&image_key) { match state.image_handles.get_mut(&image_key) {
Some(mut state) => { Some(mut state) => {
@@ -298,14 +310,12 @@ impl MyEguiApp {
TextureState::Downloading => { TextureState::Downloading => {
ui.ctx().request_repaint(); ui.ctx().request_repaint();
//nothing to do,just wait //nothing to do,just wait
ui.horizontal(|ui| {
ui.spinner(); ui.spinner();
ui.label(format!("Downloading id {}", image.id));
});
} }
TextureState::Downloaded => { TextureState::Downloaded => {
//Need to load //Need to load
let image_data = load_image_from_path(&image.thumbnail_path); let image_data =
load_image_from_path(&image.thumbnail_path);
match image_data { match image_data {
Ok(image_data) => { Ok(image_data) => {
let handle = ui.ctx().load_texture( let handle = ui.ctx().load_texture(
@@ -314,10 +324,7 @@ impl MyEguiApp {
egui::TextureFilter::Linear, egui::TextureFilter::Linear,
); );
*state.value_mut() = TextureState::Loaded(handle); *state.value_mut() = TextureState::Loaded(handle);
ui.horizontal(|ui| {
ui.spinner(); ui.spinner();
ui.label("Loading");
});
} }
Err(_) => *state.value_mut() = TextureState::Failed, Err(_) => *state.value_mut() = TextureState::Failed,
} }
@@ -326,9 +333,9 @@ impl MyEguiApp {
TextureState::Loaded(texture_handle) => { TextureState::Loaded(texture_handle) => {
//need to show //need to show
let mut size = texture_handle.size_vec2(); let mut size = texture_handle.size_vec2();
clamp_to_width(&mut size, MAX_WIDTH); clamp_to_width(&mut size, column_width);
let image_button = ImageButton::new(texture_handle, size); let image_button = ImageButton::new(texture_handle, size);
if ui.add(image_button).clicked() { if ui.add_sized(size,image_button).clicked() {
return Some(UserAction::ImageSelected(image.clone())); return Some(UserAction::ImageSelected(image.clone()));
} }
} }
@@ -343,9 +350,11 @@ impl MyEguiApp {
let path = &image.thumbnail_path; let path = &image.thumbnail_path;
//Redownload if file is too small //Redownload if file is too small
if !path.exists() if !path.exists()
|| std::fs::metadata(path).map(|m| m.len()).unwrap_or_default() < 2 || std::fs::metadata(path).map(|m| m.len()).unwrap_or_default()
< 2
{ {
image_handles.insert(image_key.clone(), TextureState::Downloading); image_handles
.insert(image_key.clone(), TextureState::Downloading);
let to_download = ToDownload { let to_download = ToDownload {
path: path.clone(), path: path.clone(),
url: image.thumbnail_url.clone(), url: image.thumbnail_url.clone(),
@@ -362,10 +371,23 @@ impl MyEguiApp {
image_handles.insert(image_key, TextureState::Downloaded); image_handles.insert(image_key, TextureState::Downloaded);
}); });
} else { } else {
image_handles.insert(image_key.clone(), TextureState::Downloaded); image_handles
.insert(image_key.clone(), TextureState::Downloaded);
} }
} }
} }
column += 1;
if column >= columns{
column=0;
ui.end_row();
}
}
None
}).inner;
if x.is_some(){
return x;
} }
} }
_ => { _ => {
@@ -719,6 +741,28 @@ impl MyEguiApp {
} }
} }
fn render_shortcut_mode_select(state: &ImageSelectState, ui: &mut egui::Ui) -> Option<UserAction> {
let mode_before = state.game_mode.clone();
let combo_box = egui::ComboBox::new("ImageModeSelect", "").selected_text(mode_before.label());
let mut mode_after = state.game_mode.clone();
combo_box.show_ui(ui, |ui| {
ui.selectable_value(
&mut mode_after,
GameMode::Shortcuts,
GameMode::Shortcuts.label(),
);
ui.selectable_value(
&mut mode_after,
GameMode::SteamGames,
GameMode::SteamGames.label(),
);
});
if !mode_after.eq(&mode_before) {
return Some(UserAction::SetGamesMode(mode_after));
}
None
}
fn render_possible_names( fn render_possible_names(
possible_names: &Vec<steamgriddb_api::search::SearchResult>, possible_names: &Vec<steamgriddb_api::search::SearchResult>,
ui: &mut egui::Ui, ui: &mut egui::Ui,
@@ -764,74 +808,88 @@ fn render_steam_game_select(ui: &mut egui::Ui, state: &ImageSelectState) -> Opti
} }
fn render_shortcut_images(ui: &mut egui::Ui, state: &ImageSelectState) -> Option<UserAction> { fn render_shortcut_images(ui: &mut egui::Ui, state: &ImageSelectState) -> Option<UserAction> {
if ui
.button("Click here if the images are for a wrong game")
.clicked()
{
return Some(UserAction::CorrectGridId);
}
let shortcut = state.selected_shortcut.as_ref().unwrap(); let shortcut = state.selected_shortcut.as_ref().unwrap();
let user_path = &state.steam_user.as_ref().unwrap().steam_user_data_folder; let user_path = &state.steam_user.as_ref().unwrap().steam_user_data_folder;
let x = if ui.available_width() > MAX_WIDTH * 3. {
if ui.available_width() > MAX_WIDTH * 3. {
ui.horizontal(|ui| { ui.horizontal(|ui| {
let x = ui.vertical(|ui| { let x = ui
let texture = texture_from_iamge_type(shortcut, &ImageType::Grid, user_path, state); .vertical(|ui| {
let texture =
texture_from_iamge_type(shortcut, &ImageType::Grid, user_path, state);
ui.label(ImageType::Grid.name()); ui.label(ImageType::Grid.name());
if render_thumbnail(ui, texture).clicked(){ if render_thumbnail(ui, texture).clicked() {
return Some(UserAction::ImageTypeSelected(ImageType::Grid)); return Some(UserAction::ImageTypeSelected(ImageType::Grid));
} }
None None
}).inner; })
if x.is_some(){ .inner;
if x.is_some() {
return x; return x;
} }
let x = ui.vertical(|ui| { let x = ui
let texture = texture_from_iamge_type(shortcut, &ImageType::Hero, user_path, state); .vertical(|ui| {
let texture =
texture_from_iamge_type(shortcut, &ImageType::Hero, user_path, state);
ui.label(ImageType::Hero.name()); ui.label(ImageType::Hero.name());
if render_thumbnail(ui, texture).clicked(){ if render_thumbnail(ui, texture).clicked() {
return Some(UserAction::ImageTypeSelected(ImageType::Hero)); return Some(UserAction::ImageTypeSelected(ImageType::Hero));
} }
let texture = let texture =
texture_from_iamge_type(shortcut, &ImageType::WideGrid, user_path, state); texture_from_iamge_type(shortcut, &ImageType::WideGrid, user_path, state);
ui.label(ImageType::WideGrid.name()); ui.label(ImageType::WideGrid.name());
if render_thumbnail(ui, texture).clicked(){ if render_thumbnail(ui, texture).clicked() {
return Some(UserAction::ImageTypeSelected(ImageType::WideGrid)); return Some(UserAction::ImageTypeSelected(ImageType::WideGrid));
} }
let texture = texture_from_iamge_type(shortcut, &ImageType::Logo, user_path, state); let texture =
texture_from_iamge_type(shortcut, &ImageType::Logo, user_path, state);
ui.label(ImageType::Logo.name()); ui.label(ImageType::Logo.name());
if render_thumbnail(ui, texture).clicked(){ if render_thumbnail(ui, texture).clicked() {
return Some(UserAction::ImageTypeSelected(ImageType::Logo)); return Some(UserAction::ImageTypeSelected(ImageType::Logo));
} }
None None
}).inner; })
if x.is_some(){ .inner;
if x.is_some() {
return x; return x;
} }
ui.vertical(|ui| { ui.vertical(|ui| {
let texture = texture_from_iamge_type(shortcut, &ImageType::Icon, user_path, state); let texture = texture_from_iamge_type(shortcut, &ImageType::Icon, user_path, state);
ui.label(ImageType::Icon.name()); ui.label(ImageType::Icon.name());
if render_thumbnail(ui, texture).clicked(){ if render_thumbnail(ui, texture).clicked() {
return Some(UserAction::ImageTypeSelected(ImageType::Icon)); return Some(UserAction::ImageTypeSelected(ImageType::Icon));
} }
let texture = let texture =
texture_from_iamge_type(shortcut, &ImageType::BigPicture, user_path, state); texture_from_iamge_type(shortcut, &ImageType::BigPicture, user_path, state);
ui.label(ImageType::BigPicture.name()); ui.label(ImageType::BigPicture.name());
if render_thumbnail(ui, texture).clicked(){ if render_thumbnail(ui, texture).clicked() {
return Some(UserAction::ImageTypeSelected(ImageType::BigPicture)); return Some(UserAction::ImageTypeSelected(ImageType::BigPicture));
} }
None None
}).inner })
}).inner .inner
})
.inner
} else { } else {
render_image_types_as_list(shortcut, user_path, state, ui) render_image_types_as_list(shortcut, user_path, state, ui)
};
if ui
.button("Click here if the images are for a wrong game")
.clicked()
{
return Some(UserAction::CorrectGridId);
} }
x
} }
fn render_image_types_as_list(shortcut: &GameType, user_path: &String, state: &ImageSelectState, ui: &mut egui::Ui) -> Option<UserAction> { fn render_image_types_as_list(
shortcut: &GameType,
user_path: &String,
state: &ImageSelectState,
ui: &mut egui::Ui,
) -> Option<UserAction> {
let types = ImageType::all(); let types = ImageType::all();
for image_type in types { for image_type in types {
let texture = texture_from_iamge_type(shortcut, image_type, user_path, state); let texture = texture_from_iamge_type(shortcut, image_type, user_path, state);
@@ -861,17 +919,34 @@ fn texture_from_iamge_type(
}) })
} }
fn render_user_select(state: &ImageSelectState, ui: &mut egui::Ui) -> UserAction { fn render_user_select(state: &ImageSelectState, ui: &mut egui::Ui) -> Option<UserAction> {
let users = state.steam_users.as_ref().unwrap(); if state.steam_user.is_none() {
if users.len() == 1 { if let Some(users) = &state.steam_users {
return UserAction::UserSelected(users[0].clone()); if users.len() > 0 {
} return Some(UserAction::UserSelected(users[0].clone()));
for user in users {
if ui.button(&user.user_id).clicked() {
return UserAction::UserSelected(user.clone());
} }
} }
UserAction::NoAction } else {
let mut selected_user = state.steam_user.as_ref().unwrap().clone();
let id_before = selected_user.user_id.clone();
if let Some(steam_users) = &state.steam_users {
if steam_users.len() > 0 {
let combo_box = egui::ComboBox::new("ImageUserSelect", "")
.selected_text(format!("Steam user id: {}", &selected_user.user_id));
combo_box.show_ui(ui, |ui| {
for user in steam_users {
ui.selectable_value(&mut selected_user, user.clone(), &user.user_id);
}
});
}
}
let id_now = selected_user.user_id.clone();
if !id_before.eq(&id_now) {
return Some(UserAction::UserSelected(selected_user.clone()));
}
}
None
} }
const MAX_WIDTH: f32 = 300.; const MAX_WIDTH: f32 = 300.;