Improve demo browser layout

Use separate columns for icons and spacing like in the server browser.

Always show scrollbar for more consistent layout.

Show ellipsis if filename is too long, also for the filename shown in the demo player.

Hide number of markers and length if the demo is invalid.
This commit is contained in:
Robert Müller 2023-07-02 23:15:27 +02:00
parent 9efab4964b
commit eb79b17308

View file

@ -593,7 +593,11 @@ void CMenus::RenderDemoPlayer(CUIRect MainView)
DemoPlayer()->GetDemoName(aDemoName, sizeof(aDemoName)); DemoPlayer()->GetDemoName(aDemoName, sizeof(aDemoName));
char aBuf[IO_MAX_PATH_LENGTH + 128]; char aBuf[IO_MAX_PATH_LENGTH + 128];
str_format(aBuf, sizeof(aBuf), Localize("Demofile: %s"), aDemoName); str_format(aBuf, sizeof(aBuf), Localize("Demofile: %s"), aDemoName);
UI()->DoLabel(&NameBar, aBuf, Button.h * 0.5f, TEXTALIGN_ML); SLabelProperties Props;
Props.m_MaxWidth = NameBar.w;
Props.m_EllipsisAtEnd = true;
Props.m_EnableWidthCheck = false;
UI()->DoLabel(&NameBar, aBuf, Button.h * 0.5f, TEXTALIGN_ML, Props);
if(IncreaseDemoSpeed) if(IncreaseDemoSpeed)
{ {
@ -1043,69 +1047,69 @@ void CMenus::RenderDemoList(CUIRect MainView)
int m_Direction; int m_Direction;
float m_Width; float m_Width;
CUIRect m_Rect; CUIRect m_Rect;
CUIRect m_Spacer;
}; };
enum enum
{ {
COL_DEMONAME = 0, COL_ICON = 0,
COL_DEMONAME,
COL_MARKERS, COL_MARKERS,
COL_LENGTH, COL_LENGTH,
COL_DATE, COL_DATE,
}; };
static CListBox s_ListBox;
static CColumn s_aCols[] = { static CColumn s_aCols[] = {
{COL_DEMONAME, SORT_DEMONAME, Localizable("Demo"), 0, 0.0f, {0}, {0}}, {-1, -1, "", -1, 2.0f, {0}},
{COL_MARKERS, SORT_MARKERS, Localizable("Markers"), 1, 75.0f, {0}, {0}}, {COL_ICON, -1, "", -1, ms_ListheaderHeight, {0}},
{COL_LENGTH, SORT_LENGTH, Localizable("Length"), 1, 75.0f, {0}, {0}}, {-1, -1, "", -1, 2.0f, {0}},
{COL_DATE, SORT_DATE, Localizable("Date"), 1, 160.0f, {0}, {0}}, {COL_DEMONAME, SORT_DEMONAME, Localizable("Demo"), 0, 0.0f, {0}},
{-1, -1, "", 1, 2.0f, {0}},
{COL_MARKERS, SORT_MARKERS, Localizable("Markers"), 1, 75.0f, {0}},
{-1, -1, "", 1, 2.0f, {0}},
{COL_LENGTH, SORT_LENGTH, Localizable("Length"), 1, 75.0f, {0}},
{-1, -1, "", 1, 2.0f, {0}},
{COL_DATE, SORT_DATE, Localizable("Date"), 1, 160.0f, {0}},
{-1, -1, "", 1, s_ListBox.ScrollbarWidthMax(), {0}},
}; };
Headers.Draw(ColorRGBA(0.0f, 0, 0, 0.15f), 0, 0); Headers.Draw(ColorRGBA(0.0f, 0.0f, 0.0f, 0.15f), IGraphics::CORNER_NONE, 0.0f);
int NumCols = std::size(s_aCols);
// do layout // do layout
for(int i = 0; i < NumCols; i++) for(auto &Col : s_aCols)
{ {
if(s_aCols[i].m_Direction == -1) if(Col.m_Direction == -1)
{ {
Headers.VSplitLeft(s_aCols[i].m_Width, &s_aCols[i].m_Rect, &Headers); Headers.VSplitLeft(Col.m_Width, &Col.m_Rect, &Headers);
if(i + 1 < NumCols)
{
Headers.VSplitLeft(2, &s_aCols[i].m_Spacer, &Headers);
}
} }
} }
for(int i = NumCols - 1; i >= 0; i--) for(int i = std::size(s_aCols) - 1; i >= 0; i--)
{ {
if(s_aCols[i].m_Direction == 1) if(s_aCols[i].m_Direction == 1)
{ {
Headers.VSplitRight(s_aCols[i].m_Width, &Headers, &s_aCols[i].m_Rect); Headers.VSplitRight(s_aCols[i].m_Width, &Headers, &s_aCols[i].m_Rect);
Headers.VSplitRight(2, &Headers, &s_aCols[i].m_Spacer);
} }
} }
for(int i = 0; i < NumCols; i++) for(auto &Col : s_aCols)
{ {
if(s_aCols[i].m_Direction == 0) if(Col.m_Direction == 0)
s_aCols[i].m_Rect = Headers; Col.m_Rect = Headers;
} }
// do headers // do headers
for(int i = 0; i < NumCols; i++) for(auto &Col : s_aCols)
{ {
if(DoButton_GridHeader(s_aCols[i].m_Caption, Localize(s_aCols[i].m_Caption), g_Config.m_BrDemoSort == s_aCols[i].m_Sort, &s_aCols[i].m_Rect)) if(DoButton_GridHeader(&Col.m_ID, Col.m_Caption, g_Config.m_BrDemoSort == Col.m_Sort, &Col.m_Rect))
{ {
if(s_aCols[i].m_Sort != -1) if(Col.m_Sort != -1)
{ {
if(g_Config.m_BrDemoSort == s_aCols[i].m_Sort) if(g_Config.m_BrDemoSort == Col.m_Sort)
g_Config.m_BrDemoSortOrder ^= 1; g_Config.m_BrDemoSortOrder ^= 1;
else else
g_Config.m_BrDemoSortOrder = 0; g_Config.m_BrDemoSortOrder = 0;
g_Config.m_BrDemoSort = s_aCols[i].m_Sort; g_Config.m_BrDemoSort = Col.m_Sort;
} }
// Don't rescan in order to keep fetched headers, just resort // Don't rescan in order to keep fetched headers, just resort
@ -1114,13 +1118,12 @@ void CMenus::RenderDemoList(CUIRect MainView)
} }
} }
static CListBox s_ListBox;
if(m_DemolistSelectedReveal) if(m_DemolistSelectedReveal)
{ {
s_ListBox.ScrollToSelected(); s_ListBox.ScrollToSelected();
m_DemolistSelectedReveal = false; m_DemolistSelectedReveal = false;
} }
s_ListBox.DoStart(ms_ListheaderHeight, m_vDemos.size(), 1, 3, m_DemolistSelectedIndex, &ListBox, false); s_ListBox.DoStart(ms_ListheaderHeight, m_vDemos.size(), 1, 3, m_DemolistSelectedIndex, &ListBox, false, IGraphics::CORNER_ALL, true);
int ItemIndex = -1; int ItemIndex = -1;
for(auto &Item : m_vDemos) for(auto &Item : m_vDemos)
@ -1131,63 +1134,61 @@ void CMenus::RenderDemoList(CUIRect MainView)
if(!ListItem.m_Visible) if(!ListItem.m_Visible)
continue; continue;
CUIRect Row = ListItem.m_Rect; for(const auto &Col : s_aCols)
CUIRect FileIcon;
Row.VSplitLeft(Row.h, &FileIcon, &Row);
Row.VSplitLeft(5.0f, 0, &Row);
FileIcon.Margin(1.0f, &FileIcon);
FileIcon.x += 2.0f;
const char *pIconType;
if(Item.m_IsLink || str_comp(Item.m_aFilename, "..") == 0)
pIconType = FONT_ICON_FOLDER_TREE;
else if(Item.m_IsDir)
pIconType = FONT_ICON_FOLDER;
else
pIconType = FONT_ICON_FILM;
ColorRGBA IconColor(1.0f, 1.0f, 1.0f, 1.0f);
if(!Item.m_IsDir && (!Item.m_InfosLoaded || !Item.m_Valid))
IconColor = ColorRGBA(0.6f, 0.6f, 0.6f, 1.0f); // not loaded
TextRender()->SetCurFont(TextRender()->GetFont(TEXT_FONT_ICON_FONT));
TextRender()->TextColor(IconColor);
TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING);
UI()->DoLabel(&FileIcon, pIconType, 12.0f, TEXTALIGN_ML);
TextRender()->SetRenderFlags(0);
TextRender()->TextColor(TextRender()->DefaultTextColor());
TextRender()->SetCurFont(nullptr);
for(int c = 0; c < NumCols; c++)
{ {
CUIRect Button; CUIRect Button;
Button.x = s_aCols[c].m_Rect.x; Button.x = Col.m_Rect.x;
Button.y = Row.y; Button.y = ListItem.m_Rect.y;
Button.h = Row.h; Button.h = ListItem.m_Rect.h;
Button.w = s_aCols[c].m_Rect.w; Button.w = Col.m_Rect.w;
int ID = s_aCols[c].m_ID; int ID = Col.m_ID;
if(ID == COL_DEMONAME) if(ID == COL_ICON)
{ {
Button.x += FileIcon.w + 6.0f; Button.Margin(1.0f, &Button);
CTextCursor Cursor;
TextRender()->SetCursor(&Cursor, Button.x, Button.y + (Button.h - 12.0f) / 2.f, 12.0f, TEXTFLAG_RENDER | TEXTFLAG_STOP_AT_END); const char *pIconType;
Cursor.m_LineWidth = Button.w; if(Item.m_IsLink || str_comp(Item.m_aFilename, "..") == 0)
TextRender()->TextEx(&Cursor, Item.m_aName, -1); pIconType = FONT_ICON_FOLDER_TREE;
else if(Item.m_IsDir)
pIconType = FONT_ICON_FOLDER;
else
pIconType = FONT_ICON_FILM;
ColorRGBA IconColor;
if(!Item.m_IsDir && (!Item.m_InfosLoaded || !Item.m_Valid))
IconColor = ColorRGBA(0.6f, 0.6f, 0.6f, 1.0f); // not loaded
else
IconColor = ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f);
TextRender()->SetCurFont(TextRender()->GetFont(TEXT_FONT_ICON_FONT));
TextRender()->TextColor(IconColor);
TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING);
UI()->DoLabel(&Button, pIconType, 12.0f, TEXTALIGN_ML);
TextRender()->SetRenderFlags(0);
TextRender()->TextColor(TextRender()->DefaultTextColor());
TextRender()->SetCurFont(nullptr);
} }
else if(ID == COL_MARKERS && !Item.m_IsDir && Item.m_InfosLoaded) else if(ID == COL_DEMONAME)
{
SLabelProperties Props;
Props.m_MaxWidth = Button.w;
Props.m_EllipsisAtEnd = true;
Props.m_EnableWidthCheck = false;
UI()->DoLabel(&Button, Item.m_aName, 12.0f, TEXTALIGN_ML, Props);
}
else if(ID == COL_MARKERS && !Item.m_IsDir && Item.m_InfosLoaded && Item.m_Valid)
{ {
char aBuf[3]; char aBuf[3];
str_format(aBuf, sizeof(aBuf), "%d", Item.NumMarkers()); str_format(aBuf, sizeof(aBuf), "%d", Item.NumMarkers());
Button.VMargin(4.0f, &Button); Button.VMargin(4.0f, &Button);
UI()->DoLabel(&Button, aBuf, 12.0f, TEXTALIGN_MR); UI()->DoLabel(&Button, aBuf, 12.0f, TEXTALIGN_MR);
} }
else if(ID == COL_LENGTH && !Item.m_IsDir && Item.m_InfosLoaded) else if(ID == COL_LENGTH && !Item.m_IsDir && Item.m_InfosLoaded && Item.m_Valid)
{ {
int Length = Item.Length();
char aBuf[32]; char aBuf[32];
str_time((int64_t)Length * 100, TIME_HOURS, aBuf, sizeof(aBuf)); str_time((int64_t)Item.Length() * 100, TIME_HOURS, aBuf, sizeof(aBuf));
Button.VMargin(4.0f, &Button); Button.VMargin(4.0f, &Button);
UI()->DoLabel(&Button, aBuf, 12.0f, TEXTALIGN_MR); UI()->DoLabel(&Button, aBuf, 12.0f, TEXTALIGN_MR);
} }
@ -1195,7 +1196,7 @@ void CMenus::RenderDemoList(CUIRect MainView)
{ {
char aBuf[64]; char aBuf[64];
str_timestamp_ex(Item.m_Date, aBuf, sizeof(aBuf), FORMAT_SPACE); str_timestamp_ex(Item.m_Date, aBuf, sizeof(aBuf), FORMAT_SPACE);
Button.VSplitRight(24.0f, &Button, 0); Button.VMargin(4.0f, &Button);
UI()->DoLabel(&Button, aBuf, 12.0f, TEXTALIGN_MR); UI()->DoLabel(&Button, aBuf, 12.0f, TEXTALIGN_MR);
} }
} }