Skip to content

Commit 9d94036

Browse files
author
Ibra
committed
bugfix: fix listbox row animation to be frame rate independent and fix animation reliability
1 parent 6266009 commit 9d94036

3 files changed

Lines changed: 70 additions & 104 deletions

File tree

Core/GameEngine/Source/GameNetwork/GameSpy/LobbyUtils.cpp

Lines changed: 0 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
#include "GameClient/GameWindowManager.h"
4545
#include "GameClient/GadgetComboBox.h"
4646
#include "GameClient/GadgetListBox.h"
47-
#include "GameClient/GadgetTextEntry.h"
4847
#include "GameClient/GameText.h"
4948
#include "GameClient/MapUtil.h"
5049
#include "GameClient/MessageBox.h"
@@ -95,38 +94,6 @@ enum {
9594
};
9695
#endif
9796

98-
// ---------------------------------------------------------------------------
99-
// row entry animation
100-
// ---------------------------------------------------------------------------
101-
102-
static std::map<int, GameRowAnim> g_gameListAnim;
103-
104-
// initialize the smoothed index for this lobbyID
105-
static float UpdateAndGetGameRowIndex(int lobbyID, float logicalIndex)
106-
{
107-
GameRowAnim& anim = g_gameListAnim[lobbyID];
108-
109-
if (!anim.alive)
110-
{
111-
// start slightly below, then glide into place
112-
anim.currentIndex = logicalIndex + 2.f;
113-
anim.targetIndex = logicalIndex;
114-
anim.alive = true;
115-
}
116-
else
117-
{
118-
anim.targetIndex = logicalIndex;
119-
}
120-
121-
// how fast to snap into place
122-
const float t = 0.2f;
123-
124-
float delta = anim.targetIndex - anim.currentIndex;
125-
anim.currentIndex += delta * t;
126-
127-
return anim.currentIndex;
128-
}
129-
13097
static NameKeyType buttonSortAlphaID = NAMEKEY_INVALID;
13198
static NameKeyType buttonSortPingID = NAMEKEY_INVALID;
13299
static NameKeyType buttonSortBuddiesID = NAMEKEY_INVALID;
@@ -1251,13 +1218,6 @@ void RefreshGameListBox(GameWindow* win, Bool showMap)
12511218
for (SortedGameList::iterator sglIt = sgl.begin(); sglIt != sgl.end(); ++sglIt)
12521219
{
12531220
LobbyEntry lobby = *sglIt;
1254-
1255-
// Logical index for this entry in the new sorted list
1256-
float logicalIndex = (float)i;
1257-
1258-
// register/update animation index for this lobbyID
1259-
UpdateAndGetGameRowIndex(lobby.lobbyID, logicalIndex);
1260-
12611221
Int index = insertGame(win, lobby, showMap);
12621222
if (lobby.lobbyID == selectedID)
12631223
{
@@ -1396,45 +1356,6 @@ void RefreshGameInfoListBox( GameWindow *mainWin, GameWindow *win )
13961356

13971357
}
13981358

1399-
// ---------------------------------------------------------------------------
1400-
// compute pixel offset for a game list row
1401-
// ---------------------------------------------------------------------------
1402-
int GetGameListRowPixelOffsetForRow(GameWindow* window, int rowIndex, int rowHeight)
1403-
{
1404-
if (!window)
1405-
return 0;
1406-
1407-
// We rely on listbox item data storing lobbyID, like RefreshGameListBox uses
1408-
Int lobbyID = (Int)GadgetListBoxGetItemData(window, rowIndex);
1409-
if (lobbyID == 0)
1410-
return 0;
1411-
1412-
GameWindow* mainGameList = GetGameListBox();
1413-
1414-
int animKey;
1415-
if (window == mainGameList)
1416-
{
1417-
// For the main game list: use lobbyID
1418-
animKey = lobbyID;
1419-
}
1420-
else
1421-
{
1422-
// For all other lists: keep per row key to avoid overlap
1423-
animKey = lobbyID * 1024 + rowIndex;
1424-
}
1425-
1426-
1427-
// Get smoothed "visual index" for this lobbyID
1428-
float visualIndex = UpdateAndGetGameRowIndex(animKey, (float)rowIndex);
1429-
1430-
// Offset = (visualIndex - logicalIndex) * rowHeight
1431-
float offsetRows = visualIndex - (float)rowIndex;
1432-
int pixelOffset = (int)(offsetRows * (float)rowHeight);
1433-
1434-
return pixelOffset;
1435-
}
1436-
1437-
14381359
void RefreshGameListBoxes( void )
14391360
{
14401361
GameWindow *main = GetGameListBox();

GeneralsMD/Code/GameEngine/Include/GameClient/GadgetTextEntry.h

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -123,16 +123,3 @@ inline Color GadgetTextEntryGetHiliteBorderColor( GameWindow *g ) { return g
123123

124124
// EXTERNALS //////////////////////////////////////////////////////////////////
125125

126-
struct GameRowAnim
127-
{
128-
float currentIndex;
129-
float targetIndex;
130-
bool alive;
131-
132-
GameRowAnim()
133-
: currentIndex(0.0f)
134-
, targetIndex(0.0f)
135-
, alive(false)
136-
{
137-
}
138-
};

GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/GUI/Gadget/W3DListBox.cpp

Lines changed: 70 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,6 @@
5656

5757
// DEFINES ////////////////////////////////////////////////////////////////////
5858

59-
int GetGameListRowPixelOffsetForRow(GameWindow* window, int rowIndex, int rowHeight);
60-
6159
// PRIVATE TYPES //////////////////////////////////////////////////////////////
6260

6361
// PRIVATE DATA ///////////////////////////////////////////////////////////////
@@ -70,6 +68,73 @@ int GetGameListRowPixelOffsetForRow(GameWindow* window, int rowIndex, int rowHei
7068
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////
7169
///////////////////////////////////////////////////////////////////////////////
7270

71+
// ApplyListBoxRowAnimation====================================================
72+
// applies row entry animation offset to drawY.
73+
// rows slide in from below their target position and settle into place
74+
//=============================================================================
75+
struct RowAnimState
76+
{
77+
Real currentIndex; // where the row is drawn right now, catches up to targetIndex over time
78+
Real targetIndex; // where the row actually belongs in the list
79+
UnsignedInt lastUpdateTime; // last time this row was drawn, used to detect when a listbox reopens
80+
void *lastItemData; // last item data seen in this slot, used to detect when the item changes
81+
82+
RowAnimState()
83+
{
84+
currentIndex = 0.f;
85+
targetIndex = 0.f;
86+
lastUpdateTime = 0;
87+
lastItemData = nullptr;
88+
}
89+
};
90+
91+
static std::map<Int, RowAnimState> theRowAnimState;
92+
static void ApplyListBoxRowAnimation(GameWindow *window, Int rowIndex, Int rowHeight, Int &drawY)
93+
{
94+
if (!window)
95+
return;
96+
97+
// shift window ID to avoid collisions with rowIndex
98+
Int rowAnimKey = (window->winGetWindowId() << 16) + rowIndex;
99+
RowAnimState &anim = theRowAnimState[rowAnimKey];
100+
UnsignedInt now = timeGetTime();
101+
Real deltaTime = 0.f;
102+
103+
if (anim.lastUpdateTime != 0)
104+
{
105+
UnsignedInt elapsedMs = now - anim.lastUpdateTime;
106+
107+
// if this row hasn't been drawn for over 60ms, the listbox was closed, reset its state so it re-animates on the next open
108+
if (elapsedMs > 60)
109+
{
110+
anim = RowAnimState();
111+
}
112+
else
113+
{
114+
deltaTime = elapsedMs / (Real)MSEC_PER_SECOND;
115+
}
116+
}
117+
118+
anim.lastUpdateTime = now;
119+
void *itemData = GadgetListBoxGetItemData(window, rowIndex);
120+
Real rowIndexF = (Real)rowIndex;
121+
122+
// trigger animation when first seen, when the row moves, or when the item in this slot changes
123+
if (anim.currentIndex < 0.f || anim.targetIndex != rowIndexF || itemData != anim.lastItemData)
124+
{
125+
const Real rowAnimStartOffset = 1.f;
126+
anim.currentIndex = rowIndexF + rowAnimStartOffset;
127+
}
128+
129+
anim.lastItemData = itemData;
130+
anim.targetIndex = rowIndexF;
131+
132+
const Real snapSpeed = 20.f;
133+
anim.currentIndex += (anim.targetIndex - anim.currentIndex) * snapSpeed * deltaTime;
134+
135+
drawY += (Int)((anim.currentIndex - rowIndexF) * (Real)rowHeight);
136+
}
137+
73138
// drawHiliteBar ==============================================================
74139
/** Draw image for the hilite bar */
75140
//=============================================================================
@@ -197,13 +262,8 @@ static void drawListBoxText( GameWindow *window, WinInstanceData *instData,
197262
IRegion2D clipRegion;
198263
ICoord2D start, end;
199264

200-
Color debugBg = TheWindowManager->winMakeColor(3, 93, 166, 20);
201-
TheWindowManager->winFillRect(
202-
debugBg,
203-
WIN_DRAW_LINE_WIDTH,
204-
x, y,
205-
x + width, y + height
206-
);
265+
Color WindowBg = TheWindowManager->winMakeColor(3, 93, 166, 20);
266+
TheWindowManager->winFillRect(WindowBg, WIN_DRAW_LINE_WIDTH, x, y, x + width, y + height);
207267

208268
//
209269
// save the clipping information region cause we're going to use it here
@@ -244,8 +304,7 @@ static void drawListBoxText( GameWindow *window, WinInstanceData *instData,
244304
selected = FALSE;
245305

246306
int rowDrawY = drawY;
247-
248-
rowDrawY += GetGameListRowPixelOffsetForRow(window, i, listLineHeight);
307+
ApplyListBoxRowAnimation(window, i, listLineHeight, rowDrawY);
249308

250309
if (list->multiSelect)
251310
{
@@ -682,4 +741,3 @@ void W3DGadgetListBoxImageDraw( GameWindow *window, WinInstanceData *instData )
682741

683742

684743
}
685-

0 commit comments

Comments
 (0)