mirror of
https://github.com/wiidev/usbloadergx.git
synced 2024-11-18 09:19:17 +01:00
9e79c9d99b
* code cleanup
460 lines
14 KiB
C++
460 lines
14 KiB
C++
/****************************************************************************
|
|
* libwiigui
|
|
*
|
|
* gui_gamecarousel.cpp
|
|
*
|
|
* GUI class definitions
|
|
***************************************************************************/
|
|
|
|
#include "gui.h"
|
|
#include "../wpad.h"
|
|
#include "../menu.h"
|
|
|
|
#include <unistd.h>
|
|
#include "gui_image_async.h"
|
|
#include "gui_gamecarousel.h"
|
|
#include "usbloader/GameList.h"
|
|
#include "../settings/cfg.h"
|
|
#include "../main.h"
|
|
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include <sstream>
|
|
|
|
#define SCALE 0.8f
|
|
#define DEG_OFFSET 7
|
|
#define RADIUS 780
|
|
#define IN_SPEED 175
|
|
#define SHIFT_SPEED 75
|
|
#define SPEED_STEP 4
|
|
#define SPEED_LIMIT 250
|
|
|
|
static inline int OFFSETLIMIT( int Offset, int gameCnt )
|
|
{
|
|
while ( Offset < 0 ) Offset += gameCnt;
|
|
return Offset % gameCnt;
|
|
}
|
|
#define GetGameIndex(pageEntry, listOffset, gameCnt) OFFSETLIMIT(listOffset+pageEntry, gameCnt)
|
|
static GuiImageData *GameCarouselLoadCoverImage( void * Arg )
|
|
{
|
|
return LoadCoverImage( ( struct discHdr * )Arg, true, false );
|
|
}
|
|
/**
|
|
* Constructor for the GuiGameCarousel class.
|
|
*/
|
|
GuiGameCarousel::GuiGameCarousel( int w, int h, const char *themePath, const u8 *imagebg, int selected, int offset ) :
|
|
noCover( nocover_png )
|
|
{
|
|
width = w;
|
|
height = h;
|
|
pagesize = ( gameList.size() < 11 ) ? gameList.size() : 11;
|
|
listOffset = 0;
|
|
selectable = true;
|
|
selectedItem = -1;
|
|
focus = 1; // allow focus
|
|
clickedItem = -1;
|
|
|
|
speed = 0;
|
|
char imgPath[100];
|
|
|
|
trigA = new GuiTrigger;
|
|
trigA->SetSimpleTrigger( -1, WPAD_BUTTON_A | WPAD_CLASSIC_BUTTON_A, PAD_BUTTON_A );
|
|
trigL = new GuiTrigger;
|
|
trigL->SetButtonOnlyTrigger( -1, WPAD_BUTTON_LEFT | WPAD_CLASSIC_BUTTON_LEFT, PAD_BUTTON_LEFT );
|
|
trigR = new GuiTrigger;
|
|
trigR->SetButtonOnlyTrigger( -1, WPAD_BUTTON_RIGHT | WPAD_CLASSIC_BUTTON_RIGHT, PAD_BUTTON_RIGHT );
|
|
trigPlus = new GuiTrigger;
|
|
trigPlus->SetButtonOnlyTrigger( -1, WPAD_BUTTON_PLUS | WPAD_CLASSIC_BUTTON_PLUS, 0 );
|
|
trigMinus = new GuiTrigger;
|
|
trigMinus->SetButtonOnlyTrigger( -1, WPAD_BUTTON_MINUS | WPAD_CLASSIC_BUTTON_MINUS, 0 );
|
|
|
|
btnSoundClick = new GuiSound( button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume );
|
|
btnSoundOver = new GuiSound( button_over_pcm, button_over_pcm_size, Settings.sfxvolume );
|
|
|
|
snprintf( imgPath, sizeof( imgPath ), "%sstartgame_arrow_left.png", CFG.theme_path );
|
|
imgLeft = new GuiImageData( imgPath, startgame_arrow_left_png );
|
|
snprintf( imgPath, sizeof( imgPath ), "%sstartgame_arrow_right.png", CFG.theme_path );
|
|
imgRight = new GuiImageData( imgPath, startgame_arrow_right_png );
|
|
|
|
int btnHeight = ( int ) lround( sqrt( RADIUS * RADIUS - 90000 ) - RADIUS - 50 );
|
|
|
|
btnLeftImg = new GuiImage( imgLeft );
|
|
if ( Settings.wsprompt == yes )
|
|
btnLeftImg->SetWidescreen( CFG.widescreen );
|
|
btnLeft = new GuiButton( imgLeft->GetWidth(), imgLeft->GetHeight() );
|
|
btnLeft->SetAlignment( ALIGN_LEFT, ALIGN_MIDDLE );
|
|
btnLeft->SetPosition( 20, btnHeight );
|
|
btnLeft->SetParent( this );
|
|
btnLeft->SetImage( btnLeftImg );
|
|
btnLeft->SetSoundOver( btnSoundOver );
|
|
btnLeft->SetTrigger( trigA );
|
|
btnLeft->SetTrigger( trigL );
|
|
btnLeft->SetTrigger( trigMinus );
|
|
btnLeft->SetEffectGrow();
|
|
|
|
btnRightImg = new GuiImage( imgRight );
|
|
if ( Settings.wsprompt == yes )
|
|
btnRightImg->SetWidescreen( CFG.widescreen );
|
|
btnRight = new GuiButton( imgRight->GetWidth(), imgRight->GetHeight() );
|
|
btnRight->SetParent( this );
|
|
btnRight->SetAlignment( ALIGN_RIGHT, ALIGN_MIDDLE );
|
|
btnRight->SetPosition( -20, btnHeight );
|
|
btnRight->SetImage( btnRightImg );
|
|
btnRight->SetSoundOver( btnSoundOver );
|
|
btnRight->SetTrigger( trigA );
|
|
btnRight->SetTrigger( trigR );
|
|
btnRight->SetTrigger( trigPlus );
|
|
btnRight->SetEffectGrow();
|
|
|
|
gamename = new GuiText( " ", 18, THEME.info );
|
|
gamename->SetParent( this );
|
|
gamename->SetAlignment( ALIGN_CENTRE, ALIGN_TOP );
|
|
gamename->SetPosition( 0, 330 );
|
|
gamename->SetMaxWidth( 280, DOTTED );
|
|
|
|
gameIndex = new int[pagesize];
|
|
game = new GuiButton * [pagesize];
|
|
coverImg = new GuiImageAsync * [pagesize];
|
|
|
|
for ( int i = 0; i < pagesize; i++ )
|
|
{
|
|
//------------------------
|
|
// Index
|
|
//------------------------
|
|
gameIndex[i] = GetGameIndex( i, listOffset, gameList.size() );
|
|
|
|
//------------------------
|
|
// Image
|
|
//------------------------
|
|
coverImg[i] = new( std::nothrow ) GuiImageAsync( GameCarouselLoadCoverImage, gameList[gameIndex[i]], sizeof( struct discHdr ), &noCover );
|
|
if ( coverImg[i] )
|
|
coverImg[i]->SetWidescreen( CFG.widescreen );
|
|
|
|
//------------------------
|
|
// GameButton
|
|
//------------------------
|
|
|
|
game[i] = new GuiButton( 122, 244 );
|
|
game[i]->SetParent( this );
|
|
game[i]->SetAlignment( ALIGN_CENTRE, ALIGN_MIDDLE );
|
|
game[i]->SetPosition( 0, 740 );
|
|
game[i]->SetImage( coverImg[i] );
|
|
game[i]->SetScale( SCALE );
|
|
game[i]->SetRumble( false );
|
|
game[i]->SetTrigger( trigA );
|
|
game[i]->SetSoundClick( btnSoundClick );
|
|
game[i]->SetClickable( true );
|
|
game[i]->SetEffect( EFFECT_GOROUND, IN_SPEED, 90 - ( pagesize - 2*i - 1 )*DEG_OFFSET / 2, RADIUS, 180, 1, 0, RADIUS );
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Destructor for the GuiGameCarousel class.
|
|
*/
|
|
GuiGameCarousel::~GuiGameCarousel()
|
|
{
|
|
delete imgRight;
|
|
delete imgLeft;
|
|
delete btnLeftImg;
|
|
delete btnRightImg;
|
|
delete btnRight;
|
|
delete btnLeft;
|
|
|
|
delete trigA;
|
|
delete trigL;
|
|
delete trigR;
|
|
delete trigPlus;
|
|
delete trigMinus;
|
|
delete btnSoundClick;
|
|
delete btnSoundOver;
|
|
delete gamename;
|
|
|
|
for ( int i = 0; i < pagesize; i++ )
|
|
{
|
|
delete coverImg[i];
|
|
delete game[i];
|
|
}
|
|
delete [] gameIndex;
|
|
delete [] coverImg;
|
|
delete [] game;
|
|
|
|
|
|
}
|
|
|
|
|
|
void GuiGameCarousel::SetFocus( int f )
|
|
{
|
|
LOCK( this );
|
|
if ( !gameList.size() ) return;
|
|
|
|
focus = f;
|
|
|
|
for ( int i = 0; i < pagesize; i++ )
|
|
game[i]->ResetState();
|
|
|
|
if ( f == 1 && selectedItem >= 0 )
|
|
game[selectedItem]->SetState( STATE_SELECTED );
|
|
}
|
|
|
|
|
|
void GuiGameCarousel::ResetState()
|
|
{
|
|
LOCK( this );
|
|
if ( state != STATE_DISABLED )
|
|
{
|
|
state = STATE_DEFAULT;
|
|
stateChan = -1;
|
|
}
|
|
|
|
for ( int i = 0; i < pagesize; i++ )
|
|
{
|
|
game[i]->ResetState();
|
|
}
|
|
}
|
|
|
|
|
|
int GuiGameCarousel::GetOffset()
|
|
{
|
|
LOCK( this );
|
|
return listOffset;
|
|
}
|
|
|
|
|
|
int GuiGameCarousel::GetClickedOption()
|
|
{
|
|
LOCK( this );
|
|
int found = -1;
|
|
if ( clickedItem >= 0 )
|
|
{
|
|
game[clickedItem]->SetState( STATE_SELECTED );
|
|
found = gameIndex[clickedItem];
|
|
clickedItem = -1;
|
|
}
|
|
return found;
|
|
}
|
|
|
|
|
|
int GuiGameCarousel::GetSelectedOption()
|
|
{
|
|
LOCK( this );
|
|
int found = -1;
|
|
for ( int i = 0; i < pagesize; i++ )
|
|
{
|
|
if ( game[i]->GetState() == STATE_SELECTED )
|
|
{
|
|
game[i]->SetState( STATE_SELECTED );
|
|
found = gameIndex[i];
|
|
break;
|
|
}
|
|
}
|
|
return found;
|
|
}
|
|
|
|
/**
|
|
* Draw the button on screen
|
|
*/
|
|
void GuiGameCarousel::Draw()
|
|
{
|
|
LOCK( this );
|
|
if ( !this->IsVisible() || !gameList.size() )
|
|
return;
|
|
|
|
for ( int i = 0; i < pagesize; i++ )
|
|
game[i]->Draw();
|
|
|
|
|
|
gamename->Draw();
|
|
|
|
if ( gameList.size() > 6 )
|
|
{
|
|
btnRight->Draw();
|
|
btnLeft->Draw();
|
|
}
|
|
|
|
//!Draw tooltip after the Images to have it on top
|
|
if ( focus && Settings.tooltips == TooltipsOn )
|
|
for ( int i = 0; i < pagesize; i++ )
|
|
game[i]->DrawTooltip();
|
|
|
|
this->UpdateEffects();
|
|
}
|
|
|
|
|
|
void GuiGameCarousel::Update( GuiTrigger * t )
|
|
{
|
|
LOCK( this );
|
|
if ( state == STATE_DISABLED || !t || !gameList.size() )
|
|
return;
|
|
|
|
btnRight->Update( t );
|
|
btnLeft->Update( t );
|
|
|
|
if ( game[0]->GetEffect() & EFFECT_GOROUND || game[pagesize-1]->GetEffect() & EFFECT_GOROUND )
|
|
{
|
|
return; // skip when rotate
|
|
}
|
|
|
|
// find selected + clicked
|
|
int selectedItem_old = selectedItem;
|
|
selectedItem = -1;
|
|
clickedItem = -1;
|
|
for ( int i = pagesize - 1; i >= 0; i-- )
|
|
{
|
|
game[i]->Update( t );
|
|
if ( game[i]->GetState() == STATE_SELECTED )
|
|
{
|
|
selectedItem = i;
|
|
}
|
|
if ( game[i]->GetState() == STATE_CLICKED )
|
|
{
|
|
clickedItem = i;
|
|
}
|
|
|
|
}
|
|
|
|
/// OnOver-Effect + GameText + Tooltop
|
|
if ( selectedItem_old != selectedItem )
|
|
{
|
|
if ( selectedItem >= 0 )
|
|
{
|
|
game[selectedItem]->SetEffect( EFFECT_SCALE, 1, 130 );
|
|
char *gameTitle = get_title( gameList[gameIndex[selectedItem]] );
|
|
gamename->SetText( gameTitle );
|
|
}
|
|
else
|
|
gamename->SetText( ( char* )NULL );
|
|
if ( selectedItem_old >= 0 )
|
|
game[selectedItem_old]->SetEffect( EFFECT_SCALE, -1, 100 );
|
|
}
|
|
// navigation
|
|
if ( focus && gameList.size() > 6 )
|
|
{
|
|
|
|
int newspeed = 0;
|
|
// Left/Right Navigation
|
|
if ( btnLeft->GetState() == STATE_CLICKED )
|
|
{
|
|
WPAD_ScanPads();
|
|
u16 buttons = 0;
|
|
for ( int i = 0; i < 4; i++ )
|
|
buttons |= WPAD_ButtonsHeld( i );
|
|
if ( !( ( buttons & WPAD_BUTTON_A ) || ( buttons & WPAD_BUTTON_MINUS ) || t->Left() ) )
|
|
{
|
|
btnLeft->ResetState();
|
|
return;
|
|
}
|
|
|
|
if ( Settings.xflip == sysmenu || Settings.xflip == yes || Settings.xflip == disk3d )
|
|
newspeed = SHIFT_SPEED;
|
|
else
|
|
newspeed = -SHIFT_SPEED;
|
|
}
|
|
else if ( btnRight->GetState() == STATE_CLICKED )
|
|
{
|
|
WPAD_ScanPads();
|
|
u16 buttons = 0;
|
|
for ( int i = 0; i < 4; i++ )
|
|
buttons |= WPAD_ButtonsHeld( i );
|
|
if ( !( ( buttons & WPAD_BUTTON_A ) || ( buttons & WPAD_BUTTON_PLUS ) || t->Right() ) )
|
|
{
|
|
btnRight->ResetState();
|
|
return;
|
|
}
|
|
if ( Settings.xflip == sysmenu || Settings.xflip == yes || Settings.xflip == disk3d )
|
|
newspeed = -SHIFT_SPEED;
|
|
else
|
|
newspeed = SHIFT_SPEED;
|
|
}
|
|
if ( newspeed )
|
|
{
|
|
if ( speed == 0 )
|
|
speed = newspeed;
|
|
else if ( speed > 0 )
|
|
{
|
|
if ( ( speed += SPEED_STEP ) > SPEED_LIMIT )
|
|
speed = SPEED_LIMIT;
|
|
}
|
|
else
|
|
{
|
|
if ( ( speed -= SPEED_STEP ) < -SPEED_LIMIT )
|
|
speed = -SPEED_LIMIT;
|
|
}
|
|
}
|
|
else
|
|
speed = 0;
|
|
|
|
|
|
if ( speed > 0 ) // rotate right
|
|
{
|
|
GuiButton *tmpButton;
|
|
listOffset = OFFSETLIMIT( listOffset - 1, gameList.size() ); // set the new listOffset
|
|
// Save right Button + TollTip and destroy right Image + Image-Data
|
|
delete coverImg[pagesize-1]; coverImg[pagesize-1] = NULL; game[pagesize-1]->SetImage( NULL );
|
|
tmpButton = game[pagesize-1];
|
|
|
|
// Move all Page-Entries one step right
|
|
for ( int i = pagesize - 1; i >= 1; i-- )
|
|
{
|
|
coverImg[i] = coverImg[i-1];
|
|
game[i] = game[i-1];
|
|
gameIndex[i] = gameIndex[i-1];
|
|
}
|
|
// set saved Button & gameIndex to right
|
|
gameIndex[0] = listOffset;
|
|
coverImg[0] = new GuiImageAsync( GameCarouselLoadCoverImage, gameList[gameIndex[0]], sizeof( struct discHdr ), &noCover );
|
|
coverImg[0] ->SetWidescreen( CFG.widescreen );
|
|
|
|
game[0] = tmpButton;
|
|
game[0] ->SetImage( coverImg[0] );
|
|
|
|
|
|
for ( int i = 0; i < pagesize; i++ )
|
|
{
|
|
game[i]->StopEffect();
|
|
game[i]->ResetState();
|
|
game[i]->SetEffect( EFFECT_GOROUND, speed, DEG_OFFSET, RADIUS, 270 - ( pagesize - 2*i + 1 )*DEG_OFFSET / 2, 1, 0, RADIUS );
|
|
game[i]->UpdateEffects(); // rotate one step for liquid scrolling
|
|
}
|
|
}
|
|
else if ( speed < 0 ) // rotate left
|
|
{
|
|
GuiButton *tmpButton;
|
|
listOffset = OFFSETLIMIT( listOffset + 1, gameList.size() ); // set the new listOffset
|
|
// Save left Button + TollTip and destroy left Image + Image-Data
|
|
delete coverImg[0]; coverImg[0] = NULL; game[0]->SetImage( NULL );
|
|
tmpButton = game[0];
|
|
|
|
// Move all Page-Entries one step left
|
|
for ( int i = 0; i < ( pagesize - 1 ); i++ )
|
|
{
|
|
coverImg[i] = coverImg[i+1];
|
|
game[i] = game[i+1];
|
|
gameIndex[i] = gameIndex[i+1];
|
|
}
|
|
// set saved Button & gameIndex to right
|
|
int ii = pagesize - 1;
|
|
gameIndex[ii] = OFFSETLIMIT( listOffset + ii, gameList.size() );
|
|
coverImg[ii] = new GuiImageAsync( GameCarouselLoadCoverImage, gameList[gameIndex[ii]], sizeof( struct discHdr ), &noCover );
|
|
coverImg[ii] ->SetWidescreen( CFG.widescreen );
|
|
|
|
game[ii] = tmpButton;
|
|
game[ii] ->SetImage( coverImg[ii] );
|
|
|
|
|
|
for ( int i = 0; i < pagesize; i++ )
|
|
{
|
|
game[i]->StopEffect();
|
|
game[i]->ResetState();
|
|
game[i]->SetEffect( EFFECT_GOROUND, speed, DEG_OFFSET, RADIUS, 270 - ( pagesize - 2*i - 3 )*DEG_OFFSET / 2, 1, 0, RADIUS );
|
|
game[i]->UpdateEffects(); // rotate one step for liquid scrolling
|
|
}
|
|
}
|
|
|
|
}
|
|
if ( updateCB )
|
|
updateCB( this );
|
|
}
|
|
|
|
|