diff --git a/Source/DiabloUI/settingsmenu.cpp b/Source/DiabloUI/settingsmenu.cpp index 94cdae470..aa2a74516 100644 --- a/Source/DiabloUI/settingsmenu.cpp +++ b/Source/DiabloUI/settingsmenu.cpp @@ -107,6 +107,41 @@ bool NeedsTwoLinesToDisplayOption(std::vector &formatArgs) return GetLineWidth("{}: {}", formatArgs.data(), formatArgs.size(), 0, GameFontTables::GameFont24, 1) >= (rectList.size.width - 90); } +void AddSettingsSpacer() +{ + vecDialogItems.push_back(std::make_unique( + std::string_view {}, static_cast(SpecialMenuEntry::None), UiFlags::ElementDisabled)); +} + +void AddSettingsSectionHeader(std::string_view text, bool addSpacer) +{ + if (addSpacer) + AddSettingsSpacer(); + vecDialogItems.push_back(std::make_unique( + text, static_cast(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 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(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(selectedOption);