Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 38 additions & 14 deletions src/server/menu/menu_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,31 @@ namespace std {

namespace {

/*
=============
IsRenderableEntry

Determines whether a menu entry has content or an action to display.
=============
*/
bool IsRenderableEntry(const MenuEntry& entry) {
return !entry.text.empty() || static_cast<bool>(entry.onSelect);
}

/*
=============
RebuildScrollability

Refreshes the scrollable flag for each entry based on renderability.
=============
*/
void RebuildScrollability(const Menu& menu) {
auto& entries = const_cast<std::vector<MenuEntry>&>(menu.entries);

for (MenuEntry& entry : entries)
entry.scrollable = IsRenderableEntry(entry);
}

/*
=============
CountScrollableEntries
Expand Down Expand Up @@ -63,29 +88,26 @@ Builds the list of entries that should be rendered based on the current
scroll offset.
=============
*/
std::vector<const MenuEntry*> CollectVisibleEntries(const std::vector<MenuEntry>& entries, int offset, int maxOffset) {
std::vector<const MenuEntry*> CollectVisibleEntries(const std::vector<MenuEntry>& entries, int offset) {
int skippedScrollable = offset;
int visibleScrollable = 0;
std::vector<const MenuEntry*> visibleEntries;
visibleEntries.reserve(entries.size());

for (const MenuEntry& entry : entries) {
if (entry.scrollable) {
if (skippedScrollable > 0) {
--skippedScrollable;
continue;
}

if (visibleScrollable >= MAX_VISIBLE_LINES)
continue;
if (!entry.scrollable)
continue;

++visibleScrollable;
visibleEntries.push_back(&entry);
if (skippedScrollable > 0) {
--skippedScrollable;
continue;
}

if (visibleScrollable < MAX_VISIBLE_LINES || offset == maxOffset)
visibleEntries.push_back(&entry);
if (visibleScrollable >= MAX_VISIBLE_LINES)
break;

++visibleScrollable;
visibleEntries.push_back(&entry);
}

return visibleEntries;
Expand Down Expand Up @@ -167,6 +189,8 @@ void Menu::Render(gentity_t* ent) const {
if (onUpdate)
onUpdate(ent, *this);

RebuildScrollability(*this);

// Do not early-return if current is invalid; still render the menu
const int selected = (current >= 0 && current < static_cast<int>(entries.size())) ? current : -1;

Expand All @@ -180,7 +204,7 @@ void Menu::Render(gentity_t* ent) const {
const bool hasAbove = (offset > 0);
const bool hasBelow = (offset < maxOffset);

std::vector<const MenuEntry*> visibleEntries = CollectVisibleEntries(entries, offset, maxOffset);
std::vector<const MenuEntry*> visibleEntries = CollectVisibleEntries(entries, offset);

int y = 32;

Expand Down
7 changes: 2 additions & 5 deletions src/server/menu/menu_system.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,8 @@ void MenuSystem::Open(gentity_t* ent, std::unique_ptr<Menu> menu) {
if (ent->client->menu.current)
Close(ent);

const int total = static_cast<int>(menu->entries.size());

for (int i = 0; i < total; ++i) {
menu->entries[i].text = TrimToWidth(menu->entries[i].text);
menu->entries[i].scrollable = (i > 0 && i < total - 1);
for (MenuEntry& entry : menu->entries) {
entry.text = TrimToWidth(entry.text);
}

// Select the first entry with a valid onSelect
Expand Down