210 lines
6.3 KiB
Lua
210 lines
6.3 KiB
Lua
-- ------------------------------------------------------------------------------ --
|
|
-- TradeSkillMaster --
|
|
-- https://tradeskillmaster.com --
|
|
-- All Rights Reserved - Detailed license information included with addon. --
|
|
-- ------------------------------------------------------------------------------ --
|
|
|
|
--- Color Functions.
|
|
-- @module Color
|
|
|
|
local _, TSM = ...
|
|
local Color = TSM.Init("Util.Color")
|
|
local Math = TSM.Include("Util.Math")
|
|
local HSLuv = TSM.Include("Util.HSLuv")
|
|
local private = {
|
|
context = {},
|
|
transparent = nil,
|
|
fullWhite = nil,
|
|
fullBlack = nil,
|
|
}
|
|
local TINT_VALUES = {
|
|
SELECTED = 15,
|
|
HOVER = 12,
|
|
SELECTED_HOVER = 20,
|
|
DISABLED = -40,
|
|
}
|
|
local OPACITY_VALUES = {
|
|
HIGHLIGHT = 50,
|
|
}
|
|
|
|
|
|
|
|
-- ============================================================================
|
|
-- Metatable
|
|
-- ============================================================================
|
|
|
|
local COLOR_MT = {
|
|
__index = {
|
|
GetTint = function(self, tintPct)
|
|
local context = private.context[self]
|
|
assert(context.hex)
|
|
if type(tintPct) == "string" then
|
|
local sign, tintKey = strmatch(tintPct, "^([%+%-])([A-Z_]+)$")
|
|
assert(TINT_VALUES[tintKey])
|
|
tintPct = tonumber(TINT_VALUES[tintKey]) * (sign == "+" and 1 or -1)
|
|
end
|
|
assert(type(tintPct) == "number")
|
|
if tintPct == 0 then
|
|
return self
|
|
end
|
|
if not context.tints[tintPct] then
|
|
local l = context.l + tintPct
|
|
l = min(l, 100)
|
|
assert(private.IsValidValue(l, 100))
|
|
local r, g, b = HSLuv.ToRGB(context.h, context.s, l)
|
|
context.tints[tintPct] = private.NewColorHelper(r, g, b, context.a)
|
|
end
|
|
return context.tints[tintPct]
|
|
end,
|
|
GetOpacity = function(self, opacityPct)
|
|
local context = private.context[self]
|
|
assert(context.hex)
|
|
if type(opacityPct) == "string" then
|
|
assert(OPACITY_VALUES[opacityPct])
|
|
opacityPct = tonumber(OPACITY_VALUES[opacityPct])
|
|
end
|
|
assert(private.IsValidValue(opacityPct, 100))
|
|
if opacityPct == 100 then
|
|
return self
|
|
end
|
|
if not context.opacities[opacityPct] then
|
|
assert(context.a == 255)
|
|
local a = Math.Round(255 * opacityPct / 100)
|
|
assert(private.IsValidValue(a, 255))
|
|
context.opacities[opacityPct] = private.NewColorHelper(context.r, context.g, context.b, a)
|
|
end
|
|
return context.opacities[opacityPct]
|
|
end,
|
|
GetRGBA = function(self)
|
|
local context = private.context[self]
|
|
assert(context.hex)
|
|
return context.r, context.g, context.b, context.a
|
|
end,
|
|
GetFractionalRGBA = function(self)
|
|
local context = private.context[self]
|
|
assert(context.hex)
|
|
return context.r / 255, context.g / 255, context.b / 255, context.a / 255
|
|
end,
|
|
IsLight = function(self)
|
|
local context = private.context[self]
|
|
assert(context.hex)
|
|
return context.l >= 50
|
|
end,
|
|
GetHex = function(self)
|
|
local context = private.context[self]
|
|
assert(context.hex)
|
|
return context.hex
|
|
end,
|
|
ColorText = function(self, text)
|
|
return self:GetTextColorPrefix()..text.."|r"
|
|
end,
|
|
GetTextColorPrefix = function(self)
|
|
local context = private.context[self]
|
|
assert(context.hex)
|
|
return format("|c%02x%02x%02x%02x", context.a, context.r, context.g, context.b)
|
|
end,
|
|
Equals = function(self, other)
|
|
return self:GetHex() == other:GetHex()
|
|
end,
|
|
},
|
|
__newindex = function(self, key, value) error("Color cannot be modified") end,
|
|
__metatable = false,
|
|
__tostring = function(self)
|
|
local context = private.context[self]
|
|
return "Color:"..context.hex
|
|
end,
|
|
}
|
|
|
|
|
|
|
|
-- ============================================================================
|
|
-- Module Loading
|
|
-- ============================================================================
|
|
|
|
Color:OnModuleLoad(function()
|
|
private.transparent = private.NewColorHelper(0, 0, 0, 0)
|
|
private.fullWhite = private.NewColorHelper(255, 255, 255, 255)
|
|
private.fullBlack = private.NewColorHelper(0, 0, 0, 255)
|
|
end)
|
|
|
|
|
|
|
|
-- ============================================================================
|
|
-- Module Functions
|
|
-- ============================================================================
|
|
|
|
--- Create a new color object from a hex string.
|
|
-- @tparam string hex The hex string which represents the color (in either "#AARRGGBB" or "#RRGGBB" format)
|
|
-- @treturn Color The color object
|
|
function Color.NewFromHex(hex)
|
|
return private.NewColorHelper(private.HexToRGBA(hex))
|
|
end
|
|
|
|
--- Returns whether or not the argument is a color object.
|
|
-- @param arg The argument to check
|
|
-- @treturn boolean Whether or not the argument is a color object.
|
|
function Color.IsInstance(arg)
|
|
return type(arg) == "table" and private.context[arg] and true or false
|
|
end
|
|
|
|
--- Gets a predefined fully-transparent color.
|
|
-- @treturn Color The color object
|
|
function Color.GetTransparent()
|
|
return private.transparent
|
|
end
|
|
|
|
--- Gets a predefined fully-opaque white color.
|
|
-- @treturn Color The color object
|
|
function Color.GetFullWhite()
|
|
return private.fullWhite
|
|
end
|
|
|
|
--- Gets a predefined fully-opaque black color.
|
|
-- @treturn Color The color object
|
|
function Color.GetFullBlack()
|
|
return private.fullBlack
|
|
end
|
|
|
|
|
|
|
|
-- ============================================================================
|
|
-- Private Helper Functions
|
|
-- ============================================================================
|
|
|
|
function private.NewColorHelper(r, g, b, a)
|
|
assert(private.IsValidValue(r, 255) and private.IsValidValue(g, 255) and private.IsValidValue(b, 255) and private.IsValidValue(a, 255))
|
|
if a == 0 then
|
|
assert(r == 0 and g == 0 and b == 0, "Invalid color with alpha of 0")
|
|
end
|
|
local context = {
|
|
tints = {},
|
|
opacities = {},
|
|
r = r,
|
|
g = g,
|
|
b = b,
|
|
a = a,
|
|
h = nil,
|
|
s = nil,
|
|
l = nil,
|
|
hex = private.RGBAToHex(r, g, b, a),
|
|
}
|
|
context.h, context.s, context.l = HSLuv.FromRGB(r, g, b)
|
|
context.hex = private.RGBAToHex(r, g, b, a)
|
|
local color = setmetatable({}, COLOR_MT)
|
|
private.context[color] = context
|
|
return color
|
|
end
|
|
|
|
function private.IsValidValue(value, maxValue)
|
|
return type(value) == "number" and value >= 0 and value <= maxValue and value == floor(value)
|
|
end
|
|
|
|
function private.HexToRGBA(hex)
|
|
local a, r, g, b = strmatch(strlower(hex), "^#([0-9a-f]?[0-9a-f]?)([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])$")
|
|
return tonumber(r, 16), tonumber(g, 16), tonumber(b, 16), tonumber(a ~= "" and a or "ff", 16)
|
|
end
|
|
|
|
function private.RGBAToHex(r, g, b, a)
|
|
return format("#%02x%02x%02x%02x", Math.Round(a), Math.Round(r), Math.Round(g), Math.Round(b))
|
|
end
|