diff --git a/data/resources/fullscreenui/back-icon.png b/data/resources/fullscreenui/back-icon.png
deleted file mode 100644
index 438aaa39b..000000000
Binary files a/data/resources/fullscreenui/back-icon.png and /dev/null differ
diff --git a/data/resources/fullscreenui/back-icon.svg b/data/resources/fullscreenui/back-icon.svg
new file mode 100644
index 000000000..b93eacaa9
--- /dev/null
+++ b/data/resources/fullscreenui/back-icon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/data/resources/fullscreenui/desktop-mode.png b/data/resources/fullscreenui/desktop-mode.png
deleted file mode 100644
index 081e031b2..000000000
Binary files a/data/resources/fullscreenui/desktop-mode.png and /dev/null differ
diff --git a/data/resources/fullscreenui/desktop-mode.svg b/data/resources/fullscreenui/desktop-mode.svg
new file mode 100644
index 000000000..a3951508c
--- /dev/null
+++ b/data/resources/fullscreenui/desktop-mode.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/data/resources/fullscreenui/settings.png b/data/resources/fullscreenui/exe-file.png
similarity index 100%
rename from data/resources/fullscreenui/settings.png
rename to data/resources/fullscreenui/exe-file.png
diff --git a/data/resources/fullscreenui/exit.png b/data/resources/fullscreenui/exit.png
deleted file mode 100644
index d0cec9db7..000000000
Binary files a/data/resources/fullscreenui/exit.png and /dev/null differ
diff --git a/data/resources/fullscreenui/exit.svg b/data/resources/fullscreenui/exit.svg
new file mode 100644
index 000000000..036e983e0
--- /dev/null
+++ b/data/resources/fullscreenui/exit.svg
@@ -0,0 +1,38 @@
+
+
diff --git a/data/resources/fullscreenui/game-list.png b/data/resources/fullscreenui/game-list.png
deleted file mode 100644
index bb6bffc16..000000000
Binary files a/data/resources/fullscreenui/game-list.png and /dev/null differ
diff --git a/data/resources/fullscreenui/game-list.svg b/data/resources/fullscreenui/game-list.svg
new file mode 100644
index 000000000..b7f38e0fe
--- /dev/null
+++ b/data/resources/fullscreenui/game-list.svg
@@ -0,0 +1,46 @@
+
+
diff --git a/data/resources/fullscreenui/start-file.png b/data/resources/fullscreenui/playlist-file.png
similarity index 100%
rename from data/resources/fullscreenui/start-file.png
rename to data/resources/fullscreenui/playlist-file.png
diff --git a/data/resources/fullscreenui/settings.svg b/data/resources/fullscreenui/settings.svg
new file mode 100644
index 000000000..89ef7c0e9
--- /dev/null
+++ b/data/resources/fullscreenui/settings.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/data/resources/fullscreenui/start-bios.png b/data/resources/fullscreenui/start-bios.png
deleted file mode 100644
index bd7405269..000000000
Binary files a/data/resources/fullscreenui/start-bios.png and /dev/null differ
diff --git a/data/resources/fullscreenui/start-bios.svg b/data/resources/fullscreenui/start-bios.svg
new file mode 100644
index 000000000..084341ea8
--- /dev/null
+++ b/data/resources/fullscreenui/start-bios.svg
@@ -0,0 +1,38 @@
+
+
diff --git a/data/resources/fullscreenui/start-disc.png b/data/resources/fullscreenui/start-disc.png
deleted file mode 100644
index 5fa911ccd..000000000
Binary files a/data/resources/fullscreenui/start-disc.png and /dev/null differ
diff --git a/data/resources/fullscreenui/start-disc.svg b/data/resources/fullscreenui/start-disc.svg
new file mode 100644
index 000000000..9f2191a49
--- /dev/null
+++ b/data/resources/fullscreenui/start-disc.svg
@@ -0,0 +1,38 @@
+
+
diff --git a/data/resources/fullscreenui/start-file.svg b/data/resources/fullscreenui/start-file.svg
new file mode 100644
index 000000000..67bf89f2d
--- /dev/null
+++ b/data/resources/fullscreenui/start-file.svg
@@ -0,0 +1,40 @@
+
+
diff --git a/src/core/fullscreen_ui.cpp b/src/core/fullscreen_ui.cpp
index 706e2addf..02e0519e0 100644
--- a/src/core/fullscreen_ui.cpp
+++ b/src/core/fullscreen_ui.cpp
@@ -80,6 +80,7 @@ using ImGuiFullscreen::ChoiceDialogOptions;
using ImGuiFullscreen::FocusResetType;
using ImGuiFullscreen::LAYOUT_FOOTER_HEIGHT;
+using ImGuiFullscreen::LAYOUT_HORIZONTAL_MENU_ITEM_IMAGE_SIZE;
using ImGuiFullscreen::LAYOUT_LARGE_FONT_SIZE;
using ImGuiFullscreen::LAYOUT_MEDIUM_FONT_SIZE;
using ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT;
@@ -249,6 +250,11 @@ static ChoiceDialogOptions GetBackgroundOptions(const TinyString& current_value)
//////////////////////////////////////////////////////////////////////////
static bool LoadResources();
static void DestroyResources();
+static GPUTexture* GetUserThemeableTexture(
+ const std::string_view png_name, const std::string_view svg_name, bool* is_colorable = nullptr,
+ const ImVec2& svg_size = LayoutScale(LAYOUT_HORIZONTAL_MENU_ITEM_IMAGE_SIZE, LAYOUT_HORIZONTAL_MENU_ITEM_IMAGE_SIZE));
+static bool UserThemeableHorizontalButton(const std::string_view png_name, const std::string_view svg_name,
+ const char* title, const char* description);
//////////////////////////////////////////////////////////////////////////
// Landing
@@ -478,12 +484,13 @@ static constexpr const std::array s_ps_button_mapping{
std::make_pair(ICON_PF_RIGHT_TRIGGER_RT, ICON_PF_RIGHT_TRIGGER_R2),
};
-static constexpr std::array s_theme_names = {FSUI_NSTR("Automatic"), FSUI_NSTR("Dark"), FSUI_NSTR("Light"),
- FSUI_NSTR("AMOLED"), FSUI_NSTR("Cobalt Sky"), FSUI_NSTR("Grey Matter"),
- FSUI_NSTR("Pinky Pals"), FSUI_NSTR("Dark Ruby"), FSUI_NSTR("Purple Rain")};
+static constexpr std::array s_theme_names = {
+ FSUI_NSTR("Automatic"), FSUI_NSTR("Dark"), FSUI_NSTR("Light"),
+ FSUI_NSTR("AMOLED"), FSUI_NSTR("Cobalt Sky"), FSUI_NSTR("Grey Matter"),
+ FSUI_NSTR("Pinky Pals"), FSUI_NSTR("Dark Ruby"), FSUI_NSTR("Purple Rain")};
-static constexpr std::array s_theme_values = {"", "Dark", "Light", "AMOLED",
- "CobaltSky", "GreyMatter", "PinkyPals", "DarkRuby", "PurpleRain"};
+static constexpr std::array s_theme_values = {"", "Dark", "Light", "AMOLED", "CobaltSky",
+ "GreyMatter", "PinkyPals", "DarkRuby", "PurpleRain"};
//////////////////////////////////////////////////////////////////////////
// State
@@ -1121,9 +1128,9 @@ bool FullscreenUI::LoadResources()
{
s_state.app_icon_texture = LoadTexture("images/duck.png");
s_state.fallback_disc_texture = LoadTexture("fullscreenui/cdrom.png");
- s_state.fallback_exe_texture = LoadTexture("fullscreenui/settings.png");
+ s_state.fallback_exe_texture = LoadTexture("fullscreenui/exe-file.png");
s_state.fallback_psf_texture = LoadTexture("fullscreenui/psf-file.png");
- s_state.fallback_playlist_texture = LoadTexture("fullscreenui/game-list.png");
+ s_state.fallback_playlist_texture = LoadTexture("fullscreenui/playlist-file.png");
return true;
}
@@ -1138,6 +1145,53 @@ void FullscreenUI::DestroyResources()
s_state.app_icon_texture.reset();
}
+GPUTexture* FullscreenUI::GetUserThemeableTexture(const std::string_view png_name, const std::string_view svg_name,
+ bool* is_colorable, const ImVec2& svg_size)
+{
+ GPUTexture* tex = ImGuiFullscreen::FindCachedTexture(png_name);
+ if (tex)
+ {
+ if (is_colorable)
+ *is_colorable = false;
+
+ return tex;
+ }
+
+ const u32 svg_width = static_cast(svg_size.x);
+ const u32 svg_height = static_cast(svg_size.y);
+ tex = ImGuiFullscreen::FindCachedTexture(svg_name, svg_width, svg_height);
+ if (tex)
+ return tex;
+
+ // slow path, check filesystem for override
+ if (EmuFolders::Resources != EmuFolders::UserResources &&
+ FileSystem::FileExists(Path::Combine(EmuFolders::UserResources, png_name).c_str()))
+ {
+ // use the user's png
+ if (is_colorable)
+ *is_colorable = false;
+
+ return ImGuiFullscreen::GetCachedTexture(png_name);
+ }
+
+ // otherwise use the system/user svg
+ if (is_colorable)
+ *is_colorable = true;
+
+ return ImGuiFullscreen::GetCachedTexture(svg_name, svg_width, svg_height);
+}
+
+bool FullscreenUI::UserThemeableHorizontalButton(const std::string_view png_name, const std::string_view svg_name,
+ const char* title, const char* description)
+{
+ bool is_colorable;
+ GPUTexture* icon = GetUserThemeableTexture(
+ png_name, svg_name, &is_colorable,
+ LayoutScale(LAYOUT_HORIZONTAL_MENU_ITEM_IMAGE_SIZE, LAYOUT_HORIZONTAL_MENU_ITEM_IMAGE_SIZE));
+ return HorizontalMenuItem(icon, title, description,
+ is_colorable ? ImGui::GetColorU32(ImGuiCol_Text) : IM_COL32(255, 255, 255, 255));
+}
+
//////////////////////////////////////////////////////////////////////////
// Utility
//////////////////////////////////////////////////////////////////////////
@@ -1873,28 +1927,29 @@ void FullscreenUI::DrawLandingWindow()
{
ResetFocusHere();
- if (HorizontalMenuItem(GetCachedTexture("fullscreenui/game-list.png"), FSUI_CSTR("Game List"),
- FSUI_CSTR("Launch a game from images scanned from your game directories.")))
+ if (UserThemeableHorizontalButton("fullscreenui/game-list.png", "fullscreenui/game-list.svg",
+ FSUI_CSTR("Game List"),
+ FSUI_CSTR("Launch a game from images scanned from your game directories.")))
{
SwitchToGameList();
}
- if (HorizontalMenuItem(
- GetCachedTexture("fullscreenui/cdrom.png"), FSUI_CSTR("Start Game"),
+ if (UserThemeableHorizontalButton(
+ "fullscreenui/cdrom.png", "fullscreenui/start-disc.svg", FSUI_CSTR("Start Game"),
FSUI_CSTR("Launch a game from a file, disc, or starts the console without any disc inserted.")))
{
s_state.current_main_window = MainWindowType::StartGame;
QueueResetFocus(FocusResetType::ViewChanged);
}
- if (HorizontalMenuItem(GetCachedTexture("fullscreenui/settings.png"), FSUI_CSTR("Settings"),
- FSUI_CSTR("Changes settings for the application.")))
+ if (UserThemeableHorizontalButton("fullscreenui/settings.png", "fullscreenui/settings.svg", FSUI_CSTR("Settings"),
+ FSUI_CSTR("Changes settings for the application.")))
{
SwitchToSettings();
}
- if (HorizontalMenuItem(GetCachedTexture("fullscreenui/exit.png"), FSUI_CSTR("Exit"),
- FSUI_CSTR("Return to desktop mode, or exit the application.")) ||
+ if (UserThemeableHorizontalButton("fullscreenui/exit.png", "fullscreenui/exit.svg", FSUI_CSTR("Exit"),
+ FSUI_CSTR("Return to desktop mode, or exit the application.")) ||
(!AreAnyDialogsOpen() && WantsToCloseMenu()))
{
s_state.current_main_window = MainWindowType::Exit;
@@ -1949,27 +2004,26 @@ void FullscreenUI::DrawStartGameWindow()
{
ResetFocusHere();
- if (HorizontalMenuItem(GetCachedTexture("fullscreenui/start-file.png"), FSUI_CSTR("Start File"),
- FSUI_CSTR("Launch a game by selecting a file/disc image.")))
+ if (HorizontalMenuItem(GetUserThemeableTexture("fullscreenui/start-file.png", "fullscreenui/start-file.svg"),
+ FSUI_CSTR("Start File"), FSUI_CSTR("Launch a game by selecting a file/disc image.")))
{
DoStartFile();
}
- if (HorizontalMenuItem(GetCachedTexture("fullscreenui/start-disc.png"), FSUI_CSTR("Start Disc"),
- FSUI_CSTR("Start a game from a disc in your PC's DVD drive.")))
+ if (HorizontalMenuItem(GetUserThemeableTexture("fullscreenui/start-disc.png", "fullscreenui/start-disc.svg"),
+ FSUI_CSTR("Start Disc"), FSUI_CSTR("Start a game from a disc in your PC's DVD drive.")))
{
DoStartDisc();
}
- if (HorizontalMenuItem(GetCachedTexture("fullscreenui/start-bios.png"), FSUI_CSTR("Start BIOS"),
- FSUI_CSTR("Start the console without any disc inserted.")))
+ if (HorizontalMenuItem(GetUserThemeableTexture("fullscreenui/start-bios.png", "fullscreenui/start-bios.svg"),
+ FSUI_CSTR("Start BIOS"), FSUI_CSTR("Start the console without any disc inserted.")))
{
DoStartBIOS();
}
- // https://www.iconpacks.net/free-icon/arrow-back-3783.html
- if (HorizontalMenuItem(GetCachedTexture("fullscreenui/back-icon.png"), FSUI_CSTR("Back"),
- FSUI_CSTR("Return to the previous menu.")) ||
+ if (HorizontalMenuItem(GetUserThemeableTexture("fullscreenui/back-icon.png", "fullscreenui/back-icon.svg"),
+ FSUI_CSTR("Back"), FSUI_CSTR("Return to the previous menu.")) ||
(!AreAnyDialogsOpen() && WantsToCloseMenu()))
{
s_state.current_main_window = MainWindowType::Landing;
@@ -2016,22 +2070,23 @@ void FullscreenUI::DrawExitWindow()
{
ResetFocusHere();
- // https://www.iconpacks.net/free-icon/arrow-back-3783.html
- if (HorizontalMenuItem(GetCachedTexture("fullscreenui/back-icon.png"), FSUI_CSTR("Back"),
- FSUI_CSTR("Return to the previous menu.")) ||
+ if (HorizontalMenuItem(GetUserThemeableTexture("fullscreenui/back-icon.png", "fullscreenui/back-icon.svg"),
+ FSUI_CSTR("Back"), FSUI_CSTR("Return to the previous menu.")) ||
WantsToCloseMenu())
{
s_state.current_main_window = MainWindowType::Landing;
QueueResetFocus(FocusResetType::ViewChanged);
}
- if (HorizontalMenuItem(GetCachedTexture("fullscreenui/exit.png"), FSUI_CSTR("Exit DuckStation"),
+ if (HorizontalMenuItem(GetUserThemeableTexture("fullscreenui/exit.png", "fullscreenui/exit.svg"),
+ FSUI_CSTR("Exit DuckStation"),
FSUI_CSTR("Completely exits the application, returning you to your desktop.")))
{
DoRequestExit();
}
- if (HorizontalMenuItem(GetCachedTexture("fullscreenui/desktop-mode.png"), FSUI_CSTR("Desktop Mode"),
+ if (HorizontalMenuItem(GetUserThemeableTexture("fullscreenui/desktop-mode.png", "fullscreenui/desktop-mode.svg"),
+ FSUI_CSTR("Desktop Mode"),
FSUI_CSTR("Exits Big Picture mode, returning to the desktop interface.")))
{
DoDesktopMode();
diff --git a/src/util/imgui_fullscreen.cpp b/src/util/imgui_fullscreen.cpp
index b945abe55..b0ade1986 100644
--- a/src/util/imgui_fullscreen.cpp
+++ b/src/util/imgui_fullscreen.cpp
@@ -372,6 +372,26 @@ std::shared_ptr ImGuiFullscreen::LoadTexture(std::string_view path,
return s_state.placeholder_texture;
}
+GPUTexture* ImGuiFullscreen::FindCachedTexture(std::string_view name)
+{
+ std::shared_ptr* tex_ptr = s_state.texture_cache.Lookup(name);
+ return tex_ptr ? tex_ptr->get() : nullptr;
+}
+
+GPUTexture* ImGuiFullscreen::FindCachedTexture(std::string_view name, u32 svg_width, u32 svg_height)
+{
+ // ignore size hints if it's not needed, don't duplicate
+ if (!TextureNeedsSVGDimensions(name))
+ return FindCachedTexture(name);
+
+ svg_width = static_cast(std::ceil(LayoutScale(static_cast(svg_width))));
+ svg_height = static_cast(std::ceil(LayoutScale(static_cast(svg_height))));
+
+ const SmallString wh_name = SmallString::from_format("{}#{}x{}", name, svg_width, svg_height);
+ std::shared_ptr* tex_ptr = s_state.texture_cache.Lookup(wh_name.view());
+ return tex_ptr ? tex_ptr->get() : nullptr;
+}
+
GPUTexture* ImGuiFullscreen::GetCachedTexture(std::string_view name)
{
std::shared_ptr* tex_ptr = s_state.texture_cache.Lookup(name);
@@ -561,6 +581,12 @@ ImRect ImGuiFullscreen::CenterImage(const ImVec2& fit_size, const ImVec2& image_
return ret;
}
+ImRect ImGuiFullscreen::CenterImage(const ImVec2& fit_rect, const GPUTexture* texture)
+{
+ const GSVector2 texture_size = GSVector2(texture->GetSizeVec());
+ return CenterImage(fit_rect, ImVec2(texture_size.x, texture_size.y));
+}
+
ImRect ImGuiFullscreen::CenterImage(const ImRect& fit_rect, const ImVec2& image_size)
{
ImRect ret(CenterImage(fit_rect.Max - fit_rect.Min, image_size));
@@ -568,6 +594,12 @@ ImRect ImGuiFullscreen::CenterImage(const ImRect& fit_rect, const ImVec2& image_
return ret;
}
+ImRect ImGuiFullscreen::CenterImage(const ImRect& fit_rect, const GPUTexture* texture)
+{
+ const GSVector2 texture_size = GSVector2(texture->GetSizeVec());
+ return CenterImage(fit_rect, ImVec2(texture_size.x, texture_size.y));
+}
+
ImRect ImGuiFullscreen::FitImage(const ImVec2& fit_size, const ImVec2& image_size)
{
ImRect rect;
@@ -2209,7 +2241,7 @@ void ImGuiFullscreen::EndHorizontalMenu()
EndFullscreenWindow();
}
-bool ImGuiFullscreen::HorizontalMenuItem(GPUTexture* icon, const char* title, const char* description)
+bool ImGuiFullscreen::HorizontalMenuItem(GPUTexture* icon, const char* title, const char* description, u32 color)
{
ImGuiWindow* window = ImGui::GetCurrentWindow();
if (window->SkipItems)
@@ -2244,11 +2276,13 @@ bool ImGuiFullscreen::HorizontalMenuItem(GPUTexture* icon, const char* title, co
bb.Max -= style.FramePadding;
const float avail_width = bb.Max.x - bb.Min.x;
- const float icon_size = LayoutScale(150.0f);
+ const float icon_size = LayoutScale(LAYOUT_HORIZONTAL_MENU_ITEM_IMAGE_SIZE);
const ImVec2 icon_pos = bb.Min + ImVec2((avail_width - icon_size) * 0.5f, 0.0f);
+ const ImRect icon_box = CenterImage(ImRect(icon_pos, icon_pos + ImVec2(icon_size, icon_size)), icon);
ImDrawList* dl = ImGui::GetWindowDrawList();
- dl->AddImage(reinterpret_cast(icon), icon_pos, icon_pos + ImVec2(icon_size, icon_size));
+ dl->AddImage(reinterpret_cast(icon), icon_box.Min, icon_box.Max, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f),
+ color);
ImFont* title_font = UIStyle.LargeFont;
const ImVec2 title_size =
diff --git a/src/util/imgui_fullscreen.h b/src/util/imgui_fullscreen.h
index cc98cdd08..b7a4ce616 100644
--- a/src/util/imgui_fullscreen.h
+++ b/src/util/imgui_fullscreen.h
@@ -42,6 +42,7 @@ static constexpr float LAYOUT_FOOTER_HEIGHT = LAYOUT_MEDIUM_FONT_SIZE + LAYOUT_F
static constexpr float LAYOUT_HORIZONTAL_MENU_HEIGHT = 320.0f;
static constexpr float LAYOUT_HORIZONTAL_MENU_PADDING = 30.0f;
static constexpr float LAYOUT_HORIZONTAL_MENU_ITEM_WIDTH = 250.0f;
+static constexpr float LAYOUT_HORIZONTAL_MENU_ITEM_IMAGE_SIZE = 150.0f;
static constexpr float LAYOUT_SHADOW_OFFSET = 1.0f;
struct ALIGN_TO_CACHE_LINE UIStyles
@@ -151,7 +152,9 @@ ALWAYS_INLINE static std::string_view RemoveHash(std::string_view s)
/// Centers an image within the specified bounds, scaling up or down as needed.
ImRect CenterImage(const ImVec2& fit_size, const ImVec2& image_size);
+ImRect CenterImage(const ImVec2& fit_rect, const GPUTexture* texture);
ImRect CenterImage(const ImRect& fit_rect, const ImVec2& image_size);
+ImRect CenterImage(const ImRect& fit_rect, const GPUTexture* texture);
/// Fits an image to the specified bounds, cropping if needed. Returns UV coordinates.
ImRect FitImage(const ImVec2& fit_size, const ImVec2& image_size);
@@ -170,6 +173,8 @@ void Shutdown(bool clear_state);
/// Texture cache.
const std::shared_ptr& GetPlaceholderTexture();
std::shared_ptr LoadTexture(std::string_view path, u32 svg_width = 0, u32 svg_height = 0);
+GPUTexture* FindCachedTexture(std::string_view name);
+GPUTexture* FindCachedTexture(std::string_view name, u32 svg_width, u32 svg_height);
GPUTexture* GetCachedTexture(std::string_view name);
GPUTexture* GetCachedTexture(std::string_view name, u32 svg_width, u32 svg_height);
GPUTexture* GetCachedTextureAsync(std::string_view name);
@@ -335,7 +340,8 @@ bool NavTab(const char* title, bool is_active, bool enabled, float width, float
bool BeginHorizontalMenu(const char* name, const ImVec2& position, const ImVec2& size, const ImVec4& bg_color,
u32 num_items);
void EndHorizontalMenu();
-bool HorizontalMenuItem(GPUTexture* icon, const char* title, const char* description);
+bool HorizontalMenuItem(GPUTexture* icon, const char* title, const char* description,
+ u32 color = IM_COL32(255, 255, 255, 255));
using FileSelectorCallback = std::function;
using FileSelectorFilters = std::vector;