From 662a9837c2bae9835430a30651cac9e258049b0e Mon Sep 17 00:00:00 2001 From: lL1l1 <82986251+lL1l1@users.noreply.github.com> Date: Mon, 20 May 2024 07:07:17 -0700 Subject: [PATCH] Rework the unit tooltips in the unit restrictions menu (#6175) The tooltips now work nicely when UI scaling is enabled. --- changelog/snippets/fix.6175.md | 4 + lua/ui/lobby/UnitsTooltip.lua | 488 +++++++++++++-------------------- 2 files changed, 193 insertions(+), 299 deletions(-) create mode 100644 changelog/snippets/fix.6175.md diff --git a/changelog/snippets/fix.6175.md b/changelog/snippets/fix.6175.md new file mode 100644 index 0000000000..194133fbc2 --- /dev/null +++ b/changelog/snippets/fix.6175.md @@ -0,0 +1,4 @@ +- (#6175) Rework the unit manager's unit tooltips using the Layouter with various fixes: + - Fix UI Scaling for unit tooltips in the unit manager. + - Display buildtime in ticks instead of MM:SS in the unit manager. + - Fix empty names appearing as empty parentheses in the unit manager. diff --git a/lua/ui/lobby/UnitsTooltip.lua b/lua/ui/lobby/UnitsTooltip.lua index 2f3dbf362e..75d7f6ca65 100644 --- a/lua/ui/lobby/UnitsTooltip.lua +++ b/lua/ui/lobby/UnitsTooltip.lua @@ -15,6 +15,8 @@ local Text = import("/lua/maui/text.lua") local TextArea = import("/lua/ui/controls/textarea.lua").TextArea local LayoutHelpers = import("/lua/maui/layouthelpers.lua") +local PixelScaleFactor = LayoutHelpers.GetPixelScaleFactor() +local Layouter = LayoutHelpers.ReusedLayoutFor local UnitsAnalyzer = import("/lua/ui/lobby/unitsanalyzer.lua") local UnitDescriptions = import("/lua/ui/help/unitdescription.lua").Description @@ -45,14 +47,6 @@ local colorMod = 'FFCB59F7' -- --FFCB59F7 local debugging = false --- convert time in ticks to a string with MM:SS format -local function stringTime(time) - time = time / 60 - local timeMM = math.floor(time / 60) - local timeSS = math.floor(math.mod(time, 60)) - return string.format("%02d:%02d", timeMM, timeSS) -end - -- initializes value to zero if it is nil local function init(value) return value > 1 and value or 0 @@ -69,72 +63,48 @@ function Create(parent, bp) Destroy() - local title = '' - - if bp.Description then - title = ' ' .. bp.Tech .. ' ' .. LOCF(bp.Description) - elseif bp.Name then - title = ' ' .. LOCF(bp.Name) - end - if bp.General.UnitName and not bp.CategoriesHash['SUBCOMMANDER'] then - title = title .. ' (' .. LOCF(bp.General.UnitName) .. ')' - end - - tooltipUI = Bitmap(parent) - tooltipUI:SetSolidColor(UIUtil.tooltipBorderColor) - tooltipUI.Depth:Set(function() return parent.Depth() + 10000 end) - LayoutHelpers.SetDimensions(tooltipUI, tooltipWidth, tooltipHeight) + -- Acts a border for the entire tooltip when the other backgrounds on top fill it in. + tooltipUI = Layouter(Bitmap(parent)):Color(UIUtil.tooltipBorderColor):Over(parent, 10000):Width(tooltipWidth):End() - tooltipUI.title = UIUtil.CreateText(tooltipUI, title, fontTextSize, UIUtil.bodyFont) - LayoutHelpers.AtLeftTopIn(tooltipUI.title, tooltipUI, 2, 2) + -- left text margin; leftwards offset from the tooltip border's left edge + local left = 7 - tooltipUI.titleBg = Bitmap(tooltipUI) - tooltipUI.titleBg:SetSolidColor(UIUtil.tooltipTitleColor) - tooltipUI.titleBg.Depth:Set(function() return tooltipUI.title.Depth() - 1 end) - tooltipUI.titleBg.Top:Set(tooltipUI.title.Top) - LayoutHelpers.AtBottomIn(tooltipUI.titleBg, tooltipUI.title, -2) + local titleString = '' - tooltipUI.titleBg.Left:Set(function() return tooltipUI.title.Left() end) - LayoutHelpers.AnchorToLeft(tooltipUI.titleBg, tooltipUI.title, - tooltipWidth + 4) + if bp.Description then + titleString = bp.Tech .. ' ' .. LOC(bp.Description) + elseif bp.Name then + titleString = LOC(bp.Name) + end - local titleHeight = math.max(tooltipUI.title.Height(), 1) + 4 - local top = titleHeight - local left = 7 + local generalUnitName = LOC(bp.General.UnitName) + if generalUnitName and generalUnitName ~= '' and not bp.CategoriesHash['SUBCOMMANDER'] then + titleString = titleString .. ' (' .. generalUnitName .. ')' + end - tooltipUI.body = Bitmap(tooltipUI) - tooltipUI.body:SetSolidColor('FF080808') ----FF080808 - LayoutHelpers.SetDimensions(tooltipUI.body, tooltipWidth-4, 300) - LayoutHelpers.AtLeftTopIn(tooltipUI.body, tooltipUI, 2, top) + -- 2px top/left border, 7px left offset + local title = Layouter(UIUtil.CreateText(tooltipUI, titleString, fontTextSize, UIUtil.bodyFont)):AtLeftTopIn(tooltipUI, 2 + left, 2):End() + tooltipUI.title = title - top = top + 2 + -- AtBottomIn -2 offset for parantheses to appear centered + local titleBg = Layouter(Bitmap(tooltipUI)):Color(UIUtil.tooltipTitleColor):Under(title):AtTopIn(title):AtBottomIn(title, -2):AtLeftIn(tooltipUI, 2):AtRightIn(tooltipUI, 2):End() + tooltipUI.titleBg = titleBg - local column1 = left - local column2 = tooltipWidth - 100 + left -- damage - local column3 = column2 - 100 -- dps - local column4 = column3 - 90 -- dpm - local column5 = column4 - 90 -- range - local column6 = column5 - 10 + -- Serves as the black background of the body of the tooltip + local body = Layouter(Bitmap(tooltipUI)):Color('FF080808'):AtLeftIn(tooltipUI, 2):AtRightIn(tooltipUI, 2):AnchorToBottom(titleBg):End() + tooltipUI.body = body - local text = nil + -- Reusable value from which text will be created local value = '' - tooltipHeight = math.max(tooltipUI.title.Height(), 1) - -- showing bp.Categories because they are more accurate than bp.Display.Abilities - local cats = UnitsAnalyzer.GetUnitsCategories(bp, false) - value = table.concat(cats, ', ') - --table.print(bp.Display.Abilities, 'Abilities') - tooltipUI.Categories = TextArea(tooltipUI, LayoutHelpers.ScaleNumber(tooltipWidth), 30) - tooltipUI.Categories:SetText(value) - tooltipUI.Categories:SetFont(fontTextName, fontTextSize-1) - tooltipUI.Categories:SetColors('FFFC9038', '00000000', UIUtil.fontColor, '00000000') ----FFFC9038 - local wrapped = Text.WrapText(value, LayoutHelpers.ScaleNumber(tooltipWidth-10), function(value) return tooltipUI.Categories:GetStringAdvance(value) end) - local wrappedHeight = (table.getsize(wrapped) or 1) * tooltipHeight - tooltipUI.Categories.Height:Set(wrappedHeight) - LayoutHelpers.AtLeftTopIn(tooltipUI.Categories, tooltipUI, left, top) - - top = top + tooltipUI.Categories.Height() - top = top + 8 + local categoriesText = TextArea(tooltipUI, tooltipWidth - 2 * left, 30) + categoriesText:SetText( table.concat(UnitsAnalyzer.GetUnitsCategories(bp, false), ', ') ) + categoriesText:SetFont(fontTextName, fontTextSize - 1) + categoriesText:SetColors('FFFC9038', nil, nil, nil) -- Only the foreground color will be changed, the rest will remain as defaults + local textAreaHeight = categoriesText:GetItemCount() * (fontTextSize + 2 /PixelScaleFactor) + Layouter(categoriesText):Height(textAreaHeight):AtLeftIn(tooltipUI, left):AtTopIn(body, 2):End() + tooltipUI.Categories = categoriesText local id = bp.ID if bp.Type == "UPGRADE" and bp.Icon and bp.SourceID then @@ -145,289 +115,209 @@ function Create(parent, bp) value = LOC(UnitDescriptions[id]) or LOC(bp.Interface.Help.HelpText) -- defaulting to description of base units for preset support commanders - if not value and bp.CategoriesHash['SUBCOMMANDER'] and string.find(id, "_") then - local baseID = StringSplit(id, '_')[1] + local underscoreIndex = string.find(id, "_") + if not value and bp.CategoriesHash['SUBCOMMANDER'] and underscoreIndex then + local baseID = string.sub(id, 1, underscoreIndex - 1) value = LOC(UnitDescriptions[baseID]) end + local description if not value then if not bp.Mod then -- show warnings only for not modded units WARN('UnitsTooltip cannot find unit description for ' .. bp.ID .. ' blueprint') end else - tooltipUI.Descr = TextArea(tooltipUI, LayoutHelpers.ScaleNumber(tooltipWidth-10), 30) - tooltipUI.Descr:SetText(value) - tooltipUI.Descr:SetFont(fontTextName, fontTextSize-1) - tooltipUI.Descr:SetColors(colorText, '00000000', UIUtil.fontColor, '00000000') - local wrapped = Text.WrapText(value, LayoutHelpers.ScaleNumber(tooltipWidth-10), function(value) return tooltipUI.Descr:GetStringAdvance(value) end) - local wrappedHeight = (table.getsize(wrapped) or 1) * tooltipHeight - tooltipUI.Descr.Height:Set(wrappedHeight) - LayoutHelpers.AtLeftTopIn(tooltipUI.Descr, tooltipUI, left, top) - - top = top + tooltipUI.Descr.Height() - top = top + 12 + description = TextArea(tooltipUI, tooltipWidth-14, 30) + description:SetText(value) + description:SetFont(fontTextName, fontTextSize-1) + description:SetColors(colorText, nil, nil, nil) + local textAreaHeight = description:GetItemCount() * (fontTextSize + 2 /PixelScaleFactor) + Layouter(description):Height(textAreaHeight):AtLeftIn(tooltipUI, 7):AnchorToBottom(categoriesText):End() + tooltipUI.Descr = description end - local perMassLabel = UIUtil.CreateText(tooltipUI, 'PER MASS ', fontTextSize-2, fontTextName) - perMassLabel:SetColor(colorText) - LayoutHelpers.AtRightTopIn(perMassLabel, tooltipUI, column5-iconSize-5, top) + -- the columns give the right margin of the text so that the numbers can grow leftwards freely + local column2 = tooltipWidth - 100 + left -- cost/damage -- 327 -- 420 - 327 = 93 + local column3 = column2 - 100 -- production/dps -- 227 -- 420 - 227 = 193 + local column4 = column3 - 90 -- defense/range -- 137 -- 420 - 137 = 283 + local column5 = column4 - 90 -- per mass -- 47 -- 420 - 47 = 373 + + -- Column headers + local costLabel = Layouter(UIUtil.CreateText(tooltipUI, 'BUILD COST ', fontTextSize-2, fontTextName)):Color(colorText) + :AtRightIn(tooltipUI, column2-iconSize-5):AnchorToBottom(description or categoriesText, 12):End() - local defenseLabel = UIUtil.CreateText(tooltipUI, 'DEFENSE ', fontTextSize-2, fontTextName) - defenseLabel:SetColor(colorText) - LayoutHelpers.AtRightTopIn(defenseLabel, tooltipUI, column4-iconSize-5, top) + local prodLabel = Layouter(UIUtil.CreateText(tooltipUI, 'PRODUCTION ', fontTextSize-2, fontTextName)):Color(colorText) + :AtRightIn(tooltipUI, column3-iconSize-5):AnchorToBottom(description or categoriesText, 12):End() - local prodLabel = UIUtil.CreateText(tooltipUI, 'PRODUCTION ', fontTextSize-2, fontTextName) - prodLabel:SetColor(colorText) - LayoutHelpers.AtRightTopIn(prodLabel, tooltipUI, column3-iconSize-5, top) + local defenseLabel = Layouter(UIUtil.CreateText(tooltipUI, 'DEFENSE ', fontTextSize-2, fontTextName)):Color(colorText) + :AtRightIn(tooltipUI, column4-iconSize-5):AnchorToBottom(description or categoriesText, 12):End() - local costLabel = UIUtil.CreateText(tooltipUI, 'BUILD COST ', fontTextSize-2, fontTextName) - costLabel:SetColor(colorText) - LayoutHelpers.AtRightTopIn(costLabel, tooltipUI, column2-iconSize-5, top) + local perMassLabel = Layouter(UIUtil.CreateText(tooltipUI, 'PER MASS ', fontTextSize-2, fontTextName)):Color(colorText) + :AtRightIn(tooltipUI, column5-iconSize-5):AnchorToBottom(description or categoriesText, 12):End() - top = top + costLabel.Height() + 1 + -- Mass/HP row local eco = UnitsAnalyzer.GetEconomyStats(bp) - local healthValue = init(bp.NewHealth or bp.Defense.Health) - local healthString = StringComma(math.floor(healthValue)) .. ' ' - HealthText = UIUtil.CreateText(tooltipUI, healthString, fontValueSize, fontValueName) - HealthText:SetColor(colorDefense) ----FF0BACF7 - LayoutHelpers.AtRightTopIn(HealthText, tooltipUI, column4, top) - HealthIcon = Bitmap(tooltipUI) - HealthIcon:SetTexture('/textures/ui/common/game/unit-build-over-panel/defense-health.dds') - LayoutHelpers.SetDimensions(HealthIcon, iconSize, iconSize) - LayoutHelpers.AtLeftTopIn(HealthIcon, tooltipUI, tooltipWidth-column4, top+2) - - local healthValue = (healthValue / eco.BuildCostMass) - local healthString = string.format("%0.2f ",healthValue) - HealthPerMassText = UIUtil.CreateText(tooltipUI, healthString, fontValueSize, fontValueName) - HealthPerMassText:SetColor(colorDefense) ----FF0BACF7 - LayoutHelpers.AtRightTopIn(HealthPerMassText, tooltipUI, column5, top) - HealthPerMassIcon = Bitmap(tooltipUI) - HealthPerMassIcon:SetTexture('/textures/ui/common/game/unit-build-over-panel/defense-health.dds') - LayoutHelpers.SetDimensions(HealthPerMassIcon, iconSize, iconSize) - LayoutHelpers.AtLeftTopIn(HealthPerMassIcon, tooltipUI, tooltipWidth-column5, top+2) + value = StringComma(eco.BuildCostMass) + local MassCostIcon = Layouter(Bitmap(tooltipUI)):Texture('/textures/ui/common/game/unit-build-over-panel/mass.dds'):Width(iconSize):Height(iconSize) + :AtRightIn(costLabel, 5):AnchorToBottom(costLabel, 2):End() + local MassCostText = Layouter(UIUtil.CreateText(tooltipUI, value, fontValueSize, fontValueName)):Color(colorMass):LeftOf(MassCostIcon, 4):AnchorToBottom(costLabel, 1):End() value = eco.YieldMass - value = value > 0 and '+' .. value or value - value = StringComma(value) .. ' ' - MassProdText = UIUtil.CreateText(tooltipUI, value, fontValueSize, fontValueName) - MassProdText:SetColor(colorMass) -- --FF2DEC28 - LayoutHelpers.AtRightTopIn(MassProdText, tooltipUI, column3, top) - MassProdIcon = Bitmap(tooltipUI) - MassProdIcon:SetTexture('/textures/ui/common/game/unit-build-over-panel/mass.dds') - LayoutHelpers.SetDimensions(MassProdIcon, iconSize, iconSize) - LayoutHelpers.AtLeftTopIn(MassProdIcon, tooltipUI, tooltipWidth-column3, top+2) - - value = StringComma(eco.BuildCostMass) .. ' ' - MassCostText = UIUtil.CreateText(tooltipUI, value, fontValueSize, fontValueName) - MassCostText:SetColor(colorMass) -- --FF2DEC28 - LayoutHelpers.AtRightTopIn(MassCostText, tooltipUI, column2, top) - MassCostIcon = Bitmap(tooltipUI) - MassCostIcon:SetTexture('/textures/ui/common/game/unit-build-over-panel/mass.dds') - LayoutHelpers.SetDimensions(MassCostIcon, iconSize, iconSize) - LayoutHelpers.AtLeftTopIn(MassCostIcon, tooltipUI, tooltipWidth-column2, top+2) - - top = top + MassCostText.Height() + 2 + value = StringComma(value > 0 and '+' .. value or value) + local MassProdIcon = Layouter(Bitmap(tooltipUI)):Texture('/textures/ui/common/game/unit-build-over-panel/mass.dds'):Width(iconSize):Height(iconSize) + :AtRightIn(prodLabel, 5):AnchorToBottom(prodLabel, 2):End() + local MassProdText = Layouter(UIUtil.CreateText(tooltipUI, value, fontValueSize, fontValueName)):Color(colorMass):LeftOf(MassProdIcon, 4):AnchorToBottom(prodLabel, 1):End() - local shieldValue = init(bp.ShieldMaxHealth or bp.Defense.Shield.ShieldMaxHealth) - local shieldString = StringComma(math.floor(shieldValue)) .. ' ' - ShieldText = UIUtil.CreateText(tooltipUI, shieldString, fontValueSize, fontValueName) - ShieldText:SetColor(colorDefense) ----FF0BACF7 - LayoutHelpers.AtRightTopIn(ShieldText, tooltipUI, column4, top) - ShieldIcon = Bitmap(tooltipUI) - ShieldIcon:SetTexture('/textures/ui/common/game/unit-build-over-panel/defense-shields.dds') - LayoutHelpers.SetDimensions(ShieldIcon, iconSize, iconSize) - LayoutHelpers.AtLeftTopIn(ShieldIcon, tooltipUI, tooltipWidth-column4, top+2) - - local shieldValue = (shieldValue / eco.BuildCostMass) - local shieldString = string.format("%0.2f",shieldValue) .. ' ' - ShieldPerMassText = UIUtil.CreateText(tooltipUI, shieldString, fontValueSize, fontValueName) - ShieldPerMassText:SetColor(colorDefense) ----FF0BACF7 - LayoutHelpers.AtRightTopIn(ShieldPerMassText, tooltipUI, column5, top) - ShieldPerMassIcon = Bitmap(tooltipUI) - ShieldPerMassIcon:SetTexture('/textures/ui/common/game/unit-build-over-panel/defense-shields.dds') - LayoutHelpers.SetDimensions(ShieldPerMassIcon, iconSize, iconSize) - LayoutHelpers.AtLeftTopIn(ShieldPerMassIcon, tooltipUI, tooltipWidth-column5, top+2) + local healthValue = init(bp.Defense.Health or bp.NewHealth) -- NewHealth is used by enhancements + value = StringComma(math.floor(healthValue)) + local HealthIcon = Layouter(Bitmap(tooltipUI)):Texture('/textures/ui/common/game/unit-build-over-panel/defense-health.dds'):Width(iconSize):Height(iconSize) + :AtRightIn(defenseLabel, 5):AnchorToBottom(defenseLabel, 2):End() + local HealthText = Layouter(UIUtil.CreateText(tooltipUI, value, fontValueSize, fontValueName)):Color(colorDefense):LeftOf(HealthIcon, 4):AnchorToBottom(defenseLabel, 1):End() + + value = string.format("%0.2f ", (healthValue / eco.BuildCostMass)) + local HealthPerMassIcon = Layouter(Bitmap(tooltipUI)):Texture('/textures/ui/common/game/unit-build-over-panel/defense-health.dds'):Width(iconSize):Height(iconSize) + :AtRightIn(perMassLabel, 5):AnchorToBottom(perMassLabel, 2):End() + local HealthPerMassText = Layouter(UIUtil.CreateText(tooltipUI, value, fontValueSize, fontValueName)):Color(colorDefense):LeftOf(HealthPerMassIcon, 4):AnchorToBottom(perMassLabel, 1):End() + + -- Energy/Shield row + + value = StringComma(math.ceil(eco.BuildCostEnergy)) + local EnergyCostIcon = Layouter(Bitmap(tooltipUI)):Texture('/textures/ui/common/game/unit-build-over-panel/energy.dds'):Width(iconSize):Height(iconSize) + :AtRightIn(MassCostIcon):AnchorToBottom(MassCostText, 3):End() + local EnergyCostText = Layouter(UIUtil.CreateText(tooltipUI, value, fontValueSize, fontValueName)):Color(colorEnergy):LeftOf(EnergyCostIcon, 4):AnchorToBottom(MassCostText, 2):End() value = eco.YieldEnergy - value = value > 0 and '+' .. value or value - value = StringComma(value) .. ' ' - EnergyProdText = UIUtil.CreateText(tooltipUI, value, fontValueSize, fontValueName) - EnergyProdText:SetColor(colorEnergy) ----FFF7B00B - LayoutHelpers.AtRightTopIn(EnergyProdText, tooltipUI, column3, top) - EnergyProdIcon = Bitmap(tooltipUI) - EnergyProdIcon:SetTexture('/textures/ui/common/game/unit-build-over-panel/energy.dds') - LayoutHelpers.SetDimensions(EnergyProdIcon, iconSize, iconSize) - LayoutHelpers.AtLeftTopIn(EnergyProdIcon, tooltipUI, tooltipWidth-column3, top+2) - - value = StringComma(math.ceil(eco.BuildCostEnergy)) .. ' ' - EnergyCostText = UIUtil.CreateText(tooltipUI, value, fontValueSize, fontValueName) - EnergyCostText:SetColor(colorEnergy) ----FFF7B00B - LayoutHelpers.AtRightTopIn(EnergyCostText, tooltipUI, column2, top) - EnergyCostIcon = Bitmap(tooltipUI) - EnergyCostIcon:SetTexture('/textures/ui/common/game/unit-build-over-panel/energy.dds') - LayoutHelpers.SetDimensions(EnergyCostIcon, iconSize, iconSize) - LayoutHelpers.AtLeftTopIn(EnergyCostIcon, tooltipUI, tooltipWidth-column2, top+2) - - top = top + EnergyCostText.Height() + 2 - - value = stringTime(math.floor(eco.BuildTime)) .. ' ' - BuildTimeText = UIUtil.CreateText(tooltipUI, value, fontValueSize, fontValueName) - BuildTimeText:SetColor(colorBuild) ----FFD9D9D9 - LayoutHelpers.AtRightTopIn(BuildTimeText, tooltipUI, column2, top) - BuildTimeIcon = Bitmap(tooltipUI) - BuildTimeIcon:SetTexture('/textures/ui/common/game/unit-build-over-panel/build-time.dds') - LayoutHelpers.SetDimensions(BuildTimeIcon, iconSize, iconSize) - LayoutHelpers.AtLeftTopIn(BuildTimeIcon, tooltipUI, tooltipWidth-column2, top+2) - - value = StringComma(eco.BuildRate).. ' ' - BuildRateText = UIUtil.CreateText(tooltipUI, value, fontValueSize, fontValueName) - BuildRateText:SetColor(colorBuild) ----FFD9D9D9 - LayoutHelpers.AtRightTopIn(BuildRateText, tooltipUI, column3, top) - BuildRateIcon = Bitmap(tooltipUI) - BuildRateIcon:SetTexture('/textures/ui/common/game/unit-build-over-panel/build-rate.dds') - LayoutHelpers.SetDimensions(BuildRateIcon, iconSize, iconSize) - LayoutHelpers.AtLeftTopIn(BuildRateIcon, tooltipUI, tooltipWidth-column3, top+2) - - top = top + BuildTimeText.Height() + 10 + value = StringComma(value > 0 and '+' .. value or value) + local EnergyProdIcon = Layouter(Bitmap(tooltipUI)):Texture('/textures/ui/common/game/unit-build-over-panel/energy.dds'):Width(iconSize):Height(iconSize) + :AtRightIn(MassProdIcon):AnchorToBottom(MassProdText, 3):End() + local EnergyProdText = Layouter(UIUtil.CreateText(tooltipUI, value, fontValueSize, fontValueName)):Color(colorEnergy):LeftOf(EnergyProdIcon, 4):AnchorToBottom(MassProdText, 2):End() + + local shieldValue = init(bp.ShieldMaxHealth or bp.Defense.Shield.ShieldMaxHealth) + value = StringComma(math.floor(shieldValue)) + local ShieldIcon = Layouter(Bitmap(tooltipUI)):Texture('/textures/ui/common/game/unit-build-over-panel/defense-shields.dds'):Width(iconSize):Height(iconSize) + :AtRightIn(HealthIcon):AnchorToBottom(HealthText, 3):End() + local ShieldText = Layouter(UIUtil.CreateText(tooltipUI, value, fontValueSize, fontValueName)):Color(colorDefense):LeftOf(ShieldIcon, 4):AnchorToBottom(HealthText, 2):End() + + value = string.format("%0.2f",(shieldValue / eco.BuildCostMass)) + local ShieldPerMassIcon = Layouter(Bitmap(tooltipUI)):Texture('/textures/ui/common/game/unit-build-over-panel/defense-shields.dds'):Width(iconSize):Height(iconSize) + :AtRightIn(HealthPerMassIcon):AnchorToBottom(HealthPerMassText, 3):End() + local ShieldPerMassText = Layouter(UIUtil.CreateText(tooltipUI, value, fontValueSize, fontValueName)):Color(colorDefense):LeftOf(ShieldPerMassIcon, 4):AnchorToBottom(HealthPerMassText, 2):End() + + -- Buildrate/time row + + value = StringComma(math.floor(eco.BuildTime)) + local BuildTimeIcon = Layouter(Bitmap(tooltipUI)):Texture('/textures/ui/common/game/unit-build-over-panel/build-time.dds'):Width(iconSize):Height(iconSize) + :AtRightIn(EnergyCostIcon):AnchorToBottom(EnergyCostText, 3):End() + local BuildTimeText = Layouter(UIUtil.CreateText(tooltipUI, value, fontValueSize, fontValueName)):Color(colorBuild):LeftOf(BuildTimeIcon, 4):AnchorToBottom(EnergyCostText, 2):End() + value = StringComma(eco.BuildRate) + local BuildRateIcon = Layouter(Bitmap(tooltipUI)):Texture('/textures/ui/common/game/unit-build-over-panel/build-rate.dds'):Width(iconSize):Height(iconSize) + :AtRightIn(EnergyProdIcon):AnchorToBottom(EnergyProdText, 3):End() + local BuildRateText = Layouter(UIUtil.CreateText(tooltipUI, value, fontValueSize, fontValueName)):Color(colorBuild):LeftOf(BuildRateIcon, 4):AnchorToBottom(EnergyProdText, 2):End() + + -- Individual weapon stat rows + + local furthestDownControl = BuildRateText local weapons = UnitsAnalyzer.GetWeaponsStats(bp) for i, weapon in weapons or {} do - top = top + 1 - local weaponText = UIUtil.CreateText(tooltipUI, weapon.Info, fontTextSize-1, fontTextName) - weaponText:SetColor('FFE1DFDF') ----FFE1DFDF - LayoutHelpers.AtLeftTopIn(weaponText, tooltipUI, left, top) - top = top + weaponText.Height() + 1 - - value = StringComma(weapon.Range) .. ' ' --RANGE - local rangeText = UIUtil.CreateText(tooltipUI, value, fontValueSize, fontValueName) - rangeText:SetColor('FFF70B0B') ----FFF70B0B - LayoutHelpers.AtRightTopIn(rangeText, tooltipUI, column4, top) - local rangeIcon = Bitmap(tooltipUI) - rangeIcon:SetTexture('/textures/ui/common/game/unit-build-over-panel/damage-range.dds') - LayoutHelpers.SetDimensions(rangeIcon, iconSize, iconSize) - LayoutHelpers.AtLeftTopIn(rangeIcon, tooltipUI, tooltipWidth-column4, top+1) - - value = string.format("%0.2f",weapon.DPM) .. ' ' - local dpmText = UIUtil.CreateText(tooltipUI, value, fontValueSize, fontValueName) - dpmText:SetColor('FFF70B0B') ----FFF70B0B - LayoutHelpers.AtRightTopIn(dpmText, tooltipUI, column5, top) - local dpmIcon = Bitmap(tooltipUI) - dpmIcon:SetTexture('/textures/ui/common/game/unit-build-over-panel/damage-per-mass.dds') - LayoutHelpers.SetDimensions(dpmIcon, iconSize, iconSize) - LayoutHelpers.AtLeftTopIn(dpmIcon, tooltipUI, tooltipWidth-column5, top+1) - - value = StringComma(weapon.DPS) .. ' ' - local dpsText = UIUtil.CreateText(tooltipUI, value, fontValueSize, fontValueName) - dpsText:SetColor('FFF70B0B') ----FFF70B0B - LayoutHelpers.AtRightTopIn(dpsText, tooltipUI, column3, top) - local dpsIcon = Bitmap(tooltipUI) - dpsIcon:SetTexture('/textures/ui/common/game/unit-build-over-panel/damage-per-second.dds') - LayoutHelpers.SetDimensions(dpsIcon, iconSize, iconSize) - LayoutHelpers.AtLeftTopIn(dpsIcon, tooltipUI, tooltipWidth-column3, top+1) - - value = StringComma(weapon.Damage) .. ' ' - local dmgText = UIUtil.CreateText(tooltipUI, value, fontValueSize, fontValueName) - dmgText:SetColor('FFF70B0B') ----FFF70B0B - LayoutHelpers.AtRightTopIn(dmgText, tooltipUI, column2, top) - local dmgIcon = Bitmap(tooltipUI) - dmgIcon:SetTexture('/textures/ui/common/game/unit-build-over-panel/damage.dds') - LayoutHelpers.SetDimensions(dmgIcon, iconSize, iconSize) - LayoutHelpers.AtLeftTopIn(dmgIcon, tooltipUI, tooltipWidth-column2, top+1) - - top = top + dmgText.Height() + local weaponText = Layouter(UIUtil.CreateText(tooltipUI, weapon.Info, fontTextSize-1, fontTextName)):Color(colorText):AtLeftIn(tooltipUI, left) + if furthestDownControl == BuildRateText then + weaponText = weaponText:AnchorToBottom(BuildTimeText, 11):End() + else + weaponText = weaponText:AnchorToBottom(furthestDownControl, 1):End() + end + + value = StringComma(weapon.Damage) + local dmgIcon = Layouter(Bitmap(tooltipUI)):Texture('/textures/ui/common/game/unit-build-over-panel/damage.dds'):Width(iconSize):Height(iconSize) + :AtRightIn(EnergyCostIcon):AnchorToBottom(weaponText, 2):End() + local dmgText = Layouter(UIUtil.CreateText(tooltipUI, value, fontValueSize, fontValueName)):Color(colorDamage):LeftOf(dmgIcon, 4):AnchorToBottom(weaponText, 1):End() + + furthestDownControl = dmgText + + value = StringComma(weapon.DPS) + local dpsIcon = Layouter(Bitmap(tooltipUI)):Texture('/textures/ui/common/game/unit-build-over-panel/damage-per-second.dds'):Width(iconSize):Height(iconSize) + :AtRightIn(EnergyProdIcon):AnchorToBottom(weaponText, 2):End() + local dpsText = Layouter(UIUtil.CreateText(tooltipUI, value, fontValueSize, fontValueName)):Color(colorDamage):LeftOf(dpsIcon, 4):AnchorToBottom(weaponText, 1):End() + + value = StringComma(weapon.Range) + local rangeIcon = Layouter(Bitmap(tooltipUI)):Texture('/textures/ui/common/game/unit-build-over-panel/damage-range.dds'):Width(iconSize):Height(iconSize) + :AtRightIn(ShieldIcon):AnchorToBottom(weaponText, 2):End() + local rangeText = Layouter(UIUtil.CreateText(tooltipUI, value, fontValueSize, fontValueName)):Color(colorDamage):LeftOf(rangeIcon, 4):AnchorToBottom(weaponText, 1):End() + + value = string.format("%0.2f", weapon.DPM) + local dpmIcon = Layouter(Bitmap(tooltipUI)):Texture('/textures/ui/common/game/unit-build-over-panel/damage-per-mass.dds'):Width(iconSize):Height(iconSize) + :AtRightIn(ShieldPerMassIcon):AnchorToBottom(weaponText, 2):End() + local dpmText = Layouter(UIUtil.CreateText(tooltipUI, value, fontValueSize, fontValueName)):Color(colorDamage):LeftOf(dpmIcon, 4):AnchorToBottom(weaponText, 1):End() end + -- Weapon stats summary row + local total = UnitsAnalyzer.GetWeaponsTotal(weapons) if total.Count > 1 then - top = top + 10 - - local weaponText = UIUtil.CreateText(tooltipUI, total.Info, fontTextSize, fontTextName) - weaponText:SetColor('FFE1DFDF') ----FFE1DFDF - LayoutHelpers.AtLeftTopIn(weaponText, tooltipUI, left, top) - top = top + weaponText.Height() + 1 - - value = StringComma(total.Range) .. ' ' --RANGE - local rangeText = UIUtil.CreateText(tooltipUI, value, fontValueSize, fontValueName) - rangeText:SetColor('FFF70B0B') ----FFF70B0B - LayoutHelpers.AtRightTopIn(rangeText, tooltipUI, column4, top) - local rangeIcon = Bitmap(tooltipUI) - rangeIcon:SetTexture('/textures/ui/common/game/unit-build-over-panel/damage-range.dds') - LayoutHelpers.SetDimensions(rangeIcon, iconSize, iconSize) - LayoutHelpers.AtLeftTopIn(rangeIcon, tooltipUI, tooltipWidth-column4, top+1) - - value = string.format("%0.2f",total.DPM) .. ' ' - local dpmText = UIUtil.CreateText(tooltipUI, value, fontValueSize, fontValueName) - dpmText:SetColor('FFF70B0B') ----FFF70B0B - LayoutHelpers.AtRightTopIn(dpmText, tooltipUI, column5, top) - local dpmIcon = Bitmap(tooltipUI) - dpmIcon:SetTexture('/textures/ui/common/game/unit-build-over-panel/damage-per-mass.dds') - LayoutHelpers.SetDimensions(dpmIcon, iconSize, iconSize) - LayoutHelpers.AtLeftTopIn(dpmIcon, tooltipUI, tooltipWidth-column5, top+1) - - value = StringComma(total.DPS) .. ' ' - local dpsText = UIUtil.CreateText(tooltipUI, value, fontValueSize, fontValueName) - dpsText:SetColor('FFF70B0B') ----FFF70B0B - LayoutHelpers.AtRightTopIn(dpsText, tooltipUI, column3, top) - local dpsIcon = Bitmap(tooltipUI) - dpsIcon:SetTexture('/textures/ui/common/game/unit-build-over-panel/damage-per-second.dds') - LayoutHelpers.SetDimensions(dpsIcon, iconSize, iconSize) - LayoutHelpers.AtLeftTopIn(dpsIcon, tooltipUI, tooltipWidth-column3, top+1) - - value = StringComma(total.Damage) .. ' ' - local dmgText = UIUtil.CreateText(tooltipUI, value, fontValueSize, fontValueName) - dmgText:SetColor('FFF70B0B') ----FFF70B0B - LayoutHelpers.AtRightTopIn(dmgText, tooltipUI, column2, top) - local dmgIcon = Bitmap(tooltipUI) - dmgIcon:SetTexture('/textures/ui/common/game/unit-build-over-panel/damage.dds') - LayoutHelpers.SetDimensions(dmgIcon, iconSize, iconSize) - LayoutHelpers.AtLeftTopIn(dmgIcon, tooltipUI, tooltipWidth-column2, top+1) - - top = top + dmgText.Height() + local weaponText = Layouter(UIUtil.CreateText(tooltipUI, total.Info, fontTextSize-1, fontTextName)):Color(colorText):AtLeftIn(tooltipUI, left) + :AnchorToBottom(furthestDownControl, 10):End() + + value = StringComma(total.Damage) + local dmgIcon = Layouter(Bitmap(tooltipUI)):Texture('/textures/ui/common/game/unit-build-over-panel/damage.dds'):Width(iconSize):Height(iconSize) + :AtRightIn(EnergyCostIcon):AnchorToBottom(weaponText, 2):End() + local dmgText = Layouter(UIUtil.CreateText(tooltipUI, value, fontValueSize, fontValueName)):Color(colorDamage):LeftOf(dmgIcon, 4):AnchorToBottom(weaponText, 1):End() + + furthestDownControl = dmgText + + value = StringComma(total.DPS) + local dpsIcon = Layouter(Bitmap(tooltipUI)):Texture('/textures/ui/common/game/unit-build-over-panel/damage-per-second.dds'):Width(iconSize):Height(iconSize) + :AtRightIn(EnergyProdIcon):AnchorToBottom(weaponText, 2):End() + local dpsText = Layouter(UIUtil.CreateText(tooltipUI, value, fontValueSize, fontValueName)):Color(colorDamage):LeftOf(dpsIcon, 4):AnchorToBottom(weaponText, 1):End() + + value = StringComma(total.Range) + local rangeIcon = Layouter(Bitmap(tooltipUI)):Texture('/textures/ui/common/game/unit-build-over-panel/damage-range.dds'):Width(iconSize):Height(iconSize) + :AtRightIn(ShieldIcon):AnchorToBottom(weaponText, 2):End() + local rangeText = Layouter(UIUtil.CreateText(tooltipUI, value, fontValueSize, fontValueName)):Color(colorDamage):LeftOf(rangeIcon, 4):AnchorToBottom(weaponText, 1):End() + + value = string.format("%0.2f", total.DPM) + local dpmIcon = Layouter(Bitmap(tooltipUI)):Texture('/textures/ui/common/game/unit-build-over-panel/damage-per-mass.dds'):Width(iconSize):Height(iconSize) + :AtRightIn(ShieldPerMassIcon):AnchorToBottom(weaponText, 2):End() + local dpmText = Layouter(UIUtil.CreateText(tooltipUI, value, fontValueSize, fontValueName)):Color(colorDamage):LeftOf(dpmIcon, 4):AnchorToBottom(weaponText, 1):End() end + -- blueprint source rows + if bp.Mod then - top = top + 10 value = 'MOD: ' .. bp.Mod.name - local mod = UIUtil.CreateText(tooltipUI, value, fontTextSize, fontTextName) - mod:SetColor(colorMod) ----FFC905DC - LayoutHelpers.AtLeftTopIn(mod, tooltipUI, left, top) - top = top + mod.Height() + local mod = Layouter(UIUtil.CreateText(tooltipUI, value, fontTextSize, fontTextName)):Color(colorMod):AtLeftIn(tooltipUI, left):AnchorToBottom(furthestDownControl, 10):End() + furthestDownControl = mod + if debugging and bp.Source then value = '' .. bp.Source - local source = UIUtil.CreateText(tooltipUI, value, fontTextSize, fontTextName) - source:SetColor(colorMod) ----FFC905DC - LayoutHelpers.AtLeftTopIn(source, tooltipUI, left, top) - top = top + source.Height() + local source = Layouter(UIUtil.CreateText(tooltipUI, value, fontTextSize, fontTextName)):Color(colorMod):AtLeftIn(tooltipUI, left):AnchorToBottom(mod):End() + furthestDownControl = source end end - --NOTE UI for debugging - --BlueprintText = UIUtil.CreateText(tooltipUI, bp.Source or '', fontValueSize, fontValueName) - --BlueprintText:SetColor('FFE4BF0C') ----FFE4BF0C - --LayoutHelpers.AtRightTopIn(BlueprintText, tooltipUI, column1, top) + -- Finalize layout - LayoutHelpers.SetHeight(tooltipUI.body, top) + body = Layouter(body):AtBottomIn(furthestDownControl, -8):End() - local tooltipHeight = titleHeight - tooltipHeight = tooltipHeight + math.max(top, 1) + 2 - LayoutHelpers.SetDimensions(tooltipUI, tooltipWidth, tooltipHeight) - tooltipUI:DisableHitTest(true) + -- Have to set the top edge position to a value to be able to calculate the overall height + tooltipUI = Layouter(tooltipUI):AtBottomIn(body, -2):Top(0):ResetHeight():Height(tooltipUI.Height()/PixelScaleFactor):ResetTop() - local frame = GetFrame(0) - if parent.Top() - tooltipUI.Height() < 0 then - tooltipUI.Top:Set(function() return parent.Bottom() end) + -- Keep the tooltip on screen + if parent.Top() - tooltipUI:Get().Height() < 0 then + tooltipUI:Top(parent.Bottom) else - tooltipUI.Bottom:Set(parent.Top) + tooltipUI:Bottom(parent.Top) end - if parent.Left() - tooltipUI.Width() < 0 then - tooltipUI.Left:Set(function() return parent.Right() end) + if parent.Left() - tooltipUI:Get().Width() < 0 then + tooltipUI:Left(parent.Right) else - tooltipUI.Right:Set(parent.Left) + tooltipUI:Right(parent.Left) end + tooltipUI = tooltipUI:DisableHitTest(true):End() + -- NOTE keep this code in case of adding tooltip animation --if not Prefs.GetOption('tooltips') then return end --local createDelay = 0.01