6819: Improve demo browser layout r=heinrich5991 a=Robyt3

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.

Screenshots:
- Browser:
   - Before:
![Browser old](https://github.com/ddnet/ddnet/assets/23437060/55f4de41-e225-43eb-b07d-435cb4f9292d)
   - After:
![Browser new2](https://github.com/ddnet/ddnet/assets/23437060/56005b8c-a4e9-47eb-80a3-50013a856852)
- Player:
   - Before:
![Player old](https://github.com/ddnet/ddnet/assets/23437060/ef74c873-a62a-4184-8f90-08289ab2b829)
   - After:
![Player new2](https://github.com/ddnet/ddnet/assets/23437060/54f06bfa-28a8-480a-9125-f5ff9ffc81e0)

## Checklist

- [X] Tested the change ingame
- [X] Provided screenshots if it is a visual change
- [ ] Tested in combination with possibly related configuration options
- [ ] Written a unit test (especially base/) or added coverage to integration test
- [ ] Considered possible null pointers and out of bounds array indexing
- [ ] Changed no physics that affect existing maps
- [ ] Tested the change with [ASan+UBSan or valgrind's memcheck](https://github.com/ddnet/ddnet/#using-addresssanitizer--undefinedbehavioursanitizer-or-valgrinds-memcheck) (optional)


Co-authored-by: Robert Müller <robytemueller@gmail.com>
This commit is contained in:
bors[bot] 2023-07-11 15:33:00 +00:00 committed by GitHub
commit c9642e103f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 78 additions and 76 deletions

View file

@ -593,7 +593,11 @@ void CMenus::RenderDemoPlayer(CUIRect MainView)
DemoPlayer()->GetDemoName(aDemoName, sizeof(aDemoName));
char aBuf[IO_MAX_PATH_LENGTH + 128];
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)
{
@ -1043,69 +1047,69 @@ void CMenus::RenderDemoList(CUIRect MainView)
int m_Direction;
float m_Width;
CUIRect m_Rect;
CUIRect m_Spacer;
};
enum
{
COL_DEMONAME = 0,
COL_ICON = 0,
COL_DEMONAME,
COL_MARKERS,
COL_LENGTH,
COL_DATE,
};
static CListBox s_ListBox;
static CColumn s_aCols[] = {
{COL_DEMONAME, SORT_DEMONAME, Localizable("Demo"), 0, 0.0f, {0}, {0}},
{COL_MARKERS, SORT_MARKERS, Localizable("Markers"), 1, 75.0f, {0}, {0}},
{COL_LENGTH, SORT_LENGTH, Localizable("Length"), 1, 75.0f, {0}, {0}},
{COL_DATE, SORT_DATE, Localizable("Date"), 1, 160.0f, {0}, {0}},
{-1, -1, "", -1, 2.0f, {0}},
{COL_ICON, -1, "", -1, ms_ListheaderHeight, {0}},
{-1, -1, "", -1, 2.0f, {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);
int NumCols = std::size(s_aCols);
Headers.Draw(ColorRGBA(0.0f, 0.0f, 0.0f, 0.15f), IGraphics::CORNER_NONE, 0.0f);
// 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);
if(i + 1 < NumCols)
{
Headers.VSplitLeft(2, &s_aCols[i].m_Spacer, &Headers);
}
Headers.VSplitLeft(Col.m_Width, &Col.m_Rect, &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)
{
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)
s_aCols[i].m_Rect = Headers;
if(Col.m_Direction == 0)
Col.m_Rect = 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;
else
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
@ -1114,13 +1118,12 @@ void CMenus::RenderDemoList(CUIRect MainView)
}
}
static CListBox s_ListBox;
if(m_DemolistSelectedReveal)
{
s_ListBox.ScrollToSelected();
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;
for(auto &Item : m_vDemos)
@ -1131,63 +1134,61 @@ void CMenus::RenderDemoList(CUIRect MainView)
if(!ListItem.m_Visible)
continue;
CUIRect Row = ListItem.m_Rect;
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++)
for(const auto &Col : s_aCols)
{
CUIRect Button;
Button.x = s_aCols[c].m_Rect.x;
Button.y = Row.y;
Button.h = Row.h;
Button.w = s_aCols[c].m_Rect.w;
Button.x = Col.m_Rect.x;
Button.y = ListItem.m_Rect.y;
Button.h = ListItem.m_Rect.h;
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;
CTextCursor Cursor;
TextRender()->SetCursor(&Cursor, Button.x, Button.y + (Button.h - 12.0f) / 2.f, 12.0f, TEXTFLAG_RENDER | TEXTFLAG_STOP_AT_END);
Cursor.m_LineWidth = Button.w;
TextRender()->TextEx(&Cursor, Item.m_aName, -1);
Button.Margin(1.0f, &Button);
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;
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];
str_format(aBuf, sizeof(aBuf), "%d", Item.NumMarkers());
Button.VMargin(4.0f, &Button);
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];
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);
UI()->DoLabel(&Button, aBuf, 12.0f, TEXTALIGN_MR);
}
@ -1195,7 +1196,7 @@ void CMenus::RenderDemoList(CUIRect MainView)
{
char aBuf[64];
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);
}
}

View file

@ -60,7 +60,7 @@ void CListBox::DoFooter(const char *pBottomText, float FooterHeight)
m_FooterHeight = FooterHeight;
}
void CListBox::DoStart(float RowHeight, int NumItems, int ItemsPerRow, int RowsPerScroll, int SelectedIndex, const CUIRect *pRect, bool Background, int BackgroundCorners)
void CListBox::DoStart(float RowHeight, int NumItems, int ItemsPerRow, int RowsPerScroll, int SelectedIndex, const CUIRect *pRect, bool Background, int BackgroundCorners, bool ForceShowScrollbar)
{
CUIRect View;
if(pRect)
@ -119,6 +119,7 @@ void CListBox::DoStart(float RowHeight, int NumItems, int ItemsPerRow, int RowsP
ScrollParams.m_Active = m_Active;
ScrollParams.m_ScrollbarWidth = ScrollbarWidthMax();
ScrollParams.m_ScrollUnit = (m_ListBoxRowHeight + m_AutoSpacing) * RowsPerScroll;
ScrollParams.m_Flags = ForceShowScrollbar ? CScrollRegionParams::FLAG_CONTENT_STATIC_WIDTH : 0;
m_ScrollRegion.Begin(&m_ListBoxView, &m_ScrollOffset, &ScrollParams);
m_ListBoxView.y += m_ScrollOffset.y;
}

View file

@ -51,7 +51,7 @@ public:
void DoAutoSpacing(float Spacing = 20.0f) { m_AutoSpacing = Spacing; }
void DoSpacing(float Spacing = 20.0f);
void DoFooter(const char *pBottomText, float FooterHeight = 20.0f); // call before DoStart to create a footer
void DoStart(float RowHeight, int NumItems, int ItemsPerRow, int RowsPerScroll, int SelectedIndex, const CUIRect *pRect = nullptr, bool Background = true, int BackgroundCorners = IGraphics::CORNER_ALL);
void DoStart(float RowHeight, int NumItems, int ItemsPerRow, int RowsPerScroll, int SelectedIndex, const CUIRect *pRect = nullptr, bool Background = true, int BackgroundCorners = IGraphics::CORNER_ALL, bool ForceShowScrollbar = false);
void ScrollToSelected() { m_ListBoxUpdateScroll = true; }
CListboxItem DoNextItem(const void *pID, bool Selected = false);
CListboxItem DoSubheader();