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:
+23
-16
@@ -1705,14 +1705,14 @@ void App::handleGlobalShortcuts()
|
|||||||
ImGuiIO& io = ImGui::GetIO();
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
|
||||||
// Keyboard shortcut: Ctrl+, to open Settings page
|
// 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);
|
setCurrentPage(ui::NavPage::Settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keyboard shortcut: Ctrl+Left/Right to cycle themes
|
// Keyboard shortcut: Ctrl+Left/Right to cycle themes
|
||||||
if (io.KeyCtrl && !io.WantTextInput) {
|
if (io.KeyCtrl && !io.WantTextInput) {
|
||||||
bool prevTheme = ImGui::IsKeyPressed(ImGuiKey_LeftArrow);
|
bool prevTheme = ImGui::IsKeyPressed(ImGuiKey_LeftArrow, false);
|
||||||
bool nextTheme = ImGui::IsKeyPressed(ImGuiKey_RightArrow);
|
bool nextTheme = ImGui::IsKeyPressed(ImGuiKey_RightArrow, false);
|
||||||
if (prevTheme || nextTheme) {
|
if (prevTheme || nextTheme) {
|
||||||
auto& skinMgr = ui::schema::SkinManager::instance();
|
auto& skinMgr = ui::schema::SkinManager::instance();
|
||||||
const auto& skins = skinMgr.available();
|
const auto& skins = skinMgr.available();
|
||||||
@@ -1721,16 +1721,23 @@ void App::handleGlobalShortcuts()
|
|||||||
for (int i = 0; i < (int)skins.size(); i++) {
|
for (int i = 0; i < (int)skins.size(); i++) {
|
||||||
if (skins[i].id == skinMgr.activeSkinId()) { cur = i; break; }
|
if (skins[i].id == skinMgr.activeSkinId()) { cur = i; break; }
|
||||||
}
|
}
|
||||||
if (prevTheme)
|
const int n = (int)skins.size();
|
||||||
cur = (cur - 1 + (int)skins.size()) % (int)skins.size();
|
const int step = prevTheme ? n - 1 : 1; // -1 (mod n) when cycling back
|
||||||
else
|
int pick = cur;
|
||||||
cur = (cur + 1) % (int)skins.size();
|
bool applied = false;
|
||||||
skinMgr.setActiveSkin(skins[cur].id);
|
for (int k = 0; k < n && !applied; k++) {
|
||||||
if (settings_) {
|
pick = (pick + step) % n;
|
||||||
settings_->setSkinId(skins[cur].id);
|
if (!skins[pick].valid) continue;
|
||||||
settings_->save();
|
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
|
// Keyboard shortcut: Ctrl+Shift+Down to toggle low-spec mode
|
||||||
// (checked BEFORE Ctrl+Down so it doesn't also trigger theme effects)
|
// (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();
|
bool newLow = !ui::effects::isLowSpecMode();
|
||||||
if (settings_) {
|
if (settings_) {
|
||||||
// Persist the mode (and the opaque window low-spec implies) but not the effect prefs:
|
// 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)
|
// 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();
|
bool newState = !ui::effects::ThemeEffects::instance().isEnabled();
|
||||||
ui::effects::ThemeEffects::instance().setEnabled(newState);
|
ui::effects::ThemeEffects::instance().setEnabled(newState);
|
||||||
ui::effects::ThemeEffects::instance().setReducedTransparency(!newState);
|
ui::effects::ThemeEffects::instance().setReducedTransparency(!newState);
|
||||||
@@ -1770,7 +1777,7 @@ void App::handleGlobalShortcuts()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Keyboard shortcut: Ctrl+Up to toggle simple gradient background
|
// 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();
|
bool newGrad = !settings_->getGradientBackground();
|
||||||
settings_->setGradientBackground(newGrad);
|
settings_->setGradientBackground(newGrad);
|
||||||
ui::schema::SkinManager::instance().setGradientMode(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)
|
// Debug: Ctrl+Shift+W to re-show first-run wizard (set to false to disable)
|
||||||
constexpr bool ENABLE_WIZARD_HOTKEY = false;
|
constexpr bool ENABLE_WIZARD_HOTKEY = false;
|
||||||
if constexpr (ENABLE_WIZARD_HOTKEY) {
|
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;
|
wizard_phase_ = WizardPhase::Appearance;
|
||||||
DEBUG_LOGF("[Debug] Wizard re-opened via Ctrl+Shift+W\n");
|
DEBUG_LOGF("[Debug] Wizard re-opened via Ctrl+Shift+W\n");
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -774,7 +774,7 @@ inline bool RenderSidebar(NavPage& current, float sidebarWidth, float contentHei
|
|||||||
do { idx++; } while (idx < (int)NavPage::Count_ && !IsNavPageVisible(static_cast<NavPage>(idx)));
|
do { idx++; } while (idx < (int)NavPage::Count_ && !IsNavPageVisible(static_cast<NavPage>(idx)));
|
||||||
if (idx < (int)NavPage::Count_) nav = true;
|
if (idx < (int)NavPage::Count_) nav = true;
|
||||||
}
|
}
|
||||||
if (ImGui::IsKeyPressed(ImGuiKey_LeftBracket))
|
if (ImGui::IsKeyPressed(ImGuiKey_LeftBracket, false))
|
||||||
collapsed = !collapsed;
|
collapsed = !collapsed;
|
||||||
if (nav) { current = static_cast<NavPage>(idx); changed = true; }
|
if (nav) { current = static_cast<NavPage>(idx); changed = true; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -172,8 +172,8 @@ void RenderBalanceTab(App* app)
|
|||||||
// Left/Right arrows: cycle through enabled balance layouts
|
// Left/Right arrows: cycle through enabled balance layouts
|
||||||
// (skip when Ctrl is held — Ctrl+Arrow cycles themes instead)
|
// (skip when Ctrl is held — Ctrl+Arrow cycles themes instead)
|
||||||
if (app->settings() && !ImGui::GetIO().WantTextInput && !ImGui::GetIO().KeyCtrl) {
|
if (app->settings() && !ImGui::GetIO().WantTextInput && !ImGui::GetIO().KeyCtrl) {
|
||||||
bool cycleUp = ImGui::IsKeyPressed(ImGuiKey_LeftArrow);
|
bool cycleUp = ImGui::IsKeyPressed(ImGuiKey_LeftArrow, false);
|
||||||
bool cycleDown = ImGui::IsKeyPressed(ImGuiKey_RightArrow);
|
bool cycleDown = ImGui::IsKeyPressed(ImGuiKey_RightArrow, false);
|
||||||
if (cycleUp || cycleDown) {
|
if (cycleUp || cycleDown) {
|
||||||
const auto& layouts = GetBalanceLayouts();
|
const auto& layouts = GetBalanceLayouts();
|
||||||
// Build list of enabled layout IDs
|
// Build list of enabled layout IDs
|
||||||
|
|||||||
@@ -2257,8 +2257,8 @@ void RenderMarketTab(App* app)
|
|||||||
// portfolio editor / market-settings modal is open.
|
// portfolio editor / market-settings modal is open.
|
||||||
if (app->settings() && !s_pfEdit.open &&
|
if (app->settings() && !s_pfEdit.open &&
|
||||||
!ImGui::GetIO().WantTextInput && !ImGui::GetIO().KeyCtrl) {
|
!ImGui::GetIO().WantTextInput && !ImGui::GetIO().KeyCtrl) {
|
||||||
bool prev = ImGui::IsKeyPressed(ImGuiKey_LeftArrow);
|
bool prev = ImGui::IsKeyPressed(ImGuiKey_LeftArrow, false);
|
||||||
bool next = ImGui::IsKeyPressed(ImGuiKey_RightArrow);
|
bool next = ImGui::IsKeyPressed(ImGuiKey_RightArrow, false);
|
||||||
if (prev || next) {
|
if (prev || next) {
|
||||||
int st = app->settings()->getPortfolioStyle();
|
int st = app->settings()->getPortfolioStyle();
|
||||||
st = next ? (st + 1) % 3 : (st + 2) % 3;
|
st = next ? (st + 1) % 3 : (st + 2) % 3;
|
||||||
|
|||||||
Reference in New Issue
Block a user