You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
DiesalLibs/DiesalGUI-2.0/DiesalGUI-2.0.lua

362 lines
11 KiB

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 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
---@diagnostic disable-next-line: undefined-field
_G.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
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, UIParent):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