MenuWidget_List.cpp (12412B)
1 /* 2 =========================================================================== 3 4 Doom 3 BFG Edition GPL Source Code 5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 6 7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). 8 9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation, either version 3 of the License, or 12 (at your option) any later version. 13 14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>. 21 22 In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. 23 24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. 25 26 =========================================================================== 27 */ 28 #pragma hdrstop 29 #include "../../idLib/precompiled.h" 30 #include "../Game_local.h" 31 32 /* 33 ================================================================================================ 34 idMenuWidget_List 35 36 Provides a paged view of this widgets children. Each child is expected to take on the following 37 naming scheme. Children outside of the given window size (NumVisibleOptions) are not rendered, 38 and will affect which type of arrow indicators are shown. 39 40 Future work: 41 - Make upIndicator another kind of widget (Image widget?) 42 ================================================================================================ 43 */ 44 45 /* 46 ======================== 47 idMenuWidget_List::Update 48 ======================== 49 */ 50 void idMenuWidget_List::Update() { 51 52 if ( GetSWFObject() == NULL ) { 53 return; 54 } 55 56 idSWFScriptObject & root = GetSWFObject()->GetRootObject(); 57 58 if ( !BindSprite( root ) ) { 59 return; 60 } 61 62 for ( int optionIndex = 0; optionIndex < GetNumVisibleOptions(); ++optionIndex ) { 63 const int childIndex = GetViewOffset() + optionIndex; 64 bool shown = false; 65 66 if ( optionIndex < GetChildren().Num() ) { 67 idMenuWidget & child = GetChildByIndex( optionIndex ); 68 const int controlIndex = GetNumVisibleOptions() - Min( GetNumVisibleOptions(), GetTotalNumberOfOptions() ) + optionIndex; 69 child.SetSpritePath( GetSpritePath(), va( "item%d", controlIndex ) ); 70 if ( child.BindSprite( root ) ) { 71 PrepareListElement( child, childIndex ); 72 child.Update(); 73 shown = true; 74 } 75 } 76 77 if ( !shown ) { 78 // hide the item 79 idSWFSpriteInstance * const sprite = GetSprite()->GetScriptObject()->GetSprite( va( "item%d", optionIndex - GetTotalNumberOfOptions() ) ); 80 if ( sprite != NULL ) { 81 sprite->SetVisible( false ); 82 } 83 } 84 } 85 86 idSWFSpriteInstance * const upSprite = GetSprite()->GetScriptObject()->GetSprite( "upIndicator" ); 87 if ( upSprite != NULL ) { 88 upSprite->SetVisible( GetViewOffset() > 0 ); 89 } 90 91 idSWFSpriteInstance * const downSprite = GetSprite()->GetScriptObject()->GetSprite( "downIndicator" ); 92 if ( downSprite != NULL ) { 93 downSprite->SetVisible( GetViewOffset() + GetNumVisibleOptions() < GetTotalNumberOfOptions() ); 94 } 95 } 96 97 /* 98 ======================== 99 idMenuWidget_List::HandleAction 100 ======================== 101 */ 102 bool idMenuWidget_List::HandleAction( idWidgetAction & action, const idWidgetEvent & event, idMenuWidget * widget, bool forceHandled ) { 103 104 const idSWFParmList & parms = action.GetParms(); 105 106 if ( action.GetType() == WIDGET_ACTION_SCROLL_VERTICAL ) { 107 const scrollType_t scrollType = static_cast< scrollType_t >( event.arg ); 108 if ( scrollType == SCROLL_SINGLE ) { 109 Scroll( parms[ 0 ].ToInteger() ); 110 } else if ( scrollType == SCROLL_PAGE ) { 111 ScrollOffset( parms[ 0 ].ToInteger() * ( GetNumVisibleOptions() - 1 ) ); 112 } else if ( scrollType == SCROLL_FULL ) { 113 ScrollOffset( parms[ 0 ].ToInteger() * 999 ); 114 } 115 return true; 116 } 117 118 return idMenuWidget::HandleAction( action, event, widget, forceHandled ); 119 } 120 121 /* 122 ======================== 123 idMenuWidget_List::ObserveEvent 124 ======================== 125 */ 126 void idMenuWidget_List::ObserveEvent( const idMenuWidget & widget, const idWidgetEvent & event ) { 127 ExecuteEvent( event ); 128 } 129 130 /* 131 ======================== 132 idMenuWidget_List::CalculatePositionFromIndexDelta 133 134 Pure functional encapsulation of how to calculate a new index and offset based on how the user 135 chose to move through the list. 136 ======================== 137 */ 138 void idMenuWidget_List::CalculatePositionFromIndexDelta( int & outIndex, int & outOffset, const int currentIndex, const int currentOffset, const int windowSize, const int maxSize, const int indexDelta, const bool allowWrapping, const bool wrapAround ) const { 139 assert( indexDelta != 0 ); 140 141 int newIndex = currentIndex + indexDelta; 142 bool wrapped = false; 143 144 if ( indexDelta > 0 ) { 145 // moving down the list 146 if ( newIndex > maxSize - 1 ) { 147 if ( allowWrapping ) { 148 if ( wrapAround ) { 149 wrapped = true; 150 newIndex = 0 + ( newIndex - maxSize ); 151 } else { 152 newIndex = 0; 153 } 154 } else { 155 newIndex = maxSize - 1; 156 } 157 } 158 } else { 159 // moving up the list 160 if ( newIndex < 0 ) { 161 if ( allowWrapping ) { 162 if ( wrapAround ) { 163 newIndex = maxSize + newIndex; 164 } else { 165 newIndex = maxSize - 1; 166 } 167 } else { 168 newIndex = 0; 169 } 170 } 171 } 172 173 // calculate the offset 174 if ( newIndex - currentOffset >= windowSize ) { 175 outOffset = newIndex - windowSize + 1; 176 } else if ( currentOffset > newIndex ) { 177 if ( wrapped ) { 178 outOffset = 0; 179 } else { 180 outOffset = newIndex; 181 } 182 } else { 183 outOffset = currentOffset; 184 } 185 186 outIndex = newIndex; 187 188 // the intended behavior is that outOffset and outIndex are always within maxSize of each 189 // other, as they are meant to model a window of items that should be visible in the list. 190 assert( outIndex - outOffset < windowSize ); 191 assert( outIndex >= outOffset && outIndex >= 0 && outOffset >= 0 ); 192 } 193 194 /* 195 ======================== 196 idMenuWidget_List::CalculatePositionFromOffsetDelta 197 ======================== 198 */ 199 void idMenuWidget_List::CalculatePositionFromOffsetDelta( int & outIndex, int & outOffset, const int currentIndex, const int currentOffset, const int windowSize, const int maxSize, const int offsetDelta ) const { 200 // shouldn't be setting both indexDelta AND offsetDelta 201 // FIXME: make this simpler code - just pass a boolean to control it? 202 assert( offsetDelta != 0 ); 203 204 const int newOffset = Max( currentIndex + offsetDelta, 0 ); 205 206 if ( newOffset >= maxSize ) { 207 // scrolling past the end - just scroll all the way to the end 208 outIndex = maxSize - 1; 209 outOffset = Max( maxSize - windowSize, 0 ); 210 } else if ( newOffset >= maxSize - windowSize ) { 211 // scrolled to the last window 212 outIndex = newOffset; 213 outOffset = Max( maxSize - windowSize, 0 ); 214 } else { 215 outIndex = outOffset = newOffset; 216 } 217 218 // the intended behavior is that outOffset and outIndex are always within maxSize of each 219 // other, as they are meant to model a window of items that should be visible in the list. 220 assert( outIndex - outOffset < windowSize ); 221 assert( outIndex >= outOffset && outIndex >= 0 && outOffset >= 0 ); 222 } 223 224 /* 225 ======================== 226 idMenuWidget_List::Scroll 227 ======================== 228 */ 229 void idMenuWidget_List::Scroll( const int scrollAmount, const bool wrapAround ) { 230 231 if ( GetTotalNumberOfOptions() == 0 ) { 232 return; 233 } 234 235 int newIndex, newOffset; 236 237 CalculatePositionFromIndexDelta( newIndex, newOffset, GetViewIndex(), GetViewOffset(), GetNumVisibleOptions(), GetTotalNumberOfOptions(), scrollAmount, IsWrappingAllowed(), wrapAround ); 238 if ( newOffset != GetViewOffset() ) { 239 SetViewOffset( newOffset ); 240 if ( menuData != NULL ) { 241 menuData->PlaySound( GUI_SOUND_FOCUS ); 242 } 243 Update(); 244 } 245 246 if ( newIndex != GetViewIndex() ) { 247 SetViewIndex( newIndex ); 248 SetFocusIndex( newIndex - newOffset ); 249 } 250 } 251 252 /* 253 ======================== 254 idMenuWidget_List::ScrollOffset 255 ======================== 256 */ 257 void idMenuWidget_List::ScrollOffset( const int scrollAmount ) { 258 259 if ( GetTotalNumberOfOptions() == 0 ) { 260 return; 261 } 262 263 int newIndex, newOffset; 264 265 CalculatePositionFromOffsetDelta( newIndex, newOffset, GetViewIndex(), GetViewOffset(), GetNumVisibleOptions(), GetTotalNumberOfOptions(), scrollAmount ); 266 if ( newOffset != GetViewOffset() ) { 267 SetViewOffset( newOffset ); 268 Update(); 269 } 270 271 if ( newIndex != GetViewIndex() ) { 272 SetViewIndex( newIndex ); 273 SetFocusIndex( newIndex - newOffset ); 274 } 275 } 276 277 //********************************************************************************* 278 // GAME BROWSER LIST 279 //******************************************************************************** 280 281 /* 282 ======================== 283 idMenuWidget_GameBrowserList::Update 284 ======================== 285 */ 286 void idMenuWidget_GameBrowserList::Update() { 287 288 if ( GetSWFObject() == NULL ) { 289 return; 290 } 291 292 idSWFScriptObject & root = GetSWFObject()->GetRootObject(); 293 294 if ( !BindSprite( root ) ) { 295 return; 296 } 297 298 for ( int optionIndex = 0; optionIndex < GetNumVisibleOptions(); ++optionIndex ) { 299 const int childIndex = GetViewOffset() + optionIndex; 300 bool shown = false; 301 if ( optionIndex < GetChildren().Num() ) { 302 idMenuWidget & child = GetChildByIndex( optionIndex ); 303 child.SetSpritePath( GetSpritePath(), va( "item%d", optionIndex ) ); 304 if ( child.BindSprite( root ) ) { 305 shown = PrepareListElement( child, childIndex ); 306 if ( shown ) { 307 child.SetState( WIDGET_STATE_NORMAL ); 308 child.GetSprite()->SetVisible( true ); 309 child.Update(); 310 } else { 311 child.GetSprite()->SetVisible( false ); 312 } 313 } 314 } 315 } 316 317 idSWFSpriteInstance * const upSprite = GetSprite()->GetScriptObject()->GetSprite( "upIndicator" ); 318 if ( upSprite != NULL ) { 319 upSprite->SetVisible( GetViewOffset() > 0 ); 320 } 321 322 idSWFSpriteInstance * const downSprite = GetSprite()->GetScriptObject()->GetSprite( "downIndicator" ); 323 if ( downSprite != NULL ) { 324 downSprite->SetVisible( GetViewOffset() + GetNumVisibleOptions() < GetTotalNumberOfOptions() ); 325 } 326 } 327 328 /* 329 ======================== 330 idMenuWidget_GameBrowserList::PrepareListElement 331 ======================== 332 */ 333 bool idMenuWidget_GameBrowserList::PrepareListElement( idMenuWidget & widget, const int childIndex ) { 334 335 if ( childIndex >= games.Num() ) { 336 return false; 337 } 338 339 idMenuWidget_ServerButton * const button = dynamic_cast< idMenuWidget_ServerButton * >( &widget ); 340 if ( button == NULL ) { 341 return false; 342 } 343 344 if ( games[childIndex].serverName.IsEmpty() ) { 345 return false; 346 } 347 348 const idBrowserEntry_t entry = games[childIndex]; 349 350 button->SetButtonInfo( entry.serverName, entry.mapName, entry.modeName, entry.index, entry.players, entry.maxPlayers, entry.joinable, entry.validMap ); 351 352 return true; 353 354 } 355 356 /* 357 ======================== 358 idMenuWidget_GameBrowserList::PrepareListElement 359 ======================== 360 */ 361 void idMenuWidget_GameBrowserList::ClearGames() { 362 games.Clear(); 363 } 364 365 /* 366 ======================== 367 idMenuWidget_GameBrowserList::PrepareListElement 368 ======================== 369 */ 370 void idMenuWidget_GameBrowserList::AddGame( idStr name_, idStrId mapName_, idStr modeName_, int index_, int players_, int maxPlayers_, bool joinable_, bool validMap_ ) { 371 372 idBrowserEntry_t entry; 373 374 entry.serverName = name_; 375 entry.index = index_; 376 entry.players = players_; 377 entry.maxPlayers = maxPlayers_; 378 entry.joinable = joinable_; 379 entry.validMap = validMap_; 380 entry.mapName = mapName_; 381 entry.modeName = modeName_; 382 383 games.Append( entry ); 384 } 385 386 /* 387 ======================== 388 idMenuWidget_GameBrowserList::GetTotalNumberOfOptions 389 ======================== 390 */ 391 int idMenuWidget_GameBrowserList::GetTotalNumberOfOptions() const { 392 return games.Num(); 393 } 394 395 /* 396 ======================== 397 idMenuWidget_GameBrowserList::PrepareListElement 398 ======================== 399 */ 400 int idMenuWidget_GameBrowserList::GetServerIndex() { 401 402 if ( GetViewIndex() < games.Num() ) { 403 return games[ GetViewIndex() ].index; 404 } 405 406 return -1; 407 408 }