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 ---@field editingObject uiobject ---@field editingProfileTable table ---@field editingProfileMap table ---@field editingOptions df_editobjectoptions ---@field editingExtraOptions table ---@field moverGuideLines table ---@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