From 6aa01fa172d5a2311db31ff73d5e3d64d5f83c5e Mon Sep 17 00:00:00 2001 From: majochem <77203255+majochem@users.noreply.github.com> Date: Tue, 21 Apr 2026 18:01:31 +0200 Subject: [PATCH 1/6] Make options menu 2-columns if too high Currently the options menu disappears off the top and bottom of the screen without possibility to scroll. This is particularly annoying, if the option for UI size scaling is one of the hidden options. It now uses a two-column layout if there is enough horizontal space, but not enough vertical space. --- src/Modules/Main.lua | 88 +++++++++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 33 deletions(-) diff --git a/src/Modules/Main.lua b/src/Modules/Main.lua index 3f6e19d74d..71f2bdac22 100644 --- a/src/Modules/Main.lua +++ b/src/Modules/Main.lua @@ -817,9 +817,15 @@ end function main:OpenOptionsPopup() local controls = { } - - local currentY = 20 - local popupWidth = 600 + -- NOTE: Height needs to be adjusted if more menu options are added + local oneColumnHeightReq = 850 -- Min height required to not split menu into two columns + local columnWidth = 600 + + local startingY = 20 + local currentY = startingY + local currentX = 0 -- initialized at `0`, only used for two-column layouts + local useTwoColumns = self.screenH < oneColumnHeightReq and self.screenW >= columnWidth * 2 + local popupWidth = useTwoColumns and columnWidth * 2 or columnWidth -- local func to make a new line with a heightModifier local function nextRow(heightModifier) @@ -831,18 +837,18 @@ function main:OpenOptionsPopup() -- local func to make a new section header local function drawSectionHeader(id, title, omitHorizontalLine) local headerBGColor ={ .6, .6, .6} - controls["section-"..id .. "-bg"] = new("RectangleOutlineControl", { "TOPLEFT", nil, "TOPLEFT" }, { 8, currentY, popupWidth - 17, 26 }, headerBGColor, 1) + controls["section-"..id .. "-bg"] = new("RectangleOutlineControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + 8, currentY, columnWidth - 17, 26 }, headerBGColor, 1) nextRow(.2) - controls["section-"..id .. "-label"] = new("LabelControl", { "TOPLEFT", nil, "TOPLEFT" }, { popupWidth / 2 - 60, currentY, 0, 16 }, "^7" .. title) + controls["section-"..id .. "-label"] = new("LabelControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + columnWidth / 2 - 60, currentY, 0, 16 }, "^7" .. title) nextRow(1.5) end local defaultLabelSpacingPx = -4 - local defaultLabelPlacementX = popupWidth*0.45 + local defaultLabelPlacementX = columnWidth*0.45 drawSectionHeader("app", "Application options") - controls.connectionProtocol = new("DropDownControl", { "TOPLEFT", nil, "TOPLEFT" }, { defaultLabelPlacementX, currentY, 100, 18 }, { + controls.connectionProtocol = new("DropDownControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 100, 18 }, { { label = "Auto", protocol = 0 }, { label = "IPv4", protocol = 1 }, { label = "IPv6", protocol = 2 }, @@ -854,7 +860,7 @@ function main:OpenOptionsPopup() controls.connectionProtocol:SelByValue(launch.connectionProtocol, "protocol") nextRow() - controls.proxyType = new("DropDownControl", { "TOPLEFT", nil, "TOPLEFT" }, { defaultLabelPlacementX, currentY, 80, 18 }, { + controls.proxyType = new("DropDownControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 80, 18 }, { { label = "HTTP", scheme = "http" }, { label = "SOCKS", scheme = "socks5" }, { label = "SOCKS5H", scheme = "socks5h" }, @@ -869,7 +875,7 @@ function main:OpenOptionsPopup() end nextRow() - controls.dpiScaleOverride = new("DropDownControl", { "TOPLEFT", nil, "TOPLEFT" }, { defaultLabelPlacementX, currentY, 150, 18 }, { + controls.dpiScaleOverride = new("DropDownControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 150, 18 }, { { label = "Use system default", percent = 0 }, { label = "100%", percent = 100 }, { label = "125%", percent = 125 }, @@ -881,13 +887,16 @@ function main:OpenOptionsPopup() }, function(index, value) self.dpiScaleOverridePercent = value.percent SetDPIScaleOverridePercent(value.percent) + self.screenW, self.screenH = GetVirtualScreenSize() -- refresh screen size immediately + self:ClosePopup() + self:OpenOptionsPopup() end) controls.dpiScaleOverrideLabel = new("LabelControl", { "RIGHT", controls.dpiScaleOverride, "LEFT" }, { defaultLabelSpacingPx, 0, 0, 16 }, "^7UI scaling override:") controls.dpiScaleOverride.tooltipText = "Overrides Windows DPI scaling inside Path of Building.\nChoose a percentage between 100% and 250% or revert to the system default." controls.dpiScaleOverride:SelByValue(self.dpiScaleOverridePercent, "percent") nextRow() - controls.buildPath = new("EditControl", { "TOPLEFT", nil, "TOPLEFT" }, { defaultLabelPlacementX, currentY, 290, 18 }) + controls.buildPath = new("EditControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 290, 18 }) controls.buildPathLabel = new("LabelControl", { "RIGHT", controls.buildPath, "LEFT" }, { defaultLabelSpacingPx, 0, 0, 16 }, "^7Build save path:") if self.buildPath ~= self.defaultBuildPath then controls.buildPath:SetText(self.buildPath) @@ -895,7 +904,7 @@ function main:OpenOptionsPopup() controls.buildPath.tooltipText = "Overrides the default save location for builds.\nThe default location is: '"..self.defaultBuildPath.."'" nextRow() - controls.nodePowerTheme = new("DropDownControl", { "TOPLEFT", nil, "TOPLEFT" }, { defaultLabelPlacementX, currentY, 100, 18 }, { + controls.nodePowerTheme = new("DropDownControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 100, 18 }, { { label = "Red & Blue", theme = "RED/BLUE" }, { label = "Red & Green", theme = "RED/GREEN" }, { label = "Green & Blue", theme = "GREEN/BLUE" }, @@ -907,7 +916,7 @@ function main:OpenOptionsPopup() controls.nodePowerTheme:SelByValue(self.nodePowerTheme, "theme") nextRow() - controls.colorPositive = new("EditControl", { "TOPLEFT", nil, "TOPLEFT" }, { defaultLabelPlacementX, currentY, 100, 18 }, tostring(self.colorPositive:gsub('^(^)', '0')), nil, nil, 8, function(buf) + controls.colorPositive = new("EditControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 100, 18 }, tostring(self.colorPositive:gsub('^(^)', '0')), nil, nil, 8, function(buf) local match = string.match(buf, "0x%x+") if match and #match == 8 then updateColorCode("POSITIVE", buf) @@ -919,7 +928,7 @@ function main:OpenOptionsPopup() "The default value is " .. tostring(defaultColorCodes.POSITIVE:gsub('^(^)', '0')) .. ".\nIf updating while inside a build, please re-load the build after saving." nextRow() - controls.colorNegative = new("EditControl", { "TOPLEFT", nil, "TOPLEFT" }, { defaultLabelPlacementX, currentY, 100, 18 }, tostring(self.colorNegative:gsub('^(^)', '0')), nil, nil, 8, function(buf) + controls.colorNegative = new("EditControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 100, 18 }, tostring(self.colorNegative:gsub('^(^)', '0')), nil, nil, 8, function(buf) local match = string.match(buf, "0x%x+") if match and #match == 8 then updateColorCode("NEGATIVE", buf) @@ -932,7 +941,7 @@ function main:OpenOptionsPopup() nextRow() - controls.colorHighlight = new("EditControl", { "TOPLEFT", nil, "TOPLEFT" }, { defaultLabelPlacementX, currentY, 100, 18 }, tostring(self.colorHighlight:gsub('^(^)', '0')), nil, nil, 8, function(buf) + controls.colorHighlight = new("EditControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 100, 18 }, tostring(self.colorHighlight:gsub('^(^)', '0')), nil, nil, 8, function(buf) local match = string.match(buf, "0x%x+") if match and #match == 8 then updateColorCode("HIGHLIGHT", buf) @@ -944,78 +953,88 @@ function main:OpenOptionsPopup() "The default value is " .. tostring(defaultColorCodes.HIGHLIGHT:gsub('^(^)', '0')) .."\nIf updating while inside a build, please re-load the build after saving." nextRow() - controls.betaTest = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { defaultLabelPlacementX, currentY, 20 }, "^7Opt-in to weekly beta test builds:", function(state) + controls.betaTest = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Opt-in to weekly beta test builds:", function(state) self.betaTest = state end) nextRow() - controls.edgeSearchHighlight = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { defaultLabelPlacementX, currentY, 20}, "^7Show search circles at viewport edge", function(state) + controls.edgeSearchHighlight = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20}, "^7Show search circles at viewport edge", function(state) self.edgeSearchHighlight = state end) nextRow() - controls.showPublicBuilds = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { defaultLabelPlacementX, currentY, 20 }, "^7Show Latest/Trending builds:", function(state) + controls.showPublicBuilds = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Show Latest/Trending builds:", function(state) self.showPublicBuilds = state end) nextRow() - controls.showFlavourText = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { defaultLabelPlacementX, currentY, 20 }, "^7Styled Tooltips with Flavour Text:", function(state) + controls.showFlavourText = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Styled Tooltips with Flavour Text:", function(state) self.showFlavourText = state end) controls.showFlavourText.tooltipText = "If updating while inside a build, please re-load the build after saving." nextRow() - controls.showAnimations = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { defaultLabelPlacementX, currentY, 20 }, "^7Show Animations:", function(state) + controls.showAnimations = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Show Animations:", function(state) self.showAnimations = state end) nextRow() - controls.showAllItemAffixes = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { defaultLabelPlacementX, currentY, 20 }, "^7Show all item affixes sliders:", function(state) + controls.showAllItemAffixes = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Show all item affixes sliders:", function(state) self.showAllItemAffixes = state end) controls.showAllItemAffixes.tooltipText = "Display all item affix slots as a stacked list instead of hiding them in dropdowns" nextRow() + + local leftColumnMaxY = currentY -- store left column height + + -- Check for two column layout + if useTwoColumns then + currentY = startingY -- reset height back to top + currentX = columnWidth + end + + -- Build-related Option Section starts drawSectionHeader("build", "Build-related options") - controls.showThousandsSeparators = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT"}, { defaultLabelPlacementX, currentY, 20 }, "^7Show thousands separators:", function(state) + controls.showThousandsSeparators = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT"}, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Show thousands separators:", function(state) self.showThousandsSeparators = state end) controls.showThousandsSeparators.state = self.showThousandsSeparators nextRow() - controls.thousandsSeparator = new("EditControl", { "TOPLEFT", nil, "TOPLEFT" }, { defaultLabelPlacementX, currentY, 30, 20 }, self.thousandsSeparator, nil, "%w", 1, function(buf) + controls.thousandsSeparator = new("EditControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 30, 20 }, self.thousandsSeparator, nil, "%w", 1, function(buf) self.thousandsSeparator = buf end) controls.thousandsSeparatorLabel = new("LabelControl", { "RIGHT", controls.thousandsSeparator, "LEFT" }, { defaultLabelSpacingPx, 0, 92, 16 }, "^7Thousands separator:") nextRow() - controls.decimalSeparator = new("EditControl", { "TOPLEFT", nil, "TOPLEFT" }, { defaultLabelPlacementX, currentY, 30, 20 }, self.decimalSeparator, nil, "%w", 1, function(buf) + controls.decimalSeparator = new("EditControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 30, 20 }, self.decimalSeparator, nil, "%w", 1, function(buf) self.decimalSeparator = buf end) controls.decimalSeparatorLabel = new("LabelControl", { "RIGHT", controls.decimalSeparator, "LEFT" }, { defaultLabelSpacingPx, 0, 92, 16 }, "^7Decimal separator:") nextRow() - controls.titlebarName = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { defaultLabelPlacementX, currentY, 20 }, "^7Show build name in window title:", function(state) + controls.titlebarName = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Show build name in window title:", function(state) self.showTitlebarName = state end) nextRow() - controls.defaultGemQuality = new("EditControl", { "TOPLEFT", nil, "TOPLEFT" }, { defaultLabelPlacementX, currentY, 80, 20 }, self.defaultGemQuality, nil, "%D", 2, function(gemQuality) + controls.defaultGemQuality = new("EditControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 80, 20 }, self.defaultGemQuality, nil, "%D", 2, function(gemQuality) self.defaultGemQuality = m_min(tonumber(gemQuality) or 0, 23) end) controls.defaultGemQuality.tooltipText = "Set the default quality that can be overwritten by build-related quality settings in the skill panel." controls.defaultGemQualityLabel = new("LabelControl", { "RIGHT", controls.defaultGemQuality, "LEFT" }, { defaultLabelSpacingPx, 0, 0, 16 }, "^7Default gem quality:") nextRow() - controls.defaultCharLevel = new("EditControl", { "TOPLEFT", nil, "TOPLEFT" }, { defaultLabelPlacementX, currentY, 80, 20 }, self.defaultCharLevel, nil, "%D", 3, function(charLevel) + controls.defaultCharLevel = new("EditControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 80, 20 }, self.defaultCharLevel, nil, "%D", 3, function(charLevel) self.defaultCharLevel = m_min(m_max(tonumber(charLevel) or 1, 1), 100) end) controls.defaultCharLevel.tooltipText = "Set the default level of your builds. If this is higher than 1, manual level mode will be enabled by default in new builds." controls.defaultCharLevelLabel = new("LabelControl", { "RIGHT", controls.defaultCharLevel, "LEFT" }, { defaultLabelSpacingPx, 0, 0, 16 }, "^7Default character level:") nextRow() - controls.defaultItemAffixQualitySlider = new("SliderControl", { "TOPLEFT", nil, "TOPLEFT" }, { defaultLabelPlacementX, currentY, 200, 20 }, function(value) + controls.defaultItemAffixQualitySlider = new("SliderControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 200, 20 }, function(value) self.defaultItemAffixQuality = round(value, 2) controls.defaultItemAffixQualityValue.label = (self.defaultItemAffixQuality * 100) .. "%" end) @@ -1025,33 +1044,33 @@ function main:OpenOptionsPopup() controls.defaultItemAffixQualityValue.label = (self.defaultItemAffixQuality * 100) .. "%" nextRow() - controls.showWarnings = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { defaultLabelPlacementX, currentY, 20 }, "^7Show build warnings:", function(state) + controls.showWarnings = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Show build warnings:", function(state) self.showWarnings = state end) controls.showWarnings.state = self.showWarnings nextRow() - controls.slotOnlyTooltips = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { defaultLabelPlacementX, currentY, 20 }, "^7Show tooltips only for affected slots:", function(state) + controls.slotOnlyTooltips = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Show tooltips only for affected slots:", function(state) self.slotOnlyTooltips = state end, "Shows comparisons in tooltips only for the slot you are currently placing the item in, instead of all slots.") controls.slotOnlyTooltips.state = self.slotOnlyTooltips nextRow() - controls.migrateEldritchImplicits = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { defaultLabelPlacementX, currentY, 20 }, "^7Copy Eldritch Implicits onto Display Item:", function(state) + controls.migrateEldritchImplicits = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Copy Eldritch Implicits onto Display Item:", function(state) self.migrateEldritchImplicits = state end) controls.migrateEldritchImplicits.tooltipText = "Apply Eldritch Implicits from current gear when comparing new gear, given the new item doesn't have any influence" controls.migrateEldritchImplicits.state = self.migrateEldritchImplicits nextRow() - controls.notSupportedModTooltips = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { defaultLabelPlacementX, currentY, 20 }, "^7Show tooltip for unsupported mods :", function(state) + controls.notSupportedModTooltips = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Show tooltip for unsupported mods :", function(state) self.notSupportedModTooltips = state end) controls.notSupportedModTooltips.tooltipText = "Show ^8(Not supported in PoB yet) ^7next to unsupported mods" controls.notSupportedModTooltips.state = self.notSupportedModTooltips nextRow() - controls.invertSliderScrollDirection = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { defaultLabelPlacementX, currentY, 20 }, "^7Invert slider scroll direction:", function(state) + controls.invertSliderScrollDirection = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Invert slider scroll direction:", function(state) self.invertSliderScrollDirection = state end) controls.invertSliderScrollDirection.tooltipText = "Default scroll direction is:\nScroll Up = Move right\nScroll Down = Move left" @@ -1059,7 +1078,7 @@ function main:OpenOptionsPopup() if launch.devMode then nextRow() - controls.disableDevAutoSave = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { defaultLabelPlacementX, currentY, 20 }, "^7Disable Dev AutoSave:", function(state) + controls.disableDevAutoSave = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Disable Dev AutoSave:", function(state) self.disableDevAutoSave = state end) controls.disableDevAutoSave.tooltipText = "Do not Autosave builds while on Dev branch" @@ -1098,6 +1117,9 @@ function main:OpenOptionsPopup() local initialShowAllItemAffixes = self.showAllItemAffixes local initialDpiScaleOverridePercent = self.dpiScaleOverridePercent + -- Adjust height in case of two-column layout + currentY = m_max(leftColumnMaxY, currentY) + -- last line with buttons has more spacing nextRow(1.5) From 26755bd801e80e5c4953f9f76a7758c80e8515ad Mon Sep 17 00:00:00 2001 From: majochem <77203255+majochem@users.noreply.github.com> Date: Wed, 22 Apr 2026 11:56:45 +0200 Subject: [PATCH 2/6] Add scrollbar to options menu as fallback Previously added 2 column layout, but if that doesn't fit, a scrollbar will be shown instead. I kept the 2 column layout as first fallback because it shows all available option on screen at once. --- src/Modules/Main.lua | 125 +++++++++++++++++++++++++++++++------------ 1 file changed, 90 insertions(+), 35 deletions(-) diff --git a/src/Modules/Main.lua b/src/Modules/Main.lua index 71f2bdac22..b382e62457 100644 --- a/src/Modules/Main.lua +++ b/src/Modules/Main.lua @@ -824,8 +824,16 @@ function main:OpenOptionsPopup() local startingY = 20 local currentY = startingY local currentX = 0 -- initialized at `0`, only used for two-column layouts - local useTwoColumns = self.screenH < oneColumnHeightReq and self.screenW >= columnWidth * 2 - local popupWidth = useTwoColumns and columnWidth * 2 or columnWidth + + -- Determine layout limits and modes + local useTwoColumns = self.screenH < oneColumnHeightReq and self.screenW >= columnWidth * 2 + local useScrollBar = self.screenH < oneColumnHeightReq and not useTwoColumns + local scrollBarWidth = useScrollBar and 18 or 0 + + local popupWidth = useTwoColumns and columnWidth * 2 or columnWidth + + -- Scrollbar anchor + controls.sectionAnchor = new("Control", { "TOPLEFT", nil, "TOPLEFT" }, { 0, 0, popupWidth, 0 }) -- local func to make a new line with a heightModifier local function nextRow(heightModifier) @@ -837,9 +845,9 @@ function main:OpenOptionsPopup() -- local func to make a new section header local function drawSectionHeader(id, title, omitHorizontalLine) local headerBGColor ={ .6, .6, .6} - controls["section-"..id .. "-bg"] = new("RectangleOutlineControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + 8, currentY, columnWidth - 17, 26 }, headerBGColor, 1) + controls["section-"..id .. "-bg"] = new("RectangleOutlineControl", { "TOPLEFT", controls.sectionAnchor, "TOPLEFT" }, { currentX + scrollBarWidth + 8, currentY, columnWidth - (scrollBarWidth * 2) - 17, 26 }, headerBGColor, 1) nextRow(.2) - controls["section-"..id .. "-label"] = new("LabelControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + columnWidth / 2 - 60, currentY, 0, 16 }, "^7" .. title) + controls["section-"..id .. "-label"] = new("LabelControl", { "TOPLEFT", controls.sectionAnchor, "TOPLEFT" }, { currentX + columnWidth / 2 - 60, currentY, 0, 16 }, "^7" .. title) nextRow(1.5) end @@ -848,7 +856,7 @@ function main:OpenOptionsPopup() drawSectionHeader("app", "Application options") - controls.connectionProtocol = new("DropDownControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 100, 18 }, { + controls.connectionProtocol = new("DropDownControl", { "TOPLEFT", controls.sectionAnchor, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 100, 18 }, { { label = "Auto", protocol = 0 }, { label = "IPv4", protocol = 1 }, { label = "IPv6", protocol = 2 }, @@ -860,7 +868,7 @@ function main:OpenOptionsPopup() controls.connectionProtocol:SelByValue(launch.connectionProtocol, "protocol") nextRow() - controls.proxyType = new("DropDownControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 80, 18 }, { + controls.proxyType = new("DropDownControl", { "TOPLEFT", controls.sectionAnchor, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 80, 18 }, { { label = "HTTP", scheme = "http" }, { label = "SOCKS", scheme = "socks5" }, { label = "SOCKS5H", scheme = "socks5h" }, @@ -875,7 +883,7 @@ function main:OpenOptionsPopup() end nextRow() - controls.dpiScaleOverride = new("DropDownControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 150, 18 }, { + controls.dpiScaleOverride = new("DropDownControl", { "TOPLEFT", controls.sectionAnchor, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 150, 18 }, { { label = "Use system default", percent = 0 }, { label = "100%", percent = 100 }, { label = "125%", percent = 125 }, @@ -896,7 +904,7 @@ function main:OpenOptionsPopup() controls.dpiScaleOverride:SelByValue(self.dpiScaleOverridePercent, "percent") nextRow() - controls.buildPath = new("EditControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 290, 18 }) + controls.buildPath = new("EditControl", { "TOPLEFT", controls.sectionAnchor, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 290, 18 }) controls.buildPathLabel = new("LabelControl", { "RIGHT", controls.buildPath, "LEFT" }, { defaultLabelSpacingPx, 0, 0, 16 }, "^7Build save path:") if self.buildPath ~= self.defaultBuildPath then controls.buildPath:SetText(self.buildPath) @@ -904,7 +912,7 @@ function main:OpenOptionsPopup() controls.buildPath.tooltipText = "Overrides the default save location for builds.\nThe default location is: '"..self.defaultBuildPath.."'" nextRow() - controls.nodePowerTheme = new("DropDownControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 100, 18 }, { + controls.nodePowerTheme = new("DropDownControl", { "TOPLEFT", controls.sectionAnchor, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 100, 18 }, { { label = "Red & Blue", theme = "RED/BLUE" }, { label = "Red & Green", theme = "RED/GREEN" }, { label = "Green & Blue", theme = "GREEN/BLUE" }, @@ -916,7 +924,7 @@ function main:OpenOptionsPopup() controls.nodePowerTheme:SelByValue(self.nodePowerTheme, "theme") nextRow() - controls.colorPositive = new("EditControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 100, 18 }, tostring(self.colorPositive:gsub('^(^)', '0')), nil, nil, 8, function(buf) + controls.colorPositive = new("EditControl", { "TOPLEFT", controls.sectionAnchor, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 100, 18 }, tostring(self.colorPositive:gsub('^(^)', '0')), nil, nil, 8, function(buf) local match = string.match(buf, "0x%x+") if match and #match == 8 then updateColorCode("POSITIVE", buf) @@ -928,7 +936,7 @@ function main:OpenOptionsPopup() "The default value is " .. tostring(defaultColorCodes.POSITIVE:gsub('^(^)', '0')) .. ".\nIf updating while inside a build, please re-load the build after saving." nextRow() - controls.colorNegative = new("EditControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 100, 18 }, tostring(self.colorNegative:gsub('^(^)', '0')), nil, nil, 8, function(buf) + controls.colorNegative = new("EditControl", { "TOPLEFT", controls.sectionAnchor, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 100, 18 }, tostring(self.colorNegative:gsub('^(^)', '0')), nil, nil, 8, function(buf) local match = string.match(buf, "0x%x+") if match and #match == 8 then updateColorCode("NEGATIVE", buf) @@ -941,7 +949,7 @@ function main:OpenOptionsPopup() nextRow() - controls.colorHighlight = new("EditControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 100, 18 }, tostring(self.colorHighlight:gsub('^(^)', '0')), nil, nil, 8, function(buf) + controls.colorHighlight = new("EditControl", { "TOPLEFT", controls.sectionAnchor, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 100, 18 }, tostring(self.colorHighlight:gsub('^(^)', '0')), nil, nil, 8, function(buf) local match = string.match(buf, "0x%x+") if match and #match == 8 then updateColorCode("HIGHLIGHT", buf) @@ -953,33 +961,33 @@ function main:OpenOptionsPopup() "The default value is " .. tostring(defaultColorCodes.HIGHLIGHT:gsub('^(^)', '0')) .."\nIf updating while inside a build, please re-load the build after saving." nextRow() - controls.betaTest = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Opt-in to weekly beta test builds:", function(state) + controls.betaTest = new("CheckBoxControl", { "TOPLEFT", controls.sectionAnchor, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Opt-in to weekly beta test builds:", function(state) self.betaTest = state end) nextRow() - controls.edgeSearchHighlight = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20}, "^7Show search circles at viewport edge", function(state) + controls.edgeSearchHighlight = new("CheckBoxControl", { "TOPLEFT", controls.sectionAnchor, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20}, "^7Show search circles at viewport edge", function(state) self.edgeSearchHighlight = state end) nextRow() - controls.showPublicBuilds = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Show Latest/Trending builds:", function(state) + controls.showPublicBuilds = new("CheckBoxControl", { "TOPLEFT", controls.sectionAnchor, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Show Latest/Trending builds:", function(state) self.showPublicBuilds = state end) nextRow() - controls.showFlavourText = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Styled Tooltips with Flavour Text:", function(state) + controls.showFlavourText = new("CheckBoxControl", { "TOPLEFT", controls.sectionAnchor, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Styled Tooltips with Flavour Text:", function(state) self.showFlavourText = state end) controls.showFlavourText.tooltipText = "If updating while inside a build, please re-load the build after saving." nextRow() - controls.showAnimations = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Show Animations:", function(state) + controls.showAnimations = new("CheckBoxControl", { "TOPLEFT", controls.sectionAnchor, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Show Animations:", function(state) self.showAnimations = state end) nextRow() - controls.showAllItemAffixes = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Show all item affixes sliders:", function(state) + controls.showAllItemAffixes = new("CheckBoxControl", { "TOPLEFT", controls.sectionAnchor, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Show all item affixes sliders:", function(state) self.showAllItemAffixes = state end) controls.showAllItemAffixes.tooltipText = "Display all item affix slots as a stacked list instead of hiding them in dropdowns" @@ -997,44 +1005,44 @@ function main:OpenOptionsPopup() -- Build-related Option Section starts drawSectionHeader("build", "Build-related options") - controls.showThousandsSeparators = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT"}, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Show thousands separators:", function(state) + controls.showThousandsSeparators = new("CheckBoxControl", { "TOPLEFT", controls.sectionAnchor, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Show thousands separators:", function(state) self.showThousandsSeparators = state end) controls.showThousandsSeparators.state = self.showThousandsSeparators nextRow() - controls.thousandsSeparator = new("EditControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 30, 20 }, self.thousandsSeparator, nil, "%w", 1, function(buf) + controls.thousandsSeparator = new("EditControl", { "TOPLEFT", controls.sectionAnchor, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 30, 20 }, self.thousandsSeparator, nil, "%w", 1, function(buf) self.thousandsSeparator = buf end) controls.thousandsSeparatorLabel = new("LabelControl", { "RIGHT", controls.thousandsSeparator, "LEFT" }, { defaultLabelSpacingPx, 0, 92, 16 }, "^7Thousands separator:") nextRow() - controls.decimalSeparator = new("EditControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 30, 20 }, self.decimalSeparator, nil, "%w", 1, function(buf) + controls.decimalSeparator = new("EditControl", { "TOPLEFT", controls.sectionAnchor, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 30, 20 }, self.decimalSeparator, nil, "%w", 1, function(buf) self.decimalSeparator = buf end) controls.decimalSeparatorLabel = new("LabelControl", { "RIGHT", controls.decimalSeparator, "LEFT" }, { defaultLabelSpacingPx, 0, 92, 16 }, "^7Decimal separator:") nextRow() - controls.titlebarName = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Show build name in window title:", function(state) + controls.titlebarName = new("CheckBoxControl", { "TOPLEFT", controls.sectionAnchor, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Show build name in window title:", function(state) self.showTitlebarName = state end) nextRow() - controls.defaultGemQuality = new("EditControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 80, 20 }, self.defaultGemQuality, nil, "%D", 2, function(gemQuality) + controls.defaultGemQuality = new("EditControl", { "TOPLEFT", controls.sectionAnchor, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 80, 20 }, self.defaultGemQuality, nil, "%D", 2, function(gemQuality) self.defaultGemQuality = m_min(tonumber(gemQuality) or 0, 23) end) controls.defaultGemQuality.tooltipText = "Set the default quality that can be overwritten by build-related quality settings in the skill panel." controls.defaultGemQualityLabel = new("LabelControl", { "RIGHT", controls.defaultGemQuality, "LEFT" }, { defaultLabelSpacingPx, 0, 0, 16 }, "^7Default gem quality:") nextRow() - controls.defaultCharLevel = new("EditControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 80, 20 }, self.defaultCharLevel, nil, "%D", 3, function(charLevel) + controls.defaultCharLevel = new("EditControl", { "TOPLEFT", controls.sectionAnchor, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 80, 20 }, self.defaultCharLevel, nil, "%D", 3, function(charLevel) self.defaultCharLevel = m_min(m_max(tonumber(charLevel) or 1, 1), 100) end) controls.defaultCharLevel.tooltipText = "Set the default level of your builds. If this is higher than 1, manual level mode will be enabled by default in new builds." controls.defaultCharLevelLabel = new("LabelControl", { "RIGHT", controls.defaultCharLevel, "LEFT" }, { defaultLabelSpacingPx, 0, 0, 16 }, "^7Default character level:") nextRow() - controls.defaultItemAffixQualitySlider = new("SliderControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 200, 20 }, function(value) + controls.defaultItemAffixQualitySlider = new("SliderControl", { "TOPLEFT", controls.sectionAnchor, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 200, 20 }, function(value) self.defaultItemAffixQuality = round(value, 2) controls.defaultItemAffixQualityValue.label = (self.defaultItemAffixQuality * 100) .. "%" end) @@ -1044,33 +1052,33 @@ function main:OpenOptionsPopup() controls.defaultItemAffixQualityValue.label = (self.defaultItemAffixQuality * 100) .. "%" nextRow() - controls.showWarnings = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Show build warnings:", function(state) + controls.showWarnings = new("CheckBoxControl", { "TOPLEFT", controls.sectionAnchor, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Show build warnings:", function(state) self.showWarnings = state end) controls.showWarnings.state = self.showWarnings nextRow() - controls.slotOnlyTooltips = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Show tooltips only for affected slots:", function(state) + controls.slotOnlyTooltips = new("CheckBoxControl", { "TOPLEFT", controls.sectionAnchor, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Show tooltips only for affected slots:", function(state) self.slotOnlyTooltips = state end, "Shows comparisons in tooltips only for the slot you are currently placing the item in, instead of all slots.") controls.slotOnlyTooltips.state = self.slotOnlyTooltips nextRow() - controls.migrateEldritchImplicits = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Copy Eldritch Implicits onto Display Item:", function(state) + controls.migrateEldritchImplicits = new("CheckBoxControl", { "TOPLEFT", controls.sectionAnchor, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Copy Eldritch Implicits onto Display Item:", function(state) self.migrateEldritchImplicits = state end) controls.migrateEldritchImplicits.tooltipText = "Apply Eldritch Implicits from current gear when comparing new gear, given the new item doesn't have any influence" controls.migrateEldritchImplicits.state = self.migrateEldritchImplicits nextRow() - controls.notSupportedModTooltips = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Show tooltip for unsupported mods :", function(state) + controls.notSupportedModTooltips = new("CheckBoxControl", { "TOPLEFT", controls.sectionAnchor, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Show tooltip for unsupported mods :", function(state) self.notSupportedModTooltips = state end) controls.notSupportedModTooltips.tooltipText = "Show ^8(Not supported in PoB yet) ^7next to unsupported mods" controls.notSupportedModTooltips.state = self.notSupportedModTooltips nextRow() - controls.invertSliderScrollDirection = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Invert slider scroll direction:", function(state) + controls.invertSliderScrollDirection = new("CheckBoxControl", { "TOPLEFT", controls.sectionAnchor, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Invert slider scroll direction:", function(state) self.invertSliderScrollDirection = state end) controls.invertSliderScrollDirection.tooltipText = "Default scroll direction is:\nScroll Up = Move right\nScroll Down = Move left" @@ -1078,7 +1086,7 @@ function main:OpenOptionsPopup() if launch.devMode then nextRow() - controls.disableDevAutoSave = new("CheckBoxControl", { "TOPLEFT", nil, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Disable Dev AutoSave:", function(state) + controls.disableDevAutoSave = new("CheckBoxControl", { "TOPLEFT", controls.sectionAnchor, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 20 }, "^7Disable Dev AutoSave:", function(state) self.disableDevAutoSave = state end) controls.disableDevAutoSave.tooltipText = "Do not Autosave builds while on Dev branch" @@ -1123,7 +1131,8 @@ function main:OpenOptionsPopup() -- last line with buttons has more spacing nextRow(1.5) - controls.save = new("ButtonControl", nil, {-45, currentY, 80, 20}, "Save", function() + -- lock the Save/Cancel buttons to the bottom so they don't scroll away + controls.save = new("ButtonControl", { "BOTTOM", nil, "BOTTOM" }, {-45, -10, 80, 20}, "Save", function() launch.connectionProtocol = tonumber(self.connectionProtocol) if controls.proxyURL.buf:match("%w") then launch.proxyURL = controls.proxyType.list[controls.proxyType.selIndex].scheme .. "://" .. controls.proxyURL.buf @@ -1148,7 +1157,7 @@ function main:OpenOptionsPopup() main:ClosePopup() main:SaveSettings() end) - controls.cancel = new("ButtonControl", nil, {45, currentY, 80, 20}, "Cancel", function() + controls.cancel = new("ButtonControl", { "BOTTOM", nil, "BOTTOM" }, {45, -10, 80, 20}, "Cancel", function() self.nodePowerTheme = initialNodePowerTheme self.colorPositive = initialColorPositive updateColorCode("POSITIVE", self.colorPositive) @@ -1179,8 +1188,54 @@ function main:OpenOptionsPopup() SetDPIScaleOverridePercent(self.dpiScaleOverridePercent) main:ClosePopup() end) - nextRow(1.5) - self:OpenPopup(popupWidth, currentY, "Options", controls, "save", nil, "cancel") + + local popupHeight = useScrollBar and (self.screenH - 20) or currentY + 30 + + if useScrollBar then + controls.scrollBar = new("ScrollBarControl", {"TOPRIGHT", nil, "TOPRIGHT"}, {-2, 25, scrollBarWidth, popupHeight - 65}, 50, "VERTICAL", true) + controls.scrollBar:SetContentDimension(currentY, popupHeight - 65) + end + + local function scrollBarFunc() + if useScrollBar then + controls.sectionAnchor.y = -controls.scrollBar.offset + end + end + + local popup = self:OpenPopup(popupWidth, popupHeight, "Options", controls, "save", nil, "cancel", useScrollBar and scrollBarFunc or nil) + + local originalDrawControls = popup.DrawControls + + -- Modify draw controls to "clip" elements in case the scrollbar is used + popup.DrawControls = function(self, viewPort) + if not useScrollBar then + return originalDrawControls(self, viewPort) + end + -- define clipping area + local x, y = self:GetPos() + local width, height = self:GetSize() + local clipY_top = y + 20 -- just below title + local clipY_bottom = y + height - 40 -- just above buttons + + for id, control in pairs(self.controls) do + -- always draw static UI elements + if control == controls.scrollBar or control == controls.save or control == controls.cancel then + if control:IsShown() and control.Draw then + control:Draw(viewPort, (self.selControl and self.selControl.hasFocus and self.selControl ~= control) or nil) + end + else + -- hide elements outside clipping area + if control:IsShown() and control.Draw then + local cx, cy = control:GetPos() + local cw, ch = control:GetSize() + + if cy >= clipY_top and (cy + ch) <= clipY_bottom then + control:Draw(viewPort, (self.selControl and self.selControl.hasFocus and self.selControl ~= control) or nil) + end + end + end + end + end end function main:SetManifestBranch(branchName) From adef8ec9fdbef0f32b323e38de40a05f961643f0 Mon Sep 17 00:00:00 2001 From: majochem <77203255+majochem@users.noreply.github.com> Date: Wed, 22 Apr 2026 16:51:29 +0200 Subject: [PATCH 3/6] Preserve initial state when re-opening / resizing Because changing UI scale values causes the popup to re-open, it would previously overwrite the "initial" values. So changing a setting and then pressing "Cancel" would not actually discard the changes, but save them instead. The `savedState` can now be passed to `OpenOptionsPopup()` in order to have the initial values persist and be restored upon cancellation. --- src/Modules/Main.lua | 109 +++++++++++++++++++++++-------------------- 1 file changed, 58 insertions(+), 51 deletions(-) diff --git a/src/Modules/Main.lua b/src/Modules/Main.lua index b382e62457..9613004d22 100644 --- a/src/Modules/Main.lua +++ b/src/Modules/Main.lua @@ -814,9 +814,40 @@ function main:ChangeUserPath(newUserPath, ignoreBuild) self:LoadSettings(ignoreBuild) self:LoadSharedItems() end - -function main:OpenOptionsPopup() +--- Opens the popup for the "Options" menu +--- @param savedState table|nil optional passing of saved values, in case of reopening `{nodePowerTheme, colorPositive, ...}` +function main:OpenOptionsPopup(savedState) local controls = { } + + -- Check for `savedState` or assign initial values + -- NOTE: update both this and the `controls.cancel` section below, when adding new options + savedState = savedState or { + nodePowerTheme = self.nodePowerTheme, + colorPositive = self.colorPositive, + colorNegative = self.colorNegative, + colorHighlight = self.colorHighlight, + showThousandsSeparators = self.showThousandsSeparators, + thousandsSeparator = self.thousandsSeparator, + decimalSeparator = self.decimalSeparator, + showTitlebarName = self.showTitlebarName, + betaTest = self.betaTest, + edgeSearchHighlight = self.edgeSearchHighlight, + defaultGemQuality = self.defaultGemQuality or 0, + defaultCharLevel = self.defaultCharLevel or 1, + defaultItemAffixQuality = self.defaultItemAffixQuality or 0.5, + showWarnings = self.showWarnings, + slotOnlyTooltips = self.slotOnlyTooltips, + migrateEldritchImplicits = self.migrateEldritchImplicits, + notSupportedModTooltips = self.notSupportedModTooltips, + invertSliderScrollDirection = self.invertSliderScrollDirection, + disableDevAutoSave = self.disableDevAutoSave, + showPublicBuilds = self.showPublicBuilds, + showFlavourText = self.showFlavourText, + showAnimations = self.showAnimations, + showAllItemAffixes = self.showAllItemAffixes, + dpiScaleOverridePercent = self.dpiScaleOverridePercent + } + -- NOTE: Height needs to be adjusted if more menu options are added local oneColumnHeightReq = 850 -- Min height required to not split menu into two columns local columnWidth = 600 @@ -897,7 +928,7 @@ function main:OpenOptionsPopup() SetDPIScaleOverridePercent(value.percent) self.screenW, self.screenH = GetVirtualScreenSize() -- refresh screen size immediately self:ClosePopup() - self:OpenOptionsPopup() + self:OpenOptionsPopup(savedState) end) controls.dpiScaleOverrideLabel = new("LabelControl", { "RIGHT", controls.dpiScaleOverride, "LEFT" }, { defaultLabelSpacingPx, 0, 0, 16 }, "^7UI scaling override:") controls.dpiScaleOverride.tooltipText = "Overrides Windows DPI scaling inside Path of Building.\nChoose a percentage between 100% and 250% or revert to the system default." @@ -1100,30 +1131,6 @@ function main:OpenOptionsPopup() controls.showFlavourText.state = self.showFlavourText controls.showAnimations.state = self.showAnimations controls.showAllItemAffixes.state = self.showAllItemAffixes - local initialNodePowerTheme = self.nodePowerTheme - local initialColorPositive = self.colorPositive - local initialColorNegative = self.colorNegative - local initialColorHighlight = self.colorHighlight - local initialThousandsSeparatorDisplay = self.showThousandsSeparators - local initialTitlebarName = self.showTitlebarName - local initialThousandsSeparator = self.thousandsSeparator - local initialDecimalSeparator = self.decimalSeparator - local initialBetaTest = self.betaTest - local initialEdgeSearchHighlight = self.edgeSearchHighlight - local initialDefaultGemQuality = self.defaultGemQuality or 0 - local initialDefaultCharLevel = self.defaultCharLevel or 1 - local initialDefaultItemAffixQuality = self.defaultItemAffixQuality or 0.5 - local initialShowWarnings = self.showWarnings - local initialSlotOnlyTooltips = self.slotOnlyTooltips - local initialMigrateEldritchImplicits = self.migrateEldritchImplicits - local initialNotSupportedModTooltips = self.notSupportedModTooltips - local initialInvertSliderScrollDirection = self.invertSliderScrollDirection - local initialDisableDevAutoSave = self.disableDevAutoSave - local initialShowPublicBuilds = self.showPublicBuilds - local initialShowFlavourText = self.showFlavourText - local initialShowAnimations = self.showAnimations - local initialShowAllItemAffixes = self.showAllItemAffixes - local initialDpiScaleOverridePercent = self.dpiScaleOverridePercent -- Adjust height in case of two-column layout currentY = m_max(leftColumnMaxY, currentY) @@ -1158,33 +1165,33 @@ function main:OpenOptionsPopup() main:SaveSettings() end) controls.cancel = new("ButtonControl", { "BOTTOM", nil, "BOTTOM" }, {45, -10, 80, 20}, "Cancel", function() - self.nodePowerTheme = initialNodePowerTheme - self.colorPositive = initialColorPositive + self.nodePowerTheme = savedState.nodePowerTheme + self.colorPositive = savedState.colorPositive updateColorCode("POSITIVE", self.colorPositive) - self.colorNegative = initialColorNegative + self.colorNegative = savedState.colorNegative updateColorCode("NEGATIVE", self.colorNegative) - self.colorHighlight = initialColorHighlight + self.colorHighlight = savedState.colorHighlight updateColorCode("HIGHLIGHT", self.colorHighlight) - self.showThousandsSeparators = initialThousandsSeparatorDisplay - self.thousandsSeparator = initialThousandsSeparator - self.decimalSeparator = initialDecimalSeparator - self.showTitlebarName = initialTitlebarName - self.betaTest = initialBetaTest - self.edgeSearchHighlight = initialEdgeSearchHighlight - self.defaultGemQuality = initialDefaultGemQuality - self.defaultCharLevel = initialDefaultCharLevel - self.defaultItemAffixQuality = initialDefaultItemAffixQuality - self.showWarnings = initialShowWarnings - self.slotOnlyTooltips = initialSlotOnlyTooltips - self.migrateEldritchImplicits = initialMigrateEldritchImplicits - self.notSupportedModTooltips = initialNotSupportedModTooltips - self.invertSliderScrollDirection = initialInvertSliderScrollDirection - self.disableDevAutoSave = initialDisableDevAutoSave - self.showPublicBuilds = initialShowPublicBuilds - self.showFlavourText = initialShowFlavourText - self.showAnimations = initialShowAnimations - self.showAllItemAffixes = initialShowAllItemAffixes - self.dpiScaleOverridePercent = initialDpiScaleOverridePercent + self.showThousandsSeparators = savedState.chousandsSeparatorDisplay + self.thousandsSeparator = savedState.thousandsSeparator + self.decimalSeparator = savedState.decimalSeparator + self.showTitlebarName = savedState.titlebarName + self.betaTest = savedState.betaTest + self.edgeSearchHighlight = savedState.edgeSearchHighlight + self.defaultGemQuality = savedState.defaultGemQuality + self.defaultCharLevel = savedState.defaultCharLevel + self.defaultItemAffixQuality = savedState.defaultItemAffixQuality + self.showWarnings = savedState.showWarnings + self.slotOnlyTooltips = savedState.slotOnlyTooltips + self.migrateEldritchImplicits = savedState.migrateEldritchImplicits + self.notSupportedModTooltips = savedState.notSupportedModTooltips + self.invertSliderScrollDirection = savedState.invertSliderScrollDirection + self.disableDevAutoSave = savedState.disableDevAutoSave + self.showPublicBuilds = savedState.showPublicBuilds + self.showFlavourText = savedState.showFlavourText + self.showAnimations = savedState.showAnimations + self.showAllItemAffixes = savedState.showAllItemAffixes + self.dpiScaleOverridePercent = savedState.dpiScaleOverridePercent SetDPIScaleOverridePercent(self.dpiScaleOverridePercent) main:ClosePopup() end) From 7e0df0ccd6ae05468826716aabffed85df70d06f Mon Sep 17 00:00:00 2001 From: majochem <77203255+majochem@users.noreply.github.com> Date: Wed, 22 Apr 2026 17:06:03 +0200 Subject: [PATCH 4/6] Fix variable name mismatch --- src/Modules/Main.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Modules/Main.lua b/src/Modules/Main.lua index 9613004d22..ab68c6efb8 100644 --- a/src/Modules/Main.lua +++ b/src/Modules/Main.lua @@ -1172,10 +1172,10 @@ function main:OpenOptionsPopup(savedState) updateColorCode("NEGATIVE", self.colorNegative) self.colorHighlight = savedState.colorHighlight updateColorCode("HIGHLIGHT", self.colorHighlight) - self.showThousandsSeparators = savedState.chousandsSeparatorDisplay + self.showThousandsSeparators = savedState.showThousandsSeparators self.thousandsSeparator = savedState.thousandsSeparator self.decimalSeparator = savedState.decimalSeparator - self.showTitlebarName = savedState.titlebarName + self.showTitlebarName = savedState.showTitlebarName self.betaTest = savedState.betaTest self.edgeSearchHighlight = savedState.edgeSearchHighlight self.defaultGemQuality = savedState.defaultGemQuality From b22ca6a91762da716a3d2bb00aa174835ae2c1b7 Mon Sep 17 00:00:00 2001 From: majochem <77203255+majochem@users.noreply.github.com> Date: Thu, 23 Apr 2026 09:33:32 +0200 Subject: [PATCH 5/6] Fix indentation atrocities --- src/Modules/Main.lua | 112 +++++++++++++++++++++---------------------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/src/Modules/Main.lua b/src/Modules/Main.lua index ab68c6efb8..417e32d0e0 100644 --- a/src/Modules/Main.lua +++ b/src/Modules/Main.lua @@ -822,31 +822,31 @@ function main:OpenOptionsPopup(savedState) -- Check for `savedState` or assign initial values -- NOTE: update both this and the `controls.cancel` section below, when adding new options savedState = savedState or { - nodePowerTheme = self.nodePowerTheme, - colorPositive = self.colorPositive, - colorNegative = self.colorNegative, - colorHighlight = self.colorHighlight, - showThousandsSeparators = self.showThousandsSeparators, - thousandsSeparator = self.thousandsSeparator, - decimalSeparator = self.decimalSeparator, - showTitlebarName = self.showTitlebarName, - betaTest = self.betaTest, - edgeSearchHighlight = self.edgeSearchHighlight, - defaultGemQuality = self.defaultGemQuality or 0, - defaultCharLevel = self.defaultCharLevel or 1, - defaultItemAffixQuality = self.defaultItemAffixQuality or 0.5, - showWarnings = self.showWarnings, - slotOnlyTooltips = self.slotOnlyTooltips, - migrateEldritchImplicits = self.migrateEldritchImplicits, - notSupportedModTooltips = self.notSupportedModTooltips, - invertSliderScrollDirection = self.invertSliderScrollDirection, - disableDevAutoSave = self.disableDevAutoSave, - showPublicBuilds = self.showPublicBuilds, - showFlavourText = self.showFlavourText, - showAnimations = self.showAnimations, - showAllItemAffixes = self.showAllItemAffixes, - dpiScaleOverridePercent = self.dpiScaleOverridePercent - } + nodePowerTheme = self.nodePowerTheme, + colorPositive = self.colorPositive, + colorNegative = self.colorNegative, + colorHighlight = self.colorHighlight, + showThousandsSeparators = self.showThousandsSeparators, + thousandsSeparator = self.thousandsSeparator, + decimalSeparator = self.decimalSeparator, + showTitlebarName = self.showTitlebarName, + betaTest = self.betaTest, + edgeSearchHighlight = self.edgeSearchHighlight, + defaultGemQuality = self.defaultGemQuality or 0, + defaultCharLevel = self.defaultCharLevel or 1, + defaultItemAffixQuality = self.defaultItemAffixQuality or 0.5, + showWarnings = self.showWarnings, + slotOnlyTooltips = self.slotOnlyTooltips, + migrateEldritchImplicits = self.migrateEldritchImplicits, + notSupportedModTooltips = self.notSupportedModTooltips, + invertSliderScrollDirection = self.invertSliderScrollDirection, + disableDevAutoSave = self.disableDevAutoSave, + showPublicBuilds = self.showPublicBuilds, + showFlavourText = self.showFlavourText, + showAnimations = self.showAnimations, + showAllItemAffixes = self.showAllItemAffixes, + dpiScaleOverridePercent = self.dpiScaleOverridePercent + } -- NOTE: Height needs to be adjusted if more menu options are added local oneColumnHeightReq = 850 -- Min height required to not split menu into two columns @@ -857,11 +857,11 @@ function main:OpenOptionsPopup(savedState) local currentX = 0 -- initialized at `0`, only used for two-column layouts -- Determine layout limits and modes - local useTwoColumns = self.screenH < oneColumnHeightReq and self.screenW >= columnWidth * 2 - local useScrollBar = self.screenH < oneColumnHeightReq and not useTwoColumns + local useTwoColumns = self.screenH < oneColumnHeightReq and self.screenW >= columnWidth * 2 + local useScrollBar = self.screenH < oneColumnHeightReq and not useTwoColumns local scrollBarWidth = useScrollBar and 18 or 0 - - local popupWidth = useTwoColumns and columnWidth * 2 or columnWidth + + local popupWidth = useTwoColumns and columnWidth * 2 or columnWidth -- Scrollbar anchor controls.sectionAnchor = new("Control", { "TOPLEFT", nil, "TOPLEFT" }, { 0, 0, popupWidth, 0 }) @@ -979,7 +979,7 @@ function main:OpenOptionsPopup(savedState) "The default value is " .. tostring(defaultColorCodes.NEGATIVE:gsub('^(^)', '0')) .. ".\nIf updating while inside a build, please re-load the build after saving." nextRow() - + controls.colorHighlight = new("EditControl", { "TOPLEFT", controls.sectionAnchor, "TOPLEFT" }, { currentX + defaultLabelPlacementX, currentY, 100, 18 }, tostring(self.colorHighlight:gsub('^(^)', '0')), nil, nil, 8, function(buf) local match = string.match(buf, "0x%x+") if match and #match == 8 then @@ -1215,34 +1215,34 @@ function main:OpenOptionsPopup(savedState) -- Modify draw controls to "clip" elements in case the scrollbar is used popup.DrawControls = function(self, viewPort) - if not useScrollBar then - return originalDrawControls(self, viewPort) - end + if not useScrollBar then + return originalDrawControls(self, viewPort) + end -- define clipping area local x, y = self:GetPos() local width, height = self:GetSize() - local clipY_top = y + 20 -- just below title - local clipY_bottom = y + height - 40 -- just above buttons - - for id, control in pairs(self.controls) do - -- always draw static UI elements - if control == controls.scrollBar or control == controls.save or control == controls.cancel then - if control:IsShown() and control.Draw then - control:Draw(viewPort, (self.selControl and self.selControl.hasFocus and self.selControl ~= control) or nil) - end - else - -- hide elements outside clipping area - if control:IsShown() and control.Draw then - local cx, cy = control:GetPos() - local cw, ch = control:GetSize() - - if cy >= clipY_top and (cy + ch) <= clipY_bottom then - control:Draw(viewPort, (self.selControl and self.selControl.hasFocus and self.selControl ~= control) or nil) - end - end - end - end - end + local clipY_top = y + 20 -- just below title + local clipY_bottom = y + height - 40 -- just above buttons + + for id, control in pairs(self.controls) do + -- always draw static UI elements + if control == controls.scrollBar or control == controls.save or control == controls.cancel then + if control:IsShown() and control.Draw then + control:Draw(viewPort, (self.selControl and self.selControl.hasFocus and self.selControl ~= control) or nil) + end + else + -- hide elements outside clipping area + if control:IsShown() and control.Draw then + local cx, cy = control:GetPos() + local cw, ch = control:GetSize() + + if cy >= clipY_top and (cy + ch) <= clipY_bottom then + control:Draw(viewPort, (self.selControl and self.selControl.hasFocus and self.selControl ~= control) or nil) + end + end + end + end + end end function main:SetManifestBranch(branchName) @@ -1357,7 +1357,7 @@ function main:OpenAboutPopup(helpSectionIndex) else local Lines = self:WrapString(line, textSize, popupWidth - 135) for i, line2 in ipairs(Lines) do - t_insert(helpList, { height = textSize, (dev and "^x8888FF" or "^7")..(i > 1 and " " or "")..line2 }) + t_insert(helpList, { height = textSize, (dev and "^x8888FF" or "^7")..(i > 1 and " " or "")..line2 }) end end end From 885778c7fdd879e46405ef50568a81f9c76996b9 Mon Sep 17 00:00:00 2001 From: majochem <77203255+majochem@users.noreply.github.com> Date: Thu, 23 Apr 2026 09:51:47 +0200 Subject: [PATCH 6/6] Revert one actually correct quadruple " " --- src/Modules/Main.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Modules/Main.lua b/src/Modules/Main.lua index 417e32d0e0..a4d66c9556 100644 --- a/src/Modules/Main.lua +++ b/src/Modules/Main.lua @@ -1357,7 +1357,7 @@ function main:OpenAboutPopup(helpSectionIndex) else local Lines = self:WrapString(line, textSize, popupWidth - 135) for i, line2 in ipairs(Lines) do - t_insert(helpList, { height = textSize, (dev and "^x8888FF" or "^7")..(i > 1 and " " or "")..line2 }) + t_insert(helpList, { height = textSize, (dev and "^x8888FF" or "^7")..(i > 1 and " " or "")..line2 }) end end end