Skip to content

fix(gui): Stop game time clock from jittering horizontally#2860

Open
bobtista wants to merge 2 commits into
TheSuperHackers:mainfrom
bobtista:bobtista/fix/game-time-clock-jitter
Open

fix(gui): Stop game time clock from jittering horizontally#2860
bobtista wants to merge 2 commits into
TheSuperHackers:mainfrom
bobtista:bobtista/fix/game-time-clock-jitter

Conversation

@bobtista

@bobtista bobtista commented Jul 7, 2026

Copy link
Copy Markdown

Addresses #2859

drawGameTime() positioned the top-right clock by subtracting live-measured text width every frame, right-anchored to the screen edge. With the proportional Tahoma font, digit widths change as the time ticks (and the .NN frame counter changes every frame), so the whole timer shifted a pixel or two each frame.

Reserve a fixed width per field from the widest digit and anchor the timer's left edge to it so the white clock no longer moves as digits change. The frame counter is butted against the timer's live right edge so the two stay adjacent.

TODO:

  • Test
  • Replicate to Generals

@greptile-apps

greptile-apps Bot commented Jul 7, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes the horizontal jitter of the in-game clock by replacing per-frame right-anchored positioning with a stable left-anchored layout computed once when font resources are refreshed. The fix is correctly replicated across both the Generals/ and GeneralsMD/ codebases despite the PR TODO checkbox being left unchecked.

  • refreshGameTimeResources() now measures the widest digit (0–9), the colon, and the dot character using the active font, then stores m_gameTimeReservedWidth (6 × maxDigitWidth + 2 × colonWidth for HH:MM:SS) and m_gameTimeFrameReservedWidth (dotWidth + 2 × maxDigitWidth for .NN). These values are computed once per font change, not every frame.
  • drawGameTime() anchors the timer's left edge to screenWidth − position.x − reservedTotal, eliminating per-frame left-edge variation. The frame counter is butted against the live right edge of the timer text, so it remains visually adjacent while the total block stays within the reserved bounds.

Confidence Score: 5/5

Safe to merge — the change is a targeted, well-scoped fix that only affects the positioning arithmetic in drawGameTime() and the one-time measurement step in refreshGameTimeResources().

The reserved-width calculation correctly accounts for all characters in the HH:MM:SS format (6 digits + 2 colons) and the .NN frame suffix (dot + 2 digits). The total block is bounded to exactly the reserved area so it cannot overflow or go off-screen. Per-frame logic is unchanged except for replacing two live-measured offsets with pre-computed constants, which is strictly safer. Both Generals and GeneralsMD are updated identically.

No files require special attention.

Important Files Changed

Filename Overview
GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp Adds width measurement for digits, colon, and dot in refreshGameTimeResources(), stores reserved widths, and uses them in drawGameTime() to anchor the timer's left edge; frame counter is placed at the live right edge of the timer text.
GeneralsMD/Code/GameEngine/Include/GameClient/InGameUI.h Adds m_gameTimeReservedWidth and m_gameTimeFrameReservedWidth Int member fields with consistent column alignment matching surrounding code.
Generals/Code/GameEngine/Source/GameClient/InGameUI.cpp Identical fix replicated from GeneralsMD; same logic for measuring reserved widths and anchoring the timer's left edge in drawGameTime().
Generals/Code/GameEngine/Include/GameClient/InGameUI.h Adds the same two reserved-width member fields as the GeneralsMD header, maintaining existing alignment style.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["refreshGameTimeResources()\ncalled on font change"] --> B["Measure widest digit width\n(iterate '0'–'9' via getWidth)"]
    B --> C["Measure colon width\nand dot width"]
    C --> D["Store m_gameTimeReservedWidth\n= 6×maxDigitWidth + 2×colonWidth"]
    C --> E["Store m_gameTimeFrameReservedWidth\n= dotWidth + 2×maxDigitWidth"]
    D --> F["drawGameTime()\ncalled every frame"]
    E --> F
    F --> G["Set timer text: HH:MM:SS\nSet frame text: .NN"]
    G --> H["Compute horizontalTimerOffset\n= screenW − posX − reservedTotal\n(STABLE — no live measurement)"]
    H --> I["Compute horizontalFrameOffset\n= timerOffset + liveTimerWidth\n(frame butts against timer text)"]
    I --> J["Draw timer at timerOffset\nDraw frame at frameOffset"]
Loading

Reviews (2): Last reviewed commit: "fix(gui): Stop game time clock from jitt..." | Re-trigger Greptile

@bobtista bobtista added Bug Something is not working right, typically is user facing Minor Severity: Minor < Major < Critical < Blocker Platform Work towards platform support, such as Linux, MacOS labels Jul 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug Something is not working right, typically is user facing Minor Severity: Minor < Major < Critical < Blocker Platform Work towards platform support, such as Linux, MacOS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant