|
|
|
local MAJOR, MINOR = "DiesalGUI-2.0", 1
|
|
|
|
|
|
|
|
---@alias Diesal.GUI DiesalGUI-2.0
|
|
|
|
|
|
|
|
---@class DiesalGUI-2.0
|
|
|
|
---@field ObjectBase Diesal.GUI.ObjectBase.Methods
|
|
|
|
---@field ObjectFactory Diesal.GUI.Object.Factory
|
|
|
|
---@field ObjectVersions Diesal.GUI.Object.Versions
|
|
|
|
---@field ObjectPool Diesal.GUI.Object.Pool
|
|
|
|
---@field counts Diesal.GUI.Object.Counts
|
|
|
|
local DiesalGUI, oldMinor = LibStub:NewLibrary(MAJOR, MINOR)
|
|
|
|
if not DiesalGUI then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
local DiesalGUIDebug = false
|
|
|
|
|
|
|
|
local DiesalGUIDebugRedFont = RED_FONT_COLOR or CreateColor(1, 0.12549020349979, 0.12549020349979, 1)
|
|
|
|
local DiesalGUIDebugWhiteFont = WHITE_FONT_COLOR or CreateColor(1, 1, 1, 1)
|
|
|
|
|
|
|
|
local DiesalGUIDebugPrint = function(...)
|
|
|
|
if not DiesalGUIDebug then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
local args = { ... }
|
|
|
|
local msgPrefix = DiesalGUIDebugRedFont:WrapTextInColorCode("[DiesalGUI Debug]: ")
|
|
|
|
local str = ""
|
|
|
|
for i = 1, #args do
|
|
|
|
str = str .. tostring(args[i]) .. " "
|
|
|
|
end
|
|
|
|
print(msgPrefix .. DiesalGUIDebugWhiteFont:WrapTextInColorCode(str))
|
|
|
|
end
|
|
|
|
|
|
|
|
---@class Diesal.GUI.Padding : { [1]: left, [2]: right, [3]: top, [4]: bottom }
|
|
|
|
|
|
|
|
---@alias Diesal.Object.Constructed DiesalAccordian | DiesalAccordianSection | DiesalBar | DiesalBranch | DiesalButton | DiesalCheckBox | DiesalComboBox | DiesalComboBoxItem | DiesalDropDown | DiesalDropDownItem | DiesalInput | DiesalQuickDoc | DiesalScrollFrame | DiesalScrollingEditBox | DiesalScrollingMessageFrame | DiesalSpinner | DiesalText | DiesalTree | DiesalWindow
|
|
|
|
---@alias Diesal.Container Diesal.GUI.Object.Window | Diesal.GUI.Object.Accordian
|
|
|
|
---@alias Diesal.Container.Contents DiesalAccordian | DiesalAccordianSection | DiesalBar | DiesalBranch | DiesalButton | DiesalCheckBox | DiesalComboBox | DiesalComboBoxItem | DiesalDropDown | DiesalDropDownItem | FontString | DiesalInput | DiesalQuickDoc | DiesalScrollFrame | DiesalScrollingEditBox | DiesalScrollingMessageFrame | DiesalSpinner | DiesalTree | DiesalText | DiesalWindow | Diesal.GUI.ObjectBase
|
|
|
|
|
|
|
|
DiesalGUI.Debug = {
|
|
|
|
Print = DiesalGUIDebugPrint,
|
|
|
|
Enabled = DiesalGUIDebug,
|
|
|
|
RedFont = DiesalGUIDebugRedFont,
|
|
|
|
WhiteFont = DiesalGUIDebugWhiteFont,
|
|
|
|
}
|
|
|
|
|
|
|
|
local DUIParent = CreateFrame("Frame", "DUIParent", UIParent)
|
|
|
|
DUIParent:SetFrameLevel(UIParent:GetFrameLevel())
|
|
|
|
DUIParent:SetSize(GetScreenWidth(), GetScreenHeight())
|
|
|
|
DUIParent:SetPoint("BOTTOM")
|
|
|
|
|
|
|
|
DiesalGUI.UIParent = DUIParent
|
|
|
|
|
|
|
|
DiesalGUI.Scaling = {
|
|
|
|
physicalWidth = 0,
|
|
|
|
physicalHeight = 0,
|
|
|
|
screenWidth = 0,
|
|
|
|
screenHeight = 0,
|
|
|
|
originalHeight = DUIParent:GetHeight(),
|
|
|
|
resolution = "0x0",
|
|
|
|
perfect = 0,
|
|
|
|
uiscale = UIParent:GetScale()
|
|
|
|
}
|
|
|
|
|
|
|
|
DiesalGUI.Scaling.physicalWidth, DiesalGUI.Scaling.physicalHeight = GetPhysicalScreenSize()
|
|
|
|
DiesalGUI.Scaling.screenWidth, DiesalGUI.Scaling.screenHeight = GetScreenWidth(), GetScreenHeight()
|
|
|
|
DiesalGUI.Scaling.resolution = format("%dx%d", DiesalGUI.Scaling.physicalWidth, DiesalGUI.Scaling.physicalHeight)
|
|
|
|
DiesalGUI.Scaling.perfect = 768 / DiesalGUI.Scaling.physicalWidth
|
|
|
|
|
|
|
|
function DiesalGUI:UIScale()
|
|
|
|
local D = self
|
|
|
|
D.Scaling.uiscale = UIParent:GetScale()
|
|
|
|
D.Scaling.screenWidth = GetScreenWidth() * UIParent:GetEffectiveScale() / 768
|
|
|
|
D.Scaling.screenHeight = GetScreenWidth() * UIParent:GetEffectiveScale() / 768
|
|
|
|
|
|
|
|
local width, height = D.Scaling.screenWidth, D.Scaling.screenHeight
|
|
|
|
|
|
|
|
D.UIParent:SetSize(width, height)
|
|
|
|
D.Scaling.originalHeight = D.UIParent:GetHeight()
|
|
|
|
end
|
|
|
|
|
|
|
|
function DiesalGUI:PixelScaleChanged(event)
|
|
|
|
if event == "UI_SCALE_CHANGED" then
|
|
|
|
local S = DiesalGUI.Scaling
|
|
|
|
S.physicalWidth, S.physicalHeight = GetPhysicalScreenSize()
|
|
|
|
S.resolution = format("%dx%d", S.physicalWidth, S.physicalHeight)
|
|
|
|
S.perfect = 768 / S.physicalHeight
|
|
|
|
end
|
|
|
|
self:UIScale()
|
|
|
|
end
|
|
|
|
|
|
|
|
DiesalGUI:UIScale()
|
|
|
|
|
|
|
|
DUIParent:SetScript("OnEvent", function(self, event, ...)
|
|
|
|
if event == "UI_SCALE_CHANGED" then
|
|
|
|
DiesalGUI:PixelScaleChanged(event)
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
|
|
|
|
DUIParent:RegisterEvent("UI_SCALE_CHANGED")
|
|
|
|
|
|
|
|
local CallbackHandler = LibStub("CallbackHandler-1.0")
|
|
|
|
|
|
|
|
local DiesalTools = LibStub("DiesalTools-2.0")
|
|
|
|
local DiesalStyle = LibStub("DiesalStyle-2.0")
|
|
|
|
|
|
|
|
local type, select, tonumber = type, select, tonumber
|
|
|
|
local setmetatable, getmetatable, next = setmetatable, getmetatable, next
|
|
|
|
local pairs, ipairs = pairs, ipairs
|
|
|
|
local tinsert, tremove = table.insert, table.remove
|
|
|
|
|
|
|
|
local CreateFrame, UIParent = CreateFrame, UIParent
|
|
|
|
|
|
|
|
DiesalGUI.callbacks = DiesalGUI.callbacks or CallbackHandler:New(DiesalGUI)
|
|
|
|
|
|
|
|
---@class Diesal.GUI.Object.Factory : { [Diesal.Object.Type]: fun(name?: string): Diesal.Object }
|
|
|
|
DiesalGUI.ObjectFactory = DiesalGUI.ObjectFactory or {}
|
|
|
|
|
|
|
|
---@class Diesal.GUI.Object.Versions : { [Diesal.Object.Type]: number }
|
|
|
|
DiesalGUI.ObjectVersions = DiesalGUI.ObjectVersions or {}
|
|
|
|
|
|
|
|
---@class Diesal.GUI.Object.Pool : { [Diesal.Object.Type]: { [Diesal.Object]: boolean } }
|
|
|
|
DiesalGUI.ObjectPool = DiesalGUI.ObjectPool or {}
|
|
|
|
|
|
|
|
---@class Diesal.GUI.Object.Counts : { [Diesal.Object.Type]: number }
|
|
|
|
DiesalGUI.counts = DiesalGUI.counts or {}
|
|
|
|
|
|
|
|
---@class Diesal.GUI.Object.AntiPool : { [Diesal.Object.Type]: boolean }
|
|
|
|
DiesalGUI.AntiPool = DiesalGUI.AntiPool or {}
|
|
|
|
|
|
|
|
local ObjectFactory = DiesalGUI.ObjectFactory
|
|
|
|
local ObjectVersions = DiesalGUI.ObjectVersions
|
|
|
|
local ObjectPool = DiesalGUI.ObjectPool
|
|
|
|
|
|
|
|
local function OnMouse(frame, button)
|
|
|
|
DiesalGUI:ClearFocus()
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Capture mouse clicks on the WorldFrame
|
|
|
|
---@param frame Frame
|
|
|
|
---@param button Button
|
|
|
|
local function WorldFrameOnMouse(frame, button)
|
|
|
|
OnMouse(frame, button)
|
|
|
|
end
|
|
|
|
|
|
|
|
---@alias Diesal.Object
|
|
|
|
---|DiesalAccordian
|
|
|
|
---|DiesalAccordianSection
|
|
|
|
---|DiesalBar
|
|
|
|
---|DiesalBranch
|
|
|
|
---|DiesalButton
|
|
|
|
---|DGUIBuilder.Container
|
|
|
|
---|DiesalCheckBox
|
|
|
|
---|DiesalComboBox
|
|
|
|
---|DiesalComboBoxItem
|
|
|
|
---|DiesalDropDown
|
|
|
|
---|DiesalDropDownItem
|
|
|
|
---|DiesalInput
|
|
|
|
---|DiesalObjectBase
|
|
|
|
---|DiesalQuickDoc
|
|
|
|
---|DiesalScrollFrame
|
|
|
|
---|DiesalScrollingEditBox
|
|
|
|
---|DiesalScrollingMessageFrame
|
|
|
|
---|DiesalSpinner
|
|
|
|
---|DiesalText
|
|
|
|
---|DiesalTree
|
|
|
|
---|DiesalWindow
|
|
|
|
|
|
|
|
WorldFrame:HookScript("OnMouseDown", WorldFrameOnMouse)
|
|
|
|
|
|
|
|
-- Objects (widgets) that are defined for use in DiesalGUI
|
|
|
|
---@alias Diesal.Object.Type
|
|
|
|
---|'"DiesalAccordian"'
|
|
|
|
---|'"DiesalAccordianSection"'
|
|
|
|
---|'"DiesalBar"'
|
|
|
|
---|'"DiesalBranch"'
|
|
|
|
---|'"DiesalButton"'
|
|
|
|
---|'"DiesalCheckBox"'
|
|
|
|
---|'"DiesalComboBox"'
|
|
|
|
---|'"DiesalComboBoxItem"'
|
|
|
|
---|'"DiesalDropDown"'
|
|
|
|
---|'"DiesalDropDownItem"'
|
|
|
|
---|'"DiesalInput"'
|
|
|
|
---|'"DiesalObjectBase"'
|
|
|
|
---|'"DiesalQuickDoc"'
|
|
|
|
---|'"DiesalScrollFrame"'
|
|
|
|
---|'"DiesalScrollingEditBox"'
|
|
|
|
---|'"DiesalScrollingMessageFrame"'
|
|
|
|
---|'"DiesalSpinner"'
|
|
|
|
---|'"DiesalText"'
|
|
|
|
---|'"DiesalTree"'
|
|
|
|
---|'"DiesalWindow"'
|
|
|
|
---|'"Container"'
|
|
|
|
---|'"VerticalLayout"'
|
|
|
|
|
|
|
|
-- Returns a new object
|
|
|
|
---@generic T
|
|
|
|
---@param objectType `T` | Diesal.Object.Type
|
|
|
|
---@param base? boolean
|
|
|
|
---@return T newObj
|
|
|
|
local function newObject(objectType, base)
|
|
|
|
if not ObjectFactory[objectType] then
|
|
|
|
error("Attempt to construct unknown Object type", 2)
|
|
|
|
end
|
|
|
|
|
|
|
|
if base then
|
|
|
|
return ObjectFactory.DiesalObjectBase(objectType)
|
|
|
|
end
|
|
|
|
|
|
|
|
ObjectPool[objectType] = ObjectPool[objectType] or {}
|
|
|
|
|
|
|
|
local newObj = next(ObjectPool[objectType])
|
|
|
|
if not newObj then
|
|
|
|
newObj = ObjectFactory[objectType]()
|
|
|
|
else
|
|
|
|
ObjectPool[objectType][newObj] = nil
|
|
|
|
end
|
|
|
|
|
|
|
|
return newObj
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Releases an object into ReleasedObjects
|
|
|
|
---@param obj Diesal.Object
|
|
|
|
---@param objectType Diesal.Object.Type
|
|
|
|
local function releaseObject(obj, objectType)
|
|
|
|
ObjectPool[objectType] = ObjectPool[objectType] or {}
|
|
|
|
|
|
|
|
if ObjectPool[objectType][obj] then
|
|
|
|
error("Attempt to Release Object that is already released", 2)
|
|
|
|
end
|
|
|
|
ObjectPool[objectType][obj] = true
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Registers an Object constructor in the ObjectFactory
|
|
|
|
---@generic T : Diesal.Object.Type
|
|
|
|
---@param Type `T`
|
|
|
|
---@param constructor fun(name: string): T
|
|
|
|
---@param version number
|
|
|
|
function DiesalGUI:RegisterObjectConstructor(Type, constructor, version)
|
|
|
|
assert(type(constructor) == "function")
|
|
|
|
assert(type(version) == "number")
|
|
|
|
|
|
|
|
local oldVersion = ObjectVersions[Type]
|
|
|
|
if oldVersion and oldVersion >= version then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
ObjectVersions[Type] = version
|
|
|
|
ObjectFactory[Type] = constructor
|
|
|
|
end
|
|
|
|
|
|
|
|
--[[ -- Registers an Object constructor in the ObjectFactory
|
|
|
|
---@generic T : Diesal.Object.Type
|
|
|
|
---@param Type `T`
|
|
|
|
---@param constructor fun(self: Diesal.GUI, type: string | Diesal.Object.Type): Diesal.GUI.ObjectBase
|
|
|
|
---@param version number
|
|
|
|
function DiesalGUI:RegisterObjectBaseConstructor(Type, constructor, version)
|
|
|
|
assert(type(constructor) == "function")
|
|
|
|
assert(type(version) == "number")
|
|
|
|
|
|
|
|
local oldVersion = ObjectVersions[Type]
|
|
|
|
if oldVersion and oldVersion >= version then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
ObjectVersions[Type] = version
|
|
|
|
ObjectFactory[Type] = constructor
|
|
|
|
|
|
|
|
self.CreateObjectBase = constructor
|
|
|
|
end ]]
|
|
|
|
|
|
|
|
-- Create a new Object
|
|
|
|
---@generic T
|
|
|
|
---@param objectType `T` | Diesal.Object.Type
|
|
|
|
---@param name? string | boolean
|
|
|
|
---@param methods? table<string, fun(self?: self, ...): any>
|
|
|
|
---@return T object
|
|
|
|
function DiesalGUI:Create(objectType, name, methods)
|
|
|
|
local base = (name ~= nil and type(name) == "boolean") and name or nil
|
|
|
|
local name = (name ~= nil and type(name) == "string") and name or nil
|
|
|
|
if ObjectFactory[objectType] then
|
|
|
|
local object
|
|
|
|
if name then -- needs a specific name, bypass the objectPool and create a new object
|
|
|
|
object = ObjectFactory[objectType](name)
|
|
|
|
else
|
|
|
|
object = newObject(objectType, base)
|
|
|
|
end
|
|
|
|
if type(methods) == "table" then
|
|
|
|
object:SetMethods(methods)
|
|
|
|
end
|
|
|
|
object:ResetSettings()
|
|
|
|
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
if object.OnAcquire then
|
|
|
|
object:OnAcquire()
|
|
|
|
end
|
|
|
|
if object.SetScale then
|
|
|
|
object:SetScale(DUIParent:GetEffectiveScale() * GetScreenHeight() / 768)
|
|
|
|
end
|
|
|
|
return object
|
|
|
|
---@diagnostic disable-next-line: missing-return
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
---@param duration number
|
|
|
|
---@param callback fun(...)
|
|
|
|
function DiesalGUI:CreateThrottle(duration, callback)
|
|
|
|
assert(callback and type(callback) == "function", "callback has to be a function ")
|
|
|
|
assert(duration and type(duration) == "number", "duration has to be a number ")
|
|
|
|
|
|
|
|
---@class Throttle : AnimationGroup, Object, FrameScriptObject
|
|
|
|
local throttle = CreateFrame("Frame", nil, DUIParent):CreateAnimationGroup()
|
|
|
|
throttle.anim = throttle:CreateAnimation("Animation")
|
|
|
|
throttle.args = {}
|
|
|
|
|
|
|
|
local mt = getmetatable(throttle)
|
|
|
|
---@cast mt Throttle, metatable
|
|
|
|
|
|
|
|
mt.__index.SetCallback = function(this, callback)
|
|
|
|
assert(callback and type(callback) == "function", "callback required to be a function ")
|
|
|
|
this:SetScript("OnFinished", function()
|
|
|
|
callback(unpack(this.args))
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
|
|
|
mt.__index.AddCallback = function(this, callback)
|
|
|
|
assert(callback and type(callback) == "function", "callback required to be a function ")
|
|
|
|
this:HookScript("OnFinished", function()
|
|
|
|
callback(unpack(this.args))
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
|
|
|
mt.__index.SetDuration = function(this, callback)
|
|
|
|
assert(callback and type(callback) == "number", "duration has to be a number ")
|
|
|
|
this.anim:SetDuration(callback)
|
|
|
|
end
|
|
|
|
|
|
|
|
mt.__call = function(this, ...)
|
|
|
|
this.args = { ... }
|
|
|
|
this:Stop()
|
|
|
|
this:Play()
|
|
|
|
end
|
|
|
|
|
|
|
|
throttle = setmetatable(throttle, mt)
|
|
|
|
|
|
|
|
throttle:SetScript("OnFinished", function()
|
|
|
|
callback(unpack(throttle.args))
|
|
|
|
end)
|
|
|
|
throttle:SetDuration(duration)
|
|
|
|
|
|
|
|
return throttle
|
|
|
|
end
|
|
|
|
|
|
|
|
---@param object Diesal.Object
|
|
|
|
function DiesalGUI:Release(object)
|
|
|
|
if object.OnRelease then
|
|
|
|
object:OnRelease()
|
|
|
|
end
|
|
|
|
|
|
|
|
object:FireEvent("OnRelease")
|
|
|
|
|
|
|
|
object:ReleaseChildren()
|
|
|
|
object:ReleaseTextures()
|
|
|
|
object:ResetFonts()
|
|
|
|
object:ResetEventListeners()
|
|
|
|
|
|
|
|
object.frame:ClearAllPoints()
|
|
|
|
object.frame:Hide()
|
|
|
|
object.frame:SetParent(UIParent)
|
|
|
|
releaseObject(object, object.type)
|
|
|
|
end
|
|
|
|
|
|
|
|
---@alias Diesal.GUI.Object.Focusable
|
|
|
|
---|Diesal.GUI.Object.ComboBox
|
|
|
|
---|Diesal.GUI.Object.DropDown
|
|
|
|
|
|
|
|
-- Set FocusedObject: Menu, Dropdown, editBox etc....
|
|
|
|
---@param object Diesal.GUI.Object.Focusable
|
|
|
|
function DiesalGUI:SetFocus(object)
|
|
|
|
if self.FocusedObject and self.FocusedObject ~= object then
|
|
|
|
DiesalGUI:ClearFocus()
|
|
|
|
end
|
|
|
|
self.FocusedObject = object
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Clear focus from the FocusedObject
|
|
|
|
function DiesalGUI:ClearFocus()
|
|
|
|
local FocusedObject = self.FocusedObject
|
|
|
|
if FocusedObject then
|
|
|
|
if FocusedObject.ClearFocus then -- FocusedObject is Focusable Frame
|
|
|
|
FocusedObject:ClearFocus()
|
|
|
|
end
|
|
|
|
self.FocusedObject = nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Mouse Input capture for any DiesalGUI interactive region
|
|
|
|
---@param frame Frame
|
|
|
|
---@param button MouseButton | "MouseWheel"
|
|
|
|
function DiesalGUI:OnMouse(frame, button)
|
|
|
|
-- print(button)
|
|
|
|
OnMouse(frame, button)
|
|
|
|
DiesalGUI.callbacks:Fire("DiesalGUI_OnMouse", frame, button)
|
|
|
|
end
|
|
|
|
|
|
|
|
--- A type-based counter to count the number of widgets created.
|
|
|
|
---@param objectType Diesal.Object.Type
|
|
|
|
function DiesalGUI:GetNextObjectNum(objectType)
|
|
|
|
if not self.counts[objectType] then
|
|
|
|
self.counts[objectType] = 0
|
|
|
|
end
|
|
|
|
self.counts[objectType] = self.counts[objectType] + 1
|
|
|
|
return self.counts[objectType]
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Return the number of created widgets for this type.
|
|
|
|
---@param objectType Diesal.Object.Type
|
|
|
|
function DiesalGUI:GetObjectCount(objectType)
|
|
|
|
return self.counts[objectType] or 0
|
|
|
|
end
|