diff --git a/Libs/DF/cooltip.lua b/Libs/DF/cooltip.lua index 54e5253d4..2e38a47ab 100644 --- a/Libs/DF/cooltip.lua +++ b/Libs/DF/cooltip.lua @@ -1992,17 +1992,17 @@ function DF:CreateCoolTip() function gameCooltip:SetSpellByID(spellId, bShowDescriptionOnly) --~spell if (type(spellId) == "number") then - spellId = C_SpellBook.GetOverrideSpell(spellId) + spellId = C_Spell.GetOverrideSpell(spellId) local spellName, spellRank, spellIcon, castTime, minRange, maxRange = GetSpellInfo(spellId) --castTime zero represents an instant cast or a channeled cast if (spellName) then - local spellDescription = GetSpellDescription(spellId) + local spellDescription = C_Spell.GetSpellDescription(spellId) local cooldownTime, globalCooldown = GetSpellBaseCooldown(spellId) --local cooldown = cooldownTime / 1000 - local bIsPassive = IsPassiveSpell(spellId, SPELLBOOK_BANK_PLAYER) + local bIsPassive = C_Spell.IsSpellPassive(spellId, SPELLBOOK_BANK_PLAYER) local chargesAvailable, maxCharges, chargeCooldownStart, rechargeTime, chargeModRate = GetSpellCharges(spellId) - local tResourceCost = GetSpellPowerCost(spellId) + local tResourceCost = C_Spell.GetSpellPowerCost(spellId) --[=[ hasRequiredAura=false, type=0, @@ -2022,7 +2022,7 @@ function DF:CreateCoolTip() gameCooltip:AddIcon(spellIcon, 1, 1, 20, 20, .1, .9, .1, .9) if (not bShowDescriptionOnly) then - if (tResourceCost.cost and tResourceCost.cost > 0) then + if (tResourceCost and tResourceCost.cost and tResourceCost.cost > 0) then if (maxRange and maxRange > 0) then gameCooltip:AddLine(tResourceCost.cost .. " " .. (_G[tResourceCost.name] or tResourceCost.name), string.format(_G.SPELL_RANGE, math.floor(maxRange)), 1, 1, 1, 1, nil, 12) else diff --git a/Libs/DF/definitions.lua b/Libs/DF/definitions.lua index cfa2151bd..8b332cdcd 100644 --- a/Libs/DF/definitions.lua +++ b/Libs/DF/definitions.lua @@ -343,6 +343,8 @@ ---@field GetRoleTCoordsAndTexture fun(self:table, roleID:number) : number, number, number, number, string ---@field AddColorToText fun(self:table, text:string, color:any) : string wrap text with a color ---@field AddClassColorToText fun(self:table, text:string, className:string) : string wrap text with a class color +---@field CreateSimplePanel fun(self:table, parent:frame, width:number?, height:number?, title:string?, frameName:string?, panelOptions:table?, savedVariableTable:table?) : simplepanel +---@field MakeDraggable fun(self:table, frame:frame) : nil --[=[ diff --git a/Libs/DF/editor.lua b/Libs/DF/editor.lua index 01080fdeb..4d6010e6e 100644 --- a/Libs/DF/editor.lua +++ b/Libs/DF/editor.lua @@ -654,6 +654,9 @@ detailsFramework.EditorMixin = { moverFrame.MovingInfo.restingX = x moverFrame.MovingInfo.restingY = y moverFrame:SetScript("OnUpdate", onTickNotMoving) + + moverFrame:GetScript("OnMouseDown")(moverFrame) + moverFrame:GetScript("OnMouseUp")(moverFrame) end self.ObjectBackgroundTexture:SetPoint("topleft", object, "topleft", 0, 0) diff --git a/Libs/DF/fw.lua b/Libs/DF/fw.lua index f2d576330..0fda4ed5e 100644 --- a/Libs/DF/fw.lua +++ b/Libs/DF/fw.lua @@ -1,6 +1,6 @@ -local dversion = 571 +local dversion = 572 local major, minor = "DetailsFramework-1.0", dversion local DF, oldminor = LibStub:NewLibrary(major, minor) @@ -40,6 +40,7 @@ local SPELLBOOK_BANK_PET = Enum.SpellBookSpellBank and Enum.SpellBookSpellBank.P local IsPassiveSpell = IsPassiveSpell or C_Spell.IsSpellPassive local GetOverrideSpell = C_SpellBook and C_SpellBook.GetOverrideSpell or C_Spell.GetOverrideSpell or GetOverrideSpell local HasPetSpells = HasPetSpells or C_SpellBook.HasPetSpells +local spellBookPetEnum = Enum.SpellBookSpellBank and Enum.SpellBookSpellBank.Pet or "pet" SMALL_NUMBER = 0.000001 ALPHA_BLEND_AMOUNT = 0.8400251 @@ -929,8 +930,11 @@ end function DF.table.deploy(t1, t2) for key, value in pairs(t2) do if (type(value) == "table") then - t1[key] = t1[key] or {} - DF.table.deploy(t1[key], t2[key]) + --check the t1 type as sometimes the key isn't the same type on both tables + if (t1[key] == nil or type(t1[key]) == "table") then + t1[key] = t1[key] or {} + DF.table.deploy(t1[key], t2[key]) + end elseif (t1[key] == nil) then t1[key] = value end @@ -1865,7 +1869,7 @@ function DF:GetAvailableSpells() local completeListOfSpells = {} --this line might not be compatible with classic - --local specId, specName, _, specIconTexture = GetSpecializationInfo(GetSpecialization()) + local specId, specName, _, specIconTexture = GetSpecializationInfo(GetSpecialization()) --local classNameLoc, className, classId = UnitClass("player") --not in use local locPlayerRace, playerRace, playerRaceId = UnitRace("player") @@ -1904,44 +1908,50 @@ function DF:GetAvailableSpells() end end + local spellBookPlayerEnum = Enum.SpellBookSpellBank and Enum.SpellBookSpellBank.Player or "player" + --get spells from the Spec spellbook - local amountOfTabs = GetNumSpellTabs() - for i = 2, amountOfTabs-1 do --starting at index 2 to ignore the general tab + for i = 1, GetNumSpellTabs() do --called "lines" in new v11 api local tabName, tabTexture, offset, numSpells, isGuild, offSpecId, shouldHide, specID = GetSpellTabInfo(i) - local bIsOffSpec = offSpecId ~= 0 - offset = offset + 1 - local tabEnd = offset + numSpells - for entryOffset = offset, tabEnd - 1 do - local spellType, spellId = GetSpellBookItemInfo(entryOffset, SPELLBOOK_BANK_PLAYER) - if (spellId) then - if (spellType == "SPELL") then - spellId = GetOverrideSpell(spellId) - local spellName = GetSpellInfo(spellId) - local bIsPassive = IsPassiveSpell(spellId, SPELLBOOK_BANK_PLAYER) - if (spellName and not bIsPassive) then - completeListOfSpells[spellId] = bIsOffSpec == false + if (tabTexture == specIconTexture) then + offset = offset + 1 + local tabEnd = offset + numSpells + --local bIsOffSpec = offSpecId ~= 0 + for entryOffset = offset, tabEnd - 1 do + local spellType, spellId = GetSpellBookItemInfo(entryOffset, spellBookPlayerEnum) + if (spellId) then + if (spellType == "SPELL" or spellType == 1) then + spellId = GetOverrideSpell(spellId) + local spellName = GetSpellInfo(spellId) + local bIsPassive = IsPassiveSpell(entryOffset, spellBookPlayerEnum) + if (spellName and not bIsPassive) then + completeListOfSpells[spellId] = true --bIsOffSpec == false + end end end end end end + local CONST_SPELLBOOK_CLASSSPELLS_TABID = 2 + local CONST_SPELLBOOK_GENERAL_TABID = 1 + --get class shared spells from the spell book - --[=[ - local tabName, tabTexture, offset, numSpells, isGuild, offSpecId = GetSpellTabInfo(2) - local bIsOffSpec = offSpecId ~= 0 + --[= + local tabName, tabTexture, offset, numSpells, isGuild, offSpecId = GetSpellTabInfo(CONST_SPELLBOOK_CLASSSPELLS_TABID) offset = offset + 1 local tabEnd = offset + numSpells + --local bIsOffSpec = offSpecId ~= 0 for entryOffset = offset, tabEnd - 1 do - local spellType, spellId = GetSpellBookItemInfo(entryOffset, "player") + local spellType, spellId = GetSpellBookItemInfo(entryOffset, spellBookPlayerEnum) if (spellId) then - if (spellType == "SPELL") then + if (spellType == "SPELL" or spellType == 1) then spellId = GetOverrideSpell(spellId) local spellName = GetSpellInfo(spellId) - local bIsPassive = IsPassiveSpell(spellId, "player") + local bIsPassive = IsPassiveSpell(spellId, spellBookPlayerEnum) if (spellName and not bIsPassive) then - completeListOfSpells[spellId] = bIsOffSpec == false + completeListOfSpells[spellId] = true --bIsOffSpec == false end end end @@ -1957,10 +1967,10 @@ function DF:GetAvailableSpells() local numPetSpells = getNumPetSpells() if (numPetSpells) then for i = 1, numPetSpells do - local spellName, _, unmaskedSpellId = GetSpellBookItemName(i, SPELLBOOK_BANK_PET) + local spellName, _, unmaskedSpellId = GetSpellBookItemName(i, spellBookPetEnum) if (unmaskedSpellId) then unmaskedSpellId = GetOverrideSpell(unmaskedSpellId) - local bIsPassive = IsPassiveSpell(unmaskedSpellId, SPELLBOOK_BANK_PET) + local bIsPassive = IsPassiveSpell(i, spellBookPetEnum) if (spellName and not bIsPassive) then completeListOfSpells[unmaskedSpellId] = true end diff --git a/Libs/DF/keybind.lua b/Libs/DF/keybind.lua index dab62b3c3..927d638dc 100644 --- a/Libs/DF/keybind.lua +++ b/Libs/DF/keybind.lua @@ -1100,6 +1100,7 @@ detailsFramework.KeybindMixin = { if (self.options.show_spells) then --the a list of all spells local allPlayerSpells = detailsFramework:GetAvailableSpells() + --bIsAvailable is a boolean that tells if the spell is from the spec the player is currently using (spells grayed out on the spellbook would be false here) for spellId, bIsAvailable in pairs(allPlayerSpells) do local spellName, _, spellIcon = GetSpellInfo(spellId) diff --git a/Libs/DF/panel.lua b/Libs/DF/panel.lua index f82bafa8d..0beb5da10 100644 --- a/Libs/DF/panel.lua +++ b/Libs/DF/panel.lua @@ -1986,6 +1986,25 @@ end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +local on_drag_start = function(self) + if (not self.bIsDragging) then + self.bIsDragging = true + self:StartMoving() + end +end + +local on_drag_stop = function(self) + self.bIsDragging = false + self:StopMovingOrSizing() +end + +function detailsFramework:MakeDraggable(frame) + frame:SetMovable(true) + frame:EnableMouse(true) + frame:RegisterForDrag("LeftButton") + frame:SetScript("OnDragStart", on_drag_start) + frame:SetScript("OnDragStop", on_drag_stop) +end local simple_panel_mouse_down = function(self, button) if (button == "RightButton") then diff --git a/Libs/DF/tabcontainer.lua b/Libs/DF/tabcontainer.lua index 3cae18945..f44158644 100644 --- a/Libs/DF/tabcontainer.lua +++ b/Libs/DF/tabcontainer.lua @@ -20,11 +20,13 @@ local PixelUtil = PixelUtil ---@field AllFramesByName table ---@field AllButtonsByName table ---@field hookList table +---@field options df_tabcontaineroptions ---@field CurrentIndex number ---@field IsContainer boolean ---@field ButtonSelectedBorderColor table ---@field ButtonNotSelectedBorderColor table ---@field CanCloseWithRightClick boolean +---@field CallOnEachTab fun(self: df_tabcontainer, callback: function, ...) ---@field SetIndex fun(self: df_tabcontainer, index: number) ---@field SelectTabByIndex fun(self: df_tabcontainer, menuIndex: number) ---@field SelectTabByName fun(self: df_tabcontainer, name: string) @@ -54,6 +56,12 @@ local tabTemplate = detailsFramework.table.copy({}, detailsFramework:GetTemplate tabTemplate.backdropbordercolor = nil detailsFramework.TabContainerMixin = { + CallOnEachTab = function(self, callback, ...) + for _, tabFrame in ipairs(self.AllFrames) do + detailsFramework:Dispatch(callback, tabFrame, ...) + end + end, + ---@param self df_tabcontainer ---@param tabIndex number ---@return df_tabcontainerframe @@ -125,6 +133,10 @@ detailsFramework.TabContainerMixin = { ---@type df_tabcontainerframe local tabFrame = tabContainer.AllFrames[menuIndex] + if (not tabFrame) then + return + end + --hide all tab frame and hide the selection glow from tab buttons for i = 1, #tabContainer.AllFrames do ---@type df_tabcontainerframe @@ -191,31 +203,33 @@ detailsFramework.TabContainerFrameMixin = { ---@param self df_tabcontainerframe ---@param button string OnMouseDown = function(self, button) - --search for UIParent - ---@type frame - local highestParent = detailsFramework:FindHighestParent(self) - local tabContainer = self:GetParent() - ---@cast tabContainer df_tabcontainer - - if (button == "LeftButton") then - if (not highestParent.IsMoving and highestParent:IsMovable()) then - highestParent:StartMoving() - highestParent.IsMoving = true - end + if (self:GetParent().options.can_move_parent) then + --search for UIParent + ---@type frame + local highestParent = detailsFramework:FindHighestParent(self) + local tabContainer = self:GetParent() + ---@cast tabContainer df_tabcontainer + + if (button == "LeftButton") then + if (not highestParent.IsMoving and highestParent:IsMovable()) then + highestParent:StartMoving() + highestParent.IsMoving = true + end - elseif (button == "RightButton") then - if (not highestParent.IsMoving and tabContainer.IsContainer) then - if (self.bIsFrontPage) then - if (tabContainer.CanCloseWithRightClick) then - if (highestParent["CloseFunction"]) then - highestParent["CloseFunction"](highestParent) - else - highestParent:Hide() + elseif (button == "RightButton") then + if (not highestParent.IsMoving and tabContainer.IsContainer) then + if (self.bIsFrontPage) then + if (tabContainer.CanCloseWithRightClick) then + if (highestParent["CloseFunction"]) then + highestParent["CloseFunction"](highestParent) + else + highestParent:Hide() + end end + else + --goes back to front page + tabContainer:SelectTabByIndex(1) end - else - --goes back to front page - tabContainer:SelectTabByIndex(1) end end end @@ -224,10 +238,12 @@ detailsFramework.TabContainerFrameMixin = { ---@param self df_tabcontainerframe ---@param button string OnMouseUp = function(self, button) - local frame = detailsFramework:FindHighestParent(self) - if (frame.IsMoving) then - frame:StopMovingOrSizing() - frame.IsMoving = false + if (self:GetParent().options.can_move_parent) then + local frame = detailsFramework:FindHighestParent(self) + if (frame.IsMoving) then + frame:StopMovingOrSizing() + frame.IsMoving = false + end end end, } @@ -249,6 +265,7 @@ detailsFramework.TabContainerFrameMixin = { ---@field button_y number? ---@field button_text_size number? ---@field container_width_offset number? +---@field can_move_parent boolean? ---creates a frame called tabContainer which is used as base for the tab container object ---the function receives a table called tabList which contains sub tables with two keys 'name' and 'text', name is the frame name and text is the text displayed on the button @@ -274,6 +291,10 @@ function detailsFramework:CreateTabContainer(parent, title, frameName, tabList, local buttonTextSize = optionsTable.button_text_size or 10 local containerWidthOffset = optionsTable.container_width_offset or 0 + if (optionsTable.can_move_parent == nil) then + optionsTable.can_move_parent = true + end + local bFirstTabIsCreateOnDemand = false --create the base frame @@ -281,6 +302,7 @@ function detailsFramework:CreateTabContainer(parent, title, frameName, tabList, local tabContainer = CreateFrame("frame", frameName, parent["widget"] or parent, "BackdropTemplate") tabContainer.hookList = hookList or {} tabContainer:SetSize(optionsTable.width or 750, optionsTable.height or 450) + tabContainer.options = optionsTable detailsFramework:Mixin(tabContainer, detailsFramework.TabContainerMixin) @@ -396,7 +418,10 @@ function detailsFramework:CreateTabContainer(parent, title, frameName, tabList, local allocatedSpaceForButtons = parentFrameWidth - ((#tabList - 2) * spaceBetweenButtons) - buttonAnchorX + containerWidthOffset local amountButtonsPerRow = math.floor(allocatedSpaceForButtons / buttonWidth) - tabContainer.AllButtons[1]:SetPoint("topleft", mainTitle, "topleft", x, y) + if (tabContainer.AllButtons[1]) then + tabContainer.AllButtons[1]:SetPoint("topleft", mainTitle, "topleft", x, y) + end + x = x + buttonWidth + 2 for i = 2, #tabContainer.AllButtons do diff --git a/Libs/DF/timeline.lua b/Libs/DF/timeline.lua index 105b67da7..ea331953b 100644 --- a/Libs/DF/timeline.lua +++ b/Libs/DF/timeline.lua @@ -44,6 +44,7 @@ local APIFrameFunctions ---@class df_timeline_options : table ---@field width number ---@field height number +---@field can_resize boolean ---@field line_height number ---@field line_padding number ---@field show_elapsed_timeline boolean @@ -186,6 +187,11 @@ detailsFramework.TimeLineElapsedTimeFunctions = { end, Refresh = function(self, elapsedTime, scale) + if (not elapsedTime) then + --invalid data passed + return + end + local parent = self:GetParent() self:SetHeight(self.options.height) @@ -239,10 +245,466 @@ function detailsFramework:CreateElapsedTimeFrame(parent, name, options) elapsedTimeFrame:SetBackdrop(elapsedTimeFrame.options.backdrop) elapsedTimeFrame:SetBackdropColor(unpack(elapsedTimeFrame.options.backdrop_color)) - elapsedTimeFrame.labels = {} + elapsedTimeFrame.labels = {} + + return elapsedTimeFrame +end + + +---@class df_lineindicator_data : table +---@field value number +---@field valueType "PERCENT"|"TIME"|"PIXELS" +---@field width number +---@field color number[] +---@field alpha number +---@field onClick fun(self:df_lineindicator_line) +---@field onEnter fun(self:df_lineindicator_line) +---@field onLeave fun(self:df_lineindicator_line) + +---@class df_lineindicator_line : button +---@field xOffset number +---@field left number +---@field index number +---@field data df_lineindicator_data +---@field Texture texture + +---@param self df_lineindicator +---@param indicator df_lineindicator_line +local lineIndicator_GetValueForMoving = function(self, indicator, leftDiff) + local targetFrame = self:LineIndicatorGetTarget() + local data = indicator.data + + if (data.valueType == "PERCENT") then + local effectiveWidth = targetFrame:GetWidth() - self.lineIndicatorXOffset + local x = indicator:GetLeft() - self.lineIndicatorXOffset + local percent = x / effectiveWidth + data.value = percent + return data.value + + elseif (data.valueType == "TIME") then + local pixelPerSecond = self.lineIndicatorPixelPerSecond + + if (leftDiff) then --leftDiff is the amount of pixels the indicator has moved + --scale the pixels per second with the scale set + pixelPerSecond = pixelPerSecond * self.lineIndicatorScale + --get the time difference in seconds by dividing the pixels moved by the pixels per second + local timeDiff = leftDiff / pixelPerSecond + --add the time difference to the current value + data.value = data.value + timeDiff + return data.value + else + local effectiveWidth = targetFrame:GetWidth() - self.lineIndicatorXOffset + local indicatorXOffset = indicator:GetLeft() - self.lineIndicatorXOffset + local timePercent = indicatorXOffset / effectiveWidth + data.value = timePercent * self.lineIndicatorTotalTime + return data.value + end + + elseif (data.valueType == "PIXELS") then + local x = indicator:GetLeft() - self.lineIndicatorXOffset + data.value = x + return data.value + end +end + +---@class df_lineindicator : table, frame +---@field lineIndicatorTotalTime number +---@field lineIndicatorXOffset number +---@field lineIndicators df_pool +---@field lineIndicatorData table +---@field lineIndicatorValueType string +---@field lineIndicatorFrameTarget frame +---@field lineIndicatorScale number +---@field lineIndicatorLineHeight number +---@field lineIndicatorLineWidth number +---@field lineIndicatorPixelPerSecond number +---@field lineIndicatorColor any +---@field lineIndicatorValueFontString fontstring +---@field LineIndicatorConstructor fun(self:df_lineindicator) +---@field LineIndicatorSetTarget fun(self:df_lineindicator, frameTarget:frame) +---@field LineIndicatorsReset fun(self:df_lineindicator) +---@field LineIndicatorCreateLine fun(self:df_lineindicator, index:number):df_lineindicator_line +---@field LineIndicatorGetLine fun(self:df_lineindicator):df_lineindicator_line +---@field LineIndicatorSetElapsedTime fun(self:df_lineindicator, totalTime:number) +---@field LineIndicatorSetLinePosition fun(self:df_lineindicator, line:df_lineindicator_line, value:number, valueType:string) +---@field LineIndicatorSetValueType fun(self:df_lineindicator, valueType:"PERCENT"|"TIME"|"PIXELS") +---@field LineIndicatorAddData fun(self:df_lineindicator, data:df_lineindicator_data) +---@field LineIndicatorSetData fun(self:df_lineindicator, data:df_lineindicator_data[]) +---@field LineIndicatorRemoveData fun(self:df_lineindicator, dataId:number|df_lineindicator_data) +---@field LineIndicatorAddLine fun(self:df_lineindicator, value:number, valueType:string) : df_lineindicator_line +---@field LineIndicatorSetXOffset fun(self:df_lineindicator, xOffset:number) +---@field LineIndicatorSetScale fun(self:df_lineindicator, scale:number) +---@field LineIndicatorRefresh fun(self:df_lineindicator) +---@field LineIndicatorSetAllLinesWidth fun(self:df_lineindicator, width:number) +---@field LineIndicatorSetAllLinesHeight fun(self:df_lineindicator, height:number) set the height of all lines +---@field LineIndicatorSetAllLinesColor fun(self:df_lineindicator, color:any, g:number?, b:number?) +---@field LineIndicatorSetLineWidth fun(self:df_lineindicator, dataId:number|df_lineindicator_data, newWidth:number) +---@field LineIndicatorSetLineColor fun(self:df_lineindicator, dataId:number|df_lineindicator_data, color:any, g:number?, b:number?) +---@field LineIndicatorSetLineAlpha fun(self:df_lineindicator, dataId:number|df_lineindicator_data, alpha:number) +---@field LineIndicatorGetTarget fun(self:df_lineindicator):frame +---@field LineIndicatorSetPixelsPerSecond fun(self:df_lineindicator, pixelsPerSecond:number) +detailsFramework.LineIndicatorMixin = { + LineIndicatorConstructor = function(self) + self.lineIndicatorTotalTime = 0 + self.lineIndicators = detailsFramework:CreatePool(detailsFramework.LineIndicatorMixin.LineIndicatorCreateLine, self) + self.lineIndicators:SetOnReset(function(lineIndicator) lineIndicator:Hide() lineIndicator:ClearAllPoints() end) + self.lineIndicatorFrameTarget = nil + self.lineIndicatorData = {} + self.lineIndicatorValueType = "PIXELS" + self.lineIndicatorXOffset = 0 + self.lineIndicatorScale = 1 + self.lineIndicatorLineHeight = 50 + self.lineIndicatorLineWidth = 3 + self.lineIndicatorColor = {1, 1, 1} + self.lineIndicatorPixelPerSecond = 20 + end, + + LineIndicatorSetTarget = function(self, frameTarget) + self.lineIndicatorFrameTarget = frameTarget + end, + + LineIndicatorGetTarget = function(self) + return self.lineIndicatorFrameTarget or self + end, + + LineIndicatorSetPixelsPerSecond = function(self, pixelsPerSecond) + self.lineIndicatorPixelPerSecond = pixelsPerSecond + end, + + --hide all indicators and clear their points + ---@param self df_lineindicator + LineIndicatorsReset = function(self) + self.lineIndicatorTotalTime = 0 + self.lineIndicators:Reset() + end, + + ---@param pool df_pool + ---@param self df_lineindicator + ---@return df_lineindicator_line + LineIndicatorCreateLine = function(pool, self) + local index = pool:GetAmount() + 1 + local parentName = self:GetName() + local indicatorName = parentName and parentName .. "LineIndicator" .. index + + local targetFrame = self:LineIndicatorGetTarget() + + ---@type df_lineindicator_line + local indicator = CreateFrame("button", indicatorName, targetFrame, "BackdropTemplate") + indicator:SetSize(3, targetFrame:GetParent():GetHeight()) + indicator:SetFrameLevel(targetFrame:GetFrameLevel() + 10) + + local texture = indicator:CreateTexture(nil, "background") + texture:SetColorTexture(1, 1, 1, 1) + texture:SetAllPoints() + + indicator:SetMovable(true) + indicator:RegisterForDrag("LeftButton") + + indicator:SetScript("OnMouseDown", function() + indicator.left = indicator:GetLeft() + end) + + indicator:SetScript("OnDragStart", function() + indicator:StartMoving() + indicator:SetUserPlaced(false) + + if (not self.lineIndicatorValueFrame) then + self.lineIndicatorValueFrame = CreateFrame("frame", nil, self) + self.lineIndicatorValueFrame:SetSize(100, 20) + self.lineIndicatorValueFrame:SetFrameLevel(self:GetFrameLevel() + 11) + + local valueString = self.lineIndicatorValueFrame:CreateFontString(nil, "overlay", "GameFontNormal") + valueString:SetPoint("left", self.lineIndicatorValueFrame, "left", 2, 0) + self.lineIndicatorValueFrame.lineIndicatorValueFontString = valueString + + valueString.Background = self.lineIndicatorValueFrame:CreateTexture(nil, "artwork") + valueString.Background:SetColorTexture(0, 0, 0, 0.7) + valueString.Background:SetPoint("topleft", valueString, "topleft", -2, 4) + valueString.Background:SetPoint("bottomright", valueString, "bottomright", 2, -4) + end + + self.lineIndicatorValueFrame:Show() + self.lineIndicatorValueFrame:ClearAllPoints() + --self.lineIndicatorValueFrame:SetPoint("bottomleft", indicator, "bottomright", 2, 0) + + local leftValue = indicator.left + + indicator:SetScript("OnUpdate", function() + local newLeftValue = indicator:GetLeft() + local leftDiff = newLeftValue - leftValue --how much the indicator was moved + + local value = lineIndicator_GetValueForMoving(self, indicator, leftDiff) + leftValue = newLeftValue + indicator.left = newLeftValue + + --detailsFramework:DebugVisibility(self.lineIndicatorValueFrame) + self.lineIndicatorValueFrame:SetPoint("topleft", targetFrame, "topleft", newLeftValue + 2, -2) + + if (indicator.data.valueType == "TIME") then + self.lineIndicatorValueFrame.lineIndicatorValueFontString:SetText(detailsFramework:IntegerToTimer(value)) + + elseif (indicator.data.valueType == "PERCENT") then + self.lineIndicatorValueFrame.lineIndicatorValueFontString:SetText(format("%.2f%%", value * 100)) + + elseif (indicator.data.valueType == "PIXELS") then + self.lineIndicatorValueFrame.lineIndicatorValueFontString:SetText(value) + end + end) + end) + + indicator:SetScript("OnDragStop", function() + if (self.lineIndicatorValueFrame) then + self.lineIndicatorValueFrame:Hide() + end + + indicator:SetScript("OnUpdate", nil) + indicator:StopMovingOrSizing() + + --[=[ not over engineering this for now + --need to auto scroll the horizontal scroll frame if the indicator is moved + --get the amount of width that the horizontal scroll frame has scrolled + local horizontalScrolled = 0 + if (self.GetHorizontalScrolledWidth) then + horizontalScrolled = self:GetHorizontalScrolledWidth() or 0 + end + + --add the amount of width that the horizontal scroll frame has scrolled to the indicator left position + if (horizontalScrolled ~= 0) then + local diff = indicator.left + horizontalScrolled + local value = lineIndicator_GetValueForMoving(self, indicator, diff) + end + --]=] + + self:LineIndicatorRefresh() + end) + + indicator.Texture = texture + + return indicator + end, + + LineIndicatorGetLine = function(self) + assert(self.lineIndicators, "LineIndicatorGetLine(): LineIndicatorConstructor() not called.") + local thisIndicator = self.lineIndicators:Acquire() + return thisIndicator + end, + + LineIndicatorSetElapsedTime = function(self, totalTime) + self.lineIndicatorTotalTime = totalTime + end, + + LineIndicatorSetLinePosition = function(self, line, value, valueType) + local targetFrame = self:LineIndicatorGetTarget() + + if (valueType) then + if (valueType == "PERCENT") then + local effectiveWidth = targetFrame:GetWidth() - self.lineIndicatorXOffset + --effectiveWidth = effectiveWidth * self.lineIndicatorScale + local x = effectiveWidth * value + line:ClearAllPoints() + line:SetPoint("topleft", targetFrame, "topleft", self.lineIndicatorXOffset + x, 0) + line.xOffset = x + + elseif (valueType == "TIME") then + assert(self.lineIndicatorTotalTime > 0, "LineIndicatorSetElapsedTime(self, totalTime) must be called before SetLineIndicatorPosition() with valueType TIME.") + + local timePercent = value / self.lineIndicatorTotalTime + + local effectiveWidth = targetFrame:GetWidth() - self.lineIndicatorXOffset + --effectiveWidth = effectiveWidth * self.lineIndicatorScale + + local x = effectiveWidth * timePercent + line:ClearAllPoints() + line:SetPoint("left", targetFrame, "left", self.lineIndicatorXOffset + x, 0) + line:SetHeight(GetScreenHeight()) + line.xOffset = x + line.left = line:GetLeft() + + elseif (valueType == "PIXELS") then + --value = value * self.lineIndicatorScale + line:ClearAllPoints() + line:SetPoint("topleft", targetFrame, "topleft", self.lineIndicatorXOffset + value, 0) + line.xOffset = x + end + end + end, + + LineIndicatorRefresh = function(self) + --release all objects + self.lineIndicators:Reset() + --redraw all objects + for i = 1, #self.lineIndicatorData do + local data = self.lineIndicatorData[i] + if (not data.valueType) then + data.valueType = self.lineIndicatorValueType + end + + local line = self:LineIndicatorAddLine(data.value, data.valueType) + + line.index = i + line.data = data + + if (self.lineIndicatorLineHeight == -1) then + line:SetHeight(GetScreenHeight() * 2) + else + line:SetHeight(self.lineIndicatorLineHeight) + end + + line:SetWidth(data.width or self.lineIndicatorLineWidth) + line:SetAlpha(data.alpha or 1) + line.Texture:SetVertexColor(unpack(self.lineIndicatorColor)) + line:SetScript("OnClick", data.onClick) + line:SetScript("OnEnter", data.onEnter) + line:SetScript("OnLeave", data.onLeave) + end + end, + + LineIndicatorSetValueType = function(self, valueType) + assert(valueType == "PERCENT" or valueType == "TIME" or valueType == "PIXELS", "SetLineIndicatorValueType(valueType): valueType must be PERCENT, TIME or PIXELS.") + self.lineIndicatorValueType = valueType + end, + + LineIndicatorAddLine = function(self, value, valueType) + local line = self:LineIndicatorGetLine() + self:LineIndicatorSetLinePosition(line, value, valueType or self.lineIndicatorValueType) + line:Show() + return line + end, + + LineIndicatorRemoveData = function(self, dataId) + assert(type(dataId) == "number" or type(dataId) == "table", "LineIndicatorRemoveData(dataId): dataId must be the data index or a data table.") + + if (type(dataId) == "number") then + local index = dataId + table.remove(self.lineIndicatorData, index) + + elseif (type(dataId) == "table") then + local dataTable = dataId + for i = 1, #self.lineIndicatorData do + if (self.lineIndicatorData[i] == dataTable) then + table.remove(self.lineIndicatorData, i) + break + end + end + end + + self:LineIndicatorRefresh() + end, + + LineIndicatorAddData = function(self, data) + self.lineIndicatorData[#self.lineIndicatorData+1] = data + self:LineIndicatorRefresh() + end, + + LineIndicatorSetData = function(self, data) + self.lineIndicatorData = data + self:LineIndicatorRefresh() + end, + + LineIndicatorSetXOffset = function(self, xOffset) + self.lineIndicatorXOffset = xOffset + end, + + LineIndicatorSetScale = function(self, scale) + self.lineIndicatorScale = scale + end, + + LineIndicatorSetAllLinesHeight = function(self, height) + assert(type(height) == "number", "LineIndicatorSetAllLinesHeight(height): height must be a number.") + self.lineIndicatorLineHeight = height + self:LineIndicatorRefresh() + end, + + LineIndicatorSetAllLinesWidth = function(self, width) + assert(type(width) == "number", "LineIndicatorSetAllLinesWidth(width): width must be a number.") + self.lineIndicatorLineWidth = width + self:LineIndicatorRefresh() + end, + + LineIndicatorSetLineWidth = function(self, dataId, newWidth) + assert(type(dataId) == "number" or type(dataId) == "table", "LineIndicatorSetLineWidth(dataId): dataId must be the data index or a data table.") + + if (type(dataId) == "number") then + local index = dataId + local data = self.lineIndicatorData[index] + if (data) then + data.width = newWidth + end + + elseif (type(dataId) == "table") then + local dataTable = dataId + for i = 1, #self.lineIndicatorData do + if (self.lineIndicatorData[i] == dataTable) then + self.lineIndicatorData[i].width = newWidth + break + end + end + end + + self:LineIndicatorRefresh() + end, + + LineIndicatorSetAllLinesColor = function(self, color, g, b) + local r, g, b = detailsFramework:ParseColors(color, g, b) + self.lineIndicatorColor[1] = r + self.lineIndicatorColor[2] = g + self.lineIndicatorColor[3] = b + self:LineIndicatorRefresh() + end, + + LineIndicatorSetLineColor = function(self, dataId, color, g, b) + assert(type(dataId) == "number" or type(dataId) == "table", "LineIndicatorSetLineColor(dataId): dataId must be the data index or a data table.") + + local r, g, b = detailsFramework:ParseColors(color, g, b) + + if (type(dataId) == "number") then + local index = dataId + local data = self.lineIndicatorData[index] + if (data) then + data.color[1] = r + data.color[2] = g + data.color[3] = b + end + + elseif (type(dataId) == "table") then + local dataTable = dataId + for i = 1, #self.lineIndicatorData do + if (self.lineIndicatorData[i] == dataTable) then + self.lineIndicatorData[i].color[1] = r + self.lineIndicatorData[i].color[2] = g + self.lineIndicatorData[i].color[3] = b + break + end + end + end + + self:LineIndicatorRefresh() + end, + + LineIndicatorSetLineAlpha = function(self, dataId, alpha) + assert(type(dataId) == "number" or type(dataId) == "table", "LineIndicatorSetLineAlpha(dataId): dataId must be the data index or a data table.") + + if (type(dataId) == "number") then + local index = dataId + local data = self.lineIndicatorData[index] + if (data) then + data.alpha = alpha + end + + elseif (type(dataId) == "table") then + local dataTable = dataId + for i = 1, #self.lineIndicatorData do + if (self.lineIndicatorData[i] == dataTable) then + self.lineIndicatorData[i].alpha = alpha + break + end + end + end + + self:LineIndicatorRefresh() + end, +} - return elapsedTimeFrame -end ---@class df_timeline_block_data : table ---@field [1] number timeInSeconds @@ -479,14 +941,19 @@ detailsFramework.TimeLine_LineMixin = { ---@class df_timeline : scrollframe, df_timeline_mixin, df_optionsmixin, df_framelayout, df_lineindicator ---@field body frame +---@field onClickCallback fun() +---@field onClickCallbackFunc fun() +---@field onClickCallbackArgs any[] +---@field resizeButton button ---@field elapsedTimeFrame df_elapsedtime ---@field horizontalSlider slider ---@field scaleSlider slider ---@field verticalSlider slider ---@field currentScale number +---@field scrolledWidth number ---@field data df_timeline_scrolldata ---@field lines df_timeline_line[] ----@field options table +---@field options df_timeline_options ---@field pixelPerSecond number ---@field totalLength number ---@field defaultColor table @@ -495,537 +962,285 @@ detailsFramework.TimeLine_LineMixin = { ---@class df_timeline_mixin : table ---@field GetLine fun(self:df_timeline, index:number):df_timeline_line ---@field ResetAllLines fun(self:df_timeline) ----@field RefreshTimeLine fun(self:df_timeline) +---@field RefreshTimeLine fun(self:df_timeline, bDoNotRefreshButtons:boolean?) ---@field SetData fun(self:df_timeline, data:table) ----@field GetData fun(self:df_timeline):table -detailsFramework.TimeLineMixin = { - GetLine = function(self, index) - local line = self.lines[index] - if (not line) then - --create a new line - ---@type df_timeline_line - line = CreateFrame("frame", "$parentLine" .. index, self.body, "BackdropTemplate") - detailsFramework:Mixin(line, detailsFramework.TimeLine_LineMixin) - self.lines[index] = line - - local lineHeader = CreateFrame("frame", nil, line, "BackdropTemplate") - lineHeader:SetPoint("topleft", line, "topleft", 0, 0) - lineHeader:SetPoint("bottomleft", line, "bottomleft", 0, 0) - lineHeader:SetScript("OnEnter", self.options.header_on_enter) - lineHeader:SetScript("OnLeave", self.options.header_on_leave) - - line.lineHeader = lineHeader - - --store the individual textures that shows the timeline information - line.blocks = {} - - if (self.options.show_elapsed_timeline) then - line:SetPoint("topleft", self.body, "topleft", 1, -((index-1) * (self.options.line_height + 1)) - 2 - self.options.elapsed_timeline_height) - else - line:SetPoint("topleft", self.body, "topleft", 1, -((index-1) * (self.options.line_height + 1)) - 1) - end - line:SetSize(1, self.options.line_height) --width is set when updating the frame - - line:SetScript("OnEnter", self.options.on_enter) - line:SetScript("OnLeave", self.options.on_leave) - line:SetMouseClickEnabled(false) - - line:SetBackdrop(self.options.backdrop) - line:SetBackdropColor(unpack(self.options.backdrop_color)) - line:SetBackdropBorderColor(unpack(self.options.backdrop_border_color)) - - local icon = detailsFramework:CreateImage(line, "", self.options.line_height, self.options.line_height) - icon:SetPoint("left", line, "left", 2, 0) - line.icon = icon - - local text = detailsFramework:CreateLabel(line, "", detailsFramework:GetTemplate("font", self.options.title_template)) - text:SetPoint("left", icon.widget, "right", 2, 0) - line.text = text - - line.backdrop_color = self.options.backdrop_color or {.1, .1, .1, .3} - line.backdrop_color_highlight = self.options.backdrop_color_highlight or {.3, .3, .3, .5} - end - - return line - end, - - ResetAllLines = function(self) - for i = 1, #self.lines do - self.lines[i]:Reset() - end - end, - - --todo - --make the on enter and leave tooltips - --set icons and texts - --skin the sliders - - RefreshTimeLine = function(self) - --debug - --self.currentScale = 1 - - --calculate the total width - local pixelPerSecond = self.options.pixels_per_second - local totalLength = self.data.length or 1 --total time - local currentScale = self.currentScale - - self.scaleSlider:Enable() - - --how many pixels represent 1 second - local bodyWidth = totalLength * pixelPerSecond * currentScale - self.body:SetWidth(bodyWidth + self.options.header_width) - self.body.effectiveWidth = bodyWidth - - --reduce the default canvas size from the body with and don't allow the max value be negative - local newMaxValue = max(bodyWidth - (self:GetWidth() - self.options.header_width), 0) - - --adjust the scale slider range - local oldMin, oldMax = self.horizontalSlider:GetMinMaxValues() - self.horizontalSlider:SetMinMaxValues(0, newMaxValue) - self.horizontalSlider:SetValue(detailsFramework:MapRangeClamped(oldMin, oldMax, 0, newMaxValue, self.horizontalSlider:GetValue())) - - local defaultColor = self.data.defaultColor or {1, 1, 1, 1} - - --cache values - self.pixelPerSecond = pixelPerSecond - self.totalLength = totalLength - self.defaultColor = defaultColor - self.headerWidth = self.options.header_width - - --calculate the total height - local lineHeight = self.options.line_height - local linePadding = self.options.line_padding - - local bodyHeight = (lineHeight + linePadding) * #self.data.lines - self.body:SetHeight(bodyHeight) - self.verticalSlider:SetMinMaxValues(0, max(bodyHeight - self:GetHeight(), 0)) - self.verticalSlider:SetValue(0) - - --refresh lines - self:ResetAllLines() - for i = 1, #self.data.lines do - local line = self:GetLine(i) - line.dataIndex = i --this index is used inside the line update function to know which data to get - line.lineHeader:SetWidth(self.options.header_width) - line:SetBlocksFromData() --the function to update runs within the line object - end - - --refresh elapsed time frame - --the elapsed frame must have a width before the refresh function is called - self.elapsedTimeFrame:ClearAllPoints() - self.elapsedTimeFrame:SetPoint("topleft", self.body, "topleft", self.options.header_width, 0) - self.elapsedTimeFrame:SetPoint("topright", self.body, "topright", 0, 0) - self.elapsedTimeFrame:Reset() - - self.elapsedTimeFrame:Refresh(self.data.length, self.currentScale) - - --refresh the indicator lines - self:LineIndicatorSetXOffset(self.options.header_width) - self:LineIndicatorSetValueType("TIME") - self:LineIndicatorSetElapsedTime(self.data.length) - self:LineIndicatorRefresh() - - end, - - ---@param self df_timeline - ---@param data df_timeline_scrolldata - SetData = function(self, data) - self.data = data - self:RefreshTimeLine() - end, - - ---@param self df_timeline - ---@return df_timeline_scrolldata - GetData = function(self) - return self.data - end, -} - ----@class df_lineindicator_data : table ----@field value number ----@field valueType "PERCENT"|"TIME"|"PIXELS" ----@field width number ----@field color number[] ----@field alpha number ----@field onClick fun(self:df_lineindicator_line) ----@field onEnter fun(self:df_lineindicator_line) ----@field onLeave fun(self:df_lineindicator_line) - ----@class df_lineindicator_line : button ----@field xOffset number ----@field index number ----@field data df_lineindicator_data ----@field Texture texture - -local lineIndicator_GetValueForMoving = function(self, indicator) - local targetFrame = self:LineIndicatorGetTarget() - local data = indicator.data - - if (data.valueType == "PERCENT") then - local effectiveWidth = targetFrame:GetWidth() - self.xOffset - local x = indicator:GetLeft() - self.xOffset - local percent = x / effectiveWidth - data.value = percent - return data.value - - elseif (data.valueType == "TIME") then - local effectiveWidth = targetFrame:GetWidth() - self.xOffset - local x = indicator:GetLeft() - self.xOffset - local timePercent = x / effectiveWidth - data.value = timePercent * self.lineIndicatorTime - return data.value - - elseif (data.valueType == "PIXELS") then - local x = indicator:GetLeft() - self.xOffset - data.value = x - return data.value - end -end - ----@class df_lineindicator : table, frame ----@field lineIndicatorTime number ----@field xOffset number ----@field lineIndicators df_pool ----@field data table ----@field valueType string ----@field frameTarget frame ----@field scale number ----@field lineHeight number ----@field lineWidth number ----@field color any ----@field ValueFontString fontstring ----@field LineIndicatorConstructor fun(self:df_lineindicator) ----@field LineIndicatorSetTarget fun(self:df_lineindicator, frameTarget:frame) ----@field LineIndicatorsReset fun(self:df_lineindicator) ----@field LineIndicatorCreateLine fun(self:df_lineindicator, index:number):df_lineindicator_line ----@field LineIndicatorGetLine fun(self:df_lineindicator):df_lineindicator_line ----@field LineIndicatorSetElapsedTime fun(self:df_lineindicator, totalTime:number) ----@field LineIndicatorSetLinePosition fun(self:df_lineindicator, line:df_lineindicator_line, value:number, valueType:string) ----@field LineIndicatorSetValueType fun(self:df_lineindicator, valueType:"PERCENT"|"TIME"|"PIXELS") ----@field LineIndicatorAddData fun(self:df_lineindicator, data:df_lineindicator_data) ----@field LineIndicatorSetData fun(self:df_lineindicator, data:df_lineindicator_data[]) ----@field LineIndicatorRemoveData fun(self:df_lineindicator, dataId:number|df_lineindicator_data) ----@field LineIndicatorAddLine fun(self:df_lineindicator, value:number, valueType:string) : df_lineindicator_line ----@field LineIndicatorSetXOffset fun(self:df_lineindicator, xOffset:number) ----@field LineIndicatorSetScale fun(self:df_lineindicator, scale:number) ----@field LineIndicatorRefresh fun(self:df_lineindicator) ----@field LineIndicatorSetAllLinesWidth fun(self:df_lineindicator, width:number) ----@field LineIndicatorSetAllLinesHeight fun(self:df_lineindicator, height:number) set the height of all lines ----@field LineIndicatorSetAllLinesColor fun(self:df_lineindicator, color:any, g:number?, b:number?) ----@field LineIndicatorSetLineWidth fun(self:df_lineindicator, dataId:number|df_lineindicator_data, newWidth:number) ----@field LineIndicatorSetLineColor fun(self:df_lineindicator, dataId:number|df_lineindicator_data, color:any, g:number?, b:number?) ----@field LineIndicatorSetLineAlpha fun(self:df_lineindicator, dataId:number|df_lineindicator_data, alpha:number) -detailsFramework.LineIndicatorMixin = { - LineIndicatorConstructor = function(self) - self.lineIndicatorTime = 0 - self.lineIndicators = detailsFramework:CreatePool(detailsFramework.LineIndicatorMixin.LineIndicatorCreateLine, self) - self.lineIndicators:SetOnReset(function(lineIndicator) lineIndicator:Hide() lineIndicator:ClearAllPoints() end) - self.frameTarget = nil - self.data = {} - self.valueType = "PIXELS" - self.xOffset = 0 - self.scale = 1 - self.lineHeight = 50 - self.lineWidth = 3 - self.color = {1, 1, 1} - end, - - LineIndicatorSetTarget = function(self, frameTarget) - self.frameTarget = frameTarget - end, - - LineIndicatorGetTarget = function(self) - return self.frameTarget or self - end, - - --hide all indicators and clear their points - ---@param self df_lineindicator - LineIndicatorsReset = function(self) - self.lineIndicatorTime = 0 - self.lineIndicators:Reset() - end, - - ---@param pool df_pool - ---@param self df_lineindicator - ---@return df_lineindicator_line - LineIndicatorCreateLine = function(pool, self) - local index = pool:GetAmount() + 1 - local parentName = self:GetName() - local indicatorName = parentName and parentName .. "LineIndicator" .. index - - local targetFrame = self:LineIndicatorGetTarget() - - ---@type df_lineindicator_line - local indicator = CreateFrame("button", indicatorName, targetFrame, "BackdropTemplate") - indicator:SetSize(3, targetFrame:GetParent():GetHeight()) - indicator:SetFrameLevel(targetFrame:GetFrameLevel() + 10) - - local texture = indicator:CreateTexture(nil, "background") - texture:SetColorTexture(1, 1, 1, 1) - texture:SetAllPoints() - - indicator:SetMovable(true) - indicator:SetScript("OnMouseDown", function(_, button) - if (button == "LeftButton") then - if (not self.ValueFontString) then - self.ValueFontString = self:CreateFontString(nil, "overlay", "GameFontNormal") - self.ValueFontString.Background = self:CreateTexture(nil, "artwork") - self.ValueFontString.Background:SetColorTexture(0, 0, 0, 0.7) - self.ValueFontString.Background:SetPoint("topleft", self.ValueFontString, "topleft", -2, 2) - self.ValueFontString.Background:SetPoint("bottomright", self.ValueFontString, "bottomright", 2, -2) - end - - local mouseX = GetCursorPosition() - local point1, point2, point3, point4, point5 = indicator:GetPoint(1) - self.ValueFontString:Show() - self.ValueFontString:ClearAllPoints() - self.ValueFontString:SetPoint("bottomleft", indicator, "bottomright", 2, 0) - - indicator:SetScript("OnUpdate", function() - local offset = GetCursorPosition() - mouseX - indicator:SetPoint(point1, point2, point3, point4 + offset, point5) - local value = lineIndicator_GetValueForMoving(self, indicator) - - if (indicator.data.valueType == "TIME") then - self.ValueFontString:SetText(detailsFramework:IntegerToTimer(value)) - - elseif (indicator.data.valueType == "PERCENT") then - self.ValueFontString:SetText(format("%.2f%%", value * 100)) - - elseif (indicator.data.valueType == "PIXELS") then - self.ValueFontString:SetText(value) - end - end) - end - end) +---@field GetData fun(self:df_timeline):table +---@field RefreshResize fun(self:df_timeline) +---@field SetCanResize fun(self:df_timeline, canResize:boolean) +---@field OnSizeChanged fun(self:df_timeline) +---@field SetOnClickCallback fun(self:df_timeline, callback:fun(), ...:any) +---@field UpdateOnClickCallback fun(self:df_timeline, button:button?) +---@field GetHorizontalScrolledWidth fun(self:df_timeline):number +detailsFramework.TimeLineMixin = { + GetHorizontalScrolledWidth = function(self) + return self.scrolledWidth + end, - indicator:SetScript("OnMouseUp", function(_, button) - if (button == "LeftButton") then - indicator:StopMovingOrSizing() - indicator:SetScript("OnUpdate", nil) - local value = lineIndicator_GetValueForMoving(self, indicator) - self.ValueFontString:Hide() - self.ValueFontString.Background:Hide() - self:LineIndicatorRefresh() - end - end) + SetOnClickCallback = function(self, callback, ...) + self.onClickCallbackArgs = {...} + self.onClickCallback = callback - indicator.Texture = texture + self.onClickCallbackFunc = function(button) + local second = button.index + self.onClickCallback(second-1, unpack(self.onClickCallbackArgs)) + end - return indicator + self:UpdateOnClickCallback() end, - LineIndicatorGetLine = function(self) - assert(self.lineIndicators, "LineIndicatorGetLine(): LineIndicatorConstructor() not called.") - local thisIndicator = self.lineIndicators:Acquire() - return thisIndicator + UpdateOnClickCallback = function(self, button) + if (button) then + button:SetScript("OnClick", self.onClickCallbackFunc) + return + else + for i = 1, #self.body.Buttons do + local thisButton = self.body.Buttons[i] + if (thisButton:IsShown()) then + thisButton:SetScript("OnClick", self.onClickCallbackFunc) + end + end + end end, - LineIndicatorSetElapsedTime = function(self, totalTime) - self.lineIndicatorTime = totalTime + RefreshResize = function(self) + if (self.options.can_resize) then + self:SetResizable(true) + self.resizeButton:Show() + self:SetScript("OnSizeChanged", self.OnSizeChanged) + else + self:SetResizable(false) + self.resizeButton:Hide() + self:SetScript("OnSizeChanged", nil) + end end, - LineIndicatorSetLinePosition = function(self, line, value, valueType) - local targetFrame = self:LineIndicatorGetTarget() - - if (valueType) then - if (valueType == "PERCENT") then - local effectiveWidth = targetFrame:GetWidth() - self.xOffset - effectiveWidth = effectiveWidth * self.scale - local x = effectiveWidth * value - line:SetPoint("left", targetFrame, "left", self.xOffset + x, 0) - line.xOffset = x + SetCanResize = function(self, bCanResize) + self.options.can_resize = bCanResize + self:RefreshResize() + end, - elseif (valueType == "TIME") then - assert(self.lineIndicatorTime > 0, "LineIndicatorSetElapsedTime(self, totalTime) must be called before SetLineIndicatorPosition() with valueType TIME.") + OnSizeChanged = function(self) + local width, height = self:GetSize() + self.horizontalSlider:SetSize(width + 20, 20) + self.horizontalSlider:SetPoint("topleft", self, "bottomleft", 0, 0) - local timePercent = value / self.lineIndicatorTime + self.scaleSlider:SetSize(width + 20, 20) + self.scaleSlider:SetPoint("topleft", self.horizontalSlider, "bottomleft", 0, -2) - local effectiveWidth = targetFrame:GetWidth() - self.xOffset - effectiveWidth = effectiveWidth * self.scale + self.verticalSlider:SetSize(20, height - 2) + self.verticalSlider:SetPoint("topleft", self, "topright", 0, 0) - local x = effectiveWidth * timePercent - line:SetPoint("left", targetFrame, "left", self.xOffset + x, 0) - line.xOffset = x + --self.body:SetHeight(height) - elseif (valueType == "PIXELS") then - value = value * self.scale - line:SetPoint("left", targetFrame, "left", self.xOffset + value, 0) - line.xOffset = x - end + if (self.data.lines) then + self:RefreshTimeLine() end end, - LineIndicatorRefresh = function(self) - --release all objects - self.lineIndicators:Reset() - --redraw all objects - for i = 1, #self.data do - local data = self.data[i] - if (not data.valueType) then - data.valueType = self.valueType + GetLine = function(self, index) + local line = self.lines[index] + if (not line) then + --create a new line + ---@type df_timeline_line + line = CreateFrame("frame", "$parentLine" .. index, self.body, "BackdropTemplate") + detailsFramework:Mixin(line, detailsFramework.TimeLine_LineMixin) + self.lines[index] = line + + local lineHeader = CreateFrame("frame", nil, line, "BackdropTemplate") + lineHeader:SetPoint("topleft", line, "topleft", 0, 0) + lineHeader:SetPoint("bottomleft", line, "bottomleft", 0, 0) + lineHeader:SetScript("OnEnter", self.options.header_on_enter) + lineHeader:SetScript("OnLeave", self.options.header_on_leave) + + line.lineHeader = lineHeader + + --store the individual textures that shows the timeline information + line.blocks = {} + + if (self.options.show_elapsed_timeline) then + line:SetPoint("topleft", self.body, "topleft", 1, -((index-1) * (self.options.line_height + 1)) - 2 - self.options.elapsed_timeline_height) + else + line:SetPoint("topleft", self.body, "topleft", 1, -((index-1) * (self.options.line_height + 1)) - 1) end + line:SetSize(1, self.options.line_height) --width is set when updating the frame - local line = self:LineIndicatorAddLine(data.value, data.valueType) + line:SetScript("OnEnter", self.options.on_enter) + line:SetScript("OnLeave", self.options.on_leave) + line:SetMouseClickEnabled(false) - line.index = i - line.data = data - line:SetHeight(self.lineHeight) - line:SetWidth(data.width or self.lineWidth) - line:SetAlpha(data.alpha or 1) - line.Texture:SetVertexColor(unpack(self.color)) - line:SetScript("OnClick", data.onClick) - line:SetScript("OnEnter", data.onEnter) - line:SetScript("OnLeave", data.onLeave) + line:SetBackdrop(self.options.backdrop) + line:SetBackdropColor(unpack(self.options.backdrop_color)) + line:SetBackdropBorderColor(unpack(self.options.backdrop_border_color)) + + local icon = detailsFramework:CreateImage(line, "", self.options.line_height, self.options.line_height) + icon:SetPoint("left", line, "left", 2, 0) + line.icon = icon + + local text = detailsFramework:CreateLabel(line, "", detailsFramework:GetTemplate("font", self.options.title_template)) + text:SetPoint("left", icon.widget, "right", 2, 0) + line.text = text + + line.backdrop_color = self.options.backdrop_color or {.1, .1, .1, .3} + line.backdrop_color_highlight = self.options.backdrop_color_highlight or {.3, .3, .3, .5} end - end, - LineIndicatorSetValueType = function(self, valueType) - assert(valueType == "PERCENT" or valueType == "TIME" or valueType == "PIXELS", "SetLineIndicatorValueType(valueType): valueType must be PERCENT, TIME or PIXELS.") - self.valueType = valueType + return line end, - LineIndicatorAddLine = function(self, value, valueType) - local line = self:LineIndicatorGetLine() - self:LineIndicatorSetLinePosition(line, value, valueType or self.valueType) - line:Show() - return line + ResetAllLines = function(self) + for i = 1, #self.lines do + self.lines[i]:Reset() + end end, - LineIndicatorRemoveData = function(self, dataId) - assert(type(dataId) == "number" or type(dataId) == "table", "LineIndicatorRemoveData(dataId): dataId must be the data index or a data table.") + --todo + --make the on enter and leave tooltips + --set icons and texts + --skin the sliders - if (type(dataId) == "number") then - local index = dataId - table.remove(self.data, index) + RefreshTimeLine = function(self, bDoNotRefreshButtons) + --debug + --self.currentScale = 1 - elseif (type(dataId) == "table") then - local dataTable = dataId - for i = 1, #self.data do - if (self.data[i] == dataTable) then - table.remove(self.data, i) - break - end - end + if (not self.data.lines) then + --print("Invalid Data!") + --invalid data + return end - self:LineIndicatorRefresh() - end, + --print(debugstack()) - LineIndicatorAddData = function(self, data) - self.data[#self.data+1] = data - self:LineIndicatorRefresh() - end, + --calculate the total width + local pixelPerSecond = self.options.pixels_per_second + local totalLength = self.data.length or 1 --total time + local currentScale = self.currentScale - LineIndicatorSetData = function(self, data) - self.data = data - self:LineIndicatorRefresh() - end, + self.scaleSlider:Enable() - LineIndicatorSetXOffset = function(self, xOffset) - self.xOffset = xOffset - end, + --how many pixels represent 1 second + local bodyWidth = totalLength * pixelPerSecond * currentScale + self.body:SetWidth(bodyWidth + self.options.header_width) + self.body.effectiveWidth = bodyWidth - LineIndicatorSetScale = function(self, scale) - self.scale = scale - end, + if (not bDoNotRefreshButtons) then + --local amountOfButtons = floor(self.body:GetWidth() / (pixelPerSecond * currentScale)) + local amountOfButtons = totalLength + local buttonHeight = self:GetHeight() + local widthPerSecond = pixelPerSecond * currentScale + + --print("Updating Buttons...", amountOfButtons, "bodyHeight??", buttonHeight, "scale:", currentScale) + + for i = 1, amountOfButtons do + local button = self.body.Buttons[i] + if (not button) then + button = CreateFrame("button", "$parentButton" .. i, self.body, "BackdropTemplate") + local overlayTexture = button:CreateTexture(nil, "overlay") + local r, g, b, a = detailsFramework:GetDefaultBackdropColor() + overlayTexture:SetColorTexture(1, 1, 1) + overlayTexture:SetAlpha(i % 2 == 0 and 0.01 or 0.02) + overlayTexture:SetAllPoints() + + --create a highlight texture + local highlightTexture = button:CreateTexture(nil, "highlight") + highlightTexture:SetColorTexture(1, 1, 1, 0.05) + highlightTexture:SetAllPoints() + + --add a backdrop + --button:SetBackdrop(self.options.backdrop) + --button:SetBackdropColor(unpack(self.options.backdrop_color)) + --button:SetBackdropBorderColor(unpack(self.options.backdrop_border_color)) + + self.body.Buttons[i] = button + end - LineIndicatorSetAllLinesHeight = function(self, height) - assert(type(height) == "number", "LineIndicatorSetAllLinesHeight(height): height must be a number.") - self.lineHeight = height - self:LineIndicatorRefresh() - end, + button:SetSize(widthPerSecond, buttonHeight) - LineIndicatorSetAllLinesWidth = function(self, width) - assert(type(width) == "number", "LineIndicatorSetAllLinesWidth(width): width must be a number.") - self.lineWidth = width - self:LineIndicatorRefresh() - end, + local xPosition = (i - 1) * widthPerSecond + xPosition = xPosition + self.options.header_width + button:SetPoint("topleft", self.body, "topleft", xPosition, 0) - LineIndicatorSetLineWidth = function(self, dataId, newWidth) - assert(type(dataId) == "number" or type(dataId) == "table", "LineIndicatorSetLineWidth(dataId): dataId must be the data index or a data table.") + self:UpdateOnClickCallback(button) - if (type(dataId) == "number") then - local index = dataId - local data = self.data[index] - if (data) then - data.width = newWidth + button:Show() + button.index = i end - elseif (type(dataId) == "table") then - local dataTable = dataId - for i = 1, #self.data do - if (self.data[i] == dataTable) then - self.data[i].width = newWidth - break - end + for i = amountOfButtons+1, #self.body.Buttons do + self.body.Buttons[i]:Hide() end end - self:LineIndicatorRefresh() - end, + --reduce the default canvas size from the body with and don't allow the max value be negative + local newMaxValue = max(bodyWidth - (self:GetWidth() - self.options.header_width), 0) - LineIndicatorSetAllLinesColor = function(self, color, g, b) - local r, g, b = detailsFramework:ParseColors(color, g, b) - self.color[1] = r - self.color[2] = g - self.color[3] = b - self:LineIndicatorRefresh() - end, + --adjust the scale slider range + local oldMin, oldMax = self.horizontalSlider:GetMinMaxValues() + self.horizontalSlider:SetMinMaxValues(0, newMaxValue) + self.horizontalSlider:SetValue(detailsFramework:MapRangeClamped(oldMin, oldMax, 0, newMaxValue, self.horizontalSlider:GetValue())) - LineIndicatorSetLineColor = function(self, dataId, color, g, b) - assert(type(dataId) == "number" or type(dataId) == "table", "LineIndicatorSetLineColor(dataId): dataId must be the data index or a data table.") + local defaultColor = self.data.defaultColor or {1, 1, 1, 1} - local r, g, b = detailsFramework:ParseColors(color, g, b) + --cache values + self.pixelPerSecond = pixelPerSecond + self.totalLength = totalLength + self.defaultColor = defaultColor + self.headerWidth = self.options.header_width - if (type(dataId) == "number") then - local index = dataId - local data = self.data[index] - if (data) then - data.color[1] = r - data.color[2] = g - data.color[3] = b - end + --calculate the total height + local lineHeight = self.options.line_height + local linePadding = self.options.line_padding - elseif (type(dataId) == "table") then - local dataTable = dataId - for i = 1, #self.data do - if (self.data[i] == dataTable) then - self.data[i].color[1] = r - self.data[i].color[2] = g - self.data[i].color[3] = b - break - end - end - end + local bodyHeight = (lineHeight + linePadding) * #self.data.lines + self.body:SetHeight(bodyHeight) + self.verticalSlider:SetMinMaxValues(0, max(bodyHeight - self:GetHeight(), 0)) + self.verticalSlider:SetValue(0) - self:LineIndicatorRefresh() - end, + --refresh lines + self:ResetAllLines() + for i = 1, #self.data.lines do + local line = self:GetLine(i) + line.dataIndex = i --this index is used inside the line update function to know which data to get + line.lineHeader:SetWidth(self.options.header_width) + line:SetBlocksFromData() --the function to update runs within the line object + end - LineIndicatorSetLineAlpha = function(self, dataId, alpha) - assert(type(dataId) == "number" or type(dataId) == "table", "LineIndicatorSetLineAlpha(dataId): dataId must be the data index or a data table.") + --refresh elapsed time frame + --the elapsed frame must have a width before the refresh function is called + self.elapsedTimeFrame:ClearAllPoints() + self.elapsedTimeFrame:SetPoint("topleft", self.body, "topleft", self.options.header_width, 0) + self.elapsedTimeFrame:SetPoint("topright", self.body, "topright", 0, 0) + self.elapsedTimeFrame:Reset() - if (type(dataId) == "number") then - local index = dataId - local data = self.data[index] - if (data) then - data.alpha = alpha - end + self.elapsedTimeFrame:Refresh(self.data.length, self.currentScale) - elseif (type(dataId) == "table") then - local dataTable = dataId - for i = 1, #self.data do - if (self.data[i] == dataTable) then - self.data[i].alpha = alpha - break - end - end - end + --refresh the indicator lines + self:LineIndicatorSetXOffset(self.options.header_width) + self:LineIndicatorSetValueType("TIME") + self:LineIndicatorSetScale(self.currentScale) + self:LineIndicatorSetElapsedTime(self.data.length) + self:LineIndicatorSetPixelsPerSecond(self.options.pixels_per_second) + --self:LineIndicator self:LineIndicatorRefresh() + + end, + + ---@param self df_timeline + ---@param data df_timeline_scrolldata + SetData = function(self, data) + self.data = data + self:RefreshTimeLine() + end, + + ---@param self df_timeline + ---@return df_timeline_scrolldata + GetData = function(self) + return self.data end, } +local onScaleChange_RefreshTimer = nil + ---@class df_timeline_body : frame, df_lineindicator @@ -1056,6 +1271,7 @@ function detailsFramework:CreateTimeLineFrame(parent, name, timelineOptions, ela --this table is changed by SetData() frameCanvas.data = {} --placeholder frameCanvas.lines = {} + --frameCanvas.clickButtons = {} frameCanvas.currentScale = 0.5 frameCanvas:SetSize(width, height) @@ -1066,12 +1282,14 @@ function detailsFramework:CreateTimeLineFrame(parent, name, timelineOptions, ela frameBody:SetSize(scrollWidth, scrollHeight) frameCanvas:LineIndicatorSetTarget(frameBody) + frameBody.Buttons = {} + frameCanvas:SetScrollChild(frameBody) frameCanvas.body = frameBody + frameCanvas.body.originalHeight = frameCanvas.body:GetHeight() frameCanvas:BuildOptionsTable(timeline_options, timelineOptions) - --create elapsed time frame frameCanvas.elapsedTimeFrame = detailsFramework:CreateElapsedTimeFrame(frameBody, frameCanvas:GetName() and frameCanvas:GetName() .. "ElapsedTimeFrame", elapsedtimeOptions) @@ -1096,9 +1314,6 @@ function detailsFramework:CreateTimeLineFrame(parent, name, timelineOptions, ela horizontalSlider:SetThumbTexture(horizontalSlider.thumb) horizontalSlider:SetOrientation("horizontal") - horizontalSlider:SetSize(width + 20, 20) - horizontalSlider:SetPoint("topleft", frameCanvas, "bottomleft") - horizontalSlider:SetMinMaxValues(0, scrollWidth) horizontalSlider:SetValue(0) horizontalSlider:SetScript("OnValueChanged", function(self) local _, maxValue = horizontalSlider:GetMinMaxValues() @@ -1128,8 +1343,6 @@ function detailsFramework:CreateTimeLineFrame(parent, name, timelineOptions, ela scaleSlider:SetThumbTexture(scaleSlider.thumb) scaleSlider:SetOrientation("horizontal") - scaleSlider:SetSize(width + 20, 20) - scaleSlider:SetPoint("topleft", horizontalSlider, "bottomleft", 0, -2) scaleSlider:SetMinMaxValues(frameCanvas.options.scale_min, frameCanvas.options.scale_max) scaleSlider:SetValue(detailsFramework:GetRangeValue(frameCanvas.options.scale_min, frameCanvas.options.scale_max, 0.5)) @@ -1138,7 +1351,16 @@ function detailsFramework:CreateTimeLineFrame(parent, name, timelineOptions, ela if (stepValue ~= frameCanvas.currentScale) then local current = stepValue frameCanvas.currentScale = stepValue - frameCanvas:RefreshTimeLine() + local bDoNotRefreshButtons = true + frameCanvas:RefreshTimeLine(bDoNotRefreshButtons) + + if (onScaleChange_RefreshTimer and not onScaleChange_RefreshTimer:IsCancelled()) then + onScaleChange_RefreshTimer:Cancel() + end + + onScaleChange_RefreshTimer = detailsFramework.Schedules.NewTimer(0.1, function() + frameCanvas:RefreshTimeLine() + end) end end) @@ -1160,9 +1382,6 @@ function detailsFramework:CreateTimeLineFrame(parent, name, timelineOptions, ela verticalSlider:SetThumbTexture(verticalSlider.thumb) verticalSlider:SetOrientation("vertical") - verticalSlider:SetSize(20, height - 2) - verticalSlider:SetPoint("topleft", frameCanvas, "topright", 0, 0) - verticalSlider:SetMinMaxValues(0, scrollHeight) verticalSlider:SetValue(0) verticalSlider:SetScript("OnValueChanged", function(self) frameCanvas:SetVerticalScroll(self:GetValue()) @@ -1191,14 +1410,22 @@ function detailsFramework:CreateTimeLineFrame(parent, name, timelineOptions, ela elseif (delta < 0 and currentHorizontal < maxValue) then local amountToScroll = frameBody:GetWidth() / 20 horizontalSlider:SetValue(currentHorizontal + amountToScroll) + local scrolledWidth = horizontalSlider:GetValue() - currentHorizontal + frameCanvas.scrolledWidth = scrolledWidth + frameBody.scrolledWidth = scrolledWidth elseif (delta > 0 and maxValue > 1) then local amountToScroll = frameBody:GetWidth() / 20 horizontalSlider:SetValue(currentHorizontal - amountToScroll) + local scrolledWidth = horizontalSlider:GetValue() - currentHorizontal + frameCanvas.scrolledWidth = scrolledWidth + frameBody.scrolledWidth = scrolledWidth end end) + frameBody.GetHorizontalScrolledWidth = frameCanvas.GetHorizontalScrolledWidth + --mouse drag frameBody:SetScript("OnMouseDown", function(self, button) local x = GetCursorPosition() @@ -1217,6 +1444,27 @@ function detailsFramework:CreateTimeLineFrame(parent, name, timelineOptions, ela frameBody:SetScript("OnUpdate", nil) end) + --create a resize button + local resizerButton = CreateFrame("button", "$parentReziser", frameCanvas) + resizerButton:SetSize(20, 20) + resizerButton:SetAlpha(0.734) + resizerButton:SetPoint("bottomright", frameCanvas, "bottomright", -2, 2) + resizerButton:SetNormalTexture("Interface\\CHATFRAME\\UI-ChatIM-SizeGrabber-Up") + resizerButton:SetPushedTexture("Interface\\CHATFRAME\\UI-ChatIM-SizeGrabber-Down") + resizerButton:SetHighlightTexture("Interface\\CHATFRAME\\UI-ChatIM-SizeGrabber-Highlight") + resizerButton:SetFrameLevel(frameCanvas:GetFrameLevel() + 20) + + resizerButton:SetScript("OnMouseDown", function() + frameCanvas:StartSizing("bottomright") + end) + resizerButton:SetScript("OnMouseUp", function() + frameCanvas:StopMovingOrSizing() + end) + + frameCanvas.resizeButton = resizerButton + frameCanvas:RefreshResize() + frameCanvas:OnSizeChanged() + return frameCanvas end diff --git a/boot.lua b/boot.lua index 278c579bb..ad61aeed9 100644 --- a/boot.lua +++ b/boot.lua @@ -19,8 +19,8 @@ local addonName, Details222 = ... local version, build, date, tvs = GetBuildInfo() - Details.build_counter = 13096 - Details.alpha_build_counter = 13096 --if this is higher than the regular counter, use it instead + Details.build_counter = 13111 + Details.alpha_build_counter = 13111 --if this is higher than the regular counter, use it instead Details.dont_open_news = true Details.game_version = version Details.userversion = version .. " " .. Details.build_counter diff --git a/functions/profiles.lua b/functions/profiles.lua index 44d4c1ca1..99eba22f4 100644 --- a/functions/profiles.lua +++ b/functions/profiles.lua @@ -1653,7 +1653,7 @@ local default_global_data = { grow_direction = "left", }, - autoclose_time = 40, + autoclose_time = 90, mythicrun_time_type = 1, --1: combat time (the amount of time the player is in combat) 2: run time (the amount of time it took to finish the mythic+ run) }, --implementar esse time_type quando estiver dando refresh na janela diff --git a/luaserver.lua b/luaserver.lua index ddb512346..615c92af3 100644 --- a/luaserver.lua +++ b/luaserver.lua @@ -577,6 +577,7 @@ BackdropTemplateMixin = {} ---@field SetMouseClickEnabled fun(self: frame, enabled: boolean) ---@field StartMoving fun(self: frame) ---@field IsMovable fun(self: frame) : boolean +---@field IsMouseEnabled fun(self: frame) : boolean ---@field StartSizing fun(self: frame, sizingpoint: sizingpoint?) ---@field StopMovingOrSizing fun(self: frame) ---@field GetAttribute fun(self: frame, name: string) : any