ddnet/src/game/client/components/menus_demo.cpp

727 lines
25 KiB
C++

/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#include <base/tl/string.h>
#include <base/math.h>
#include <engine/demo.h>
#include <engine/keys.h>
#include <engine/graphics.h>
#include <engine/textrender.h>
#include <engine/storage.h>
#include <game/client/render.h>
#include <game/client/gameclient.h>
#include <game/localization.h>
#include <game/client/ui.h>
#include <game/generated/client_data.h>
#include "maplayers.h"
#include "menus.h"
int CMenus::DoButton_DemoPlayer(const void *pID, const char *pText, int Checked, const CUIRect *pRect)
{
RenderTools()->DrawUIRect(pRect, vec4(1,1,1, Checked ? 0.10f : 0.5f)*ButtonColorMul(pID), CUI::CORNER_ALL, 5.0f);
UI()->DoLabel(pRect, pText, 14.0f, 0);
return UI()->DoButtonLogic(pID, pText, Checked, pRect);
}
int CMenus::DoButton_Sprite(const void *pID, int ImageID, int SpriteID, int Checked, const CUIRect *pRect, int Corners)
{
RenderTools()->DrawUIRect(pRect, Checked ? vec4(1.0f, 1.0f, 1.0f, 0.10f) : vec4(1.0f, 1.0f, 1.0f, 0.5f)*ButtonColorMul(pID), Corners, 5.0f);
Graphics()->TextureSet(g_pData->m_aImages[ImageID].m_Id);
Graphics()->QuadsBegin();
if(!Checked)
Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.5f);
RenderTools()->SelectSprite(SpriteID);
IGraphics::CQuadItem QuadItem(pRect->x, pRect->y, pRect->w, pRect->h);
Graphics()->QuadsDrawTL(&QuadItem, 1);
Graphics()->QuadsEnd();
return UI()->DoButtonLogic(pID, "", Checked, pRect);
}
void CMenus::RenderDemoPlayer(CUIRect MainView)
{
const IDemoPlayer::CInfo *pInfo = DemoPlayer()->BaseInfo();
const float SeekBarHeight = 15.0f;
const float ButtonbarHeight = 20.0f;
const float NameBarHeight = 20.0f;
const float Margins = 5.0f;
float TotalHeight;
if(!m_MenuActive)
return;
TotalHeight = SeekBarHeight+ButtonbarHeight+NameBarHeight+Margins*3;
MainView.HSplitBottom(TotalHeight, 0, &MainView);
MainView.VSplitLeft(50.0f, 0, &MainView);
MainView.VSplitRight(450.0f, &MainView, 0);
RenderTools()->DrawUIRect(&MainView, ms_ColorTabbarActive, CUI::CORNER_T, 10.0f);
MainView.Margin(5.0f, &MainView);
CUIRect SeekBar, ButtonBar, NameBar;
int CurrentTick = pInfo->m_CurrentTick - pInfo->m_FirstTick;
int TotalTicks = pInfo->m_LastTick - pInfo->m_FirstTick;
MainView.HSplitTop(SeekBarHeight, &SeekBar, &ButtonBar);
ButtonBar.HSplitTop(Margins, 0, &ButtonBar);
ButtonBar.HSplitBottom(NameBarHeight, &ButtonBar, &NameBar);
NameBar.HSplitTop(4.0f, 0, &NameBar);
// do seekbar
{
static int s_SeekBarID = 0;
void *id = &s_SeekBarID;
char aBuffer[128];
// draw seek bar
RenderTools()->DrawUIRect(&SeekBar, vec4(0,0,0,0.5f), CUI::CORNER_ALL, 5.0f);
// draw filled bar
float Amount = CurrentTick/(float)TotalTicks;
CUIRect FilledBar = SeekBar;
FilledBar.w = 10.0f + (FilledBar.w-10.0f)*Amount;
RenderTools()->DrawUIRect(&FilledBar, vec4(1,1,1,0.5f), CUI::CORNER_ALL, 5.0f);
// draw markers
for(int i = 0; i < pInfo->m_NumTimelineMarkers; i++)
{
float Ratio = (pInfo->m_aTimelineMarkers[i]-pInfo->m_FirstTick) / (float)TotalTicks;
Graphics()->TextureSet(-1);
Graphics()->QuadsBegin();
Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f);
IGraphics::CQuadItem QuadItem(SeekBar.x + (SeekBar.w-10.0f)*Ratio, SeekBar.y, UI()->PixelSize(), SeekBar.h);
Graphics()->QuadsDrawTL(&QuadItem, 1);
Graphics()->QuadsEnd();
}
// draw time
str_format(aBuffer, sizeof(aBuffer), "%d:%02d / %d:%02d",
CurrentTick/SERVER_TICK_SPEED/60, (CurrentTick/SERVER_TICK_SPEED)%60,
TotalTicks/SERVER_TICK_SPEED/60, (TotalTicks/SERVER_TICK_SPEED)%60);
UI()->DoLabel(&SeekBar, aBuffer, SeekBar.h*0.70f, 0);
// do the logic
int Inside = UI()->MouseInside(&SeekBar);
if(UI()->ActiveItem() == id)
{
if(!UI()->MouseButton(0))
UI()->SetActiveItem(0);
else
{
static float PrevAmount = 0.0f;
float Amount = (UI()->MouseX()-SeekBar.x)/(float)SeekBar.w;
if(Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT))
{
Amount = PrevAmount + (Amount-PrevAmount) * 0.05f;
if(Amount > 0.0f && Amount < 1.0f && absolute(PrevAmount-Amount) >= 0.0001f)
{
//PrevAmount = Amount;
m_pClient->OnReset();
m_pClient->m_SuppressEvents = true;
DemoPlayer()->SetPos(Amount);
m_pClient->m_SuppressEvents = false;
m_pClient->m_pMapLayersBackGround->EnvelopeUpdate();
m_pClient->m_pMapLayersForeGround->EnvelopeUpdate();
}
}
else
{
if(Amount > 0.0f && Amount < 1.0f && absolute(PrevAmount-Amount) >= 0.001f)
{
PrevAmount = Amount;
m_pClient->OnReset();
m_pClient->m_SuppressEvents = true;
DemoPlayer()->SetPos(Amount);
m_pClient->m_SuppressEvents = false;
m_pClient->m_pMapLayersBackGround->EnvelopeUpdate();
m_pClient->m_pMapLayersForeGround->EnvelopeUpdate();
}
}
}
}
else if(UI()->HotItem() == id)
{
if(UI()->MouseButton(0))
UI()->SetActiveItem(id);
}
if(Inside)
UI()->SetHotItem(id);
}
if(CurrentTick == TotalTicks)
{
m_pClient->OnReset();
DemoPlayer()->Pause();
DemoPlayer()->SetPos(0);
}
bool IncreaseDemoSpeed = false, DecreaseDemoSpeed = false;
// do buttons
CUIRect Button;
// combined play and pause button
ButtonBar.VSplitLeft(ButtonbarHeight, &Button, &ButtonBar);
static int s_PlayPauseButton = 0;
if(!pInfo->m_Paused)
{
if(DoButton_Sprite(&s_PlayPauseButton, IMAGE_DEMOBUTTONS, SPRITE_DEMOBUTTON_PAUSE, false, &Button, CUI::CORNER_ALL))
DemoPlayer()->Pause();
}
else
{
if(DoButton_Sprite(&s_PlayPauseButton, IMAGE_DEMOBUTTONS, SPRITE_DEMOBUTTON_PLAY, false, &Button, CUI::CORNER_ALL))
DemoPlayer()->Unpause();
}
// stop button
ButtonBar.VSplitLeft(Margins, 0, &ButtonBar);
ButtonBar.VSplitLeft(ButtonbarHeight, &Button, &ButtonBar);
static int s_ResetButton = 0;
if(DoButton_Sprite(&s_ResetButton, IMAGE_DEMOBUTTONS, SPRITE_DEMOBUTTON_STOP, false, &Button, CUI::CORNER_ALL))
{
m_pClient->OnReset();
DemoPlayer()->Pause();
DemoPlayer()->SetPos(0);
}
// slowdown
ButtonBar.VSplitLeft(Margins, 0, &ButtonBar);
ButtonBar.VSplitLeft(ButtonbarHeight, &Button, &ButtonBar);
static int s_SlowDownButton = 0;
if(DoButton_Sprite(&s_SlowDownButton, IMAGE_DEMOBUTTONS, SPRITE_DEMOBUTTON_SLOWER, 0, &Button, CUI::CORNER_ALL) || Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN))
DecreaseDemoSpeed = true;
// fastforward
ButtonBar.VSplitLeft(Margins, 0, &ButtonBar);
ButtonBar.VSplitLeft(ButtonbarHeight, &Button, &ButtonBar);
static int s_FastForwardButton = 0;
if(DoButton_Sprite(&s_FastForwardButton, IMAGE_DEMOBUTTONS, SPRITE_DEMOBUTTON_FASTER, 0, &Button, CUI::CORNER_ALL))
IncreaseDemoSpeed = true;
// speed meter
ButtonBar.VSplitLeft(Margins*3, 0, &ButtonBar);
char aBuffer[64];
if(pInfo->m_Speed >= 1.0f)
str_format(aBuffer, sizeof(aBuffer), "x%.0f", pInfo->m_Speed);
else
str_format(aBuffer, sizeof(aBuffer), "x%.2f", pInfo->m_Speed);
UI()->DoLabel(&ButtonBar, aBuffer, Button.h*0.7f, -1);
// close button
ButtonBar.VSplitRight(ButtonbarHeight*3, &ButtonBar, &Button);
static int s_ExitButton = 0;
if(DoButton_DemoPlayer(&s_ExitButton, Localize("Close"), 0, &Button))
Client()->Disconnect();
// demo name
char aDemoName[64] = {0};
DemoPlayer()->GetDemoName(aDemoName, sizeof(aDemoName));
char aBuf[128];
str_format(aBuf, sizeof(aBuf), Localize("Demofile: %s"), aDemoName);
CTextCursor Cursor;
TextRender()->SetCursor(&Cursor, NameBar.x, NameBar.y, Button.h*0.5f, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END);
Cursor.m_LineWidth = MainView.w;
TextRender()->TextEx(&Cursor, aBuf, -1);
if(IncreaseDemoSpeed || Input()->KeyPresses(KEY_MOUSE_WHEEL_UP))
{
if(pInfo->m_Speed < 0.1f) DemoPlayer()->SetSpeed(0.1f);
else if(pInfo->m_Speed < 0.25f) DemoPlayer()->SetSpeed(0.25f);
else if(pInfo->m_Speed < 0.5f) DemoPlayer()->SetSpeed(0.5f);
else if(pInfo->m_Speed < 0.75f) DemoPlayer()->SetSpeed(0.75f);
else if(pInfo->m_Speed < 1.0f) DemoPlayer()->SetSpeed(1.0f);
else if(pInfo->m_Speed < 2.0f) DemoPlayer()->SetSpeed(2.0f);
else if(pInfo->m_Speed < 4.0f) DemoPlayer()->SetSpeed(4.0f);
else DemoPlayer()->SetSpeed(8.0f);
}
else if(DecreaseDemoSpeed || Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN))
{
if(pInfo->m_Speed > 4.0f) DemoPlayer()->SetSpeed(4.0f);
else if(pInfo->m_Speed > 2.0f) DemoPlayer()->SetSpeed(2.0f);
else if(pInfo->m_Speed > 1.0f) DemoPlayer()->SetSpeed(1.0f);
else if(pInfo->m_Speed > 0.75f) DemoPlayer()->SetSpeed(0.75f);
else if(pInfo->m_Speed > 0.5f) DemoPlayer()->SetSpeed(0.5f);
else if(pInfo->m_Speed > 0.25f) DemoPlayer()->SetSpeed(0.25f);
else if(pInfo->m_Speed > 0.1f) DemoPlayer()->SetSpeed(0.1f);
else DemoPlayer()->SetSpeed(0.05f);
}
}
static CUIRect gs_ListBoxOriginalView;
static CUIRect gs_ListBoxView;
static float gs_ListBoxRowHeight;
static int gs_ListBoxItemIndex;
static int gs_ListBoxSelectedIndex;
static int gs_ListBoxNewSelected;
static int gs_ListBoxDoneEvents;
static int gs_ListBoxNumItems;
static int gs_ListBoxItemsPerRow;
static float gs_ListBoxScrollValue;
static bool gs_ListBoxItemActivated;
void CMenus::UiDoListboxStart(const void *pID, const CUIRect *pRect, float RowHeight, const char *pTitle, const char *pBottomText, int NumItems,
int ItemsPerRow, int SelectedIndex, float ScrollValue)
{
CUIRect Scroll, Row;
CUIRect View = *pRect;
CUIRect Header, Footer;
// draw header
View.HSplitTop(ms_ListheaderHeight, &Header, &View);
RenderTools()->DrawUIRect(&Header, vec4(1,1,1,0.25f), CUI::CORNER_T, 5.0f);
UI()->DoLabel(&Header, pTitle, Header.h*ms_FontmodHeight, 0);
// draw footers
View.HSplitBottom(ms_ListheaderHeight, &View, &Footer);
RenderTools()->DrawUIRect(&Footer, vec4(1,1,1,0.25f), CUI::CORNER_B, 5.0f);
Footer.VSplitLeft(10.0f, 0, &Footer);
UI()->DoLabel(&Footer, pBottomText, Header.h*ms_FontmodHeight, 0);
// background
RenderTools()->DrawUIRect(&View, vec4(0,0,0,0.15f), 0, 0);
// prepare the scroll
#if defined(__ANDROID__)
View.VSplitRight(50, &View, &Scroll);
#else
View.VSplitRight(15, &View, &Scroll);
#endif
// setup the variables
gs_ListBoxOriginalView = View;
gs_ListBoxSelectedIndex = SelectedIndex;
gs_ListBoxNewSelected = SelectedIndex;
gs_ListBoxItemIndex = 0;
gs_ListBoxRowHeight = RowHeight;
gs_ListBoxNumItems = NumItems;
gs_ListBoxItemsPerRow = ItemsPerRow;
gs_ListBoxDoneEvents = 0;
gs_ListBoxScrollValue = ScrollValue;
gs_ListBoxItemActivated = false;
// do the scrollbar
View.HSplitTop(gs_ListBoxRowHeight, &Row, 0);
int NumViewable = (int)(gs_ListBoxOriginalView.h/Row.h) + 1;
int Num = (NumItems+gs_ListBoxItemsPerRow-1)/gs_ListBoxItemsPerRow-NumViewable+1;
if(Num < 0)
Num = 0;
if(Num > 0)
{
if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP) && UI()->MouseInside(&View))
gs_ListBoxScrollValue -= 3.0f/Num;
if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN) && UI()->MouseInside(&View))
gs_ListBoxScrollValue += 3.0f/Num;
if(gs_ListBoxScrollValue < 0.0f) gs_ListBoxScrollValue = 0.0f;
if(gs_ListBoxScrollValue > 1.0f) gs_ListBoxScrollValue = 1.0f;
}
Scroll.HMargin(5.0f, &Scroll);
gs_ListBoxScrollValue = DoScrollbarV(pID, &Scroll, gs_ListBoxScrollValue);
// the list
gs_ListBoxView = gs_ListBoxOriginalView;
gs_ListBoxView.VMargin(5.0f, &gs_ListBoxView);
UI()->ClipEnable(&gs_ListBoxView);
gs_ListBoxView.y -= gs_ListBoxScrollValue*Num*Row.h;
}
CMenus::CListboxItem CMenus::UiDoListboxNextRow()
{
static CUIRect s_RowView;
CListboxItem Item = {0};
if(gs_ListBoxItemIndex%gs_ListBoxItemsPerRow == 0)
gs_ListBoxView.HSplitTop(gs_ListBoxRowHeight /*-2.0f*/, &s_RowView, &gs_ListBoxView);
s_RowView.VSplitLeft(s_RowView.w/(gs_ListBoxItemsPerRow-gs_ListBoxItemIndex%gs_ListBoxItemsPerRow)/(UI()->Scale()), &Item.m_Rect, &s_RowView);
Item.m_Visible = 1;
//item.rect = row;
Item.m_HitRect = Item.m_Rect;
//CUIRect select_hit_box = item.rect;
if(gs_ListBoxSelectedIndex == gs_ListBoxItemIndex)
Item.m_Selected = 1;
// make sure that only those in view can be selected
if(Item.m_Rect.y+Item.m_Rect.h > gs_ListBoxOriginalView.y)
{
if(Item.m_HitRect.y < Item.m_HitRect.y) // clip the selection
{
Item.m_HitRect.h -= gs_ListBoxOriginalView.y-Item.m_HitRect.y;
Item.m_HitRect.y = gs_ListBoxOriginalView.y;
}
}
else
Item.m_Visible = 0;
// check if we need to do more
if(Item.m_Rect.y > gs_ListBoxOriginalView.y+gs_ListBoxOriginalView.h)
Item.m_Visible = 0;
gs_ListBoxItemIndex++;
return Item;
}
CMenus::CListboxItem CMenus::UiDoListboxNextItem(const void *pId, bool Selected)
{
int ThisItemIndex = gs_ListBoxItemIndex;
if(Selected)
{
if(gs_ListBoxSelectedIndex == gs_ListBoxNewSelected)
gs_ListBoxNewSelected = ThisItemIndex;
gs_ListBoxSelectedIndex = ThisItemIndex;
}
CListboxItem Item = UiDoListboxNextRow();
if(Item.m_Visible && UI()->DoButtonLogic(pId, "", gs_ListBoxSelectedIndex == gs_ListBoxItemIndex, &Item.m_HitRect))
gs_ListBoxNewSelected = ThisItemIndex;
// process input, regard selected index
if(gs_ListBoxSelectedIndex == ThisItemIndex)
{
if(!gs_ListBoxDoneEvents)
{
gs_ListBoxDoneEvents = 1;
if(m_EnterPressed || (UI()->ActiveItem() == pId && Input()->MouseDoubleClick()))
{
gs_ListBoxItemActivated = true;
UI()->SetActiveItem(0);
}
else
{
for(int i = 0; i < m_NumInputEvents; i++)
{
int NewIndex = -1;
if(m_aInputEvents[i].m_Flags&IInput::FLAG_PRESS)
{
if(m_aInputEvents[i].m_Key == KEY_DOWN) NewIndex = gs_ListBoxNewSelected + 1;
if(m_aInputEvents[i].m_Key == KEY_UP) NewIndex = gs_ListBoxNewSelected - 1;
}
if(NewIndex > -1 && NewIndex < gs_ListBoxNumItems)
{
// scroll
float Offset = (NewIndex/gs_ListBoxItemsPerRow-gs_ListBoxNewSelected/gs_ListBoxItemsPerRow)*gs_ListBoxRowHeight;
int Scroll = gs_ListBoxOriginalView.y > Item.m_Rect.y+Offset ? -1 :
gs_ListBoxOriginalView.y+gs_ListBoxOriginalView.h < Item.m_Rect.y+Item.m_Rect.h+Offset ? 1 : 0;
if(Scroll)
{
int NumViewable = (int)(gs_ListBoxOriginalView.h/gs_ListBoxRowHeight) + 1;
int ScrollNum = (gs_ListBoxNumItems+gs_ListBoxItemsPerRow-1)/gs_ListBoxItemsPerRow-NumViewable+1;
if(Scroll < 0)
{
int Num = (gs_ListBoxOriginalView.y-Item.m_Rect.y-Offset+gs_ListBoxRowHeight-1.0f)/gs_ListBoxRowHeight;
gs_ListBoxScrollValue -= (1.0f/ScrollNum)*Num;
}
else
{
int Num = (Item.m_Rect.y+Item.m_Rect.h+Offset-(gs_ListBoxOriginalView.y+gs_ListBoxOriginalView.h)+gs_ListBoxRowHeight-1.0f)/
gs_ListBoxRowHeight;
gs_ListBoxScrollValue += (1.0f/ScrollNum)*Num;
}
if(gs_ListBoxScrollValue < 0.0f) gs_ListBoxScrollValue = 0.0f;
if(gs_ListBoxScrollValue > 1.0f) gs_ListBoxScrollValue = 1.0f;
}
gs_ListBoxNewSelected = NewIndex;
}
}
}
}
//selected_index = i;
CUIRect r = Item.m_Rect;
r.Margin(1.5f, &r);
RenderTools()->DrawUIRect(&r, vec4(1,1,1,0.5f), CUI::CORNER_ALL, 4.0f);
}
return Item;
}
int CMenus::UiDoListboxEnd(float *pScrollValue, bool *pItemActivated)
{
UI()->ClipDisable();
if(pScrollValue)
*pScrollValue = gs_ListBoxScrollValue;
if(pItemActivated)
*pItemActivated = gs_ListBoxItemActivated;
return gs_ListBoxNewSelected;
}
int CMenus::DemolistFetchCallback(const char *pName, int IsDir, int StorageType, void *pUser)
{
CMenus *pSelf = (CMenus *)pUser;
int Length = str_length(pName);
if((pName[0] == '.' && (pName[1] == 0 ||
(pName[1] == '.' && pName[2] == 0 && !str_comp(pSelf->m_aCurrentDemoFolder, "demos")))) ||
(!IsDir && (Length < 5 || str_comp(pName+Length-5, ".demo"))))
return 0;
CDemoItem Item;
str_copy(Item.m_aFilename, pName, sizeof(Item.m_aFilename));
if(IsDir)
{
str_format(Item.m_aName, sizeof(Item.m_aName), "%s/", pName);
Item.m_Valid = false;
}
else
{
str_copy(Item.m_aName, pName, min(static_cast<int>(sizeof(Item.m_aName)), Length-4));
Item.m_InfosLoaded = false;
}
Item.m_IsDir = IsDir != 0;
Item.m_StorageType = StorageType;
pSelf->m_lDemos.add_unsorted(Item);
return 0;
}
void CMenus::DemolistPopulate()
{
m_lDemos.clear();
if(!str_comp(m_aCurrentDemoFolder, "demos"))
m_DemolistStorageType = IStorage::TYPE_ALL;
Storage()->ListDirectory(m_DemolistStorageType, m_aCurrentDemoFolder, DemolistFetchCallback, this);
m_lDemos.sort_range();
}
void CMenus::DemolistOnUpdate(bool Reset)
{
m_DemolistSelectedIndex = Reset ? m_lDemos.size() > 0 ? 0 : -1 :
m_DemolistSelectedIndex >= m_lDemos.size() ? m_lDemos.size()-1 : m_DemolistSelectedIndex;
m_DemolistSelectedIsDir = m_DemolistSelectedIndex < 0 ? false : m_lDemos[m_DemolistSelectedIndex].m_IsDir;
}
void CMenus::RenderDemoList(CUIRect MainView)
{
static int s_Inited = 0;
if(!s_Inited)
{
DemolistPopulate();
DemolistOnUpdate(true);
s_Inited = 1;
}
char aFooterLabel[128] = {0};
if(m_DemolistSelectedIndex >= 0)
{
CDemoItem *Item = &m_lDemos[m_DemolistSelectedIndex];
if(str_comp(Item->m_aFilename, "..") == 0)
str_copy(aFooterLabel, Localize("Parent Folder"), sizeof(aFooterLabel));
else if(m_DemolistSelectedIsDir)
str_copy(aFooterLabel, Localize("Folder"), sizeof(aFooterLabel));
else
{
if(!Item->m_InfosLoaded)
{
char aBuffer[512];
str_format(aBuffer, sizeof(aBuffer), "%s/%s", m_aCurrentDemoFolder, Item->m_aFilename);
Item->m_Valid = DemoPlayer()->GetDemoInfo(Storage(), aBuffer, Item->m_StorageType, &Item->m_Info);
Item->m_InfosLoaded = true;
}
if(!Item->m_Valid)
str_copy(aFooterLabel, Localize("Invalid Demo"), sizeof(aFooterLabel));
else
str_copy(aFooterLabel, Localize("Demo details"), sizeof(aFooterLabel));
}
}
// render background
RenderTools()->DrawUIRect(&MainView, ms_ColorTabbarActive, CUI::CORNER_ALL, 10.0f);
MainView.Margin(10.0f, &MainView);
CUIRect ButtonBar, RefreshRect, PlayRect, DeleteRect, RenameRect, FileIcon, ListBox;
MainView.HSplitBottom(ms_ButtonHeight+5.0f, &MainView, &ButtonBar);
ButtonBar.HSplitTop(5.0f, 0, &ButtonBar);
ButtonBar.VSplitRight(130.0f, &ButtonBar, &PlayRect);
ButtonBar.VSplitLeft(130.0f, &RefreshRect, &ButtonBar);
ButtonBar.VSplitLeft(10.0f, 0, &ButtonBar);
ButtonBar.VSplitLeft(120.0f, &DeleteRect, &ButtonBar);
ButtonBar.VSplitLeft(10.0f, 0, &ButtonBar);
ButtonBar.VSplitLeft(120.0f, &RenameRect, &ButtonBar);
MainView.HSplitBottom(140.0f, &ListBox, &MainView);
// render demo info
MainView.VMargin(5.0f, &MainView);
MainView.HSplitBottom(5.0f, &MainView, 0);
RenderTools()->DrawUIRect(&MainView, vec4(0,0,0,0.15f), CUI::CORNER_B, 4.0f);
if(!m_DemolistSelectedIsDir && m_DemolistSelectedIndex >= 0 && m_lDemos[m_DemolistSelectedIndex].m_Valid)
{
CUIRect Left, Right, Labels;
MainView.Margin(20.0f, &MainView);
MainView.VSplitMid(&Labels, &MainView);
// left side
Labels.HSplitTop(20.0f, &Left, &Labels);
Left.VSplitLeft(150.0f, &Left, &Right);
UI()->DoLabelScaled(&Left, Localize("Created:"), 14.0f, -1);
UI()->DoLabelScaled(&Right, m_lDemos[m_DemolistSelectedIndex].m_Info.m_aTimestamp, 14.0f, -1);
Labels.HSplitTop(5.0f, 0, &Labels);
Labels.HSplitTop(20.0f, &Left, &Labels);
Left.VSplitLeft(150.0f, &Left, &Right);
UI()->DoLabelScaled(&Left, Localize("Type:"), 14.0f, -1);
UI()->DoLabelScaled(&Right, m_lDemos[m_DemolistSelectedIndex].m_Info.m_aType, 14.0f, -1);
Labels.HSplitTop(5.0f, 0, &Labels);
Labels.HSplitTop(20.0f, &Left, &Labels);
Left.VSplitLeft(150.0f, &Left, &Right);
UI()->DoLabelScaled(&Left, Localize("Length:"), 14.0f, -1);
int Length = ((m_lDemos[m_DemolistSelectedIndex].m_Info.m_aLength[0]<<24)&0xFF000000) | ((m_lDemos[m_DemolistSelectedIndex].m_Info.m_aLength[1]<<16)&0xFF0000) |
((m_lDemos[m_DemolistSelectedIndex].m_Info.m_aLength[2]<<8)&0xFF00) | (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aLength[3]&0xFF);
char aBuf[64];
str_format(aBuf, sizeof(aBuf), "%d:%02d", Length/60, Length%60);
UI()->DoLabelScaled(&Right, aBuf, 14.0f, -1);
Labels.HSplitTop(5.0f, 0, &Labels);
Labels.HSplitTop(20.0f, &Left, &Labels);
Left.VSplitLeft(150.0f, &Left, &Right);
UI()->DoLabelScaled(&Left, Localize("Version:"), 14.0f, -1);
str_format(aBuf, sizeof(aBuf), "%d", m_lDemos[m_DemolistSelectedIndex].m_Info.m_Version);
UI()->DoLabelScaled(&Right, aBuf, 14.0f, -1);
// right side
Labels = MainView;
Labels.HSplitTop(20.0f, &Left, &Labels);
Left.VSplitLeft(150.0f, &Left, &Right);
UI()->DoLabelScaled(&Left, Localize("Map:"), 14.0f, -1);
UI()->DoLabelScaled(&Right, m_lDemos[m_DemolistSelectedIndex].m_Info.m_aMapName, 14.0f, -1);
Labels.HSplitTop(5.0f, 0, &Labels);
Labels.HSplitTop(20.0f, &Left, &Labels);
Left.VSplitLeft(20.0f, 0, &Left);
Left.VSplitLeft(130.0f, &Left, &Right);
UI()->DoLabelScaled(&Left, Localize("Size:"), 14.0f, -1);
unsigned Size = (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aMapSize[0]<<24) | (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aMapSize[1]<<16) |
(m_lDemos[m_DemolistSelectedIndex].m_Info.m_aMapSize[2]<<8) | (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aMapSize[3]);
if(Size > 1024*1024)
str_format(aBuf, sizeof(aBuf), Localize("%.2f MiB"), float(Size)/(1024*1024));
else
str_format(aBuf, sizeof(aBuf), Localize("%.2f KiB"), float(Size)/1024);
UI()->DoLabelScaled(&Right, aBuf, 14.0f, -1);
Labels.HSplitTop(5.0f, 0, &Labels);
Labels.HSplitTop(20.0f, &Left, &Labels);
Left.VSplitLeft(20.0f, 0, &Left);
Left.VSplitLeft(130.0f, &Left, &Right);
UI()->DoLabelScaled(&Left, Localize("Crc:"), 14.0f, -1);
unsigned Crc = (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aMapCrc[0]<<24) | (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aMapCrc[1]<<16) |
(m_lDemos[m_DemolistSelectedIndex].m_Info.m_aMapCrc[2]<<8) | (m_lDemos[m_DemolistSelectedIndex].m_Info.m_aMapCrc[3]);
str_format(aBuf, sizeof(aBuf), "%08x", Crc);
UI()->DoLabelScaled(&Right, aBuf, 14.0f, -1);
Labels.HSplitTop(5.0f, 0, &Labels);
Labels.HSplitTop(20.0f, &Left, &Labels);
Left.VSplitLeft(150.0f, &Left, &Right);
UI()->DoLabelScaled(&Left, Localize("Netversion:"), 14.0f, -1);
UI()->DoLabelScaled(&Right, m_lDemos[m_DemolistSelectedIndex].m_Info.m_aNetversion, 14.0f, -1);
}
static int s_DemoListId = 0;
static float s_ScrollValue = 0;
#if defined(__ANDROID__)
UiDoListboxStart(&s_DemoListId, &ListBox, 50.0f, Localize("Demos"), aFooterLabel, m_lDemos.size(), 1, m_DemolistSelectedIndex, s_ScrollValue);
#else
UiDoListboxStart(&s_DemoListId, &ListBox, 17.0f, Localize("Demos"), aFooterLabel, m_lDemos.size(), 1, m_DemolistSelectedIndex, s_ScrollValue);
#endif
for(sorted_array<CDemoItem>::range r = m_lDemos.all(); !r.empty(); r.pop_front())
{
CListboxItem Item = UiDoListboxNextItem((void*)(&r.front()));
if(Item.m_Visible)
{
Item.m_Rect.VSplitLeft(Item.m_Rect.h, &FileIcon, &Item.m_Rect);
Item.m_Rect.VSplitLeft(5.0f, 0, &Item.m_Rect);
DoButton_Icon(IMAGE_FILEICONS, r.front().m_IsDir?SPRITE_FILE_FOLDER:SPRITE_FILE_DEMO1, &FileIcon);
UI()->DoLabel(&Item.m_Rect, r.front().m_aName, Item.m_Rect.h*ms_FontmodHeight, -1);
}
}
bool Activated = false;
m_DemolistSelectedIndex = UiDoListboxEnd(&s_ScrollValue, &Activated);
DemolistOnUpdate(false);
static int s_RefreshButton = 0;
if(DoButton_Menu(&s_RefreshButton, Localize("Refresh"), 0, &RefreshRect))
{
DemolistPopulate();
DemolistOnUpdate(false);
}
static int s_PlayButton = 0;
if(DoButton_Menu(&s_PlayButton, m_DemolistSelectedIsDir?Localize("Open"):Localize("Play"), 0, &PlayRect) || Activated)
{
if(m_DemolistSelectedIndex >= 0)
{
if(m_DemolistSelectedIsDir) // folder
{
if(str_comp(m_lDemos[m_DemolistSelectedIndex].m_aFilename, "..") == 0) // parent folder
fs_parent_dir(m_aCurrentDemoFolder);
else // sub folder
{
char aTemp[256];
str_copy(aTemp, m_aCurrentDemoFolder, sizeof(aTemp));
str_format(m_aCurrentDemoFolder, sizeof(m_aCurrentDemoFolder), "%s/%s", aTemp, m_lDemos[m_DemolistSelectedIndex].m_aFilename);
m_DemolistStorageType = m_lDemos[m_DemolistSelectedIndex].m_StorageType;
}
DemolistPopulate();
DemolistOnUpdate(true);
}
else // file
{
char aBuf[512];
str_format(aBuf, sizeof(aBuf), "%s/%s", m_aCurrentDemoFolder, m_lDemos[m_DemolistSelectedIndex].m_aFilename);
const char *pError = Client()->DemoPlayer_Play(aBuf, m_lDemos[m_DemolistSelectedIndex].m_StorageType);
if(pError)
PopupMessage(Localize("Error"), str_comp(pError, "error loading demo") ? pError : Localize("Error loading demo"), Localize("Ok"));
else
{
UI()->SetActiveItem(0);
return;
}
}
}
}
if(!m_DemolistSelectedIsDir)
{
static int s_DeleteButton = 0;
if(DoButton_Menu(&s_DeleteButton, Localize("Delete"), 0, &DeleteRect) || m_DeletePressed)
{
if(m_DemolistSelectedIndex >= 0)
{
UI()->SetActiveItem(0);
m_Popup = POPUP_DELETE_DEMO;
return;
}
}
static int s_RenameButton = 0;
if(DoButton_Menu(&s_RenameButton, Localize("Rename"), 0, &RenameRect))
{
if(m_DemolistSelectedIndex >= 0)
{
UI()->SetActiveItem(0);
m_Popup = POPUP_RENAME_DEMO;
str_copy(m_aCurrentDemoFile, m_lDemos[m_DemolistSelectedIndex].m_aFilename, sizeof(m_aCurrentDemoFile));
return;
}
}
}
}