Browse Source

Add section headers to Gameplay settings

Add section headers to Gameplay settings

Add lightweight, non-selectable section headers and spacers to the
Gameplay settings menu so related options are easier to scan.

Keep the change UI-only by injecting disabled list rows while building
the settings list, without changing the options model or option behavior.

Also make the initial focus fall back to the first selectable option if
the previously selected option is no longer visible.
pull/8513/head
morfidon 1 day ago
parent
commit
124ba1b931
  1. 51
      Source/DiabloUI/settingsmenu.cpp

51
Source/DiabloUI/settingsmenu.cpp

@ -107,6 +107,41 @@ bool NeedsTwoLinesToDisplayOption(std::vector<DrawStringFormatArg> &formatArgs)
return GetLineWidth("{}: {}", formatArgs.data(), formatArgs.size(), 0, GameFontTables::GameFont24, 1) >= (rectList.size.width - 90);
}
void AddSettingsSpacer()
{
vecDialogItems.push_back(std::make_unique<UiListItem>(
std::string_view {}, static_cast<int>(SpecialMenuEntry::None), UiFlags::ElementDisabled));
}
void AddSettingsSectionHeader(std::string_view text, bool addSpacer)
{
if (addSpacer)
AddSettingsSpacer();
vecDialogItems.push_back(std::make_unique<UiListItem>(
text, static_cast<int>(SpecialMenuEntry::None), UiFlags::ColorWhitegold | UiFlags::ElementDisabled));
}
void MaybeAddGameplaySectionHeader(OptionEntryBase *pEntry)
{
if (selectedCategory != &GetOptions().Gameplay)
return;
const GameplayOptions &gameplay = GetOptions().Gameplay;
// Sections are intentionally lightweight: just labels/separators, no submenus and no changes to options model.
if (pEntry == &gameplay.friendlyFire) {
AddSettingsSectionHeader(_("Game Rules"), false);
} else if (pEntry == &gameplay.runInTown) {
AddSettingsSectionHeader(_("Controls"), true);
} else if (pEntry == &gameplay.experienceBar) {
AddSettingsSectionHeader(_("Interface"), true);
} else if (pEntry == &gameplay.autoRefillBelt) {
AddSettingsSectionHeader(_("Items & Auto Pickup"), true);
} else if (pEntry == &gameplay.disableCripplingShrines) {
AddSettingsSectionHeader(_("Safety & Focus"), true);
}
}
void CleanUpSettingsUI()
{
UiInitList_clear();
@ -419,11 +454,21 @@ void UiSettingsMenu()
}
} break;
case ShownMenuType::Settings: {
bool foundSelectedOption = false;
std::optional<size_t> firstSelectableItemIndex;
for (OptionEntryBase *pEntry : selectedCategory->GetEntries()) {
if (!IsValidEntry(pEntry))
continue;
if (selectedOption == pEntry)
MaybeAddGameplaySectionHeader(pEntry);
if (!firstSelectableItemIndex.has_value())
firstSelectableItemIndex = vecDialogItems.size();
if (selectedOption == pEntry) {
itemToSelect = vecDialogItems.size();
foundSelectedOption = true;
}
auto formatArgs = CreateDrawStringFormatArgForEntry(pEntry);
const int optionId = static_cast<int>(vecOptions.size());
if (NeedsTwoLinesToDisplayOption(formatArgs)) {
@ -434,6 +479,10 @@ void UiSettingsMenu()
}
vecOptions.push_back(pEntry);
}
// If previously selected option doesn't exist (e.g., became invisible),
// set focus on first real option, not on header.
if (!foundSelectedOption && firstSelectableItemIndex.has_value())
itemToSelect = *firstSelectableItemIndex;
} break;
case ShownMenuType::ListOption: {
auto *pOptionList = static_cast<OptionEntryListBase *>(selectedOption);

Loading…
Cancel
Save