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.
2260 lines
82 KiB
2260 lines
82 KiB
--========================================================--
|
|
-- Scorpio Core System --
|
|
-- --
|
|
-- Author : kurapica125@outlook.com --
|
|
-- Create Date : 2020/07/13 --
|
|
-- Update Date : 2021/06/24 --
|
|
--========================================================--
|
|
|
|
--========================================================--
|
|
Scorpio "Scorpio.UI.Core" "1.1.0"
|
|
--========================================================--
|
|
|
|
import "System.Reactive"
|
|
|
|
--- Clear the property value of the target object
|
|
local NIL = Namespace.SaveNamespace("Scorpio.UI.NIL", prototype { __tostring = function() return "nil" end })
|
|
|
|
--- Clear the property value of the settings(so other settings may be used for the property)
|
|
local CLEAR = Namespace.SaveNamespace("Scorpio.UI.CLEAR", prototype { __tostring = function() return "clear" end })
|
|
|
|
local isObjectType = Class.IsObjectType
|
|
local isTypeValidDisabled = System.Platform.TYPE_VALIDATION_DISABLED
|
|
local isUIObject = UI.IsUIObject
|
|
local isUIObjectType = UI.IsUIObjectType
|
|
local isSubType = Class.IsSubType
|
|
local clone = Toolset.clone
|
|
local yield = coroutine.yield
|
|
local tinsert = table.insert
|
|
local tremove = table.remove
|
|
local strlower = strlower
|
|
local tsort = table.sort
|
|
local isObservable = function(val) return type(val) == "table" and isObjectType(val, IObservable) end
|
|
local gettable = function(self, key) local val = self[key] if not val or val == NIL or val == CLEAR then val = {} self[key] = val end return val end
|
|
|
|
local parentPath = {}
|
|
|
|
local CHILD_SETTING = 0 -- For children
|
|
|
|
local INSTANT_STYLE_UI_CLASS = {}
|
|
|
|
----------------------------------------------
|
|
-- Helper - Property --
|
|
----------------------------------------------
|
|
local _Property = {}
|
|
|
|
local _RecycleHolder = CreateFrame("Frame") _RecycleHolder:Hide()
|
|
local _PropertyChildName = setmetatable({}, META_WEAKKEY)
|
|
local _PropertyChildMap = setmetatable({}, { __index = function(self, prop) local val = setmetatable({}, META_WEAKALL) rawset(self, prop, val) return val end })
|
|
local _PropertyChildRecycle = setmetatable({}, {
|
|
__index = function(self, type)
|
|
if isSubType(type, AnimationGroup) or isSubType(type, ControlPoint) or isSubType(type, Animation) then
|
|
-- No recycle for the animation type, since they can't change their parent
|
|
-- No recycle to the mask texture, it's very special since its parent should be the texture's parent
|
|
rawset(self, type, false)
|
|
return false
|
|
else
|
|
local recycle = Recycle(type, "__" .. Namespace.GetNamespaceName(type):gsub("%.", "_") .. "%d", _RecycleHolder)
|
|
rawset(self, type, recycle)
|
|
return recycle
|
|
end
|
|
end,
|
|
__call = function(self, obj)
|
|
if obj.Disposed then return true end
|
|
|
|
local cls = getmetatable(obj)
|
|
local recycle = cls and rawget(self, cls)
|
|
|
|
if recycle then
|
|
obj:SetParent(_RecycleHolder)
|
|
if obj.ClearAllPoints then obj:ClearAllPoints() end
|
|
recycle(obj)
|
|
|
|
return true
|
|
end
|
|
end
|
|
})
|
|
|
|
-- The objservable map
|
|
local _ObsProp = setmetatable({}, META_WEAKKEY)
|
|
|
|
local function dispatchPropertySetting(cls, prop, setting, oldsetting, root)
|
|
local settings = _Property[cls]
|
|
if not settings then return end -- So it doesn't finished the definition
|
|
|
|
if root then
|
|
oldsetting = settings[prop]
|
|
elseif settings[prop] and oldsetting ~= settings[prop] then
|
|
return
|
|
end
|
|
|
|
settings[prop] = setting
|
|
|
|
for scls in Class.GetSubTypes(cls) do
|
|
dispatchPropertySetting(scls, prop, setting, oldsetting)
|
|
end
|
|
end
|
|
|
|
Runtime.OnTypeDefined = Runtime.OnTypeDefined + function(ptype, cls)
|
|
if ptype == Class and IsUIObjectType(cls) then
|
|
if not _Property[cls] then
|
|
Trace("[Scorpio.UI]Init Property List for %q", tostring(cls))
|
|
|
|
local super = Class.GetSuperClass(cls)
|
|
if super and _Property[super] then
|
|
_Property[cls] = clone(_Property[super])
|
|
else
|
|
_Property[cls] = {}
|
|
end
|
|
end
|
|
|
|
local _Prop = System.Property
|
|
|
|
-- Scan the class's property
|
|
for name, feature in Class.GetFeatures(cls) do
|
|
if _Prop.Validate(feature) and not _Prop.IsStatic(feature) and _Prop.IsWritable(feature) and not _Prop.IsIndexer(feature) and not __Observable__.IsObservableProperty(feature) then
|
|
Trace("[Scorpio.UI]Define Property %s for %s", name, tostring(cls))
|
|
|
|
local ptype = _Prop.GetType(feature)
|
|
UI.Property {
|
|
name = name,
|
|
type = ptype,
|
|
require = cls,
|
|
set = function(self, val) self[name] = val end,
|
|
get = _Prop.IsReadable(feature) and function(self) return self[name] end or nil,
|
|
default = _Prop.GetDefault(feature),
|
|
nilable = not _Prop.IsValueRequired(feature),
|
|
childtype = ptype and IsUIObjectType(ptype) and ptype or nil,
|
|
}
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local applyStylesOnFrame, setCustomStyle
|
|
|
|
local function applyProperty(self, prop, value)
|
|
--Trace("[Scorpio.UI]Apply Property:%s - %s", prop.name, tostring(value))
|
|
|
|
-- Check the observable map
|
|
local map = _ObsProp[self]
|
|
if map and map[prop] then
|
|
map[prop]:Unsubscribe()
|
|
map[prop]:Resubscribe()
|
|
end
|
|
|
|
if value == nil then
|
|
if map then map[prop] = nil end
|
|
if prop.clear then return prop.clear(self) end
|
|
if prop.default ~= nil then return prop.set(self, clone(prop.default, true)) end
|
|
if prop.nilable then return prop.set(self, nil) end
|
|
elseif prop.set then
|
|
local pset = prop.set
|
|
if isObservable(value) then
|
|
if not map then
|
|
map = {}
|
|
_ObsProp[self] = map
|
|
end
|
|
|
|
if not map[prop] then
|
|
if prop.clear then
|
|
local clear = prop.clear
|
|
map[prop] = Observer(function(val) if val ~= nil then return pset(self, val) else return clear(self) end end)
|
|
elseif prop.nilable then
|
|
map[prop] = Observer(function(val) return pset(self, val) end)
|
|
elseif prop.default ~= nil then
|
|
local dft = prop.default
|
|
map[prop] = Observer(function(val) if val ~= nil then return pset(self, val) else return pset(self, dft) end end)
|
|
else
|
|
map[prop] = Observer(function(val) if val ~= nil then return pset(self,val) end end )
|
|
end
|
|
end
|
|
|
|
value:Subscribe(map[prop])
|
|
else
|
|
-- Check for the child type
|
|
if value == true and prop.childtype then return end
|
|
if map then map[prop] = nil end
|
|
pset(self, clone(value, true))
|
|
end
|
|
elseif prop.childtype and isObservable(value) then
|
|
if not map then
|
|
map = {}
|
|
_ObsProp[self] = map
|
|
end
|
|
if not map[prop] then
|
|
map[prop] = Observer(function(val)
|
|
if type(val) == "table" and getmetatable(val) == nil then
|
|
local child, new = prop.get(self)
|
|
if child then
|
|
local ok, err = pcall(setCustomStyle, child, nil, val, 1, new)
|
|
if not ok then
|
|
Error("[Scorpio.UI]Set custom style to child %q of %s failed - %s", prop.name, self:GetName(true), err)
|
|
end
|
|
else
|
|
Error("[Scorpio.UI]Auto-gen property child %q for %s failed", prop.name, self:GetName(true))
|
|
end
|
|
else
|
|
prop.clear(self)
|
|
end
|
|
end)
|
|
end
|
|
value:Subscribe(map[prop])
|
|
end
|
|
end
|
|
|
|
local function getUIPrototype(self)
|
|
local cls = getmetatable(self)
|
|
if isUIObjectType(cls) then return cls, true end
|
|
if self.GetObjectType then return UI[self:GetObjectType()], false end
|
|
end
|
|
|
|
----------------------------------------------
|
|
-- Helper - Style --
|
|
----------------------------------------------
|
|
local _StyleMethods = {}
|
|
local _StyleOwner
|
|
local _StyleAccessor
|
|
local _StyleQueue = Queue()
|
|
local _ClearQueue = Queue()
|
|
local _ClassQueue = Queue()
|
|
local _Recycle = Recycle()
|
|
|
|
local _DefaultStyle = {}
|
|
local _CustomStyle = setmetatable({}, META_WEAKKEY)
|
|
local _ClassFrames = {}
|
|
|
|
local _CurrentStyleTarget -- The current style target could be used in other systems(ex. Reactive)
|
|
|
|
-- Combine the sharable settings
|
|
local function prepareSettings(settings, target, final, cache, paths)
|
|
-- Simple Check
|
|
if #settings == 0 and not final then return settings end
|
|
|
|
local isClassSkin = getmetatable(target) == nil
|
|
local props = _Property[isClassSkin and target[#target] or getUIPrototype(target)]
|
|
if not props then return final or settings end -- No more operations
|
|
|
|
local needRecycle = not cache
|
|
cache = cache or _Recycle()
|
|
if needRecycle and final then for k in pairs(final) do cache[strlower(k)] = k end end
|
|
final = final or {}
|
|
|
|
local classCnt
|
|
|
|
if isClassSkin then
|
|
paths = paths or {}
|
|
classCnt = #target
|
|
end
|
|
|
|
-- Check the share features provided with N-th index
|
|
local shares = _Recycle()
|
|
|
|
for k, v in pairs(settings) do
|
|
local tk = type(k)
|
|
|
|
if tk == "number" then
|
|
tinsert(shares, k)
|
|
elseif tk == "string" then
|
|
local lk = strlower(k)
|
|
local ek = cache[lk]
|
|
local prop = props[lk]
|
|
|
|
if isClassSkin then
|
|
local element
|
|
|
|
paths[classCnt] = k
|
|
|
|
for i = 1, #target do
|
|
element = __Template__.GetElementType(target[i], unpack(paths, i))
|
|
if element then break end
|
|
end
|
|
|
|
paths[classCnt] = nil
|
|
|
|
if element then
|
|
tinsert(target, element)
|
|
tinsert(paths, k)
|
|
|
|
if not ek then
|
|
cache[lk] = k
|
|
if type(v) == "table" and getmetatable(v) == nil then
|
|
final[k] = prepareSettings(v, target, nil, nil, paths)
|
|
else
|
|
-- Error but will be checked later
|
|
final[k] = v
|
|
end
|
|
elseif type(final[ek]) == "table" and getmetatable(final[ek]) == nil and type(v) == "table" and getmetatable(v) == nil then
|
|
-- Combine the share settings
|
|
final[ek] = prepareSettings(v, target, final[ek], nil, paths)
|
|
end
|
|
|
|
tremove(target)
|
|
tremove(paths)
|
|
elseif prop and prop.childtype then
|
|
if not ek then
|
|
cache[lk] = k
|
|
if type(v) == "table" and getmetatable(v) == nil then
|
|
final[k]= prepareSettings(v, { prop.childtype } )
|
|
else
|
|
-- Error or observable object will be checked later
|
|
final[k]= v
|
|
end
|
|
elseif type(final[ek]) == "table" and getmetatable(final[ek]) == nil and type(v) == "table" and getmetatable(v) == nil then
|
|
-- Combine the share settings
|
|
final[ek] = prepareSettings(v, { prop.childtype }, final[ek])
|
|
end
|
|
elseif not ek then
|
|
-- No property check here, the error would be raised by the caller
|
|
cache[lk] = k
|
|
final[k] = v
|
|
end
|
|
else
|
|
local element = UIObject.GetChild(target, k) or prop and prop.childtype and { prop.childtype }
|
|
|
|
if element then
|
|
if not ek then
|
|
cache[lk] = k
|
|
if type(v) == "table" and getmetatable(v) == nil then
|
|
final[k]= prepareSettings(v, element)
|
|
else
|
|
-- Error or observable object will be checked later
|
|
final[k]= v
|
|
end
|
|
elseif type(final[ek]) == "table" and getmetatable(final[ek]) == nil and type(v) == "table" and getmetatable(v) == nil then
|
|
-- Combine the share settings
|
|
final[ek] = prepareSettings(v, element, final[ek])
|
|
end
|
|
elseif not ek then
|
|
-- No property check here, the error would be raised by the caller
|
|
cache[lk] = k
|
|
final[k] = v
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if #shares > 0 then
|
|
tsort(shares)
|
|
|
|
for i = #shares, 1, -1 do
|
|
-- Could have its own share skins
|
|
prepareSettings(settings[shares[i]], target, final, cache, paths)
|
|
end
|
|
end
|
|
|
|
_Recycle(wipe(shares))
|
|
if needRecycle then _Recycle(wipe(cache)) end
|
|
return final
|
|
end
|
|
|
|
local function collectPropertyChild(frame)
|
|
if _ClearQueue[frame] then return end
|
|
if _ClearQueue.Count == 0 then FireSystemEvent("SCORPIO_UI_COLLECT_PROPERTY_CHILD") end
|
|
_ClearQueue[frame] = true
|
|
_ClearQueue:Enqueue(frame)
|
|
end
|
|
|
|
local function applyStyle(frame)
|
|
if _StyleQueue[frame] then return end
|
|
if _StyleQueue.Count == 0 then FireSystemEvent("SCORPIO_UI_APPLY_STYLE") end
|
|
_StyleQueue[frame] = true
|
|
_StyleQueue:Enqueue(frame)
|
|
end
|
|
|
|
local function queueClassFrames(class)
|
|
if not _ClassQueue[class] then
|
|
if _ClassQueue.Count == 0 then FireSystemEvent("SCORPIO_UI_UPDATE_CLASS_SKIN") end
|
|
_ClassQueue[class] = true
|
|
_ClassQueue:Enqueue(class)
|
|
end
|
|
|
|
for scls in Class.GetSubTypes(class) do
|
|
queueClassFrames(scls)
|
|
end
|
|
end
|
|
|
|
local function emptyDefaultStyle(settings)
|
|
if settings and settings ~= NIL and settings ~= CLEAR then
|
|
for k, v in pairs(settings) do
|
|
if k == CHILD_SETTING then
|
|
for child, csetting in pairs(v) do
|
|
emptyDefaultStyle(csetting)
|
|
end
|
|
else
|
|
settings[k] = CLEAR
|
|
end
|
|
end
|
|
end
|
|
|
|
return settings
|
|
end
|
|
|
|
function setCustomStyle(target, pname, value, stack, nodirectapply)
|
|
local custom = gettable(_CustomStyle, target)
|
|
local haschanges = false
|
|
local directapply = not _StyleQueue[target] and not nodirectapply
|
|
|
|
local props = _Property[getUIPrototype(target)]
|
|
if not props then error("The target has no property definitions", stack + 1) end
|
|
|
|
if pname then
|
|
local prop = props[pname]
|
|
local cval
|
|
|
|
if prop.childtype then
|
|
cval = true -- So the system can track the child property
|
|
directapply = false
|
|
|
|
if value == nil or value == CLEAR or value == NIL then
|
|
cval = value or CLEAR
|
|
elseif type(value) == "table" and getmetatable(value) == nil then
|
|
local child, new= prop.get(target)
|
|
if child then
|
|
setCustomStyle(child, nil, value, stack + 1, new)
|
|
else
|
|
error(strformat("The target has no child element from %q", pname), stack + 1)
|
|
end
|
|
elseif isObservable(value) then
|
|
-- So the property child could be generatd dynamically
|
|
cval = value
|
|
directapply = true
|
|
else
|
|
error(strformat("The %q is a child poperty, its setting should be a table", pname), stack + 1)
|
|
end
|
|
else
|
|
if value == nil or value == NIL or value == CLEAR then
|
|
directapply = false
|
|
cval = value or CLEAR
|
|
elseif isObservable(value) then
|
|
cval = value
|
|
else
|
|
if prop.validate then
|
|
local ret, msg = prop.validate(prop.type, value)
|
|
if msg then error(Struct.GetErrorMessage(msg, prop.name), stack + 1) end
|
|
value = ret
|
|
end
|
|
|
|
cval = value
|
|
end
|
|
end
|
|
|
|
if custom[pname] ~= cval then
|
|
custom[pname] = cval
|
|
haschanges = true
|
|
|
|
if directapply then
|
|
return applyProperty(target, prop, cval)
|
|
end
|
|
end
|
|
else
|
|
if type(value) ~= "table" then
|
|
error("The style settings must be property key value pair or a table contains the key-value pairs", stack + 1)
|
|
end
|
|
|
|
value = prepareSettings(value, target) -- Copy and remove the share settings
|
|
|
|
directapply = directapply and _Recycle()
|
|
|
|
for pn, pv in pairs(value) do
|
|
if type(pn) ~= "string" then
|
|
error("The style property name must be string", stack + 1)
|
|
end
|
|
|
|
local child = UIObject.GetChild(target, pn)
|
|
|
|
if child then
|
|
setCustomStyle(child, nil, pv, stack + 1)
|
|
else
|
|
local cval
|
|
local ln = strlower(pn)
|
|
local prop = props[ln]
|
|
local isclear = false
|
|
|
|
if not prop then error(strformat("The %q isn't a valid property for the target", pn), stack + 1) end
|
|
|
|
if prop.childtype then
|
|
cval = true
|
|
|
|
if pv == CLEAR or pv == NIL then
|
|
cval = pv
|
|
isclear = true
|
|
elseif type(pv) == "table" and getmetatable(pv) == nil then
|
|
local new
|
|
child, new = prop.get(target)
|
|
if child then
|
|
setCustomStyle(child, nil, pv, stack + 1, new)
|
|
else
|
|
error(strformat("The target has no child element from %q", pn), stack + 1)
|
|
end
|
|
else
|
|
error(strformat("The %q is a child poperty, its setting should be a table", pname), stack + 1)
|
|
end
|
|
else
|
|
if pv == NIL or pv == CLEAR then
|
|
cval = pv
|
|
isclear = true
|
|
elseif isObservable(pv) then
|
|
cval = pv
|
|
else
|
|
if prop.validate then
|
|
local ret, msg = prop.validate(prop.type, pv)
|
|
if msg then error(Struct.GetErrorMessage(msg, prop.name), stack + 1) end
|
|
pv = ret
|
|
end
|
|
|
|
cval = pv
|
|
end
|
|
end
|
|
|
|
if custom[ln] ~= cval then
|
|
custom[ln] = cval
|
|
haschanges = true
|
|
|
|
if directapply then
|
|
if isclear then
|
|
_Recycle(wipe(directapply))
|
|
directapply = nil
|
|
else
|
|
directapply[ln] = cval
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if directapply then
|
|
-- Instant apply custon settings, no need queue it
|
|
_CurrentStyleTarget = target
|
|
local ok, err = pcall(applyStylesOnFrame, target, directapply)
|
|
if not ok then Error("[Scorpio.UI]Apply Style Failed: %s", tostring(err)) end
|
|
_CurrentStyleTarget = nil
|
|
end
|
|
end
|
|
|
|
return haschanges and applyStyle(target)
|
|
end
|
|
|
|
local function registerFrame(cls, frame)
|
|
local map = _ClassFrames[cls]
|
|
if not map then
|
|
map = setmetatable({}, META_WEAKKEY)
|
|
_ClassFrames[cls] = map
|
|
end
|
|
|
|
map[frame] = true
|
|
|
|
applyStyle(frame)
|
|
end
|
|
|
|
local function unregisterFrame(frame)
|
|
_ClassFrames[getmetatable(frame)][frame] = nil
|
|
end
|
|
|
|
----------------------------------------------
|
|
-- Helper - Skin --
|
|
----------------------------------------------
|
|
local _Skins = {}
|
|
local _ActiveSkin = {}
|
|
|
|
local function copyBaseSkinSettings(container, base)
|
|
for k, v in pairs(base) do
|
|
if k == CHILD_SETTING then
|
|
for name, setting in pairs(v) do
|
|
copyBaseSkinSettings(gettable(gettable(container, k), name), setting)
|
|
end
|
|
else
|
|
container[k] = v
|
|
end
|
|
end
|
|
end
|
|
|
|
local function saveSkinSettings(classes, paths, container, settings)
|
|
if type(settings) ~= "table" then throw("The skin settings for " .. class .. "must be table") end
|
|
|
|
local pathIdx = #classes
|
|
local class = classes[pathIdx]
|
|
local props = _Property[class]
|
|
|
|
settings = prepareSettings(settings, classes) -- Copy and remove the share settings
|
|
|
|
-- Check inherit
|
|
for name, value in pairs(settings) do
|
|
if type(name) ~= "string" then throw("The skin settings only accpet string values as key") end
|
|
if strlower(name) == "inherit" then
|
|
settings[name] = nil
|
|
|
|
if type(value) ~= "string" then throw("The inherit only accpet skin name as value") end
|
|
local base = _Skins[strlower(value)]
|
|
if not base then
|
|
throw(strformat("The skin named %q doesn't existed", value))
|
|
elseif not base[class] then
|
|
throw(strformat("The skin named %q doesn't provide skin for %s", value, tostring(class)))
|
|
end
|
|
|
|
copyBaseSkinSettings(container, base[class])
|
|
|
|
break
|
|
end
|
|
end
|
|
|
|
for name, value in pairs(settings) do
|
|
local element
|
|
|
|
paths[pathIdx] = name
|
|
|
|
for i = 1, pathIdx do
|
|
element = __Template__.GetElementType(classes[i], unpack(paths, i))
|
|
if element then break end
|
|
end
|
|
|
|
paths[pathIdx] = nil
|
|
|
|
if element then
|
|
tinsert(classes, element)
|
|
tinsert(paths, name)
|
|
saveSkinSettings(classes, paths, gettable(gettable(container, CHILD_SETTING), name), value)
|
|
tremove(classes)
|
|
tremove(paths)
|
|
elseif props then
|
|
name = strlower(name)
|
|
local prop = props[name]
|
|
if not prop then throw(strformat("The %q isn't a valid property for %s", name, tostring(class))) end
|
|
|
|
if prop.childtype then
|
|
container[name] = true -- So we can easily track the child property settings
|
|
|
|
if value == NIL or value == CLEAR or isObservable(value) then
|
|
if container[CHILD_SETTING] then container[CHILD_SETTING][name] = nil end
|
|
container[name] = value
|
|
elseif type(value) == "table" and getmetatable(value) == nil then
|
|
saveSkinSettings({ prop.childtype }, {}, gettable(gettable(container, CHILD_SETTING), name), value)
|
|
else
|
|
throw(strformat("The %q is a child generated from property, need table as settings", name))
|
|
end
|
|
else
|
|
if value == NIL or value == CLEAR then
|
|
container[name] = value
|
|
elseif isObservable(value) then
|
|
container[name] = value
|
|
else
|
|
if prop.validate then
|
|
local ret, msg = prop.validate(prop.type, value)
|
|
if msg then throw(Struct.GetErrorMessage(msg, prop.name)) end
|
|
value = ret
|
|
end
|
|
|
|
container[name] = value
|
|
end
|
|
end
|
|
else
|
|
throw("The " .. class .. " has no property definitions")
|
|
end
|
|
end
|
|
end
|
|
|
|
local function copyToDefault(settings, default)
|
|
for name, value in pairs(settings) do
|
|
if name == CHILD_SETTING then
|
|
local childsettings = gettable(default, CHILD_SETTING)
|
|
for element, setting in pairs(value) do
|
|
copyToDefault(setting, gettable(childsettings, element))
|
|
end
|
|
else
|
|
default[name] = value
|
|
end
|
|
end
|
|
end
|
|
|
|
local function activeSkin(name, class, skin, force)
|
|
if force and _ActiveSkin[class] and _ActiveSkin[class] ~= name then return end
|
|
if not force and _ActiveSkin[class] == name then return end
|
|
_ActiveSkin[class] = name
|
|
|
|
local default = emptyDefaultStyle(_DefaultStyle[class]) or {}
|
|
_DefaultStyle[class] = default
|
|
|
|
copyToDefault(skin, default)
|
|
queueClassFrames(class)
|
|
end
|
|
|
|
----------------------------------------------
|
|
-- UIObject --
|
|
----------------------------------------------
|
|
__Abstract__() __Sealed__() class "UIObject"(function(_ENV)
|
|
|
|
----------------------------------------------
|
|
-- Helpers --
|
|
----------------------------------------------
|
|
local _NameMap = setmetatable({}, META_WEAKKEY)
|
|
local _ChildMap = setmetatable({}, META_WEAKKEY)
|
|
|
|
local _SetParent = getRealMethodCache("SetParent")
|
|
local _GetParent = getRealMethodCache("GetParent")
|
|
local _GetNew = getRealMetaMethodCache("__new")
|
|
|
|
local validate = Struct.ValidateValue
|
|
local isClass = Class.Validate
|
|
|
|
----------------------------------------------
|
|
-- event --
|
|
----------------------------------------------
|
|
-- Fired when parent is changed
|
|
event "OnParentChanged"
|
|
|
|
----------------------------------------------
|
|
-- Static Methods --
|
|
----------------------------------------------
|
|
--- Gets the ui object with the full name
|
|
__Static__() __Arguments__{ NEString }
|
|
function FromName(name)
|
|
local obj
|
|
|
|
for str in name:gmatch("[^%.]+") do
|
|
if not obj then
|
|
obj = validate(UI, _G[str], true)
|
|
else
|
|
local children = _ChildMap[obj[0]]
|
|
obj = children and children[str] or UIObject.GetPropertyChild(obj, str)
|
|
end
|
|
if not obj then return end
|
|
end
|
|
|
|
return obj
|
|
end
|
|
|
|
----------------------------------------------
|
|
-- Methods --
|
|
----------------------------------------------
|
|
--- Gets the ui object's name or full name
|
|
__Final__() function GetName(self, full)
|
|
local name = _NameMap[self[0]]
|
|
if name then
|
|
if full then
|
|
local globalUI = _G[name]
|
|
if globalUI and (globalUI == self or IsSameUI(self, globalUI)) then
|
|
return name
|
|
else
|
|
local parent = self:GetParent()
|
|
local pname = parent and parent:GetName(true)
|
|
name = _PropertyChildName[self] or name
|
|
if pname then return pname .. "." .. name end
|
|
end
|
|
else
|
|
return name
|
|
end
|
|
else
|
|
local getName = self.GetName
|
|
if getName ~= GetName then
|
|
return getName(self)
|
|
end
|
|
end
|
|
end
|
|
|
|
__Final__() function GetDebugName(self)
|
|
return self:GetName(true)
|
|
end
|
|
|
|
--- Gets the parent of ui object
|
|
__Final__() function GetParent(self)
|
|
local parent = (_GetParent[getmetatable(self)] or self.GetParent)(self)
|
|
return parent and GetProxyUI(parent)
|
|
end
|
|
|
|
--- Sets the ui object's name
|
|
__Final__() __Arguments__{ NEString }
|
|
function SetName(self, name)
|
|
local oname = _NameMap[self[0]]
|
|
if oname == name then return end
|
|
|
|
local globalUI = _G[oname]
|
|
if globalUI and (globalUI == self or IsSameUI(self, globalUI)) then
|
|
error("Usage: UI:SetName(name) - UI with global name can't change its name", 2)
|
|
end
|
|
|
|
local parent = self:GetParent()
|
|
if parent then
|
|
local pui = parent[0]
|
|
local children = _ChildMap[pui]
|
|
|
|
if children and children[name] then
|
|
error("Usage: UI:SetName(name) - the name is used by another child", 2)
|
|
end
|
|
|
|
if not children then
|
|
children = {}
|
|
_ChildMap[pui] = children
|
|
end
|
|
|
|
children[oname] = nil
|
|
children[name] = self
|
|
end
|
|
|
|
_NameMap[self[0]] = name
|
|
end
|
|
|
|
--- Sets the ui object's parent
|
|
__Final__()
|
|
function SetParent(self, parent)
|
|
if parent and not isUIObject(parent) then error("Usage : UI:SetParent([parent]) : the parent is not valid.", 2) end
|
|
|
|
local oparent = self:GetParent()
|
|
if oparent == parent or IsSameUI(oparent, parent) then return end
|
|
|
|
local name = _NameMap[self[0]]
|
|
local setParent = _SetParent[getmetatable(self)]
|
|
if not setParent then error("Usage : UI:SetParent([parent]) : the ui element can't change its parent.", 2) end
|
|
|
|
if oparent then
|
|
local pui = oparent[0]
|
|
if _ChildMap[pui] and _ChildMap[pui][name] == self then
|
|
_ChildMap[pui][name] = nil
|
|
end
|
|
end
|
|
|
|
if parent == nil then return pcall(setParent, self, nil) end
|
|
|
|
local pui = parent[0]
|
|
local children = _ChildMap[pui]
|
|
|
|
if children and children[name] and children[name] ~= self then error("Usage : UI:SetParent([parent]) : parent has another child with the same name.", 2) end
|
|
|
|
setParent(self, GetRawUI(parent))
|
|
|
|
if not children then
|
|
children = {}
|
|
_ChildMap[pui] = children
|
|
end
|
|
|
|
children[name] = self
|
|
|
|
OnParentChanged(self, parent, oparent)
|
|
end
|
|
|
|
--- Gets the children of the frame
|
|
__Iterator__() function GetChilds(self)
|
|
local children = _ChildMap[self[0]]
|
|
if children then
|
|
for name, child in pairs(children) do
|
|
yield(name, child)
|
|
end
|
|
end
|
|
end
|
|
|
|
--- Gets the child with the given name
|
|
function GetChild(self, name)
|
|
if name == 0 then return end
|
|
|
|
local children = _ChildMap[self[0]]
|
|
return children and children[name]
|
|
end
|
|
|
|
----------------------------------------------
|
|
-- Dispose --
|
|
----------------------------------------------
|
|
function Dispose(self)
|
|
local ui = self[0]
|
|
local name = _NameMap[ui]
|
|
|
|
unregisterFrame(self)
|
|
|
|
self:SetParent(nil)
|
|
|
|
-- Dispose the children
|
|
local children = _ChildMap[ui]
|
|
if children then
|
|
for _, obj in pairs(children) do obj:Dispose() end
|
|
end
|
|
|
|
-- Clear it from the _G
|
|
local globalUI = _G[name]
|
|
if globalUI and (globalUI == self or IsSameUI(self, globalUI)) then
|
|
_G[name] = nil
|
|
end
|
|
|
|
-- Clear register datas
|
|
_NameMap[ui] = nil
|
|
_ChildMap[ui] = nil
|
|
end
|
|
|
|
----------------------------------------------
|
|
-- Constructor --
|
|
----------------------------------------------
|
|
__Final__() __Arguments__{ NEString, UI/UIParent, Any * 0 }
|
|
function __exist(cls, name, parent, ...)
|
|
local children = _ChildMap[parent[0]]
|
|
local object = children and children[name]
|
|
|
|
if object then
|
|
if getmetatable(object) == cls then
|
|
return object
|
|
else
|
|
throw(("Usage : %s(name, parent, ...) - the parent already has a child named '%s'."):format(Namespace.GetNamespaceName(cls), name))
|
|
end
|
|
end
|
|
end
|
|
|
|
__Final__() __Arguments__{ UI }
|
|
function __exist(cls, ui)
|
|
local proxy = UI.GetProxyUI(ui)
|
|
if proxy and isClass(getmetatable(proxy)) then return proxy end
|
|
end
|
|
|
|
__Final__() __Arguments__{ NEString, UI/UIParent, Any * 0 }
|
|
function __new(cls, name, parent, ...)
|
|
local self = _GetNew[cls](cls, name, parent, ...)
|
|
parent = parent[0]
|
|
|
|
local children = _ChildMap[parent]
|
|
|
|
if not children then
|
|
children = {}
|
|
_ChildMap[parent] = children
|
|
end
|
|
|
|
children[name] = self
|
|
_NameMap[self[0]] = name
|
|
|
|
registerFrame(cls, self)
|
|
|
|
return self
|
|
end
|
|
|
|
__Final__() __Arguments__{ UI }
|
|
function __new(cls, ui)
|
|
local self = { [0] = ui[0] }
|
|
UI.RegisterProxyUI(self)
|
|
UI.RegisterRawUI(ui)
|
|
return self
|
|
end
|
|
|
|
----------------------------------------------
|
|
-- Meta-Method --
|
|
----------------------------------------------
|
|
__index = GetChild
|
|
end)
|
|
|
|
----------------------------------------------
|
|
-- __Bubbling__ --
|
|
----------------------------------------------
|
|
__Sealed__() class "__Bubbling__" (function(_ENV)
|
|
extend "IApplyAttribute"
|
|
|
|
local getChild = UIObject.GetChild
|
|
|
|
local function getTarget(owner, name)
|
|
for s in name:gmatch("[^%.]+") do
|
|
owner = owner and getChild(owner, s)
|
|
end
|
|
return owner
|
|
end
|
|
|
|
-----------------------------------------------------------
|
|
-- property --
|
|
-----------------------------------------------------------
|
|
property "AttributeTarget" { set = false, default = AttributeTargets.Event }
|
|
|
|
-----------------------------------------------------------
|
|
-- method --
|
|
-----------------------------------------------------------
|
|
--- apply changes on the target
|
|
-- @param target the target
|
|
-- @param targettype the target type
|
|
-- @param manager the definition manager of the target
|
|
-- @param owner the target's owner
|
|
-- @param name the target's name in the owner
|
|
-- @param stack the stack level
|
|
function ApplyAttribute(self, target, targettype, manager, owner, name, stack)
|
|
local map = self[1]
|
|
|
|
Event.SetEventChangeHandler(target, function(delegate, owner, eventname)
|
|
if not delegate.PopupeBinded then
|
|
delegate.PopupeBinded = true
|
|
|
|
for name, events in pairs(map) do
|
|
local child = type(name) == "number" and owner or getTarget(owner, name)
|
|
|
|
if not child then
|
|
error(("The child named %q doesn't existed in object of %s"):format(name, tostring(getmetatable(owner))))
|
|
end
|
|
|
|
for event in events:gmatch("%w+") do
|
|
child[event] = child[event] + function(self, ...) return delegate(owner, ...) end
|
|
end
|
|
end
|
|
end
|
|
end, stack + 1)
|
|
end
|
|
|
|
-----------------------------------------------------------
|
|
-- constructor --
|
|
-----------------------------------------------------------
|
|
__Arguments__{ struct { [NEString] = NEString } }
|
|
function __new(_, map)
|
|
return { map }, true
|
|
end
|
|
end)
|
|
|
|
----------------------------------------------
|
|
-- Template --
|
|
----------------------------------------------
|
|
__Sealed__() class "__Template__" (function (_ENV)
|
|
extend "IInitAttribute"
|
|
|
|
local _Template = {}
|
|
local CHILDREN_MAP = 1
|
|
|
|
local isUIObjectType = UI.IsUIObjectType
|
|
local getSuperCTOR = Class.GetSuperMetaMethod
|
|
local yield = coroutine.yield
|
|
local getSuperClass = Class.GetSuperClass
|
|
local tinsert = table.insert
|
|
local tremove = table.remove
|
|
local strformat = string.format
|
|
local tconcat = table.concat
|
|
|
|
local function getElementType(cls, ...)
|
|
local pathCnt = select("#", ...)
|
|
local scls = cls
|
|
|
|
repeat
|
|
local childtree = _Template[scls]
|
|
local i = 1
|
|
local name = select(i, ...)
|
|
|
|
while childtree and i < pathCnt do
|
|
local child = childtree[name]
|
|
|
|
if child then
|
|
-- Check if it's created by the child
|
|
local type = getElementType(child, select(i + 1, ...))
|
|
if type then return type, false end
|
|
end
|
|
|
|
childtree = childtree[CHILDREN_MAP] and childtree[CHILDREN_MAP][name]
|
|
i = i + 1
|
|
name = select(i, ...)
|
|
end
|
|
|
|
local type = childtree and i == pathCnt and childtree[name]
|
|
if type then return type, scls == cls end
|
|
|
|
scls = getSuperClass(scls)
|
|
until not scls
|
|
end
|
|
|
|
local function parseChildTree(cls, path, elements, supercls, stack)
|
|
local childtree = {}
|
|
local container = _Template
|
|
local pathCnt = #path
|
|
local pathIdx = pathCnt + 1
|
|
|
|
-- Save the tree
|
|
if pathCnt == 0 then
|
|
_Template[cls] = childtree
|
|
else
|
|
container = _Template[cls]
|
|
|
|
for i = 1, #path - 1 do
|
|
container = container[CHILDREN_MAP][path[i]]
|
|
end
|
|
|
|
container[CHILDREN_MAP] = container[CHILDREN_MAP] or {}
|
|
container[CHILDREN_MAP][path[#path]]= childtree
|
|
end
|
|
|
|
-- Save settings to the tree
|
|
local subtree
|
|
|
|
for k, v in pairs(elements) do
|
|
if type(k) == "string" and isUIObjectType(v) then
|
|
path[pathIdx] = k
|
|
|
|
-- Check if already created by children or super class
|
|
if getElementType(cls, unpack(path)) or (supercls and getElementType(supercls, unpack(path))) then
|
|
error(strformat("The the child element named %q is already defined", k), stack + 1)
|
|
end
|
|
|
|
path[pathIdx] = nil
|
|
|
|
childtree[k] = v
|
|
elseif type(k) == "number" and type(v) == "table" and getmetatable(v) == nil and not subtree then
|
|
subtree = v
|
|
else
|
|
error("The __Template__'s element type must be an ui object type", stack + 1)
|
|
end
|
|
end
|
|
|
|
if subtree then
|
|
childtree[CHILDREN_MAP] = {}
|
|
|
|
for name, subelements in pairs(subtree) do
|
|
if type(name) == "string" and type(subelements) == "table" and getmetatable(subelements) == nil then
|
|
path[pathIdx] = name
|
|
|
|
if getElementType(cls, unpack(path)) or (supercls and getElementType(supercls, unpack(path))) then
|
|
parseChildTree(cls, path, subelements, supercls, stack + 1)
|
|
else
|
|
error(strformat("The child with the path %q doesn't existed", tconcat(path, ".")), stack + 1)
|
|
end
|
|
else
|
|
error("The __Template__'s element's children settings must be a table", stack + 1)
|
|
end
|
|
end
|
|
|
|
path[pathIdx] = nil
|
|
end
|
|
end
|
|
|
|
local function generateChildren(self, childtree)
|
|
local temp
|
|
for k, v in pairs(childtree) do
|
|
if k ~= CHILDREN_MAP then
|
|
if INSTANT_STYLE_UI_CLASS[v] then
|
|
temp = temp or _Recycle()
|
|
temp[k] = v
|
|
else
|
|
v(k, self)
|
|
end
|
|
end
|
|
end
|
|
|
|
if temp then
|
|
for k, v in pairs(temp) do v(k, self) end
|
|
_Recycle(wipe(temp))
|
|
end
|
|
|
|
if childtree[CHILDREN_MAP] then
|
|
for name, tree in pairs(childtree[CHILDREN_MAP]) do
|
|
generateChildren(self:GetChild(name), tree)
|
|
end
|
|
end
|
|
end
|
|
|
|
-----------------------------------------------------------
|
|
-- static method --
|
|
-----------------------------------------------------------
|
|
__Arguments__{ - UIObject, NEString * 1 }
|
|
__Static__() GetElementType = getElementType
|
|
|
|
-----------------------------------------------------------
|
|
-- method --
|
|
-----------------------------------------------------------
|
|
--- modify the target's definition
|
|
-- @param target the target
|
|
-- @param targettype the target type
|
|
-- @param definition the target's definition
|
|
-- @param owner the target's owner
|
|
-- @param name the target's name in the owner
|
|
-- @param stack the stack level
|
|
-- @return definition the new definition
|
|
function InitDefinition(self, target, targettype, definition, owner, name, stack)
|
|
if targettype == AttributeTargets.Method then
|
|
if name ~= "__ctor" then error("The __Template__ can only be used on the constructor, not nomral method", stack + 1) end
|
|
if type(self[1]) ~= "table" then error("The __Template__ lack the element settings", stack + 1) end
|
|
|
|
parseChildTree(owner, {}, self[1], nil, stack + 1)
|
|
|
|
return function(self, ...)
|
|
local sctor = getSuperCTOR(owner, "__ctor")
|
|
if sctor then sctor(self, ...) end
|
|
|
|
generateChildren(self, _Template[owner])
|
|
|
|
return definition(self, ...)
|
|
end
|
|
elseif targettype == AttributeTargets.Class then
|
|
if type(definition) == "table" then
|
|
local new = { self[0] }
|
|
local elements = {}
|
|
|
|
for k, v in pairs(definition) do
|
|
if type(k) == "string" and isUIObjectType(v) then
|
|
elements[k] = v
|
|
elseif type(k) == "number" and type(v) == "table" and elements[CHILDREN_MAP] == nil then
|
|
elements[CHILDREN_MAP] = v
|
|
else
|
|
new[k] = v
|
|
end
|
|
end
|
|
|
|
parseChildTree(target, {}, elements, self[0], stack + 1)
|
|
|
|
new.__ctor = function(self, ...)
|
|
local sctor = getSuperCTOR(target, "__ctor")
|
|
if sctor then sctor(self, ...) end
|
|
|
|
generateChildren(self, _Template[target])
|
|
end
|
|
|
|
return new
|
|
else
|
|
error("The __Template__ require the class use table of element type settings as definition", stack + 1)
|
|
end
|
|
end
|
|
end
|
|
|
|
-----------------------------------------------------------
|
|
-- property --
|
|
-----------------------------------------------------------
|
|
property "AttributeTarget" { type = AttributeTargets, default = AttributeTargets.Method + AttributeTargets.Class }
|
|
|
|
-----------------------------------------------------------
|
|
-- constructor --
|
|
-----------------------------------------------------------
|
|
__Arguments__{ - UIObject/nil }
|
|
function __new(_, cls) return { [0] = cls }, true end
|
|
|
|
__Arguments__{ RawTable }
|
|
function __new(_, setting) return { setting }, true end
|
|
end)
|
|
|
|
----------------------------------------------
|
|
-- Style Property --
|
|
----------------------------------------------
|
|
__Sealed__() struct "Scorpio.UI.Property" {
|
|
name = { type = NEString, require = true },
|
|
type = { type = AnyType },
|
|
require = { type = ClassType + struct { ClassType }, require = true },
|
|
set = { type = Function },
|
|
get = { type = Function },
|
|
clear = { type = Function },
|
|
default = { type = Any },
|
|
nilable = { type = Boolean },
|
|
childtype = { type = - UIObject },
|
|
depends = { type = struct { NEString } }, -- Processed after other properties
|
|
override = { type = struct { NEString } }, -- override other properties
|
|
|
|
__valid = function(self)
|
|
if not self.childtype and not self.set then
|
|
return "%s.set is required"
|
|
end
|
|
end,
|
|
|
|
__init = function(self)
|
|
local setting = {
|
|
name = self.name,
|
|
type = self.type,
|
|
set = self.set,
|
|
get = self.get,
|
|
clear = self.clear,
|
|
default = clone(self.default, true),
|
|
nilable = self.nilable,
|
|
childtype = self.childtype,
|
|
}
|
|
|
|
if self.childtype then
|
|
-- The child property type should be handled specially
|
|
local name = self.name
|
|
local childtype = self.childtype
|
|
local set = self.set
|
|
local nilable = self.nilable
|
|
local clear = self.clear
|
|
local childname = strlower(self.name)
|
|
|
|
setting.get = function(self, try)
|
|
local child = _PropertyChildMap[setting][self]
|
|
if child or try then return child, false end
|
|
|
|
local recycle = _PropertyChildRecycle[childtype]
|
|
|
|
if recycle then
|
|
child = recycle()
|
|
if child then child:SetParent(self) end
|
|
else
|
|
child = childtype(childname, self)
|
|
end
|
|
|
|
if child then
|
|
if set then set(self, child) end
|
|
_PropertyChildMap[setting][self]= child
|
|
_PropertyChildName[child] = childname
|
|
end
|
|
|
|
return child, true
|
|
end
|
|
|
|
setting.clear = function(self)
|
|
local child = _PropertyChildMap[setting][self]
|
|
if not child then return end
|
|
|
|
collectPropertyChild(child)
|
|
|
|
if _PropertyChildRecycle[childtype] then
|
|
_PropertyChildMap[setting][self] = nil
|
|
end
|
|
|
|
if clear then
|
|
clear(self, child)
|
|
elseif nilable and set then
|
|
set(self, nil)
|
|
end
|
|
end
|
|
end
|
|
|
|
if self.type then
|
|
if isTypeValidDisabled then
|
|
if Struct.Validate(self.type) and not Struct.IsImmutable(self.type) then
|
|
setting.validate = Struct.ValidateValue
|
|
end
|
|
else
|
|
if Enum.Validate(self.type) then
|
|
setting.validate = Enum.ValidateValue
|
|
elseif Struct.Validate(self.type) then
|
|
setting.validate = Struct.ValidateValue
|
|
elseif Class.Validate(self.type) then
|
|
setting.validate = Class.ValidateValue
|
|
elseif Interface.Validate(self.type) then
|
|
setting.validate = Interface.ValidateValue
|
|
end
|
|
end
|
|
|
|
if setting.default ~= nil and setting.validate then
|
|
setting.default = setting.validate(setting.type, setting.default)
|
|
end
|
|
end
|
|
|
|
if self.depends then
|
|
setting.depends = {}
|
|
for i, v in ipairs(self.depends) do
|
|
setting.depends[i] = strlower(v)
|
|
end
|
|
end
|
|
|
|
if self.override then
|
|
setting.override = {}
|
|
for i, v in ipairs(self.override) do
|
|
setting.override[i] = strlower(v)
|
|
end
|
|
end
|
|
|
|
local name = strlower(self.name)
|
|
|
|
if isUIObjectType(self.require) then
|
|
dispatchPropertySetting(self.require, name, setting, nil, true)
|
|
else
|
|
for _, cls in ipairs(self.require) do
|
|
dispatchPropertySetting(cls, name, setting, nil, true)
|
|
end
|
|
end
|
|
end,
|
|
}
|
|
|
|
----------------------------------------------
|
|
-- Style Accessor --
|
|
----------------------------------------------
|
|
-- Style[UnitFrame].HealthBar.Color = { r = 1, g = 0, b = 0 }
|
|
-- Style[frame].Alpha = 0.5
|
|
-- Style[UnitFrame].HealthBar = { color = { r = 1, g = 0, b = 1 }, alpha = 0.8 }
|
|
local Style = Namespace.SaveNamespace("Scorpio.UI.Style", prototype {
|
|
__tostring = Namespace.GetNamespaceName,
|
|
__index = function(self, key)
|
|
if type(key) == "string" then
|
|
return _StyleMethods[key]
|
|
end
|
|
|
|
if isUIObject(key) then
|
|
_StyleOwner = key
|
|
return _StyleAccessor
|
|
end
|
|
end,
|
|
__newindex = function(self, key, value)
|
|
if type(key) == "string" and type(value) == "function" then
|
|
if _StyleMethods[key] then
|
|
error(("The method named %s already existed in Scorpio.UI.Style"):format(key), 2)
|
|
end
|
|
|
|
if Attribute.HaveRegisteredAttributes() then
|
|
Attribute.SaveAttributes(value, AttributeTargets.Function, 2)
|
|
local ret = Attribute.InitDefinition(value, AttributeTargets.Function, value, self, key, 2)
|
|
if ret ~= value then
|
|
Attribute.ToggleTarget(value, ret)
|
|
value = ret
|
|
end
|
|
Attribute.ApplyAttributes (value, AttributeTargets.Function, nil, self, key, 2)
|
|
Attribute.AttachAttributes(value, AttributeTargets.Function, self, key, 2)
|
|
end
|
|
|
|
_StyleMethods[key] = value
|
|
return
|
|
end
|
|
|
|
if isUIObject(key) then
|
|
setCustomStyle(key, nil, value, 2)
|
|
return
|
|
end
|
|
|
|
error("The Scorpio.UI.Style access is denied", 2)
|
|
end
|
|
})
|
|
|
|
_StyleAccessor = prototype {
|
|
__metatable = Style,
|
|
__index = function(self, key)
|
|
local target = _StyleOwner
|
|
if target and type(key) == "string" then
|
|
local star = UIObject.GetChild(target, key)
|
|
if star then
|
|
_StyleOwner = star
|
|
return _StyleAccessor
|
|
else
|
|
_StyleOwner = nil
|
|
|
|
local cls = getUIPrototype(target)
|
|
local prop = cls and _Property[cls] and _Property[cls][strlower(key)]
|
|
if prop then
|
|
if prop.childtype then
|
|
_StyleOwner = prop.get(target)
|
|
if _StyleOwner then return _StyleAccessor end
|
|
elseif prop.get then
|
|
return prop.get(target)
|
|
else
|
|
error("The " .. key .. " property has no get method")
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
_StyleOwner = nil
|
|
error("The sub key must be child name or property name", 2)
|
|
end,
|
|
__newindex = function(self, key, value)
|
|
local target = _StyleOwner
|
|
_StyleOwner = nil
|
|
|
|
if target and type(key) == "string" then
|
|
local star = UIObject.GetChild(target, key)
|
|
if star then
|
|
setCustomStyle(star, nil, value, 2)
|
|
return
|
|
else
|
|
key = strlower(key)
|
|
local cls = getUIPrototype(target)
|
|
local prop = cls and _Property[cls] and _Property[cls][key]
|
|
if prop then
|
|
setCustomStyle(target, key, value, 2)
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
error("The sub key must be child name or property name", 2)
|
|
end
|
|
}
|
|
|
|
__Iterator__() __Arguments__{ UI }
|
|
function Style.GetCustomStyles(frame)
|
|
local custom = _CustomStyle[frame]
|
|
if custom then
|
|
local props = _Property[getmetatable(frame)]
|
|
|
|
for name, value in pairs(custom) do
|
|
yield(props[name].name, (clone(value)))
|
|
end
|
|
end
|
|
end
|
|
|
|
__Arguments__{ - UIObject, NEString * 0 } __Iterator__()
|
|
function Style.GetDefaultStyles(class, ...)
|
|
local default = _DefaultStyle[class]
|
|
|
|
if default then
|
|
if select("#", ...) > 0 then
|
|
class = __Template__.GetElementType(class, ...)
|
|
if not class then return end
|
|
|
|
for i = 1, select("#", ...) do
|
|
local name = select(i, ...)
|
|
|
|
default = default[CHILD_SETTING]
|
|
default = default and default[name]
|
|
if not default then return end
|
|
end
|
|
end
|
|
|
|
local props = _Property[class]
|
|
|
|
for name, value in pairs(default) do
|
|
if name ~= 0 then
|
|
yield(props[name].name, (clone(value)))
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
----------------------------------------------
|
|
-- Skin System --
|
|
----------------------------------------------
|
|
local SkinSettings = struct { [ - UIObject ] = Table }
|
|
|
|
__Arguments__{ NEString, SkinSettings/nil }:Throwable()
|
|
function Style.RegisterSkin(name, settings)
|
|
name = strlower(name)
|
|
if _Skins[name] then return false end
|
|
|
|
local skins = {}
|
|
_Skins[name] = skins
|
|
|
|
if settings then
|
|
for class, setting in pairs(settings) do
|
|
local skin = {}
|
|
skins[class] = skin
|
|
|
|
saveSkinSettings({class}, {}, skin, setting)
|
|
end
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
__Arguments__{ NEString, SkinSettings }:Throwable()
|
|
function Style.UpdateSkin(name, settings)
|
|
name = strlower(name)
|
|
local skins = _Skins[name]
|
|
|
|
if not skins then
|
|
throw("Usage: Style.UpdateSkin(name, settings) - the name doesn't existed")
|
|
end
|
|
|
|
for class, setting in pairs(settings) do
|
|
local skin = emptyDefaultStyle(skins[class]) or {}
|
|
skins[class] = skin
|
|
|
|
saveSkinSettings({class}, {}, skin, setting)
|
|
activeSkin(name, class, skin, true)
|
|
end
|
|
end
|
|
|
|
__Arguments__{ NEString, - UIObject/nil }:Throwable()
|
|
function Style.ActiveSkin(name, class)
|
|
name = strlower(name)
|
|
local skins = _Skins[name]
|
|
|
|
if not skins then
|
|
throw("Usage: Style.ActiveSkin(name[, uitype]) - the name doesn't existed")
|
|
end
|
|
|
|
if class then
|
|
local skin = skins[class]
|
|
if not skin then
|
|
throw("Usage: Style.ActiveSkin(name[, uitype]) - the skin doesn't have settings for " .. class)
|
|
end
|
|
|
|
return activeSkin(name, class, skin)
|
|
else
|
|
for cls, skin in pairs(skins) do
|
|
activeSkin(name, cls, skin)
|
|
end
|
|
end
|
|
end
|
|
|
|
__Arguments__{ - UIObject }
|
|
function Style.GetActiveSkin(class)
|
|
return _ActiveSkin[class]
|
|
end
|
|
|
|
__Arguments__{ - UIObject } __Iterator__()
|
|
function Style.GetSkins(class)
|
|
for name, skins in pairs(_Skins) do
|
|
if skins[class] then
|
|
yield(name)
|
|
end
|
|
end
|
|
end
|
|
|
|
__Arguments__{ -UIObject } __Iterator__()
|
|
function Style.GetProperties(class)
|
|
local props = _Property[class]
|
|
if props then
|
|
for name, prop in pairs(props) do
|
|
yield(name, prop.childtype or prop.type)
|
|
end
|
|
end
|
|
end
|
|
|
|
__Arguments__{ - UIObject, String }
|
|
function Style.GetProperty(class, name)
|
|
local props = _Property[class]
|
|
local prop = props and props[strlower(name)]
|
|
if prop then return prop.childtype or prop.type end
|
|
end
|
|
|
|
function Style.GetCurrentTarget()
|
|
return _CurrentStyleTarget
|
|
end
|
|
|
|
Style.RegisterSkin("Default")
|
|
|
|
export { Scorpio.UI.Property }
|
|
|
|
----------------------------------------------
|
|
-- Skin System Services --
|
|
----------------------------------------------
|
|
local function genPriority(props, name, styles, priority, lvl)
|
|
lvl = (lvl or 0) + 1
|
|
local maxlvl = lvl
|
|
|
|
for _, dep in ipairs(props[name].depends) do
|
|
local val = styles[dep]
|
|
if val ~= nil and val ~= NIL and val ~= CLEAR then
|
|
priority[dep] = max(priority[dep] or 0, lvl)
|
|
|
|
if props[dep].depends then
|
|
maxlvl = max(maxlvl, genPriority(props, dep, styles, priority, lvl))
|
|
end
|
|
end
|
|
end
|
|
|
|
return maxlvl
|
|
end
|
|
|
|
function applyStylesOnFrame(frame, styles)
|
|
local props = _Property[getUIPrototype(frame)]
|
|
if not props then return _Recycle(wipe(styles)) end
|
|
|
|
local priority = _Recycle()
|
|
local maxlvl = 0
|
|
|
|
-- Generate the priority to apply the styles based on the depends
|
|
for name, value in pairs(styles) do
|
|
if value == NIL or value == CLEAR then
|
|
applyProperty(frame, props[name], nil)
|
|
styles[name] = nil
|
|
elseif props[name].depends then
|
|
maxlvl = max(maxlvl, genPriority(props, name, styles, priority))
|
|
end
|
|
end
|
|
|
|
-- Process with priority
|
|
for i = maxlvl, 1, -1 do
|
|
for name, lv in pairs(priority) do
|
|
if lv == i and styles[name] ~= nil then
|
|
applyProperty(frame, props[name], styles[name])
|
|
styles[name] = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Process the rest
|
|
for name, value in pairs(styles) do
|
|
applyProperty(frame, props[name], value)
|
|
end
|
|
|
|
_Recycle(wipe(priority))
|
|
_Recycle(wipe(styles))
|
|
end
|
|
|
|
local function clearStylesOnFrame(frame, styles)
|
|
local props = _Property[getUIPrototype(frame)]
|
|
if not props then return _Recycle(wipe(styles)) end
|
|
|
|
local priority = _Recycle()
|
|
local maxlvl = 0
|
|
|
|
-- Generate the priority to apply the styles based on the depends
|
|
for name, value in pairs(styles) do
|
|
if value == NIL or value == CLEAR then
|
|
applyProperty(frame, props[name], nil)
|
|
styles[name] = nil
|
|
elseif props[name].depends then
|
|
maxlvl = max(maxlvl, genPriority(props, name, styles, priority))
|
|
end
|
|
end
|
|
|
|
-- Process with priority
|
|
for i = maxlvl, 1, -1 do
|
|
for name, lv in pairs(priority) do
|
|
if lv == i and styles[name] ~= nil then
|
|
applyProperty(frame, props[name], nil)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Process property with no priority
|
|
for name, value in pairs(styles) do
|
|
if not priority[name] then
|
|
applyProperty(frame, props[name], nil)
|
|
end
|
|
end
|
|
|
|
_Recycle(wipe(priority))
|
|
_Recycle(wipe(styles))
|
|
end
|
|
|
|
local function buildTempStyle(frame, forClear)
|
|
local styles = _Recycle()
|
|
local paths = _Recycle()
|
|
local children = _Recycle()
|
|
local tempClass = _Recycle()
|
|
|
|
local props = _Property[getUIPrototype(frame)]
|
|
if not props then return styles, children end -- No properties can be found
|
|
|
|
-- Prepare the style settings
|
|
-- Custom -> Root Parent Class -> ... -> Parent Class -> Frame Class -> Super Class
|
|
local name = _PropertyChildName[frame] or UIObject.GetName(frame)
|
|
local parent = UIObject.GetParent(frame)
|
|
|
|
while parent and name do
|
|
local cls = getmetatable(parent)
|
|
|
|
tinsert(paths, 1, name)
|
|
|
|
if cls and isUIObjectType(cls) then
|
|
wipe(tempClass)
|
|
|
|
repeat
|
|
tinsert(tempClass, cls)
|
|
cls = Class.GetSuperClass(cls)
|
|
until not cls
|
|
|
|
for i = #tempClass, 1, -1 do
|
|
cls = tempClass[i]
|
|
|
|
local default = _DefaultStyle[cls]
|
|
local index = 1
|
|
|
|
while default and paths[index] do
|
|
default = default[CHILD_SETTING]
|
|
default = default and default[paths[index]]
|
|
index = index + 1
|
|
end
|
|
|
|
if default then
|
|
-- The parent -> ... -> child style settings
|
|
for prop, value in pairs(default) do
|
|
if prop == CHILD_SETTING then
|
|
for name in pairs(value) do
|
|
children[name] = true
|
|
|
|
if styles[name] and styles[name] ~= true and isObservable(styles[name]) then
|
|
-- So don't create the property child dynamicly
|
|
styles[name] = true
|
|
end
|
|
end
|
|
else
|
|
if value ~= CLEAR or styles[prop] == nil then
|
|
styles[prop] = value
|
|
|
|
-- Check childtype
|
|
if props[prop].childtype then
|
|
-- So we need create the property child dynamicly
|
|
if forClear then
|
|
children[prop] = true
|
|
elseif isObservable(value) then
|
|
children[prop] = nil
|
|
end
|
|
end
|
|
|
|
-- Check override
|
|
if props[prop].override then
|
|
if value ~= CLEAR then
|
|
for _, op in ipairs(props[prop].override) do
|
|
styles[op] = nil -- The overridden property won't be applied
|
|
end
|
|
else
|
|
for _, op in ipairs(props[prop].override) do
|
|
if styles[op] ~= nil and styles[op] ~= CLEAR then
|
|
styles[prop]= nil
|
|
break
|
|
else
|
|
styles[op] = nil
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
name = _PropertyChildName[parent] or UIObject.GetName(parent)
|
|
parent = UIObject.GetParent(parent)
|
|
end
|
|
|
|
_Recycle(wipe(paths))
|
|
_Recycle(wipe(tempClass))
|
|
|
|
if _CustomStyle[frame] then
|
|
for prop, value in pairs(_CustomStyle[frame]) do
|
|
if value ~= CLEAR or styles[prop] == nil then
|
|
styles[prop] = value
|
|
|
|
-- Check dynamic property child
|
|
if props[prop].childtype then
|
|
if forClear then
|
|
children[prop] = true
|
|
elseif isObservable(value) then
|
|
children[prop] = nil
|
|
end
|
|
end
|
|
|
|
-- Check override
|
|
if props[prop].override then
|
|
if value ~= CLEAR then
|
|
for _, op in ipairs(props[prop].override) do
|
|
styles[op] = nil -- The overridden property won't be applied
|
|
end
|
|
else
|
|
for _, op in ipairs(props[prop].override) do
|
|
if styles[op] ~= nil and styles[op] ~= CLEAR then
|
|
styles[prop]= nil
|
|
break
|
|
else
|
|
styles[op] = nil
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local cls = getmetatable(frame)
|
|
|
|
if isUIObjectType(cls) then
|
|
while cls do
|
|
local default = _DefaultStyle[cls]
|
|
|
|
if default then
|
|
for prop, value in pairs(default) do
|
|
if prop == CHILD_SETTING then
|
|
for name in pairs(value) do
|
|
if forClear or not isObservable(styles[name]) then
|
|
children[name] = true
|
|
end
|
|
end
|
|
elseif styles[prop] == nil or styles[prop] == CLEAR then
|
|
local noOverride = true
|
|
|
|
-- Check property child
|
|
if forClear and props[prop].childtype then
|
|
children[prop] = true
|
|
end
|
|
|
|
-- Check override
|
|
if props[prop].override then
|
|
for _, op in ipairs(props[prop].override) do
|
|
if styles[op] ~= nil and styles[op] ~= CLEAR then
|
|
noOverride = false
|
|
break
|
|
else
|
|
styles[op] = nil -- Clear the CLEAR value styles
|
|
end
|
|
end
|
|
end
|
|
|
|
if noOverride then
|
|
styles[prop] = value
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
cls = Class.GetSuperClass(cls)
|
|
end
|
|
end
|
|
|
|
return styles, children
|
|
end
|
|
|
|
local function clearStyle(frame)
|
|
local props = _Property[getUIPrototype(frame)]
|
|
|
|
if props and not frame.Disposed then
|
|
local debugname = frame:GetName(true) or frame:GetObjectType()
|
|
local clearChilds = _Recycle()
|
|
|
|
Trace("[Scorpio.UI]Clear Style: %s%s", debugname, _PropertyChildName[frame] and (" - " .. _PropertyChildName[frame]) or "")
|
|
|
|
local styles, children = buildTempStyle(frame, true)
|
|
|
|
-- Clear the children
|
|
for name in pairs(children) do
|
|
local child = UIObject.GetChild(frame, name)
|
|
|
|
if child then
|
|
clearStyle(child)
|
|
elseif props[name] and props[name].childtype then
|
|
child = props[name].get(frame, true)
|
|
styles[name] = CLEAR
|
|
if child then
|
|
clearChilds[child] = true
|
|
_ClearQueue[child] = true
|
|
|
|
clearStyle(child)
|
|
end
|
|
end
|
|
end
|
|
|
|
_Recycle(wipe(children))
|
|
|
|
-- Apply the style settings
|
|
local ok, err = pcall(clearStylesOnFrame, frame, styles)
|
|
if not ok then Error("[Scorpio.UI]Clear Style: %s - Failed: %s", debugname, tostring(err)) end
|
|
|
|
for child in pairs(clearChilds) do
|
|
_ClearQueue[child] = nil
|
|
end
|
|
|
|
_Recycle(wipe(clearChilds))
|
|
end
|
|
|
|
_CustomStyle[frame] = nil
|
|
if _PropertyChildName[frame] and _PropertyChildRecycle(frame) then
|
|
_PropertyChildName[frame] = nil
|
|
end
|
|
|
|
Continue() -- Smoothing the process
|
|
end
|
|
|
|
function ApplyFrameStyles(self, styles, debugname)
|
|
-- Apply the style settings
|
|
_CurrentStyleTarget = self
|
|
local ok, err = pcall(applyStylesOnFrame, self, styles)
|
|
if not ok then Error("[Scorpio.UI]Apply Style: %s - Failed: %s", debugname, tostring(err)) end
|
|
_CurrentStyleTarget = nil
|
|
end
|
|
|
|
__Service__(true)
|
|
function ApplyStyleService()
|
|
while true do
|
|
local frame = _StyleQueue:Dequeue()
|
|
|
|
while frame do
|
|
if _StyleQueue[frame] then
|
|
local props = _Property[getUIPrototype(frame)]
|
|
|
|
if props and not frame.Disposed then
|
|
local debugname = frame:GetName(true) or frame:GetObjectType()
|
|
|
|
Trace("[Scorpio.UI]Apply Style: %s%s", debugname, _PropertyChildName[frame] and (" - " .. _PropertyChildName[frame]) or "")
|
|
|
|
local styles, children = buildTempStyle(frame)
|
|
|
|
-- Queue the children
|
|
for name in pairs(children) do
|
|
local child = UIObject.GetChild(frame, name)
|
|
|
|
if child then
|
|
applyStyle(child)
|
|
elseif props[name] and props[name].childtype and styles[name] == true then
|
|
styles[name] = nil
|
|
child = props[name].get(frame)
|
|
if child then applyStyle(child) end
|
|
end
|
|
end
|
|
|
|
_Recycle(wipe(children))
|
|
|
|
_StyleQueue[frame] = nil
|
|
|
|
-- Apply the style settings
|
|
local isProtected = frame.IsProtected
|
|
if isProtected and isProtected(frame) then
|
|
NoCombat(ApplyFrameStyles, frame, styles, debugname)
|
|
else
|
|
ApplyFrameStyles(frame, styles, debugname)
|
|
end
|
|
|
|
Continue() -- Smoothing the process
|
|
else
|
|
_StyleQueue[frame] = nil
|
|
end
|
|
end
|
|
|
|
frame = _StyleQueue:Dequeue()
|
|
end
|
|
|
|
NextEvent("SCORPIO_UI_APPLY_STYLE")
|
|
end
|
|
end
|
|
|
|
__Service__(true)
|
|
function CollectPropertyChildService()
|
|
while true do
|
|
local frame = _ClearQueue:Dequeue()
|
|
|
|
while frame do
|
|
if _ClearQueue[frame] then
|
|
_ClearQueue[frame] = nil
|
|
|
|
clearStyle(frame)
|
|
end
|
|
|
|
Continue()
|
|
frame = _ClearQueue:Dequeue()
|
|
end
|
|
|
|
-- For safe
|
|
for k, v in pairs(_ClearQueue) do if v == true then _ClearQueue[k] = nil end end
|
|
|
|
NextEvent("SCORPIO_UI_COLLECT_PROPERTY_CHILD")
|
|
end
|
|
end
|
|
|
|
__Service__(true)
|
|
function QueueClassFramesService()
|
|
while true do
|
|
local class = _ClassQueue:Dequeue()
|
|
|
|
while class do
|
|
if _ClassQueue[class] then
|
|
_ClassQueue[class] = nil -- Allow queue again
|
|
|
|
local count = 1
|
|
local frames = _ClassFrames[class]
|
|
|
|
if frames then
|
|
for frame in pairs(frames) do
|
|
applyStyle(frame)
|
|
count = count + 1
|
|
|
|
if count > 30 then
|
|
count = 1
|
|
Continue()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
Continue()
|
|
|
|
class = _ClassQueue:Dequeue()
|
|
end
|
|
|
|
NextEvent("SCORPIO_UI_UPDATE_CLASS_SKIN")
|
|
end
|
|
end
|
|
|
|
-- Apply the style on the frame instantly
|
|
function UIObject:InstantApplyStyle(skipCheck)
|
|
_StyleQueue[self] = nil
|
|
|
|
local props = _Property[getUIPrototype(self)]
|
|
if not props then return end
|
|
|
|
local debugname = self:GetName(true) or self:GetObjectType()
|
|
|
|
Trace("[Scorpio.UI]Instant Apply Style: %s%s", debugname, _PropertyChildName[frame] and (" - " .. _PropertyChildName[frame]) or "")
|
|
|
|
local styles, children = buildTempStyle(self)
|
|
|
|
-- Check the location props, dirty but should be only property has relationship
|
|
if not skipCheck and type(styles["location"]) == "table" then
|
|
for _, loc in ipairs(styles["location"]) do
|
|
if type(loc) == "table" and not UIObject.GetRelativeUI(self, loc.relativeTo) then
|
|
_StyleQueue[self] = true
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Queue the children
|
|
for name in pairs(children) do
|
|
local child = UIObject.GetChild(self, name)
|
|
children[name] = false
|
|
|
|
if child then
|
|
children[name] = child
|
|
elseif props[name] and props[name].childtype and styles[name] == true then
|
|
styles[name] = nil
|
|
child = props[name].get(self)
|
|
if child then children[name] = child end
|
|
end
|
|
end
|
|
|
|
for name, child in pairs(children) do
|
|
if child then UIObject.InstantApplyStyle(child, true) end
|
|
end
|
|
|
|
_Recycle(wipe(children))
|
|
|
|
-- Apply the style settings
|
|
_CurrentStyleTarget = self
|
|
local ok, err = pcall(applyStylesOnFrame, self, styles)
|
|
if not ok then Error("[Scorpio.UI]Apply Style: %s - Failed: %s", debugname, tostring(err)) end
|
|
_CurrentStyleTarget = nil
|
|
end
|
|
|
|
-- Get the child property name of the frame if it's generated by the property
|
|
function UIObject:GetChildPropertyName()
|
|
return _PropertyChildName[self]
|
|
end
|
|
|
|
-- Get the child generated from the given property name
|
|
__Arguments__{ String, Boolean/nil }
|
|
function UIObject:GetPropertyChild(name, create)
|
|
self = GetProxyUI(self)
|
|
|
|
local props = _Property[getUIPrototype(self)]
|
|
local prop = props and props[strlower(name)]
|
|
|
|
return prop and prop.childtype and prop.get(self, not create)
|
|
end
|
|
|
|
-- Get the relative UI Elements based on the name, nil means parent
|
|
__Arguments__{ NEString/nil }
|
|
function UIObject:GetRelativeUI(relativeTo)
|
|
if relativeTo then
|
|
local rtar
|
|
|
|
-- $parent.xxx.xxx
|
|
if relativeTo:find(".", 1, true) then
|
|
for pattern in relativeTo:gmatch("[^%.]+") do
|
|
if pattern:lower() == "$parent" then
|
|
rtar = (rtar or self):GetParent()
|
|
else
|
|
local p = rtar or self:GetParent()
|
|
rtar = UIObject.GetChild(p, pattern) or UIObject.GetPropertyChild(p, pattern)
|
|
end
|
|
if not rtar then break end
|
|
end
|
|
else
|
|
local p = self:GetParent()
|
|
rtar = p and (UIObject.GetChild(p, relativeTo) or UIObject.GetPropertyChild(p, relativeTo))
|
|
end
|
|
|
|
return rtar or UIObject.FromName(relativeTo)
|
|
else
|
|
return self:GetParent()
|
|
end
|
|
end
|
|
|
|
--- Gets the relative ui access name
|
|
__Arguments__{ UI }
|
|
function UIObject:GetRelativeUIName(frame)
|
|
local parent = self:GetParent()
|
|
|
|
-- Use nil represent the parent
|
|
if UI.IsSameUI(frame, parent) then return nil end
|
|
|
|
-- Use brother's name
|
|
if UI.IsSameUI(frame:GetParent(), parent) then return frame:GetName() end
|
|
|
|
local fullName = UIObject.GetName(self, true)
|
|
local tarFullName = UIObject.GetName(frame, true)
|
|
|
|
-- Check the max same prefix
|
|
local index = 1
|
|
if fullName and tarFullName then
|
|
while fullName:byte(index) == tarFullName:byte(index) do index = index + 1 end
|
|
if index > #fullName and tarFullName:byte(index) == 46 then
|
|
-- the frame is a child
|
|
return tarFullName:sub(index + 1, -1)
|
|
elseif index > #tarFullName and fullName:byte(index) == 46 then
|
|
-- means parent
|
|
return (fullName:sub(index + 1, -1):gsub("[^%.]+", "$parent"))
|
|
end
|
|
|
|
while index > 1 and fullName:byte(index - 1) ~= 46 do index = index - 1 end
|
|
return index > 1 and (fullName:sub(index, -1):gsub("[^%.]+", "$parent") .. "." .. tarFullName:sub(index, -1)) or tarFullName
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
----------------------------------------------
|
|
-- Tool Attribute --
|
|
----------------------------------------------
|
|
--- Define a child property based on the target ui class
|
|
__Sealed__() class "__ChildProperty__" (function(_ENV)
|
|
extend "IAttachAttribute"
|
|
|
|
-----------------------------------------------------------
|
|
-- property --
|
|
-----------------------------------------------------------
|
|
property "AttributeTarget" { set = false, default = AttributeTargets.Class }
|
|
|
|
-----------------------------------------------------------
|
|
-- method --
|
|
-----------------------------------------------------------
|
|
--- attach data on the target
|
|
-- @param target the target
|
|
-- @param targettype the target type
|
|
-- @param owner the target's owner
|
|
-- @param name the target's name in the owner
|
|
-- @param stack the stack level
|
|
-- @return data the attribute data to be attached
|
|
function AttachAttribute(self, target, targettype, owner, name, stack)
|
|
UI.Property {
|
|
name = self.Name or Namespace.GetNamespaceName(target, true),
|
|
require = self.Require,
|
|
childtype = target,
|
|
}
|
|
end
|
|
|
|
-----------------------------------------------------------
|
|
-- constructor --
|
|
-----------------------------------------------------------
|
|
__Arguments__{ - UIObject/Frame, NEString/nil }
|
|
function __ctor(self, reqframe, name)
|
|
self.Require = reqframe
|
|
self.Name = name
|
|
end
|
|
end)
|
|
|
|
--- Define a UI class to do the InstantApplyStyle
|
|
__Sealed__() class "__InstantApplyStyle__" (function(_ENV)
|
|
extend "IInitAttribute" "IApplyAttribute"
|
|
|
|
local getSuperCTOR = Class.GetSuperMetaMethod
|
|
|
|
-----------------------------------------------------------
|
|
-- method --
|
|
-----------------------------------------------------------
|
|
--- modify the target's definition
|
|
-- @param target the target
|
|
-- @param targettype the target type
|
|
-- @param definition the target's definition
|
|
-- @param owner the target's owner
|
|
-- @param name the target's name in the owner
|
|
-- @param stack the stack level
|
|
-- @return definition the new definition
|
|
function InitDefinition(self, target, targettype, definition, owner, name, stack)
|
|
if targettype == AttributeTargets.Method then
|
|
if name ~= "__ctor" then error("The __InstantApplyStyle__ can only be used on the constructor, not nomral method", stack + 1) end
|
|
|
|
INSTANT_STYLE_UI_CLASS[owner] = true
|
|
|
|
return function(self, ...)
|
|
definition(self, ...)
|
|
return _StyleQueue[self] and self:InstantApplyStyle()
|
|
end
|
|
end
|
|
end
|
|
|
|
-----------------------------------------------------------
|
|
--- apply changes on the target
|
|
-- @param target the target
|
|
-- @param targettype the target type
|
|
-- @param manager the definition manager of the target
|
|
-- @param owner the target's owner
|
|
-- @param name the target's name in the owner
|
|
-- @param stack the stack level
|
|
function ApplyAttribute(self, target, targettype, manager, owner, name, stack)
|
|
if targettype == AttributeTargets.Class then
|
|
INSTANT_STYLE_UI_CLASS[target] = true
|
|
|
|
local ctor = Class.GetMetaMethod(target, "__ctor")
|
|
|
|
manager.__ctor = ctor and function(self, ...)
|
|
ctor(self, ...)
|
|
return _StyleQueue[self] and self:InstantApplyStyle()
|
|
end or function(self, ...)
|
|
local sctor = getSuperCTOR(target, "__ctor")
|
|
if sctor then sctor(self, ...) end
|
|
return _StyleQueue[self] and self:InstantApplyStyle()
|
|
end
|
|
end
|
|
end
|
|
|
|
-----------------------------------------------------------
|
|
-- property --
|
|
-----------------------------------------------------------
|
|
property "AttributeTarget" { type = AttributeTargets, default = AttributeTargets.Method + AttributeTargets.Class }
|
|
|
|
property "Priority" { type = AttributePriority, default = AttributePriority.Lowest }
|
|
end)
|
|
|