Skip to content

feat(gui): Add configurable menu transition speed#2840

Open
bobtista wants to merge 5 commits into
TheSuperHackers:mainfrom
bobtista:bobtista/feat/configurable-menu-transition-speed
Open

feat(gui): Add configurable menu transition speed#2840
bobtista wants to merge 5 commits into
TheSuperHackers:mainfrom
bobtista:bobtista/feat/configurable-menu-transition-speed

Conversation

@bobtista

@bobtista bobtista commented Jun 28, 2026

Copy link
Copy Markdown

Closes #2839

Adds a per-user MenuTransitionSpeed option (in Options.ini) that scales the speed of menu/window transition animations, as requested following #2056. Defaults to 1.0 (unchanged behavior); values range from 0.1 (10× slower) to 10.0 (10× faster).

The multiplier is applied to the existing frame-rate-decoupled time step from #2056, so transitions still step through every state and never skip.

Todo

  • testing
  • replicate to Generals

@greptile-apps

greptile-apps Bot commented Jun 28, 2026

Copy link
Copy Markdown

Greptile Summary

Adds a per-user MenuTransitionSpeed option in Options.ini that scales menu/window transition animation speed via a m_gameWindowTransitionSpeed multiplier stored in GlobalData and applied to the existing frame-rate-decoupled time step in TransitionGroup::update(). The change is replicated symmetrically across both Generals and Zero Hour builds.

  • OptionPreferences::getGameWindowTransitionSpeed() reads the INI key, falls back to 1.0f, and rejects non-positive values — but the enforced range (0, 1000] is wider than the [0.1, 10.0] range documented in the PR description.
  • GlobalData in both Generals/ and GeneralsMD/ receives the new field, initialized to 1.0f and populated from OptionPreferences inside parseGameDataDefinition.
  • GameWindowTransitions.cpp gains a GlobalData.h include and multiplies timeScale by the new field; the existing loop already handles large time steps safely via an isFinished early-exit.

Confidence Score: 5/5

Safe to merge; the change is additive, defaults to existing behavior, and the transition loop already handles large time-step values gracefully.

The multiplier is applied to a path already guarded by an isFinished early-exit, so even extreme INI values cannot produce an infinite loop or skip animation states. The default of 1.0f preserves unchanged behavior for all existing users.

Core/GameEngine/Source/Common/OptionPreferences.cpp — the clamping bounds should be reconciled with the documented range.

Important Files Changed

Filename Overview
Core/GameEngine/Include/Common/OptionPreferences.h Adds getGameWindowTransitionSpeed() declaration; clean, consistent with existing getter signatures
Core/GameEngine/Source/Common/OptionPreferences.cpp Implements getGameWindowTransitionSpeed(); enforced range (>0, ≤1000) doesn't match documented range (0.1–10.0) from PR description
Core/GameEngine/Source/GameClient/GUI/GameWindowTransitions.cpp Multiplies timeScale by TheGlobalData->m_gameWindowTransitionSpeed; correctly placed alongside existing TheFramePacer dereference pattern
Generals/Code/GameEngine/Include/Common/GlobalData.h Adds m_gameWindowTransitionSpeed field; annotation, placement, and type match surrounding members
Generals/Code/GameEngine/Source/Common/GlobalData.cpp Initializes m_gameWindowTransitionSpeed = 1.0f in constructor and reads it from OptionPreferences in parseGameDataDefinition; mirrors the pattern of adjacent fields
GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h Mirrors the Generals GlobalData.h change; field declaration and annotation are consistent
GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp Mirrors the Generals GlobalData.cpp change; initialization and preference-loading are consistent

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant INI as Options.ini
    participant OP as OptionPreferences
    participant GD as GlobalData
    participant TG as TransitionGroup::update()
    participant FP as FramePacer

    INI->>OP: GameWindowTransitionSpeed value
    OP->>OP: "getGameWindowTransitionSpeed() clamp >0 <= 1000 default 1.0"
    OP->>GD: "parseGameDataDefinition() m_gameWindowTransitionSpeed = speed"
    Note over GD: initialized to 1.0f in ctor

    FP->>TG: getBaseOverUpdateFpsRatio()
    GD->>TG: m_gameWindowTransitionSpeed
    TG->>TG: "timeScale = ratio x speed"
    TG->>TG: step frames prevFrame to newFrame break on isFinished
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant INI as Options.ini
    participant OP as OptionPreferences
    participant GD as GlobalData
    participant TG as TransitionGroup::update()
    participant FP as FramePacer

    INI->>OP: GameWindowTransitionSpeed value
    OP->>OP: "getGameWindowTransitionSpeed() clamp >0 <= 1000 default 1.0"
    OP->>GD: "parseGameDataDefinition() m_gameWindowTransitionSpeed = speed"
    Note over GD: initialized to 1.0f in ctor

    FP->>TG: getBaseOverUpdateFpsRatio()
    GD->>TG: m_gameWindowTransitionSpeed
    TG->>TG: "timeScale = ratio x speed"
    TG->>TG: step frames prevFrame to newFrame break on isFinished
Loading

Reviews (3): Last reviewed commit: "refactor(gui): Replicate game window tra..." | Re-trigger Greptile

@bobtista bobtista self-assigned this Jun 28, 2026

@Caball009 Caball009 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you lower this Sleep? As far as I'm concerned it can be set to 1ms.

return 1.0f;

Real speed = (Real) atof(it->second.str());
return clamp(0.1f, speed, 10.0f);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really see the need for lower than 1.0 (certainly not 0.1), but would appreciate the option for up to 30.0.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it even need a ceiling? Let the user put 999999 if he wants it instant?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm happy with something like 1 - 1000. Literally no ceiling a user could theoretically cause an int overflow. At 1000 it would be practically instant.

// transitions cannot skip a state when the render frame rate dips below the base rate.
const Real timeScale = TheFramePacer->getBaseOverUpdateFpsRatio();
// TheSuperHackers @feature bobtista 28/06/2026 Scale by the user menu transition speed preference.
const Real timeScale = TheFramePacer->getBaseOverUpdateFpsRatio() * TheGlobalData->m_menuTransitionSpeed;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe also apply this to the movement speed of Control Bar, Diplomacy Screen? Or does that need to be a separate setting?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those don't go through TransitionGroup they use the sliding/animate-window path. Could make another PR for those - would you want it to be a separate setting or controlled by this one?


Bool getShowMoneyPerMinute() const;

Real getMenuTransitionSpeed() const;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe GameWindowTransitionSpeed if this applies to all GUI, not just inside the menu? (Quit Screen is not Menu, but Ingame)

@bobtista

Copy link
Copy Markdown
Author

Can you lower this Sleep? As far as I'm concerned it can be set to 1ms.

It's iteration-bound (frame-pacer frozen in that loop), so lowering Sleep speeds the fade up rather than smoothing it, is that what you want? It would be like 30x faster right? Maybe the proper change is to route the loop through the frame pacer, which would presumably be in its own PR.

@Caball009

Caball009 commented Jun 28, 2026

Copy link
Copy Markdown

It's iteration-bound (frame-pacer frozen in that loop), so lowering Sleep speeds the fade up rather than smoothing it, is that what you want? It would be like 30x faster right?

Right, perhaps 1000.0f / (30 * TheWritableGlobalData->m_menuTransitionSpeed) as duration is better. Having a a hardcoded 33 msec delay makes no sense anymore now that the transition speed is variable.

Maybe the proper change is to route the loop through the frame pacer, which would presumably be in its own PR.

I don't think that's necessary.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make menu transition animation speed configurable

3 participants