fix(ui): stop global shortcuts auto-repeating; skip invalid skins

The global keyboard shortcuts tested keys without disabling key-repeat,
so holding one down fired it every frame: the theme cycle, balance and
market layout cycles, the effects and background toggles, the settings
and sidebar-collapse keys all re-triggered on a held key. Cycling themes
also stepped onto skins that had failed to load and wrote the broken
pick into settings.

- Every one-shot shortcut now checks for a fresh key press only, so a
  held key fires once.
- Theme cycling skips over invalid skins and only applies, persists, and
  shows the toast when a skin actually loads; if none can load, settings
  are left untouched and no toast appears.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-10-13 17:42:29 -05:00
co-authored by Claude Opus 4.8
parent 96352ce9ad
commit 95605dd7e9
4 changed files with 28 additions and 21 deletions
+23 -16
View File
@@ -1705,14 +1705,14 @@ void App::handleGlobalShortcuts()
ImGuiIO& io = ImGui::GetIO();
// Keyboard shortcut: Ctrl+, to open Settings page
if (io.KeyCtrl && ImGui::IsKeyPressed(ImGuiKey_Comma)) {
if (io.KeyCtrl && ImGui::IsKeyPressed(ImGuiKey_Comma, false)) {
setCurrentPage(ui::NavPage::Settings);
}
// Keyboard shortcut: Ctrl+Left/Right to cycle themes
if (io.KeyCtrl && !io.WantTextInput) {
bool prevTheme = ImGui::IsKeyPressed(ImGuiKey_LeftArrow);
bool nextTheme = ImGui::IsKeyPressed(ImGuiKey_RightArrow);
bool prevTheme = ImGui::IsKeyPressed(ImGuiKey_LeftArrow, false);
bool nextTheme = ImGui::IsKeyPressed(ImGuiKey_RightArrow, false);
if (prevTheme || nextTheme) {
auto& skinMgr = ui::schema::SkinManager::instance();
const auto& skins = skinMgr.available();
@@ -1721,16 +1721,23 @@ void App::handleGlobalShortcuts()
for (int i = 0; i < (int)skins.size(); i++) {
if (skins[i].id == skinMgr.activeSkinId()) { cur = i; break; }
}
if (prevTheme)
cur = (cur - 1 + (int)skins.size()) % (int)skins.size();
else
cur = (cur + 1) % (int)skins.size();
skinMgr.setActiveSkin(skins[cur].id);
if (settings_) {
settings_->setSkinId(skins[cur].id);
settings_->save();
const int n = (int)skins.size();
const int step = prevTheme ? n - 1 : 1; // -1 (mod n) when cycling back
int pick = cur;
bool applied = false;
for (int k = 0; k < n && !applied; k++) {
pick = (pick + step) % n;
if (!skins[pick].valid) continue;
applied = skinMgr.setActiveSkin(skins[pick].id);
}
ui::Notifications::instance().info(std::string(TR("appx_theme_prefix")) + skins[cur].name);
if (applied) {
if (settings_) {
settings_->setSkinId(skins[pick].id);
settings_->save();
}
ui::Notifications::instance().info(std::string(TR("appx_theme_prefix")) + skins[pick].name);
}
// else: every skin invalid / failed to load; leave settings untouched, no toast
}
}
}
@@ -1742,7 +1749,7 @@ void App::handleGlobalShortcuts()
// Keyboard shortcut: Ctrl+Shift+Down to toggle low-spec mode
// (checked BEFORE Ctrl+Down so it doesn't also trigger theme effects)
if (io.KeyCtrl && io.KeyShift && !io.WantTextInput && ImGui::IsKeyPressed(ImGuiKey_DownArrow)) {
if (io.KeyCtrl && io.KeyShift && !io.WantTextInput && ImGui::IsKeyPressed(ImGuiKey_DownArrow, false)) {
bool newLow = !ui::effects::isLowSpecMode();
if (settings_) {
// Persist the mode (and the opaque window low-spec implies) but not the effect prefs:
@@ -1758,7 +1765,7 @@ void App::handleGlobalShortcuts()
}
// Keyboard shortcut: Ctrl+Down to toggle theme effects (Shift excluded)
else if (io.KeyCtrl && !io.KeyShift && !io.WantTextInput && ImGui::IsKeyPressed(ImGuiKey_DownArrow)) {
else if (io.KeyCtrl && !io.KeyShift && !io.WantTextInput && ImGui::IsKeyPressed(ImGuiKey_DownArrow, false)) {
bool newState = !ui::effects::ThemeEffects::instance().isEnabled();
ui::effects::ThemeEffects::instance().setEnabled(newState);
ui::effects::ThemeEffects::instance().setReducedTransparency(!newState);
@@ -1770,7 +1777,7 @@ void App::handleGlobalShortcuts()
}
// Keyboard shortcut: Ctrl+Up to toggle simple gradient background
if (io.KeyCtrl && !io.KeyShift && !io.WantTextInput && ImGui::IsKeyPressed(ImGuiKey_UpArrow)) {
if (io.KeyCtrl && !io.KeyShift && !io.WantTextInput && ImGui::IsKeyPressed(ImGuiKey_UpArrow, false)) {
bool newGrad = !settings_->getGradientBackground();
settings_->setGradientBackground(newGrad);
ui::schema::SkinManager::instance().setGradientMode(newGrad);
@@ -1781,7 +1788,7 @@ void App::handleGlobalShortcuts()
// Debug: Ctrl+Shift+W to re-show first-run wizard (set to false to disable)
constexpr bool ENABLE_WIZARD_HOTKEY = false;
if constexpr (ENABLE_WIZARD_HOTKEY) {
if (io.KeyCtrl && io.KeyShift && ImGui::IsKeyPressed(ImGuiKey_W)) {
if (io.KeyCtrl && io.KeyShift && ImGui::IsKeyPressed(ImGuiKey_W, false)) {
wizard_phase_ = WizardPhase::Appearance;
DEBUG_LOGF("[Debug] Wizard re-opened via Ctrl+Shift+W\n");
}