Partially fix unicode name truncation.

This fix ensures that name truncation always happens on codepoint
boundaries and that a name has exactly 5 visible codepoints. This should
cover all possible cases unless names can have grapheme clusters, afaik
this is not the case.
This commit is contained in:
2023-07-26 03:15:13 +02:00
parent 30d8248832
commit b201ef789a

View File

@@ -1,7 +1,7 @@
-- Copyright 2023 <omicron.me@protonmail.com> -- Copyright 2023 <omicron.me@protonmail.com>
-- --
-- This file is part of Omicron Frames -- This file is part of Omicron Frames
-- --
-- Omicron Frames is free software: you can redistribute it and/or modify it -- Omicron Frames is free software: you can redistribute it and/or modify it
-- under the terms of the GNU General Public License as published by the Free -- under the terms of the GNU General Public License as published by the Free
-- Software Foundation, either version 3 of the License, or (at your option) -- Software Foundation, either version 3 of the License, or (at your option)
@@ -13,7 +13,7 @@
-- more details. -- more details.
-- --
-- You should have received a copy of the GNU General Public License along with -- You should have received a copy of the GNU General Public License along with
-- Omicron Frames. If not, see <https://www.gnu.org/licenses/>. -- Omicron Frames. If not, see <https://www.gnu.org/licenses/>.
local omif = select(2, ...) local omif = select(2, ...)
local types = omif.GetModule("types") local types = omif.GetModule("types")
@@ -35,24 +35,39 @@ types.UnitFrame = UnitFrame
local colors = { local colors = {
hostile = {0.5, 0.0, 0.0}, hostile = { 0.5, 0.0, 0.0 },
neutral = {0.7, 0.7, 0.0}, neutral = { 0.7, 0.7, 0.0 },
healthy = {0, 0.1, 0}, healthy = { 0, 0.1, 0 },
high = {0, 1.0, 0}, high = { 0, 1.0, 0 },
mid = {1.0, 1.0, 0}, mid = { 1.0, 1.0, 0 },
low = {1.0, 0.0, 0}, low = { 1.0, 0.0, 0 },
offline = {0.7, 0.7, 0.7}, offline = { 0.7, 0.7, 0.7 },
dead = {0.7, 0.7, 0.7}, dead = { 0.7, 0.7, 0.7 },
magic = {0.4, 0.4, 1.0}, magic = { 0.4, 0.4, 1.0 },
disease = {0.4, 0.2, 0.0}, disease = { 0.4, 0.2, 0.0 },
poison = {0.0, 0.7, 0.7}, poison = { 0.0, 0.7, 0.7 },
curse = {0.7, 0.0, 0.7}, curse = { 0.7, 0.0, 0.7 },
immune = {0.0, 0.2, 0.4}, immune = { 0.0, 0.2, 0.4 },
bomb = {1.0, 0.7, 0.7}, bomb = { 1.0, 0.7, 0.7 },
cyan = {0.0, 0.8, 0.8}, cyan = { 0.0, 0.8, 0.8 },
white = {1.0, 1.0, 1.0} white = { 1.0, 1.0, 1.0 }
} }
-- This trucates _codepoints_ not graphemes. If combination codepoints are
-- contained in the string, it will not properly truncate and may return
-- incorrect graphemes
local function utf8_truncate(s, n)
local init = 0
for _ = 1, n do
local _, last = string.find(s, ".[\128-\191]*", init + 1)
if not last then
return s
end
init = last
end
return string.sub(s, 1, init)
end
---@param unit string ---@param unit string
---@param config table ---@param config table
function UnitFrame:Init(unit, config) function UnitFrame:Init(unit, config)
@@ -127,7 +142,7 @@ function UnitFrame:StartRangeTicker()
if self.rangeTicker then if self.rangeTicker then
return return
end end
local delta = 0.45 + (fastrandom(0, 100)/1000) local delta = 0.45 + (fastrandom(0, 100) / 1000)
self.rangeTicker = C_Timer.NewTicker(delta, function() self.rangeTicker = C_Timer.NewTicker(delta, function()
self:UpdateRange() self:UpdateRange()
end) end)
@@ -157,7 +172,7 @@ end
-- Returns -- Returns
-- true if the unit is in range -- true if the unit is in range
-- false if the unit is not in range -- false if the unit is not in range
-- nil if it could not be determined -- nil if it could not be determined
function UnitFrame:IsInRange() function UnitFrame:IsInRange()
local unit = self.unit local unit = self.unit
local friendlySpell = self.rangeFriendly local friendlySpell = self.rangeFriendly
@@ -169,8 +184,8 @@ function UnitFrame:IsInRange()
elseif enemySpell and UnitCanAttack("player", unit) then elseif enemySpell and UnitCanAttack("player", unit) then
return IsSpellInRange(enemySpell, unit) == 1 return IsSpellInRange(enemySpell, unit) == 1
end end
-- Fall back to raid/party only range check -- Fall back to raid/party only range check
local inRange, checkedRange = UnitInRange(unit) local inRange, checkedRange = UnitInRange(unit)
if checkedRange then if checkedRange then
return inRange return inRange
@@ -181,7 +196,7 @@ end
function UnitFrame:UpdateRange() function UnitFrame:UpdateRange()
local unit = self.unit local unit = self.unit
if UnitIsDeadOrGhost(unit) then if UnitIsDeadOrGhost(unit) then
self.secureFrame:SetAlpha(1.0) self.secureFrame:SetAlpha(1.0)
return return
@@ -235,7 +250,7 @@ function UnitFrame:PrepareWheelBinds(bindings)
-- scroll wheel to the current unit frame button when the mouse enters and -- scroll wheel to the current unit frame button when the mouse enters and
-- to unbind it when the mouse leaves. When the keybind is triggered the -- to unbind it when the mouse leaves. When the keybind is triggered the
-- button will receive a click with a custom button name -- button will receive a click with a custom button name
-- Build a table with all lines of the scroll wheel + modifier combinations -- Build a table with all lines of the scroll wheel + modifier combinations
-- we want to bind, then concat it with newlines into a full script -- we want to bind, then concat it with newlines into a full script
local unit = self.unit local unit = self.unit
@@ -247,13 +262,13 @@ function UnitFrame:PrepareWheelBinds(bindings)
local prefix = ModifiersToPrefix(bind.mods) local prefix = ModifiersToPrefix(bind.mods)
local button = bind.button == "wheel-up" and "MOUSEWHEELUP" or "MOUSEWHEELDOWN" local button = bind.button == "wheel-up" and "MOUSEWHEELUP" or "MOUSEWHEELDOWN"
table.insert(bindScript, table.insert(bindScript,
string.format([[self:SetBindingClick(false, "%s%s", "OmicronSecureFrame%s", "%s%s")]], string.format([[self:SetBindingClick(false, "%s%s", "OmicronSecureFrame%s", "%s%s")]],
prefix, button, unit, prefix, bind.button prefix, button, unit, prefix, bind.button
) )
) )
end end
end end
local secure = self.secureFrame local secure = self.secureFrame
local bindScript = table.concat(bindScript, "\n") local bindScript = table.concat(bindScript, "\n")
@@ -272,7 +287,7 @@ function UnitFrame:SetMouseBinds(binds)
secure:RegisterForClicks("AnyDown") secure:RegisterForClicks("AnyDown")
self:PrepareWheelBinds(binds) self:PrepareWheelBinds(binds)
for _, bind in ipairs(binds) do for _, bind in ipairs(binds) do
local prefix = ModifiersToPrefix(bind.mods) local prefix = ModifiersToPrefix(bind.mods)
local button local button
@@ -295,7 +310,7 @@ function UnitFrame:SetMouseBinds(binds)
if bind.kind == "macro" then if bind.kind == "macro" then
self:SetMacroAction(button, bind.data) self:SetMacroAction(button, bind.data)
elseif bind.kind == "spell" then elseif bind.kind == "spell" then
self:SetSpellAction(button, bind.data) self:SetSpellAction(button, bind.data)
else else
secure:SetAttribute(button, bind.kind) secure:SetAttribute(button, bind.kind)
end end
@@ -455,7 +470,7 @@ end
function UnitFrame:ROLE_CHANGED_INFORM(name, changer, old, new) function UnitFrame:ROLE_CHANGED_INFORM(name, changer, old, new)
if UnitName(self.unit) == name then if UnitName(self.unit) == name then
self:UpdateRole(new) self:UpdateRole(new)
end end
end end
@@ -563,7 +578,7 @@ function UnitFrame:UpdateHealthColor()
elseif isFriend and pct >= 0.90 then elseif isFriend and pct >= 0.90 then
self.hp:SetColor(unpack(colors.healthy)) self.hp:SetColor(unpack(colors.healthy))
elseif isFriend and pct >= 0.75 then elseif isFriend and pct >= 0.75 then
local progress = (pct - 0.75) / (0.90-0.75) local progress = (pct - 0.75) / (0.90 - 0.75)
self.hp:SetInterpolatedColor(colors.mid, colors.high, progress) self.hp:SetInterpolatedColor(colors.mid, colors.high, progress)
else else
local progress = pct / 0.75 local progress = pct / 0.75
@@ -580,25 +595,10 @@ function UnitFrame:UpdateName()
-- TODO: UnitClass can return Unknown and the color can be nil. Having -- TODO: UnitClass can return Unknown and the color can be nil. Having
-- the unit exist but not fully loaded is possibly a state we want to -- the unit exist but not fully loaded is possibly a state we want to
-- handle more generally -- handle more generally
local color = RAID_CLASS_COLORS[class] or {r=1, g=1, b=1} local color = RAID_CLASS_COLORS[class] or { r = 1, g = 1, b = 1 }
self.name:SetTextColor(color.r, color.g, color.b) self.name:SetTextColor(color.r, color.g, color.b)
else else
self.name:SetTextColor(1, 1, 1) self.name:SetTextColor(1, 1, 1)
end end
self.name:SetText(UnitName(self.unit):sub(1, 5)) self.name:SetText(utf8_truncate((UnitName(self.unit)), 5))
end end
--[[
-- UNIT_AURA
-- UNIT_CLASSIFICATION_CHANGED
-- UNIT_COMBAT
-- UNIT_CONNECTION
-- UNIT_DISPLAYPOWER
-- UNIT_FACTION
-- UNIT_FLAGS
-- UNIT_LEVEL
-- UNIT_MANA
-- UNIT_HEALTH_PREDICTION
-- UNIT_PHASE
]]--