From a376afb2d72c0f75930d03930edf27c04a476144 Mon Sep 17 00:00:00 2001 From: Brues <5278969+brues-code@users.noreply.github.com> Date: Mon, 2 Nov 2026 12:38:21 -0600 Subject: [PATCH] UI scale: size borders from the scale the game applies, mirror its options GetPerfectPixel divided by the uiScale option, or by 1 while useUiScale was off, though the game's automatic scale is never under 0.9, so default borders came out about a tenth of a pixel thin. The game scales UIParent only after add-ons and saved variables load, so the pixel now comes from GetPhysicalScreenSize and the scale the game is about to apply, worked out from the same options and rules as OnDisplaySizeChanged. UI_SCALE_CHANGED offers a reload once when the pixel size changes. The preset dropdown and the pixelperfect module go: its presets were pixel perfect only at 1080p and its restore path never ran. The settings show the game's own Use UI-Scale and UI-Scale options, plus a Pixel Perfect UI-Scale checkbox (one UI unit per screen pixel, kept through window resizes) that overrides them. The pixel perfect border and HiDPI options go too; border sizes now count real pixels. CVAR settings rows write their option through their own config instead of this:GetParent(), which a dropdown's menu button is not. The first-run slider stops at 0.64, below which the game drops uiScale for its automatic scale. --- api/api.lua | 61 ++++++++++++++++++++++++++++------------ api/config.lua | 2 -- init/modules.xml | 1 - modules/firstrun.lua | 39 +++++++++---------------- modules/gui.lua | 31 ++++++++++++-------- modules/pixelperfect.lua | 43 ---------------------------- pfUI.lua | 4 +++ 7 files changed, 79 insertions(+), 102 deletions(-) delete mode 100644 modules/pixelperfect.lua diff --git a/api/api.lua b/api/api.lua index 0405b00d..e2850c1a 100644 --- a/api/api.lua +++ b/api/api.lua @@ -992,28 +992,53 @@ function pfUI.api.GetBorderSize(pref) end end +-- The scale UIParent ends up with: one UI unit per screen pixel when pixel +-- perfect, else the game's own. The game applies its scale only after the +-- add-ons and their saved variables are loaded (CGGameUI::Initialize ends in +-- OnDisplaySizeChanged), so at load time it is worked out from the same +-- options and rules: uiScale while useUiScale is on and it is at least 0.64, +-- else 768 over the window height kept within 0.9 and 1. Windows narrower +-- than 4:3 cap either at 3/4 of their aspect. +local function GetUIScale() + if pfUI_config.global.pixelperfect == "1" then return PixelUtil.GetPixelToUIUnitFactor() end + + local width, height = GetPhysicalScreenSize() + local aspect = GetCVar("widescreen") == "1" and width / height or 4/3 + local function fit(scale) + return aspect < 4/3 and math.min(scale, aspect * .75) or scale + end + + if GetCVar("useUiScale") == "1" then + local scale = fit(tonumber(GetCVar("uiScale"))) + if scale >= .64 then return scale end + end + + return math.max(fit(height > 768 and 768 / height or 1), .9) +end + +-- [ UpdateUIScale ] +-- Gives UIParent the pixel perfect scale when enabled, else leaves the game's +-- own. The engine puts its own scale back on every window resize and scale +-- option change, so this runs again on each UI_SCALE_CHANGED. Borders keep +-- the pixel size they were built with, so a change of it offers a reload once. +function pfUI.api.UpdateUIScale() + if pfUI_config.global.pixelperfect == "1" then + UIParent:SetScale(PixelUtil.GetPixelToUIUnitFactor()) + end + + if not pfUI.pixel or pfUI.scaleprompt then return end + if math.abs(PixelUtil.GetPixelToUIUnitFactor() / UIParent:GetScale() - pfUI.pixel) < .0001 then return end + + pfUI.scaleprompt = true + CreateQuestionDialog(T["The UI-Scale has changed. Reload the UI to redraw the borders?"], ReloadUI) +end + -- [ GetPerfectPixel ] --- Returns a number that equals a real pixel on regular scaled frames. --- Respects the current UI-scale and calculates a real pixel based on --- the screen resolution and the 768px sized drawlayer. +-- Returns the size of one physical pixel in UIParent units. function pfUI.api.GetPerfectPixel() if pfUI.pixel then return pfUI.pixel end - if pfUI_config.appearance.border.pixelperfect == "1" then - local scale = GetCVar("useUiScale") == "1" and GetCVar("uiScale") or "1" - local resolution = GetCVar("gxResolution") - local _, _, screenwidth, screenheight = strfind(resolution, "(.+)x(.+)") - - pfUI.pixel = 768 / screenheight / scale - pfUI.pixel = pfUI.pixel > 1 and 1 or pfUI.pixel - - -- autodetect and zoom for HiDPI displays - if pfUI_config.appearance.border.hidpi == "1" then - pfUI.pixel = pfUI.pixel < .5 and pfUI.pixel * 2 or pfUI.pixel - end - else - pfUI.pixel = .7 - end + pfUI.pixel = PixelUtil.GetPixelToUIUnitFactor() / GetUIScale() pfUI.backdrop = { bgFile = "Interface\\BUTTONS\\WHITE8X8", tile = false, tileSize = 0, diff --git a/api/config.lua b/api/config.lua index 8987796c..5ee3ead3 100644 --- a/api/config.lua +++ b/api/config.lua @@ -129,9 +129,7 @@ function pfUI:LoadConfig() pfUI:UpdateConfig("appearance", "border", "color", "0.2,0.2,0.2,1") pfUI:UpdateConfig("appearance", "border", "shadow", "0") pfUI:UpdateConfig("appearance", "border", "shadow_intensity", ".35") - pfUI:UpdateConfig("appearance", "border", "pixelperfect", "1") pfUI:UpdateConfig("appearance", "border", "force_blizz", "0") - pfUI:UpdateConfig("appearance", "border", "hidpi", "1") pfUI:UpdateConfig("appearance", "border", "default", "3") pfUI:UpdateConfig("appearance", "border", "nameplates", "-1") pfUI:UpdateConfig("appearance", "border", "actionbars", "-1") diff --git a/init/modules.xml b/init/modules.xml index febafbdc..bfb2ece6 100644 --- a/init/modules.xml +++ b/init/modules.xml @@ -54,7 +54,6 @@ - diff --git a/modules/firstrun.lua b/modules/firstrun.lua index 072d408e..1f5c46e2 100644 --- a/modules/firstrun.lua +++ b/modules/firstrun.lua @@ -136,17 +136,19 @@ pfUI:RegisterModule("firstrun", function () local f = CreateFirstRunPage() f.text:SetText(T["A new installation of |cff33ffccpf|rUI ships with 4 prebuilt design profiles. Click below if you wish to load one of these profiles."]) + local function LoadProfile(name) + _G["pfUI_config"] = CopyTable(pfUI_profiles[name]) + pfUI_init.selected_profile = name + pfUI:LoadConfig() + ReloadUI() + end + f.Modern = CreateFrame("Button", nil, f, "UIPanelButtonTemplate") f.Modern:SetSize(120, 20) f.Modern:SetPoint("BOTTOM", -65, 100) f.Modern:SetTextColor(1,1,1) f.Modern:SetText("Modern") - f.Modern:SetScript("OnClick", function() - _G["pfUI_config"] = CopyTable(pfUI_profiles["Modern"]) - pfUI_init.selected_profile = "Modern" - pfUI:LoadConfig() - ReloadUI() - end) + f.Modern:SetScript("OnClick", function() LoadProfile("Modern") end) SkinButton(f.Modern) f.Nostalgia = CreateFrame("Button", nil, f, "UIPanelButtonTemplate") @@ -154,12 +156,7 @@ pfUI:RegisterModule("firstrun", function () f.Nostalgia:SetPoint("BOTTOM", 65, 100) f.Nostalgia:SetTextColor(1,1,1) f.Nostalgia:SetText("Nostalgia") - f.Nostalgia:SetScript("OnClick", function() - _G["pfUI_config"] = CopyTable(pfUI_profiles["Nostalgia"]) - pfUI_init.selected_profile = "Nostalgia" - pfUI:LoadConfig() - ReloadUI() - end) + f.Nostalgia:SetScript("OnClick", function() LoadProfile("Nostalgia") end) SkinButton(f.Nostalgia) f.Legacy = CreateFrame("Button", nil, f, "UIPanelButtonTemplate") @@ -167,12 +164,7 @@ pfUI:RegisterModule("firstrun", function () f.Legacy:SetPoint("BOTTOM", 65, 75) f.Legacy:SetTextColor(1,1,1) f.Legacy:SetText("Legacy") - f.Legacy:SetScript("OnClick", function() - _G["pfUI_config"] = CopyTable(pfUI_profiles["Legacy"]) - pfUI_init.selected_profile = "Legacy" - pfUI:LoadConfig() - ReloadUI() - end) + f.Legacy:SetScript("OnClick", function() LoadProfile("Legacy") end) SkinButton(f.Legacy) f.Slim = CreateFrame("Button", nil, f, "UIPanelButtonTemplate") @@ -180,12 +172,7 @@ pfUI:RegisterModule("firstrun", function () f.Slim:SetPoint("BOTTOM", -65, 75) f.Slim:SetTextColor(1,1,1) f.Slim:SetText("Slim") - f.Slim:SetScript("OnClick", function() - _G["pfUI_config"] = CopyTable(pfUI_profiles["Slim"]) - pfUI_init.selected_profile = "Slim" - pfUI:LoadConfig() - ReloadUI() - end) + f.Slim:SetScript("OnClick", function() LoadProfile("Slim") end) SkinButton(f.Slim) f.Slider = CreateFrame("Slider", "pfFirstRunWizardScaleSlider", f, "OptionsSliderTemplate") @@ -196,14 +183,14 @@ pfUI:RegisterModule("firstrun", function () f.Slider:SetSize(240, 20) f.Slider:SetPoint("BOTTOM", 0, 50) f.Slider:SetOrientation('HORIZONTAL') - f.Slider:SetMinMaxValues(0.5, 2.0) + -- the game drops a uiScale under 0.64 for its automatic scale at login + f.Slider:SetMinMaxValues(0.64, 2.0) f.Slider:SetValue(UIParent:GetScale()) f.Slider:SetScript("OnMouseUp", function() local scale = round(this:GetValue(),2) SetCVar("uiScale", scale) SetCVar("useUiScale", 1) - UIParent:SetScale(scale) this:SetValue(scale) end) diff --git a/modules/gui.lua b/modules/gui.lua index 77a325b7..03397952 100644 --- a/modules/gui.lua +++ b/modules/gui.lua @@ -130,7 +130,7 @@ pfUI:RegisterModule("gui", function () category = {} category[config] = tostring(GetCVar(config)) ufunc = function() - SetCVar(this:GetParent().config, this:GetParent().category[config]) + SetCVar(config, category[config]) end end @@ -881,14 +881,6 @@ pfUI:RegisterModule("gui", function () "35:35", "40:40", }, - ["scaling"] = { - "0:" .. T["Off"], - "4:" .. T["Huge (PixelPerfect)"], - "5:" .. T["Large"], - "6:" .. T["Medium"], - "7:" .. T["Small"], - "8:" .. T["Tiny (PixelPerfect)"], - }, ["abbrevnum"] = { "0:" .. T["Full Numbers (4250)"], "1:" .. T["Abbreviate 2 Decimals (4.25k)"], @@ -1508,7 +1500,24 @@ pfUI:RegisterModule("gui", function () CreateConfig(nil, T["Settings"], nil, nil, "header") CreateConfig(nil, T["Language"], C.global, "language", "dropdown", pfUI.gui.dropdowns.languages) - CreateConfig(U["pixelperfect"], T["Enable UI-Scale"], C.global, "pixelperfect", "dropdown", pfUI.gui.dropdowns.scaling) + -- the game's own scale options, as in its Video Options; the list keeps + -- a value set there that is none of these steps + CreateConfig(nil, T["Use UI-Scale"], "CVAR", "useUiScale", "checkbox") + CreateConfig(nil, T["UI-Scale"], "CVAR", "uiScale", "dropdown", function() + local current = GetCVar("uiScale") + local values, found = {}, nil + for _, scale in ipairs({ .64, .65, .7, .75, .8, .85, .9, .95, 1, 1.1, 1.2, 1.3, 1.4, 1.5 }) do + if math.abs(scale - tonumber(current)) < .001 then + found = true + table.insert(values, current .. ":" .. scale) + else + table.insert(values, tostring(scale)) + end + end + if not found then table.insert(values, 1, current .. ":" .. round(tonumber(current), 2)) end + return values + end) + CreateConfig(nil, string.format(T["Pixel Perfect UI-Scale: %s (Overrides UI-Scale)"], round(PixelUtil.GetPixelToUIUnitFactor(), 2)), C.global, "pixelperfect", "checkbox") CreateConfig(nil, T["Use Original Game Fonts"], C.global, "font_blizzard", "checkbox") CreateConfig(nil, T["Enable Region Compatible Font"], C.global, "force_region", "checkbox") CreateConfig(nil, T["Standard Text Font"], C.global, "font_default", "dropdown", pfUI.gui.dropdowns.fonts) @@ -1585,8 +1594,6 @@ pfUI:RegisterModule("gui", function () CreateConfig(nil, T["Frame Shadow Intensity"], C.appearance.border, "shadow_intensity", "dropdown", pfUI.gui.dropdowns.uf_debuff_indicator_size) CreateConfig(nil) -- spacer CreateConfig(nil, T["Force Blizzard Borders (|cffffaaaaExperimental|r)"], C.appearance.border, "force_blizz", "checkbox") - CreateConfig(nil, T["Enable Pixel Perfect Borders"], C.appearance.border, "pixelperfect", "checkbox") - CreateConfig(nil, T["Scale Border On HiDPI Displays"], C.appearance.border, "hidpi", "checkbox") CreateConfig(nil) -- spacer CreateConfig(nil, T["Global Border Size"], C.appearance.border, "default", "dropdown", pfUI.gui.dropdowns.border) CreateConfig(nil, T["Action Bar Border Size"], C.appearance.border, "actionbars", "dropdown", pfUI.gui.dropdowns.border) diff --git a/modules/pixelperfect.lua b/modules/pixelperfect.lua deleted file mode 100644 index 872b5d37..00000000 --- a/modules/pixelperfect.lua +++ /dev/null @@ -1,43 +0,0 @@ -pfUI:RegisterModule("pixelperfect", function () - -- pre-calculated min values - local statics = { - [4] = 1.4222222222222, - [5] = 1.1377777777778, - [6] = 0.94814814814815, - [7] = 0.81269841269841, - [8] = 0.71111111111111, - } - - -- pixel perfect - local function pixelperfect() - local conf = tonumber(C.global.pixelperfect) - if conf < 4 then - -- restore gamesettings - local scale = GetCVar("uiScale") - local use = GetCVar("useUiScale") - - if use == 1 then - UIParent:SetScale(tonumber(use)) - else - UIParent:SetScale(.9) - end - else - local scale = conf and statics[conf] or 1 - - SetCVar("uiScale", scale) - SetCVar("useUiScale", 1) - - UIParent:SetScale(scale) - end - end - - -- pixelperfect: native UIScale listener - if tonumber(C.global.pixelperfect) > 0 then - pfUI.events:RegisterFrameEventAndCallback("PLAYER_ENTERING_WORLD", pixelperfect) - pixelperfect() - end - - pfUI.pixelperfect = { - UpdateConfig = pixelperfect - } -end) diff --git a/pfUI.lua b/pfUI.lua index 6b1fe21d..ea9d3d0d 100644 --- a/pfUI.lua +++ b/pfUI.lua @@ -392,6 +392,10 @@ pfUI:SetScript("OnEvent", function() SetupConfigEnvironment() pfUI:UpdateFonts() + -- scale UIParent before the modules size their borders from it + pfUI.api.UpdateUIScale() + pfUI.events:RegisterFrameEventAndCallback("UI_SCALE_CHANGED", pfUI.api.UpdateUIScale) + -- load modules for _, m in pairs(this.modules) do if not ( pfUI_config["disabled"] and pfUI_config["disabled"][m] == "1" ) then