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.

1019 lines
40 KiB

local detailsFramework = _G["DetailsFramework"]
if (not detailsFramework or not DetailsFrameworkCanLoad) then
return
end
---@cast detailsFramework detailsframework
local CreateFrame = CreateFrame
local unpack = unpack
local wipe = table.wipe
local _
--[=[
file description: this file has the code for the object editor
the object editor itself is a frame and has a scrollframe as canvas showing another frame where there's the options for the editing object
--]=]
--the editor doesn't know which key in the profileTable holds the current value for an attribute, so it uses a map table to find it.
--the mapTable is a table with the attribute name as a key, and the value is the profile key. For example, {["size"] = "text_size"} means profileTable["text_size"] = 10.
---@class df_editor : frame, df_optionsmixin, df_editormixin
---@field options table
---@field registeredObjects df_editor_objectinfo[]
---@field registeredObjectsByID table<any, df_editor_objectinfo>
---@field editingObject uiobject
---@field editingProfileTable table
---@field editingProfileMap table
---@field editingOptions df_editobjectoptions
---@field editingExtraOptions table
---@field moverGuideLines table<string, texture>
---@field onEditCallback function
---@field optionsFrame frame
---@field overTheTopFrame frame
---@field objectSelector df_scrollbox
---@field moverFrame frame
---@field canvasScrollBox df_canvasscrollbox
---@class df_editor_attribute
---@field key string?
---@field label string?
---@field widget string
---@field default any?
---@field minvalue number?
---@field maxvalue number?
---@field step number?
---@field usedecimals boolean?
---@field subkey string?
---@class df_editor_objectinfo : table
---@field object uiobject
---@field label string
---@field id any
---@field profiletable table
---@field profilekeymap table
---@field subtablepath string?
---@field extraoptions table
---@field callback function?
---@field options df_editobjectoptions
---@field selectButton button
--which object attributes are used to build the editor menu for each object type
local attributes = {
---@type df_editor_attribute[]
FontString = {
{
key = "text",
label = "Text",
widget = "textentry",
default = "font string text",
setter = function(widget, value) widget:SetText(value) end,
},
{
key = "size",
label = "Size",
widget = "range",
minvalue = 5,
maxvalue = 120,
setter = function(widget, value) widget:SetFont(widget:GetFont(), value, select(3, widget:GetFont())) end
},
{
key = "font",
label = "Font",
widget = "fontdropdown",
setter = function(widget, value)
local font = LibStub:GetLibrary("LibSharedMedia-3.0"):Fetch("font", value)
widget:SetFont(font, select(2, widget:GetFont()))
end
},
{
key = "color",
label = "Color",
widget = "color",
setter = function(widget, value) widget:SetTextColor(unpack(value)) end
},
{
key = "alpha",
label = "Alpha",
widget = "range",
setter = function(widget, value) widget:SetAlpha(value) end
},
{widget = "blank"},
{
key = "shadow",
label = "Draw Shadow",
widget = "toggle",
setter = function(widget, value) widget:SetShadowColor(widget:GetShadowColor(), select(2, widget:GetShadowColor()), select(3, widget:GetShadowColor()), value and 0.5 or 0) end
},
{
key = "shadowcolor",
label = "Shadow Color",
widget = "color",
setter = function(widget, value) widget:SetShadowColor(unpack(value)) end
},
{
key = "shadowoffsetx",
label = "Shadow X Offset",
widget = "range",
minvalue = -10,
maxvalue = 10,
setter = function(widget, value) widget:SetShadowOffset(value, select(2, widget:GetShadowOffset())) end
},
{
key = "shadowoffsety",
label = "Shadow Y Offset",
widget = "range",
minvalue = -10,
maxvalue = 10,
setter = function(widget, value) widget:SetShadowOffset(widget:GetShadowOffset(), value) end
},
{
key = "outline",
label = "Outline",
widget = "outlinedropdown",
setter = function(widget, value) widget:SetFont(widget:GetFont(), select(2, widget:GetFont()), value) end
},
{widget = "blank"},
{
key = "anchor",
label = "Anchor",
widget = "anchordropdown",
setter = function(widget, value) detailsFramework:SetAnchor(widget, value, widget:GetParent()) end
},
{
key = "anchoroffsetx",
label = "Anchor X Offset",
widget = "range",
minvalue = -100,
maxvalue = 100,
setter = function(widget, value) detailsFramework:SetAnchor(widget, value, widget:GetParent()) end
},
{
key = "anchoroffsety",
label = "Anchor Y Offset",
widget = "range",
minvalue = -100,
maxvalue = 100,
setter = function(widget, value) detailsFramework:SetAnchor(widget, value, widget:GetParent()) end
},
{
key = "rotation",
label = "Rotation",
widget = "range",
usedecimals = true,
minvalue = 0,
maxvalue = math.pi*2,
setter = function(widget, value) widget:SetRotation(value) end
},
{
key = "scale",
label = "Scale",
widget = "range",
usedecimals = true,
minvalue = 0.65,
maxvalue = 2.5,
setter = function(widget, value) widget:SetScale(value) end
},
}
}
---@class df_editormixin : table
---@field GetAllRegisteredObjects fun(self:df_editor):df_editor_objectinfo[]
---@field GetEditingObject fun(self:df_editor):uiobject
---@field GetEditingOptions fun(self:df_editor):df_editobjectoptions
---@field GetExtraOptions fun(self:df_editor):table
---@field GetEditingProfile fun(self:df_editor):table, table
---@field GetOnEditCallback fun(self:df_editor):function
---@field GetOptionsFrame fun(self:df_editor):frame
---@field GetCanvasScrollBox fun(self:df_editor):df_canvasscrollbox
---@field GetObjectSelector fun(self:df_editor):df_scrollbox
---@field EditObject fun(self:df_editor, object:uiobject, profileTable:table?, profileKeyMap:table?, extraOptions:table?, callback:function?, options:df_editobjectoptions?)
---@field PrepareObjectForEditing fun(self:df_editor)
---@field CreateMoverGuideLines fun(self:df_editor)
---@field GetOverTheTopFrame fun(self:df_editor):frame
---@field GetMoverFrame fun(self:df_editor):frame
---@field StartObjectMovement fun(self:df_editor, anchorSettings:df_anchor)
---@field StopObjectMovement fun(self:df_editor)
---@field RegisterObject fun(self:df_editor, object:uiobject, localizedLabel:string, id:any, profileTable:table, subTablePath:string, profileKeyMap:table, extraOptions:table?, callback:function?, options:df_editobjectoptions?):df_editor_objectinfo
---@field UnregisterObject fun(self:df_editor, object:uiobject)
---@field EditObjectById fun(self:df_editor, id:any)
---@field EditObjectByIndex fun(self:df_editor, index:number)
---@field UpdateGuideLinesAnchors fun(self:df_editor)
---@field GetObjectByRef fun(self:df_editor, object:uiobject):df_editor_objectinfo
---@field GetObjectByIndex fun(self:df_editor, index:number):df_editor_objectinfo
---@field GetObjectById fun(self:df_editor, id:any):df_editor_objectinfo
---@field CreateObjectSelectionList fun(self:df_editor, scroll_width:number, scroll_height:number, scroll_lines:number, scroll_line_height:number):df_scrollbox
---@field OnHide fun(self:df_editor)
---@field UpdateProfileTableOnAllRegisteredObjects fun(self:df_editor, profileTable:table)
---@field GetProfileTableFromObject fun(self:df_editor, object:df_editor_objectinfo):table
---@class df_editobjectoptions : table
---@field use_colon boolean if true a colon is shown after the option name
---@field can_move boolean if true the object can be moved
---@field use_guide_lines boolean if true guide lines are shown when the object is being moved
---@field text_template table
---@type df_editobjectoptions
local editObjectDefaultOptions = {
use_colon = false,
can_move = true,
use_guide_lines = true,
text_template = detailsFramework:GetTemplate("font", "OPTIONS_FONT_TEMPLATE"),
}
local getParentTable = function(profileTable, profileKey)
local parentPath
if (profileKey:match("%]$")) then
parentPath = profileKey:gsub("%s*%[.*%]%s*$", "")
else
parentPath = profileKey:gsub("%.[^.]*$", "")
end
local parentTable = detailsFramework.table.getfrompath(profileTable, parentPath)
return parentTable
end
detailsFramework.EditorMixin = {
---@param self df_editor
GetEditingObject = function(self)
return self.editingObject
end,
---@param self df_editor
---@return df_editobjectoptions
GetEditingOptions = function(self)
return self.editingOptions
end,
---@param self df_editor
---@return table
GetExtraOptions = function(self)
return self.editingExtraOptions
end,
---@param self df_editor
---@return table, table
GetEditingProfile = function(self)
return self.editingProfileTable, self.editingProfileMap
end,
---@param self df_editor
---@return function
GetOnEditCallback = function(self)
return self.onEditCallback
end,
GetOptionsFrame = function(self)
return self.optionsFrame
end,
GetOverTheTopFrame = function(self)
return self.overTheTopFrame
end,
GetMoverFrame = function(self)
return self.moverFrame
end,
GetCanvasScrollBox = function(self)
return self.canvasScrollBox
end,
GetObjectSelector = function(self)
return self.objectSelector
end,
EditObjectById = function(self, id)
---@type df_editor_objectinfo
local objectRegistered = self:GetObjectById(id)
assert(type(objectRegistered) == "table", "EditObjectById() object not found.")
self:EditObject(objectRegistered)
self.objectSelector:RefreshMe()
end,
EditObjectByIndex = function(self, index)
---@type df_editor_objectinfo
local objectRegistered = self:GetObjectByIndex(index)
assert(type(objectRegistered) == "table", "EditObjectById() object not found.")
self:EditObject(objectRegistered)
self.objectSelector:RefreshMe()
end,
---@param self df_editor
---@param registeredObject df_editor_objectinfo
EditObject = function(self, registeredObject)
--clear previous values
self.editingObject = nil
self.editingProfileMap = nil
self.editingProfileTable = nil
self.editingOptions = nil
self.editingExtraOptions = nil
self.onEditCallback = nil
local object = registeredObject.object
local profileKeyMap = registeredObject.profilekeymap
local extraOptions = registeredObject.extraoptions
local callback = registeredObject.callback
local options = registeredObject.options
local profileTable = self:GetProfileTableFromObject(registeredObject)
assert(type(profileTable) == "table", "EditObject() profileTable is invalid.")
--as there's no other place which this members are set, there is no need to create setter functions
self.editingObject = object
self.editingProfileMap = profileKeyMap
self.editingProfileTable = profileTable
self.editingOptions = options
self.editingExtraOptions = extraOptions
if (type(callback) == "function") then
self.onEditCallback = callback
end
self:PrepareObjectForEditing()
end,
---@param self df_editor
CreateMoverGuideLines = function(self)
local overTheTopFrame = self:GetOverTheTopFrame()
local moverFrame = self:GetMoverFrame()
self.moverGuideLines = {
left = overTheTopFrame:CreateTexture(nil, "overlay"),
right = overTheTopFrame:CreateTexture(nil, "overlay"),
top = overTheTopFrame:CreateTexture(nil, "overlay"),
bottom = overTheTopFrame:CreateTexture(nil, "overlay"),
}
for side, texture in pairs(self.moverGuideLines) do
texture:SetColorTexture(.8, .8, .8, 0.1)
texture:SetSize(1, 1)
texture:SetDrawLayer("overlay", 7)
texture:Hide()
if (side == "left" or side == "right") then
texture:SetHeight(1)
texture:SetWidth(GetScreenWidth())
else
texture:SetWidth(1)
texture:SetHeight(GetScreenHeight())
end
end
end,
UpdateGuideLinesAnchors = function(self)
local object = self:GetEditingObject()
for side, texture in pairs(self.moverGuideLines) do
texture:ClearAllPoints()
if (side == "left" or side == "right") then
if (side == "left") then
texture:SetPoint("right", object, "left", -2, 0)
else
texture:SetPoint("left", object, "right", 2, 0)
end
else
if (side == "top") then
texture:SetPoint("bottom", object, "top", 0, 2)
else
texture:SetPoint("top", object, "bottom", 0, -2)
end
end
end
end,
PrepareObjectForEditing = function(self) --~edit
--get the object and its profile table with the current values
local object = self:GetEditingObject()
local profileTable, profileMap = self:GetEditingProfile()
profileMap = profileMap or {}
if (not object or not profileTable) then
return
end
--get the object type
local objectType = object:GetObjectType()
local attributeList
--get options and extra options
local editingOptions = self:GetEditingOptions()
local extraOptions = self:GetExtraOptions()
--get the attribute list for the object type
if (objectType == "FontString") then
---@cast object fontstring
attributeList = attributes[objectType]
end
--if there's extra options, add the attributeList to a new table and right after the extra options
if (extraOptions and #extraOptions > 0) then
local attributeListWithExtraOptions = {}
for i = 1, #attributeList do
attributeListWithExtraOptions[#attributeListWithExtraOptions+1] = attributeList[i]
end
attributeListWithExtraOptions[#attributeListWithExtraOptions+1] = {widget = "blank", default = true}
for i = 1, #extraOptions do
attributeListWithExtraOptions[#attributeListWithExtraOptions+1] = extraOptions[i]
end
attributeList = attributeListWithExtraOptions
end
local anchorSettings
--table to use on DF:BuildMenuVolatile()
local menuOptions = {}
for i = 1, #attributeList do
local option = attributeList[i]
if (option.widget == "blank") then
menuOptions[#menuOptions+1] = {type = "blank"}
else
--get the key to be used on profile table
local profileKey = profileMap[option.key]
local value
--if the key contains a dot or a bracket, it means it's a table path, example: "text_settings[1].width"
if (profileKey and (profileKey:match("%.") or profileKey:match("%["))) then
value = detailsFramework.table.getfrompath(profileTable, profileKey)
else
value = profileTable[profileKey]
end
--if no value is found, attempt to get a default
if (type(value) == "nil") then
value = option.default
end
local bHasValue = type(value) ~= "nil"
local minValue = option.minvalue
local maxValue = option.maxvalue
if (option.key == "anchoroffsetx") then
minValue = -object:GetParent():GetWidth()/2
maxValue = object:GetParent():GetWidth()/2
elseif (option.key == "anchoroffsety") then
minValue = -object:GetParent():GetHeight()/2
maxValue = object:GetParent():GetHeight()/2
end
if (bHasValue) then
local parentTable = getParentTable(profileTable, profileKey)
if (option.key == "anchor" or option.key == "anchoroffsetx" or option.key == "anchoroffsety") then
anchorSettings = parentTable
end
menuOptions[#menuOptions+1] = {
type = option.widget,
name = option.label,
get = function() return value end,
set = function(widget, fixedValue, newValue, ...)
--color is a table with 4 indexes for each color plus alpha
if (option.widget == "range" or option.widget == "slider") then
if (not option.usedecimals) then
newValue = math.floor(newValue)
end
elseif (option.widget == "color") then
--calor callback sends the red color in the fixedParameter slot
local r, g, b, alpha = fixedValue, newValue, ...
--need to use the same table from the profile table
parentTable[1] = r
parentTable[2] = g
parentTable[3] = b
parentTable[4] = alpha
newValue = parentTable
end
detailsFramework.table.setfrompath(profileTable, profileKey, newValue)
if (self:GetOnEditCallback()) then
self:GetOnEditCallback()(object, option.key, newValue, profileTable, profileKey)
end
--update the widget visual
--anchoring uses SetAnchor() which require the anchorTable to be passed
if (option.key == "anchor" or option.key == "anchoroffsetx" or option.key == "anchoroffsety") then
anchorSettings = parentTable
if (option.key == "anchor") then
anchorSettings.x = 0
anchorSettings.y = 0
end
self:StopObjectMovement()
option.setter(object, parentTable)
if (editingOptions.can_move) then
self:StartObjectMovement(anchorSettings)
end
else
option.setter(object, newValue)
end
end,
min = minValue,
max = maxValue,
step = option.step,
usedecimals = option.usedecimals,
id = option.key,
}
end
end
end
--at this point, the optionsTable is ready to be used on DF:BuildMenuVolatile()
menuOptions.align_as_pairs = true
menuOptions.align_as_pairs_length = 150
menuOptions.widget_width = 180
menuOptions.slider_buttons_to_left = true
local optionsFrame = self:GetOptionsFrame()
local canvasScrollBox = self:GetCanvasScrollBox()
local bUseColon = editingOptions.use_colon
local bSwitchIsCheckbox = true
local maxHeight = 5000
local amountOfOptions = #menuOptions
local optionsFrameHeight = amountOfOptions * 20
optionsFrame:SetHeight(optionsFrameHeight)
--templates
local options_text_template = self.options.text_template or detailsFramework:GetTemplate("font", "OPTIONS_FONT_TEMPLATE")
local options_dropdown_template = detailsFramework:GetTemplate("dropdown", "OPTIONS_DROPDOWN_TEMPLATE")
local options_switch_template = detailsFramework:GetTemplate("switch", "OPTIONS_CHECKBOX_TEMPLATE")
local options_slider_template = detailsFramework:GetTemplate("slider", "OPTIONS_SLIDER_TEMPLATE")
local options_button_template = detailsFramework:GetTemplate("button", "OPTIONS_BUTTON_TEMPLATE")
--~build ~menu ~volatile
detailsFramework:BuildMenuVolatile(optionsFrame, menuOptions, 2, -2, maxHeight, bUseColon, options_text_template, options_dropdown_template, options_switch_template, bSwitchIsCheckbox, options_slider_template, options_button_template)
if (editingOptions.can_move) then
self:StartObjectMovement(anchorSettings)
end
end,
---@param self df_editor
---@param anchorSettings df_anchor
StartObjectMovement = function(self, anchorSettings)
local object = self:GetEditingObject()
local moverFrame = self:GetMoverFrame()
moverFrame:EnableMouse(true)
moverFrame:SetMovable(true)
moverFrame:ClearAllPoints()
moverFrame:Show()
--update guidelines
if (self:GetEditingOptions().use_guide_lines) then
--self:UpdateGuideLinesAnchors()
--show all four guidelines
for side, texture in pairs(self.moverGuideLines) do
texture:Show()
end
end
local optionsFrame = self:GetOptionsFrame()
--getting the size this way due to the cascade of different scales that are applyed to unitFrame objects
moverFrame:ClearAllPoints()
moverFrame:SetPoint("topleft", object, "topleft", -5, 5)
moverFrame:SetPoint("bottomright", object, "bottomright", 5, -5)
local moverWidth, moverHeight = moverFrame:GetSize()
local objectWidth, objectHeight = object:GetSize()
moverFrame:SetSize(moverWidth, moverHeight)
detailsFramework:SetAnchor(moverFrame, anchorSettings, object:GetParent())
local currentPosX, currentPosY
moverFrame:SetScript("OnMouseDown", function()
object:ClearAllPoints()
object:SetPoint("topleft", moverFrame, "topleft", 0, 0)
currentPosX, currentPosY = moverFrame:GetCenter()
moverFrame.bIsMoving = true
moverFrame:StartMoving()
end)
moverFrame:SetScript("OnMouseUp", function()
moverFrame:StopMovingOrSizing()
moverFrame.bIsMoving = false
--0.64 UIParent
--0.96 object
--local scale = object:GetEffectiveScale() / UIParent:GetEffectiveScale() --1.5
local originX = anchorSettings.x
local originY = anchorSettings.y
local newPosX, newPosY = moverFrame:GetCenter()
local xOffset = newPosX - currentPosX
local yOffset = newPosY - currentPosY
xOffset = xOffset
yOffset = yOffset
anchorSettings.x = (originX + xOffset)
anchorSettings.y = (originY + yOffset)
local anchorXSlider = optionsFrame:GetWidgetById("anchoroffsetx")
anchorXSlider:SetValueNoCallback(anchorSettings.x)
local anchorYSlider = optionsFrame:GetWidgetById("anchoroffsety")
anchorYSlider:SetValueNoCallback(anchorSettings.y)
object:ClearAllPoints()
detailsFramework:SetAnchor(object, anchorSettings, object:GetParent())
--save the new position
local profileTable, profileMap = self:GetEditingProfile()
local profileKey = profileMap.anchor
local parentTable = getParentTable(profileTable, profileKey)
parentTable.x = anchorSettings.x
parentTable.y = anchorSettings.y
if (self:GetOnEditCallback()) then
self:GetOnEditCallback()(object, "x", anchorSettings.x, profileTable, profileKey)
self:GetOnEditCallback()(object, "y", anchorSettings.x, profileTable, profileKey)
end
end)
--detailsFramework:SetAnchor(moverFrame, anchorSettings)
--detailsFramework:SetAnchor(object, anchorSettings, moverFrame)
moverFrame:SetScript("OnUpdate", function() --not in use
--if the object isn't moving, make the mover follow the object position
if (false and moverFrame.bIsMoving) then
--object:ClearAllPoints()
--object:SetPoint("topleft", moverFrame, "topleft", 0, 0)
--if the object is moving, check if the moverFrame has moved
local newPosX, newPosY = moverFrame:GetCenter()
--did the frame moved?
if (newPosX ~= currentPosX) then
local xOffset = newPosX - currentPosX
anchorSettings.x = anchorSettings.x + xOffset
local anchorXSlider = optionsFrame:GetWidgetById("anchoroffsetx")
anchorXSlider:SetValueNoCallback(anchorSettings.x)
end
if (newPosY ~= currentPosY) then
local yOffset = newPosY - currentPosY
anchorSettings.y = anchorSettings.y + yOffset
local anchorYSlider = optionsFrame:GetWidgetById("anchoroffsety")
anchorYSlider:SetValueNoCallback(anchorSettings.y)
end
currentPosX, currentPosY = newPosX, newPosY
end
end)
end,
---@param self df_editor
StopObjectMovement = function(self)
local moverFrame = self:GetMoverFrame()
moverFrame:EnableMouse(false)
moverFrame:SetScript("OnUpdate", nil)
--hide all four guidelines
for side, texture in pairs(self.moverGuideLines) do
texture:Hide()
end
moverFrame:Hide()
end,
---@param self df_editor
---@param object df_editor_objectinfo
GetProfileTableFromObject = function(self, object)
local profileTable = object.profiletable
local subTablePath = object.subtablepath
if (type(subTablePath) == "string" and subTablePath ~= "") then
local subTable = detailsFramework.table.getfrompath(profileTable, subTablePath)
assert(type(subTable) == "table", "GetProfileTableFromObject() subTablePath is invalid.")
return subTable
end
return profileTable
end,
UpdateProfileTableOnAllRegisteredObjects = function(self, profileTable)
assert(type(profileTable) == "table", "UpdateProfileTableOnAllRegisteredObjects() expects a table on #1 parameter.")
local registeredObjects = self:GetAllRegisteredObjects()
for i = 1, #registeredObjects do
local objectRegistered = registeredObjects[i]
objectRegistered.profiletable = profileTable
end
local objectSelector = self:GetObjectSelector()
objectSelector:RefreshMe()
end,
RegisterObject = function(self, object, localizedLabel, id, profileTable, subTablePath, profileKeyMap, extraOptions, callback, options)
assert(type(object) == "table", "RegisterObjectToEdit() expects an UIObject on #1 parameter.")
assert(object.GetObjectType, "RegisterObjectToEdit() expects an UIObject on #1 parameter.")
assert(type(profileTable) == "table", "RegisterObjectToEdit() expects a table on #4 parameter.")
assert(type(id) ~= "nil" and type(id) ~= "boolean", "RegisterObjectToEdit() expects an ID on parameter #3.")
assert(type(callback) == "function" or callback == nil, "RegisterObjectToEdit() expects a function or nil as the #7 parameter.")
local registeredObjects = self:GetAllRegisteredObjects()
--is object already registered?
for i = 1, #registeredObjects do
local objectRegistered = registeredObjects[i]
if (objectRegistered.object == object) then
error("RegisterObjectToEdit() object already registered.")
end
end
--deploy the options table
options = type(options) == "table" and options or {}
detailsFramework.table.deploy(options, editObjectDefaultOptions)
localizedLabel = type(localizedLabel) == "string" and localizedLabel or "invalid label"
--a button to select the widget
local selectButton = CreateFrame("button", "$parentSelectButton" .. id, object:GetParent())
selectButton:SetAllPoints(object)
---@type df_editor_objectinfo
local objectRegistered = {
object = object,
label = localizedLabel,
id = id,
profiletable = profileTable,
subtablepath = subTablePath,
profilekeymap = profileKeyMap,
extraoptions = extraOptions or {},
callback = callback,
options = options,
selectButton = selectButton,
}
selectButton:SetScript("OnClick", function()
self:EditObject(objectRegistered)
end)
registeredObjects[#registeredObjects+1] = objectRegistered
self.registeredObjectsByID[id] = objectRegistered
local objectSelector = self:GetObjectSelector()
objectSelector:RefreshMe()
--what to do after an object is registered?
return objectRegistered
end,
UnregisterObject = function(self, object)
local registeredObjects = self:GetAllRegisteredObjects()
for i = 1, #registeredObjects do
local objectRegistered = registeredObjects[i]
if (objectRegistered.object == object) then
self.registeredObjectsByID[objectRegistered.id] = nil
table.remove(registeredObjects, i)
break
end
end
local objectSelector = self:GetObjectSelector()
objectSelector:RefreshMe()
--stop editing the object
end,
---@param self df_editor
---@return df_editor_objectinfo[]
GetAllRegisteredObjects = function(self)
return self.registeredObjects
end,
---@param self df_editor
---@return df_editor_objectinfo?
GetObjectByRef = function(self, object)
local registeredObjects = self:GetAllRegisteredObjects()
for i = 1, #registeredObjects do
local objectRegistered = registeredObjects[i]
if (objectRegistered.object == object) then
return objectRegistered
end
end
end,
GetObjectByIndex = function(self, index)
local registeredObjects = self:GetAllRegisteredObjects()
return registeredObjects[index]
end,
GetObjectById = function(self, id)
return self.registeredObjectsByID[id]
end,
CreateObjectSelectionList = function(self, scroll_width, scroll_height, scroll_lines, scroll_line_height)
local editorFrame = self
local refreshFunc = function(self, data, offset, totalLines) --~refresh
self.SelectionTexture:Hide()
self.SelectionTexture:ClearAllPoints()
local objectCurrentBeingEdited = editorFrame:GetEditingObject()
for i = 1, totalLines do
local index = i + offset
---@type df_editor_objectinfo
local objectRegistered = data[index]
if (objectRegistered) then
local line = self:GetLine(i)
line.index = index
if (objectRegistered.object:GetObjectType() == "Texture") then
line.Icon:SetAtlas("AnimCreate_Icon_Texture")
elseif (objectRegistered.object:GetObjectType() == "FontString") then
line.Icon:SetAtlas("AnimCreate_Icon_Text")
end
line.Label:SetText(objectRegistered.label)
if (objectRegistered.object == objectCurrentBeingEdited) then
self.SelectionTexture:SetAllPoints(line)
self.SelectionTexture:Show()
end
end
end
end
local createLineFunc = function(self, index) -- ~createline --~line
local line = CreateFrame("button", "$parentLine" .. index, self, "BackdropTemplate")
line:SetPoint("topleft", self, "topleft", 1, -((index-1)*(scroll_line_height+1)) - 1)
line:SetSize(scroll_width - 2, scroll_line_height)
line:SetBackdrop({bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true})
if (index % 2 == 0) then
line:SetBackdropColor(.1, .1, .1, .1)
else
line:SetBackdropColor(.1, .1, .1, .4)
end
detailsFramework:CreateHighlightTexture(line, "HighlightTexture")
detailsFramework:Mixin(line, detailsFramework.HeaderFunctions)
line:SetScript("OnClick", function(self)
local objectRegistered = editorFrame:GetObjectByIndex(self.index)
editorFrame:EditObject(objectRegistered)
editorFrame.objectSelector:RefreshMe()
end)
--icon
local objectIcon = line:CreateTexture("$parentIcon", "overlay")
objectIcon:SetSize(scroll_line_height - 2, scroll_line_height - 2)
--object label
local objectLabel = line:CreateFontString("$parentLabel", "overlay", "GameFontNormal")
objectIcon:SetPoint("left", line, "left", 2, 0)
objectLabel:SetPoint("left", objectIcon, "right", 2, 0)
line.Icon = objectIcon
line.Label = objectLabel
return line
end
local selectObjectScrollBox = detailsFramework:CreateScrollBox(self:GetParent(), "$parentSelectObjectScrollBox", refreshFunc, editorFrame:GetAllRegisteredObjects(), scroll_width, scroll_height, scroll_lines, scroll_line_height)
detailsFramework:ReskinSlider(selectObjectScrollBox)
local selectionTexture = selectObjectScrollBox:CreateTexture(nil, "overlay")
selectionTexture:SetColorTexture(1, 1, 0, 0.2)
selectObjectScrollBox.SelectionTexture = selectionTexture
function selectObjectScrollBox:RefreshMe()
selectObjectScrollBox:SetData(editorFrame:GetAllRegisteredObjects())
selectObjectScrollBox:Refresh()
end
--create lines
for i = 1, scroll_lines do
selectObjectScrollBox:CreateLine(createLineFunc)
end
return selectObjectScrollBox
end,
OnHide = function(self)
self:StopObjectMovement()
end,
}
---@class df_editor_defaultoptions : table
---@field width number
---@field height number
---@field options_width number
---@field create_object_list boolean
---@field object_list_width number
---@field object_list_height number
---@field object_list_lines number
---@field object_list_line_height number
---@field text_template table
--editorFrame.options.text_template
---@type df_editor_defaultoptions
local editorDefaultOptions = {
width = 400,
height = 548,
options_width = 340,
create_object_list = true,
object_list_width = 200,
object_list_height = 420,
object_list_lines = 20,
object_list_line_height = 20,
text_template = detailsFramework:GetTemplate("font", "OPTIONS_FONT_TEMPLATE"),
}
function detailsFramework:CreateEditor(parent, name, options)
name = name or ("DetailsFrameworkEditor" .. math.random(100000, 10000000))
local editorFrame = CreateFrame("frame", name, parent, "BackdropTemplate")
detailsFramework:Mixin(editorFrame, detailsFramework.EditorMixin)
detailsFramework:Mixin(editorFrame, detailsFramework.OptionsFunctions)
editorFrame:SetScript("OnHide", editorFrame.OnHide)
editorFrame.registeredObjects = {}
editorFrame.registeredObjectsByID = {}
editorFrame:BuildOptionsTable(editorDefaultOptions, options)
editorFrame:SetSize(editorFrame.options.width, editorFrame.options.height)
--The options frame holds the options for the object being edited. It is used as the parent frame for the BuildMenuVolatile() function.
local optionsFrame = CreateFrame("frame", name .. "OptionsFrame", editorFrame, "BackdropTemplate")
optionsFrame:SetSize(editorFrame.options.options_width, 5000)
local canvasScrollBoxOptions = {
width = editorFrame.options.options_width,
height = 400,
reskin_slider = true,
}
local canvasFrame = detailsFramework:CreateCanvasScrollBox(editorFrame, optionsFrame, name .. "CanvasScrollBox", canvasScrollBoxOptions)
if (editorFrame.options.create_object_list) then
local scrollWidth = editorFrame.options.object_list_width
local scrollHeight = editorFrame.options.object_list_height
local scrollLinesAmount = editorFrame.options.object_list_lines
local scrollLineHeight = editorFrame.options.object_list_line_height
local objectSelector = editorFrame:CreateObjectSelectionList(scrollWidth, scrollHeight, scrollLinesAmount, scrollLineHeight)
objectSelector:SetPoint("topleft", editorFrame, "topleft", 0, -2)
objectSelector:SetBackdropBorderColor(0, 0, 0, 0)
editorFrame.objectSelector = objectSelector
objectSelector:RefreshMe()
local nScrollBarWidth = 30
canvasFrame:SetPoint("topleft", objectSelector, "topright", nScrollBarWidth, 0)
canvasFrame:SetPoint("bottomleft", objectSelector, "bottomright", -nScrollBarWidth, 0)
else
canvasFrame:SetPoint("topleft", editorFrame, "topleft", 2, -2)
canvasFrame:SetPoint("bottomleft", editorFrame, "bottomleft", 2, 0)
end
--over the top frame is a frame that is always on top of everything else
local OTTFrame = CreateFrame("frame", "$parentOTTFrame", UIParent)
OTTFrame:SetFrameStrata("TOOLTIP")
editorFrame.overTheTopFrame = OTTFrame
--frame that is used to move the object
local moverFrame = CreateFrame("frame", "$parentMoverFrame", OTTFrame, "BackdropTemplate")
moverFrame:SetClampedToScreen(true)
detailsFramework:ApplyStandardBackdrop(moverFrame)
moverFrame:SetBackdropColor(.10, .10, .10, 0)
moverFrame.__background:SetAlpha(0.1)
editorFrame.moverFrame = moverFrame
editorFrame:CreateMoverGuideLines()
editorFrame.optionsFrame = optionsFrame
editorFrame.canvasScrollBox = canvasFrame
return editorFrame
end