ToC Updates

This commit is contained in:
Tercio Jose
2023-07-11 19:10:52 -03:00
parent 5180459d67
commit ded52a8383
68 changed files with 42484 additions and 4858 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
## Interface: 100100
## Interface: 100105
## Title: Details! Damage Meter
## Notes: Essential tool to impress that chick in your raid.
## SavedVariables: _detalhes_global, __details_backup
@@ -1,4 +1,4 @@
## Interface: 100100
## Interface: 100105
## Title: Details!: Compare 2.0
## Notes: Replace the Compare tab in the player breakdown window.
## RequiredDeps: Details
@@ -1,4 +1,4 @@
## Interface: 100100
## Interface: 100105
## Title: Details!: Storage
## Notes: Stores information for Details! Damage Meter
## DefaultState: Enabled
@@ -0,0 +1,585 @@
---@alias plugintype
---| "SOLO"
---| "RAID"
---| "TOOLBAR"
---| "STATUSBAR"
---@alias detailsevent
---| "DETAILS_INSTANCE_OPEN"
---| "DETAILS_INSTANCE_CLOSE"
---| "DETAILS_INSTANCE_SIZECHANGED"
---| "DETAILS_INSTANCE_STARTRESIZE"
---| "DETAILS_INSTANCE_ENDRESIZE"
---| "DETAILS_INSTANCE_STARTSTRETCH"
---| "DETAILS_INSTANCE_ENDSTRETCH"
---| "DETAILS_INSTANCE_CHANGESEGMENT"
---| "DETAILS_INSTANCE_CHANGEATTRIBUTE"
---| "DETAILS_INSTANCE_CHANGEMODE"
---| "DETAILS_INSTANCE_NEWROW"
---| "DETAILS_OPTIONS_MODIFIED"
---| "DETAILS_DATA_RESET"
---| "DETAILS_DATA_SEGMENTREMOVED"
---| "COMBAT_ENCOUNTER_START"
---| "COMBAT_ENCOUNTER_END"
---| "COMBAT_PLAYER_ENTER"
---| "COMBAT_PLAYER_LEAVE"
---| "COMBAT_PLAYER_TIMESTARTED"
---| "COMBAT_BOSS_WIPE"
---| "COMBAT_BOSS_DEFEATED"
---| "COMBAT_BOSS_FOUND"
---| "COMBAT_INVALID"
---| "COMBAT_PREPOTION_UPDATED"
---| "COMBAT_CHARTTABLES_CREATING"
---| "COMBAT_CHARTTABLES_CREATED"
---| "COMBAT_ENCOUNTER_PHASE_CHANGED"
---| "COMBAT_ARENA_START"
---| "COMBAT_ARENA_END"
---| "COMBAT_MYTHICDUNGEON_START"
---| "COMBAT_MYTHICDUNGEON_END"
---| "GROUP_ONENTER"
---| "GROUP_ONLEAVE"
---| "ZONE_TYPE_CHANGED"
---| "REALM_CHANNEL_ENTER"
---| "REALM_CHANNEL_LEAVE"
---| "COMM_EVENT_RECEIVED"
---| "COMM_EVENT_SENT"
---| "UNIT_SPEC"
---| "UNIT_TALENTS"
---| "PLAYER_TARGET"
---| "DETAILS_PROFILE_APPLYED"
---@alias containertype number this container type is the number used to identify the actorcontainer type when using combat:GetContainer(containertype), can be 1, 2, 3, or 4.
---@alias actorclass string this is the class of the actor, can be "WARRIOR", "PALADIN", "HUNTER", "ROGUE", "PRIEST", "DEATHKNIGHT", "SHAMAN", "MAGE", "WARLOCK", "MONK", "DRUID", "DEMONHUNTER"
---@alias actorspec number this is the specID of the actor
---@alias uniquecombatid number a unique ID to point to a single combat, each character has its ID counter, use with Details:DoesCombatWithUIDExists(); Details:GetCombatByUID(); retrive with combat:GetCombatUID()
---@class petinfo : table
---@field key1 ownername
---@field key2 guid
---@field key3 unixtime
---@field key4 boolean
---@field key5 petname
---@field key6 guid
---@class details
---@field SpellTableMixin spelltablemixin
---@field GetInstance fun(self: details) : instance
---@field GetWindow fun(self: details) : instance this is an alias of GetInstance
---@field GetCombat fun(self: details) : combat
---@field GetSpellSchoolFormatedName fun(self: details, spellschool: spellschool) : string
---@field CommaValue fun(self: details, number: number) : string
---@field CreateEventListener fun(self: details) : table
---@class detailseventlistener : table
---@field RegisterEvent fun(self: detailseventlistener, event: detailsevent, callback: function)
---@field UnregisterEvent fun(self: detailseventlistener, event: detailsevent)
---@class deathtable : table
---@field key1 any[] what happened to the player before death
---@field key2 number unix time
---@field key3 string player name
---@field key4 string player class
---@field key5 number max health
---@field key6 string time of death as string
---@field dead boolean just a boolean to indicate this is a death table
---@field last_cooldown {key1: unixtime, key2: spellid}
---@field dead_at combattime
---@field spec specializationid
---@class customspellinfo : {name: string, isPassive: boolean, itemId: number, icon: string|number}
---@class customiteminfo: {itemId: number, isPassive: boolean}
---@class savedspelldata : {key1: number, key2: string, key3: number}
---@class alternatepowertable : {last: number, total: number}
---@class combat : table
---@field bIsClosed boolean if true the combat is closed (passed by the EndCombat() function)
---@field __destroyedBy string
---@field amountCasts {[string]: table<string, number>}
---@field instance_type instancetype "raid" or "party" or "pvp" or "arena" or "none" or "scenario"
---@field end_time number
---@field start_time number
---@field combat_counter number
---@field is_trash boolean while in raid this is set to true if the combat isn't raid boss, in dungeon this is set to true if the combat isn't a boss or if the dungeon isn't a mythic+
---@field raid_roster table<string, string> [unitName] = unitGUID
---@field overall_added boolean is true when the combat got added into the overall combat
---@field is_mythic_dungeon_trash boolean
---@field is_mythic_dungeon_run_id number
---@field is_mythic_dungeon_segment boolean
---@field trinketProcs table<actorname, table<spellid, {cooldown: number, total: number}>>
---@field alternate_power table<actorname, alternatepowertable>
---@field totals {key1: table, key2: table, key3: table, key3: table}
---@field totals_grupo {key1: table, key2: table, key3: table, key3: table}
---@field __destroyed boolean
---@field PhaseData table
---@field is_boss table
---@field is_world_trash_combat boolean when true this combat is a regular combat done in the world, not in a dungeon, raid, battleground, arena, ...
---@field player_last_events table<string, table[]> record the latest events of each player, latter used to build the death log
---@field GetCombatUID fun(combat: combat) : uniquecombatid
---@field GetTimeData fun(combat: combat, dataName: string) : table
---@field GetPhases fun(combat: combat) : table
---@field GetCombatTime fun(combat) : number
---@field GetDeaths fun(combat) : table --get the table which contains the deaths of the combat
---@field GetStartTime fun(combat: combat) : number
---@field SetStartTime fun(combat: combat, time: number)
---@field GetEndTime fun(combat: combat) : number
---@field GetDifficulty fun(combat: combat) : number return the dungeon or raid difficulty for boss fights
---@field GetEncounterCleuID fun(combat: combat) : number return the encounterId for boss fights, this number is gotten from the ENCOUNTER_START event
---@field GetBossInfo fun(combat: combat) : table a table containing many informations about the boss fight
---@field SetEndTime fun(combat: combat, time: number)
---@field CopyDeathsFrom fun(combat1: combat, combat2: combat, bMythicPlus: boolean) copy the deaths from combat2 to combat1, use true on bMythicPlus if the combat is from a mythic plus run
---@field GetContainer fun(combat: combat, containerType: containertype) : actorcontainer get an actorcontainer, containerType can be 1 for damage, 2 heal, 3 resources, 4 utility
---@field GetSpellCastAmount fun(combat: combat, actorName: string, spellName: string) : number get the amount of times a spell was casted
---@field RemoveActorFromSpellCastTable fun(combat: combat, actorName: string)
---@field GetSpellCastTable fun(combat: combat, actorName: string|nil) : table
---@field GetSpellUptime fun(combat: combat, actorName: string, spellId: number, auraType: string|nil) : number get the uptime of a buff or debuff
---@field GetActor fun(combat: combat, containerType: number, playerName: string) : actor
---@field CreateAlternatePowerTable fun(combat: combat, actorName: string) : alternatepowertable
---@field GetCombatNumber fun(combat: combat) : number get a unique number representing the combatId, each combat has a unique number
---@field SetDate fun(combat: combat, startDate: string, endDate: string) set the start and end date of the combat, format: "H:M:S"
---@field GetDate fun(combat: combat) : string, string get the start and end date of the combat, format: "H:M:S"
---@field GetRoster fun(combat: combat) : table<string, string> get the roster of the combat, the table contains the names of the players in the combat
---@field InstanceType fun(combat: combat) : string get the instance type of the combat, can be "raid" or "party" or "pvp" or "arena" or "none"
---@field IsTrash fun(combat: combat) : boolean is true if the combat is a trash combat
---@class actorcontainer : table contains two tables _ActorTable and _NameIndexTable, the _ActorTable contains the actors, the _NameIndexTable contains the index of the actors in the _ActorTable, making quick to reorder them without causing overhead
---@field need_refresh boolean when true the container is dirty and needs to be refreshed
---@field _ActorTable table array of actors
---@field _NameIndexTable table<string, number> [actorName] = actorIndex in the _ActorTable, actorcontainer:Remap() refreshes the _NameIndexTable
---@field GetActor fun(container: actorcontainer, actorName: string) get an actor by its name
---@field GetSpellSource fun(container: actorcontainer, spellId: number) get the first actor found which casted the spell
---@field GetAmount fun(container: actorcontainer, actorName: string, key: string) get the amount of actor[key]
---@field GetTotal fun(container: actorcontainer, key: string) get the total amount of actor[key] for all actors
---@field GetTotalOnRaid fun(container: actorcontainer, key: string, combat: combat) get the total amount of actor[key] only for the actors which are in the raid
---@field GetActorTable fun(container: actorcontainer) get the table<actorIndex, actorObject> which contains the actors
---@field ListActors fun(container: actorcontainer) usage: for index, actorObject in container:ListActors() do
---@field RemoveActor fun(container: actorcontainer, actor: actor) remove an actor from the container
---@field GetType fun(container: actorcontainer) : number get the container type, 1 for damage, 2 for heal, 3 for energy, 4 for utility
---@field Remap fun(container: actorcontainer) refreshes the _NameIndexTable part of the container
---@field Cleanup fun(container: actorcontainer) remove all destroyed actors from the container
---@class spellcontainer : table
---@field _ActorTable table store [spellId] = spelltable
---@field GetSpell fun(container: spellcontainer, spellId: number) get a spell by its id, does not create if not found
---@field ListActors fun(container: spellcontainer) : any, any usage: for spellId, spelltable in container:ListActors() do
---@field ListSpells fun(container: spellcontainer) : any, any usage: for spellId, spelltable in container:ListActors() do
---@field HasTwoOrMoreSpells fun(container: spellcontainer) : boolean return true if the container has two or more spells
---@field GetOrCreateSpell fun(self: spellcontainer, spellId: number, bCanCreateSpellIfMissing: boolean|nil, cleuToken: string|nil) : spelltable
---@class friendlyfiretable : table
---@field total number total amount of friendly fire caused by the actor
---@field spells table<spellid, number> spellId = total
---@class spelltable : table
---@field uptime number
---@field total number
---@field spellschool number
---@field counter number amount of hits
---@field c_amt number critical hits by a damage or heal spell
---@field c_min number min damage or healing done by critical hits of the spell
---@field c_max number min damage or healing done by critical hits of the spell
---@field c_total number total damage or heal made by critical hits of the spell
---@field n_amt number normal hits by a damage or heal spell
---@field n_min number min damage or healing done by normal hits of the spell
---@field n_max number min damage or healing done by normal hits of the spell
---@field n_total number total damage or heal made by normal hits of the spell
---@field targets table<string, number> store the [target name] = total value
---@field targets_overheal table<string, number>
---@field targets_absorbs table<string, number>
---@field extra table store extra data
---@field id number --spellid
---@field is_shield boolean --true if the spell is a shield
---@field successful_casted number successful casted times (only for enemies)
---@field g_amt number glacing hits
---@field g_dmg number
---@field r_amt number --resisted
---@field r_dmg number
---@field b_amt number --blocked
---@field b_dmg number
---@field a_amt number --absorved
---@field a_dmg number
---@field e_total number
---@field e_amt number
---@field e_lvl table<number, number>
---@field e_dmg table<number, number>
---@field e_heal table<number, number>
---@field isReflection boolean
---@field totalabsorb number healing absorbed
---@field absorbed number damage absorbed by shield | healing absorbed by buff or debuff
---@field overheal number
---@field totaldenied number
---@class targettable : {[string]: number}
---@class actor : table
---@field owner actor
---@field tipo number the container type
---@field ownerName string name of the owner of the pet, a pet without an owner is considered an orphan and be suitable for garbage collection
---@field pets table<number, string>
---@field arena_enemy boolean if true the actor is an enemy in an arena match
---@field dps_started boolean if true the actor started to do damage or healing
---@field start_time unixtime when this actor started to be tracked
---@field end_time number when this actor stopped to be tracked, end_time - start_time is the activity time of the actor
---@field displayName string actor name shown in the regular window
---@field pvp boolean indicates if the actor is a part of a pvp match
---@field flag_original number original actor flag from what was received in the combat log
---@field debuff_uptime_spells table
---@field buff_uptime_spells table
---@field spells spellcontainer
---@field aID number|string actorID is a realm-playername or npcID
---@field spellicon number|string
---@field cooldowns_defensive_spells table
---@field nome string name of the actor
---@field isTank boolean if true the player had the spec TANK during the combat
---@field serial string
---@field spec number
---@field grupo boolean
---@field fight_component boolean
---@field boss_fight_component boolean
---@field pvp_component boolean
---@field boss boolean
---@field last_event unixtime
---@field total_without_pet number
---@field total number
---@field targets targettable
---@field GetSpell fun(actor: actor, spellId: number) : spelltable
---@field BuildSpellTargetFromBreakdownSpellData fun(actor: actor, bkSpellData: spelltableadv) : table
---@field BuildSpellTargetFromSpellTable fun(actor: actor, spellTable: spelltable) : table
---@field raid_targets table<number, number>
---@field IsPlayer fun(actor: actor) : boolean return true if the actor is controlled by a player
---@field IsPetOrGuardian fun(actor: actor) : boolean return true if the actor is a pet or guardian
---@field IsGroupPlayer fun(actor: actor) : boolean return true if the actor is a player in the group (or was in the group during the combat)
---@field GetSpellContainer fun(actor: actor, containerType: "debuff"|"buff"|"spell"|"cooldowns"|"dispel") : spellcontainer
---@field Class fun(actor: actor) : string get the ingame class of the actor
---@field Spec fun(actor: actor) : string get the ingame spec of the actor
---@field Name fun(actor: actor) : string get the name of the actor
---@field Tempo fun(actor: actor) : number get the activity or effective time of the actor
---@field GetPets fun(actor: actor) : table<number, string> get a table with all pet names that belong to the player
---@field GetSpellList fun(actor: actor) : table<number, spelltable>
---@field GetSpellContainerNames fun(container: actorcontainer) : string[] get the table which contains the names of the spell containers
---@class actordamage : actor
---@field friendlyfire_total number
---@field friendlyfire table<actorname, friendlyfiretable>
---@field damage_taken number amount of damage the actor took during the segment
---@field damage_from table<string, boolean> store the name of the actors which damaged the actor, format: [actorName] = true
---@field totalabsorbed number amount of damage dealt by the actor by got absorbed by the target, this is a "ABSORB" type of miss but still counts as damage done
---@class actorheal : actor
---@field healing_taken number amount of healing the actor took during the segment
---@field totalover number amount of healing that was overhealed
---@field totalabsorb number amount of healing that was absorbed
---@field heal_enemy_amt number amount of healing done to enemies this included enemy to enemy heals
---@field totaldenied number amount of healing that was denied by the target - from cleu event SPELL_HEAL_ABSORBED
---@field totalover_without_pet number amount of healing that was overhealed without the pet healing
---@field healing_from table<string, boolean> store the name of the actors which healed the actor, format: [actorName] = true
---@field heal_enemy table<number, number> store the amount of healing done by each spell that landed into an enemy, format: [spellId] = healing done
---@field targets_overheal table<string, number> [targetName] = overheal
---@field targets_absorbs table<string, number> [targetName] = absorbs
---@class actorresource : actor
---@field powertype number power type of the actor
---@field alternatepower number alternate power of the actor
---@class actorutility : actor
---@field cc_break number amount of times the actor broke a cc
---@field interrupt number amount of times the actor interrupted a spell
---@field ress number amount of times the actor ressed a player
---@field dead number amount of times the actor died
---@field cooldowns_defensive number amount of times the actor used a defensive cooldown
---@field buff_uptime number amount of time the actor had a buff
---@field debuff_uptime number amount of time the actor had a debuff
---@field cc_done number amount of times the actor applyed a crowdcontrol on a target
---@field cc_done_targets table<string, number> [targetName] = amount of times the actor cc'd the target
---@field cc_done_spells spellcontainer
---@field dispell number amount of times the actor dispelled a buff or debuff
---@field dispell_spells spellcontainer
---@field dispell_targets table<string, number> [targetName] = amount
---@field dispell_oque table<number, number> [spellId] = amount, amount of times the actor dispelled the spellId
--interrupt_targets interrupt_spells interrompeu_oque
--cc_break_targets cc_break_spells cc_break_oque
---@class segmentid : number
---@class instanceid : number
---@class attributeid : number
---@class modeid : number
---@class instance : table
---@field segmento segmentid
---@field showing combat
---@field meu_id instanceid
---@field is_interacting boolean
---@field modo modeid
---@field atributo attributeid
---@field sub_atributo attributeid
---@field ativa boolean
---@field freezed boolean
---@field sub_atributo_last table
---@field row_info table
---@field GetSize fun(instance: instance) : width, height
---@field GetInstanceGroup fun() : table
---@field GetCombat fun(instance: instance)
---@field ChangeIcon fun(instance: instance)
---@field CheckIntegrity fun(instance: instance)
---@field SetMode fun(instance: instance, mode: modeid)
---@field GetMode fun(instance: instance) : modeid
---@field IsInteracting fun(instance: instance) : boolean
---@field IsLowerInstance fun(instance: instance) : boolean
---@field IsEnabled fun(instance: instance) : boolean
---@field GetId fun(instance: instance) : instanceid
---@field GetSegmentId fun(instance: instance) : segmentid
---@field RefreshCombat fun(instance: instance)
---@field Freeze fun(instance: instance)
---@field UnFreeze fun(instance: instance)
---@field SetSegment fun(instance: instance, segment: segmentid, force: boolean|nil)
---@field GetDisplay fun(instance: instance) : attributeid, attributeid
---@field ResetWindow fun(instance: instance, resetType: number|nil, segmentId: segmentid|nil)
---@field RefreshData fun(instance: instance, force: boolean|nil)
---@field RefreshWindow fun(instance: instance, force: boolean|nil)
---@class trinketdata : table
---@field itemName string
---@field spellName string
---@field lastActivation number
---@field lastPlayerName string
---@field totalCooldownTime number
---@field activations number
---@field lastCombatId number
---@field minTime number
---@field maxTime number
---@field averageTime number
---@class tabframe : frame this is the tab frame object for the breakdown window
---@class breakdownwindow : frame
---@field shownPluginObject table
---@field BreakdownSideMenuFrame frame frame attached to the left or right side of the breakdown window
---@field BreakdownPluginSelectionFrame frame frame which has buttons to select a plugin to show in the breakdown window
---@field BreakdownTabsFrame frame where the tab buttons are located (parent frame)
---@field RegisteredPluginButtons button[] table which contains plugins buttons that are registered to the breakdown window
---@field RegisterPluginButton fun(button: button) register a plugin button to the breakdown window
---@field GetShownPluginObject fun() : table get the plugin object that is currently shown in the breakdown window
---@class breakdownscrolldata : table
---@field totalValue number total done by the actor
---@field combatTime number
---@field [spelltableadv] spelltableadv indexed part of the table
---@class headercolumndatasaved : {enabled: boolean, width: number, align: string}
---@class breakdownexpandbutton : button
---@field texture texture
---@class breakdownspellscrollframe : df_scrollboxmixin, scrollframe
---@field Header df_headerframe
---@field RefreshMe fun(scrollFrame: breakdownspellscrollframe, data: table|nil)
---@field SortKey string
---@field SortOrder string
---@class breakdowntargetscrollframe : df_scrollboxmixin, scrollframe
---@field Header df_headerframe
---@field RefreshMe fun(scrollFrame: breakdowntargetscrollframe, data: table|nil)
---@class breakdowngenericscrollframe : df_scrollboxmixin, scrollframe
---@field Header df_headerframe
---@field RefreshMe fun(scrollFrame: breakdowngenericscrollframe, data: table|nil)
---@class breakdownphasescrollframe : df_scrollboxmixin, scrollframe
---@field Header df_headerframe
---@field RefreshMe fun(scrollFrame: breakdownphasescrollframe, data: table|nil)
---@class breakdownphasebar : button, df_headerfunctions
---@field index number
---@field Icon texture
---@field InLineTexts fontstring[]
---@field statusBar breakdownspellbarstatusbar
---@class breakdowngenericbar : button, df_headerfunctions
---@field index number
---@field rank number
---@field name string
---@field percent number
---@field amount number
---@field total number
---@field actorName string
---@field Icon texture
---@field InLineTexts fontstring[]
---@field statusBar breakdownspellbarstatusbar
---@field bIsFromLeftScroll boolean
---@field bIsFromRightScroll boolean
---@class breakdowntargetbar : button, df_headerfunctions
---@field index number
---@field rank number
---@field name string
---@field percent number
---@field amount number
---@field total number
---@field actorName string
---@field bkTargetData breakdowntargettable
---@field Icon texture
---@field InLineTexts fontstring[]
---@field statusBar breakdownspellbarstatusbar
---@class breakdownspellbar : button, df_headerfunctions
---@field index number
---@field rank number
---@field spellId number
---@field name string
---@field combatTime number
---@field perSecond number
---@field percent number
---@field amountCasts number
---@field average number
---@field castAverage number
---@field onMouseUpTime number GetTime() of when the spellbar got OnMouseUp event
---@field cursorPosX number mouse position when the spellbar got OnMouseDown event
---@field cursorPosY number mouse position when the spellbar got OnMouseDown event
---@field spellTable spelltable
---@field bkSpellData spelltableadv
---@field statusBar breakdownspellbarstatusbar
---@field expandButton breakdownexpandbutton
---@field spellIconFrame frame
---@field spellIcon texture
---@field targetsSquareFrame breakdowntargetframe
---@field targetsSquareTexture texture
---@field overlayTexture texture
---@field bIsExpandedSpell boolean
---@field ExpandedChildren breakdownspellbar[] store the spellbars which are expanded from this spellbar (spellbars shown when the expand button is pressed)
---@field InLineTexts fontstring[]
---@class breakdownspellbarstatusbar : statusbar
---@field backgroundTexture texture
---@field overlayTexture texture
---@field highlightTexture texture
---spelltableadv is similar to spelltable but allow custom members, methods and any modification isn't save to saved variables
---@class spelltableadv : spelltable, spelltablemixin
---@field expanded boolean if is true the show the nested spells
---@field spellTables spelltable[]
---@field nestedData bknesteddata[]
---@field bCanExpand boolean
---@field expandedIndex number
---@field bIsExpanded boolean
---@field statusBarValue number
---@field npcId npcid
---@field actorName string --when showing an actor header, this is the actor name
---@field bIsActorHeader boolean if this is true, the spellbar is an actor header, which is a bar with the actor name with the actor spells nested
---@field actorIcon textureid|texturepath
---@class bknesteddata : {spellId: number, spellTable: spelltable, actorName: string, value: number, bIsActorHeader: boolean} fills .nestedData table in spelltableadv, used to store the nested spells data, 'value' is set when the breakdown sort the values by the selected header
---@class breakdowntargetframe : frame
---@field spellId number
---@field bkSpellData spelltableadv
---@field spellTable spelltable
---@field texture texture
---@field bIsMainLine boolean
---@class breakdowntargettablelist : breakdowntargettable[]
---@field totalValue number
---@field totalValueOverheal number
---@field combatTime number
---@class breakdowntargettable : table
---@field name string
---@field total number
---@field overheal number|nil
---@field absorbed number|nil
---@field statusBarValue number
---@class breakdownspelldatalist : spelltableadv[]
---@field totalValue number
---@field combatTime number
---@class breakdownspellstab : tabframe
---@field SpellScrollFrame breakdownspellscrollframe
---@field SpellBlockFrame breakdownspellblockframe
---@class breakdownspellblockframe : frame container for the spellblocks in the breakdown window
---@field SpellBlocks breakdownspellblock[] array of spellblocks
---@field blocksInUse number number of blocks currently in use
---@field UpdateBlocks fun(self: breakdownspellblockframe) update the blocks
---@field ClearBlocks fun(self: breakdownspellblockframe) clear all blocks
---@field GetBlock fun(self: breakdownspellblockframe, index: number) : breakdownspellblock return the block at the index
---@field GetBlocksInUse fun(self: breakdownspellblockframe) : number return the number of blocks currently in use
---@field GetBlocksAmount fun(self: breakdownspellblockframe) : number return the total blocks created
---@field ShowEmptyBlock fun(self: breakdownspellblockframe, index: number) show the empty block
---@class breakdownspellblock : statusbar breakdownspellblock object which is created inside the breakdownspellblockframe
---@field Lines breakdownspellblockline[]
---@field reportButton button
---@field overlay texture
---@field statusBarTexture texture
---@field sparkTexture texture
---@field gradientTexture texture
---@field backgroundTexture texture
---@field GetLine fun(self: breakdownspellblock, index: number) : breakdownspellblockline
---@field GetLines fun(self: breakdownspellblock) : breakdownspellblockline, breakdownspellblockline, breakdownspellblockline
---@field SetColor fun(self: breakdownspellblock, r: any, g: number|nil, b: number|nil, a: number|nil)
---@class breakdownspellblockline : frame a line inside a breakdownspellblock, there's 3 of them in each breakdownspellblock
---@field leftText fontstring
---@field centerText fontstring
---@field rightText fontstring
---@class breakdownspelltab
---@field selectedSpellBar breakdownspellbar
---@field TabFrame breakdownspellstab
---@field mainAttribute number
---@field subAttribute number
---@field TargetScrollFrame breakdowntargetscrollframe
---@field PhaseScrollFrame breakdownphasescrollframe
---@field GenericScrollFrameLeft breakdowngenericscrollframe
---@field GenericScrollFrameRight breakdowngenericscrollframe
---@field SpellContainerFrame df_framecontainer
---@field BlocksContainerFrame df_framecontainer
---@field TargetsContainerFrame df_framecontainer
---@field PhaseContainerFrame df_framecontainer
---@field GenericContainerFrameLeft df_framecontainer
---@field GenericContainerFrameRight df_framecontainer
---@field GetActor fun() : actor
---@field GetCombat fun() : combat
---@field GetInstance fun() : instance
---@field GetSpellScrollFrame fun() : breakdownspellscrollframe
---@field GetSpellBlockFrame fun() : breakdownspellblockframe
---@field GetTargetScrollFrame fun() : breakdowntargetscrollframe
---@field GetSpellScrollContainer fun() : df_framecontainer
---@field GetSpellBlockContainer fun() : df_framecontainer
---@field GetTargetScrollContainer fun() : df_framecontainer
---@field OnProfileChange fun()
---@field UpdateHeadersSettings fun(containerType: string)
---@field BuildHeaderTable fun(containerType: string) : {name: string, width: number, text: string, align: string}[]
---@field SelectSpellBar fun(spellBar: breakdownspellbar)
---@field UnSelectSpellBar fun()
---@field GetSelectedSpellBar fun() : breakdownspellbar
---@field HasSelectedSpellBar fun() : boolean
---@field OnShownTab fun()
---@field OnCreateTabCallback fun(tabButton: button, tabFrame: frame)
---@field CreateSpellBlock fun(spellBlockContainer: breakdownspellblockframe, index: number) : breakdownspellblock
---@field CreateSpellBlockContainer fun(tabFrame: tabframe) : breakdownspellblockframe
---@field UpdateShownSpellBlock fun()
---@field CreateTargetContainer fun(tabFrame: tabframe) : breakdowntargetscrollframe
---@field CreateGenericContainers fun(tabFrame: tabframe) : breakdowngenericscrollframe, breakdowngenericscrollframe
---@field CreateSpellScrollContainer fun(tabFrame: tabframe) : breakdownspellscrollframe
---@field CreateTargetBar fun(self: breakdowntargetscrollframe, index: number) : breakdowntargetbar
---@field CreateSpellBar fun(self: breakdownspellscrollframe, index: number) : breakdownspellbar
---@class timemachine : table
---@field Ticker fun() runs each second and check if actors are performing damage and healing actions, if the actor isn't, stop the activity time of that actor
---@field Start fun() start the time machine, called once from the start.lua
---@field Cleanup fun() check for actors with __destroyed flag and remove them from the time machine
---@field Restart fun() reset all data inside the time machine
---@field AddActor fun(actor: actor) add the actor to the time machine
---@field RemoveActor fun(actor: actor) remove the actor from the time machine
---@field StopTime fun(actor: actor) stop the time of the actor
---@field SetOrGetPauseState fun(actor: actor, bPause: boolean|nil) : boolean|nil set or get the pause state of the actor, if bPause is nil, then it will return the current pause state
---@class details222 : table
---@field TimeMachine timemachine
@@ -8,6 +8,10 @@
enUS.lua
ptBR.lua
frames.lua
Details_EncounterDetails.lua
Details_EncounterDetails.lua
frames_chart.lua
frames_emote.lua
frames_phases.lua
frames_summary.lua
frames.lua
File diff suppressed because it is too large Load Diff
@@ -1,4 +1,4 @@
## Interface: 100100
## Interface: 100105
## Title: Details!: Encounter Breakdown (plugin)
## Notes: Show detailed information about a boss encounter. Also provide damage per phase, graphic charts, easy weakauras creation.
## RequiredDeps: Details
@@ -10,6 +10,12 @@
enUS.lua
ptBR.lua
frames.lua
Details_EncounterDetails.lua
frames_chart.lua
frames_emote.lua
frames_phases.lua
frames_summary.lua
frames.lua
@@ -8,6 +8,10 @@
enUS.lua
ptBR.lua
frames.lua
Details_EncounterDetails.lua
Details_EncounterDetails.lua
frames_chart.lua
frames_emote.lua
frames_phases.lua
frames_summary.lua
frames.lua
@@ -0,0 +1,8 @@
---@class ed_barline : button
---@field statusBar statusbar
---@field lineText1 fontstring
---@field lineText4 fontstring
---@field statusBarTexture texture
---@field Icon texture
@@ -0,0 +1,11 @@
background.blp
border_1.blp
border_2.blp
border_3.blp
cooltip_background.blp
feedback_sites.blp
icons.blp
mail.blp
*.json
CHANGES.txt
@@ -0,0 +1,66 @@
DFPixelUtil = {};
function DFPixelUtil.GetPixelToUIUnitFactor()
local physicalWidth, physicalHeight = GetPhysicalScreenSize();
return 768.0 / physicalHeight;
end
function DFPixelUtil.GetNearestPixelSize(uiUnitSize, layoutScale, minPixels)
if uiUnitSize == 0 and (not minPixels or minPixels == 0) then
return 0;
end
local uiUnitFactor = DFPixelUtil.GetPixelToUIUnitFactor();
local numPixels = Round((uiUnitSize * layoutScale) / uiUnitFactor);
local rawNumPixels = numPixels;
if minPixels then
if uiUnitSize < 0.0 then
if numPixels > -minPixels then
numPixels = -minPixels;
end
else
if numPixels < minPixels then
numPixels = minPixels;
end
end
end
return numPixels * uiUnitFactor / layoutScale;
end
function DFPixelUtil.SetWidth(region, width, minPixels)
region:SetWidth(DFPixelUtil.GetNearestPixelSize(width, region:GetEffectiveScale(), minPixels));
end
function DFPixelUtil.SetHeight(region, height, minPixels)
region:SetHeight(DFPixelUtil.GetNearestPixelSize(height, region:GetEffectiveScale(), minPixels));
end
function DFPixelUtil.SetSize(region, width, height, minWidthPixels, minHeightPixels)
DFPixelUtil.SetWidth(region, width, minWidthPixels);
DFPixelUtil.SetHeight(region, height, minHeightPixels);
end
function DFPixelUtil.SetPoint(region, point, relativeTo, relativePoint, offsetX, offsetY, minOffsetXPixels, minOffsetYPixels)
region:SetPoint(point, relativeTo, relativePoint,
DFPixelUtil.GetNearestPixelSize(offsetX, region:GetEffectiveScale(), minOffsetXPixels),
DFPixelUtil.GetNearestPixelSize(offsetY, region:GetEffectiveScale(), minOffsetYPixels)
);
end
function DFPixelUtil.SetStatusBarValue(statusBar, value)
local width = statusBar:GetWidth();
if width and width > 0.0 then
local min, max = statusBar:GetMinMaxValues();
local percent = ClampedPercentageBetween(value, min, max);
if percent == 0.0 or percent == 1.0 then
statusBar:SetValue(value);
else
local numPixels = DFPixelUtil.GetNearestPixelSize(statusBar:GetWidth() * percent, statusBar:GetEffectiveScale());
local roundedValue = Lerp(min, max, numPixels / width);
statusBar:SetValue(roundedValue);
end
else
statusBar:SetValue(value);
end
end
@@ -0,0 +1,5 @@
## Interface: 100100
## Title: Lib: LibDFramework-1.0
## Notes: Base Framework for many Addons
load.xml
@@ -0,0 +1,132 @@
local DF = _G ["DetailsFramework"]
local _
if (not DF or not DetailsFrameworkCanLoad) then
return
end
--runs when the addon received addon_loaded
local addonPreLoad = function(addonFrame, event, ...)
--check if the saved variables table is created, if not create one
_G[addonFrame.__savedVarsName] = _G[addonFrame.__savedVarsName] or {}
if (addonFrame.__savedVarsDefaultTemplate) then
--load saved vars for this character
DF.SavedVars.LoadSavedVarsForPlayer(addonFrame)
end
if (addonFrame.OnLoad) then
DF:Dispatch(addonFrame.OnLoad, addonFrame, ...)
end
end
--runs when the addon received player_login
local addonInit = function(addonFrame, event, ...)
if (addonFrame.OnInit) then
DF:Dispatch(addonFrame.OnInit, addonFrame, ...)
end
end
--when the player logout or reloadUI
local addonUnload = function(addonFrame, event, ...)
--close saved tables
DF.SavedVars.CloseSavedTable(addonFrame.db)
end
local addonEvents = {
["ADDON_LOADED"] = addonPreLoad,
["PLAYER_LOGIN"] = addonInit,
["PLAYER_LOGOUT"] = addonUnload,
}
local addonOnEvent = function(addonFrame, event, ...)
local func = addonEvents[event]
if (func) then
func(addonFrame, event, ...)
else
--might be a registered event from the user
if (addonFrame[event]) then
DF:CoreDispatch(addonFrame.__name, addonFrame[event], addonFrame, event, ...)
end
end
end
local getAddonName = function(addonFrame)
return addonFrame:GetName()
end
function DF:CreateNewAddOn(addonName, globalSavedVariablesName, savedVarsTemplate)
local newAddon = CreateFrame("frame", addonName, UIParent)
newAddon.__name = addonName
newAddon.__savedVarsName = globalSavedVariablesName
newAddon.__savedVarsDefaultTemplate = savedVarsTemplate
newAddon.GetAddonName = getAddonName
newAddon:RegisterEvent("ADDON_LOADED")
newAddon:RegisterEvent("PLAYER_LOGIN")
newAddon:RegisterEvent("PLAYER_LOGOUT")
newAddon:SetScript("OnEvent", addonOnEvent)
return newAddon
end
--old create addon
function DF:CreateAddOn (name, global_saved, global_table, options_table, broker)
local addon = LibStub("AceAddon-3.0"):NewAddon (name, "AceConsole-3.0", "AceEvent-3.0", "AceTimer-3.0", "DetailsFramework-1.0", "AceComm-3.0")
_G [name] = addon
addon.__name = name
function addon:OnInitialize()
if (global_saved) then
if (broker and broker.Minimap and not global_table.Minimap) then
DF:Msg(name, "broker.Minimap is true but no global.Minimap declared.")
end
self.db = LibStub("AceDB-3.0"):New (global_saved, global_table or {}, true)
end
if (options_table) then
LibStub("AceConfig-3.0"):RegisterOptionsTable (name, options_table)
addon.OptionsFrame1 = LibStub("AceConfigDialog-3.0"):AddToBlizOptions (name, name)
LibStub("AceConfig-3.0"):RegisterOptionsTable (name .. "-Profiles", LibStub("AceDBOptions-3.0"):GetOptionsTable (self.db))
addon.OptionsFrame2 = LibStub("AceConfigDialog-3.0"):AddToBlizOptions (name .. "-Profiles", "Profiles", name)
end
if (broker) then
local broker_click_function = broker.OnClick
if (not broker_click_function and options_table) then
broker_click_function = function()
InterfaceOptionsFrame_OpenToCategory (name)
InterfaceOptionsFrame_OpenToCategory (name)
end
end
local databroker = LibStub("LibDataBroker-1.1"):NewDataObject (name, {
type = broker.type or "launcher",
icon = broker.icon or [[Interface\PvPRankBadges\PvPRank15]],
text = broker.text or "",
OnTooltipShow = broker.OnTooltipShow,
OnClick = broker_click_function
})
if (databroker and broker.Minimap and global_table.Minimap) then
LibStub("LibDBIcon-1.0"):Register (name, databroker, addon.db.profile.Minimap)
end
end
if (addon.OnInit) then
xpcall(addon.OnInit, geterrorhandler(), addon)
end
end
return addon
end
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,3 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ .. \FrameXML\UI.xsd">
<Script file="button.lua"/>
</Ui>
@@ -0,0 +1,109 @@
--documentation: see the header of the file charts.lua
--1º example: making a simple chart, just copy and paste this code into a lua file and run it
do
local ChartFrameTest = ChartFrameExample1 or DetailsFramework:CreateGraphicLineFrame(UIParent, "ChartFrameExample1")
ChartFrameTest:SetPoint("left", UIParent, "left", 10, 0) --set the position of the chart
ChartFrameTest:SetSize(800, 600) --set the size of the chart
DetailsFramework:ApplyStandardBackdrop(ChartFrameTest) --apply a backdrop to this example hence see the frame size
--set the data (required)
local data = {1, 2, 30, 25, 6, 5, 4, 8, 7, 4, 1, 12, 15, 24, 18, 17, 14, 15, 8, 4, 14, 42, 22, 25, 30, 35, 39, 8, 7, 4, 1, 2, 5, 4, 8, 7, 4, 12, 12, 4}
local smoothnessLevel = 1 --(optional, default: 1)
ChartFrameTest:SetData(data, smoothnessLevel)
--draw the chart
ChartFrameTest:Plot()
end
--2º example: setting the color, thickness and scale of the line:
do
local ChartFrameTest = ChartFrameExample2 or DetailsFramework:CreateGraphicLineFrame(UIParent, "ChartFrameExample2")
ChartFrameTest:SetPoint("left", UIParent, "left", 10, 0) --set the position of the chart
ChartFrameTest:SetSize(800, 600) --set the size of the chart
DetailsFramework:ApplyStandardBackdrop(ChartFrameTest) --apply a backdrop to this example hence see the frame size
--set the line thickness (optional, default: 2)
local lineThickness = 3
ChartFrameTest:SetLineThickness(lineThickness)
--set the chart color (optional, default: "white")
local lineColor = {r = 1, g = 1, b = 0} --set it to "yellow"
ChartFrameTest:SetColor(lineColor) --using {r = 1, g = 1, b = 0}
ChartFrameTest:SetColor("yellow") --using the color name
ChartFrameTest:SetColor(1, 1, 0) --passing the rgb directly
ChartFrameTest:SetColor({1, 1, 0}) --using an index table
--set the data (required)
local data = {1, 2, 30, 25, 6, 5, 4, 8, 7, 4, 1, 12, 15, 24 ,18, 17 ,14, 15, 8 , 4, 14, 42, 22, 25, 30, 35, 39, 8, 7, 4, 1, 2, 5, 4 ,8, 7 ,4, 12, 12 , 4}
local smoothnessLevel = 1 --(optional, default: 1)
ChartFrameTest:SetData(data, smoothnessLevel)
--height modifier, if for some reason need to scale the chart height
local heightScale = 1 --(optional, default: 1)
--draw the chart
ChartFrameTest:Plot(heightScale)
end
--3º example: setting the axes lines and labels
do
local ChartFrameTest = ChartFrameExample3 or DetailsFramework:CreateGraphicLineFrame(UIParent, "ChartFrameExample3")
ChartFrameTest:SetPoint("left", UIParent, "left", 10, 0)
ChartFrameTest:SetSize(800, 600)
DetailsFramework:ApplyStandardBackdrop(ChartFrameTest)
--create guide lines in the left and bottom of the chart
local xOffset = 48 --pixels from the left border of the chart
local yOffset = 28 --pixels from the bottom border of the chart
local whichSide = "left" --which side of vertical line should be placed
local thickness = 1
local amountYLabels = 10 --amounf of texts indicating the scale of the chart
local amountXLabels = 10
local r, g, b, a = 1, 1, 1, 1
ChartFrameTest:CreateAxesLines(xOffset, yOffset, whichSide, thickness, amountYLabels, amountXLabels, r, g, b, a)
--the labels in the bottom line can be 'time', 'number' or 'value'
ChartFrameTest:SetXAxisDataType("time")
--set the data to be used in the bottom line labels, how the data is formatted depends on the type set above
ChartFrameTest:SetXAxisData(10) --with type 'time' the chart interprets this as seconds and shows 1:00 to 10:00
ChartFrameTest:SetXAxisDataType("number")
ChartFrameTest:SetXAxisData(600) --the chart interprets this as a 'number' type and displays it as 60, 120, 180.
ChartFrameTest:SetXAxisDataType("value")
ChartFrameTest:SetXAxisData("hello", "world", 1, 2, 3, 4, "chart", 0, 1, 0) --and 'value' show the values passed
--setting the data, doesn't matter if it is set at the top or right before Plot()
local data = {1, 2, 30, 25, 6, 5, 4, 8, 7, 4, 1, 12, 15, 24 ,18, 17 ,14, 15, 8 , 4, 14, 42, 22, 25, 30, 35, 39, 8, 7, 4, 1, 2, 5, 4 ,8, 7 ,4, 12, 12 , 4}
ChartFrameTest:SetData(data) --smoothnessLevel is absent here, it'll use 1 as default
ChartFrameTest:Plot()
end
--4º example: a multi line chart is a chart which supports multiple lines, each line can have a different color, name, smoothnessLevel and thickness
do
local ChartFrameTest = ChartFrameExample4 or DetailsFramework:CreateGraphicMultiLineFrame(UIParent, "ChartFrameExample4")
ChartFrameTest:SetPoint("left", UIParent, "left", 10, 0)
ChartFrameTest:SetSize(800, 600)
DetailsFramework:ApplyStandardBackdrop(ChartFrameTest)
--when using multi-line, the Reset() function instructs the chart to discard the previous data as new data is about to be added
ChartFrameTest:Reset()
--smoothnessLevel, name, red, green, blue, alpha
local smoothnessLevel = 2 --(optional, default: 0)
local line1Name, line2Name, line3Name = "Line 1", "Line 2", "Line 3" --show the line name at the top right corner (optional, default none)
local line1Color, line2Color, line3Color = "lime", "purple", "orange" --(optional, default "white")
--add data into the chart (it plots a line for each data added when :Plot() is called)
local data1 = {1, 2, 30, 25, 6, 5, 4, 8, 7, 4, 1, 12, 15, 24 ,18, 17 ,14, 15, 8 , 4, 14, 42, 22, 25, 30, 35, 39, 8, 7, 4, 1, 2, 5, 4}
ChartFrameTest:AddData(data1, smoothnessLevel, line1Name, line1Color)
local data2 = {3, 5, 20, 25, 6, 5, 15, 18, 12, 14, 11, 8, 7, 8 ,7, 4 ,1, 25, 26 , 30, 28, 20, 22, 25, 20, 15, 10, 8, 7, 4, 1, 2, 5, 4}
ChartFrameTest:AddData(data2, smoothnessLevel, line2Name, line2Color)
local data3 = {5, 7, 15, 30, 6, 2, 10, 13, 10, 5, 11, 8, 7, 5, 3, 1, 1, 8, 10 , 12, 15, 20, 25, 25, 20, 17, 12, 7, 7, 6, 4, 5, 6, 5}
ChartFrameTest:AddData(data3, smoothnessLevel, line3Name, line3Color)
ChartFrameTest:Plot()
end
@@ -0,0 +1,884 @@
local detailsFramework = _G["DetailsFramework"]
if (not detailsFramework or not DetailsFrameworkCanLoad) then
return
end
local CreateFrame = CreateFrame
local unpack = unpack
local wipe = table.wipe
local _
---@class chart_guideline : fontstring
---@field circleTexture texture
---@field guideLine line
---@class chart_nameindicator : frame
---@field Texture texture
---@field Label fontstring
---@alias x_axisdatatype
---| "time" when setting the text into the labels, it will be converted into a time format
---| "number" same as timer, but the number is not comverted to time
---| "value" a fixed table with values is passed by the SetXAxisData() function
---@class df_chartshared: table
---@field yAxisLine line the vertical line which can be anchored in the left or right side of the frame, if the chart is a multi chart, this line is shared by all charts
---@field xAxisLine line the horizontal line which can be anchored in the top or bottom side of the frame, if the chart is a multi chart, this line is shared by all charts
---@field xAxisDataNumber any if the data type of the x axis is "number" or "time"
---@field xAxisDataValues table if the data type of the x axis is "value"
---@field xAxisDataType x_axisdatatype the data type of the x axis, if time, the x axis will be a time axis, if value, the x axis will be a value axis
---@field yAxisLabels chart_guideline[] the vertical axis labels to indicate the values of the chart data
---@field xAxisLabels fontstring[] the horizontal axis labels to indicate the values of the chart data
---@field plotFrame frame the plot frame which is the frame that will hold the chart lines
---@field lineThickness number the thickness of the chart lines
---@field chartLeftOffset number the offset of the left side of the chart frame to the plot frame
---@field chartBottomOffset number the offset of the bottom side of the chart frame to the plot frame
---@field xAxisLabelsYOffset number default: -6, the offset of the horizontal axis labels to the horizontal axis line (y coordinate)
---@field smoothnessLevel number default: 0, the smoothness level of the chart lines, 0 is no smoothness
---@field SetAxesColor fun(self: df_chartmulti, red: number|string|table|nil, green: number|nil, blue: number|nil, alpha: number|nil) : boolean set the color of both axis lines
---@field SetAxesThickness fun(self: df_chartmulti, thickness: number) : boolean set the thickness of both axis lines
---@field CreateAxesLines fun(self: df_chartmulti|df_chart, xOffset: number, yOffset: number, whichSide: "left"|"right", thickness: number, amountYLabels: number, amountXLabels: number, red: any, green: number|nil, blue: number|nil, alpha: number|nil)
---@field SetXAxisDataType fun(self: df_chartmulti|df_chart, dataType: x_axisdatatype) : boolean set the data type of the x axis, if time, the x axis will be a time axis, if value, the x axis will be a value axis
---@field SetXAxisData fun(self: df_chartmulti|df_chart, data: any) set the data of the x axis, if time, the x axis will be a time axis, if value, the x axis will be a value axis
---@field SharedContrustor fun(self: df_chartmulti|df_chart) set default values for fields used on both chart types
---@field IsMultiChart fun(self: df_chartmulti|df_chart) : boolean return true if the chart is a multi chart
---@param self df_chart|df_chartmulti
local chartFrameSharedConstructor = function(self)
self.xAxisDataType = "number"
self.lineThickness = 2
self.xAxisDataNumber = 0
self.xAxisDataValues = {}
self.xAxisLabels = {}
self.yAxisLabels = {}
self.chartLeftOffset = 0
self.chartBottomOffset = 0
self.xAxisLabelsYOffset = -6
self.smoothnessLevel = 0
end
---@class df_chart: frame, df_data, df_value, df_chartshared
---@field _dataInfo df_data
---@field color number[] red green blue alpha
---@field height number
---@field nextLine number
---@field minValue number
---@field maxValue number
---@field data number[]
---@field lines line[]
---@field fixedLineWidth number
---@field chartName string
---@field ChartFrameConstructor fun(self: df_chart) set the default values for the chart frame
---@field GetLine fun(self: df_chart) : line return a line and also internally handle next line
---@field GetLines fun(self: df_chart) : line[] return a table with all lines already created
---@field GetLineWidth fun(self: df_chart) : number calculate the width of each drawn line
---@field SetLineWidth fun(self: df_chart, width: number) set the line width to a fixed value
---@field GetAmountLines fun(self: df_chart) : number return the amount of lines in use
---@field OnSizeChanged fun(self: df_chart)
---@field HideLines fun(self: df_chart) hide all lines already created
---@field Reset fun(self: df_chart) hide all lines and reset the next line to 1
---@field SetColor fun(self: df_chart, r: number|string|table|nil, g: number|nil, b: number|nil, a: number|nil) set the color for the lines
---@field GetColor fun(self: df_chart) : red, green, blue, alpha
---@field SetLineThickness fun(self: df_chart, thickness: number) set the line thickness
---@field CalcYAxisPointForValue fun(self: df_chart, value: number, plotFrameHeightScaled: number) : number
---@field UpdateFrameSizeCache fun(self: df_chart)
---@field Plot fun(self: df_chart, yPointScale: number|nil, bUpdateLabels: boolean|nil) draw the graphic using lines and following the data set by SetData() or AddData() in multi chart
---@class df_chartmulti : df_chart, df_chartshared
---@field chartFrames df_chart[]
---@field nextChartselframe number
---@field biggestDataValue number
---@field nextChartFrame number
---@field lineNameIndicators chart_nameindicator[]
---@field MultiChartFrameConstructor fun(self: df_chartmulti)
---@field GetCharts fun(self: df_chartmulti) : df_chart[]
---@field GetChart fun(self: df_chartmulti) : df_chart
---@field AddData fun(self: df_chartmulti, data: table, name: string, red: any, green: number|nil, blue: number|nil, alpha: number|nil)
---@field GetAmountCharts fun(self: df_chartmulti): number
---@field HideCharts fun(self: df_chartmulti)
---@field Reset fun(self: df_chartmulti)
---@field SetChartsMinMaxValues fun(self: df_chartmulti, minValue: number, maxValue: number)
---@field SetMaxDataSize fun(self: df_chartmulti, dataSize: number)
---@field GetMaxDataSize fun(self: df_chartmulti)
---@field SetLineThickness fun(self: df_chart, thickness: number) set the line thickness for all chart frames
---@field UpdateChartNamesIndicator fun(self: df_chartmulti) if the chart names has been passed while adding data, this function will update the chart names indicator
---@field Plot fun(self: df_chartmulti) draw the graphic using lines and following the data set by SetData() or AddData() in multi chart
---create the plot frame which is the frame that will hold the chart lines
---@param self df_chartmulti|df_chart
---@return frame
local createPlotFrame = function(self)
local plotFrame = CreateFrame("frame", "$parentPlotFrame", self, "BackdropTemplate")
plotFrame:SetAllPoints()
self.plotFrame = plotFrame
return plotFrame
end
---generate the vertical axis labels to indicate the values of the chart data
---@param parent frame
---@param amountLabels number
---@param labelsTable chart_guideline[]
---@param red number
---@param green number
---@param blue number
---@param alpha number
local createVerticalAxisLabels = function(parent, amountLabels, labelsTable, red, green, blue, alpha)
for i = 1, amountLabels do
---@type fontstring
local label = parent:CreateFontString("$parentYAxisLabel" .. i, "overlay", "GameFontNormal")
---@cast label chart_guideline
label:SetJustifyH("right")
label:SetTextColor(red, green, blue, alpha)
detailsFramework:SetFontSize(label, 11)
table.insert(labelsTable, label)
local circleTexture = parent:CreateTexture("$parentYAxisLabel" .. i .. "CircleTexture", "border")
circleTexture:SetSize(4, 4)
circleTexture:SetTexture([[Interface\CHARACTERFRAME\TempPortraitAlphaMaskSmall]])
circleTexture:SetVertexColor(red, green, blue, alpha)
circleTexture:SetPoint("right", label, "right", 5, 0)
local guideLine = parent:CreateLine("$parentYAxisLabel" .. i .. "GuideLine", "border")
guideLine:SetThickness(1)
guideLine:SetColorTexture(red, green, blue, 0.05)
label.circleTexture = circleTexture
label.guideLine = guideLine
end
end
---generate the horizontal axis labels to indicate the values of the chart data
---@param parent frame
---@param amountLabels number
---@param labelsTable fontstring[]
---@param red number
---@param green number
---@param blue number
---@param alpha number
local createHorizontalAxisLabels = function(parent, amountLabels, labelsTable, red, green, blue, alpha)
for i = 1, amountLabels do
local label = parent:CreateFontString("$parentXAxisLabel" .. i, "overlay", "GameFontNormal")
label:SetJustifyH("left")
label:SetTextColor(red, green, blue, alpha)
detailsFramework:SetFontSize(label, 11)
table.insert(labelsTable, label)
end
end
---create the x and y axis lines with their labels
---@param self df_chart|df_chartmulti
---@param xOffset number
---@param yOffset number
---@param whichSide "left"|"right"
---@param thickness number
---@param amountYLabels number
---@param amountXLabels number
---@param red any
---@param green number|nil
---@param blue number|nil
---@param alpha number|nil
---@return boolean
local createAxesLines = function(self, xOffset, yOffset, whichSide, thickness, amountYLabels, amountXLabels, red, green, blue, alpha)
if (self.axisCreated) then
return false
end
local plotFrame = self.plotFrame
self.chartLeftOffset = xOffset or 48
self.chartBottomOffset = yOffset or 28
whichSide = whichSide or "left"
thickness = thickness or 1
amountYLabels = amountYLabels or 10
amountXLabels = amountXLabels or 10
red = red or 1
green = green or 1
blue = blue or 1
alpha = alpha or 1
--adjust the plotFrame size and point taking in consideration of the left and bottom offsets, this is done to free space for the axis labels
plotFrame:SetSize(self:GetWidth() - self.chartLeftOffset - 10, self:GetHeight() - self.chartBottomOffset - 20)
plotFrame:ClearAllPoints()
plotFrame:SetPoint("topleft", self, "topleft", self.chartLeftOffset, -1)
plotFrame:SetPoint("bottomright", self, "bottomright", -1, self.chartBottomOffset)
--this is the vertical line which can be anchored in the left or right side of the frame, it separates the chart lines from the labels
---@type line
local yAxisLine = plotFrame:CreateLine("$parentYAxisLine", "overlay")
self.yAxisLine = yAxisLine
--and the horizontal line which is always anchored in the bottom of the frame
---@type line
local xAxisLine = plotFrame:CreateLine("$parentXAxisLine", "overlay")
self.xAxisLine = xAxisLine
--vertical axis point
if (whichSide == "left") then
yAxisLine:SetStartPoint("topleft", plotFrame, 0, -1)
yAxisLine:SetEndPoint("bottomleft", plotFrame, 0, self.chartBottomOffset * -1)
else
yAxisLine:SetStartPoint("topright", plotFrame, 0, -1)
yAxisLine:SetEndPoint("bottomleft", plotFrame, 0, self.chartBottomOffset)
end
--horizontal axis point
xAxisLine:SetStartPoint("bottomleft", plotFrame, self.chartLeftOffset * -1, 0)
xAxisLine:SetEndPoint("bottomright", plotFrame, -1, 0)
red, green, blue, alpha = detailsFramework:ParseColors(red, green, blue, alpha)
self:SetAxesColor(red, green, blue, alpha)
--set the thickness of the both axis lines
self:SetAxesThickness(thickness)
createVerticalAxisLabels(plotFrame, amountYLabels, self.yAxisLabels, red, green, blue, alpha)
createHorizontalAxisLabels(plotFrame, amountXLabels, self.xAxisLabels, red, green, blue, alpha)
self.axisCreated = true
return true
end
---@param self df_chartmulti|df_chart
---@param ... any
local setXAxisData = function(self, ...)
--when the data type is set to time, the x axis data is a number which represents the biggest time in seconds of all charts added
if (self.xAxisDataType == "time" or self.xAxisDataType == "number") then
self.xAxisDataNumber = math.max(self.xAxisDataNumber, select(1, ...))
else
wipe(self.xAxisDataValues)
self.xAxisDataValues = {...}
end
end
---@param self df_chartmulti|df_chart
---@param dataType x_axisdatatype
local setXAxisDataType = function(self, dataType)
assert(type(dataType) == "string", "string expected on :SetXAxisDataType(string)")
self.xAxisDataType = dataType
if (dataType == "time" or dataType == "number") then
self.xAxisDataNumber = 0
elseif (dataType == "value") then
wipe(self.xAxisDataValues)
end
end
---updates the values of the labels on the axes to reflect the data shown
---@param self df_chart|df_chartmulti
local updateLabelValues = function(self)
local maxValue = self:GetMaxValue()
local height = self.plotFrame:GetHeight()
local verticalLabelCount = #self.yAxisLabels
local heightStep = height / verticalLabelCount
--update the labels in the vertical axis line
for i = 1, verticalLabelCount do
local label = self.yAxisLabels[i]
local value = maxValue * (i / verticalLabelCount)
label:ClearAllPoints()
label:SetPoint("topright", self.yAxisLine, "bottomleft", -6, heightStep * i + self.chartBottomOffset)
label:SetText(detailsFramework.FormatNumber(value))
label.circleTexture:ClearAllPoints()
label.circleTexture:SetPoint("center", self.yAxisLine, "bottomleft", -2, heightStep * i - 5 + self.chartBottomOffset)
label.guideLine:SetStartPoint("center", label.circleTexture, 0, 0)
label.guideLine:SetEndPoint("bottomright", self.plotFrame, 0, heightStep * i - 5)
end
--update the labels in the horizontal axis line
local xAxisDataType = self.xAxisDataType
local horizontalLabelCount = #self.xAxisLabels
local width = self.plotFrame:GetWidth()
local widthStep = width / horizontalLabelCount
for i = horizontalLabelCount, 1, -1 do
local label = self.xAxisLabels[i]
label:ClearAllPoints()
label:SetJustifyH("right")
--set the point of each x axis label
label:SetPoint("topright", self.plotFrame, "bottomleft", widthStep * i, self.xAxisLabelsYOffset or -6)
--get the type set for the x axis labels and format the value accordingly
if (xAxisDataType == "time" or xAxisDataType == "number") then
local maxNumberValue = self.xAxisDataNumber
local thisValue = maxNumberValue * (i / horizontalLabelCount)
if (xAxisDataType == "time") then
label:SetText(detailsFramework:IntegerToTimer(thisValue))
else
label:SetText(detailsFramework.FormatNumber(thisValue))
end
elseif (xAxisDataType == "value") then
label:SetText(self.xAxisDataValues[i])
end
end
end
detailsFramework.ChartFrameSharedMixin = {
---set the color of both axis lines
---@param self df_chart|df_chartmulti
---@param red any
---@param green number|nil
---@param blue number|nil
---@param alpha number|nil
---@return boolean bColorChanged return true if the color was set, false if the axis lines are not created yet
SetAxesColor = function(self, red, green, blue, alpha)
if (not self.yAxisLine) then
return false
end
--set the color of both axis lines
red, green, blue, alpha = detailsFramework:ParseColors(red, green, blue, alpha)
self.yAxisLine:SetColorTexture(red, green, blue, alpha)
self.xAxisLine:SetColorTexture(red, green, blue, alpha)
--iterage over all labels and set their color
for i = 1, #self.yAxisLabels do
self.yAxisLabels[i]:SetTextColor(red, green, blue, alpha)
end
for i = 1, #self.xAxisLabels do
self.xAxisLabels[i]:SetTextColor(red, green, blue, alpha)
end
return true
end,
---set the thickness of both axis lines
---@param self df_chart|df_chartmulti
---@param thickness number
---@return boolean bThicknessChanged return true if the thickness was set, false if the axis lines are not created yet
SetAxesThickness = function(self, thickness)
if (not self.yAxisLine) then
return false
end
self.yAxisLine:SetThickness(thickness)
self.xAxisLine:SetThickness(thickness)
return true
end,
---create the x and y axis lines with their labels
---@param self df_chart|df_chartmulti
---@param xOffset number
---@param yOffset number
---@param whichSide "left"|"right"
---@param thickness number
---@param amountYLabels number
---@param amountXLabels number
---@param red any
---@param green number|nil
---@param blue number|nil
---@param alpha number|nil
---@return boolean
CreateAxesLines = function(self, xOffset, yOffset, whichSide, thickness, amountYLabels, amountXLabels, red, green, blue, alpha)
return createAxesLines(self, xOffset, yOffset, whichSide, thickness, amountYLabels, amountXLabels, red, green, blue, alpha)
end,
---@param self df_chartmulti|df_chart
---@param ... any
SetXAxisData = function(self, ...)
setXAxisData(self, ...)
end,
---@param self df_chartmulti|df_chart
---@param dataType x_axisdatatype
SetXAxisDataType = function(self, dataType)
setXAxisDataType(self, dataType)
end,
}
detailsFramework.ChartFrameMixin = {
---set the default values for the chart frame
---@param self df_chart
ChartFrameConstructor = function(self)
self.nextLine = 1
self.minValue = 0
self.maxValue = 1
self.lineThickness = 2
self.data = {}
self.lines = {}
self.color = {1, 1, 1, 1}
--OnSizeChanged
self:SetScript("OnSizeChanged", self.OnSizeChanged)
chartFrameSharedConstructor(self)
end,
IsMultiChart = function(self)
return false
end,
---get the chart color
---@param self df_chart
---@return number red
---@return number green
---@return number blue
---@return number alpha
GetColor = function(self)
return unpack(self.color)
end,
---set the color for the lines
---@param self df_chart
---@param r number
---@param g number
---@param b number
---@param a number|nil
SetColor = function(self, r, g, b, a)
r, g, b, a = detailsFramework:ParseColors(r, g, b, a)
self.color[1] = r
self.color[2] = g
self.color[3] = b
self.color[4] = a or 1
end,
---internally handle next line
---@param self df_chart
GetLine = function(self)
---@type line
local line = self.lines[self.nextLine]
if (not line) then
---@type line
line = self.plotFrame:CreateLine(nil, "overlay", nil, 5)
self.lines[self.nextLine] = line
end
self.nextLine = self.nextLine + 1
line:Show()
return line
end,
---return all lines created for this chart
---@param self df_chart
---@return line[]
GetLines = function(self)
return self.lines
end,
---return the amount of lines in use
---@param self df_chart
---@return number
GetAmountLines = function(self)
return self.nextLine - 1
end,
---hide all lines already created
---@param self df_chart
HideLines = function(self)
local allLines = self:GetLines()
for i = 1, #allLines do
local line = allLines[i]
line:Hide()
end
end,
---hide all lines and reset the next line to 1
---@param self df_chart
Reset = function(self)
self:HideLines()
self.nextLine = 1
end,
---@param self df_chart
---@param value number
SetLineThickness = function(self, value)
assert(type(value) == "number", "number expected on :SetLineThickness(number)")
self.lineThickness = value
end,
---calculate the width of each drawn line
---@param self df_chart
GetLineWidth = function(self)
--self:SetLineWidth(nil) to erase the fixed value
if (self.fixedLineWidth) then
return self.fixedLineWidth
else
local amountData = self:GetDataSize()
local frameWidth = self.plotFrame:GetWidth()
return frameWidth / amountData
end
end,
---set the line width to a fixed value
---@param self df_chart
---@param width number
SetLineWidth = function(self, width)
assert(type(width) == "number", "number expected on :SetLineWidth(number)")
self.fixedLineWidth = width
end,
---@param self df_chart
---@param value number
---@param plotFrameHeightScaled number
---@return number
CalcYAxisPointForValue = function(self, value, plotFrameHeightScaled)
return value / self.maxValue * (plotFrameHeightScaled)
end,
---@param self df_chart
UpdateFrameSizeCache = function(self)
self.width = self:GetWidth()
self.height = self:GetHeight()
end,
---@param self df_chart
OnSizeChanged = function(self)
self:UpdateFrameSizeCache()
end,
---@param self df_chart
---@param yPointScale number|nil
---@param bUpdateLabels boolean|nil
Plot = function(self, yPointScale, bUpdateLabels)
--debug
--self:SetData({38, 26, 12, 63, 100, 96, 42, 94, 25, 75, 61, 54, 71, 40, 34, 100, 66, 90, 39, 13, 99, 18, 72, 18, 83, 45, 56, 24, 33, 85, 95, 71, 15, 66, 19, 58, 52, 9, 83, 99, 100, 4, 3, 56, 6, 80, 94, 7, 40, 55, 98, 92, 20, 9, 35, 89, 72, 7, 13, 81, 29, 78, 55, 70, 12, 33, 39, 3, 84, 31, 10, 53, 51, 69, 66, 58, 71, 60, 31, 71, 27, 76, 21, 75, 15, 89, 2, 81, 72, 78, 74, 80, 97, 10, 59, 0, 31, 5, 1, 82, 71, 89, 78, 94, 74, 20, 65, 72, 56, 40, 92, 91, 40, 79, 4, 56, 18, 88, 88, 20, 20, 10, 47, 26, 80, 26, 75, 21, 57, 10, 67, 66, 84, 83, 14, 47, 83, 9, 7, 73, 63, 32, 64, 20, 40, 3, 46, 54, 17, 37, 82, 66, 65, 22, 12, 1, 100, 41, 1, 72, 38, 41, 71, 69, 88, 34, 10, 50, 9, 25, 19, 27, 3, 13, 40, 75, 3, 11, 93, 58, 81, 80, 93, 25, 74, 68, 91, 87, 79, 48, 66, 53, 64, 18, 51, 19, 32, 4, 21, 43})
self:UpdateFrameSizeCache()
--max amount of data is the max amount of point the chart will have
local maxLines = self:GetDataSize()
--calculate where the first point height will be
local firstValue = self:GetDataFirstValue()
assert(firstValue, "Can't Plot(), chart has no data, use Chart:SetData(table)")
local plotFrameHeightScaled = self.plotFrame:GetHeight() * (yPointScale or 1)
local currentXPoint = 0
local currentYPoint = self:CalcYAxisPointForValue(firstValue, plotFrameHeightScaled)
--calculate the width space which line should have
local eachLineWidth = self:GetLineWidth()
self:ResetDataIndex()
for i = 1, maxLines do
local line = self:GetLine()
line:SetColorTexture(unpack(self.color))
if (line.thickness ~= self.lineThickness) then
line:SetThickness(self.lineThickness)
line.thickness = self.lineThickness
end
--the start point starts where the latest point finished
line:SetStartPoint("bottomleft", currentXPoint, currentYPoint)
--move x
currentXPoint = currentXPoint + eachLineWidth
--end point
local value = self:GetDataNextValue()
currentYPoint = self:CalcYAxisPointForValue(value, plotFrameHeightScaled)
line:SetEndPoint("bottomleft", currentXPoint, currentYPoint)
end
if (bUpdateLabels or bUpdateLabels == nil) then
updateLabelValues(self)
end
end,
}
---create a chart frame object
---@param parent frame
---@param name string|nil
---@return df_chart
local createChartFrame = function(parent, name)
---@type df_chart
local chartFrame = CreateFrame("frame", name, parent, "BackdropTemplate")
detailsFramework:Mixin(chartFrame, detailsFramework.DataMixin)
detailsFramework:Mixin(chartFrame, detailsFramework.ValueMixin)
detailsFramework:Mixin(chartFrame, detailsFramework.ChartFrameMixin)
detailsFramework:Mixin(chartFrame, detailsFramework.ChartFrameSharedMixin)
chartFrame:DataConstructor()
chartFrame:ValueConstructor()
chartFrame:ChartFrameConstructor()
--when a new data is set, update the min and max values
local onSetDataCallback = function(data, smoothnessLevel)
local newData = {}
smoothnessLevel = smoothnessLevel or 0
if (smoothnessLevel > 0) then
smoothnessLevel = smoothnessLevel + 2
for i = 1, #data do
local thisValue = 0
local amountDataAdded = 0
--calculate the sum within the window
for o = i - math.floor(smoothnessLevel / 2), i + math.floor(smoothnessLevel / 2) do
if o >= 1 and o <= #data then
thisValue = thisValue + data[o]
amountDataAdded = amountDataAdded + 1
end
end
--calculate the average and store in the smoothedData value
local average = thisValue / amountDataAdded
table.insert(newData, average)
end
else
newData = data
end
chartFrame:SetDataRaw(newData)
local minValue, maxValue = chartFrame:GetDataMinMaxValues()
chartFrame:SetMinMaxValues(minValue, maxValue)
--clear the lines
chartFrame:HideLines()
end
chartFrame:AddDataChangeCallback(onSetDataCallback)
createPlotFrame(chartFrame) --creates chartFrame.plotFrame
return chartFrame
end
function detailsFramework:CreateGraphicLineFrame(parent, name)
---@type df_chart
local newGraphicFrame = createChartFrame(parent, name)
return newGraphicFrame
end
detailsFramework.MultiChartFrameMixin = {
MultiChartFrameConstructor = function(self)
self.nextChartselframe = 1
self.biggestDataValue = 0
self.lineThickness = 2
self.nextChartFrame = 1
self.chartFrames = {}
self.lineNameIndicators = {}
chartFrameSharedConstructor(self)
end,
IsMultiChart = function(self)
return true
end,
---add a new chart data and create a new chart frame if necessary to the multi chart
---@param self df_chartmulti
---@param data table
---@param smoothnessLevel number|nil
---@param name string|nil
---@param red any
---@param green number|nil
---@param blue number|nil
---@param alpha number|nil
AddData = function(self, data, smoothnessLevel, name, red, green, blue, alpha)
assert(type(data) == "table", "MultiChartFrame:AddData() usage: AddData(table)")
local chartFrame = self:GetChart()
red, green, blue, alpha = detailsFramework:ParseColors(red, green, blue, alpha)
chartFrame:SetColor(red, green, blue, alpha)
chartFrame:SetData(data, smoothnessLevel)
chartFrame.chartName = name or ""
self:SetMaxValueIfBigger(chartFrame:GetMaxValue())
self:SetMinValueIfLower(chartFrame:GetMinValue())
local dataAmount = chartFrame:GetDataSize()
self:SetMaxDataSize(dataAmount)
end,
---internally handle next line
---@param self df_chartmulti
---@return df_chart
GetChart = function(self)
local chartFrame = self.chartFrames[self.nextChartFrame]
if (not chartFrame) then
chartFrame = createChartFrame(self, "$parentChartFrame" .. self.nextChartFrame)
chartFrame:SetAllPoints()
chartFrame:UpdateFrameSizeCache()
self.chartFrames[self.nextChartFrame] = chartFrame
end
self.nextChartFrame = self.nextChartFrame + 1
chartFrame:Show()
return chartFrame
end,
---get all charts added to the multi chart frame
---@param self df_chartmulti
---@return df_chart[]
GetCharts = function(self)
return self.chartFrames
end,
---get the amount of charts added to the multi chart frame
---@param self df_chartmulti
---@return number
GetAmountCharts = function(self)
return self.nextChartFrame - 1
end,
---hide all charts
---@param self df_chartmulti
HideCharts = function(self)
local charts = self:GetCharts()
for i = 1, #charts do
local chartFrame = charts[i]
chartFrame:Hide()
end
end,
---reset the multi chart frame
---@param self df_chartmulti
Reset = function(self)
self:HideCharts()
self.nextChartFrame = 1
end,
---set the min and max values of all charts
---@param self df_chartmulti
---@param minValue number
---@param maxValue number
SetChartsMinMaxValues = function(self, minValue, maxValue)
local allCharts = self:GetCharts()
for i = 1, self:GetAmountCharts() do
local chartFrame = allCharts[i]
chartFrame:SetMinMaxValues(minValue, maxValue)
end
end,
---set the max data size of all charts
---@param self df_chartmulti
---@param dataSize number
SetMaxDataSize = function(self, dataSize)
self.biggestDataValue = math.max(self.biggestDataValue, dataSize)
end,
---get the max data size of all charts
---@param self df_chartmulti
---@return number
GetMaxDataSize = function(self)
return self.biggestDataValue
end,
---@param self df_chartmulti
---@param value number
SetLineThickness = function(self, value)
assert(type(value) == "number", "number expected on :SetLineThickness(number)")
self.lineThickness = value
end,
---@param self df_chartmulti
UpdateChartNamesIndicator = function(self)
local allCharts = self:GetCharts()
local allChartsAmount = self:GetAmountCharts()
--hide all indicators already created
for i = 1, #self.lineNameIndicators do
local thisIndicator = self.lineNameIndicators[i]
thisIndicator:Hide()
end
local nameIndicatorIndex = 1
for i = allChartsAmount, 1, -1 do
local chartFrame = allCharts[i]
local chartName = chartFrame.chartName
local red, green, blue, alpha = chartFrame:GetColor()
---@type chart_nameindicator
local thisIndicator = self.lineNameIndicators[nameIndicatorIndex]
if (not thisIndicator) then
---@type chart_nameindicator
thisIndicator = CreateFrame("frame", "$parentLineNameIndicator" .. i, self)
thisIndicator:SetSize(60, 12)
thisIndicator:Hide()
if (nameIndicatorIndex == 1) then
thisIndicator:SetPoint("topright", self, "topright", nameIndicatorIndex * -10, -10)
end
thisIndicator.Texture = thisIndicator:CreateTexture("$parentTexture", "overlay")
thisIndicator.Texture:SetSize(12, 12)
thisIndicator.Label = thisIndicator:CreateFontString("$parentLabel", "overlay", "GameFontNormal")
detailsFramework:SetFontSize(thisIndicator.Label, 11)
detailsFramework:SetFontColor(thisIndicator.Label, "white")
thisIndicator.Texture:SetPoint("left", thisIndicator, "left", 0, 0)
thisIndicator.Label:SetPoint("left", thisIndicator.Texture, "right", 2, 0)
self.lineNameIndicators[nameIndicatorIndex] = thisIndicator
end
thisIndicator.Texture:SetColorTexture(red, green, blue, alpha)
thisIndicator.Label:SetText(chartName)
local textWidth = thisIndicator.Label:GetStringWidth()
thisIndicator:SetWidth(math.max(textWidth + thisIndicator.Texture:GetWidth() + 4, 85))
if (nameIndicatorIndex > 1) then
local previousIndicator = self.lineNameIndicators[nameIndicatorIndex-1]
thisIndicator:SetPoint("topright", previousIndicator, "topleft", -2, 0)
end
nameIndicatorIndex = nameIndicatorIndex + 1
if (chartName ~= "") then
thisIndicator:Show()
end
end
end,
---draw all the charts added to the multi chart frame
---@param multiChartFrame df_chartmulti
Plot = function(multiChartFrame)
local minValue, multiChartMaxValue = multiChartFrame:GetMinMaxValues()
local plotAreaWidth = multiChartFrame.plotFrame:GetWidth() --if there's no axis, the plotFrame has no width
local maxDataSize = multiChartFrame:GetMaxDataSize()
local eachLineWidth = plotAreaWidth / maxDataSize
local allCharts = multiChartFrame:GetCharts()
for i = 1, multiChartFrame:GetAmountCharts() do
local chartFrame = allCharts[i]
chartFrame.chartLeftOffset = multiChartFrame.chartLeftOffset
chartFrame.chartBottomOffset = multiChartFrame.chartLeftOffset
chartFrame.plotFrame:ClearAllPoints()
chartFrame.plotFrame:SetAllPoints(multiChartFrame.plotFrame)
chartFrame:SetLineThickness(multiChartFrame.lineThickness)
chartFrame:SetLineWidth(eachLineWidth)
--get the percentage of how small this data is compared to the biggest data
--this percentage is then used to scale down the to fit correctly the fontStrings showing the value metrics
local yPointScale = chartFrame.maxValue / multiChartMaxValue
local bUpdateLabels = false
chartFrame:Plot(yPointScale, bUpdateLabels)
end
updateLabelValues(multiChartFrame)
multiChartFrame:UpdateChartNamesIndicator()
end,
}
---create a chart frame object with support to multi lines
---@param parent frame
---@param name string|nil
---@return df_chartmulti
function detailsFramework:CreateGraphicMultiLineFrame(parent, name)
name = name or ("DetailsMultiChartFrameID" .. math.random(1, 10000000))
---@type df_chartmulti
local chartFrame = CreateFrame("frame", name, parent, "BackdropTemplate")
detailsFramework:Mixin(chartFrame, detailsFramework.ValueMixin)
detailsFramework:Mixin(chartFrame, detailsFramework.MultiChartFrameMixin)
detailsFramework:Mixin(chartFrame, detailsFramework.ChartFrameSharedMixin)
chartFrame:ValueConstructor()
chartFrame:MultiChartFrameConstructor()
createPlotFrame(chartFrame) --creates chartFrame.plotFrame
return chartFrame
end
@@ -0,0 +1,184 @@
do
local DF = _G ["DetailsFramework"]
if (not DF or not DetailsFrameworkCanLoad) then
return
end
DF.alias_text_colors = DF.alias_text_colors or {}
local defaultColors = {
["HUNTER"] = {0.67, 0.83, 0.45},
["WARLOCK"] = {0.58, 0.51, 0.79},
["PRIEST"] = {1.0, 1.0, 1.0},
["PALADIN"] = {0.96, 0.55, 0.73},
["MAGE"] = {0.41, 0.8, 0.94},
["ROGUE"] = {1.0, 0.96, 0.41},
["DRUID"] = {1.0, 0.49, 0.04},
["SHAMAN"] = {0.0, 0.44, 0.87},
["WARRIOR"] = {0.78, 0.61, 0.43},
["DEATHKNIGHT"] = {0.77, 0.12, 0.23},
["MONK"] = {0.0, 1.00, 0.59},
["DEMONHUNTER"] = {0.64, 0.19, 0.79},
["EVOKER"] = {0.20, 0.58, 0.50},
["dark1"] = {0.1215, 0.1176, 0.1294},
["dark2"] = {0.2215, 0.2176, 0.2294},
["dark3"] = {0.3215, 0.3176, 0.3294},
["aliceblue"] = {0.941176, 0.972549, 1, 1},
["antiquewhite"] = {0.980392, 0.921569, 0.843137, 1},
["aqua"] = {0, 1, 1, 1},
["aquamarine"] = {0.498039, 1, 0.831373, 1},
["azure"] = {0.941176, 1, 1, 1},
["beige"] = {0.960784, 0.960784, 0.862745, 1},
["bisque"] = {1, 0.894118, 0.768627, 1},
["black"] = {0, 0, 0, 1},
["blanchedalmond"] = {1, 0.921569, 0.803922, 1},
["blue"] = {0, 0, 1, 1},
["blueviolet"] = {0.541176, 0.168627, 0.886275, 1},
["brown"] = {0.647059, 0.164706, 0.164706, 1},
["burlywood"] = {0.870588, 0.721569, 0.529412, 1},
["cadetblue"] = {0.372549, 0.619608, 0.627451, 1},
["chartreuse"] = {0.498039, 1, 0, 1},
["chocolate"] = {0.823529, 0.411765, 0.117647, 1},
["coral"] = {1, 0.498039, 0.313725, 1},
["cornflowerblue"] = {0.392157, 0.584314, 0.929412, 1},
["cornsilk"] = {1, 0.972549, 0.862745, 1},
["crimson"] = {0.862745, 0.0784314, 0.235294, 1},
["cyan"] = {0, 1, 1, 1},
["darkblue"] = {0, 0, 0.545098, 1},
["darkcyan"] = {0, 0.545098, 0.545098, 1},
["darkgoldenrod"] = {0.721569, 0.52549, 0.0431373, 1},
["darkgray"] = {0.662745, 0.662745, 0.662745, 1},
["darkgreen"] = {0, 0.392157, 0, 1},
["darkkhaki"] = {0.741176, 0.717647, 0.419608, 1},
["darkmagenta"] = {0.545098, 0, 0.545098, 1},
["darkolivegreen"] = {0.333333, 0.419608, 0.184314, 1},
["darkorange"] = {1, 0.54902, 0, 1},
["darkorchid"] = {0.6, 0.196078, 0.8, 1},
["darkred"] = {0.545098, 0, 0, 1},
["darksalmon"] = {0.913725, 0.588235, 0.478431, 1},
["darkseagreen"] = {0.560784, 0.737255, 0.560784, 1},
["darkslateblue"] = {0.282353, 0.239216, 0.545098, 1},
["darkslategray"] = {0.184314, 0.309804, 0.309804, 1},
["darkturquoise"] = {0, 0.807843, 0.819608, 1},
["darkviolet"] = {0.580392, 0, 0.827451, 1},
["deeppink"] = {1, 0.0784314, 0.576471, 1},
["deepskyblue"] = {0, 0.74902, 1, 1},
["dimgray"] = {0.411765, 0.411765, 0.411765, 1},
["dimgrey"] = {0.411765, 0.411765, 0.411765, 1},
["dodgerblue"] = {0.117647, 0.564706, 1, 1},
["firebrick"] = {0.698039, 0.133333, 0.133333, 1},
["firebrickdark"] = {0.258039, 0.033333, 0.033333, 1},
["floralwhite"] = {1, 0.980392, 0.941176, 1},
["forestgreen"] = {0.133333, 0.545098, 0.133333, 1},
["fuchsia"] = {1, 0, 1, 1},
["gainsboro"] = {0.862745, 0.862745, 0.862745, 1},
["ghostwhite"] = {0.972549, 0.972549, 1, 1},
["gold"] = {1, 0.843137, 0, 1},
["goldenrod"] = {0.854902, 0.647059, 0.12549, 1},
["gray"] = {0.501961, 0.501961, 0.501961, 1},
["green"] = {0, 0.501961, 0, 1},
["greenyellow"] = {0.678431, 1, 0.184314, 1},
["honeydew"] = {0.941176, 1, 0.941176, 1},
["hotpink"] = {1, 0.411765, 0.705882, 1},
["indianred"] = {0.803922, 0.360784, 0.360784, 1},
["indigo"] = {0.294118, 0, 0.509804, 1},
["ivory"] = {1, 1, 0.941176, 1},
["khaki"] = {0.941176, 0.901961, 0.54902, 1},
["lavender"] = {0.901961, 0.901961, 0.980392, 1},
["lavenderblush"] = {1, 0.941176, 0.960784, 1},
["lawngreen"] = {0.486275, 0.988235, 0, 1},
["lemonchiffon"] = {1, 0.980392, 0.803922, 1},
["lightblue"] = {0.678431, 0.847059, 0.901961, 1},
["lightcoral"] = {0.941176, 0.501961, 0.501961, 1},
["lightcyan"] = {0.878431, 1, 1, 1},
["lightgoldenrodyellow"] = {0.980392, 0.980392, 0.823529, 1},
["lightgray"] = {0.827451, 0.827451, 0.827451, 1},
["lightgreen"] = {0.564706, 0.933333, 0.564706, 1},
["lightpink"] = {1, 0.713725, 0.756863, 1},
["lightsalmon"] = {1, 0.627451, 0.478431, 1},
["lightseagreen"] = {0.12549, 0.698039, 0.666667, 1},
["lightskyblue"] = {0.529412, 0.807843, 0.980392, 1},
["lightslategray"] = {0.466667, 0.533333, 0.6, 1},
["lightsteelblue"] = {0.690196, 0.768627, 0.870588, 1},
["lightyellow"] = {1, 1, 0.878431, 1},
["lime"] = {0, 1, 0, 1},
["limegreen"] = {0.196078, 0.803922, 0.196078, 1},
["linen"] = {0.980392, 0.941176, 0.901961, 1},
["magenta"] = {1, 0, 1, 1},
["maroon"] = {0.501961, 0, 0, 1},
["mediumaquamarine"] = {0.4, 0.803922, 0.666667, 1},
["mediumblue"] = {0, 0, 0.803922, 1},
["mediumorchid"] = {0.729412, 0.333333, 0.827451, 1},
["mediumpurple"] = {0.576471, 0.439216, 0.858824, 1},
["mediumseagreen"] = {0.235294, 0.701961, 0.443137, 1},
["mediumslateblue"] = {0.482353, 0.407843, 0.933333, 1},
["mediumspringgreen"] = {0, 0.980392, 0.603922, 1},
["mediumturquoise"] = {0.282353, 0.819608, 0.8, 1},
["mediumvioletred"] = {0.780392, 0.0823529, 0.521569, 1},
["midnightblue"] = {0.0980392, 0.0980392, 0.439216, 1},
["mintcream"] = {0.960784, 1, 0.980392, 1},
["mistyrose"] = {1, 0.894118, 0.882353, 1},
["moccasin"] = {1, 0.894118, 0.709804, 1},
["navajowhite"] = {1, 0.870588, 0.678431, 1},
["navy"] = {0, 0, 0.501961, 1},
["none"] ={0, 0, 0, 0},
["oldlace"] = {0.992157, 0.960784, 0.901961, 1},
["olive"] = {0.501961, 0.501961, 0, 1},
["olivedrab"] = {0.419608, 0.556863, 0.137255, 1},
["orange"] = {1, 0.647059, 0, 1},
["orangered"] = {1, 0.270588, 0, 1},
["orchid"] = {0.854902, 0.439216, 0.839216, 1},
["palegoldenrod"] = {0.933333, 0.909804, 0.666667, 1},
["palegreen"] = {0.596078, 0.984314, 0.596078, 1},
["paleturquoise"] = {0.686275, 0.933333, 0.933333, 1},
["palevioletred"] = {0.858824, 0.439216, 0.576471, 1},
["papayawhip"] = {1, 0.937255, 0.835294, 1},
["peachpuff"] = {1, 0.854902, 0.72549, 1},
["peru"] = {0.803922, 0.521569, 0.247059, 1},
["pink"] = {1, 0.752941, 0.796078, 1},
["plum"] = {0.866667, 0.627451, 0.866667, 1},
["powderblue"] = {0.690196, 0.878431, 0.901961, 1},
["purple"] = {0.501961, 0, 0.501961, 1},
["red"] = {1, 0, 0, 1},
["rosybrown"] = {0.737255, 0.560784, 0.560784, 1},
["royalblue"] = {0.254902, 0.411765, 0.882353, 1},
["saddlebrown"] = {0.545098, 0.270588, 0.0745098, 1},
["salmon"] = {0.980392, 0.501961, 0.447059, 1},
["sandybrown"] = {0.956863, 0.643137, 0.376471, 1},
["seagreen"] = {0.180392, 0.545098, 0.341176, 1},
["seashell"] = {1, 0.960784, 0.933333, 1},
["sienna"] = {0.627451, 0.321569, 0.176471, 1},
["silver"] = {0.752941, 0.752941, 0.752941, 1},
["skyblue"] = {0.529412, 0.807843, 0.921569, 1},
["slateblue"] = {0.415686, 0.352941, 0.803922, 1},
["slategray"] = {0.439216, 0.501961, 0.564706, 1},
["snow"] = {1, 0.980392, 0.980392, 1},
["springgreen"] = {0, 1, 0.498039, 1},
["steelblue"] = {0.27451, 0.509804, 0.705882, 1},
["tan"] = {0.823529, 0.705882, 0.54902, 1},
["teal"] = {0, 0.501961, 0.501961, 1},
["thistle"] = {0.847059, 0.74902, 0.847059, 1},
["tomato"] = {1, 0.388235, 0.278431, 1},
["transparent"] ={0, 0, 0, 0},
["turquoise"] = {0.25098, 0.878431, 0.815686, 1},
["violet"] = {0.933333, 0.509804, 0.933333, 1},
["wheat"] = {0.960784, 0.870588, 0.701961, 1},
["white"] = {1, 1, 1, 1},
["whitesmoke"] = {0.960784, 0.960784, 0.960784, 1},
["yellow"] = {1, 1, 0, 1},
["yellowgreen"] = {0.603922, 0.803922, 0.196078, 1}
}
function DF:GetDefaultColorList()
return defaultColors
end
for colorName, colorTable in pairs(defaultColors) do
DF.alias_text_colors [colorName] = colorTable
end
end
@@ -0,0 +1,565 @@
local detailsFramework = _G ["DetailsFramework"]
if (not detailsFramework or not DetailsFrameworkCanLoad) then
return
end
local _
local DF = detailsFramework
local CreateFrame = CreateFrame
local wipe = wipe
local unpack = unpack
---@class df_framecontainer : frame, dfframecontainermixin, df_optionsmixin
---@field bIsSizing boolean
---@field options table
---@field currentWidth number
---@field currentHeight number
---@field bottomLeftResizer framecontainerresizer
---@field bottomRightResizer framecontainerresizer
---@field topLeftResizer framecontainerresizer
---@field topRightResizer framecontainerresizer
---@field topResizer framecontainerresizer
---@field bottomResizer framecontainerresizer
---@field leftResizer framecontainerresizer
---@field rightResizer framecontainerresizer
---@field cornerResizers framecontainerresizer[]
---@field sideResizers framecontainerresizer[]
---@field components table<frame, boolean>
---@field moverFrame frame
---@field movableChildren table<frame, boolean>
---@field settingChangedCallback fun(frameContainer: df_framecontainer, settingName: string, settingValue: any)
---@field OnSizeChanged fun(frameContainer: df_framecontainer)
---@field OnResizerMouseDown fun(resizerButton: button, mouseButton: string)
---@field OnResizerMouseUp fun(resizerButton: button, mouseButton: string)
---@field HideResizer fun(frameContainer: df_framecontainer)
---@field ShowResizer fun(frameContainer: df_framecontainer)
---@field OnInitialize fun(frameContainer: df_framecontainer)
---@field SetResizeLocked fun(frameContainer: df_framecontainer, isLocked: boolean)
---@field SetMovableLocked fun(frameContainer: df_framecontainer, isLocked: boolean)
---@field CheckResizeLockedState fun(frameContainer: df_framecontainer)
---@field CheckMovableLockedState fun(frameContainer: df_framecontainer)
---@field CreateMover fun(frameContainer: df_framecontainer)
---@field CreateResizers fun(frameContainer: df_framecontainer)
---@field RegisterChildForDrag fun(frameContainer: df_framecontainer, child: frame)
---@field UnregisterChildForDrag fun(frameContainer: df_framecontainer, child: frame)
---@field RefreshChildrenState fun(frameContainer: df_framecontainer)
---@field OnChildDragStart fun(frameContainer: df_framecontainer, child: frame)
---@field OnChildDragStop fun(frameContainer: df_framecontainer, child: frame)
---@field SetSettingChangedCallback fun(frameContainer: df_framecontainer, callback: fun(frameContainer: df_framecontainer, settingName: string, settingValue: any))
---@field SendSettingChangedCallback fun(frameContainer: df_framecontainer, settingName: string, settingValue: any)
---@class framecontainerresizer : button
---@field sizingFrom string
---@class dfframecontainermixin
detailsFramework.FrameContainerMixin = {
--methods
---run when the user click on the resizer
---@param resizerButton framecontainerresizer
---@param mouseButton string
OnResizerMouseDown = function(resizerButton, mouseButton)
if (mouseButton ~= "LeftButton") then
return
end
---@type df_framecontainer
local frameContainer = resizerButton:GetParent() --Cannot assign `frame` to `df_framecontainer`. .. but df_framecontainer is inherited from frame
if (frameContainer.bIsSizing) then
return
end
frameContainer.bIsSizing = true
frameContainer:StartSizing(resizerButton.sizingFrom)
end,
---run when the user click on the resizer
---@param resizerButton framecontainerresizer
---@param mouseButton string
OnResizerMouseUp = function(resizerButton, mouseButton)
---@type df_framecontainer
local frameContainer = resizerButton:GetParent() --Cannot assign `frame` to `df_framecontainer`. .. but df_framecontainer is inherited from frame
if (not frameContainer.bIsSizing) then
return
end
frameContainer:StopMovingOrSizing()
frameContainer.bIsSizing = false
end,
---hide resizer
---@param frameContainer df_framecontainer
HideResizer = function(frameContainer)
for i = 1, #frameContainer.cornerResizers do
frameContainer.cornerResizers[i]:Hide()
end
for i = 1, #frameContainer.sideResizers do
frameContainer.sideResizers[i]:Hide()
end
end,
---show resizer
---@param frameContainer df_framecontainer
ShowResizer = function(frameContainer)
--corner resizers
if (frameContainer.options.use_bottomleft_resizer) then
frameContainer.bottomLeftResizer:Show()
end
if (frameContainer.options.use_bottomright_resizer) then
frameContainer.bottomRightResizer:Show()
end
if (frameContainer.options.use_topleft_resizer) then
frameContainer.topRightResizer:Show()
end
if (frameContainer.options.use_topright_resizer) then
frameContainer.topRightResizer:Show()
end
--side resizers
if (frameContainer.options.use_top_resizer) then
frameContainer.topResizer:Show()
end
if (frameContainer.options.use_bottom_resizer) then
frameContainer.bottomResizer:Show()
end
if (frameContainer.options.use_left_resizer) then
frameContainer.leftResizer:Show()
end
if (frameContainer.options.use_right_resizer) then
frameContainer.rightResizer:Show()
end
end,
---check the lock state and show or hide the resizer, set the frame as movable or not, resizeable or not
---@param frameContainer df_framecontainer
CheckResizeLockedState = function(frameContainer)
if (frameContainer.options.is_locked) then
frameContainer:HideResizer()
frameContainer:SetResizable(false)
else
frameContainer:ShowResizer()
frameContainer:SetResizable(true)
end
end,
---check if the framecontainer can be moved and show or hide the mover
---@param frameContainer df_framecontainer
CheckMovableLockedState = function(frameContainer)
if (frameContainer.options.is_movement_locked) then
frameContainer:SetMovable(false)
frameContainer:EnableMouse(false)
frameContainer.moverFrame:Hide()
else
frameContainer:SetMovable(true)
frameContainer:EnableMouse(true)
frameContainer.moverFrame:Show()
end
end,
---set the lock state
---@param frameContainer df_framecontainer
---@param isLocked boolean
SetResizeLocked = function(frameContainer, isLocked)
frameContainer.options.is_locked = isLocked
frameContainer:SendSettingChangedCallback("is_locked", isLocked)
frameContainer:CheckResizeLockedState()
end,
---set the state of the mover frame
---@param frameContainer df_framecontainer
---@param isLocked boolean
SetMovableLocked = function(frameContainer, isLocked)
frameContainer.options.is_movement_locked = isLocked
frameContainer:SendSettingChangedCallback("is_movement_locked", isLocked)
frameContainer:CheckMovableLockedState()
end,
---create a mover to move the frame
---@param frameContainer df_framecontainer
CreateMover = function(frameContainer)
local mover = CreateFrame("button", nil, frameContainer)
frameContainer.moverFrame = mover
mover:SetAllPoints(frameContainer)
mover:EnableMouse(false)
mover:SetMovable(true)
mover:SetScript("OnMouseDown", function(self, mouseButton)
if (mouseButton ~= "LeftButton" or frameContainer.options.is_movement_locked) then
return
end
frameContainer:StartMoving()
end)
mover:SetScript("OnMouseUp", function(self, mouseButton)
if (mouseButton ~= "LeftButton" or frameContainer.options.is_movement_locked) then
return
end
frameContainer:StopMovingOrSizing()
end)
end,
---create four corner resizer and four side resizer
---@param frameContainer df_framecontainer
CreateResizers = function(frameContainer)
local parent = frameContainer:GetParent()
--create resizers for the container corners
local bottomLeftResizer, bottomRightResizer = detailsFramework:CreateResizeGrips(frameContainer, nil, parent:GetName() .. "BottomLeftResizer", parent:GetName() .. "BottomRightResizer")
frameContainer.bottomLeftResizer = bottomLeftResizer
frameContainer.bottomRightResizer = bottomRightResizer
local topLeftResizer, topRightResizer = detailsFramework:CreateResizeGrips(frameContainer, nil, parent:GetName() .. "TopLeftResizer", parent:GetName() .. "TopRightResizer")
frameContainer.topLeftResizer = topLeftResizer
frameContainer.topRightResizer = topRightResizer
local topResizer, bottomResizer = detailsFramework:CreateResizeGrips(frameContainer, nil, parent:GetName() .. "TopResizer", parent:GetName() .. "BottomResizer")
frameContainer.topResizer = topResizer
frameContainer.bottomResizer = bottomResizer
local leftResizer, rightResizer = detailsFramework:CreateResizeGrips(frameContainer, nil, parent:GetName() .. "LeftResizer", parent:GetName() .. "RightResizer")
frameContainer.leftResizer = leftResizer
frameContainer.rightResizer = rightResizer
frameContainer.cornerResizers = {
bottomLeftResizer,
bottomRightResizer,
topLeftResizer,
topRightResizer,
}
frameContainer.sideResizers = {
topResizer,
bottomResizer,
leftResizer,
rightResizer,
}
--add all resizers to the frameContainer.components table
for i = 1, #frameContainer.cornerResizers do
frameContainer.components[frameContainer.cornerResizers[i]] = true
end
for i = 1, #frameContainer.sideResizers do
frameContainer.components[frameContainer.sideResizers[i]] = true
end
--hide all resizers
frameContainer:HideResizer()
end,
---run when the container is created
---@param frameContainer df_framecontainer
OnInitialize = function(frameContainer) --õninit ~init ~oninit
--set the default members
frameContainer.bIsSizing = false
frameContainer:SetSize(frameContainer.options.width, frameContainer.options.height)
--iterate among the corner resizers and set the mouse down and up scripts
for i = 1, #frameContainer.cornerResizers do
frameContainer.cornerResizers[i]:SetScript("OnMouseDown", frameContainer.OnResizerMouseDown)
frameContainer.cornerResizers[i]:SetScript("OnMouseUp", frameContainer.OnResizerMouseUp)
end
local sideResizeThickness = 2
--iterate among the side resizers and set the mouse down and up scripts; set the texture color; clear all points; set the thickness
for i = 1, #frameContainer.sideResizers do
frameContainer.sideResizers[i]:SetScript("OnMouseDown", frameContainer.OnResizerMouseDown)
frameContainer.sideResizers[i]:SetScript("OnMouseUp", frameContainer.OnResizerMouseUp)
frameContainer.sideResizers[i]:GetNormalTexture():SetColorTexture(1, 1, 1, 0.6)
frameContainer.sideResizers[i]:GetHighlightTexture():SetColorTexture(detailsFramework:ParseColors("aqua"))
frameContainer.sideResizers[i]:GetPushedTexture():SetColorTexture(1, 1, 1, 1)
frameContainer.sideResizers[i]:ClearAllPoints()
--can use SetSize here because the width or height are set by the point, e.g. 'topleft' to 'topright' overwrite the width set here
frameContainer.sideResizers[i]:SetSize(sideResizeThickness, sideResizeThickness)
end
--flip the corner resizers texturess
frameContainer.topLeftResizer:GetNormalTexture():SetTexCoord(1, 0, 1, 0)
frameContainer.topLeftResizer:GetHighlightTexture():SetTexCoord(1, 0, 1, 0)
frameContainer.topLeftResizer:GetPushedTexture():SetTexCoord(1, 0, 1, 0)
frameContainer.topRightResizer:GetNormalTexture():SetTexCoord(0, 1, 1, 0)
frameContainer.topRightResizer:GetHighlightTexture():SetTexCoord(0, 1, 1, 0)
frameContainer.topRightResizer:GetPushedTexture():SetTexCoord(0, 1, 1, 0)
frameContainer.topLeftResizer:ClearAllPoints()
frameContainer.topLeftResizer:SetPoint("topleft", frameContainer, "topleft", 0, 0)
frameContainer.topRightResizer:ClearAllPoints()
frameContainer.topRightResizer:SetPoint("topright", frameContainer, "topright", 0, 0)
--resize from for the corner resizers
frameContainer.bottomLeftResizer.sizingFrom = "bottomleft"
frameContainer.bottomRightResizer.sizingFrom = "bottomright"
frameContainer.topLeftResizer.sizingFrom = "topleft"
frameContainer.topRightResizer.sizingFrom = "topright"
--resize from for the side resizers
frameContainer.topResizer.sizingFrom = "top"
frameContainer.bottomResizer.sizingFrom = "bottom"
frameContainer.leftResizer.sizingFrom = "left"
frameContainer.rightResizer.sizingFrom = "right"
--set the side resizer points
frameContainer.topResizer:SetPoint("topleft", frameContainer, "topleft", 0, 2)
frameContainer.topResizer:SetPoint("topright", frameContainer, "topright", 0, 2)
frameContainer.bottomResizer:SetPoint("bottomleft", frameContainer, "bottomleft", 0, -2)
frameContainer.bottomResizer:SetPoint("bottomright", frameContainer, "bottomright", 0, -2)
frameContainer.leftResizer:SetPoint("topleft", frameContainer, "topleft", -2, 0)
frameContainer.leftResizer:SetPoint("bottomleft", frameContainer, "bottomleft", -2, 0)
frameContainer.rightResizer:SetPoint("topright", frameContainer, "topright", 2, 0)
frameContainer.rightResizer:SetPoint("bottomright", frameContainer, "bottomright", 2, 0)
if (frameContainer.options.is_locked) then
frameContainer:HideResizer()
else
frameContainer:ShowResizer()
end
frameContainer:CheckResizeLockedState()
frameContainer:CheckMovableLockedState()
if (frameContainer.SetMinResize) then --old versions of the game uses this method
frameContainer:SetMinResize(50, 50)
frameContainer:SetMaxResize(1000,1000)
else
frameContainer:SetResizeBounds(50, 50, 1000, 1000) --new versions has this method
end
end,
---run when the container has its size changed
---@param frameContainer df_framecontainer
OnSizeChanged = function(frameContainer)
---@type frame[]
local children = {frameContainer:GetChildren()}
---@type number
local childrenAmount = #children
--get the container size before its size was changed and calculate the percent of the difference between the old size and the new size
--adding +1 to the width and height difference to prevent the child from shrinking to 0, so it is scaled by 1
---@type number
local widthDifference = 1 + (frameContainer:GetWidth() - frameContainer.currentWidth) / frameContainer.currentWidth
---@type number
local heightDifference = 1 + (frameContainer:GetHeight() - frameContainer.currentHeight) / frameContainer.currentHeight
for i = 1, childrenAmount do
---@type frame
local child = children[i]
--if the child is a component, skip it
if (not frameContainer.components[child]) then
child:SetWidth(child:GetWidth() * widthDifference)
child:SetHeight(child:GetHeight() * heightDifference)
end
end
--update the current size of the container
frameContainer.currentWidth = frameContainer:GetWidth()
frameContainer.currentHeight = frameContainer:GetHeight()
frameContainer:SendSettingChangedCallback("width", frameContainer.currentWidth)
frameContainer:SendSettingChangedCallback("height", frameContainer.currentHeight)
end,
OnChildDragStop = function(child)
child:StopMovingOrSizing()
child:SetScript("OnUpdate", nil)
end,
---@param child frame
OnChildDragStart = function(child)
---@type df_framecontainer
local frameContainer = child:GetParent()
---get the coordinates for the frame container, which is called 'boundingBox' for convenience
---@type objectcoordinates
local boundingBox = detailsFramework:GetObjectCoordinates(frameContainer)
child:StartMoving()
--save the current point of the child, so it can be restored if the child is dragged outside the container
local childPoints = {}
for pointIndex = 1, child:GetNumPoints() do
childPoints[pointIndex] = {child:GetPoint(pointIndex)}
end
child:SetScript("OnUpdate", function(self)
---@type objectcoordinates
local childPos = detailsFramework:GetObjectCoordinates(self)
--check if the borders of the rectangle 'rec' collided with the borders of the rectangle 'bbox'
if ((childPos.left < boundingBox.left or childPos.right > boundingBox.right) or (childPos.top > boundingBox.top or childPos.bottom < boundingBox.bottom)) then
child:ClearAllPoints()
for pointIndex = 1, #childPoints do
child:SetPoint(unpack(childPoints[pointIndex]))
end
else
wipe(childPoints)
for pointIndex = 1, child:GetNumPoints() do
childPoints[pointIndex] = {child:GetPoint(pointIndex)}
end
end
end)
end,
---check if the children can be moved and set the properties on thisFrame
---@param frameContainer df_framecontainer
RefreshChildrenState = function(frameContainer)
frameContainer:EnableMouse(true)
if (frameContainer.options.can_move_children) then
for child, _ in pairs(frameContainer.movableChildren) do
child:EnableMouse(true)
child:SetMovable(true)
child:RegisterForDrag("LeftButton")
child:SetScript("OnDragStart", detailsFramework.FrameContainerMixin.OnChildDragStart)
child:SetScript("OnDragStop", detailsFramework.FrameContainerMixin.OnChildDragStop)
end
else
for child, _ in pairs(frameContainer.movableChildren) do
child:EnableMouse(false)
child:SetMovable(false)
child:RegisterForDrag("")
child:SetScript("OnDragStart", nil)
child:SetScript("OnDragStop", nil)
end
end
end,
---@param frameContainer df_framecontainer
---@param child frame
RegisterChildForDrag = function(frameContainer, child)
frameContainer.movableChildren[child] = true
frameContainer:RefreshChildrenState()
child:SetFrameStrata(frameContainer:GetFrameStrata())
child:SetFrameLevel(frameContainer:GetFrameLevel() + 1)
end,
---@param frameContainer df_framecontainer
---@param child frame
UnregisterChildForDrag = function(frameContainer, child)
frameContainer.movableChildren[child] = nil
frameContainer:RefreshChildrenState()
end,
---@param frameContainer df_framecontainer
---@param callback function
SetSettingChangedCallback = function(frameContainer, callback)
frameContainer.settingChangedCallback = callback
end,
---send a callback to the setting changed callback
---@param frameContainer df_framecontainer
---@param key string
---@param value any
SendSettingChangedCallback = function(frameContainer, key, value)
if (type(frameContainer.settingChangedCallback) == "function") then
detailsFramework:Dispatch(frameContainer.settingChangedCallback, frameContainer, key, value)
end
end,
}
---these are the default settings for the frame container; these keys can be accessed by df_framecontainer.options[key]
---@type table<string, any>
local frameContainerOptions = {
--default settings
width = 300,
height = 150,
is_locked = true, --can or not be resized
is_movement_locked = true, --can or not be moved
can_move_children = true,
use_topleft_resizer = false,
use_topright_resizer = false,
use_bottomleft_resizer = false,
use_bottomright_resizer = false,
use_top_resizer = false,
use_bottom_resizer = false,
use_left_resizer = false,
use_right_resizer = false,
}
---create a frame container, which is a frame that envelops another frame, and can be moved, resized, etc.
---@param parent frame
---@param options table|nil
---@param frameName string|nil
---@return df_framecontainer
function DF:CreateFrameContainer(parent, options, frameName)
---@type df_framecontainer
local frameContainer = CreateFrame("frame", frameName or ("$parentFrameContainer" .. math.random(10000, 99999)), parent, "BackdropTemplate")
frameContainer.components = {}
frameContainer.movableChildren = {}
frameContainer:EnableMouse(false)
detailsFramework:Mixin(frameContainer, detailsFramework.FrameContainerMixin)
detailsFramework:Mixin(frameContainer, detailsFramework.OptionsFunctions)
frameContainer:CreateResizers()
frameContainer:CreateMover()
frameContainer:BuildOptionsTable(frameContainerOptions, options)
frameContainer:OnInitialize()
frameContainer.currentWidth = frameContainer:GetWidth()
frameContainer.currentHeight = frameContainer:GetHeight()
frameContainer:SetScript("OnSizeChanged", frameContainer.OnSizeChanged)
return frameContainer
end
function DF:CreateFrameContainerTest(parent, options, frameName)
local container = DF:CreateFrameContainer(parent, options, frameName)
container:SetSize(400, 400)
container:SetPoint("center", _G.UIParent, "center", 0, 0)
detailsFramework:ApplyStandardBackdrop(container)
local verticalLastBox = nil
for i = 1, 4 do
local lastBox = nil
for o = 1, 4 do
local frame = CreateFrame("frame", "$parentFrame" .. i .. o, container, "BackdropTemplate")
container:RegisterChildForDrag(frame)
frame:SetBackdrop({edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true})
frame:SetBackdropColor(0, 0, 0, 0.5)
frame:SetBackdropBorderColor(1, 1, 1, 0.5)
frame:SetSize(98, 98)
if (lastBox) then
frame:SetPoint("TOPLEFT", lastBox, "topright", 1, 0)
else
if (verticalLastBox) then
frame:SetPoint("TOPLEFT", verticalLastBox, "bottomleft", 0, -1)
verticalLastBox = frame
else
local x = 1 + (o - 1) * 99
local y = -10 - (i - 1) * 99
frame:SetPoint("TOPLEFT", container, "TOPLEFT", x, y)
verticalLastBox = frame
end
end
lastBox = frame
end
end
C_Timer.After(2, function()
--container:SetResizeLocked(true)
end)
end
--C_Timer.After(2, function()
-- DetailsFramework:CreateFrameContainerTest(UIParent)
--end)
--[=[
/run DetailsFramework:CreateFrameContainerTest(UIParent)
C_Timer.After(2, function()
DetailsFramework:CreateFrameContainerTest(UIParent)
end)
--]=]
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,5 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ .. \FrameXML\UI.xsd">
<Script file="cooltip.lua"/>
</Ui>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,4 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ .. \FrameXML\UI.xsd">
<Script file="dropdown.lua"/>
</Ui>
@@ -0,0 +1,18 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/..\FrameXML\UI.xsd">
<Script file="Libs\LibStub\LibStub.lua"/>
<Include file="Libs\CallbackHandler-1.0\CallbackHandler-1.0.lua"/>
<Include file="Libs\LibSharedMedia-3.0\lib.xml"/>
<Include file="Libs\AceGUI-3.0\AceGUI-3.0.xml"/>
<Include file="Libs\AceDBOptions-3.0\AceDBOptions-3.0.xml"/>
<Include file="Libs\AceLocale-3.0\AceLocale-3.0.xml" />
<Include file="Libs\AceAddon-3.0\AceAddon-3.0.xml" />
<Include file="Libs\AceDB-3.0\AceDB-3.0.xml" />
<Include file="Libs\AceTimer-3.0\AceTimer-3.0.xml" />
<Include file="Libs\AceConfig-3.0\AceConfig-3.0.xml"/>
<Include file="Libs\AceEvent-3.0\AceEvent-3.0.xml"/>
<Include file="Libs\AceComm-3.0\AceComm-3.0.xml" />
<Include file="Libs\AceConsole-3.0\AceConsole-3.0.xml"/>
<Include file="Libs\AceSerializer-3.0\AceSerializer-3.0.xml"/>
<Script file="Libs\LibDataBroker-1.1\LibDataBroker-1.1.lua"/>
<Script file="Libs\LibDBIcon-1.0\LibDBIcon-1.0.lua"/>
</Ui>
@@ -0,0 +1,116 @@
local detailsFramework = _G ["DetailsFramework"]
if (not detailsFramework) then
return
end
local IS_WOW_PROJECT_MAINLINE = WOW_PROJECT_ID == WOW_PROJECT_MAINLINE
local IS_WOW_PROJECT_NOT_MAINLINE = WOW_PROJECT_ID ~= WOW_PROJECT_MAINLINE
local IS_WOW_PROJECT_CLASSIC_ERA = WOW_PROJECT_ID == WOW_PROJECT_CLASSIC
detailsFramework.CastInfo = detailsFramework.CastInfo or {}
--NOTE: This NEEDs a chance to run, as Plater is depending on this working and LibCC is not bundled neccessarily in other addons.
-- for classic era use LibClassicCasterino:
--in vanilla wow, other addons might load the framework before an addon with libCasterino loads
--check here if libCasterino is loaded, if is, check if the framework is already using libCasterino, if not, make it use
function detailsFramework:LoadLCC(LibCC)
local fCast = CreateFrame("frame")
local getCastBar = function(unitId)
local plateFrame = C_NamePlate.GetNamePlateForUnit (unitId)
if (not plateFrame) then
return
end
local castBar = plateFrame.unitFrame and plateFrame.unitFrame.castBar
if (not castBar) then
return
end
return castBar
end
local triggerCastEvent = function(castBar, event, unitId, ...)
if (castBar and castBar.OnEvent) then
return castBar.OnEvent (castBar, event, unitId)
end
end
local funcCast = function(event, unitId, ...)
local castBar = getCastBar (unitId)
if (castBar) then
return triggerCastEvent (castBar, event, unitId)
end
end
fCast.UNIT_SPELLCAST_START = function(self, event, unitId, ...)
return triggerCastEvent (getCastBar (unitId), event, unitId)
end
fCast.UNIT_SPELLCAST_STOP = function(self, event, unitId, ...)
return triggerCastEvent (getCastBar (unitId), event, unitId)
end
fCast.UNIT_SPELLCAST_DELAYED = function(self, event, unitId, ...)
return triggerCastEvent (getCastBar (unitId), event, unitId)
end
fCast.UNIT_SPELLCAST_FAILED = function(self, event, unitId, ...)
return triggerCastEvent (getCastBar (unitId), event, unitId)
end
fCast.UNIT_SPELLCAST_INTERRUPTED = function(self, event, unitId, ...)
return triggerCastEvent (getCastBar (unitId), event, unitId)
end
fCast.UNIT_SPELLCAST_CHANNEL_START = function(self, event, unitId, ...)
return triggerCastEvent (getCastBar (unitId), event, unitId)
end
fCast.UNIT_SPELLCAST_CHANNEL_UPDATE = function(self, event, unitId, ...)
return triggerCastEvent (getCastBar (unitId), event, unitId)
end
fCast.UNIT_SPELLCAST_CHANNEL_STOP = function(self, event, unitId, ...)
return triggerCastEvent (getCastBar (unitId), event, unitId)
end
LibCC.RegisterCallback(fCast,"UNIT_SPELLCAST_START", funcCast)
LibCC.RegisterCallback(fCast,"UNIT_SPELLCAST_DELAYED", funcCast) -- only for player
LibCC.RegisterCallback(fCast,"UNIT_SPELLCAST_STOP", funcCast)
LibCC.RegisterCallback(fCast,"UNIT_SPELLCAST_FAILED", funcCast)
LibCC.RegisterCallback(fCast,"UNIT_SPELLCAST_INTERRUPTED", funcCast)
LibCC.RegisterCallback(fCast,"UNIT_SPELLCAST_CHANNEL_START", funcCast)
LibCC.RegisterCallback(fCast,"UNIT_SPELLCAST_CHANNEL_UPDATE", funcCast) -- only for player
LibCC.RegisterCallback(fCast,"UNIT_SPELLCAST_CHANNEL_STOP", funcCast)
detailsFramework.CastInfo.UnitCastingInfo = function(unit)
return LibCC:UnitCastingInfo (unit)
end
detailsFramework.CastInfo.UnitChannelInfo = function(unit)
return LibCC:UnitChannelInfo (unit)
end
end
if IS_WOW_PROJECT_CLASSIC_ERA then
local LibCC = LibStub("LibClassicCasterino", true)
if (LibCC and not _G.DetailsFrameworkLCCLoaded) then
detailsFramework:LoadLCC(LibCC)
_G.DetailsFrameworkLCCLoaded = true
elseif not LibCC then
detailsFramework.CastInfo.UnitCastingInfo = CastingInfo
detailsFramework.CastInfo.UnitChannelInfo = ChannelInfo
end
else -- end classic era
detailsFramework.CastInfo.UnitCastingInfo = UnitCastingInfo
detailsFramework.CastInfo.UnitChannelInfo = UnitChannelInfo
end
if (not DetailsFrameworkCanLoad) then
return
end
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,686 @@
local detailsFramework = DetailsFramework
if (not detailsFramework or not DetailsFrameworkCanLoad) then
return
end
local unpack = unpack
local CreateFrame = CreateFrame
local geterrorhandler = geterrorhandler
local wipe = wipe
--definitions
---@class df_headercolumndata : {key: string, name: string, icon: string, texcoord: table, text: string, canSort: boolean, selected: boolean, width: number, height: number, align: string, offset: number}
---@class df_headerchild : uiobject
---@field FramesToAlign table
---@class df_headerframe : frame, df_headermixin, df_optionsmixin
---@field columnHeadersCreated df_headercolumnframe[]
---@field options table
---@field HeaderTable df_headercolumndata[]
---@field columnSelected number
---@class df_headermixin : table
---@field NextHeader number
---@field HeaderWidth number
---@field HeaderHeight number
---@field OnColumnSettingChangeCallback function
---@field GetColumnWidth fun(self: df_headerframe, columnId: number) : number
---@field SetHeaderTable fun(self: df_headerframe, table)
---@field GetSelectedColumn fun(self: df_headerframe) : number, string, string, string
---@field Refresh fun(self: df_headerframe)
---@field UpdateSortArrow fun(self: df_headerframe, columnHeader: df_headercolumnframe, defaultShown: boolean|nil, defaultOrder: string|nil)
---@field UpdateColumnHeader fun(self: df_headerframe, columnHeader: df_headercolumnframe, headerIndex)
---@field ResetColumnHeaderBackdrop fun(self: df_headerframe, columnHeader: df_headercolumnframe)
---@field SetBackdropColorForSelectedColumnHeader fun(self: df_headerframe, columnHeader: df_headercolumnframe)
---@field ClearColumnHeader fun(self: df_headerframe, columnHeader: df_headercolumnframe)
---@field GetNextHeader fun(self: df_headerframe) : df_headercolumnframe
---@field SetColumnSettingChangedCallback fun(self: df_headerframe, func: function) : boolean
---@class df_headercolumnframe : button
---@field Icon texture
---@field Text fontstring
---@field Arrow texture
---@field Separator texture
---@field resizerButton df_headerresizer
---@field bIsRezising boolean
---@field bInUse boolean
---@field columnData table
---@field order string
---@field columnIndex number
---@field columnAlign string
---@field XPosition number
---@field columnOffset number
---@field key string used to sort the values
---@class df_headerresizer : button
---@field texture texture
--mixed functions
---@class df_headerfunctions : table
detailsFramework.HeaderFunctions = {
---comment
---@param self df_headerchild
---@param frame uiobject
AddFrameToHeaderAlignment = function(self, frame)
self.FramesToAlign = self.FramesToAlign or {}
table.insert(self.FramesToAlign, frame)
end,
---comment
---@param self df_headerchild
ResetFramesToHeaderAlignment = function(self)
wipe(self.FramesToAlign)
end,
SetFramesToHeaderAlignment = function(self, ...)
---@cast self df_headerchild
wipe(self.FramesToAlign)
self.FramesToAlign = {...}
end,
GetFramesFromHeaderAlignment = function(self, frame)
return self.FramesToAlign or {}
end,
---@param self uiobject
---@param headerFrame df_headerframe
---@param anchor string
AlignWithHeader = function(self, headerFrame, anchor)
local columnHeaderFrames = headerFrame.columnHeadersCreated
anchor = anchor or "topleft"
---@cast self df_headerchild
for i = 1, #self.FramesToAlign do
---@type uiobject
local uiObject = self.FramesToAlign[i]
uiObject:ClearAllPoints()
---@type df_headercolumnframe
local columnHeader = columnHeaderFrames[i]
if (columnHeader) then
local offset = 0
if (columnHeader.columnAlign == "right") then
offset = columnHeader:GetWidth()
end
if (uiObject:GetObjectType() == "FontString") then
---@cast uiObject fontstring
if (columnHeader.columnAlign == "right") then
uiObject:SetJustifyH("right")
elseif (columnHeader.columnAlign == "left") then
uiObject:SetJustifyH("left")
elseif (columnHeader.columnAlign == "center") then
uiObject:SetJustifyH("center")
end
end
uiObject:SetPoint(columnHeader.columnAlign, self, anchor, columnHeader.XPosition + columnHeader.columnOffset + offset, 0)
end
end
end,
---comment
---@param columnHeader df_headercolumnframe
---@param buttonClicked string
OnClick = function(columnHeader, buttonClicked)
--get the header main frame
local headerFrame = columnHeader:GetParent()
---@cast headerFrame df_headerframe
--if this header does not have a clickable header, just ignore
if (not headerFrame.columnSelected) then
return
end
--check if this column has 'canSort' key, otherwise ignore the click
if (not columnHeader.columnData.canSort) then
return
end
--get the latest column header selected
---@type df_headercolumnframe
local previousColumnHeader = headerFrame.columnHeadersCreated[headerFrame.columnSelected]
previousColumnHeader.Arrow:Hide()
headerFrame:ResetColumnHeaderBackdrop(previousColumnHeader)
headerFrame:SetBackdropColorForSelectedColumnHeader(columnHeader)
if (headerFrame.columnSelected == columnHeader.columnIndex) then
columnHeader.order = columnHeader.order ~= "ASC" and "ASC" or "DESC"
end
headerFrame.columnOrder = columnHeader.order
--set the new column header selected
headerFrame.columnSelected = columnHeader.columnIndex
headerFrame:UpdateSortArrow(columnHeader)
if (headerFrame.options.header_click_callback) then
--callback with the main header frame, column header, column index and column order as payload
local okay, errortext = pcall(headerFrame.options.header_click_callback, headerFrame, columnHeader, columnHeader.columnIndex, columnHeader.order)
if (not okay) then
print("DF: Header onClick callback error:", errortext)
end
end
end,
---comment
---@param self button
---@param buttonClicked string
OnMouseDown = function(self, buttonClicked)
if (buttonClicked == "LeftButton") then
end
end,
---comment
---@param self button
---@param buttonClicked string
OnMouseUp = function(self, buttonClicked)
if (buttonClicked == "LeftButton") then
end
end,
}
---@class df_headermixin : table
detailsFramework.HeaderMixin = {
---@param self df_headerframe
---@param columnId number
---@return number
GetColumnWidth = function(self, columnId)
return self.HeaderTable[columnId].width
end,
---@param self df_headerframe
---@param newTable table
SetHeaderTable = function(self, newTable)
self.columnHeadersCreated = self.columnHeadersCreated or {}
self.HeaderTable = newTable
self.NextHeader = 1
self.HeaderWidth = 0
self.HeaderHeight = 0
self:Refresh()
end,
---@param self df_headerframe
---@param func function
---@return boolean
SetColumnSettingChangedCallback = function(self, func)
if (type(func) ~= "function") then
self.OnColumnSettingChangeCallback = nil
return false
end
self.OnColumnSettingChangeCallback = func
return true
end,
--return which header is current selected and the the order ASC DESC
---@param self df_headerframe
---@return number, string, string, string
GetSelectedColumn = function(self)
---@type number
local columnSelected = self.columnSelected
---@type df_headercolumnframe
local columnHeader = self.columnHeadersCreated[columnSelected or 1]
return columnSelected, columnHeader.order, columnHeader.key, columnHeader.columnData.name
end,
--clean up and rebuild the header following the header options
--@self: main header frame
---@param self df_headerframe
Refresh = function(self)
--refresh background frame
self:SetBackdrop(self.options.backdrop)
self:SetBackdropColor(unpack(self.options.backdrop_color))
self:SetBackdropBorderColor(unpack(self.options.backdrop_border_color))
--reset all header frames
for i = 1, #self.columnHeadersCreated do
local columnHeader = self.columnHeadersCreated[i]
columnHeader.bInUse = false
columnHeader:Hide()
end
local previousColumnHeader
local growDirection = string.lower(self.options.grow_direction)
--amount of headers to be updated
local headerSize = #self.HeaderTable
--update header frames
for i = 1, headerSize do
--get the header button, a new one is created if it doesn't exists yet
local columnHeader = self:GetNextHeader()
self:UpdateColumnHeader(columnHeader, i)
--grow direction
if (not previousColumnHeader) then
columnHeader:SetPoint("topleft", self, "topleft", 0, 0)
if (growDirection == "right") then
if (self.options.use_line_separators) then
columnHeader.Separator:Show()
columnHeader.Separator:SetWidth(self.options.line_separator_width)
columnHeader.Separator:SetColorTexture(unpack(self.options.line_separator_color))
columnHeader.Separator:ClearAllPoints()
if (self.options.line_separator_gap_align) then
columnHeader.Separator:SetPoint("topleft", columnHeader, "topright", 0, 0)
else
columnHeader.Separator:SetPoint("topright", columnHeader, "topright", 0, 0)
end
columnHeader.Separator:SetHeight(self.options.line_separator_height)
end
end
else
if (growDirection == "right") then
columnHeader:SetPoint("topleft", previousColumnHeader, "topright", self.options.padding, 0)
if (self.options.use_line_separators) then
columnHeader.Separator:Show()
columnHeader.Separator:SetWidth(self.options.line_separator_width)
columnHeader.Separator:SetColorTexture(unpack(self.options.line_separator_color))
columnHeader.Separator:ClearAllPoints()
if (self.options.line_separator_gap_align) then
columnHeader.Separator:SetPoint("topleft", columnHeader, "topright", 0, 0)
else
columnHeader.Separator:SetPoint("topleft", columnHeader, "topright", 0, 0)
end
columnHeader.Separator:SetHeight(self.options.line_separator_height)
if (headerSize == i) then
columnHeader.Separator:Hide()
end
end
elseif (growDirection == "left") then
columnHeader:SetPoint("topright", previousColumnHeader, "topleft", -self.options.padding, 0)
elseif (growDirection == "bottom") then
columnHeader:SetPoint("topleft", previousColumnHeader, "bottomleft", 0, -self.options.padding)
elseif (growDirection == "top") then
columnHeader:SetPoint("bottomleft", previousColumnHeader, "topleft", 0, self.options.padding)
end
end
previousColumnHeader = columnHeader
end
self:SetSize(self.HeaderWidth, self.HeaderHeight)
end,
---@param self df_headerframe
---@param columnHeader df_headercolumnframe
---@param defaultShown boolean
---@param defaultOrder string
UpdateSortArrow = function(self, columnHeader, defaultShown, defaultOrder)
local options = self.options
local order = defaultOrder or columnHeader.order
local arrowIcon = columnHeader.Arrow
if (type(defaultShown) ~= "boolean") then
arrowIcon:Show()
else
arrowIcon:SetShown(defaultShown)
if (defaultShown) then
self:SetBackdropColorForSelectedColumnHeader(columnHeader)
end
end
arrowIcon:SetAlpha(options.arrow_alpha)
if (order == "ASC") then
arrowIcon:SetTexture(options.arrow_up_texture)
arrowIcon:SetTexCoord(unpack(options.arrow_up_texture_coords))
arrowIcon:SetSize(unpack(options.arrow_up_size))
elseif (order == "DESC") then
arrowIcon:SetTexture(options.arrow_down_texture)
arrowIcon:SetTexCoord(unpack(options.arrow_down_texture_coords))
arrowIcon:SetSize(unpack(options.arrow_down_size))
end
end,
---@param self df_headerframe
---@param columnHeader df_headercolumnframe
---@param headerIndex number
UpdateColumnHeader = function(self, columnHeader, headerIndex)
--this is the data to update the columnHeader
local columnData = self.HeaderTable[headerIndex]
columnHeader.key = columnData.key or "total"
if (columnData.icon) then
columnHeader.Icon:SetTexture(columnData.icon)
if (columnData.texcoord) then
columnHeader.Icon:SetTexCoord(unpack(columnData.texcoord))
else
columnHeader.Icon:SetTexCoord(0, 1, 0, 1)
end
columnHeader.Icon:SetPoint("left", columnHeader, "left", self.options.padding, 0)
columnHeader.Icon:Show()
end
if (columnData.text) then
columnHeader.Text:SetText(columnData.text)
--text options
detailsFramework:SetFontColor(columnHeader.Text, self.options.text_color)
detailsFramework:SetFontSize(columnHeader.Text, self.options.text_size)
detailsFramework:SetFontOutline(columnHeader.Text, self.options.text_shadow)
--point
if (not columnData.icon) then
columnHeader.Text:SetPoint("left", columnHeader, "left", self.options.padding, 0)
else
columnHeader.Text:SetPoint("left", columnHeader.Icon, "right", self.options.padding, 0)
end
columnHeader.Text:Show()
end
--column header index
columnHeader.columnIndex = headerIndex
if (columnData.canSort) then
columnHeader.order = "DESC"
columnHeader.Arrow:SetTexture(self.options.arrow_up_texture)
else
columnHeader.Arrow:Hide()
end
if (columnData.selected) then
columnHeader.Arrow:Show()
columnHeader.Arrow:SetAlpha(.843)
self:UpdateSortArrow(columnHeader, true, columnHeader.order)
self.columnSelected = headerIndex
else
if (columnData.canSort) then
self:UpdateSortArrow(columnHeader, false, columnHeader.order)
end
end
--size
if (columnData.width) then
columnHeader:SetWidth(columnData.width)
end
if (columnData.height) then
columnHeader:SetHeight(columnData.height)
end
columnHeader.XPosition = self.HeaderWidth -- + self.options.padding
columnHeader.YPosition = self.HeaderHeight -- + self.options.padding
columnHeader.columnAlign = columnData.align or "left"
columnHeader.columnOffset = columnData.offset or 0
--add the header piece size to the total header size
local growDirection = string.lower(self.options.grow_direction)
if (growDirection == "right" or growDirection == "left") then
self.HeaderWidth = self.HeaderWidth + columnHeader:GetWidth() + self.options.padding
self.HeaderHeight = math.max(self.HeaderHeight, columnHeader:GetHeight())
elseif (growDirection == "top" or growDirection == "bottom") then
self.HeaderWidth = math.max(self.HeaderWidth, columnHeader:GetWidth())
self.HeaderHeight = self.HeaderHeight + columnHeader:GetHeight() + self.options.padding
end
local bShowColumnHeaderReziser = self.options.reziser_shown
if (bShowColumnHeaderReziser) then
local resizerButton = columnHeader.resizerButton
resizerButton:Show()
resizerButton.texture:SetVertexColor(unpack(self.options.reziser_color))
resizerButton:SetWidth(self.options.reziser_width)
resizerButton:SetHeight(columnHeader:GetHeight())
else
columnHeader.resizerButton:Hide()
end
columnHeader:Show()
columnHeader.bInUse = true
columnHeader.columnData = columnData
end,
---reset column header backdrop
---@param self df_headerframe
---@param columnHeader df_headercolumnframe
ResetColumnHeaderBackdrop = function(self, columnHeader)
columnHeader:SetBackdrop(self.options.header_backdrop)
columnHeader:SetBackdropColor(unpack(self.options.header_backdrop_color))
columnHeader:SetBackdropBorderColor(unpack(self.options.header_backdrop_border_color))
end,
---@param self df_headerframe
---@param columnHeader df_headercolumnframe
SetBackdropColorForSelectedColumnHeader = function(self, columnHeader)
columnHeader:SetBackdropColor(unpack(self.options.header_backdrop_color_selected))
end,
---clear the column header
---@param self df_headerframe
---@param columnHeader df_headercolumnframe
ClearColumnHeader = function(self, columnHeader)
columnHeader:SetSize(self.options.header_width, self.options.header_height)
self:ResetColumnHeaderBackdrop(columnHeader)
columnHeader:ClearAllPoints()
columnHeader.Icon:SetTexture("")
columnHeader.Icon:Hide()
columnHeader.Text:SetText("")
columnHeader.Text:Hide()
end,
---get the next column header, create one if doesn't exists
---@param self df_headerframe
GetNextHeader = function(self)
local nextHeader = self.NextHeader
local columnHeader = self.columnHeadersCreated[nextHeader]
if (not columnHeader) then
--create a new column header
---@type df_headercolumnframe
columnHeader = CreateFrame("button", "$parentHeaderIndex" .. nextHeader, self, "BackdropTemplate")
columnHeader:SetScript("OnClick", detailsFramework.HeaderFunctions.OnClick)
columnHeader:SetMovable(true)
columnHeader:SetResizable(true)
--header icon
detailsFramework:CreateImage(columnHeader, "", self.options.header_height, self.options.header_height, "ARTWORK", nil, "Icon", "$parentIcon")
--header separator
detailsFramework:CreateImage(columnHeader, "", 1, 1, "ARTWORK", nil, "Separator", "$parentSeparator")
--header name text
detailsFramework:CreateLabel(columnHeader, "", self.options.text_size, self.options.text_color, "GameFontNormal", "Text", "$parentText", "ARTWORK")
--header selected and order icon
detailsFramework:CreateImage(columnHeader, self.options.arrow_up_texture, 12, 12, "ARTWORK", nil, "Arrow", "$parentArrow")
---rezise button
---@type df_headerresizer
local resizerButton = CreateFrame("button", "$parentResizer", columnHeader)
resizerButton:SetWidth(4)
resizerButton:SetFrameLevel(columnHeader:GetFrameLevel()+2)
resizerButton:SetPoint("topright", columnHeader, "topright", -1, -1)
resizerButton:SetPoint("bottomright", columnHeader, "bottomright", -1, 1)
resizerButton:EnableMouse(true)
resizerButton:RegisterForClicks("LeftButtonDown", "LeftButtonUp")
columnHeader.resizerButton = resizerButton
resizerButton:SetScript("OnEnter", function()
resizerButton.texture:SetVertexColor(1, 1, 1, 0.9)
end)
resizerButton:SetScript("OnLeave", function()
resizerButton.texture:SetVertexColor(unpack(self.options.reziser_color))
end)
resizerButton:SetScript("OnMouseDown", function() --move this to a single function
if (not columnHeader.bIsRezising) then
--get the string length to know the min size
local textLength = columnHeader.Text:GetStringWidth() + 6
columnHeader:SetResizeBounds(math.max(textLength, self.options.reziser_min_width), columnHeader:GetHeight(), self.options.reziser_max_width, columnHeader:GetHeight())
columnHeader.bIsRezising = true
columnHeader:StartSizing("right")
end
end)
resizerButton:SetScript("OnMouseUp", function()
if (columnHeader.bIsRezising) then
columnHeader.bIsRezising = false
columnHeader:StopMovingOrSizing()
--callback or modify into a passed by table?
if (self.OnColumnSettingChangeCallback) then --need to get the header name
local columnName = columnHeader.columnData.name
xpcall(self.OnColumnSettingChangeCallback, geterrorhandler(), self, "width", columnName, columnHeader:GetWidth())
end
end
end)
resizerButton:SetScript("OnHide", function()
if (columnHeader.bIsRezising) then
columnHeader:StopMovingOrSizing()
columnHeader.bIsRezising = false
end
end)
resizerButton.texture = resizerButton:CreateTexture(nil, "overlay")
resizerButton.texture:SetAllPoints()
resizerButton.texture:SetColorTexture(1, 1, 1, 1)
local xOffset = self.options.reziser_shown and -5 or -1
columnHeader.Arrow:SetPoint("right", columnHeader, "right", xOffset, 0)
columnHeader.Separator:Hide()
columnHeader.Arrow:Hide()
self:UpdateSortArrow(columnHeader, false, "DESC")
table.insert(self.columnHeadersCreated, columnHeader)
columnHeader = columnHeader
end
self:ClearColumnHeader(columnHeader)
self.NextHeader = self.NextHeader + 1
return columnHeader
end,
---return a header button by passing its name (.name on the column table)
---@param self df_headerframe
---@param columnName string
---@return df_headercolumnframe|nil
GetHeaderColumnByName = function(self, columnName)
for _, headerColumnFrame in ipairs(self.columnHeadersCreated) do
if (headerColumnFrame.columnData.name == columnName) then
return headerColumnFrame
end
end
end,
NextHeader = 1,
HeaderWidth = 0,
HeaderHeight = 0,
}
--default options
local default_header_options = {
backdrop = {edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true},
backdrop_color = {0, 0, 0, 0.2},
backdrop_border_color = {0.1, 0.1, 0.1, .2},
text_color = {1, 1, 1, 1},
text_size = 10,
text_shadow = false,
grow_direction = "RIGHT",
padding = 2,
reziser_shown = false, --make sure to set the callback function with: header:SetOnColumnResizeScript(callbackFunction)
reziser_width = 2,
reziser_color = {1, 0.6, 0, 0.6},
reziser_min_width = 16,
reziser_max_width = 200,
--each piece of the header
header_backdrop = {bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true},
header_backdrop_color = {0, 0, 0, 0.5},
header_backdrop_color_selected = {0.3, 0.3, 0.3, 0.5},
header_backdrop_border_color = {0, 0, 0, 0},
header_width = 120,
header_height = 20,
arrow_up_texture = [[Interface\Buttons\Arrow-Up-Down]],
arrow_up_texture_coords = {0, 1, 6/16, 1},
arrow_up_size = {12, 11},
arrow_down_texture = [[Interface\Buttons\Arrow-Down-Down]],
arrow_down_texture_coords = {0, 1, 0, 11/16},
arrow_down_size = {12, 11},
arrow_alpha = 0.659,
use_line_separators = false,
line_separator_color = {.1, .1, .1, .6},
line_separator_width = 1,
line_separator_height = 200,
line_separator_gap_align = false,
}
---create a df_headerframe, alias 'header'.
---a header is a frame that can hold multiple columns which are also frames, each column is a df_headercolumnframe, these columns are arranged in horizontal form.
---a header is used to organize columns giving them a name/title, a way to sort and align them.
---each column is placed on the right side of the previous column.
---@param parent frame
---@param headerTable table
---@param options table|nil
---@param frameName string|nil
---@return df_headerframe
function detailsFramework:CreateHeader(parent, headerTable, options, frameName)
---create the header frame which is returned by this function
---@type df_headerframe
local newHeader = CreateFrame("frame", frameName or "$parentHeaderLine", parent, "BackdropTemplate")
detailsFramework:Mixin(newHeader, detailsFramework.OptionsFunctions)
detailsFramework:Mixin(newHeader, detailsFramework.HeaderMixin)
newHeader:BuildOptionsTable(default_header_options, options)
--set the backdrop and backdrop color following the values in the options table
newHeader:SetBackdrop(newHeader.options.backdrop)
newHeader:SetBackdropColor(unpack(newHeader.options.backdrop_color))
newHeader:SetBackdropBorderColor(unpack(newHeader.options.backdrop_border_color))
newHeader:SetHeaderTable(headerTable)
return newHeader
end
--[=[example:
C_Timer.After(1, function()
local parent = UIParent
--declare the columns the headerFrame will have
---@type df_headercolumndata[]
local headerTable = {
{name = "playername", text = "Player Name", width = 120, align = "left", canSort = true},
{name = "damage", text = "Damage Done", width = 80, align = "right", canSort = true},
{name = "points", text = "Total Points", width = 80, align = "right", canSort = false},
}
local frameName = "MyAddOnOptionsFrame"
local options = {}
local headerFrame = DetailsFramework:CreateHeader(parent, headerTable, options, frameName)
headerFrame:SetPoint("center", parent, "center", 10, -10)
end)
--]=]
@@ -0,0 +1,123 @@
local DF = _G ["DetailsFramework"]
if (not DF or not DetailsFrameworkCanLoad) then
return
end
local _
local rawset = rawset --lua local
local rawget = rawget --lua local
local APIHelpFunctions = false
local HelpMetaFunctions = {}
local get_members_function_index = {}
HelpMetaFunctions.__index = function(_table, _member_requested)
local func = get_members_function_index [_member_requested]
if (func) then
return func (_table, _member_requested)
end
local fromMe = rawget (_table, _member_requested)
if (fromMe) then
return fromMe
end
return HelpMetaFunctions [_member_requested]
end
local set_members_function_index = {}
HelpMetaFunctions.__newindex = function(_table, _key, _value)
local func = set_members_function_index [_key]
if (func) then
return func (_table, _value)
else
return rawset (_table, _key, _value)
end
end
function HelpMetaFunctions:AddHelp (width, height, x, y, buttonX, buttonY, text, anchor)
self.helpTable [#self.helpTable + 1] = {
HighLightBox = {x = x, y = y, width = width, height = height},
ButtonPos = { x = buttonX, y = buttonY},
ToolTipDir = anchor or "RIGHT",
ToolTipText = text
}
end
function HelpMetaFunctions:SetPoint(v1, v2, v3, v4, v5)
v1, v2, v3, v4, v5 = DF:CheckPoints (v1, v2, v3, v4, v5, self)
if (not v1) then
print("Invalid parameter for SetPoint")
return
end
return self.widget:SetPoint(v1, v2, v3, v4, v5)
end
function HelpMetaFunctions:ShowHelp()
if (not HelpPlate_IsShowing (self.helpTable)) then
HelpPlate_Show (self.helpTable, self.frame, self.button, true)
else
HelpPlate_Hide (true)
end
end
local nameCounter = 1
function DF:NewHelp (parent, width, height, x, y, buttonWidth, buttonHeight, name)
local help = {}
if (parent.dframework) then
parent = parent.widget
end
local helpButton = CreateFrame("button", name or "DetailsFrameworkHelpButton"..nameCounter, parent, "MainHelpPlateButton")
nameCounter = nameCounter + 1
if (not APIHelpFunctions) then
APIHelpFunctions = true
local idx = getmetatable(helpButton).__index
for funcName, funcAddress in pairs(idx) do
if (not HelpMetaFunctions [funcName]) then
HelpMetaFunctions [funcName] = function(object, ...)
local x = loadstring ( "return _G."..object.button:GetName()..":"..funcName.."(...)")
return x (...)
end
end
end
end
if (buttonWidth and buttonHeight) then
helpButton:SetWidth(buttonWidth)
helpButton:SetHeight(buttonHeight)
helpButton.I:SetWidth(buttonWidth*0.8)
helpButton.I:SetHeight(buttonHeight*0.8)
helpButton.Ring:SetWidth(buttonWidth)
helpButton.Ring:SetHeight(buttonHeight)
helpButton.Ring:SetPoint("center", buttonWidth*.2, -buttonWidth*.2)
end
help.helpTable = {
FramePos = {x = x, y = y},
FrameSize = {width = width, height = height}
}
help.frame = parent
help.button = helpButton
help.widget = helpButton
help.I = helpButton.I
help.Ring = helpButton.Ring
helpButton:SetScript("OnClick", function()
help:ShowHelp()
end)
setmetatable(help, HelpMetaFunctions)
return help
end
@@ -0,0 +1,487 @@
local detailsFramework = DetailsFramework
if (not detailsFramework or not DetailsFrameworkCanLoad) then
return
end
local unpack = unpack
local CreateFrame = CreateFrame
local PixelUtil = PixelUtil
detailsFramework.IconMixin = {
---create a new icon frame
---@param self frame the parent frame
---@param iconName string the name of the icon frame
---@return frame
CreateIcon = function(self, iconName)
local iconFrame = CreateFrame("frame", iconName, self, "BackdropTemplate")
iconFrame.Texture = iconFrame:CreateTexture(nil, "artwork")
PixelUtil.SetPoint(iconFrame.Texture, "topleft", iconFrame, "topleft", 1, -1)
PixelUtil.SetPoint(iconFrame.Texture, "bottomright", iconFrame, "bottomright", -1, 1)
iconFrame.Border = iconFrame:CreateTexture(nil, "background")
iconFrame.Border:SetAllPoints()
iconFrame.Border:SetColorTexture(0, 0, 0)
iconFrame:SetBackdrop({edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1})
iconFrame:SetBackdropBorderColor(0, 0, 0, 0)
iconFrame:EnableMouse(false)
local cooldownFrame = CreateFrame("cooldown", "$parentCooldown", iconFrame, "CooldownFrameTemplate, BackdropTemplate")
cooldownFrame:SetAllPoints()
cooldownFrame:EnableMouse(false)
cooldownFrame:SetFrameLevel(iconFrame:GetFrameLevel()+1)
iconFrame.Cooldown = cooldownFrame
iconFrame.CountdownText = cooldownFrame:CreateFontString(nil, "overlay", "GameFontNormal")
iconFrame.CountdownText:SetPoint("center", iconFrame, "center", 0, 0)
iconFrame.CountdownText:Hide()
iconFrame.StackText = iconFrame:CreateFontString(nil, "overlay", "GameFontNormal")
iconFrame.StackText:SetPoint("center", iconFrame, "bottomright", 0, 0)
iconFrame.StackText:Hide()
iconFrame.Desc = iconFrame:CreateFontString(nil, "overlay", "GameFontNormal")
iconFrame.Desc:SetPoint("bottom", iconFrame, "top", 0, 2)
iconFrame.Desc:Hide()
return iconFrame
end,
GetIcon = function(self)
local iconFrame = self.IconPool[self.NextIcon]
if (not iconFrame) then
local newIconFrame = self:CreateIcon("$parentIcon" .. self.NextIcon)
newIconFrame.parentIconRow = self
newIconFrame.Cooldown:SetHideCountdownNumbers(self.options.surpress_blizzard_cd_timer)
newIconFrame.Cooldown.noCooldownCount = self.options.surpress_tulla_omni_cc
newIconFrame.CountdownText:ClearAllPoints()
newIconFrame.CountdownText:SetPoint(self.options.text_anchor or "center", iconFrame, self.options.text_rel_anchor or "center", self.options.text_x_offset or 0, self.options.text_y_offset or 0)
newIconFrame.StackText:ClearAllPoints()
newIconFrame.StackText:SetPoint(self.options.stack_text_anchor or "center", iconFrame, self.options.stack_text_rel_anchor or "bottomright", self.options.stack_text_x_offset or 0, self.options.stack_text_y_offset or 0)
newIconFrame.Desc:ClearAllPoints()
newIconFrame.Desc:SetPoint(self.options.desc_text_anchor or "bottom", iconFrame, self.options.desc_text_rel_anchor or "top", self.options.desc_text_x_offset or 0, self.options.desc_text_y_offset or 2)
self.IconPool[self.NextIcon] = newIconFrame
iconFrame = newIconFrame
end
iconFrame:ClearAllPoints()
local anchor = self.options.anchor
local anchorTo = self.NextIcon == 1 and self or self.IconPool[self.NextIcon - 1]
local xPadding = self.NextIcon == 1 and self.options.left_padding or self.options.icon_padding or 1
local growDirection = self.options.grow_direction
if (growDirection == 1) then --grow to right
if (self.NextIcon == 1) then
PixelUtil.SetPoint(iconFrame, "left", anchorTo, "left", xPadding, 0)
else
PixelUtil.SetPoint(iconFrame, "left", anchorTo, "right", xPadding, 0)
end
elseif (growDirection == 2) then --grow to left
if (self.NextIcon == 1) then
PixelUtil.SetPoint(iconFrame, "right", anchorTo, "right", xPadding, 0)
else
PixelUtil.SetPoint(iconFrame, "right", anchorTo, "left", xPadding, 0)
end
end
detailsFramework:SetFontColor(iconFrame.CountdownText, self.options.text_color)
self.NextIcon = self.NextIcon + 1
return iconFrame
end,
--adds only if not existing already in the cache
AddSpecificIcon = function(self, identifierKey, spellId, borderColor, startTime, duration, forceTexture, descText, count, debuffType, caster, canStealOrPurge, spellName, isBuff)
if (not identifierKey or identifierKey == "") then
return
end
if (not self.AuraCache[identifierKey]) then
local icon = self:SetIcon(spellId, borderColor, startTime, duration, forceTexture, descText, count, debuffType, caster, canStealOrPurge, spellName, isBuff or false)
icon.identifierKey = identifierKey
self.AuraCache[identifierKey] = true
end
end,
SetIcon = function(self, spellId, borderColor, startTime, duration, forceTexture, descText, count, debuffType, caster, canStealOrPurge, spellName, isBuff, modRate)
local actualSpellName, _, spellIcon = GetSpellInfo(spellId)
if forceTexture then
spellIcon = forceTexture
end
spellName = spellName or actualSpellName or "unknown_aura"
modRate = modRate or 1
if (spellIcon) then
local iconFrame = self:GetIcon()
iconFrame.Texture:SetTexture(spellIcon)
iconFrame.Texture:SetTexCoord(unpack(self.options.texcoord))
if (borderColor) then
iconFrame:SetBackdropBorderColor(detailsFramework:ParseColors(borderColor))
else
iconFrame:SetBackdropBorderColor(0, 0, 0 ,0)
end
if (startTime) then
CooldownFrame_Set(iconFrame.Cooldown, startTime, duration, true, true, modRate)
if (self.options.show_text) then
iconFrame.CountdownText:Show()
local now = GetTime()
iconFrame.timeRemaining = (startTime + duration - now) / modRate
iconFrame.expirationTime = startTime + duration
local formattedTime = (iconFrame.timeRemaining > 0) and self.options.decimal_timer and iconFrame.parentIconRow.FormatCooldownTimeDecimal(iconFrame.timeRemaining) or iconFrame.parentIconRow.FormatCooldownTime(iconFrame.timeRemaining) or ""
iconFrame.CountdownText:SetText(formattedTime)
iconFrame.CountdownText:SetPoint(self.options.text_anchor or "center", iconFrame, self.options.text_rel_anchor or "center", self.options.text_x_offset or 0, self.options.text_y_offset or 0)
detailsFramework:SetFontSize(iconFrame.CountdownText, self.options.text_size)
detailsFramework:SetFontFace (iconFrame.CountdownText, self.options.text_font)
detailsFramework:SetFontOutline (iconFrame.CountdownText, self.options.text_outline)
if self.options.on_tick_cooldown_update then
iconFrame.lastUpdateCooldown = now
iconFrame:SetScript("OnUpdate", self.OnIconTick)
else
iconFrame:SetScript("OnUpdate", nil)
end
else
iconFrame:SetScript("OnUpdate", nil)
iconFrame.CountdownText:Hide()
end
iconFrame.Cooldown:SetReverse(self.options.cooldown_reverse)
iconFrame.Cooldown:SetDrawSwipe(self.options.cooldown_swipe_enabled)
iconFrame.Cooldown:SetEdgeTexture(self.options.cooldown_edge_texture)
iconFrame.Cooldown:SetHideCountdownNumbers(self.options.surpress_blizzard_cd_timer)
else
iconFrame.timeRemaining = nil
iconFrame.expirationTime = nil
iconFrame:SetScript("OnUpdate", nil)
iconFrame.CountdownText:Hide()
end
if (descText and self.options.desc_text) then
iconFrame.Desc:Show()
iconFrame.Desc:SetText(descText.text)
iconFrame.Desc:SetTextColor(detailsFramework:ParseColors(descText.text_color or self.options.desc_text_color))
iconFrame.Desc:SetPoint(self.options.desc_text_anchor or "bottom", iconFrame, self.options.desc_text_rel_anchor or "top", self.options.desc_text_x_offset or 0, self.options.desc_text_y_offset or 2)
detailsFramework:SetFontSize(iconFrame.Desc, descText.text_size or self.options.desc_text_size)
detailsFramework:SetFontFace(iconFrame.Desc, self.options.desc_text_font)
detailsFramework:SetFontOutline(iconFrame.Desc, self.options.desc_text_outline)
else
iconFrame.Desc:Hide()
end
if (count and count > 1 and self.options.stack_text) then
iconFrame.StackText:Show()
iconFrame.StackText:SetText(count)
iconFrame.StackText:SetTextColor(detailsFramework:ParseColors(self.options.stack_text_color))
iconFrame.StackText:SetPoint(self.options.stack_text_anchor or "center", iconFrame, self.options.stack_text_rel_anchor or "bottomright", self.options.stack_text_x_offset or 0, self.options.stack_text_y_offset or 0)
detailsFramework:SetFontSize(iconFrame.StackText, self.options.stack_text_size)
detailsFramework:SetFontFace(iconFrame.StackText, self.options.stack_text_font)
detailsFramework:SetFontOutline(iconFrame.StackText, self.options.stack_text_outline)
else
iconFrame.StackText:Hide()
end
PixelUtil.SetSize(iconFrame, self.options.icon_width, self.options.icon_height)
iconFrame:Show()
--update the size of the frame
self:SetWidth((self.options.left_padding * 2) + (self.options.icon_padding * (self.NextIcon-2)) + (self.options.icon_width * (self.NextIcon - 1)))
self:SetHeight(self.options.icon_height + (self.options.top_padding * 2))
--make information available
iconFrame.spellId = spellId
iconFrame.startTime = startTime
iconFrame.duration = duration
iconFrame.count = count
iconFrame.debuffType = debuffType
iconFrame.caster = caster
iconFrame.canStealOrPurge = canStealOrPurge
iconFrame.isBuff = isBuff
iconFrame.spellName = spellName
iconFrame.identifierKey = nil -- only used for "specific" add/remove
--add the spell into the cache
self.AuraCache[spellId or -1] = true
self.AuraCache[spellName] = true
self.AuraCache.canStealOrPurge = self.AuraCache.canStealOrPurge or canStealOrPurge
self.AuraCache.hasEnrage = self.AuraCache.hasEnrage or debuffType == "" --yes, enrages are empty-string...
--show the frame
self:Show()
return iconFrame
end
end,
OnIconTick = function(self, deltaTime)
local now = GetTime()
if (self.lastUpdateCooldown + 0.05) <= now then
self.timeRemaining = self.expirationTime - now
if self.timeRemaining > 0 then
if self.parentIconRow.options.decimal_timer then
self.CountdownText:SetText(self.parentIconRow.FormatCooldownTimeDecimal(self.timeRemaining))
else
self.CountdownText:SetText(self.parentIconRow.FormatCooldownTime(self.timeRemaining))
end
else
self.CountdownText:SetText("")
end
self.lastUpdateCooldown = now
end
end,
FormatCooldownTime = function(formattedTime)
if (formattedTime >= 3600) then
formattedTime = math.floor(formattedTime / 3600) .. "h"
elseif (formattedTime >= 60) then
formattedTime = math.floor(formattedTime / 60) .. "m"
else
formattedTime = math.floor(formattedTime)
end
return formattedTime
end,
FormatCooldownTimeDecimal = function(formattedTime)
if formattedTime < 10 then
return ("%.1f"):format(formattedTime)
elseif formattedTime < 60 then
return ("%d"):format(formattedTime)
elseif formattedTime < 3600 then
return ("%d:%02d"):format(formattedTime/60%60, formattedTime%60)
elseif formattedTime < 86400 then
return ("%dh %02dm"):format(formattedTime/(3600), formattedTime/60%60)
else
return ("%dd %02dh"):format(formattedTime/86400, (formattedTime/3600) - (math.floor(formattedTime/86400) * 24))
end
end,
RemoveSpecificIcon = function(self, identifierKey)
if (not identifierKey or identifierKey == "") then
return
end
table.wipe(self.AuraCache)
local iconPool = self.IconPool
local countStillShown = 0
for i = 1, self.NextIcon -1 do
local iconFrame = iconPool[i]
if (iconFrame.identifierKey and iconFrame.identifierKey == identifierKey) then
iconFrame:Hide()
iconFrame:ClearAllPoints()
iconFrame.identifierKey = nil
else
self.AuraCache[iconFrame.spellId] = true
self.AuraCache[iconFrame.spellName] = true
self.AuraCache.canStealOrPurge = self.AuraCache.canStealOrPurge or iconFrame.canStealOrPurge
self.AuraCache.hasEnrage = self.AuraCache.hasEnrage or iconFrame.debuffType == "" --yes, enrages are empty-string...
countStillShown = countStillShown + 1
end
end
self:AlignAuraIcons()
end,
ClearIcons = function(self, resetBuffs, resetDebuffs)
resetBuffs = resetBuffs ~= false
resetDebuffs = resetDebuffs ~= false
table.wipe(self.AuraCache)
local iconPool = self.IconPool
for i = 1, self.NextIcon -1 do
local iconFrame = iconPool[i]
if (iconFrame.isBuff == nil) then
iconFrame:Hide()
iconFrame:ClearAllPoints()
elseif (resetBuffs and iconFrame.isBuff) then
iconFrame:Hide()
iconFrame:ClearAllPoints()
elseif (resetDebuffs and not iconFrame.isBuff) then
iconFrame:Hide()
iconFrame:ClearAllPoints()
else
self.AuraCache[iconFrame.spellId] = true
self.AuraCache[iconFrame.spellName] = true
self.AuraCache.canStealOrPurge = self.AuraCache.canStealOrPurge or iconFrame.canStealOrPurge
self.AuraCache.hasEnrage = self.AuraCache.hasEnrage or iconFrame.debuffType == "" --yes, enrages are empty-string...
end
end
self:AlignAuraIcons()
end,
AlignAuraIcons = function(self)
local iconPool = self.IconPool
local iconAmount = #iconPool
local countStillShown = 0
table.sort(iconPool, function(i1, i2) return i1:IsShown() and not i2:IsShown() end)
if iconAmount == 0 then
self:Hide()
else
--re-anchor not hidden
for i = 1, iconAmount do
local iconFrame = iconPool[i]
local anchor = self.options.anchor
local anchorTo = i == 1 and self or self.IconPool[i - 1]
local xPadding = i == 1 and self.options.left_padding or self.options.icon_padding or 1
local growDirection = self.options.grow_direction
countStillShown = countStillShown + (iconFrame:IsShown() and 1 or 0)
iconFrame:ClearAllPoints()
if (growDirection == 1) then --grow to right
if (i == 1) then
PixelUtil.SetPoint(iconFrame, "left", anchorTo, "left", xPadding, 0)
else
PixelUtil.SetPoint(iconFrame, "left", anchorTo, "right", xPadding, 0)
end
elseif (growDirection == 2) then --grow to left
if (i == 1) then
PixelUtil.SetPoint(iconFrame, "right", anchorTo, "right", xPadding, 0)
else
PixelUtil.SetPoint(iconFrame, "right", anchorTo, "left", xPadding, 0)
end
end
end
end
self.NextIcon = countStillShown + 1
end,
GetIconGrowDirection = function(self)
local side = self.options.anchor.side
if (side == 1) then
return 1
elseif (side == 2) then
return 2
elseif (side == 3) then
return 1
elseif (side == 4) then
return 1
elseif (side == 5) then
return 2
elseif (side == 6) then
return 1
elseif (side == 7) then
return 2
elseif (side == 8) then
return 1
elseif (side == 9) then
return 1
elseif (side == 10) then
return 1
elseif (side == 11) then
return 2
elseif (side == 12) then
return 1
elseif (side == 13) then
return 1
end
end,
OnOptionChanged = function(self, optionName)
self:SetBackdropColor(unpack(self.options.backdrop_color))
self:SetBackdropBorderColor(unpack(self.options.backdrop_border_color))
end,
}
local default_icon_row_options = {
icon_width = 20,
icon_height = 20,
texcoord = {.1, .9, .1, .9},
show_text = true,
text_color = {1, 1, 1, 1},
text_size = 12,
text_font = "Arial Narrow",
text_outline = "NONE",
text_anchor = "center",
text_rel_anchor = "center",
text_x_offset = 0,
text_y_offset = 0,
desc_text = true,
desc_text_color = {1, 1, 1, 1},
desc_text_size = 7,
desc_text_font = "Arial Narrow",
desc_text_outline = "NONE",
desc_text_anchor = "bottom",
desc_text_rel_anchor = "top",
desc_text_x_offset = 0,
desc_text_y_offset = 2,
stack_text = true,
stack_text_color = {1, 1, 1, 1},
stack_text_size = 10,
stack_text_font = "Arial Narrow",
stack_text_outline = "NONE",
stack_text_anchor = "center",
stack_text_rel_anchor = "bottomright",
stack_text_x_offset = 0,
stack_text_y_offset = 0,
left_padding = 1, --distance between right and left
top_padding = 1, --distance between top and bottom
icon_padding = 1, --distance between each icon
backdrop = {},
backdrop_color = {0, 0, 0, 0.5},
backdrop_border_color = {0, 0, 0, 1},
anchor = {side = 6, x = 2, y = 0},
grow_direction = 1, --1 = to right 2 = to left
surpress_blizzard_cd_timer = false,
surpress_tulla_omni_cc = false,
on_tick_cooldown_update = true,
decimal_timer = false,
cooldown_reverse = false,
cooldown_swipe_enabled = true,
cooldown_edge_texture = "Interface\\Cooldown\\edge",
}
function detailsFramework:CreateIconRow(parent, name, options)
local newIconRowFrame = CreateFrame("frame", name, parent, "BackdropTemplate")
newIconRowFrame.IconPool = {}
newIconRowFrame.NextIcon = 1
newIconRowFrame.AuraCache = {}
detailsFramework:Mixin(newIconRowFrame, detailsFramework.IconMixin)
detailsFramework:Mixin(newIconRowFrame, detailsFramework.OptionsFunctions)
newIconRowFrame:BuildOptionsTable(default_icon_row_options, options)
newIconRowFrame:SetSize(newIconRowFrame.options.icon_width, newIconRowFrame.options.icon_height + (newIconRowFrame.options.top_padding * 2))
newIconRowFrame:SetBackdrop(newIconRowFrame.options.backdrop)
newIconRowFrame:SetBackdropColor(unpack(newIconRowFrame.options.backdrop_color))
newIconRowFrame:SetBackdropBorderColor(unpack(newIconRowFrame.options.backdrop_border_color))
return newIconRowFrame
end
@@ -0,0 +1,27 @@
local detailsFramework = _G["DetailsFramework"]
if (not detailsFramework or not DetailsFrameworkCanLoad) then
return
end
--namespace
detailsFramework.Items = {}
local containerAPIVersion = 1
if (detailsFramework.IsDragonflightAndBeyond()) then
containerAPIVersion = 2
end
function detailsFramework.Items.GetContainerItemInfo(containerIndex, slotIndex)
if (containerAPIVersion == 2 and C_Container and C_Container.GetContainerItemInfo) then
local itemInfo = C_Container.GetContainerItemInfo(containerIndex, slotIndex)
return itemInfo.iconFileID, itemInfo.stackCount, itemInfo.isLocked, itemInfo.quality, itemInfo.isReadable, itemInfo.hasLoot, itemInfo.hyperlink, itemInfo.isFiltered, itemInfo.hasNoValue, itemInfo.itemID, itemInfo.isBound
else
return GetContainerItemInfo(containerIndex, slotIndex)
end
end
function detailsFramework.Items.IsItemSoulbound(containerIndex, slotIndex)
local bIsBound = select(11, detailsFramework.Items.GetContainerItemInfo(containerIndex, slotIndex))
return bIsBound
end
@@ -0,0 +1,341 @@
local detailsFramework = _G["DetailsFramework"]
if (not detailsFramework or not DetailsFrameworkCanLoad) then
return
end
local _
local loadedAPILabelFunctions = false
do
local metaPrototype = {
WidgetType = "label",
dversion = detailsFramework.dversion,
}
--check if there's a metaPrototype already existing
if (_G[detailsFramework.GlobalWidgetControlNames["label"]]) then
--get the already existing metaPrototype
local oldMetaPrototype = _G[detailsFramework.GlobalWidgetControlNames ["label"]]
--check if is older
if ( (not oldMetaPrototype.dversion) or (oldMetaPrototype.dversion < detailsFramework.dversion) ) then
--the version is older them the currently loading one
--copy the new values into the old metatable
for funcName, _ in pairs(metaPrototype) do
oldMetaPrototype[funcName] = metaPrototype[funcName]
end
end
else
--first time loading the framework
_G[detailsFramework.GlobalWidgetControlNames ["label"]] = metaPrototype
end
end
local LabelMetaFunctions = _G[detailsFramework.GlobalWidgetControlNames ["label"]]
detailsFramework:Mixin(LabelMetaFunctions, detailsFramework.SetPointMixin)
detailsFramework:Mixin(LabelMetaFunctions, detailsFramework.ScriptHookMixin)
------------------------------------------------------------------------------------------------------------
--metatables
LabelMetaFunctions.__call = function(object, value)
return object.label:SetText(value)
end
------------------------------------------------------------------------------------------------------------
--members
--get text
local gmember_text = function(object)
return object.label:GetText()
end
--text width
local gmember_width = function(object)
return object.label:GetStringWidth()
end
--text height
local gmember_height = function(object)
return object.label:GetStringHeight()
end
--text color
local gmember_textcolor = function(object)
return object.label:GetTextColor()
end
--text font
local gmember_textfont = function(object)
local fontface = object.label:GetFont()
return fontface
end
--text size
local gmember_textsize = function(object)
local _, fontsize = object.label:GetFont()
return fontsize
end
LabelMetaFunctions.GetMembers = LabelMetaFunctions.GetMembers or {}
detailsFramework:Mixin(LabelMetaFunctions.GetMembers, detailsFramework.LayeredRegionMetaFunctionsGet)
detailsFramework:Mixin(LabelMetaFunctions.GetMembers, detailsFramework.DefaultMetaFunctionsGet)
LabelMetaFunctions.GetMembers["width"] = gmember_width
LabelMetaFunctions.GetMembers["height"] = gmember_height
LabelMetaFunctions.GetMembers["text"] = gmember_text
LabelMetaFunctions.GetMembers["fontcolor"] = gmember_textcolor
LabelMetaFunctions.GetMembers["fontface"] = gmember_textfont
LabelMetaFunctions.GetMembers["fontsize"] = gmember_textsize
LabelMetaFunctions.GetMembers["textcolor"] = gmember_textcolor --alias
LabelMetaFunctions.GetMembers["textfont"] = gmember_textfont --alias
LabelMetaFunctions.GetMembers["textsize"] = gmember_textsize --alias
LabelMetaFunctions.__index = function(object, key)
local func = LabelMetaFunctions.GetMembers[key]
if (func) then
return func(object, key)
end
local alreadyHaveKey = rawget(object, key)
if (alreadyHaveKey) then
return alreadyHaveKey
end
return LabelMetaFunctions[key]
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--text
local smember_text = function(object, value)
return object.label:SetText(value)
end
--text color
local smember_textcolor = function(object, value)
local value1, value2, value3, value4 = detailsFramework:ParseColors(value)
return object.label:SetTextColor(value1, value2, value3, value4)
end
--text font
local smember_textfont = function(object, value)
return detailsFramework:SetFontFace(object.label, value)
end
--text size
local smember_textsize = function(object, value)
return detailsFramework:SetFontSize(object.label, value)
end
--text align
local smember_textalign = function(object, value)
if (value == "<") then
value = "left"
elseif (value == ">") then
value = "right"
elseif (value == "|") then
value = "center"
end
return object.label:SetJustifyH(value)
end
--text valign
local smember_textvalign = function(object, value)
if (value == "^") then
value = "top"
elseif (value == "_") then
value = "bottom"
elseif (value == "|") then
value = "middle"
end
return object.label:SetJustifyV(value)
end
--field size width
local smember_width = function(object, value)
return object.label:SetWidth(value)
end
--field size height
local smember_height = function(object, value)
return object.label:SetHeight(value)
end
--outline (shadow)
local smember_outline = function(object, value)
detailsFramework:SetFontOutline(object.label, value)
end
--text rotation
local smember_rotation = function(object, rotation)
if (type(rotation) == "number") then
if (not object.__rotationAnimation) then
object.__rotationAnimation = detailsFramework:CreateAnimationHub(object.label)
object.__rotationAnimation.rotator = detailsFramework:CreateAnimation(object.__rotationAnimation, "rotation", 1, 0, 0)
object.__rotationAnimation.rotator:SetEndDelay(10^8)
object.__rotationAnimation.rotator:SetSmoothProgress(1)
end
object.__rotationAnimation.rotator:SetDegrees(rotation)
object.__rotationAnimation:Play()
object.__rotationAnimation:Pause()
end
end
LabelMetaFunctions.SetMembers = LabelMetaFunctions.SetMembers or {}
detailsFramework:Mixin(LabelMetaFunctions.SetMembers, detailsFramework.LayeredRegionMetaFunctionsSet)
detailsFramework:Mixin(LabelMetaFunctions.SetMembers, detailsFramework.DefaultMetaFunctionsSet)
LabelMetaFunctions.SetMembers["align"] = smember_textalign
LabelMetaFunctions.SetMembers["valign"] = smember_textvalign
LabelMetaFunctions.SetMembers["text"] = smember_text
LabelMetaFunctions.SetMembers["width"] = smember_width
LabelMetaFunctions.SetMembers["height"] = smember_height
LabelMetaFunctions.SetMembers["fontcolor"] = smember_textcolor
LabelMetaFunctions.SetMembers["color"] = smember_textcolor--alias
LabelMetaFunctions.SetMembers["fontface"] = smember_textfont
LabelMetaFunctions.SetMembers["fontsize"] = smember_textsize
LabelMetaFunctions.SetMembers["textcolor"] = smember_textcolor--alias
LabelMetaFunctions.SetMembers["textfont"] = smember_textfont--alias
LabelMetaFunctions.SetMembers["textsize"] = smember_textsize--alias
LabelMetaFunctions.SetMembers["shadow"] = smember_outline
LabelMetaFunctions.SetMembers["outline"] = smember_outline--alias
LabelMetaFunctions.SetMembers["rotation"] = smember_rotation
LabelMetaFunctions.__newindex = function(object, key, value)
local func = LabelMetaFunctions.SetMembers[key]
if (func) then
return func(object, value)
else
return rawset(object, key, value)
end
end
------------------------------------------------------------------------------------------------------------
--methods
--text text
function LabelMetaFunctions:SetTextTruncated(text, maxWidth)
self.widget:SetText(text)
detailsFramework:TruncateText(self.widget, maxWidth)
end
--textcolor
function LabelMetaFunctions:SetTextColor(r, g, b, a)
r, g, b, a = detailsFramework:ParseColors(r, g, b, a)
return self.label:SetTextColor(r, g, b, a)
end
------------------------------------------------------------------------------------------------------------
--template
function LabelMetaFunctions:SetTemplate(template)
if (template.size) then
detailsFramework:SetFontSize(self.label, template.size)
end
if (template.color) then
local r, g, b, a = detailsFramework:ParseColors(template.color)
self:SetTextColor(r, g, b, a)
end
if (template.font) then
local SharedMedia = LibStub:GetLibrary("LibSharedMedia-3.0")
local font = SharedMedia:Fetch("font", template.font)
detailsFramework:SetFontFace(self.label, font)
end
end
------------------------------------------------------------------------------------------------------------
--object constructor
function detailsFramework:CreateLabel(parent, text, size, color, font, member, name, layer)
return detailsFramework:NewLabel(parent, nil, name, member, text, font, size, color, layer)
end
function detailsFramework:NewLabel(parent, container, name, member, text, font, size, color, layer)
if (not parent) then
return error("Details! Framework: parent not found.", 2)
end
if (not container) then
container = parent
end
if (not name) then
name = "DetailsFrameworkLabelNumber" .. detailsFramework.LabelNameCounter
detailsFramework.LabelNameCounter = detailsFramework.LabelNameCounter + 1
end
if (name:find("$parent")) then
local parentName = detailsFramework.GetParentName(parent)
name = name:gsub("$parent", parentName)
end
local labelObject = {type = "label", dframework = true}
if (member) then
parent[member] = labelObject
end
if (parent.dframework) then
parent = parent.widget
end
if (container.dframework) then
container = container.widget
end
if (not font or font == "") then
font = "GameFontNormal"
end
labelObject.label = parent:CreateFontString(name, layer or "OVERLAY", font)
labelObject.widget = labelObject.label
labelObject.label.MyObject = labelObject
if (not loadedAPILabelFunctions) then
loadedAPILabelFunctions = true
local idx = getmetatable(labelObject.label).__index
for funcName, funcAddress in pairs(idx) do
if (not LabelMetaFunctions[funcName]) then
LabelMetaFunctions[funcName] = function(object, ...)
local x = loadstring( "return _G['"..object.label:GetName().."']:"..funcName.."(...)")
return x(...)
end
end
end
end
--if the text is a table, it means a language table has been passed
if (type(text) == "table") then
local locTable = text
if (detailsFramework.Language.IsLocTable(locTable)) then
detailsFramework.Language.SetTextWithLocTable(labelObject.widget, locTable)
else
labelObject.label:SetText(text)
end
else
labelObject.label:SetText(text)
end
labelObject.label:SetJustifyH("left")
if (color) then
local r, g, b, a = detailsFramework:ParseColors(color)
labelObject.label:SetTextColor(r, g, b, a)
end
if (size and type(size) == "number") then
detailsFramework:SetFontSize(labelObject.label, size)
end
labelObject.HookList = {}
setmetatable(labelObject, LabelMetaFunctions)
--if template has been passed as the third parameter
if (size and type(size) == "table") then
labelObject:SetTemplate(size)
end
return labelObject
end
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,40 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/..\FrameXML\UI.xsd">
<Script file ="DFPixelUtil.lua"/>
<Script file="fw.lua"/>
<Script file="mixins.lua"/>
<Script file="util.lua"/>
<Script file="header.lua"/>
<Script file="containers.lua"/>
<Script file="iteminfo.lua"/>
<Script file="addon.lua"/>
<Script file="colors.lua"/>
<Script file="help.lua"/>
<Script file="schedules.lua"/>
<Script file="label.lua"/>
<Script file="picture.lua"/>
<Script file="slider.lua"/>
<Script file="scrollbar.lua"/>
<Script file="spells.lua"/>
<Script file="math.lua"/>
<Script file="savedvars.lua"/>
<Script file="languages.lua"/>
<Script file="timebar.lua"/>
<Script file="charts.lua"/>
<Script file="scripting.lua"/>
<Script file="externals.lua"/>
<Include file="tutorial_alert.xml"/>
<Include file="split_bar.xml"/>
<Include file="textentry.xml"/>
<Include file="button.xml"/>
<Include file="cooltip.xml"/>
<Include file="dropdown.xml"/>
<Include file="normal_bar.xml"/>
<Include file="panel.xml"/>
<Script file="icon.lua"/>
<Script file="pictureedit.lua"/>
<Script file="auras.lua"/>
<Script file="tabcontainer.lua"/>
</Ui>
@@ -0,0 +1,176 @@
local DF = _G["DetailsFramework"]
if (not DF or not DetailsFrameworkCanLoad) then
return
end
local UnitExists = UnitExists
local atan2 = math.atan2
local pi = math.pi
local abs = math.abs
SMALL_FLOAT = 0.000001
--find distance between two players
function DF:GetDistance_Unit(unit1, unit2)
if (UnitExists(unit1) and UnitExists(unit2)) then
local u1X, u1Y = UnitPosition(unit1)
local u2X, u2Y = UnitPosition(unit2)
local dX = u2X - u1X
local dY = u2Y - u1Y
return ((dX*dX) + (dY*dY)) ^ .5
end
return 0
end
--find distance between two points
function DF:GetDistance_Point(x1, y1, x2, y2)
local dx = x2 - x1
local dy = y2 - y1
return ((dx * dx) + (dy * dy)) ^ .5
end
--find a rotation for an object from a point to another point
function DF:FindLookAtRotation(x1, y1, x2, y2)
return atan2 (y2 - y1, x2 - x1) + pi
end
--find the value scale between two given values. e.g: value of 500 in a range 0-100 result in 10 in a scale for 0-10
function DF:MapRangeClamped(inputX, inputY, outputX, outputY, value)
return DF:GetRangeValue(outputX, outputY, Clamp(DF:GetRangePercent(inputX, inputY, value), 0, 1))
end
--find the value scale between two given values. e.g: value of 75 in a range 0-100 result in 7.5 in a scale for 0-10
function DF:MapRangeUnclamped(inputX, inputY, outputX, outputY, value)
return DF:GetRangeValue(outputX, outputY, DF:GetRangePercent(inputX, inputY, value))
end
--find the normalized percent of the value in the range. e.g range of 200-400 and a value of 250 result in 0.25
function DF:GetRangePercent(minValue, maxValue, value)
return (value - minValue) / max((maxValue - minValue), SMALL_FLOAT)
end
--find the value in the range given from a normalized percent. e.g range of 200-400 and a percent of 0.8 result in 360
function DF:GetRangeValue(minValue, maxValue, percent)
return Lerp(minValue, maxValue, percent)
end
function DF:GetColorRangeValue(r1, g1, b1, r2, g2, b2, value)
local newR = DF:LerpNorm(r1, r2, value)
local newG = DF:LerpNorm(g1, g2, value)
local newB = DF:LerpNorm(b1, b2, value)
return newR, newG, newB
end
--dot product of two 2D Vectors
function DF:GetDotProduct(value1, value2)
return (value1.x * value2.x) + (value1.y * value2.y)
end
function DF:GetBezierPoint(value, point1, point2, point3)
local bP1 = Lerp(point1, point2, value)
local bP2 = Lerp(point2, point3, value)
return Lerp(bP1, bP2, value)
end
--normalized value 0-1 result in the value on the range given, e.g 200-400 range with a value of .5 result in 300
function DF:LerpNorm(minValue, maxValue, value)
return (minValue + value * (maxValue - minValue))
end
--change the color by the deltaTime
function DF:LerpLinearColor(deltaTime, interpSpeed, r1, g1, b1, r2, g2, b2)
deltaTime = deltaTime * interpSpeed
local r = r1 + (r2 - r1) * deltaTime
local g = g1 + (g2 - g1) * deltaTime
local b = b1 + (b2 - b1) * deltaTime
return r, g, b
end
--check if a number is near another number by a tolerance
function DF:IsNearlyEqual(value1, value2, tolerance)
tolerance = tolerance or SMALL_FLOAT
return abs(value1 - value2) <= tolerance
end
--check if a number is near zero
function DF:IsNearlyZero(value, tolerance)
tolerance = tolerance or SMALL_FLOAT
return abs(value) <= tolerance
end
--check if a number is within a two other numbers, if isInclusive is true, it'll include the max value
function DF:IsWithin(minValue, maxValue, value, isInclusive)
if (isInclusive) then
return ((value >= minValue) and (value <= maxValue))
else
return ((value >= minValue) and (value < maxValue))
end
end
--dont allow a number ot be lower or bigger than a certain range
function DF:Clamp(minValue, maxValue, value)
return value < minValue and minValue or value < maxValue and value or maxValue
end
--from http://lua-users.org/wiki/SimpleRound cut fractions on a float
function DF:Round(num, numDecimalPlaces)
local mult = 10^(numDecimalPlaces or 0)
return math.floor(num * mult + 0.5) / mult
end
local BoundingBox = {}
BoundingBox.CoordinatesData = {
["topleft"] = {["x"] = 'number', ["y"] = 'number'},
["topright"] = {["x"] = 'number', ["y"] = 'number'},
["bottomleft"] = {["x"] = 'number', ["y"] = 'number'},
["bottomright"] = {["x"] = 'number', ["y"] = 'number'},
["center"] = {["x"] = 'number', ["y"] = 'number'},
["width"] = 'number',
["height"] = 'number',
}
---@class objectcoordinates
---@field topleft {["x"]: number, ["y"]: number}
---@field topright {["x"]: number, ["y"]: number}
---@field bottomleft {["x"]: number, ["y"]: number}
---@field bottomright {["x"]: number, ["y"]: number}
---@field center {["x"]: number, ["y"]: number}
---@field width number
---@field height number
---@field left number
---@field right number
---@field top number
---@field bottom number
---return the coordinates of the four corners of an object
---@param object uiobject
---@return objectcoordinates
function DF:GetObjectCoordinates(object)
local centerX, centerY = object:GetCenter()
local width = object:GetWidth()
local height = object:GetHeight()
local halfWidth = width / 2
local halfHeight = height / 2
return {
["width"] = width,
["height"] = height,
["left"] = centerX - halfWidth,
["right"] = centerX + halfWidth,
["top"] = centerY + halfHeight,
["bottom"] = centerY - halfHeight,
["center"] = {x = centerX, y = centerY},
["topleft"] = {x = centerX - halfWidth, y = centerY + halfHeight},
["topright"] = {x = centerX + halfWidth, y = centerY + halfHeight},
["bottomleft"] = {x = centerX - halfWidth, y = centerY - halfHeight},
["bottomright"] = {x = centerX + halfWidth, y = centerY - halfHeight},
}
end
function DF:ScaleBack()
end
@@ -0,0 +1,965 @@
local detailsFramework = _G["DetailsFramework"]
if (not detailsFramework or not DetailsFrameworkCanLoad) then
return
end
local _
local getFrame = function(frame)
return rawget(frame, "widget") or frame
end
detailsFramework.WidgetFunctions = {
GetCapsule = function(self)
return self.MyObject
end,
GetObject = function(self)
return self.MyObject
end,
}
detailsFramework.DefaultMetaFunctionsGet = {
parent = function(object)
return object:GetParent()
end,
shown = function(object)
return object:IsShown()
end,
}
detailsFramework.TooltipHandlerMixin = {
SetTooltip = function(self, tooltip)
if (tooltip) then
if (detailsFramework.Language.IsLocTable(tooltip)) then
--register the locTable as a tableKey
local locTable = tooltip
detailsFramework.Language.RegisterTableKeyWithLocTable(self, "have_tooltip", locTable)
else
self.have_tooltip = tooltip
end
else
self.have_tooltip = nil
end
end,
GetTooltip = function(self)
return self.have_tooltip
end,
ShowTooltip = function(self)
local tooltipText = self:GetTooltip()
if (type(tooltipText) == "function") then
local tooltipFunction = tooltipText
local gotTooltip, tooltipString = xpcall(tooltipFunction, geterrorhandler())
if (gotTooltip) then
tooltipText = tooltipString
end
end
if (tooltipText) then
GameCooltip:Preset(2)
GameCooltip:AddLine(tooltipText)
GameCooltip:ShowCooltip(getFrame(self), "tooltip")
end
end,
HideTooltip = function(self)
local tooltipText = self:GetTooltip()
if (tooltipText) then
if (GameCooltip:IsOwner(getFrame(self))) then
GameCooltip:Hide()
end
end
end,
}
detailsFramework.DefaultMetaFunctionsSet = {
parent = function(object, value)
return object:SetParent(value)
end,
show = function(object, value)
if (value) then
return object:Show()
else
return object:Hide()
end
end,
hide = function(object, value)
if (value) then
return object:Hide()
else
return object:Show()
end
end,
}
detailsFramework.DefaultMetaFunctionsSet.shown = detailsFramework.DefaultMetaFunctionsSet.show
detailsFramework.LayeredRegionMetaFunctionsSet = {
drawlayer = function(object, value)
object.image:SetDrawLayer(value)
end,
sublevel = function(object, value)
local drawLayer = object:GetDrawLayer()
object:SetDrawLayer(drawLayer, value)
end,
}
detailsFramework.LayeredRegionMetaFunctionsGet = {
drawlayer = function(object)
return object.image:GetDrawLayer()
end,
sublevel = function(object)
local _, subLevel = object.image:GetDrawLayer()
return subLevel
end,
}
detailsFramework.FrameMixin = {
SetFrameStrata = function(self, strata)
self = getFrame(self)
if (type(strata) == "table" and strata.GetObjectType) then
local UIObject = strata
self:SetFrameStrata(UIObject:GetFrameStrata())
else
self:SetFrameStrata(strata)
end
end,
SetFrameLevel = function(self, level, UIObject)
self = getFrame(self)
if (not UIObject) then
self:SetFrameLevel(level)
else
local framelevel = UIObject:GetFrameLevel(UIObject) + level
self:SetFrameLevel(framelevel)
end
end,
SetSize = function(self, width, height)
self = getFrame(self)
if (width) then
self:SetWidth(width)
end
if (height) then
self:SetHeight(height)
end
end,
SetBackdrop = function(self, ...)
self = getFrame(self)
self:SetBackdrop(...)
end,
SetBackdropColor = function(self, ...)
self = getFrame(self)
self:SetBackdropColor(...)
end,
SetBackdropBorderColor = function(self, ...)
self = getFrame(self)
getFrame(self):SetBackdropBorderColor(...)
end,
}
local doublePoint = {
["lefts"] = true,
["rights"] = true,
["tops"] = true,
["bottoms"] = true,
["left-left"] = true,
["right-right"] = true,
["top-top"] = true,
["bottom-bottom"] = true,
["bottom-top"] = true,
["top-bottom"] = true,
["right-left"] = true,
["left-right"] = true,
}
detailsFramework.SetPointMixin = {
SetPoint = function(object, anchorName1, anchorObject, anchorName2, xOffset, yOffset)
if (doublePoint[anchorName1]) then
object:ClearAllPoints()
local anchorTo
if (anchorObject and type(anchorObject) == "table") then
xOffset, yOffset = anchorName2 or 0, xOffset or 0
anchorTo = getFrame(anchorObject)
else
xOffset, yOffset = anchorObject or 0, anchorName2 or 0
anchorTo = object:GetParent()
end
--offset always inset to inner
if (anchorName1 == "lefts") then
object:SetPoint("topleft", anchorTo, "topleft", xOffset, -yOffset)
object:SetPoint("bottomleft", anchorTo, "bottomleft", xOffset, yOffset)
elseif (anchorName1 == "rights") then
object:SetPoint("topright", anchorTo, "topright", xOffset, -yOffset)
object:SetPoint("bottomright", anchorTo, "bottomright", xOffset, yOffset)
elseif (anchorName1 == "tops") then
object:SetPoint("topleft", anchorTo, "topleft", xOffset, -yOffset)
object:SetPoint("topright", anchorTo, "topright", -xOffset, -yOffset)
elseif (anchorName1 == "bottoms") then
object:SetPoint("bottomleft", anchorTo, "bottomleft", xOffset, yOffset)
object:SetPoint("bottomright", anchorTo, "bottomright", -xOffset, yOffset)
elseif (anchorName1 == "left-left") then
object:SetPoint("left", anchorTo, "left", xOffset, yOffset)
elseif (anchorName1 == "right-right") then
object:SetPoint("right", anchorTo, "right", xOffset, yOffset)
elseif (anchorName1 == "top-top") then
object:SetPoint("top", anchorTo, "top", xOffset, yOffset)
elseif (anchorName1 == "bottom-bottom") then
object:SetPoint("bottom", anchorTo, "bottom", xOffset, yOffset)
elseif (anchorName1 == "bottom-top") then
object:SetPoint("bottomleft", anchorTo, "topleft", xOffset, yOffset)
object:SetPoint("bottomright", anchorTo, "topright", -xOffset, yOffset)
elseif (anchorName1 == "top-bottom") then
object:SetPoint("topleft", anchorTo, "bottomleft", xOffset, -yOffset)
object:SetPoint("topright", anchorTo, "bottomright", -xOffset, -yOffset)
elseif (anchorName1 == "right-left") then
object:SetPoint("topright", anchorTo, "topleft", xOffset, -yOffset)
object:SetPoint("bottomright", anchorTo, "bottomleft", xOffset, yOffset)
elseif (anchorName1 == "left-right") then
object:SetPoint("topleft", anchorTo, "topright", xOffset, -yOffset)
object:SetPoint("bottomleft", anchorTo, "bottomright", xOffset, yOffset)
end
return
end
xOffset = xOffset or 0
yOffset = yOffset or 0
anchorName1, anchorObject, anchorName2, xOffset, yOffset = detailsFramework:CheckPoints(anchorName1, anchorObject, anchorName2, xOffset, yOffset, object)
if (not anchorName1) then
error("SetPoint: Invalid parameter.")
return
end
if (not object.widget) then
local SetPoint = getmetatable(object).__index.SetPoint
return SetPoint(object, anchorName1, anchorObject, anchorName2, xOffset, yOffset)
else
return object.widget:SetPoint(anchorName1, anchorObject, anchorName2, xOffset, yOffset)
end
end,
}
---mixin for options
---@class df_optionsmixin
detailsFramework.OptionsFunctions = {
SetOption = function(self, optionName, optionValue)
if (self.options) then
self.options [optionName] = optionValue
else
self.options = {}
self.options [optionName] = optionValue
end
if (self.OnOptionChanged) then
detailsFramework:Dispatch (self.OnOptionChanged, self, optionName, optionValue)
end
end,
GetOption = function(self, optionName)
return self.options and self.options [optionName]
end,
GetAllOptions = function(self)
if (self.options) then
local optionsTable = {}
for key, _ in pairs(self.options) do
optionsTable [#optionsTable + 1] = key
end
return optionsTable
else
return {}
end
end,
BuildOptionsTable = function(self, defaultOptions, userOptions)
self.options = self.options or {}
detailsFramework.table.deploy(self.options, userOptions or {})
detailsFramework.table.deploy(self.options, defaultOptions or {})
end
}
--payload mixin
detailsFramework.PayloadMixin = {
ClearPayload = function(self)
self.payload = {}
end,
SetPayload = function(self, ...)
self.payload = {...}
return self.payload
end,
AddPayload = function(self, ...)
local currentPayload = self.payload or {}
self.payload = currentPayload
for i = 1, select("#", ...) do
local value = select(i, ...)
currentPayload[#currentPayload+1] = value
end
return self.payload
end,
GetPayload = function(self)
return self.payload
end,
DumpPayload = function(self)
return unpack(self.payload)
end,
--does not copy wow objects, just pass them to the new table, tables strings and numbers are copied entirely
DuplicatePayload = function(self)
local duplicatedPayload = detailsFramework.table.duplicate({}, self.payload)
return duplicatedPayload
end,
}
---mixin to use with DetailsFramework:Mixin(table, detailsFramework.ScriptHookMixin)
---
---@class DetailsFramework.ScriptHookMixin
detailsFramework.ScriptHookMixin = {
RunHooksForWidget = function(self, event, ...)
local hooks = self.HookList[event]
if (not hooks) then
print(self.widget:GetName(), "no hooks for", event)
return
end
for i, func in ipairs(hooks) do
local success, canInterrupt = xpcall(func, geterrorhandler(), ...)
if (not success) then
--error("Details! Framework: " .. event .. " hook for " .. self:GetName() .. ": " .. canInterrupt)
return false
elseif (canInterrupt) then
return true
end
end
end,
SetHook = function(self, hookType, func)
if (self.HookList[hookType]) then
if (type(func) == "function") then
local isRemoval = false
for i = #self.HookList[hookType], 1, -1 do
if (self.HookList[hookType][i] == func) then
table.remove(self.HookList[hookType], i)
isRemoval = true
break
end
end
if (not isRemoval) then
table.insert(self.HookList[hookType], func)
end
else
if (detailsFramework.debug) then
print(debugstack())
error("Details! Framework: invalid function for widget " .. self.WidgetType .. ".")
end
end
else
if (detailsFramework.debug) then
error("Details! Framework: unknown hook type for widget " .. self.WidgetType .. ": '" .. hookType .. "'.")
end
end
end,
HasHook = function(self, hookType, func)
if (self.HookList[hookType]) then
if (type(func) == "function") then
for i = #self.HookList[hookType], 1, -1 do
if (self.HookList[hookType][i] == func) then
return true
end
end
end
end
end,
ClearHooks = function(self)
for hookType, hookTable in pairs(self.HookList) do
table.wipe(hookTable)
end
end,
}
---mixin to use with DetailsFramework:Mixin(table, detailsFramework.SortFunctions)
---add methods to be used on scrollframes
---@class df_scrollboxmixin
detailsFramework.ScrollBoxFunctions = {
---refresh the scrollbox by resetting all lines created with :CreateLine(), then calling the refresh_func which was set at :CreateScrollBox()
---@param self table
---@return table
Refresh = function(self)
--hide all frames and tag as not in use
self._LinesInUse = 0
for index, frame in ipairs(self.Frames) do
if (not self.DontHideChildrenOnPreRefresh) then
frame:Hide()
end
frame._InUse = nil
end
local offset = 0
if (self.IsFauxScroll) then
self:UpdateFaux(#self.data, self.LineAmount, self.LineHeight)
offset = self:GetOffsetFaux()
end
--call the refresh function
detailsFramework:CoreDispatch((self:GetName() or "ScrollBox") .. ":Refresh()", self.refresh_func, self, self.data, offset, self.LineAmount)
--hide all frames that are not in use
for index, frame in ipairs(self.Frames) do
if (not frame._InUse) then
frame:Hide()
else
frame:Show()
end
end
self:Show()
local frameName = self:GetName()
if (frameName) then
if (self.HideScrollBar) then
local scrollBar = _G[frameName .. "ScrollBar"]
if (scrollBar) then
scrollBar:Hide()
end
else
--[=[ --maybe in the future I visit this again
local scrollBar = _G[frameName .. "ScrollBar"]
local height = self:GetHeight()
local totalLinesRequired = #self.data
local linesShown = self._LinesInUse
local percent = linesShown / totalLinesRequired
local thumbHeight = height * percent
scrollBar.ThumbTexture:SetSize(12, thumbHeight)
print("thumbHeight:", thumbHeight)
--]=]
end
end
return self.Frames
end,
OnVerticalScroll = function(self, offset)
self:OnVerticalScrollFaux(offset, self.LineHeight, self.Refresh)
return true
end,
---create a line within the scrollbox
---@param self table is the scrollbox
---@param func function|nil function to create the line object, this function will receive the line index as argument and return a table with the line object
---@return table line object (table)
CreateLine = function(self, func)
if (not func) then
func = self.CreateLineFunc
end
local okay, newLine = pcall(func, self, #self.Frames+1)
if (okay) then
if (not newLine) then
error("ScrollFrame:CreateLine() function did not returned a line, use: 'return line'")
end
tinsert(self.Frames, newLine)
newLine.Index = #self.Frames
return newLine
else
error("ScrollFrame:CreateLine() error on creating a line: " .. newLine)
end
end,
CreateLines = function(self, callback, lineAmount)
for i = 1, lineAmount do
self:CreateLine(callback)
end
end,
GetLine = function(self, lineIndex)
local line = self.Frames[lineIndex]
if (line) then
line._InUse = true
end
self._LinesInUse = self._LinesInUse + 1
return line
end,
SetData = function(self, data)
self.data = data
end,
GetData = function(self)
return self.data
end,
GetFrames = function(self)
return self.Frames
end,
GetLines = function(self) --alias of GetFrames
return self.Frames
end,
GetNumFramesCreated = function(self)
return #self.Frames
end,
GetNumFramesShown = function(self)
return self.LineAmount
end,
SetNumFramesShown = function(self, newAmount)
--hide frames which won't be used
if (newAmount < #self.Frames) then
for i = newAmount+1, #self.Frames do
self.Frames[i]:Hide()
end
end
--set the new amount
self.LineAmount = newAmount
end,
SetFramesHeight = function(self, height)
self.LineHeight = height
self:OnSizeChanged()
self:Refresh()
end,
OnSizeChanged = function(self)
if (self.ReajustNumFrames) then
--how many lines the scroll can show
local amountOfFramesToShow = floor(self:GetHeight() / self.LineHeight)
--how many lines the scroll already have
local totalFramesCreated = self:GetNumFramesCreated()
--how many lines are current shown
local totalFramesShown = self:GetNumFramesShown()
--the amount of frames increased
if (amountOfFramesToShow > totalFramesShown) then
for i = totalFramesShown+1, amountOfFramesToShow do
--check if need to create a new line
if (i > totalFramesCreated) then
self:CreateLine(self.CreateLineFunc)
end
end
--the amount of frames decreased
elseif (amountOfFramesToShow < totalFramesShown) then
--hide all frames above the new amount to show
for i = totalFramesCreated, amountOfFramesToShow, -1 do
if (self.Frames[i]) then
self.Frames[i]:Hide()
end
end
end
--set the new amount of frames
self:SetNumFramesShown(amountOfFramesToShow)
--refresh lines
self:Refresh()
end
end,
--moved functions from blizzard faux scroll that are called from insecure code environment
--this reduces the amount of taints while using the faux scroll frame
GetOffsetFaux = function(self)
return self.offset or 0
end,
OnVerticalScrollFaux = function(self, value, itemHeight, updateFunction)
local scrollbar = self:GetChildFramesFaux();
scrollbar:SetValue(value);
self.offset = math.floor((value / itemHeight) + 0.5);
if (updateFunction) then
updateFunction(self)
end
end,
GetChildFramesFaux = function(frame)
local frameName = frame:GetName();
if frameName then
return _G[ frameName.."ScrollBar" ], _G[ frameName.."ScrollChildFrame" ], _G[ frameName.."ScrollBarScrollUpButton" ], _G[ frameName.."ScrollBarScrollDownButton" ];
else
return frame.ScrollBar, frame.ScrollChildFrame, frame.ScrollBar.ScrollUpButton, frame.ScrollBar.ScrollDownButton;
end
end,
UpdateFaux = function(frame, numItems, numToDisplay, buttonHeight, button, smallWidth, bigWidth, highlightFrame, smallHighlightWidth, bigHighlightWidth, alwaysShowScrollBar)
local scrollBar, scrollChildFrame, scrollUpButton, scrollDownButton = frame:GetChildFramesFaux();
-- If more than one screen full of items then show the scrollbar
local showScrollBar;
if ( numItems > numToDisplay or alwaysShowScrollBar ) then
frame:Show();
showScrollBar = 1;
else
scrollBar:SetValue(0);
frame:Hide();
end
if ( frame:IsShown() ) then
local scrollFrameHeight = 0;
local scrollChildHeight = 0;
if ( numItems > 0 ) then
scrollFrameHeight = (numItems - numToDisplay) * buttonHeight;
scrollChildHeight = numItems * buttonHeight;
if ( scrollFrameHeight < 0 ) then
scrollFrameHeight = 0;
end
scrollChildFrame:Show();
else
scrollChildFrame:Hide();
end
local maxRange = (numItems - numToDisplay) * buttonHeight;
if (maxRange < 0) then
maxRange = 0;
end
scrollBar:SetMinMaxValues(0, maxRange);
scrollBar:SetValueStep(buttonHeight);
scrollBar:SetStepsPerPage(numToDisplay-1);
scrollChildFrame:SetHeight(scrollChildHeight);
-- Arrow button handling
if ( scrollBar:GetValue() == 0 ) then
scrollUpButton:Disable();
else
scrollUpButton:Enable();
end
if ((scrollBar:GetValue() - scrollFrameHeight) == 0) then
scrollDownButton:Disable();
else
scrollDownButton:Enable();
end
-- Shrink because scrollbar is shown
if ( highlightFrame ) then
highlightFrame:SetWidth(smallHighlightWidth);
end
if ( button ) then
for i=1, numToDisplay do
_G[button..i]:SetWidth(smallWidth);
end
end
else
-- Widen because scrollbar is hidden
if ( highlightFrame ) then
highlightFrame:SetWidth(bigHighlightWidth);
end
if ( button ) then
for i=1, numToDisplay do
_G[button..i]:SetWidth(bigWidth);
end
end
end
return showScrollBar;
end,
}
--back compatibility, can be removed in the future (28/04/2023)
---@class DetailsFramework.ScrollBoxFunctions : df_scrollboxmixin
local SortMember = ""
local SortByMember = function(t1, t2)
return t1[SortMember] > t2[SortMember]
end
local SortByMemberReverse = function(t1, t2)
return t1[SortMember] < t2[SortMember]
end
---mixin to use with DetailsFramework:Mixin(table, detailsFramework.SortFunctions)
---adds the method Sort() to a table, this method can be used to sort another table by a member, can't sort itself
---@class DetailsFramework.SortFunctions
detailsFramework.SortFunctions = {
---sort a table by a member
---@param self table
---@param tThisTable table
---@param sMemberName string
---@param bIsReverse boolean
Sort = function(self, tThisTable, sMemberName, bIsReverse)
SortMember = sMemberName
if (not bIsReverse) then
table.sort(tThisTable, SortByMember)
else
table.sort(tThisTable, SortByMemberReverse)
end
end
}
---@class df_data : table
---@field _dataInfo {data: table, dataCurrentIndex: number, callbacks: function[]}
---@field callbacks table<function, any[]>
---@field dataCurrentIndex number
---@field DataConstructor fun(self: df_data)
---@field AddDataChangeCallback fun(self: df_data, callback: function, ...: any)
---@field RemoveDataChangeCallback fun(self: df_data, callback: function)
---@field GetData fun(self: df_data)
---@field GetDataSize fun(self: df_data) : number
---@field GetDataFirstValue fun(self: df_data) : any
---@field GetDataLastValue fun(self: df_data) : any
---@field GetDataMinMaxValues fun(self: df_data) : number, number
---@field GetDataMinMaxValueFromSubTable fun(self: df_data, key: string) : number, number when data uses sub tables, get the min max values from a specific index or key, if the value stored is number, return the min and max values
---@field SetData fun(self: df_data, data: table, anyValue: any)
---@field SetDataRaw fun(self: df_data, data: table) set the data without triggering callback
---@field GetDataNextValue fun(self: df_data) : any
---@field ResetDataIndex fun(self: df_data)
---mixin to use with DetailsFramework:Mixin(table, detailsFramework.DataMixin)
---add 'data' to a table, this table can be used to store data for the object
---@class DetailsFramework.DataMixin
detailsFramework.DataMixin = {
---initialize the data table
---@param self table
DataConstructor = function(self)
self._dataInfo = {
data = {},
dataCurrentIndex = 1,
callbacks = {},
}
end,
---when data is changed, functions registered with this function will be called
---@param self table
---@param func function
---@param ... unknown
AddDataChangeCallback = function(self, func, ...)
assert(type(func) == "function", "invalid function for AddDataChangeCallback.")
local allCallbacks = self._dataInfo.callbacks
allCallbacks[func] = {...}
end,
---remove a previous registered callback function
---@param self table
---@param func function
RemoveDataChangeCallback = function(self, func)
assert(type(func) == "function", "invalid function for RemoveDataChangeCallback.")
local allCallbacks = self._dataInfo.callbacks
allCallbacks[func] = nil
end,
---set the data without callback
---@param self table
---@param data table
SetDataRaw = function(self, data)
assert(type(data) == "table", "invalid table for SetData.")
self._dataInfo.data = data
self:ResetDataIndex()
end,
---set the data table
---@param self table
---@param data table
---@param anyValue any @any value to pass to the callback functions before the payload is added
SetData = function(self, data, anyValue)
assert(type(data) == "table", "invalid table for SetData.")
self._dataInfo.data = data
self:ResetDataIndex()
local allCallbacks = self._dataInfo.callbacks
for func, payload in pairs(allCallbacks) do
xpcall(func, geterrorhandler(), data, anyValue, unpack(payload))
end
end,
---get the data table
---@param self table
GetData = function(self)
return self._dataInfo.data
end,
---get the next value from the data table
---@param self table
---@return any
GetDataNextValue = function(self)
local currentValue = self._dataInfo.dataCurrentIndex
local value = self:GetData()[currentValue]
self._dataInfo.dataCurrentIndex = self._dataInfo.dataCurrentIndex + 1
return value
end,
---reset the data index, making GetDataNextValue() return the first value again
---@param self table
ResetDataIndex = function(self)
self._dataInfo.dataCurrentIndex = 1
end,
---get the size of the data table
---@param self table
---@return number
GetDataSize = function(self)
return #self:GetData()
end,
---get the first value from the data table
---@param self table
---@return any
GetDataFirstValue = function(self)
return self:GetData()[1]
end,
---get the last value from the data table
---@param self table
---@return any
GetDataLastValue = function(self)
local data = self:GetData()
return data[#data]
end,
---get the min and max values from the data table, if the value stored is number, return the min and max values
---could be used together with SetMinMaxValues from the df_value mixin
---@param self table
---@return number, number
GetDataMinMaxValues = function(self)
local minDataValue = 0
local maxDataValue = 0
local data = self:GetData()
for i = 1, #data do
local thisData = data[i]
if (thisData > maxDataValue) then
maxDataValue = thisData
elseif (thisData < minDataValue) then
minDataValue = thisData
end
end
return minDataValue, maxDataValue
end,
---when data uses sub tables, get the min max values from a specific index or key, if the value stored is number, return the min and max values
---@param self table
---@param key string
---@return number, number
GetDataMinMaxValueFromSubTable = function(self, key)
local minDataValue = 0
local maxDataValue = 0
local data = self:GetData()
for i = 1, #data do
local thisData = data[i]
if (thisData[key] > maxDataValue) then
maxDataValue = thisData[key]
elseif (thisData[key] < minDataValue) then
minDataValue = thisData[key]
end
end
return minDataValue, maxDataValue
end,
}
---@class df_value : table
---@field minValue number
---@field maxValue number
---@field ValueConstructor fun(self: df_value)
---@field SetMinMaxValues fun(self: df_value, minValue: number, maxValue: number)
---@field GetMinMaxValues fun(self: df_value) : number, number
---@field GetMinValue fun(self: df_value) : number
---@field GetMaxValue fun(self: df_value) : number
---@field SetMinValue fun(self: df_value, minValue: number)
---@field SetMinValueIfLower fun(self: df_value, ...: number)
---@field SetMaxValue fun(self: df_value, maxValue: number)
---@field SetMaxValueIfBigger fun(self: df_value, ...: number)
---mixin to use with DetailsFramework:Mixin(table, detailsFramework.ValueMixin)
---add support to min value and max value into a table or object
---@class DetailsFramework.ValueMixin
detailsFramework.ValueMixin = {
---initialize the value table
---@param self table
ValueConstructor = function(self)
self.minValue = 0
self.maxValue = 1
end,
---set the min and max values
---@param self table
---@param minValue number
---@param maxValue number
SetMinMaxValues = function(self, minValue, maxValue)
self.minValue = minValue
self.maxValue = maxValue
end,
---get the min and max values
---@param self table
---@return number, number
GetMinMaxValues = function(self)
return self.minValue, self.maxValue
end,
---get the min value
---@param self table
---@return number
GetMinValue = function(self)
return self.minValue
end,
---get the max value
---@param self table
---@return number
GetMaxValue = function(self)
return self.maxValue
end,
---set the min value
---@param self table
---@param minValue number
SetMinValue = function(self, minValue)
self.minValue = minValue
end,
---set the min value if one of the values passed is lower than the current min value
---@param self table
---@param ... number
SetMinValueIfLower = function(self, ...)
self.minValue = math.min(self.minValue, ...)
end,
---set the max value
---@param self table
---@param maxValue number
SetMaxValue = function(self, maxValue)
self.maxValue = maxValue
end,
---set the max value if one of the values passed is bigger than the current max value
---@param self table
---@param ... number
SetMaxValueIfBigger = function(self, ...)
self.maxValue = math.max(self.maxValue, ...)
end,
}
@@ -0,0 +1,861 @@
local DF = _G["DetailsFramework"]
if (not DF or not DetailsFrameworkCanLoad) then
return
end
local _
local _unpack = unpack
local type = type
local _math_floor = math.floor
local SharedMedia = LibStub:GetLibrary("LibSharedMedia-3.0")
local APIBarFunctions
do
local metaPrototype = {
WidgetType = "normal_bar",
dversion = DF.dversion,
}
--check if there's a metaPrototype already existing
if (_G[DF.GlobalWidgetControlNames["normal_bar"]]) then
--get the already existing metaPrototype
local oldMetaPrototype = _G[DF.GlobalWidgetControlNames["normal_bar"]]
--check if is older
if ( (not oldMetaPrototype.dversion) or (oldMetaPrototype.dversion < DF.dversion) ) then
--the version is older them the currently loading one
--copy the new values into the old metatable
for funcName, _ in pairs(metaPrototype) do
oldMetaPrototype[funcName] = metaPrototype[funcName]
end
end
else
--first time loading the framework
_G[DF.GlobalWidgetControlNames["normal_bar"]] = metaPrototype
end
end
local BarMetaFunctions = _G[DF.GlobalWidgetControlNames["normal_bar"]]
DF:Mixin(BarMetaFunctions, DF.ScriptHookMixin)
------------------------------------------------------------------------------------------------------------
--metatables
BarMetaFunctions.__call = function(object, value)
if (not value) then
return object.statusbar:GetValue()
else
return object.statusbar:SetValue(value)
end
end
BarMetaFunctions.__add = function(v1, v2)
if (type(v1) == "table") then
local v = v1.statusbar:GetValue()
v = v + v2
v1.statusbar:SetValue(v)
else
local v = v2.statusbar:GetValue()
v = v + v1
v2.statusbar:SetValue(v)
end
end
BarMetaFunctions.__sub = function(v1, v2)
if (type(v1) == "table") then
local v = v1.statusbar:GetValue()
v = v - v2
v1.statusbar:SetValue(v)
else
local v = v2.statusbar:GetValue()
v = v - v1
v2.statusbar:SetValue(v)
end
end
------------------------------------------------------------------------------------------------------------
--members
--tooltip
local function gmember_tooltip (_object)
return _object:GetTooltip()
end
--shown
local gmember_shown = function(_object)
return _object.statusbar:IsShown()
end
--frame width
local gmember_width = function(_object)
return _object.statusbar:GetWidth()
end
--frame height
local gmember_height = function(_object)
return _object.statusbar:GetHeight()
end
--value
local gmember_value = function(_object)
return _object.statusbar:GetValue()
end
--right text
local gmember_rtext = function(_object)
return _object.textright:GetText()
end
--left text
local gmember_ltext = function(_object)
return _object.textleft:GetText()
end
--left color
local gmember_color = function(_object)
local r, g, b, a = _object._texture:GetVertexColor()
return r, g, b, a
end
--icon
local gmember_icon = function(_object)
return _object._icon:GetTexture()
end
--texture
local gmember_texture = function(_object)
return _object._texture:GetTexture()
end
--font size
local gmember_textsize = function(_object)
local _, fontsize = _object.textleft:GetFont()
return fontsize
end
--font face
local gmember_textfont = function(_object)
local fontface = _object.textleft:GetFont()
return fontface
end
--font color
local gmember_textcolor = function(_object)
return _object.textleft:GetTextColor()
end
--alpha
local gmember_alpha= function(_object)
return _object:GetAlpha()
end
BarMetaFunctions.GetMembers = BarMetaFunctions.GetMembers or {}
BarMetaFunctions.GetMembers ["tooltip"] = gmember_tooltip
BarMetaFunctions.GetMembers ["shown"] = gmember_shown
BarMetaFunctions.GetMembers ["width"] = gmember_width
BarMetaFunctions.GetMembers ["height"] = gmember_height
BarMetaFunctions.GetMembers ["value"] = gmember_value
BarMetaFunctions.GetMembers ["lefttext"] = gmember_ltext
BarMetaFunctions.GetMembers ["righttext"] = gmember_rtext
BarMetaFunctions.GetMembers ["color"] = gmember_color
BarMetaFunctions.GetMembers ["icon"] = gmember_icon
BarMetaFunctions.GetMembers ["texture"] = gmember_texture
BarMetaFunctions.GetMembers ["fontsize"] = gmember_textsize
BarMetaFunctions.GetMembers ["fontface"] = gmember_textfont
BarMetaFunctions.GetMembers ["fontcolor"] = gmember_textcolor
BarMetaFunctions.GetMembers ["textsize"] = gmember_textsize --alias
BarMetaFunctions.GetMembers ["textfont"] = gmember_textfont --alias
BarMetaFunctions.GetMembers ["textcolor"] = gmember_textcolor --alias
BarMetaFunctions.GetMembers ["alpha"] = gmember_alpha
BarMetaFunctions.__index = function(_table, _member_requested)
local func = BarMetaFunctions.GetMembers [_member_requested]
if (func) then
return func (_table, _member_requested)
end
local fromMe = rawget (_table, _member_requested)
if (fromMe) then
return fromMe
end
return BarMetaFunctions [_member_requested]
end
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--tooltip
local smember_tooltip = function(_object, _value)
return _object:SetTooltip (_value)
end
--show
local smember_shown = function(_object, _value)
if (_value) then
return _object:Show()
else
return _object:Hide()
end
end
--hide
local smember_hide = function(_object, _value)
if (_value) then
return _object:Hide()
else
return _object:Show()
end
end
--width
local smember_width = function(_object, _value)
return _object.statusbar:SetWidth(_value)
end
--height
local smember_height = function(_object, _value)
return _object.statusbar:SetHeight(_value)
end
--statusbar value
local smember_value = function(_object, _value)
_object.statusbar:SetValue(_value)
return _object.div:SetPoint("left", _object.statusbar, "left", _value * (_object.statusbar:GetWidth()/100) - 16, 0)
end
--right text
local smember_rtext = function(_object, _value)
return _object.textright:SetText(_value)
end
--left text
local smember_ltext = function(_object, _value)
return _object.textleft:SetText(_value)
end
--color
local smember_color = function(_object, _value)
local _value1, _value2, _value3, _value4 = DF:ParseColors(_value)
_object.statusbar:SetStatusBarColor(_value1, _value2, _value3, _value4)
_object._texture.original_colors = {_value1, _value2, _value3, _value4}
_object.timer_texture:SetVertexColor(_value1, _value2, _value3, _value4)
_object.timer_textureR:SetVertexColor(_value1, _value2, _value3, _value4)
return _object._texture:SetVertexColor(_value1, _value2, _value3, _value4)
end
--background color
local smember_backgroundcolor = function(_object, _value)
local _value1, _value2, _value3, _value4 = DF:ParseColors(_value)
return _object.background:SetVertexColor(_value1, _value2, _value3, _value4)
end
--icon
local smember_icon = function(_object, _value)
if (type(_value) == "table") then
local _value1, _value2 = _unpack(_value)
_object._icon:SetTexture(_value1)
if (_value2) then
_object._icon:SetTexCoord(_unpack(_value2))
end
else
_object._icon:SetTexture(_value)
end
return
end
--texture
local smember_texture = function(_object, _value)
if (type(_value) == "table") then
local _value1, _value2 = _unpack(_value)
_object._texture:SetTexture(_value1)
_object.timer_texture:SetTexture(_value1)
_object.timer_textureR:SetTexture(_value1)
if (_value2) then
_object._texture:SetTexCoord(_unpack(_value2))
_object.timer_texture:SetTexCoord(_unpack(_value2))
_object.timer_textureR:SetTexCoord(_unpack(_value2))
end
else
if (_value:find("\\")) then
_object._texture:SetTexture(_value)
else
local file = SharedMedia:Fetch ("statusbar", _value)
if (file) then
_object._texture:SetTexture(file)
_object.timer_texture:SetTexture(file)
_object.timer_textureR:SetTexture(file)
else
_object._texture:SetTexture(_value)
_object.timer_texture:SetTexture(_value)
_object.timer_textureR:SetTexture(_value)
end
end
end
return
end
--background texture
local smember_backgroundtexture = function(_object, _value)
if (_value:find("\\")) then
_object.background:SetTexture(_value)
else
local file = SharedMedia:Fetch ("statusbar", _value)
if (file) then
_object.background:SetTexture(file)
else
_object.background:SetTexture(_value)
end
end
return
end
--font face
local smember_textfont = function(_object, _value)
DF:SetFontFace (_object.textleft, _value)
return DF:SetFontFace (_object.textright, _value)
end
--font size
local smember_textsize = function(_object, _value)
DF:SetFontSize(_object.textleft, _value)
return DF:SetFontSize(_object.textright, _value)
end
--font color
local smember_textcolor = function(_object, _value)
local _value1, _value2, _value3, _value4 = DF:ParseColors(_value)
_object.textleft:SetTextColor(_value1, _value2, _value3, _value4)
return _object.textright:SetTextColor(_value1, _value2, _value3, _value4)
end
--outline (shadow)
local smember_outline = function(_object, _value)
DF:SetFontOutline (_object.textleft, _value)
return DF:SetFontOutline (_object.textright, _value)
end
--alpha
local smember_alpha= function(_object, _value)
return _object:SetAlpha(_value)
end
BarMetaFunctions.SetMembers = BarMetaFunctions.SetMembers or {}
BarMetaFunctions.SetMembers["tooltip"] = smember_tooltip
BarMetaFunctions.SetMembers["shown"] = smember_shown
BarMetaFunctions.SetMembers["width"] = smember_width
BarMetaFunctions.SetMembers["height"] = smember_height
BarMetaFunctions.SetMembers["value"] = smember_value
BarMetaFunctions.SetMembers["righttext"] = smember_rtext
BarMetaFunctions.SetMembers["lefttext"] = smember_ltext
BarMetaFunctions.SetMembers["color"] = smember_color
BarMetaFunctions.SetMembers["backgroundcolor"] = smember_backgroundcolor
BarMetaFunctions.SetMembers["icon"] = smember_icon
BarMetaFunctions.SetMembers["texture"] = smember_texture
BarMetaFunctions.SetMembers["backgroundtexture"] = smember_backgroundtexture
BarMetaFunctions.SetMembers["fontsize"] = smember_textsize
BarMetaFunctions.SetMembers["fontface"] = smember_textfont
BarMetaFunctions.SetMembers["fontcolor"] = smember_textcolor
BarMetaFunctions.SetMembers["textsize"] = smember_textsize --alias
BarMetaFunctions.SetMembers["textfont"] = smember_textfont --alias
BarMetaFunctions.SetMembers["textcolor"] = smember_textcolor --alias
BarMetaFunctions.SetMembers["shadow"] = smember_outline
BarMetaFunctions.SetMembers["outline"] = smember_outline --alias
BarMetaFunctions.SetMembers["alpha"] = smember_alpha
BarMetaFunctions.__newindex = function(_table, _key, _value)
local func = BarMetaFunctions.SetMembers [_key]
if (func) then
return func (_table, _value)
else
return rawset (_table, _key, _value)
end
end
------------------------------------------------------------------------------------------------------------
--methods
--show & hide
function BarMetaFunctions:Show()
self.statusbar:Show()
end
function BarMetaFunctions:Hide()
self.statusbar:Hide()
end
--return color
function BarMetaFunctions:GetVertexColor()
return self._texture:GetVertexColor()
end
--set value (status bar)
function BarMetaFunctions:SetValue(value)
if (not value) then
value = 0
end
self.statusbar:SetValue(value)
self.div:SetPoint("left", self.statusbar, "left", value * (self.statusbar:GetWidth()/100) - 16, 0)
end
--set point
function BarMetaFunctions:SetPoint(v1, v2, v3, v4, v5)
v1, v2, v3, v4, v5 = DF:CheckPoints (v1, v2, v3, v4, v5, self)
if (not v1) then
print("Invalid parameter for SetPoint")
return
end
return self.widget:SetPoint(v1, v2, v3, v4, v5)
end
--set sizes
function BarMetaFunctions:SetSize(w, h)
if (w) then
self.statusbar:SetWidth(w)
end
if (h) then
self.statusbar:SetHeight(h)
end
end
--set texture
function BarMetaFunctions:SetTexture(texture)
self._texture:SetTexture(texture)
end
--set texts
function BarMetaFunctions:SetLeftText (text)
self.textleft:SetText(text)
end
function BarMetaFunctions:SetRightText (text)
self.textright:SetText(text)
end
--set color
function BarMetaFunctions:SetColor (r, g, b, a)
r, g, b, a = DF:ParseColors(r, g, b, a)
self._texture:SetVertexColor(r, g, b, a)
self.statusbar:SetStatusBarColor(r, g, b, a)
self._texture.original_colors = {r, g, b, a}
end
--set icons
function BarMetaFunctions:SetIcon (texture, ...)
self._icon:SetTexture(texture)
if (...) then
local L, R, U, D = _unpack(...)
self._icon:SetTexCoord(L, R, U, D)
end
end
--show div
function BarMetaFunctions:ShowDiv (bool)
if (bool) then
self.div:Show()
else
self.div:Hide()
end
end
-- tooltip
function BarMetaFunctions:SetTooltip (tooltip)
if (tooltip) then
return rawset (self, "have_tooltip", tooltip)
else
return rawset (self, "have_tooltip", nil)
end
end
function BarMetaFunctions:GetTooltip()
return rawget (self, "have_tooltip")
end
-- frame levels
function BarMetaFunctions:GetFrameLevel()
return self.statusbar:GetFrameLevel()
end
function BarMetaFunctions:SetFrameLevel(level, frame)
if (not frame) then
return self.statusbar:SetFrameLevel(level)
else
local framelevel = frame:GetFrameLevel (frame) + level
return self.statusbar:SetFrameLevel(framelevel)
end
end
-- frame stratas
function BarMetaFunctions:SetFrameStrata()
return self.statusbar:GetFrameStrata()
end
function BarMetaFunctions:SetFrameStrata(strata)
if (type(strata) == "table") then
self.statusbar:SetFrameStrata(strata:GetFrameStrata())
else
self.statusbar:SetFrameStrata(strata)
end
end
--container
function BarMetaFunctions:SetContainer (container)
self.container = container
end
------------------------------------------------------------------------------------------------------------
--scripts
local OnEnter = function(frame)
local capsule = frame.MyObject
local kill = capsule:RunHooksForWidget("OnEnter", frame, capsule)
if (kill) then
return
end
frame.MyObject.background:Show()
if (frame.MyObject.have_tooltip) then
GameCooltip2:Reset()
GameCooltip2:AddLine(frame.MyObject.have_tooltip)
GameCooltip2:ShowCooltip(frame, "tooltip")
end
end
local OnLeave = function(frame)
local capsule = frame.MyObject
local kill = capsule:RunHooksForWidget("OnLeave", frame, capsule)
if (kill) then
return
end
frame.MyObject.background:Hide()
if (frame.MyObject.have_tooltip) then
GameCooltip2:ShowMe(false)
end
end
local OnHide = function(frame)
local capsule = frame.MyObject
local kill = capsule:RunHooksForWidget("OnHide", frame, capsule)
if (kill) then
return
end
end
local OnShow = function(frame)
local capsule = frame.MyObject
local kill = capsule:RunHooksForWidget("OnShow", frame, capsule)
if (kill) then
return
end
end
local OnMouseDown = function(frame, button)
local capsule = frame.MyObject
local kill = capsule:RunHooksForWidget("OnMouseDown", frame, button, capsule)
if (kill) then
return
end
if (not frame.MyObject.container.isLocked and frame.MyObject.container:IsMovable()) then
if (not frame.isLocked and frame:IsMovable()) then
frame.MyObject.container.isMoving = true
frame.MyObject.container:StartMoving()
end
end
end
local OnMouseUp = function(frame, button)
local capsule = frame.MyObject
local kill = capsule:RunHooksForWidget("OnMouseUp", frame, button, capsule)
if (kill) then
return
end
if (frame.MyObject.container.isMoving) then
frame.MyObject.container:StopMovingOrSizing()
frame.MyObject.container.isMoving = false
end
end
------------------------------------------------------------------------------------------------------------
--timer
function BarMetaFunctions:OnTimerEnd()
local capsule = self
local kill = capsule:RunHooksForWidget("OnTimerEnd", self.widget, capsule)
if (kill) then
return
end
self.timer_texture:Hide()
self.timer_textureR:Hide()
self.div_timer:Hide()
self:Hide()
self.timer = false
end
function BarMetaFunctions:CancelTimerBar(no_timer_end)
if (not self.HasTimer) then
return
end
if (self.TimerScheduled) then
DF:CancelTimer(self.TimerScheduled)
self.TimerScheduled = nil
else
if (self.statusbar:GetScript("OnUpdate")) then
self.statusbar:SetScript("OnUpdate", nil)
end
end
self.righttext = ""
self.timer_texture:Hide()
self.timer_textureR:Hide()
if (not no_timer_end) then
self:OnTimerEnd()
end
end
local OnUpdate = function(self, elapsed)
--percent of elapsed
local pct = abs(self.end_timer - GetTime() - self.tempo) / self.tempo
if (self.inverse) then
self.t:SetWidth(self.total_size * pct)
else
self.t:SetWidth(self.total_size * abs(pct-1))
end
--right text
self.remaining = self.remaining - elapsed
if (self.MyObject.RightTextIsTimer) then
self.righttext:SetText(DF:IntegerToTimer(self.remaining))
else
self.righttext:SetText(_math_floor(self.remaining))
end
if (pct >= 1) then
self.righttext:SetText("")
self:SetScript("OnUpdate", nil)
self.MyObject.HasTimer = nil
self.MyObject:OnTimerEnd()
end
end
function BarMetaFunctions:SetTimer (tempo, end_at)
if (end_at) then
self.statusbar.tempo = end_at - tempo
self.statusbar.remaining = end_at - GetTime()
self.statusbar.end_timer = end_at
else
self.statusbar.tempo = tempo
self.statusbar.remaining = tempo
self.statusbar.end_timer = GetTime() + tempo
end
self.statusbar.total_size = self.statusbar:GetWidth()
self.statusbar.inverse = self.BarIsInverse
self(0)
self.div_timer:Show()
self.background:Show()
self:Show()
if (self.LeftToRight) then
self.timer_texture:Hide()
self.timer_textureR:Show()
self.statusbar.t = self.timer_textureR
self.timer_textureR:ClearAllPoints()
self.timer_textureR:SetPoint("right", self.statusbar, "right")
self.div_timer:SetPoint("left", self.timer_textureR, "left", -14, -1)
else
self.timer_texture:Show()
self.timer_textureR:Hide()
self.statusbar.t = self.timer_texture
self.timer_texture:ClearAllPoints()
self.timer_texture:SetPoint("left", self.statusbar, "left")
self.div_timer:SetPoint("left", self.timer_texture, "right", -16, -1)
end
if (self.BarIsInverse) then
self.statusbar.t:SetWidth(1)
else
self.statusbar.t:SetWidth(self.statusbar.total_size)
end
self.timer = true
self.HasTimer = true
self.TimerScheduled = DF:ScheduleTimer("StartTimeBarAnimation", 0.1, self)
end
function DF:StartTimeBarAnimation (timebar)
timebar.TimerScheduled = nil
timebar.statusbar:SetScript("OnUpdate", OnUpdate)
end
------------------------------------------------------------------------------------------------------------
--object constructor
function DetailsFrameworkNormalBar_OnCreate (self)
self.texture.original_colors = {1, 1, 1, 1}
self.background.original_colors = {.3, .3, .3, .3}
self.timertexture.original_colors = {.3, .3, .3, .3}
return true
end
local build_statusbar = function(self)
self:SetSize(300, 14)
self.background = self:CreateTexture("$parent_background", "BACKGROUND")
self.background:Hide()
self.background:SetAllPoints()
self.background:SetTexture([[Interface\PaperDollInfoFrame\UI-Character-Skills-Bar]])
self.background:SetVertexColor(.3, .3, .3, .3)
self.timertexture = self:CreateTexture("$parent_timerTexture", "ARTWORK")
self.timertexture:Hide()
self.timertexture:SetSize(300, 14)
self.timertexture:SetTexture([[Interface\PaperDollInfoFrame\UI-Character-Skills-Bar]])
self.timertexture:SetPoint("LEFT", self, "LEFT")
self.timertextureR = self:CreateTexture("$parent_timerTextureR", "ARTWORK")
self.timertextureR:Hide()
self.timertextureR:SetSize(300, 14)
self.timertextureR:SetTexture([[Interface\PaperDollInfoFrame\UI-Character-Skills-Bar]])
self.timertextureR:SetPoint("TOPRIGHT", self, 0, 0)
self.timertextureR:SetPoint("BOTTOMRIGHT", self, 0, 0)
self.texture = self:CreateTexture("$parent_statusbarTexture", "ARTWORK")
self.texture:SetSize(300, 14)
self.texture:SetTexture([[Interface\PaperDollInfoFrame\UI-Character-Skills-Bar]])
self:SetStatusBarTexture(self.texture)
self.icontexture = self:CreateTexture("$parent_icon", "OVERLAY")
self.icontexture:SetSize(14, 14)
self.icontexture:SetPoint("LEFT", self, "LEFT")
self.sparkmouseover = self:CreateTexture("$parent_sparkMouseover", "OVERLAY")
self.sparkmouseover:SetSize(32, 32)
self.sparkmouseover:SetTexture([[Interface\CastingBar\UI-CastingBar-Spark]])
self.sparkmouseover:SetBlendMode("ADD")
self.sparkmouseover:SetPoint("LEFT", self, "RIGHT", -16, -1)
self.sparkmouseover:Hide()
self.sparktimer = self:CreateTexture("$parent_sparkTimer", "OVERLAY")
self.sparktimer:SetSize(32, 32)
self.sparktimer:SetPoint("LEFT", self.timertexture, "RIGHT", -16, -1)
self.sparktimer:SetTexture([[Interface\CastingBar\UI-CastingBar-Spark]])
self.sparktimer:SetBlendMode("ADD")
self.sparktimer:Hide()
self.lefttext = self:CreateFontString("$parent_TextLeft", "OVERLAY", "GameFontHighlight")
self.lefttext:SetJustifyH("LEFT")
self.lefttext:SetPoint("LEFT", self.icontexture, "RIGHT", 3, 0)
DF:SetFontSize(self.lefttext, 10)
self.righttext = self:CreateFontString("$parent_TextRight", "OVERLAY", "GameFontHighlight")
self.righttext:SetJustifyH("LEFT")
DF:SetFontSize(self.righttext, 10)
self.righttext:SetPoint("RIGHT", self, "RIGHT", -3, 0)
DetailsFrameworkNormalBar_OnCreate (self)
end
function DF:CreateBar (parent, texture, w, h, value, member, name)
return DF:NewBar (parent, parent, name, member, w, h, value, texture)
end
function DF:NewBar (parent, container, name, member, w, h, value, texture_name)
if (not name) then
name = "DetailsFrameworkBarNumber" .. DF.BarNameCounter
DF.BarNameCounter = DF.BarNameCounter + 1
elseif (not parent) then
return error("Details! FrameWork: parent not found.", 2)
elseif (not container) then
container = parent
end
if (name:find("$parent")) then
local parentName = DF.GetParentName(parent)
name = name:gsub("$parent", parentName)
end
local BarObject = {type = "bar", dframework = true}
if (member) then
parent [member] = BarObject
end
if (parent.dframework) then
parent = parent.widget
end
if (container.dframework) then
container = container.widget
end
value = value or 0
w = w or 150
h = h or 14
--default members:
--misc
BarObject.locked = false
BarObject.container = container
--create widgets
BarObject.statusbar = CreateFrame("statusbar", name, parent)
DF:Mixin(BarObject.statusbar, DF.WidgetFunctions)
build_statusbar (BarObject.statusbar)
BarObject.widget = BarObject.statusbar
if (not APIBarFunctions) then
APIBarFunctions = true
local idx = getmetatable(BarObject.statusbar).__index
for funcName, funcAddress in pairs(idx) do
if (not BarMetaFunctions [funcName]) then
BarMetaFunctions [funcName] = function(object, ...)
local x = loadstring ( "return _G['"..object.statusbar:GetName().."']:"..funcName.."(...)")
return x (...)
end
end
end
end
BarObject.statusbar:SetHeight(h)
BarObject.statusbar:SetWidth(w)
BarObject.statusbar:SetFrameLevel(parent:GetFrameLevel()+1)
BarObject.statusbar:SetMinMaxValues(0, 100)
BarObject.statusbar:SetValue(value or 50)
BarObject.statusbar.MyObject = BarObject
BarObject.timer_texture = _G [name .. "_timerTexture"]
BarObject.timer_texture:SetWidth(w)
BarObject.timer_texture:SetHeight(h)
BarObject.timer_textureR = _G [name .. "_timerTextureR"]
BarObject.timer_textureR:Hide()
BarObject._texture = _G [name .. "_statusbarTexture"]
BarObject.background = _G [name .. "_background"]
BarObject._icon = _G [name .. "_icon"]
BarObject.textleft = _G [name .. "_TextLeft"]
BarObject.textright = _G [name .. "_TextRight"]
BarObject.div = _G [name .. "_sparkMouseover"]
BarObject.div_timer = _G [name .. "_sparkTimer"]
--hooks
BarObject.HookList = {
OnEnter = {},
OnLeave = {},
OnHide = {},
OnShow = {},
OnMouseDown = {},
OnMouseUp = {},
OnTimerEnd = {},
}
BarObject.statusbar:SetScript("OnEnter", OnEnter)
BarObject.statusbar:SetScript("OnLeave", OnLeave)
BarObject.statusbar:SetScript("OnHide", OnHide)
BarObject.statusbar:SetScript("OnShow", OnShow)
BarObject.statusbar:SetScript("OnMouseDown", OnMouseDown)
BarObject.statusbar:SetScript("OnMouseUp", OnMouseUp)
--set class
setmetatable(BarObject, BarMetaFunctions)
--set texture
if (texture_name) then
smember_texture (BarObject, texture_name)
end
return BarObject
end --endd
@@ -0,0 +1,3 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ .. \FrameXML\UI.xsd">
<Script file="normal_bar.lua"/>
</Ui>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,3 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ .. \FrameXML\UI.xsd">
<Script file="panel.lua"/>
</Ui>
@@ -0,0 +1,346 @@
local detailsFramework = _G["DetailsFramework"]
if (not detailsFramework or not DetailsFrameworkCanLoad) then
return
end
local _
local APIImageFunctions = false
do
local metaPrototype = {
WidgetType = "image",
dversion = detailsFramework.dversion,
}
--check if there's a metaPrototype already existing
if (_G[detailsFramework.GlobalWidgetControlNames["image"]]) then
--get the already existing metaPrototype
local oldMetaPrototype = _G[detailsFramework.GlobalWidgetControlNames["image"]]
--check if is older
if ( (not oldMetaPrototype.dversion) or (oldMetaPrototype.dversion < detailsFramework.dversion) ) then
--the version is older them the currently loading one
--copy the new values into the old metatable
for funcName, _ in pairs(metaPrototype) do
oldMetaPrototype[funcName] = metaPrototype[funcName]
end
end
else
--first time loading the framework
_G[detailsFramework.GlobalWidgetControlNames["image"]] = metaPrototype
end
end
local ImageMetaFunctions = _G[detailsFramework.GlobalWidgetControlNames["image"]]
detailsFramework:Mixin(ImageMetaFunctions, detailsFramework.SetPointMixin)
detailsFramework:Mixin(ImageMetaFunctions, detailsFramework.ScriptHookMixin)
------------------------------------------------------------------------------------------------------------
--metatables
ImageMetaFunctions.__call = function(object, value)
return object.image:SetTexture(value)
end
------------------------------------------------------------------------------------------------------------
--members
--frame width
local gmember_width = function(object)
return object.image:GetWidth()
end
--frame height
local gmember_height = function(object)
return object.image:GetHeight()
end
--texture
local gmember_texture = function(object)
return object.image:GetTexture()
end
--alpha
local gmember_alpha = function(object)
return object.image:GetAlpha()
end
--saturation
local gmember_saturation = function(object)
return object.image:GetDesaturated()
end
--atlas
local gmember_atlas = function(object)
return object.image:GetAtlas()
end
--texcoords
local gmember_texcoord = function(object)
return object.image:GetTexCoord()
end
ImageMetaFunctions.GetMembers = ImageMetaFunctions.GetMembers or {}
detailsFramework:Mixin(ImageMetaFunctions.GetMembers, detailsFramework.DefaultMetaFunctionsGet)
detailsFramework:Mixin(ImageMetaFunctions.GetMembers, detailsFramework.LayeredRegionMetaFunctionsGet)
ImageMetaFunctions.GetMembers["alpha"] = gmember_alpha
ImageMetaFunctions.GetMembers["width"] = gmember_width
ImageMetaFunctions.GetMembers["height"] = gmember_height
ImageMetaFunctions.GetMembers["texture"] = gmember_texture
ImageMetaFunctions.GetMembers["blackwhite"] = gmember_saturation
ImageMetaFunctions.GetMembers["desaturated"] = gmember_saturation
ImageMetaFunctions.GetMembers["atlas"] = gmember_atlas
ImageMetaFunctions.GetMembers["texcoord"] = gmember_texcoord
ImageMetaFunctions.__index = function(object, key)
local func = ImageMetaFunctions.GetMembers[key]
if (func) then
return func(object, key)
end
local fromMe = rawget(object, key)
if (fromMe) then
return fromMe
end
return ImageMetaFunctions[key]
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--texture
local smember_texture = function(object, value)
if (type(value) == "table") then
local red, green, blue, alpha = detailsFramework:ParseColors(value)
object.image:SetTexture(red, green, blue, alpha)
else
if (detailsFramework:IsHtmlColor(value)) then
local red, green, blue, alpha = detailsFramework:ParseColors(value)
object.image:SetTexture(red, green, blue, alpha)
else
object.image:SetTexture(value)
end
end
end
--width
local smember_width = function(object, value)
return object.image:SetWidth(value)
end
--height
local smember_height = function(object, value)
return object.image:SetHeight(value)
end
--alpha
local smember_alpha = function(object, value)
return object.image:SetAlpha(value)
end
--color
local smember_color = function(object, value)
local red, green, blue, alpha = detailsFramework:ParseColors(value)
object.image:SetColorTexture(red, green, blue, alpha)
end
--vertex color
local smember_vertexcolor = function(object, value)
local red, green, blue, alpha = detailsFramework:ParseColors(value)
object.image:SetVertexColor(red, green, blue, alpha)
end
--desaturated
local smember_desaturated = function(object, value)
if (value) then
object:SetDesaturated(true)
else
object:SetDesaturated(false)
end
end
--texcoords
local smember_texcoord = function(object, value)
if (value) then
object:SetTexCoord(unpack(value))
else
object:SetTexCoord(0, 1, 0, 1)
end
end
--atlas
local smember_atlas = function(object, value)
if (value) then
object:SetAtlas(value)
end
end
--gradient
local smember_gradient = function(object, value)
if (type(value) == "table" and value.gradient and value.fromColor and value.toColor) then
object.image:SetColorTexture(1, 1, 1, 1)
local fromColor = detailsFramework:FormatColor("tablemembers", value.fromColor)
local toColor = detailsFramework:FormatColor("tablemembers", value.toColor)
object.image:SetGradient(value.gradient, fromColor, toColor)
else
error("texture.gradient expect a table{gradient = 'gradient type', fromColor = 'color', toColor = 'color'}")
end
end
ImageMetaFunctions.SetMembers = ImageMetaFunctions.SetMembers or {}
detailsFramework:Mixin(ImageMetaFunctions.SetMembers, detailsFramework.DefaultMetaFunctionsSet)
detailsFramework:Mixin(ImageMetaFunctions.SetMembers, detailsFramework.LayeredRegionMetaFunctionsSet)
ImageMetaFunctions.SetMembers["alpha"] = smember_alpha
ImageMetaFunctions.SetMembers["width"] = smember_width
ImageMetaFunctions.SetMembers["height"] = smember_height
ImageMetaFunctions.SetMembers["texture"] = smember_texture
ImageMetaFunctions.SetMembers["texcoord"] = smember_texcoord
ImageMetaFunctions.SetMembers["color"] = smember_color
ImageMetaFunctions.SetMembers["vertexcolor"] = smember_vertexcolor
ImageMetaFunctions.SetMembers["blackwhite"] = smember_desaturated
ImageMetaFunctions.SetMembers["desaturated"] = smember_desaturated
ImageMetaFunctions.SetMembers["atlas"] = smember_atlas
ImageMetaFunctions.SetMembers["gradient"] = smember_gradient
ImageMetaFunctions.__newindex = function(object, key, value)
local func = ImageMetaFunctions.SetMembers[key]
if (func) then
return func(object, value)
else
return rawset(object, key, value)
end
end
------------------------------------------------------------------------------------------------------------
--methods
--size
function ImageMetaFunctions:SetSize(width, height)
if (width) then
self.image:SetWidth(width)
end
if (height) then
return self.image:SetHeight(height)
end
end
function ImageMetaFunctions:SetGradient(gradientType, fromColor, toColor)
fromColor = detailsFramework:FormatColor("tablemembers", fromColor)
toColor = detailsFramework:FormatColor("tablemembers", toColor)
self.image:SetGradient(gradientType, fromColor, toColor)
end
------------------------------------------------------------------------------------------------------------
--object constructor
function detailsFramework:CreateTexture(parent, texture, width, height, layer, coords, member, name)
return detailsFramework:NewImage(parent, texture, width, height, layer, coords, member, name)
end
function detailsFramework:CreateImage(parent, texture, width, height, layer, coords, member, name)
return detailsFramework:NewImage(parent, texture, width, height, layer, coords, member, name)
end
function detailsFramework:NewImage(parent, texture, width, height, layer, texCoord, member, name)
if (not parent) then
return error("DetailsFrameWork: NewImage() parent not found.", 2)
end
if (not name) then
name = "DetailsFrameworkPictureNumber" .. detailsFramework.PictureNameCounter
detailsFramework.PictureNameCounter = detailsFramework.PictureNameCounter + 1
end
if (name:find("$parent")) then
local parentName = detailsFramework.GetParentName(parent)
name = name:gsub("$parent", parentName)
end
local ImageObject = {type = "image", dframework = true}
if (member) then
parent[member] = ImageObject
end
if (parent.dframework) then
parent = parent.widget
end
texture = texture or ""
ImageObject.image = parent:CreateTexture(name, layer or "overlay")
ImageObject.widget = ImageObject.image
detailsFramework:Mixin(ImageObject.image, detailsFramework.WidgetFunctions)
if (not APIImageFunctions) then
APIImageFunctions = true
local idx = getmetatable(ImageObject.image).__index
for funcName, funcAddress in pairs(idx) do
if (not ImageMetaFunctions[funcName]) then
ImageMetaFunctions[funcName] = function(object, ...)
local x = loadstring( "return _G['" .. object.image:GetName() .. "']:" .. funcName .. "(...)")
return x(...)
end
end
end
end
ImageObject.image.MyObject = ImageObject
if (width) then
ImageObject.image:SetWidth(width)
end
if (height) then
ImageObject.image:SetHeight(height)
end
if (texture) then
if (type(texture) == "table") then
if (texture.gradient) then
if (detailsFramework.IsDragonflight() or detailsFramework.IsWotLKWowWithRetailAPI()) then
ImageObject.image:SetColorTexture(1, 1, 1, 1)
local fromColor = detailsFramework:FormatColor("tablemembers", texture.fromColor)
local toColor = detailsFramework:FormatColor("tablemembers", texture.toColor)
ImageObject.image:SetGradient(texture.gradient, fromColor, toColor)
else
local fromR, fromG, fromB, fromA = detailsFramework:ParseColors(texture.fromColor)
local toR, toG, toB, toA = detailsFramework:ParseColors(texture.toColor)
ImageObject.image:SetColorTexture(1, 1, 1, 1)
ImageObject.image:SetGradientAlpha(texture.gradient, fromR, fromG, fromB, fromA, toR, toG, toB, toA)
end
else
local r, g, b, a = detailsFramework:ParseColors(texture)
ImageObject.image:SetColorTexture(r, g, b, a)
end
elseif (type(texture) == "string") then
local isAtlas = C_Texture.GetAtlasInfo(texture)
if (isAtlas) then
ImageObject.image:SetAtlas(texture)
else
if (detailsFramework:IsHtmlColor(texture)) then
local r, g, b = detailsFramework:ParseColors(texture)
ImageObject.image:SetColorTexture(r, g, b)
else
ImageObject.image:SetTexture(texture)
end
end
else
ImageObject.image:SetTexture(texture)
end
end
if (texCoord and type(texCoord) == "table" and texCoord[4]) then
ImageObject.image:SetTexCoord(unpack(texCoord))
end
ImageObject.HookList = {
}
setmetatable(ImageObject, ImageMetaFunctions)
return ImageObject
end
@@ -0,0 +1,607 @@
local DF = _G["DetailsFramework"]
if (not DF or not DetailsFrameworkCanLoad) then
return
end
local _
local texCoordinates
local CreateImageEditorFrame = function()
local editorWindow = DF:NewPanel(UIParent, nil, "DetailsFrameworkImageEdit", nil, 650, 500, false)
editorWindow:SetPoint("center", UIParent, "center")
editorWindow:SetResizable(true)
editorWindow:SetMovable(true)
editorWindow:SetClampedToScreen(true)
tinsert(UISpecialFrames, "DetailsFrameworkImageEdit")
editorWindow:SetFrameStrata("TOOLTIP")
if (not DetailsFramework.IsDragonflight()) then
editorWindow:SetMaxResize(500, 500)
else
editorWindow:SetResizeBounds(100, 100, 500, 500)
end
_G.DetailsFrameworkImageEditTable = editorWindow
editorWindow.hooks = {}
local background = DF:NewImage(editorWindow, nil, nil, nil, "background", nil, "background", "$parentBackground")
background:SetAllPoints()
background:SetTexture(0, 0, 0, .8)
local edit_texture = DF:NewImage(editorWindow, nil, 500, 500, "artwork", nil, "edit_texture", "$parentImage")
edit_texture:SetAllPoints()
_G.DetailsFrameworkImageEdit_EditTexture = edit_texture
local background_frame = CreateFrame("frame", "DetailsFrameworkImageEditBackground", DetailsFrameworkImageEdit, "BackdropTemplate")
background_frame:SetPoint("topleft", DetailsFrameworkImageEdit, "topleft", -10, 30)
background_frame:SetFrameStrata("TOOLTIP")
background_frame:SetFrameLevel(editorWindow:GetFrameLevel())
background_frame:SetSize(790, 560)
background_frame:SetResizable(true)
background_frame:SetMovable(true)
background_frame:SetScript("OnMouseDown", function()
editorWindow:StartMoving()
end)
background_frame:SetScript("OnMouseUp", function()
editorWindow:StopMovingOrSizing()
end)
DF:CreateTitleBar (background_frame, "Image Editor")
DF:ApplyStandardBackdrop(background_frame, false, 0.98)
DF:CreateStatusBar(background_frame)
background_frame:SetBackdrop({edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true})
background_frame:SetBackdropColor(0, 0, 0, 0.9)
background_frame:SetBackdropBorderColor(0, 0, 0, 1)
local haveHFlip = false
local haveVFlip = false
--Top Slider
local topCoordTexture = DF:NewImage(editorWindow, nil, nil, nil, "overlay", nil, nil, "$parentImageTopCoord")
topCoordTexture:SetPoint("topleft", editorWindow, "topleft")
topCoordTexture:SetPoint("topright", editorWindow, "topright")
topCoordTexture:SetColorTexture(1, 0, 0)
topCoordTexture.height = 1
topCoordTexture.alpha = .2
local topSlider = DF:NewSlider (editorWindow, nil, "$parentTopSlider", "topSlider", 100, 100, 0.1, 100, 0.1, 0)
topSlider:SetAllPoints(editorWindow.widget)
topSlider:SetOrientation("VERTICAL")
topSlider.backdrop = nil
topSlider.fractional = true
topSlider:SetHook("OnEnter", function() return true end)
topSlider:SetHook("OnLeave", function() return true end)
local topSliderThumpTexture = topSlider:CreateTexture(nil, "overlay")
topSliderThumpTexture:SetColorTexture(1, 1, 1)
topSliderThumpTexture:SetWidth(512)
topSliderThumpTexture:SetHeight(1)
topSlider:SetThumbTexture (topSliderThumpTexture)
topSlider:SetHook("OnValueChange", function(_, _, value)
topCoordTexture.image:SetHeight(editorWindow.frame:GetHeight()/100*value)
if (editorWindow.callback_func) then
editorWindow.accept(nil, nil, true)
end
end)
topSlider:Hide()
--Bottom Slider
local bottomCoordTexture = DF:NewImage(editorWindow, nil, nil, nil, "overlay", nil, nil, "$parentImageBottomCoord")
bottomCoordTexture:SetPoint("bottomleft", editorWindow, "bottomleft", 0, 0)
bottomCoordTexture:SetPoint("bottomright", editorWindow, "bottomright", 0, 0)
bottomCoordTexture:SetColorTexture(1, 0, 0)
bottomCoordTexture.height = 1
bottomCoordTexture.alpha = .2
local bottomSlider = DF:NewSlider (editorWindow, nil, "$parentBottomSlider", "bottomSlider", 100, 100, 0.1, 100, 0.1, 100)
bottomSlider:SetAllPoints(editorWindow.widget)
bottomSlider:SetOrientation("VERTICAL")
bottomSlider.backdrop = nil
bottomSlider.fractional = true
bottomSlider:SetHook("OnEnter", function() return true end)
bottomSlider:SetHook("OnLeave", function() return true end)
local bottomSliderThumpTexture = bottomSlider:CreateTexture(nil, "overlay")
bottomSliderThumpTexture:SetColorTexture(1, 1, 1)
bottomSliderThumpTexture:SetWidth(512)
bottomSliderThumpTexture:SetHeight(1)
bottomSlider:SetThumbTexture (bottomSliderThumpTexture)
bottomSlider:SetHook("OnValueChange", function(_, _, value)
value = math.abs(value-100)
bottomCoordTexture.image:SetHeight(math.max(editorWindow.frame:GetHeight()/100*value, 1))
if (editorWindow.callback_func) then
editorWindow.accept(nil, nil, true)
end
end)
bottomSlider:Hide()
--Left Slider
local leftCoordTexture = DF:NewImage(editorWindow, nil, nil, nil, "overlay", nil, nil, "$parentImageLeftCoord")
leftCoordTexture:SetPoint("topleft", editorWindow, "topleft", 0, 0)
leftCoordTexture:SetPoint("bottomleft", editorWindow, "bottomleft", 0, 0)
leftCoordTexture:SetColorTexture(1, 0, 0)
leftCoordTexture.width = 1
leftCoordTexture.alpha = .2
local leftSlider = DF:NewSlider (editorWindow, nil, "$parentLeftSlider", "leftSlider", 100, 100, 0.1, 100, 0.1, 0.1)
leftSlider:SetAllPoints(editorWindow.widget)
leftSlider.backdrop = nil
leftSlider.fractional = true
leftSlider:SetHook("OnEnter", function() return true end)
leftSlider:SetHook("OnLeave", function() return true end)
local leftSliderThumpTexture = leftSlider:CreateTexture(nil, "overlay")
leftSliderThumpTexture:SetColorTexture(1, 1, 1)
leftSliderThumpTexture:SetWidth(1)
leftSliderThumpTexture:SetHeight(512)
leftSlider:SetThumbTexture (leftSliderThumpTexture)
leftSlider:SetHook("OnValueChange", function(_, _, value)
leftCoordTexture.image:SetWidth(editorWindow.frame:GetWidth()/100*value)
if (editorWindow.callback_func) then
editorWindow.accept(nil, nil, true)
end
end)
leftSlider:Hide()
--Right Slider
local rightCoordTexture = DF:NewImage(editorWindow, nil, nil, nil, "overlay", nil, nil, "$parentImageRightCoord")
rightCoordTexture:SetPoint("topright", editorWindow, "topright", 0, 0)
rightCoordTexture:SetPoint("bottomright", editorWindow, "bottomright", 0, 0)
rightCoordTexture:SetColorTexture(1, 0, 0)
rightCoordTexture.width = 1
rightCoordTexture.alpha = .2
local rightSlider = DF:NewSlider (editorWindow, nil, "$parentRightSlider", "rightSlider", 100, 100, 0.1, 100, 0.1, 100)
rightSlider:SetAllPoints(editorWindow.widget)
rightSlider.backdrop = nil
rightSlider.fractional = true
rightSlider:SetHook("OnEnter", function() return true end)
rightSlider:SetHook("OnLeave", function() return true end)
--[
local rightSliderThumpTexture = rightSlider:CreateTexture(nil, "overlay")
rightSliderThumpTexture:SetColorTexture(1, 1, 1)
rightSliderThumpTexture:SetWidth(1)
rightSliderThumpTexture:SetHeight(512)
rightSlider:SetThumbTexture (rightSliderThumpTexture)
--]]
rightSlider:SetHook("OnValueChange", function(_, _, value)
value = math.abs(value-100)
rightCoordTexture.image:SetWidth(math.max(editorWindow.frame:GetWidth()/100*value, 1))
if (editorWindow.callback_func) then
editorWindow.accept(nil, nil, true)
end
end)
rightSlider:Hide()
--Edit Buttons
local buttonsBackground = DF:NewPanel(UIParent, nil, "DetailsFrameworkImageEditButtonsBg", nil, 115, 230)
--buttonsBackground:SetPoint("topleft", window, "topright", 2, 0)
buttonsBackground:SetPoint("topright", background_frame, "topright", -8, -10)
buttonsBackground:Hide()
--buttonsBackground:SetMovable(true)
tinsert(UISpecialFrames, "DetailsFrameworkImageEditButtonsBg")
buttonsBackground:SetFrameStrata("TOOLTIP")
local alphaFrameShown = false
local editingSide = nil
local lastButton = nil
local alphaFrame
local originalColor = {0.9999, 0.8196, 0}
local enableTexEdit = function(button, bottom, side)
if (alphaFrameShown) then
alphaFrame:Hide()
alphaFrameShown = false
button.text:SetTextColor(unpack(originalColor))
end
if (ColorPickerFrame:IsShown()) then
ColorPickerFrame:Hide()
end
if (lastButton) then
lastButton.text:SetTextColor(unpack(originalColor))
end
if (editingSide == side) then
editorWindow [editingSide.."Slider"]:Hide()
editingSide = nil
return
elseif (editingSide) then
editorWindow [editingSide.."Slider"]:Hide()
end
editingSide = side
button.text:SetTextColor(1, 1, 1)
lastButton = button
editorWindow [side.."Slider"]:Show()
end
local yMod = -10
local leftTexCoordButton = DF:NewButton(buttonsBackground, nil, "$parentLeftTexButton", nil, 100, 20, enableTexEdit, "left", nil, nil, "Crop Left", 1)
leftTexCoordButton:SetPoint("topright", buttonsBackground, "topright", -8, -10 + yMod)
leftTexCoordButton:SetTemplate(DF:GetTemplate("dropdown", "OPTIONS_DROPDOWN_TEMPLATE"))
local rightTexCoordButton = DF:NewButton(buttonsBackground, nil, "$parentRightTexButton", nil, 100, 20, enableTexEdit, "right", nil, nil, "Crop Right", 1)
rightTexCoordButton:SetPoint("topright", buttonsBackground, "topright", -8, -30 + yMod)
rightTexCoordButton:SetTemplate(DF:GetTemplate("dropdown", "OPTIONS_DROPDOWN_TEMPLATE"))
local topTexCoordButton = DF:NewButton(buttonsBackground, nil, "$parentTopTexButton", nil, 100, 20, enableTexEdit, "top", nil, nil, "Crop Top", 1)
topTexCoordButton:SetPoint("topright", buttonsBackground, "topright", -8, -50 + yMod)
topTexCoordButton:SetTemplate(DF:GetTemplate("dropdown", "OPTIONS_DROPDOWN_TEMPLATE"))
local bottomTexCoordButton = DF:NewButton(buttonsBackground, nil, "$parentBottomTexButton", nil, 100, 20, enableTexEdit, "bottom", nil, nil, "Crop Bottom", 1)
bottomTexCoordButton:SetPoint("topright", buttonsBackground, "topright", -8, -70 + yMod)
bottomTexCoordButton:SetTemplate(DF:GetTemplate("dropdown", "OPTIONS_DROPDOWN_TEMPLATE"))
local Alpha = DF:NewButton(buttonsBackground, nil, "$parentBottomAlphaButton", nil, 100, 20, alpha, nil, nil, nil, "Alpha", 1)
Alpha:SetPoint("topright", buttonsBackground, "topright", -8, -115 + yMod)
Alpha:SetTemplate(DF:GetTemplate("dropdown", "OPTIONS_DROPDOWN_TEMPLATE"))
--overlay color
local selectedColor = function(default)
if (default) then
edit_texture:SetVertexColor(unpack(default))
if (editorWindow.callback_func) then
editorWindow.accept(nil, nil, true)
end
else
edit_texture:SetVertexColor(ColorPickerFrame:GetColorRGB())
if (editorWindow.callback_func) then
editorWindow.accept(nil, nil, true)
end
end
end
local changeColor = function()
ColorPickerFrame.func = nil
ColorPickerFrame.opacityFunc = nil
ColorPickerFrame.cancelFunc = nil
ColorPickerFrame.previousValues = nil
local right, g, bottom = edit_texture:GetVertexColor()
ColorPickerFrame:SetColorRGB (right, g, bottom)
ColorPickerFrame:SetParent(buttonsBackground.widget)
ColorPickerFrame.hasOpacity = false
ColorPickerFrame.previousValues = {right, g, bottom}
ColorPickerFrame.func = selectedColor
ColorPickerFrame.cancelFunc = selectedColor
ColorPickerFrame:ClearAllPoints()
ColorPickerFrame:SetPoint("left", buttonsBackground.widget, "right")
ColorPickerFrame:Show()
if (alphaFrameShown) then
alphaFrame:Hide()
alphaFrameShown = false
Alpha.button.text:SetTextColor(unpack(originalColor))
end
if (lastButton) then
lastButton.text:SetTextColor(unpack(originalColor))
if (editingSide) then
editorWindow [editingSide.."Slider"]:Hide()
end
end
end
local changeColorButton = DF:NewButton(buttonsBackground, nil, "$parentOverlayColorButton", nil, 100, 20, changeColor, nil, nil, nil, "Color", 1)
changeColorButton:SetPoint("topright", buttonsBackground, "topright", -8, -95 + yMod)
changeColorButton:SetTemplate(DF:GetTemplate("dropdown", "OPTIONS_DROPDOWN_TEMPLATE"))
alphaFrame = DF:NewPanel(buttonsBackground, nil, "DetailsFrameworkImageEditAlphaBg", nil, 40, 225)
alphaFrame:SetPoint("topleft", buttonsBackground, "topright", 2, 0)
alphaFrame:Hide()
local alphaSlider = DF:NewSlider (alphaFrame, nil, "$parentAlphaSlider", "alphaSlider", 30, 220, 1, 100, 1, edit_texture:GetAlpha()*100)
alphaSlider:SetPoint("top", alphaFrame, "top", 0, -5)
alphaSlider:SetOrientation("VERTICAL")
alphaSlider.thumb:SetSize(40, 30)
--leftSlider.backdrop = nil
--leftSlider.fractional = true
local alpha = function(button)
if (ColorPickerFrame:IsShown()) then
ColorPickerFrame:Hide()
end
if (lastButton) then
lastButton.text:SetTextColor(unpack(originalColor))
if (editingSide) then
editorWindow [editingSide.."Slider"]:Hide()
end
end
if (not alphaFrameShown) then
alphaFrame:Show()
alphaSlider:SetValue(edit_texture:GetAlpha()*100)
alphaFrameShown = true
button.text:SetTextColor(1, 1, 1)
else
alphaFrame:Hide()
alphaFrameShown = false
button.text:SetTextColor(unpack(originalColor))
end
end
Alpha.clickfunction = alpha
alphaSlider:SetHook("OnValueChange", function(_, _, value)
edit_texture:SetAlpha(value/100)
if (editorWindow.callback_func) then
editorWindow.accept(nil, nil, true)
end
end)
local resizer = CreateFrame("Button", nil, editorWindow.widget, "BackdropTemplate")
resizer:SetNormalTexture([[Interface\AddOns\Details\images\skins\default_skin]])
resizer:SetHighlightTexture([[Interface\AddOns\Details\images\skins\default_skin]])
resizer:GetNormalTexture():SetTexCoord(0.00146484375, 0.01513671875, 0.24560546875, 0.25927734375)
resizer:GetHighlightTexture():SetTexCoord(0.00146484375, 0.01513671875, 0.24560546875, 0.25927734375)
resizer:SetWidth(16)
resizer:SetHeight(16)
resizer:SetPoint("BOTTOMRIGHT", editorWindow.widget, "BOTTOMRIGHT", 0, 0)
resizer:EnableMouse(true)
resizer:SetFrameLevel(editorWindow.widget:GetFrameLevel() + 2)
resizer:SetScript("OnMouseDown", function(self, button)
editorWindow.widget:StartSizing("BOTTOMRIGHT")
end)
resizer:SetScript("OnMouseUp", function(self, button)
editorWindow.widget:StopMovingOrSizing()
end)
editorWindow.widget:SetScript("OnMouseDown", function()
editorWindow.widget:StartMoving()
end)
editorWindow.widget:SetScript("OnMouseUp", function()
editorWindow.widget:StopMovingOrSizing()
end)
editorWindow.widget:SetScript("OnSizeChanged", function()
edit_texture.width = editorWindow.width
edit_texture.height = editorWindow.height
leftSliderThumpTexture:SetHeight(editorWindow.height)
rightSliderThumpTexture:SetHeight(editorWindow.height)
topSliderThumpTexture:SetWidth(editorWindow.width)
bottomSliderThumpTexture:SetWidth(editorWindow.width)
rightCoordTexture.image:SetWidth(math.max( (editorWindow.frame:GetWidth() / 100 * math.abs(rightSlider:GetValue()-100)), 1))
leftCoordTexture.image:SetWidth(editorWindow.frame:GetWidth()/100*leftSlider:GetValue())
bottomCoordTexture:SetHeight(math.max( (editorWindow.frame:GetHeight() / 100 * math.abs(bottomSlider:GetValue()-100)), 1))
topCoordTexture:SetHeight(editorWindow.frame:GetHeight()/100*topSlider:GetValue())
if (editorWindow.callback_func) then
editorWindow.accept(nil, nil, true)
end
end)
--flip button
local flip = function(button, bottom, side)
if (side == 1) then
haveHFlip = not haveHFlip
if (editorWindow.callback_func) then
editorWindow.accept(nil, nil, true)
end
elseif (side == 2) then
haveVFlip = not haveVFlip
if (editorWindow.callback_func) then
editorWindow.accept(nil, nil, true)
end
end
end
local flipButtonH = DF:NewButton(buttonsBackground, nil, "$parentFlipButton", nil, 100, 20, flip, 1, nil, nil, "Flip H", 1)
flipButtonH:SetPoint("topright", buttonsBackground, "topright", -8, -140 + yMod)
flipButtonH:SetTemplate(DF:GetTemplate("dropdown", "OPTIONS_DROPDOWN_TEMPLATE"))
--select area to crop
local dragFrame = CreateFrame("frame", nil, background_frame, "BackdropTemplate")
dragFrame:EnableMouse(false)
dragFrame:SetFrameStrata("TOOLTIP")
dragFrame:SetPoint("topleft", edit_texture.widget, "topleft")
dragFrame:SetPoint("bottomright", edit_texture.widget, "bottomright")
dragFrame:SetBackdrop({edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Worldmap\UI-QuestBlob-Inside]], tileSize = 256, tile = true})
dragFrame:SetBackdropColor(1, 1, 1, .2)
dragFrame:Hide()
local selectionBoxUp = dragFrame:CreateTexture(nil, "overlay")
selectionBoxUp:SetHeight(1)
selectionBoxUp:SetColorTexture(1, 1, 1)
local selectionBoxDown = dragFrame:CreateTexture(nil, "overlay")
selectionBoxDown:SetHeight(1)
selectionBoxDown:SetColorTexture(1, 1, 1)
local selectionBoxLeft = dragFrame:CreateTexture(nil, "overlay")
selectionBoxLeft:SetWidth(1)
selectionBoxLeft:SetColorTexture(1, 1, 1)
local selectionBoxRight = dragFrame:CreateTexture(nil, "overlay")
selectionBoxRight:SetWidth(1)
selectionBoxRight:SetColorTexture(1, 1, 1)
function dragFrame.ClearSelectionBoxPoints()
selectionBoxUp:ClearAllPoints()
selectionBoxDown:ClearAllPoints()
selectionBoxLeft:ClearAllPoints()
selectionBoxRight:ClearAllPoints()
end
local startCropFunc = function()
dragFrame:Show()
dragFrame:EnableMouse(true)
end
local cropSelection = DF:NewButton(buttonsBackground, nil, "$parentCropSelection", nil, 100, 20, startCropFunc, 2, nil, nil, "Crop Selection", 1)
cropSelection:InstallCustomTexture()
dragFrame.OnTick = function(self, deltaTime)
local x1, y1 = unpack(self.ClickedAt)
local x2, y2 = GetCursorPosition()
dragFrame.ClearSelectionBoxPoints()
if (x2 > x1) then
--right
if (y1 > y2) then
--top
selectionBoxUp:SetPoint("topleft", UIParent, "bottomleft", x1, y1)
selectionBoxUp:SetPoint("topright", UIParent, "bottomleft", x2, y1)
selectionBoxLeft:SetPoint("topleft", UIParent, "bottomleft", x1, y1)
selectionBoxLeft:SetPoint("bottomleft", UIParent, "bottomleft", x1, y2)
else
--bottom
end
else
--left
if (y2 > y1) then
--top
else
--bottom
end
end
end
dragFrame:SetScript("OnMouseDown", function(self, MouseButton)
if (MouseButton == "LeftButton") then
self.ClickedAt = {GetCursorPosition()}
dragFrame:SetScript("OnUpdate", dragFrame.OnTick)
end
end)
dragFrame:SetScript("OnMouseUp", function(self, MouseButton)
if (MouseButton == "LeftButton") then
self.ReleaseAt = {GetCursorPosition()}
dragFrame:EnableMouse(false)
dragFrame:Hide()
dragFrame:SetScript("OnUpdate", nil)
print(self.ClickedAt[1], self.ClickedAt[2], self.ReleaseAt[1], self.ReleaseAt[2])
end
end)
--accept
editorWindow.accept = function(self, bottom, keepEditing)
if (not keepEditing) then
buttonsBackground:Hide()
editorWindow:Hide()
alphaFrame:Hide()
ColorPickerFrame:Hide()
end
local coords = {}
local left, right, top, bottom = leftSlider.value/100, rightSlider.value/100, topSlider.value/100, bottomSlider.value/100
if (haveHFlip) then
coords [1] = right
coords [2] = left
else
coords [1] = left
coords [2] = right
end
if (haveVFlip) then
coords [3] = bottom
coords [4] = top
else
coords [3] = top
coords [4] = bottom
end
return editorWindow.callback_func(edit_texture.width, edit_texture.height, {edit_texture:GetVertexColor()}, edit_texture:GetAlpha(), coords, editorWindow.extra_param)
end
local acceptButton = DF:NewButton(buttonsBackground, nil, "$parentAcceptButton", nil, 100, 20, editorWindow.accept, nil, nil, nil, "Done", 1)
acceptButton:SetPoint("topright", buttonsBackground, "topright", -8, -200)
acceptButton:SetTemplate(DF:GetTemplate("dropdown", "OPTIONS_DROPDOWN_TEMPLATE"))
function DF:RefreshImageEditor()
if (edit_texture.maximize) then
DetailsFrameworkImageEdit:SetSize(266, 226)
else
DetailsFrameworkImageEdit:SetSize(edit_texture.width, edit_texture.height)
end
local left, right, top, bottom = unpack(texCoordinates)
if (left > right) then
haveHFlip = true
leftSlider:SetValue(right * 100)
rightSlider:SetValue(left * 100)
else
haveHFlip = false
leftSlider:SetValue(left * 100)
rightSlider:SetValue(right * 100)
end
if (top > bottom) then
haveVFlip = true
topSlider:SetValue(bottom * 100)
bottomSlider:SetValue(top * 100)
else
haveVFlip = false
topSlider:SetValue(top * 100)
bottomSlider:SetValue(bottom * 100)
end
if (editorWindow.callback_func) then
editorWindow.accept(nil, nil, true)
end
end
editorWindow:Hide()
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function DF:ImageEditor(callback, texture, texcoord, colors, width, height, extraParam, alpha, maximize)
if (not _G.DetailsFrameworkImageEdit) then
CreateImageEditorFrame()
end
local window = _G.DetailsFrameworkImageEditTable
texcoord = texcoord or {0, 1, 0, 1}
texCoordinates = texcoord
colors = colors or {1, 1, 1, 1}
alpha = alpha or 1
_G.DetailsFrameworkImageEdit_EditTexture:SetTexture(texture)
_G.DetailsFrameworkImageEdit_EditTexture.width = width
_G.DetailsFrameworkImageEdit_EditTexture.height = height
_G.DetailsFrameworkImageEdit_EditTexture.maximize = maximize
_G.DetailsFrameworkImageEdit_EditTexture:SetVertexColor(colors [1], colors [2], colors [3])
_G.DetailsFrameworkImageEdit_EditTexture:SetAlpha(alpha)
DF.Schedules.NewTimer(0.2, DF.RefreshImageEditor)
window:Show()
window.callback_func = callback
window.extra_param = extraParam
DetailsFrameworkImageEditButtonsBg:Show()
DetailsFrameworkImageEditButtonsBg:SetBackdrop(nil)
table.wipe(window.hooks)
end
@@ -0,0 +1,219 @@
--stopped doing the duplicate savedTable
local DF = _G ["DetailsFramework"]
if (not DF or not DetailsFrameworkCanLoad) then
return
end
--create namespace
DF.SavedVars = {}
function DF.SavedVars.CreateNewSavedTable(dbTable, savedTableName)
local defaultVars = dbTable.defaultSavedVars
local newSavedTable = DF.table.deploy({}, defaultVars)
dbTable.profiles[savedTableName] = newSavedTable
return newSavedTable
end
function DF.SavedVars.GetOrCreateAddonSavedTablesPlayerList(addonFrame)
local addonGlobalSavedTable = _G[addonFrame.__savedVarsName]
--player list
local playerList = addonGlobalSavedTable.__savedVarsByGUID
if (not playerList) then
addonGlobalSavedTable.__savedVarsByGUID = {}
end
--saved variables table
if (not addonGlobalSavedTable.__savedVars) then
addonGlobalSavedTable.__savedVars = {}
end
return addonGlobalSavedTable.__savedVarsByGUID
end
--addon statup
function DF.SavedVars.LoadSavedVarsForPlayer(addonFrame)
local playerSerial = UnitGUID("player")
--savedTableObject is equivalent of "addon.db"
local dbTable = DF.SavedVars.CreateSavedVarsTable(addonFrame, addonFrame.__savedVarsDefaultTemplate)
addonFrame.__savedVarsDefaultTemplate = nil
addonFrame.db = dbTable
--load players list
local savedVarsName = DF.SavedVars.GetOrCreateAddonSavedTablesPlayerList(addonFrame)
local playerSavedTableName = savedVarsName[playerSerial]
if (not playerSavedTableName) then
savedVarsName[playerSerial] = "Default"
playerSavedTableName = savedVarsName[playerSerial]
end
local savedTable = addonFrame.db:GetSavedTable(playerSavedTableName)
if (not savedTable) then
--create a new saved table for this character
savedTable = addonFrame.db:CreateNewSavedTable(playerSavedTableName)
end
DF.SavedVars.SetSavedTable(dbTable, playerSavedTableName, true, true)
return savedTable
end
function DF.SavedVars.TableCleanUpRecursive(t, default)
for key, value in pairs(t) do
if (type(value) == "table") then
DF.SavedVars.TableCleanUpRecursive(value, default[key])
else
if (value == default[key]) then
t[key] = nil
end
end
end
end
function DF.SavedVars.CloseSavedTable(dbTable)
local currentSavedTable = dbTable:GetSavedTable(dbTable:GetCurrentSavedTableName())
local default = dbTable.defaultSavedVars
if (type(currentSavedTable) == "table") then
DF.SavedVars.TableCleanUpRecursive(currentSavedTable, default)
--save
local addonGlobalSavedTable = _G[dbTable.addonFrame.__savedVarsName]
addonGlobalSavedTable.__savedVars[dbTable:GetCurrentSavedTableName()] = currentSavedTable
end
end
--base functions
function DF.SavedVars.SetSavedTable(dbTable, savedTableName, createIfNonExistant, isFromInit)
local savedTableToBeApplied = dbTable:GetSavedTable(savedTableName)
if (savedTableToBeApplied) then
if (not isFromInit) then
--callback unload profile table
local currentSavedTable = dbTable:GetSavedTable(dbTable:GetCurrentSavedTableName())
dbTable:TriggerCallback("OnProfileUnload", currentSavedTable)
DF.SavedVars.CloseSavedTable(dbTable, currentSavedTable)
end
dbTable.profile = savedTableToBeApplied
dbTable.currentSavedTableName = savedTableName
dbTable:TriggerCallback("OnProfileLoad", savedTableToBeApplied)
else
if (createIfNonExistant) then
local newSavedTable = dbTable:CreateNewSavedTable(savedTableName)
--callback unload profile table
local currentSavedTable = dbTable:GetSavedTable(dbTable:GetCurrentSavedTableName())
dbTable:TriggerCallback("OnProfileUnload", currentSavedTable)
DF.SavedVars.CloseSavedTable(dbTable, currentSavedTable)
dbTable.profile = newSavedTable
dbTable.currentSavedTableName = savedTableName
dbTable:TriggerCallback("OnProfileLoad", newSavedTable)
else
DF:Msg("profile does not exists", savedTableName)
return
end
end
end
function DF.SavedVars.GetSavedTables(dbTable)
return dbTable.profiles
end
function DF.SavedVars.GetSavedTable(dbTable, savedTableName)
local profiles = dbTable:GetSavedTables()
return profiles[savedTableName]
end
function DF.SavedVars.GetCurrentSavedTableName(dbTable)
return dbTable.currentSavedTableName
end
--duplicate savedTable
function DF.SavedVars.DuplicateSavedTable(dbTable, savedTableName)
local originalSavedTable = dbTable:GetSavedTable(savedTableName)
if (originalSavedTable) then
local newSavedTable = DF.table.copy({}, originalSavedTable)
end
end
--callbacks
function DF.SavedVars.TriggerCallback(dbTable, callbackName, savedTable)
local registeredCallbacksTable = dbTable.registeredCallbacks[callbackName]
for i = 1, #registeredCallbacksTable do
local callback = registeredCallbacksTable[i]
DF:CoreDispatch(dbTable.addonFrame.__name, callback.func, savedTable, unpack(callback.payload))
end
end
function DF.SavedVars.RegisterCallback(dbTable, callbackName, func, ...)
local registeredCallbacksTable = dbTable.registeredCallbacks[callbackName]
if (registeredCallbacksTable) then
--check for duplicates
for i = 1, #registeredCallbacksTable do
if (registeredCallbacksTable[i].func == func) then
return
end
end
--register
registeredCallbacksTable[#registeredCallbacksTable+1] = {func = func, payload = {...}}
return true
end
end
function DF.SavedVars.UnregisterCallback(dbTable, callbackName, func)
local registeredCallbacksTable = dbTable.registeredCallbacks[callbackName]
if (registeredCallbacksTable) then
for i = 1, #registeredCallbacksTable do
if (registeredCallbacksTable[i].func == func) then
tremove(registeredCallbacksTable, i)
return true
end
end
end
end
function DF.SavedVars.CreateSavedVarsTable(addonFrame, templateTable)
local dbTable = {
profiles = {},
defaultSavedVars = templateTable,
currentSavedTableName = "",
addonFrame = addonFrame,
--methods
GetSavedTable = DF.SavedVars.GetSavedTable,
SetSavedTable = DF.SavedVars.SetSavedTable,
GetSavedTables = DF.SavedVars.GetSavedTables,
GetCurrentSavedTableName = DF.SavedVars.GetCurrentSavedTableName,
CreateNewSavedTable = DF.SavedVars.CreateNewSavedTable,
TriggerCallback = DF.SavedVars.TriggerCallback,
--back compatibility with ace3DB
GetCurrentProfile = DF.SavedVars.GetCurrentSavedTableName,
GetProfile = DF.SavedVars.GetSavedTable,
GetProfiles = DF.SavedVars.GetSavedTables,
SetProfile = DF.SavedVars.SetSavedTable,
RegisterCallback = DF.SavedVars.RegisterCallback,
registeredCallbacks = {
["OnProfileLoad"] = {},
["OnProfileUnload"] = {},
["OnProfileCopied"] = {},
["OnProfileReset"] = {},
["OnDatabaseLoad"] = {},
["OnDatabaseShutdown"] = {},
},
}
return dbTable
end
@@ -0,0 +1,73 @@
local DF = _G["DetailsFramework"]
if (not DF or not DetailsFrameworkCanLoad) then
return
end
local C_Timer = _G.C_Timer
local unpack = table.unpack or _G.unpack
--make a namespace for schedules
DF.Schedules = DF.Schedules or {}
--run a scheduled function with its payload
local triggerScheduledTick = function(tickerObject)
local payload = tickerObject.payload
local callback = tickerObject.callback
local result, errortext = pcall(callback, unpack(payload))
if (not result) then
DF:Msg("error on scheduler: ", tickerObject.path, tickerObject.name, errortext)
end
return result
end
--schedule to repeat a task with an interval of @time
function DF.Schedules.NewTicker(time, callback, ...)
local payload = {...}
local newTicker = C_Timer.NewTicker(time, triggerScheduledTick)
newTicker.payload = payload
newTicker.callback = callback
newTicker.expireAt = GetTime() + time
--debug
newTicker.path = debugstack()
--
return newTicker
end
--schedule a task with an interval of @time
function DF.Schedules.NewTimer(time, callback, ...)
local payload = {...}
local newTimer = C_Timer.NewTimer(time, triggerScheduledTick)
newTimer.payload = payload
newTimer.callback = callback
newTimer.expireAt = GetTime() + time
--debug
newTimer.path = debugstack()
--
return newTimer
end
--cancel an ongoing ticker
function DF.Schedules.Cancel(tickerObject)
--ignore if there's no ticker object
if (tickerObject) then
return tickerObject:Cancel()
end
end
--schedule a task with an interval of @time without payload
function DF.Schedules.After(time, callback)
C_Timer.After(time, callback)
end
function DF.Schedules.SetName(object, name)
object.name = name
end
function DF.Schedules.RunNextTick(callback)
return DF.Schedules.After(0, callback)
end
@@ -0,0 +1,132 @@
local detailsFramework = DetailsFramework
if (not detailsFramework or not DetailsFrameworkCanLoad) then
return
end
local unpack = unpack
local CreateFrame = CreateFrame
local geterrorhandler = geterrorhandler
local wipe = wipe
local parseCodeForNamedLocalFunctions = function(codeBlock, startIndex, listOfFunctionsFound)
local nestedLevel = 0
local endIndex = startIndex
local currentQuote = ""
---@type number for the 'function' keyword, need to ignore the one that started the 'local function' capture
local ignoreFunctionIndex = startIndex + 6
---@type boolean
local bFoundEnd = false
---@type boolean
local bIsInString = false
---@type boolean
local bIsInComment = false
while (endIndex <= #codeBlock) do
local char = string.sub(codeBlock, endIndex, endIndex)
--check if the character is inside a comment
if (char == "-") then
local nextChar = string.sub(codeBlock, endIndex + 1, endIndex + 1)
if nextChar == "-" then
bIsInComment = true
end
elseif (char == "\n") then
bIsInComment = false
end
if (not bIsInComment) then
--check if it is inside a string
if (char == "'" or char == '"') then
if (not bIsInString) then
bIsInString = true
currentQuote = char
elseif (bIsInString and currentQuote == char) then
bIsInString = false
currentQuote = ""
end
end
if (not bIsInString) then
--check if the word starts with "i", "f", "d" or "e"
if (char == "i") then
local nextChars = string.sub(codeBlock, endIndex, endIndex + 1)
if (nextChars == "if") then
nestedLevel = nestedLevel + 1
end
elseif (char == "f") then
local nextChars = string.sub(codeBlock, endIndex, endIndex + 7)
--also check if the index isn't the one that started the 'local function' capture
if (nextChars == "function" and endIndex ~= ignoreFunctionIndex) then
nestedLevel = nestedLevel + 1
end
--for 'do' keyword, used by for and while and also by the 'do' keyword itself creating a block
elseif (char == "d") then
local nextChars = string.sub(codeBlock, endIndex, endIndex + 1)
if (nextChars == "do") then
nestedLevel = nestedLevel + 1
end
elseif (char == "e") then
local nextChars = string.sub(codeBlock, endIndex, endIndex + 2)
if (nextChars == "end") then
if (nestedLevel > 0) then
--reduce the nested level by 1
nestedLevel = nestedLevel - 1
else
--if the nested level is zero then the end of the function got found
bFoundEnd = true
endIndex = endIndex + 2 --adjust endIndex to include the 'end' keyword
break
end
end
end
end
end
endIndex = endIndex + 1
end
if (bFoundEnd) then
---@type string get the function body
local functionBody = string.sub(codeBlock, startIndex, endIndex)
table.insert(listOfFunctionsFound, functionBody)
return endIndex
end
end
---search a code block for named local functions and bring them to the top of the code block
---this is useful for when you want to call a function before it's defined
---same thing as been implemented in Lua 5.2 but not in WoW Lua
---@param codeBlock string
function detailsFramework:BringNamedLocalFunctionToTop(codeBlock)
---@type string[]
local listOfFunctionsFound = {}
---@type number|nil
local startIndex = string.find(codeBlock, "local function")
while startIndex do
startIndex = parseCodeForNamedLocalFunctions(codeBlock, startIndex, listOfFunctionsFound)
if (not startIndex) then
break
end
startIndex = string.find(codeBlock, "local function", startIndex + 1)
end
for i = #listOfFunctionsFound, 1, -1 do
local thisMatch = listOfFunctionsFound[i]
local blockStartIndex = thisMatch[2]
local blockEndIndex = thisMatch[3]
codeBlock = codeBlock:sub(1, blockStartIndex - 1) .. codeBlock:sub(blockEndIndex + 1)
end
for i = #listOfFunctionsFound, 1, -1 do
codeBlock = listOfFunctionsFound[i][1] .. "\n\n" .. codeBlock
end
end
@@ -0,0 +1,233 @@
--note: this scroll bar is using legacy code and shouldn't be used on creating new stuff
local DF = _G["DetailsFramework"]
if (not DF or not DetailsFrameworkCanLoad) then
return
end
function DF:CreateScrollBar(master, scrollContainer, x, y)
return DF:NewScrollBar(master, scrollContainer, x, y)
end
function DF:NewScrollBar(parent, scrollContainer, x, y)
local newSlider = CreateFrame("Slider", nil, parent, "BackdropTemplate")
newSlider.scrollMax = 560
newSlider:SetPoint("TOPLEFT", parent, "TOPRIGHT", x, y)
newSlider.ativo = true
newSlider.bg = newSlider:CreateTexture(nil, "BACKGROUND")
newSlider.bg:SetAllPoints(true)
newSlider.bg:SetTexture(0, 0, 0, 0)
newSlider.thumb = newSlider:CreateTexture(nil, "OVERLAY")
newSlider.thumb:SetTexture("Interface\\Buttons\\UI-ScrollBar-Knob")
newSlider.thumb:SetSize(29, 30)
newSlider:SetThumbTexture(newSlider.thumb)
newSlider:SetOrientation("VERTICAL")
newSlider:SetSize(16, 100)
newSlider:SetMinMaxValues(0, newSlider.scrollMax)
newSlider:SetValue(0)
newSlider.ultimo = 0
local upButton = CreateFrame("Button", nil, parent,"BackdropTemplate")
upButton:SetPoint("BOTTOM", newSlider, "TOP", 0, -12)
upButton.x = 0
upButton.y = -12
upButton:SetWidth(29)
upButton:SetHeight(32)
upButton:SetNormalTexture("Interface\\BUTTONS\\UI-ScrollBar-ScrollUpButton-Up")
upButton:SetPushedTexture("Interface\\BUTTONS\\UI-ScrollBar-ScrollUpButton-Down")
upButton:SetDisabledTexture("Interface\\BUTTONS\\UI-ScrollBar-ScrollUpButton-Disabled")
upButton:Show()
upButton:Disable()
local downDutton = CreateFrame("Button", nil, parent,"BackdropTemplate")
downDutton:SetPoint("TOP", newSlider, "BOTTOM", 0, 12)
downDutton.x = 0
downDutton.y = 12
downDutton:SetWidth(29)
downDutton:SetHeight(32)
downDutton:SetNormalTexture("Interface\\BUTTONS\\UI-ScrollBar-ScrollDownButton-Up")
downDutton:SetPushedTexture("Interface\\BUTTONS\\UI-ScrollBar-ScrollDownButton-Down")
downDutton:SetDisabledTexture("Interface\\BUTTONS\\UI-ScrollBar-ScrollDownButton-Disabled")
downDutton:Show()
downDutton:Disable()
parent.baixo = downDutton
parent.cima = upButton
parent.slider = newSlider
downDutton:SetScript("OnMouseDown", function(self)
if (not newSlider:IsEnabled()) then
return
end
local current = newSlider:GetValue()
local minValue, maxValue = newSlider:GetMinMaxValues()
if (current + 5 < maxValue) then
newSlider:SetValue(current + 5)
else
newSlider:SetValue(maxValue)
end
self.precionado = true
self.last_up = -0.3
self:SetScript("OnUpdate", function(self, elapsed)
self.last_up = self.last_up + elapsed
if (self.last_up > 0.03) then
self.last_up = 0
local current = newSlider:GetValue()
local minValue, maxValue = newSlider:GetMinMaxValues()
if (current + 2 < maxValue) then
newSlider:SetValue(current + 2)
else
newSlider:SetValue(maxValue)
end
end
end)
end)
downDutton:SetScript("OnMouseUp", function(self)
self.precionado = false
self:SetScript("OnUpdate", nil)
end)
upButton:SetScript("OnMouseDown", function(self)
if (not newSlider:IsEnabled()) then
return
end
local current = newSlider:GetValue()
if (current - 5 > 0) then
newSlider:SetValue(current - 5)
else
newSlider:SetValue(0)
end
self.precionado = true
self.last_up = -0.3
self:SetScript("OnUpdate", function(self, elapsed)
self.last_up = self.last_up + elapsed
if (self.last_up > 0.03) then
self.last_up = 0
local current = newSlider:GetValue()
if (current - 2 > 0) then
newSlider:SetValue(current - 2)
else
newSlider:SetValue(0)
end
end
end)
end)
upButton:SetScript("OnMouseUp", function(self)
self.precionado = false
self:SetScript("OnUpdate", nil)
end)
upButton:SetScript("OnEnable", function(self)
local current = newSlider:GetValue()
if (current == 0) then
upButton:Disable()
end
end)
newSlider:SetScript("OnValueChanged", function(self)
local current = self:GetValue()
parent:SetVerticalScroll(current)
local minValue, maxValue = newSlider:GetMinMaxValues()
if (current == minValue) then
upButton:Disable()
elseif (not upButton:IsEnabled()) then
upButton:Enable()
end
if (current == maxValue) then
downDutton:Disable()
elseif (not downDutton:IsEnabled()) then
downDutton:Enable()
end
end)
newSlider:SetScript("OnShow", function(self)
upButton:Show()
downDutton:Show()
end)
newSlider:SetScript("OnDisable", function(self)
upButton:Disable()
downDutton:Disable()
end)
newSlider:SetScript("OnEnable", function(self)
upButton:Enable()
downDutton:Enable()
end)
parent:SetScript("OnMouseWheel", function(self, delta)
if (not newSlider:IsEnabled()) then
return
end
local current = newSlider:GetValue()
if (delta < 0) then
local minValue, maxValue = newSlider:GetMinMaxValues()
if (current + (parent.wheel_jump or 20) < maxValue) then
newSlider:SetValue(current + (parent.wheel_jump or 20))
else
newSlider:SetValue(maxValue)
end
elseif (delta > 0) then
if (current + (parent.wheel_jump or 20) > 0) then
newSlider:SetValue(current - (parent.wheel_jump or 20))
else
newSlider:SetValue(0)
end
end
end)
function newSlider:Altura(height)
self:SetHeight(height)
end
function newSlider:Update(desativar)
if (desativar) then
newSlider:Disable()
newSlider:SetValue(0)
newSlider.ativo = false
parent:EnableMouseWheel(false)
return
end
self.scrollMax = scrollContainer:GetHeight() - parent:GetHeight()
if (self.scrollMax > 0) then
newSlider:SetMinMaxValues(0, self.scrollMax)
if (not newSlider.ativo) then
newSlider:Enable()
newSlider.ativo = true
parent:EnableMouseWheel(true)
end
else
newSlider:Disable()
newSlider:SetValue(0)
newSlider.ativo = false
parent:EnableMouseWheel(false)
end
end
function newSlider:cimaPoint(x, y)
upButton:SetPoint("BOTTOM", newSlider, "TOP", x, y - 12)
end
function newSlider:baixoPoint(x, y)
downDutton:SetPoint("TOP", newSlider, "BOTTOM", x, y + 12)
end
return newSlider
end
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,810 @@
local DF = _G ["DetailsFramework"]
if (not DF or not DetailsFrameworkCanLoad) then
return
end
local _
local rawset = rawset --lua local
local rawget = rawget --lua local
local setmetatable = setmetatable --lua local
local _unpack = unpack --lua local
local type = type --lua local
local _math_floor = math.floor --lua local
local maxStatusBarValue = 100000000
local cleanfunction = function() end
local APISplitBarFunctions
do
local metaPrototype = {
WidgetType = "split_bar",
dversion = DF.dversion,
}
--check if there's a metaPrototype already existing
if (_G[DF.GlobalWidgetControlNames["split_bar"]]) then
--get the already existing metaPrototype
local oldMetaPrototype = _G[DF.GlobalWidgetControlNames["split_bar"]]
--check if is older
if ( (not oldMetaPrototype.dversion) or (oldMetaPrototype.dversion < DF.dversion) ) then
--the version is older them the currently loading one
--copy the new values into the old metatable
for funcName, _ in pairs(metaPrototype) do
oldMetaPrototype[funcName] = metaPrototype[funcName]
end
end
else
--first time loading the framework
_G[DF.GlobalWidgetControlNames["split_bar"]] = metaPrototype
end
end
local SplitBarMetaFunctions = _G[DF.GlobalWidgetControlNames["split_bar"]]
DF:Mixin(SplitBarMetaFunctions, DF.ScriptHookMixin)
------------------------------------------------------------------------------------------------------------
--metatables
SplitBarMetaFunctions.__call = function(_table, value)
if (not value) then
return _table.statusbar:GetValue()
else
_table.spark:SetPoint("left", _table.statusbar, "left", value * (_table.statusbar:GetWidth()/100) - 18, 0)
return _table.statusbar:SetValue(value)
end
end
SplitBarMetaFunctions.__add = function(v1, v2)
if (type(v1) == "table") then
local v = v1.statusbar:GetValue()
v = v + v2
v1.spark:SetPoint("left", v1.statusbar, "left", value * (v1.statusbar:GetWidth()/100) - 18, 0)
v1.statusbar:SetValue(v)
else
local v = v2.statusbar:GetValue()
v = v + v1
v2.spark:SetPoint("left", v2.statusbar, "left", value * (v2.statusbar:GetWidth()/100) - 18, 0)
v2.statusbar:SetValue(v)
end
end
SplitBarMetaFunctions.__sub = function(v1, v2)
if (type(v1) == "table") then
local v = v1.statusbar:GetValue()
v = v - v2
v1.spark:SetPoint("left", v1.statusbar, "left", value * (v1.statusbar:GetWidth()/100) - 18, 0)
v1.statusbar:SetValue(v)
else
local v = v2.statusbar:GetValue()
v = v - v1
v2.spark:SetPoint("left", v2.statusbar, "left", value * (v2.statusbar:GetWidth()/100) - 18, 0)
v2.statusbar:SetValue(v)
end
end
------------------------------------------------------------------------------------------------------------
--members
--tooltip
local function gmember_tooltip (_object)
return _object:GetTooltip()
end
--shown
local gmember_shown = function(_object)
return _object.statusbar:IsShown()
end
--frame width
local gmember_width = function(_object)
return _object.statusbar:GetWidth()
end
--frame height
local gmember_height = function(_object)
return _object.statusbar:GetHeight()
end
--value
local gmember_value = function(_object)
return _object.statusbar:GetValue()
end
--right text
local gmember_rtext = function(_object)
return _object.textright:GetText()
end
--left text
local gmember_ltext = function(_object)
return _object.textleft:GetText()
end
--right color
local gmember_rcolor = function(_object)
return _object.rightTexture.original_colors
end
--left color
local gmember_lcolor = function(_object)
return _object.texture.original_colors
end
--right icon
local gmember_ricon = function(_object)
return _object.iconright:GetTexture()
end
--left icon
local gmember_licon = function(_object)
return _object.iconleft:GetTexture()
end
--texture
local gmember_texture = function(_object)
return _object.texture:GetTexture()
end
--font size
local gmember_textsize = function(_object)
local _, fontsize = _object.textleft:GetFont()
return fontsize
end
--font face
local gmember_textfont = function(_object)
local fontface = _object.textleft:GetFont()
return fontface
end
--font color
local gmember_textcolor = function(_object)
return _object.textleft:GetTextColor()
end
SplitBarMetaFunctions.GetMembers = SplitBarMetaFunctions.GetMembers or {}
SplitBarMetaFunctions.GetMembers ["tooltip"] = gmember_tooltip
SplitBarMetaFunctions.GetMembers ["shown"] = gmember_shown
SplitBarMetaFunctions.GetMembers ["width"] = gmember_width
SplitBarMetaFunctions.GetMembers ["height"] = gmember_height
SplitBarMetaFunctions.GetMembers ["value"] = gmember_value
SplitBarMetaFunctions.GetMembers ["righttext"] = gmember_rtext
SplitBarMetaFunctions.GetMembers ["lefttext"] = gmember_ltext
SplitBarMetaFunctions.GetMembers ["rightcolor"] = gmember_rcolor
SplitBarMetaFunctions.GetMembers ["leftcolor"] = gmember_lcolor
SplitBarMetaFunctions.GetMembers ["righticon"] = gmember_ricon
SplitBarMetaFunctions.GetMembers ["lefticon"] = gmember_licon
SplitBarMetaFunctions.GetMembers ["texture"] = gmember_texture
SplitBarMetaFunctions.GetMembers ["fontsize"] = gmember_textsize
SplitBarMetaFunctions.GetMembers ["fontface"] = gmember_textfont
SplitBarMetaFunctions.GetMembers ["fontcolor"] = gmember_textcolor
SplitBarMetaFunctions.GetMembers ["textsize"] = gmember_textsize --alias
SplitBarMetaFunctions.GetMembers ["textfont"] = gmember_textfont --alias
SplitBarMetaFunctions.GetMembers ["textcolor"] = gmember_textcolor --alias
SplitBarMetaFunctions.__index = function(_table, _member_requested)
local func = SplitBarMetaFunctions.GetMembers [_member_requested]
if (func) then
return func (_table, _member_requested)
end
local fromMe = rawget (_table, _member_requested)
if (fromMe) then
return fromMe
end
return SplitBarMetaFunctions [_member_requested]
end
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--tooltip
local smember_tooltip = function(_object, _value)
return _object:SetTooltip (_value)
end
--show
local smember_shown = function(_object, _value)
if (_value) then
return _object:Show()
else
return _object:Hide()
end
end
--hide
local smember_hide = function(_object, _value)
if (_value) then
return _object:Hide()
else
return _object:Show()
end
end
--width
local smember_width = function(_object, _value)
return _object.statusbar:SetWidth(_value)
end
--height
local smember_height = function(_object, _value)
return _object.statusbar:SetHeight(_value)
end
--statusbar value
local smember_value = function(_object, _value)
_object.statusbar:SetValue(_value)
return _object.spark:SetPoint("left", _object.statusbar, "left", _value * (_object.statusbar:GetWidth()/100) - 18, 0)
end
--right text
local smember_rtext = function(_object, _value)
return _object.textright:SetText(_value)
end
--left text
local smember_ltext = function(_object, _value)
return _object.textleft:SetText(_value)
end
--right color
local smember_rcolor = function(_object, _value)
local _value1, _value2, _value3, _value4 = DF:ParseColors(_value)
_object.rightTexture.original_colors = {_value1, _value2, _value3, _value4}
return _object.rightTexture:SetVertexColor(_value1, _value2, _value3, _value4)
end
--left color
local smember_lcolor = function(_object, _value)
local _value1, _value2, _value3, _value4 = DF:ParseColors(_value)
_object.statusbar:SetStatusBarColor(_value1, _value2, _value3, _value4)
_object.texture.original_colors = {_value1, _value2, _value3, _value4}
return _object.texture:SetVertexColor(_value1, _value2, _value3, _value4)
end
--right icon
local smember_ricon = function(_object, _value)
if (type(_value) == "table") then
local _value1, _value2 = _unpack(_value)
_object.iconright:SetTexture(_value1)
if (_value2) then
_object.iconright:SetTexCoord(_unpack(_value2))
end
else
_object.iconright:SetTexture(_value)
end
return
end
--left icon
local smember_licon = function(_object, _value)
if (type(_value) == "table") then
local _value1, _value2 = _unpack(_value)
_object.iconleft:SetTexture(_value1)
if (_value2) then
_object.iconleft:SetTexCoord(_unpack(_value2))
end
else
_object.iconleft:SetTexture(_value)
end
return
end
--texture
local smember_texture = function(_object, _value)
if (type(_value) == "table") then
local _value1, _value2 = _unpack(_value)
_object.texture:SetTexture(_value1)
_object.rightTexture:SetTexture(_value1)
if (_value2) then
_object.texture:SetTexCoord(_unpack(_value2))
_object.rightTexture:SetTexCoord(_unpack(_value2))
end
else
_object.texture:SetTexture(_value)
_object.rightTexture:SetTexture(_value)
end
return
end
--font face
local smember_textfont = function(_object, _value)
DF:SetFontFace (_object.textleft, _value)
return DF:SetFontFace (_object.textright, _value)
end
--font size
local smember_textsize = function(_object, _value)
DF:SetFontSize(_object.textleft, _value)
return DF:SetFontSize(_object.textright, _value)
end
--font color
local smember_textcolor = function(_object, _value)
local _value1, _value2, _value3, _value4 = DF:ParseColors(_value)
_object.textleft:SetTextColor(_value1, _value2, _value3, _value4)
return _object.textright:SetTextColor(_value1, _value2, _value3, _value4)
end
SplitBarMetaFunctions.SetMembers = SplitBarMetaFunctions.SetMembers or {}
SplitBarMetaFunctions.SetMembers ["tooltip"] = smember_tooltip
SplitBarMetaFunctions.SetMembers ["shown"] = smember_shown
SplitBarMetaFunctions.SetMembers ["width"] = smember_width
SplitBarMetaFunctions.SetMembers ["height"] = smember_height
SplitBarMetaFunctions.SetMembers ["value"] = smember_value
SplitBarMetaFunctions.SetMembers ["righttext"] = smember_rtext
SplitBarMetaFunctions.SetMembers ["lefttext"] = smember_ltext
SplitBarMetaFunctions.SetMembers ["rightcolor"] = smember_rcolor
SplitBarMetaFunctions.SetMembers ["leftcolor"] = smember_lcolor
SplitBarMetaFunctions.SetMembers ["righticon"] = smember_ricon
SplitBarMetaFunctions.SetMembers ["lefticon"] = smember_licon
SplitBarMetaFunctions.SetMembers ["texture"] = smember_texture
SplitBarMetaFunctions.SetMembers ["fontsize"] = smember_textsize
SplitBarMetaFunctions.SetMembers ["fontface"] = smember_textfont
SplitBarMetaFunctions.SetMembers ["fontcolor"] = smember_textcolor
SplitBarMetaFunctions.SetMembers ["textsize"] = smember_textsize --alias
SplitBarMetaFunctions.SetMembers ["textfont"] = smember_textfont --alias
SplitBarMetaFunctions.SetMembers ["textcolor"] = smember_textcolor --alias
SplitBarMetaFunctions.__newindex = function(_table, _key, _value)
local func = SplitBarMetaFunctions.SetMembers [_key]
if (func) then
return func (_table, _value)
else
return rawset (_table, _key, _value)
end
end
------------------------------------------------------------------------------------------------------------
--methods
--show & hide
function SplitBarMetaFunctions:Show()
return self.statusbar:Show()
end
function SplitBarMetaFunctions:Hide()
return self.statusbar:Hide()
end
-- set split
function SplitBarMetaFunctions:SetSplit (value)
if (not value) then
value = self.statusbar:GetValue()
elseif (value < 0 or value > 100) then
return
end
self.statusbar:SetValue(value)
self.spark:SetPoint("left", self.statusbar, "left", value * (self.statusbar:GetWidth()/100) - 18, 0)
end
-- setpoint
function SplitBarMetaFunctions:SetPoint(v1, v2, v3, v4, v5)
v1, v2, v3, v4, v5 = DF:CheckPoints (v1, v2, v3, v4, v5, self)
if (not v1) then
print("Invalid parameter for SetPoint")
return
end
return self.widget:SetPoint(v1, v2, v3, v4, v5)
end
-- sizes
function SplitBarMetaFunctions:SetSize(w, h)
if (w) then
self.statusbar:SetWidth(w)
end
if (h) then
self.statusbar:SetHeight(h)
end
end
-- texture
function SplitBarMetaFunctions:SetTexture(texture)
self.rightTexture:SetTexture(texture)
self.texture:SetTexture(texture)
end
function SplitBarMetaFunctions:SetBackgroundTexture(texture)
self.background:SetTexture(texture)
end
-- texts
function SplitBarMetaFunctions:SetLeftText (text)
self.textleft:SetText(text)
end
function SplitBarMetaFunctions:SetRightText (text)
self.textright:SetText(text)
end
-- colors
function SplitBarMetaFunctions:SetLeftColor (r, g, b, a)
r, g, b, a = DF:ParseColors(r, g, b, a)
self.texture:SetVertexColor(r, g, b, a)
self.texture.original_colors = {r, g, b, a}
end
function SplitBarMetaFunctions:SetRightColor (r, g, b, a)
r, g, b, a = DF:ParseColors(r, g, b, a)
self.rightTexture:SetVertexColor(r, g, b, a)
self.rightTexture.original_colors = {r, g, b, a}
end
function SplitBarMetaFunctions:SetBackgroundColor (r, g, b, a)
r, g, b, a = DF:ParseColors(r, g, b, a)
self.background:SetVertexColor(r, g, b, a)
self.background.original_colors = {r, g, b, a}
end
function SplitBarMetaFunctions:GetLeftColor()
return self.texture:GetVertexColor()
end
function SplitBarMetaFunctions:GetRightColor()
return self.rightTexture:GetVertexColor()
end
-- icons
function SplitBarMetaFunctions:SetLeftIcon (texture, ...)
self.iconleft:SetTexture(texture)
if (...) then
local L, R, U, D = unpack(...)
self.iconleft:SetTexCoord(L, R, U, D)
end
end
function SplitBarMetaFunctions:SetRightIcon (texture, ...)
self.iconright:SetTexture(texture)
if (...) then
local L, R, U, D = unpack(...)
self.iconright:SetTexCoord(L, R, U, D)
end
end
-- tooltip
function SplitBarMetaFunctions:SetTooltip (tooltip)
if (tooltip) then
return rawset (self, "have_tooltip", tooltip)
else
return rawset (self, "have_tooltip", nil)
end
end
function SplitBarMetaFunctions:GetTooltip()
return rawget (self, "have_tooltip")
end
-- frame levels
function SplitBarMetaFunctions:GetFrameLevel()
return self.statusbar:GetFrameLevel()
end
function SplitBarMetaFunctions:SetFrameLevel(level, frame)
if (not frame) then
return self.statusbar:SetFrameLevel(level)
else
local framelevel = frame:GetFrameLevel (frame) + level
return self.statusbar:SetFrameLevel(framelevel)
end
end
-- frame stratas
function SplitBarMetaFunctions:SetFrameStrata(strata)
if (type(strata) == "table") then
self.statusbar:SetFrameStrata(strata:GetFrameStrata())
else
self.statusbar:SetFrameStrata(strata)
end
end
-- animation
--animation with acceleration ~animation ~healthbaranimation
local animateLeftWithAccel = function(self, deltaTime)
local currentPercent = DetailsFramework:GetRangePercent(self.targetValue, self.startValue, self.currentValue)
currentPercent = abs(currentPercent - 1)
currentPercent = min(0.9, currentPercent)
currentPercent = max(0.5, currentPercent)
local animationMultiplier = math.sin(currentPercent * math.pi)
local valueChange = self.step * (deltaTime * animationMultiplier)
self.currentValue = self.currentValue - valueChange
local barWidth = self:GetWidth()
self.currentValue = Clamp(self.currentValue, 0, maxStatusBarValue)
self.statusbar:SetValue(self.currentValue)
self.rightTexture:SetWidth(barWidth - barWidth*self.currentValue)
if (self.currentValue - 0.001 <= self.targetValue) then
self.targetValue = Clamp(self.targetValue, 0, maxStatusBarValue)
self:SetValue(self.targetValue)
self.currentValue = self.targetValue
if (not self.SparkAlwaysShow) then
self.spark:Hide()
end
self.widget:SetScript("OnUpdate", nil)
return
end
self.spark:SetPoint("center", self.widget, "left", self.currentValue * barWidth, 0)
self.spark:Show()
end
local animateRightWithAccel = function(self, deltaTime)
--get the animation elapsed percent
local currentPercent = DetailsFramework:GetRangePercent(self.startValue, self.targetValue, self.currentValue)
currentPercent = min(0.9, currentPercent) --slow down the animation but avoid very slow
currentPercent = max(0.5, currentPercent) --default: 0.1, using 0.5 makes the animation start fast and go slow
--get the sine value and scale time with it
local animationMultiplier = math.sin(currentPercent * math.pi)
local valueChange = self.step * (deltaTime * animationMultiplier)
self.currentValue = self.currentValue + valueChange
local barWidth = self:GetWidth()
self.currentValue = Clamp(self.currentValue, 0, maxStatusBarValue)
self.statusbar:SetValue(self.currentValue)
local rightTextureSize = barWidth - barWidth*self.currentValue
self.rightTexture:SetWidth(rightTextureSize)
if (self.currentValue + 0.001 >= self.targetValue) then
self.targetValue = Clamp(self.targetValue, 0, maxStatusBarValue)
self:SetValue(self.targetValue)
self.currentValue = self.targetValue
if (not self.SparkAlwaysShow) then
self.spark:Hide()
end
self.widget:SetScript("OnUpdate", nil)
return
end
self.spark:SetPoint("center", self.widget, "left", self.currentValue * barWidth, 0)
self.spark:Show()
end
local onUpdate = function(self, deltaTime)
self = self.MyObject
--select the animation function
--target is always equal to current
if (self.targetValue > self.currentValue) then
animateRightWithAccel(self, deltaTime)
else
animateLeftWithAccel(self, deltaTime)
end
end
function SplitBarMetaFunctions:EnableAnimations()
return
end
function SplitBarMetaFunctions:DisableAnimations()
self.widget:SetScript("OnUpdate", nil)
end
function SplitBarMetaFunctions:SetValueWithAnimation(value)
if (self.widget:GetScript("OnUpdate") == nil) then
self.widget:SetScript("OnUpdate", onUpdate)
self.widget:SetMinMaxValues(0, 1)
self.spark:ClearAllPoints()
self.spark:SetHeight(self:GetHeight() * 2.6)
self.spark:SetAlpha(0.4)
end
self.startValue = self.currentValue
self.step = abs(value - self.currentValue)
self.targetValue = value
self.rightTexture:Show()
end
------------------------------------------------------------------------------------------------------------
--scripts
local OnEnter = function(frame)
local capsule = frame.MyObject
local kill = capsule:RunHooksForWidget("OnEnter", frame, capsule)
if (kill) then
return
end
if (frame.MyObject.have_tooltip) then
GameCooltip2:Reset()
GameCooltip2:AddLine(frame.MyObject.have_tooltip)
GameCooltip2:ShowCooltip(frame, "tooltip")
end
end
local OnLeave = function(frame)
local capsule = frame.MyObject
local kill = capsule:RunHooksForWidget("OnLeave", frame, capsule)
if (kill) then
return
end
if (frame.MyObject.have_tooltip) then
DF.popup:ShowMe(false)
end
end
local OnHide = function(frame)
local capsule = frame.MyObject
local kill = capsule:RunHooksForWidget("OnHide", frame, capsule)
if (kill) then
return
end
end
local OnShow = function(frame)
local capsule = frame.MyObject
local kill = capsule:RunHooksForWidget("OnShow", frame, capsule)
if (kill) then
return
end
end
local OnMouseDown = function(frame, button)
local capsule = frame.MyObject
local kill = capsule:RunHooksForWidget("OnMouseDown", frame, button, capsule)
if (kill) then
return
end
if (not frame.MyObject.container.isLocked and frame.MyObject.container:IsMovable()) then
if (not frame.isLocked and frame:IsMovable()) then
frame.MyObject.container.isMoving = true
frame.MyObject.container:StartMoving()
end
end
end
local OnMouseUp = function(frame, button)
local capsule = frame.MyObject
local kill = capsule:RunHooksForWidget("OnMouseUp", frame, button, capsule)
if (kill) then
return
end
if (frame.MyObject.container.isMoving) then
frame.MyObject.container:StopMovingOrSizing()
frame.MyObject.container.isMoving = false
end
end
local OnSizeChanged = function(statusbar)
statusbar.MyObject.spark:SetPoint("left", statusbar, "left", statusbar:GetValue() * (statusbar:GetWidth()/100) - 18, 0)
statusbar.MyObject.rightTexture:SetWidth(statusbar:GetWidth() - statusbar.MyObject.texture:GetWidth())
end
------------------------------------------------------------------------------------------------------------
--object constructor
function DetailsFrameworkSplitlBar_OnCreate (self)
self.texture.original_colors = {1, 1, 1, 1}
self.rightTexture.original_colors = {.5, .5, .5, 1}
self.spark:SetPoint("left", self, "left", self:GetValue() * (self:GetWidth()/100) - 18, 0)
return true
end
function DF:CreateSplitBar(parent, width, height, member, name)
return DF:NewSplitBar(parent, nil, name, member, width, height)
end
local build_statusbar = function(self)
self:SetSize(300, 14)
self.background = self:CreateTexture("$parent_StatusBarBackground", "BACKGROUND")
self.background:SetPoint("topright", self, "topright")
self.background:SetPoint("bottomright", self, "bottomright")
self.background:SetPoint("topleft", self, "topleft")
self.background:SetPoint("bottomleft", self, "bottomleft")
self.background:SetTexture([[Interface\PaperDollInfoFrame\UI-Character-Skills-Bar]])
self.background:SetVertexColor(.3, .3, .3, 1)
--this is the left texture and it grows to the right, it is embed within the bar by SetStatusBarTexture
self.texture = self:CreateTexture("$parent_StatusBarTexture", "ARTWORK", nil, 1)
self.texture:Hide()
self.texture:SetSize(300, 14)
self.texture:SetTexture([[Interface\PaperDollInfoFrame\UI-Character-Skills-Bar]])
--this is the right texture and its size is the bar:GetWidth() - self.texture:GetWidth()
self.rightTexture = self:CreateTexture("$parent_StatusBarTextureRight", "ARTWORK", nil, 2)
self.rightTexture:Hide()
self.rightTexture:SetSize(300, 14)
self.rightTexture:SetTexture([[Interface\PaperDollInfoFrame\UI-Character-Skills-Bar]])
self.rightTexture:SetPoint("topright", self, "topright")
self.rightTexture:SetPoint("bottomright", self, "bottomright")
self.rightTexture:SetVertexColor(1, 0, 0)
self.lefticon = self:CreateTexture("$parent_IconLeft", "OVERLAY")
self.lefticon:SetSize(14, 14)
self.lefticon:SetPoint("LEFT", self, "LEFT")
self.righticon = self:CreateTexture("$parent_IconRight", "OVERLAY")
self.righticon:SetSize(14, 14)
self.righticon:SetPoint("RIGHT", self, "RIGHT")
self.spark = self:CreateTexture("$parent_Spark", "OVERLAY")
self.spark:SetTexture([[Interface\CastingBar\UI-CastingBar-Spark]])
self.spark:SetBlendMode("ADD")
self.spark:SetSize(32, 32)
self.spark:SetPoint("LEFT", self, "RIGHT", -17, -1)
self.lefttext = self:CreateFontString("$parent_TextLeft", "OVERLAY", "GameFontHighlight")
DF:SetFontSize(self.lefttext, 10)
self.lefttext:SetJustifyH("left")
self.lefttext:SetPoint("LEFT", self.lefticon, "RIGHT", 3, 0)
self.righttext = self:CreateFontString("$parent_TextRight", "OVERLAY", "GameFontHighlight")
DF:SetFontSize(self.righttext, 10)
self.righttext:SetJustifyH("right")
self.righttext:SetPoint("RIGHT", self.righticon, "LEFT", -3, 0)
self:SetStatusBarTexture(self.texture)
self:SetMinMaxValues(1, 100)
self:SetValue(50)
DetailsFrameworkSplitlBar_OnCreate (self)
end
function DF:NewSplitBar (parent, container, name, member, w, h)
if (not name) then
name = "DetailsFrameworkSplitbar" .. DF.SplitBarCounter
DF.SplitBarCounter = DF.SplitBarCounter + 1
end
if (not parent) then
return error("Details! FrameWork: parent not found.", 2)
end
if (not container) then
container = parent
end
if (name:find("$parent")) then
local parentName = DF.GetParentName(parent)
name = name:gsub("$parent", parentName)
end
local SplitBarObject = {type = "barsplit", dframework = true}
if (member) then
parent [member] = SplitBarObject
end
if (parent.dframework) then
parent = parent.widget
end
if (container.dframework) then
container = container.widget
end
--default members:
--misc
SplitBarObject.locked = false
SplitBarObject.container = container
SplitBarObject.currentValue = 0.5
--create widgets
SplitBarObject.statusbar = CreateFrame("statusbar", name, parent, "BackdropTemplate")
build_statusbar (SplitBarObject.statusbar)
SplitBarObject.spark = SplitBarObject.statusbar.spark
SplitBarObject.widget = SplitBarObject.statusbar
if (not APISplitBarFunctions) then
APISplitBarFunctions = true
local idx = getmetatable(SplitBarObject.statusbar).__index
for funcName, funcAddress in pairs(idx) do
if (not SplitBarMetaFunctions [funcName]) then
SplitBarMetaFunctions [funcName] = function(object, ...)
local x = loadstring ( "return _G['"..object.statusbar:GetName().."']:"..funcName.."(...)")
return x (...)
end
end
end
end
SplitBarObject.statusbar:SetHeight(h or 200)
SplitBarObject.statusbar:SetWidth(w or 14)
SplitBarObject.statusbar:SetValue(0.5)
SplitBarObject.statusbar.MyObject = SplitBarObject
SplitBarObject.textleft = _G [name .. "_TextLeft"]
SplitBarObject.textright = _G [name .. "_TextRight"]
SplitBarObject.iconleft = _G [name .. "_IconLeft"]
SplitBarObject.iconright = _G [name .. "_IconRight"]
SplitBarObject.background = _G [name .. "_StatusBarBackground"]
SplitBarObject.texture = _G [name .. "_StatusBarTexture"]
SplitBarObject.rightTexture = _G [name .. "_StatusBarTextureRight"]
--hooks
SplitBarObject.HookList = {
OnEnter = {},
OnLeave = {},
OnHide = {},
OnShow = {},
OnMouseDown = {},
OnMouseUp = {},
OnSizeChanged = {},
}
SplitBarObject.statusbar:SetScript("OnEnter", OnEnter)
SplitBarObject.statusbar:SetScript("OnLeave", OnLeave)
SplitBarObject.statusbar:SetScript("OnHide", OnHide)
SplitBarObject.statusbar:SetScript("OnShow", OnShow)
SplitBarObject.statusbar:SetScript("OnMouseDown", OnMouseDown)
SplitBarObject.statusbar:SetScript("OnMouseUp", OnMouseUp)
SplitBarObject.statusbar:SetScript("OnSizeChanged", OnSizeChanged)
setmetatable(SplitBarObject, SplitBarMetaFunctions)
return SplitBarObject
end
@@ -0,0 +1,3 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ .. \FrameXML\UI.xsd">
<Script file="split_bar.lua"/>
</Ui>
@@ -0,0 +1,422 @@
local detailsFramework = DetailsFramework
if (not detailsFramework or not DetailsFrameworkCanLoad) then
return
end
local unpack = unpack
local CreateFrame = CreateFrame
local PixelUtil = PixelUtil
---@class df_tabinfotable : {name: string, text: string}
---@class df_tabcontainer : frame
---@field AllFrames df_tabcontainerframe[]
---@field AllButtons df_tabcontainerbutton[]
---@field AllFramesByName table<string, df_tabcontainerframe>
---@field AllButtonsByName table<string, df_tabcontainerbutton>
---@field hookList table
---@field CurrentIndex number
---@field IsContainer boolean
---@field ButtonSelectedBorderColor table
---@field ButtonNotSelectedBorderColor table
---@field CanCloseWithRightClick boolean
---@field SetIndex fun(self: df_tabcontainer, index: number)
---@field SelectTabByIndex fun(self: df_tabcontainer, menuIndex: number)
---@field SelectTabByName fun(self: df_tabcontainer, name: string)
---@field CreateUnderlineGlow fun(button: button)
---@field OnShow fun(self: df_tabcontainer)
---@field GetTabFrameByName fun(self: df_tabcontainer, name: string): df_tabcontainerframe
---@field GetTabFrameByIndex fun(self: df_tabcontainer, index: number): df_tabcontainerframe
---@field GetTabButtonByName fun(self: df_tabcontainer, name: string): df_tabcontainerbutton
---@field GetTabButtonByIndex fun(self: df_tabcontainer, index: number): df_tabcontainerbutton
---@class df_tabcontainerframe : frame
---@field bIsFrontPage boolean
---@field titleText fontstring
---@field tabIndex number
---@field OnMouseDown fun(self: df_tabcontainerframe, button: string)
---@field OnMouseUp fun(self: df_tabcontainerframe, button: string)
---@field RefreshOptions fun(self: df_tabcontainerframe)|nil
---@class df_tabcontainerbutton : button
---@field selectedUnderlineGlow texture
---@field textsize number
---@field mainFrame df_tabcontainer
---@field leftSelectionIndicator texture
--create a template for the tab buttons
local tabTemplate = detailsFramework.table.copy({}, detailsFramework:GetTemplate("button", "OPTIONS_BUTTON_TEMPLATE"))
tabTemplate.backdropbordercolor = nil
detailsFramework.TabContainerMixin = {
---@param self df_tabcontainer
---@param tabIndex number
---@return df_tabcontainerframe
GetTabFrameByIndex = function(self, tabIndex)
return self.AllFrames[tabIndex]
end,
---@param self df_tabcontainer
---@param name string
---@return df_tabcontainerframe
GetTabFrameByName = function(self, name)
return self.AllFramesByName[name]
end,
---@param self df_tabcontainer
---@param tabIndex number
---@return df_tabcontainerbutton
GetTabButtonByIndex = function(self, tabIndex)
return self.AllButtons[tabIndex]
end,
---@param self df_tabcontainer
---@param name string
---@return df_tabcontainerbutton
GetTabButtonByName = function(self, name)
return self.AllButtonsByName[name]
end,
---@param self df_tabcontainer
---@param backdropTable backdrop|nil
---@param backdropColorTable table|string|nil
---@param backdropBorderColorTable table|string|nil
SetTabFramesBackdrop = function(self, backdropTable, backdropColorTable, backdropBorderColorTable)
for tabIndex, tabFrame in ipairs(self.AllFrames) do
if (backdropTable) then
tabFrame:SetBackdrop(backdropTable)
end
if (backdropColorTable) then
local r, g, b, a = detailsFramework:ParseColors(backdropColorTable)
tabFrame:SetBackdropColor(r, g, b, a)
end
if (backdropBorderColorTable) then
local r, g, b, a = detailsFramework:ParseColors(backdropColorTable)
tabFrame:SetBackdropBorderColor(r, g, b, a)
end
end
end,
---create a underglow texture for the selected tab, this texture is a small yellow bright gradient below the button
---@param self df_tabcontainerbutton
CreateUnderlineGlow = function(self)
local selectedGlow = self:CreateTexture(nil, "background", nil, -4)
selectedGlow:SetPoint("topleft", self["widget"], "bottomleft", -7, 0)
selectedGlow:SetPoint("topright", self["widget"], "bottomright", 7, 0)
selectedGlow:SetTexture([[Interface\BUTTONS\UI-Panel-Button-Glow]])
selectedGlow:SetTexCoord(0, 95/128, 30/64, 38/64)
selectedGlow:SetBlendMode("ADD")
selectedGlow:SetHeight(8)
selectedGlow:SetAlpha(.75)
selectedGlow:Hide()
self.selectedUnderlineGlow = selectedGlow
end,
---@param tabContainer df_tabcontainer
---@param menuIndex number
SelectTabByIndex = function(tabContainer, menuIndex)
---@type df_tabcontainerbutton
local tabButton = tabContainer.AllButtons[menuIndex]
---@type df_tabcontainerframe
local tabFrame = tabContainer.AllFrames[menuIndex]
--hide all tab frame and hide the selection glow from tab buttons
for i = 1, #tabContainer.AllFrames do
---@type df_tabcontainerframe
local thisTabFrame = tabContainer.AllFrames[i]
thisTabFrame:Hide()
---@type df_tabcontainerbutton
local thisTabButton = tabContainer.AllButtons[i]
if (tabContainer.ButtonNotSelectedBorderColor) then
thisTabButton:SetBackdropBorderColor(unpack(tabContainer.ButtonNotSelectedBorderColor))
end
if (thisTabButton.selectedUnderlineGlow) then
thisTabButton.selectedUnderlineGlow:Hide()
end
end
tabFrame:Show()
if (tabFrame.RefreshOptions) then
tabFrame:RefreshOptions()
end
if (tabContainer.ButtonSelectedBorderColor) then
tabButton:SetBackdropBorderColor(unpack(tabContainer.ButtonSelectedBorderColor))
end
if (tabButton.selectedUnderlineGlow) then
tabButton.selectedUnderlineGlow:Show()
end
tabContainer.CurrentIndex = menuIndex
if (tabContainer.hookList.OnSelectIndex) then
detailsFramework:QuickDispatch(tabContainer.hookList.OnSelectIndex, tabContainer, tabButton)
end
end,
---@param tabContainer df_tabcontainer
---@param name string
SelectTabByName = function(tabContainer, name)
---@type df_tabcontainerframe
local tabFrame = tabContainer.AllFramesByName[name]
if (tabFrame) then
local tabIndex = tabFrame.tabIndex
tabContainer:SelectTabByIndex(tabIndex)
else
error("df_tabcontainer:SelectTabByName(name): param #2 'name' not found within 'tabContainer.AllFramesByName'.")
end
end,
---@param self df_tabcontainer
---@param index number
SetIndex = function(self, index)
self.CurrentIndex = index
end,
---@param self df_tabcontainer
OnShow = function(self)
local index = self.CurrentIndex
self:SelectTabByIndex(index)
end
}
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
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
end
end
end,
---@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
end
end,
}
---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
---then the function iterate amongst the tabList and create a frame and a button for each entry using the value of the 'text' key as the text for the button and 'name' for the name of the frame
---when the user click on a button, the tabContainer hide all frames and show the frame which was created together with that button
---@param parent frame the parent frame
---@param title string a string to use as the title of the tab container, the title is always shown
---@param frameName string the frame name to pass into the CreateFrame function
---@param tabList df_tabinfotable[] the list of tabs to create, each entry has a 'name' and 'text' keys
---@param optionsTable {button_border_color: table, button_selected_border_color: table, right_click_y: number, hide_click_label: boolean, close_text_alpha: number, rightbutton_always_close: boolean, right_click_interact: boolean, y_offset: number, button_width: number, button_height: number, button_x: number, button_y: number, button_text_size: number, container_width_offset: number}|nil
---@param hookList table<string, function>|nil
---@param languageInfo any
---@return df_tabcontainer
function detailsFramework:CreateTabContainer(parent, title, frameName, tabList, optionsTable, hookList, languageInfo)
optionsTable = optionsTable or {}
local parentFrameWidth = parent:GetWidth()
local yOffset = optionsTable.y_offset or 0
local buttonWidth = optionsTable.button_width or 160
local buttonHeight = optionsTable.button_height or 20
local buttonAnchorX = optionsTable.button_x or 230
local buttonAnchorY = optionsTable.button_y or 0
local buttonTextSize = optionsTable.button_text_size or 10
local containerWidthOffset = optionsTable.container_width_offset or 0
--create the base frame
---@type df_tabcontainer
local tabContainer = CreateFrame("frame", frameName, parent["widget"] or parent, "BackdropTemplate")
tabContainer.hookList = hookList or {}
detailsFramework:Mixin(tabContainer, detailsFramework.TabContainerMixin)
--create the fontstring which show the title
---@type fontstring
local mainTitle = detailsFramework:CreateLabel(tabContainer, title, 24, "white")
mainTitle:SetPoint("topleft", tabContainer, "topleft", 10, -30 + yOffset)
tabContainer.AllFrames = {}
tabContainer.AllButtons = {}
tabContainer.AllFramesByName = {}
tabContainer.AllButtonsByName = {}
tabContainer.CurrentIndex = 1
tabContainer.IsContainer = true
tabContainer.ButtonSelectedBorderColor = optionsTable.button_selected_border_color or {1, 1, 0, 1}
tabContainer.ButtonNotSelectedBorderColor = optionsTable.button_border_color or {0, 0, 0, 0}
if (optionsTable.right_click_interact ~= nil) then
tabContainer.CanCloseWithRightClick = optionsTable.right_click_interact
else
tabContainer.CanCloseWithRightClick = true
end
--languageInfo
local addonId = languageInfo and languageInfo.language_addonId or "none"
for tabIndex, tabInfo in ipairs(tabList) do
--create a frame which will be shown when the tabButton is clicked
--when this tab isn't selected, this frame is hidden
---@type df_tabcontainerframe
local tabFrame = CreateFrame("frame", "$parent" .. tabInfo.name, tabContainer, "BackdropTemplate")
detailsFramework:Mixin(tabFrame, detailsFramework.TabContainerFrameMixin)
tabFrame:SetAllPoints()
tabFrame:SetFrameLevel(210)
tabFrame:SetScript("OnMouseDown", tabFrame.OnMouseDown)
tabFrame:SetScript("OnMouseUp", tabFrame.OnMouseUp)
tabFrame.tabIndex = tabIndex
tabFrame:Hide()
--attempt to get the localized text from the language system using the addonId and the frameInfo.text
local phraseId = tabInfo.text
local bIsLanguagePrahseID = detailsFramework.Language.DoesPhraseIDExistsInDefaultLanguage(addonId, phraseId)
--create the fontstring which show this tab text, this text is only shown when the tab is shown
local titleLabel = detailsFramework:CreateLabel(tabFrame, "", 16, "silver")
if (bIsLanguagePrahseID) then
DetailsFramework.Language.RegisterObjectWithDefault(addonId, titleLabel, tabInfo.text, tabInfo.text)
else
titleLabel:SetText(tabInfo.text)
end
titleLabel:SetPoint("topleft", mainTitle, "bottomleft", 0, 0)
tabFrame.titleText = titleLabel
---@type df_tabcontainerbutton
local tabButton = detailsFramework:CreateButton(tabContainer, function() tabContainer:SelectTabByIndex(tabIndex) end, buttonWidth, buttonHeight, tabInfo.text, tabIndex, nil, nil, nil, "$parentTabButton" .. tabInfo.name, false, tabTemplate)
PixelUtil.SetSize(tabButton, buttonWidth, buttonHeight)
tabButton:SetFrameLevel(220)
tabButton.textsize = buttonTextSize
tabButton.mainFrame = tabContainer
tabContainer.CreateUnderlineGlow(tabButton)
--register the fontstring with the language system
if (bIsLanguagePrahseID) then
DetailsFramework.Language.RegisterObjectWithDefault(addonId, tabButton["widget"], tabInfo.text, tabInfo.text)
end
local rightClickToBack
if (tabIndex == 1 or optionsTable.rightbutton_always_close) then
rightClickToBack = detailsFramework:CreateLabel(tabFrame, "right click to close", 10, "gray")
rightClickToBack:SetPoint("bottomright", tabFrame, "bottomright", -1, optionsTable.right_click_y or 0)
if (optionsTable.close_text_alpha) then
rightClickToBack:SetAlpha(optionsTable.close_text_alpha)
end
tabFrame.bIsFrontPage = true
else
rightClickToBack = detailsFramework:CreateLabel(tabFrame, "right click to go back to main menu", 10, "gray")
rightClickToBack:SetPoint("bottomright", tabFrame, "bottomright", -1, optionsTable.right_click_y or 0)
if (optionsTable.close_text_alpha) then
rightClickToBack:SetAlpha(optionsTable.close_text_alpha)
end
end
if (optionsTable.hide_click_label) then
rightClickToBack:Hide()
end
table.insert(tabContainer.AllFrames, tabFrame)
table.insert(tabContainer.AllButtons, tabButton)
tabContainer.AllFramesByName[tabInfo.name] = tabFrame
tabContainer.AllFramesByName[tabInfo.text] = tabFrame
tabContainer.AllButtonsByName[tabInfo.name] = tabButton
tabContainer.AllButtonsByName[tabInfo.text] = tabButton
end
--order buttons
local x = buttonAnchorX
local y = buttonAnchorY
local spaceBetweenButtons = 3
local allocatedSpaceForButtons = parentFrameWidth - ((#tabList - 2) * spaceBetweenButtons) - buttonAnchorX + containerWidthOffset
local amountButtonsPerRow = math.floor(allocatedSpaceForButtons / buttonWidth)
tabContainer.AllButtons[1]:SetPoint("topleft", mainTitle, "topleft", x, y)
x = x + buttonWidth + 2
for i = 2, #tabContainer.AllButtons do
local button = tabContainer.AllButtons[i]
PixelUtil.SetPoint(button, "topleft", mainTitle, "topleft", x, y)
x = x + buttonWidth + 2
if (i % amountButtonsPerRow == 0) then
x = buttonAnchorX
y = y - buttonHeight - 1
end
end
--when show the frame, reset to the current internal index
tabContainer:SetScript("OnShow", tabContainer.OnShow)
--select the first frame
local defaultTab = 1
tabContainer:SelectTabByIndex(defaultTab)
return tabContainer
end
--[=[example:
local parent = UIParent
local title = "My AddOn Options"
local frameName = "MyAddOnOptionsFrame"
local tabList = {
{name = "GeneralSettings", text = "General Settings"},
{name = "AdvancedSettings", text = "Advanced Settings"},
{name = "AboutTheAddon", text = "Addon Info"},
}
local optionsTable = {}
local hookList = {}
local languageInfo = {language_addonId = "MyAddOnTocName"}
local tabContainer = DetailsFramework:CreateTabContainer(parent, title, frameName, tabList, optionsTable, hookList, languageInfo)
tabContainer:SetPoint("center", UIParent, "center", 0, 0)
tabContainer:SetSize(750, 450)
tabContainer:Show()
--ways for getting a tab frame and start to create widgets inside it
local tabIndex = 1
local generalSettingsTabFrame = tabContainer:GetTabFrameByIndex(tabIndex) --using a tabIndex
local advancedSettingsTabFrame = tabContainer:GetTabFrameByName("Advanced Settings") --using the tab text
local aboutTabFrame = tabContainer:GetTabFrameByName("AboutTheAddon") --using the tab name
--clicking on tab buttons will automatically show the tab frame, to select a tab frame without clicking on the button, use:
tabContainer:SelectTabByIndex(tabIndex) --using a tabIndex
tabContainer:SelectTabByName("Advanced Settings") --using the tab text
tabContainer:SelectTabByName("AdvancedSettings") --using the tab name
--modify the background color by applying a backdrop
local backdropTable = {edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true}
local backdropColor = {DetailsFramework:GetDefaultBackdropColor()}
local backdropBorderColor = {0, 0, 0, 1}
tabContainer:SetTabFramesBackdrop(backdropTable, backdropColor, backdropBorderColor)
--]=]
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,3 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/..\FrameXML\UI.xsd">
<Script file="textentry.lua"/>
</Ui>
@@ -0,0 +1,462 @@
local DF = _G["DetailsFramework"]
if (not DF or not DetailsFrameworkCanLoad) then
return
end
local _
local type = type
local floor = math.floor
local GetTime = GetTime
local APITimeBarFunctions
do
local metaPrototype = {
WidgetType = "timebar",
dversion = DF.dversion,
}
--check if there's a metaPrototype already existing
if (_G[DF.GlobalWidgetControlNames["timebar"]]) then
--get the already existing metaPrototype
local oldMetaPrototype = _G[DF.GlobalWidgetControlNames["timebar"]]
--check if is older
if ( (not oldMetaPrototype.dversion) or (oldMetaPrototype.dversion < DF.dversion) ) then
--the version is older them the currently loading one
--copy the new values into the old metatable
for funcName, _ in pairs(metaPrototype) do
oldMetaPrototype[funcName] = metaPrototype[funcName]
end
end
else
--first time loading the framework
_G[DF.GlobalWidgetControlNames["timebar"]] = metaPrototype
end
end
local TimeBarMetaFunctions = _G[DF.GlobalWidgetControlNames["timebar"]]
DF:Mixin(TimeBarMetaFunctions, DF.ScriptHookMixin)
--methods
TimeBarMetaFunctions.SetMembers = TimeBarMetaFunctions.SetMembers or {}
TimeBarMetaFunctions.GetMembers = TimeBarMetaFunctions.GetMembers or {}
TimeBarMetaFunctions.__index = function(table, key)
local func = TimeBarMetaFunctions.GetMembers[key]
if (func) then
return func(table, key)
end
local fromMe = rawget(table, key)
if (fromMe) then
return fromMe
end
return TimeBarMetaFunctions[key]
end
TimeBarMetaFunctions.__newindex = function(table, key, value)
local func = TimeBarMetaFunctions.SetMembers[key]
if (func) then
return func(table, value)
else
return rawset(table, key, value)
end
end
--scripts
local OnEnterFunc = function(statusBar)
local kill = statusBar.MyObject:RunHooksForWidget("OnEnter", statusBar, statusBar.MyObject)
if (kill) then
return
end
if (statusBar.MyObject.tooltip) then
GameCooltip2:Reset()
GameCooltip2:AddLine(statusBar.MyObject.tooltip)
GameCooltip2:ShowCooltip(statusBar, "tooltip")
end
end
local OnLeaveFunc = function(statusBar)
local kill = statusBar.MyObject:RunHooksForWidget("OnLeave", statusBar, statusBar.MyObject)
if (kill) then
return
end
if (statusBar.MyObject.tooltip) then
GameCooltip2:Hide()
end
end
local OnHideFunc = function(statusBar)
local kill = statusBar.MyObject:RunHooksForWidget("OnHide", statusBar, statusBar.MyObject)
if (kill) then
return
end
end
local OnShowFunc = function(statusBar)
local kill = statusBar.MyObject:RunHooksForWidget("OnShow", statusBar, statusBar.MyObject)
if (kill) then
return
end
end
local OnMouseDownFunc = function(statusBar, mouseButton)
local kill = statusBar.MyObject:RunHooksForWidget("OnMouseDown", statusBar, statusBar.MyObject)
if (kill) then
return
end
end
local OnMouseUpFunc = function(statusBar, mouseButton)
local kill = statusBar.MyObject:RunHooksForWidget("OnMouseUp", statusBar, statusBar.MyObject)
if (kill) then
return
end
end
--timer functions
function TimeBarMetaFunctions:SetIconSize(width, height)
if (width and not height) then
self.statusBar.icon:SetWidth(width)
elseif (not width and height) then
self.statusBar.icon:SetHeight(height)
elseif (width and height) then
self.statusBar.icon:SetSize(width, height)
end
end
function TimeBarMetaFunctions:SetIcon(texture, L, R, T, B)
if (texture) then
self.statusBar.icon:Show()
self.statusBar.icon:SetPoint("left", self.statusBar, "left", 2, 0)
self.statusBar.icon:SetSize(self.statusBar:GetHeight()-2, self.statusBar:GetHeight()-2)
self.statusBar.leftText:ClearAllPoints()
self.statusBar.leftText:SetPoint("left", self.statusBar.icon, "right", 2, 0)
self.statusBar.icon:SetTexture(texture)
if (L) then
self.statusBar.icon:SetTexCoord(L, R, T, B)
end
else
self.statusBar.icon:Hide()
self.statusBar.leftText:ClearAllPoints()
self.statusBar.leftText:SetPoint("left", self.statusBar, "left", 2, 0)
end
end
function TimeBarMetaFunctions:GetIcon()
return self.statusBar.icon
end
function TimeBarMetaFunctions:SetTexture(texture)
self.statusBar.barTexture:SetTexture(texture)
end
function TimeBarMetaFunctions:SetColor(color, green, blue, alpha)
local r, g, b, a = DF:ParseColors(color, green, blue, alpha)
self.statusBar.barTexture:SetVertexColor(r, g, b, a)
end
function TimeBarMetaFunctions:SetLeftText(text)
self.statusBar.leftText:SetText(text)
end
function TimeBarMetaFunctions:SetRightText(text)
self.statusBar.rightText:SetText(text)
end
function TimeBarMetaFunctions:SetFont(font, size, color, shadow)
if (font) then
DF:SetFontFace(self.statusBar.leftText, font)
end
if (size) then
DF:SetFontSize(self.statusBar.leftText, size)
end
if (color) then
DF:SetFontColor(self.statusBar.leftText, color)
end
if (shadow) then
DF:SetFontOutline(self.statusBar.leftText, shadow)
end
end
function TimeBarMetaFunctions:SetThrottle(seconds)
if (seconds and seconds > 0) then
self.statusBar.isUsingThrottle = true
self.statusBar.amountThrottle = seconds
else
self.statusBar.isUsingThrottle = false
end
end
function TimeBarMetaFunctions:SetDirection(direction)
direction = direction or "right"
self.direction = direction
end
function TimeBarMetaFunctions:HasTimer()
return self.statusBar.hasTimer
end
function TimeBarMetaFunctions:StopTimer()
if (self.statusBar.hasTimer) then
self.statusBar.hasTimer = nil
local kill = self:RunHooksForWidget("OnTimerEnd", self.statusBar, self)
if (kill) then
return
end
end
local statusBar = self.statusBar
statusBar:SetScript("OnUpdate", nil)
statusBar:SetMinMaxValues(0, 100)
statusBar:SetValue(100)
statusBar.rightText:SetText("")
statusBar.spark:Hide()
end
function TimeBarMetaFunctions:ShowSpark(state, alpha, color)
if (type(state) == "boolean" and state == false) then
self.statusBar.dontShowSpark = true
else
self.statusBar.dontShowSpark = nil
end
if (alpha) then
self.statusBar.sparkAlpha = alpha
else
self.statusBar.sparkAlpha = nil
end
if (color) then
local r, g, b = DF:ParseColors(color)
if (r and g and b) then
self.statusBar.sparkColorR = r
self.statusBar.sparkColorG = g
self.statusBar.sparkColorB = b
end
else
self.statusBar.sparkColorR = nil
self.statusBar.sparkColorG = nil
self.statusBar.sparkColorB = nil
end
end
local OnUpdateFunc = function(self, deltaTime)
if (self.isUsingThrottle) then
self.throttle = self.throttle + deltaTime
if (self.throttle < self.amountThrottle) then
return
end
self.throttle = 0
end
local timeNow = GetTime()
self:SetValue(timeNow)
--adjust the spark
local spark = self.spark
local startTime, endTime = self:GetMinMaxValues()
if (not self.dontShowSpark) then
if (self.direction == "right") then
local pct = abs((timeNow - endTime) / (endTime - startTime))
pct = abs(1 - pct)
spark:SetPoint("left", self, "left", (self:GetWidth() * pct) - 16, 0)
spark:Show()
else
spark:SetPoint("right", self, "right", self:GetWidth() * (timeNow/self.endTime), 0)
end
end
local timeLeft = floor(endTime - timeNow)
local formatedTimeLeft = DF:IntegerToTimer(timeLeft)
self.rightText:SetText(formatedTimeLeft)
--check if finished
if (timeNow >= self.endTime) then
self.MyObject:StopTimer()
end
end
function TimeBarMetaFunctions:SetTimer(currentTime, startTime, endTime)
self.statusBar:Show()
if (not currentTime or currentTime == 0) then
self:StopTimer()
return
end
if (startTime and endTime) then
if (self.statusBar.hasTimer and currentTime == self.statusBar.timeLeft1) then
--it is the same timer called again
return
end
self.statusBar.startTime = startTime
self.statusBar.endTime = endTime
else
local bForceNewTimer = type(startTime) == "boolean" and startTime
if (self.statusBar.hasTimer and currentTime == self.statusBar.timeLeft2 and not bForceNewTimer) then
--it is the same timer called again
return
end
self.statusBar.startTime = GetTime()
self.statusBar.endTime = GetTime() + currentTime
self.statusBar.timeLeft2 = currentTime
end
self.statusBar:SetMinMaxValues(self.statusBar.startTime, self.statusBar.endTime)
if (self.direction == "right") then
self.statusBar:SetReverseFill(false)
else
self.statusBar:SetReverseFill(true)
end
if (self.statusBar.dontShowSpark) then
self.statusBar.spark:Hide()
else
self.statusBar.spark:Show()
self.statusBar.spark:SetHeight(self.statusBar:GetHeight()+20)
if (self.statusBar.sparkAlpha) then
self.statusBar.spark:SetAlpha(self.statusBar.sparkAlpha)
else
self.statusBar.spark:SetAlpha(1)
end
if (self.statusBar.sparkColorR) then
self.statusBar.spark:SetVertexColor(self.statusBar.sparkColorR, self.statusBar.sparkColorG, self.statusBar.sparkColorB)
else
self.statusBar.spark:SetVertexColor(1, 1, 1)
end
end
self.statusBar.hasTimer = true
self.statusBar.direction = self.direction
self.statusBar.throttle = 0
self.statusBar:SetScript("OnUpdate", OnUpdateFunc)
local kill = self:RunHooksForWidget("OnTimerStart", self.statusBar, self)
if (kill) then
return
end
end
function DF:CreateTimeBar(parent, texture, width, height, value, member, name)
if (not name) then
name = "DetailsFrameworkBarNumber" .. DF.BarNameCounter
DF.BarNameCounter = DF.BarNameCounter + 1
elseif (not parent) then
return error("Details! FrameWork: parent not found.", 2)
end
if (name:find("$parent")) then
local parentName = DF.GetParentName(parent)
name = name:gsub("$parent", parentName)
end
local timeBar = {
type = "timebar",
dframework = true
}
if (member) then
parent[member] = timeBar
end
if (parent.dframework) then
parent = parent.widget
end
value = value or 0
width = width or 150
height = height or 14
timeBar.locked = false
timeBar.statusBar = CreateFrame("statusbar", name, parent, "BackdropTemplate")
timeBar.widget = timeBar.statusBar
DF:Mixin(timeBar.statusBar, DF.WidgetFunctions)
timeBar.statusBar.MyObject = timeBar
timeBar.direction = "right"
if (not APITimeBarFunctions) then
APITimeBarFunctions = true
local idx = getmetatable(timeBar.statusBar).__index
for funcName, funcAddress in pairs(idx) do
if (not TimeBarMetaFunctions[funcName]) then
TimeBarMetaFunctions[funcName] = function(object, ...)
local x = loadstring("return _G['"..object.statusBar:GetName().."']:"..funcName.."(...)")
return x(...)
end
end
end
end
--create widgets
timeBar.statusBar:SetWidth(width)
timeBar.statusBar:SetHeight(height)
timeBar.statusBar:SetFrameLevel(parent:GetFrameLevel()+1)
timeBar.statusBar:SetMinMaxValues(0, 100)
timeBar.statusBar:SetValue(value or 100)
timeBar.statusBar:EnableMouse(false)
timeBar.statusBar.backgroundTexture = timeBar.statusBar:CreateTexture(nil, "border")
timeBar.statusBar.backgroundTexture:SetColorTexture(.1, .1, .1, .6)
timeBar.statusBar.backgroundTexture:SetAllPoints()
timeBar.statusBar.barTexture = timeBar.statusBar:CreateTexture(nil, "artwork")
timeBar.statusBar.barTexture:SetTexture(texture or [[Interface\WorldStateFrame\WORLDSTATEFINALSCORE-HIGHLIGHT]])
timeBar.statusBar:SetStatusBarTexture(timeBar.statusBar.barTexture)
timeBar.statusBar.spark = timeBar.statusBar:CreateTexture(nil, "overlay", nil, 7)
timeBar.statusBar.spark:SetTexture([[Interface\CastingBar\UI-CastingBar-Spark]])
timeBar.statusBar.spark:SetBlendMode("ADD")
timeBar.statusBar.spark:Hide()
timeBar.statusBar.icon = timeBar.statusBar:CreateTexture(nil, "overlay", nil, 5)
timeBar.statusBar.icon:SetPoint("left", timeBar.statusBar, "left", 2, 0)
timeBar.statusBar.leftText = timeBar.statusBar:CreateFontString("$parentLeftText", "overlay", "GameFontNormal", 4)
timeBar.statusBar.leftText:SetPoint("left", timeBar.statusBar.icon, "right", 2, 0)
timeBar.statusBar.rightText = timeBar.statusBar:CreateFontString(nil, "overlay", "GameFontNormal", 4)
timeBar.statusBar.rightText:SetPoint("right", timeBar.statusBar, "right", -2, 0)
timeBar.statusBar.rightText:SetJustifyH("left")
--hooks
timeBar.HookList = {
OnEnter = {},
OnLeave = {},
OnHide = {},
OnShow = {},
OnMouseDown = {},
OnMouseUp = {},
OnTimerStart = {},
OnTimerEnd = {},
}
timeBar.statusBar:SetScript("OnEnter", OnEnterFunc)
timeBar.statusBar:SetScript("OnLeave", OnLeaveFunc)
timeBar.statusBar:SetScript("OnHide", OnHideFunc)
timeBar.statusBar:SetScript("OnShow", OnShowFunc)
timeBar.statusBar:SetScript("OnMouseDown", OnMouseDownFunc)
timeBar.statusBar:SetScript("OnMouseUp", OnMouseUpFunc)
--set class
setmetatable(timeBar, TimeBarMetaFunctions)
return timeBar
end
@@ -0,0 +1,2 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/..\FrameXML\UI.xsd">
</Ui>
@@ -0,0 +1,24 @@
local DetailsFramework = _G["DetailsFramework"]
if (not DetailsFramework or not DetailsFrameworkCanLoad) then
return
end
local _
local DF = DetailsFramework
--backdrop namespace
DF.BackdropUtil = {}
function DF.BackdropUtil:SetColorStripe(frame, index, backdrop, color1, color2)
if (backdrop == nil or type(backdrop) == "table") then
frame:SetBackdrop(backdrop and {bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true})
end
if (index % 2 == 0) then
local r, g, b, a = DF:ParseColors(color1 or {.2, .2, .2, 0.5})
frame:SetBackdropColor(r, g, b, a)
else
local r, g, b, a = DF:ParseColors(color2 or {.3, .3, .3, 0.5})
frame:SetBackdropColor(r, g, b, a)
end
end
@@ -0,0 +1,401 @@
--uiobject: is an object that represents a UI element, such as a frame, a texture, or a button. UIObjects are the base class for all UI elements in the WoW API.
--3D World: is an object which is placed behind|below all UI elements, cannot be parent of any object, in the 3D World object is where the game world is rendered
--size: corresponds to the height and height of an object, it is measure in pixels, must be bigger than zero.
--scale: the size of an object is multiplied by this value, it is measure in percentage, must be between 0.65 and 2.40.
--alpha: corresponds to the transparency of an object, the bigger is the value less transparent is the object, it is measure in percentage, must be between 0 and 1, zero is fully transparent and one is fully opaque.
--controller: abstract term to define who's in control of an entity, can be the server or a player.
--npc: an entity shown in the 3d world with a name and a health bar, can be friendly or hostile, can be interacted with, always controlled by the server.
--player: is an entity that represents a player character, the controller is always player.
--pet: represents a npc controlled by the server and can accept commands from the player.
--guadians: represents a npc, the server has the possess of the controller, don't accept commands like pets, helps attacking the enemies of the npc or player.
--role: is a string that represents the role of a unit, such as tank, healer, or damage dealer. only players can have a role.
---@alias role
---| "TANK"
---| "HEALER"
---| "DAMAGER"
---| "NONE"
---@alias anchorpoint
---| "topleft"
---| "topright"
---| "bottomleft"
---| "bottomright"
---| "top"
---| "bottom"
---| "left"
---| "right"
---| "center"
---@alias framestrata
---| "background"
---| "low"
---| "medium"
---| "high"
---| "dialog"
---| "fullscreen"
---| "fullscreen_dialog"
---| "tooltip"
---| "BACKGROUND"
---| "LOW"
---| "MEDIUM"
---| "HIGH"
---| "DIALOG"
---| "FULLSCREEN"
---| "FULLSCREEN_DIALOG"
---| "TOOLTIP"
---@alias sizingpoint
---| "top"
---| "topright"
---| "right"
---| "bottomright"
---| "bottom"
---| "bottomleft"
---| "left"
---| "topleft"
---@alias drawlayer
---| "background"
---| "border"
---| "artwork"
---| "overlay"
---| "highlight"
---@alias buttontype
---| "AnyUp"
---| "AnyDown"
---| "LeftButtonDown"
---| "LeftButtonUp"
---| "MiddleButtonUp"
---| "MiddleButtonDown"
---| "RightButtonDown"
---| "RightButtonUp"
---| "Button4Up"
---| "Button4Down"
---| "Button5Up"
---| "Button5Down"
---@alias justifyh
---| "left"
---| "right"
---| "center"
---@alias justifyv
---| "top"
---| "bottom"
---| "middle"
---@alias orientation
---| "HORIZONTAL"
---| "VERTICAL"
---@alias width number property that represents the horizontal size of a UI element, such as a frame or a texture. Gotten from the first result of GetWidth() or from the first result of GetSize(). It is expected a GetWidth() or GetSize() when the type 'height' is used.
---@alias height number property that represents the vertical size of a UI element, such as a frame or a texture. Gotten from the first result of GetHeight() or from the second result of GetSize(). It is expected a GetHeight() or GetSize() when the type 'height' is used.
---@alias red number color value representing the red component of a color, the value must be between 0 and 1. To retrieve a color from a string or table use: local red, green, blue, alpha = DetailsFramework:ParseColors(color)
---@alias green number color value representing the green component of a color, the value must be between 0 and 1. To retrieve a color from a string or table use: local red, green, blue, alpha = DetailsFramework:ParseColors(color)
---@alias blue number color value representing the blue component of a color, the value must be between 0 and 1. To retrieve a color from a string or table use: local red, green, blue, alpha = DetailsFramework:ParseColors(color)
---@alias alpha number @number(0-1.0) value representing the alpha (transparency) of a UIObject, the value must be between 0 and 1. 0 is fully transparent, 1 is fully opaque.
---@alias unit string string that represents a unit in the game, such as the player, a party member, or a raid member.
---@alias health number amount of hit points (health) of a unit. This value can be changed by taking damage or healing.
---@alias spellid number each spell in the game has a unique spell id, this id can be used to identify a spell.
---@alias actorname string name of a unit
---@alias spellname string name of a spell
---@alias spellschool number each spell in the game has a school, such as fire, frost, shadow and many others. This value can be used to identify the school of a spell.
---@alias actorid string unique id of a unit (GUID)
---@alias serial string unique id of a unit (GUID)
---@alias color table, string @table(r: red|number, g: green|number, b: blue|number, a: alpha|number) @string(color name) @hex (000000-ffffff) value representing a color, the value must be a table with the following fields: r, g, b, a. r, g, b are numbers between 0 and 1, a is a number between 0 and 1. To retrieve a color from a string or table use: local red, green, blue, alpha = DetailsFramework:ParseColors(color)
---@alias scale number @number(0.65-2.40) value representing the scale factor of the UIObject, the value must be between 0.65 and 2.40, the width and height of the UIObject will be multiplied by this value.
---@alias script string, function is a piece of code that is executed in response to a specific event, such as a button click or a frame update. Scripts can be used to implement behavior and logic for UI elements.
---@alias event string is a notification that is sent to a frame when something happens, such as a button click or a frame update. Events can be used to trigger scripts.
---@alias backdrop table @table(bgFile: string, edgeFile: string, tile: edgeSize: number, backgroundColor: color, borderColor: color) is a table that contains information about the backdrop of a frame. The backdrop is the background of a frame, which can be a solid color, a gradient, or a texture.
---@alias npcid number a number that identifies a specific npc in the game.
---@alias textureid number each texture from the game client has an id.
---@alias texturepath string access textures from addons.
---@alias unixtime number
---@alias valueamount number used to represent a value, such as a damage amount, a healing amount, or a resource amount.
---@class _G
---@field RegisterAttributeDriver fun(statedriver: frame, attribute: string, conditional: string)
---@field RegisterStateDriver fun(statedriver: frame, attribute: string, conditional: string)
---@field UnitGUID fun(unit: string): string
---@field UnitName fun(unit: string): string
---@field GetCursorPosition fun(): number, number return the position of the cursor on the screen, in pixels, relative to the bottom left corner of the screen.
---@field C_Timer C_Timer
---@class timer : table
---@field Cancel fun(self: timer)
---@field IsCancelled fun(self: timer): boolean
---@class C_Timer : table
---@field After fun(delay: number, func: function)
---@field NewTimer fun(delay: number, func: function): timer
---@field NewTicker fun(interval: number, func: function, iterations: number|nil): timer
---@class C_ChallengeMode : table
---@field GetActiveKeystoneInfo fun(): number, number[], boolean @returns keystoneLevel, affixIDs, wasActive
---@class tablesize : {H: number, W: number}
---@class tablecoords : {L: number, R: number, T: number, B: number}
---@class texturecoords: {left: number, right: number, top: number, bottom: number}
---@class objectsize : {height: number, width: number}
---@class texturetable : {texture: string, coords: texturecoords, size: objectsize}
---@class uiobject
---@field GetObjectType fun(self: uiobject) : string
---@field Show fun(self: uiobject) make the object be shown on the user screen
---@field Hide fun(self: uiobject) make the object be hidden from the user screen
---@field SetShown fun(self: uiobject, state: boolean) show or hide the object
---@field IsShown fun(self: uiobject) : boolean return if the object is shown or not
---@field SetAllPoints fun(self: uiobject, target: uiobject|nil) set the object to be the same size as its parent or the target object
---@field SetParent fun(self: uiobject, parent: frame) set the parent object of the object
---@field SetSize fun(self: uiobject, width: width|number, height: height|number) set the width and height of the object
---@field SetWidth fun(self: uiobject, width: width|number) set only the width of the object
---@field SetHeight fun(self: uiobject, height: height|number) set only the height of the object
---@field SetAlpha fun(self: uiobject, alpha: alpha|number) set the transparency of the object
---@field SetScale fun(self: uiobject, scale: scale|number)
---@field GetWidth fun(self: uiobject) : width|number
---@field GetHeight fun(self: uiobject) : height|number
---@field GetScale fun(self: uiobject) : scale|number
---@field GetAlpha fun(self: uiobject) : alpha|number
---@field GetSize fun(self: uiobject) : width|number, height|number
---@field GetParent fun(self: uiobject) : frame
---@field GetPoint fun(self: uiobject, index: number): string, frame, string, number, number
---@field GetCenter fun(self: uiobject): number, number
---@field SetPoint fun(self: uiobject, point: anchorpoint, relativeFrame: uiobject, relativePoint: anchorpoint, xOffset: number, yOffset: number)
---@field ClearAllPoints fun(self: uiobject)
---@field CreateAnimationGroup fun(self: uiobject, name: string|nil, templateName: string|nil) : animationgroup
---@class animationgroup : uiobject
---@field CreateAnimation fun(self: animationgroup, animationType: string, name: string|nil, inheritsFrom: string|nil) : animation
---@field GetAnimation fun(self: animationgroup, name: string) : animation
---@field GetAnimations fun(self: animationgroup) : table
---@field GetDuration fun(self: animationgroup) : number
---@field GetEndDelay fun(self: animationgroup) : number
---@field GetLoopState fun(self: animationgroup) : boolean
---@field GetScript fun(self: animationgroup, event: string) : function
---@field GetSmoothProgress fun(self: animationgroup) : boolean
---@field IsDone fun(self: animationgroup) : boolean
---@field IsPaused fun(self: animationgroup) : boolean
---@field IsPlaying fun(self: animationgroup) : boolean
---@field Pause fun(self: animationgroup)
---@field Play fun(self: animationgroup)
---@field Resume fun(self: animationgroup)
---@field SetDuration fun(self: animationgroup, duration: number)
---@field SetEndDelay fun(self: animationgroup, delay: number)
---@field SetLooping fun(self: animationgroup, loop: boolean)
---@field SetScript fun(self: animationgroup, event: string, handler: function|nil) "OnEvent"|"OnShow"
---@field SetSmoothProgress fun(self: animationgroup, smooth: boolean)
---@field Stop fun(self: animationgroup)
---@class animation : uiobject
---@field GetDuration fun(self: animation) : number
---@field GetEndDelay fun(self: animation) : number
---@field GetOrder fun(self: animation) : number
---@field GetScript fun(self: animation, event: string) : function
---@field GetSmoothing fun(self: animation) : string
---@field IsDone fun(self: animation) : boolean
---@field IsPaused fun(self: animation) : boolean
---@field IsPlaying fun(self: animation) : boolean
---@field Pause fun(self: animation)
---@field Play fun(self: animation)
---@field Resume fun(self: animation)
---@field SetDuration fun(self: animation, duration: number)
---@field SetEndDelay fun(self: animation, delay: number)
---@field SetOrder fun(self: animation, order: number)
---@field SetScript fun(self: animation, event: string, handler: function|nil)
---@field SetSmoothing fun(self: animation, smoothing: string)
---@field Stop fun(self: animation)
---@class line : uiobject
---@field GetEndPoint fun(self: line) : relativePoint: anchorpoint, relativeTo: anchorpoint, offsetX: number, offsetY: number
---@field GetStartPoint fun(self: line) : relativePoint: anchorpoint, relativeTo: anchorpoint, offsetX: number, offsetY: number
---@field GetThickness fun(self: line) : number
---@field SetStartPoint fun(self: line, point: anchorpoint, relativeFrame: uiobject|number, relativePoint: anchorpoint|number, xOffset: number|nil, yOffset: number|nil)
---@field SetEndPoint fun(self: line, point: anchorpoint, relativeFrame: uiobject|number, relativePoint: anchorpoint|number, xOffset: number|nil, yOffset: number|nil)
---@field SetColorTexture fun(self: line, red: number, green: number, blue: number, alpha: number)
---@field SetThickness fun(self: line, thickness: number)
---@class frame : uiobject
---@field CreateLine fun(self: frame, name: string|nil, drawLayer: drawlayer, templateName: string|nil, subLevel: number|nil) : line
---@field SetID fun(self: frame, id: number) set an ID for the frame
---@field SetAttribute fun(self: frame, name: string, value: any)
---@field SetScript fun(self: frame, event: string, handler: function|nil)
---@field GetScript fun(self: frame, event: string) : function
---@field SetFrameStrata fun(self: frame, strata: framestrata)
---@field SetFrameLevel fun(self: frame, level: number)
---@field SetClampedToScreen fun(self: frame, clamped: boolean)
---@field SetClampRectInsets fun(self: frame, left: number, right: number, top: number, bottom: number)
---@field SetMovable fun(self: frame, movable: boolean)
---@field SetUserPlaced fun(self: frame, userPlaced: boolean)
---@field SetBackdrop fun(self: frame, backdrop: backdrop|table)
---@field SetBackdropColor fun(self: frame, red: red|number, green: green|number, blue: blue|number, alpha: alpha|number)
---@field SetBackdropBorderColor fun(self: frame, red: red|number, green: green|number, blue: blue|number, alpha: alpha|number)
---@field SetHitRectInsets fun(self: frame, left: number, right: number, top: number, bottom: number)
---@field SetToplevel fun(self: frame, toplevel: boolean)
---@field SetPropagateKeyboardInput fun(self: frame, propagate: boolean)
---@field SetPropagateGamepadInput fun(self: frame, propagate: boolean)
---@field StartMoving fun(self: frame)
---@field IsMovable fun(self: frame) : boolean
---@field StartSizing fun(self: frame, sizingpoint: sizingpoint|nil)
---@field StopMovingOrSizing fun(self: frame)
---@field GetAttribute fun(self: frame, name: string) : any
---@field GetFrameLevel fun(self: frame) : number
---@field GetFrameStrata fun(self: frame) : framestrata
---@field GetNumChildren fun(self: frame) : number
---@field GetNumPoints fun(self: frame) : number
---@field GetNumRegions fun(self: frame) : number
---@field GetName fun(self: frame) : string
---@field GetChildren fun(self: frame) : frame[]
---@field GetRegions fun(self: frame) : region[]
---@field CreateTexture fun(self: frame, name: string|nil, layer: drawlayer, inherits: string|nil, subLayer: number|nil) : texture
---@field CreateFontString fun(self: frame, name: string|nil, layer: drawlayer, inherits: string|nil, subLayer: number|nil) : fontstring
---@field EnableMouse fun(self: frame, enable: boolean) enable mouse interaction
---@field SetResizable fun(self: frame, enable: boolean) enable resizing of the frame
---@field EnableMouseWheel fun(self: frame, enable: boolean) enable mouse wheel scrolling
---@field RegisterForDrag fun(self: frame, button: string) register the frame for drag events, allowing it to be dragged by the mouse
---@field SetResizeBounds fun(self: frame, minWidth: number, minHeight: number, maxWidth: number, maxHeight: number) set the minimum and maximum size of the frame
---@field RegisterEvent fun(self: frame, event: string) register for an event, trigers "OnEvent" script when the event is fired
---@field HookScript fun(self: frame, event: string, handler: function) run a function after the frame's script has been executed, carrying the same arguments
---@class button : frame
---@field Click fun(self: button)
---@field SetNormalTexture fun(self: button, texture: textureid|texturepath)
---@field SetPushedTexture fun(self: button, texture: textureid|texturepath)
---@field SetHighlightTexture fun(self: button, texture: textureid|texturepath)
---@field SetDisabledTexture fun(self: button, texture: textureid|texturepath)
---@field SetCheckedTexture fun(self: button, texture: textureid|texturepath)
---@field SetNormalFontObject fun(self: button, fontString: fontstring)
---@field SetHighlightFontObject fun(self: button, fontString: fontstring)
---@field SetDisabledFontObject fun(self: button, fontString: fontstring)
---@field SetText fun(self: button, text: string)
---@field GetText fun(self: button) : string
---@field SetTextInsets fun(self: button, left: number, right: number, top: number, bottom: number)
---@field GetTextInsets fun(self: button) : number, number, number, number
---@field SetDisabledTextColor fun(self: button, r: red|number, g: green|number, b: blue|number, a: alpha|number)
---@field GetDisabledTextColor fun(self: button) : number, number, number, number
---@field SetFontString fun(self: button, fontString: fontstring)
---@field GetFontString fun(self: button) : fontstring
---@field SetButtonState fun(self: button, state: string, enable: boolean)
---@field GetButtonState fun(self: button, state: string) : boolean
---@field RegisterForClicks fun(self: button, button1: nil|buttontype, button2: nil|buttontype, button3: nil|buttontype, button4: nil|buttontype)
---@field GetNormalTexture fun(self: button) : texture
---@field GetPushedTexture fun(self: button) : texture
---@field GetHighlightTexture fun(self: button) : texture
---@field GetDisabledTexture fun(self: button) : texture
---@class statusbar : frame
---@field SetStatusBarColor fun(self: statusbar, r: red|number, g: green|number, b: blue|number, a: alpha|number)
---@field SetStatusBarTexture fun(self: statusbar, path: string|texture)
---@field GetStatusBarTexture fun(self: statusbar) : texture
---@field SetMinMaxValues fun(self: statusbar, minValue: number, maxValue: number)
---@field SetValue fun(self: statusbar, value: number)
---@field SetValueStep fun(self: statusbar, valueStep: number)
---@field SetOrientation fun(self: statusbar, orientation: orientation)
---@field SetReverseFill fun(self: statusbar, reverseFill: boolean)
---@field GetMinMaxValues fun(self: statusbar) : number, number
---@field GetValue fun(self: statusbar) : number
---@field GetValueStep fun(self: statusbar) : number
---@field GetOrientation fun(self: statusbar) : orientation
---@field GetReverseFill fun(self: statusbar) : boolean
---@class scrollframe : frame
---@field SetScrollChild fun(self: scrollframe, child: frame)
---@field GetScrollChild fun(self: scrollframe) : frame
---@field SetHorizontalScroll fun(self: scrollframe, offset: number)
---@field SetVerticalScroll fun(self: scrollframe, offset: number)
---@field GetHorizontalScroll fun(self: scrollframe) : number
---@field GetVerticalScroll fun(self: scrollframe) : number
---@field GetHorizontalScrollRange fun(self: scrollframe) : number
---@field GetVerticalScrollRange fun(self: scrollframe) : number
---@class region : uiobject
---@class fontstring : region
---@field SetDrawLayer fun(self: fontstring, layer: drawlayer, subLayer: number|nil)
---@field SetFont fun(self: fontstring, font: string, size: number, flags: string)
---@field SetText fun(self: fontstring, text: string|number)
---@field GetText fun(self: fontstring) : string
---@field GetFont fun(self: fontstring) : string, number, string
---@field GetStringWidth fun(self: fontstring) : number return the width of the string in pixels
---@field SetShadowColor fun(self: fontstring, r: red|number, g: green|number, b: blue|number, a: alpha|number)
---@field GetShadowColor fun(self: fontstring) : number, number, number, number
---@field SetShadowOffset fun(self: fontstring, offsetX: number, offsetY: number)
---@field GetShadowOffset fun(self: fontstring) : number, number
---@field SetTextColor fun(self: fontstring, r: red|number, g: green|number, b: blue|number, a: alpha|number)
---@field GetTextColor fun(self: fontstring) : number, number, number, number
---@field SetJustifyH fun(self: fontstring, justifyH: justifyh)
---@field GetJustifyH fun(self: fontstring) : string
---@field SetJustifyV fun(self: fontstring, justifyV: justifyv)
---@field GetJustifyV fun(self: fontstring) : string
---@field SetNonSpaceWrap fun(self: fontstring, nonSpaceWrap: boolean)
---@field GetNonSpaceWrap fun(self: fontstring) : boolean
---@field SetIndentedWordWrap fun(self: fontstring, indentedWordWrap: boolean)
---@field GetIndentedWordWrap fun(self: fontstring) : boolean
---@field SetMaxLines fun(self: fontstring, maxLines: number)
---@field GetMaxLines fun(self: fontstring) : number
---@field SetWordWrap fun(self: fontstring, wordWrap: boolean)
---@field GetWordWrap fun(self: fontstring) : boolean
---@field SetSpacing fun(self: fontstring, spacing: number)
---@field GetSpacing fun(self: fontstring) : number
---@field SetLineSpacing fun(self: fontstring, lineSpacing: number)
---@field GetLineSpacing fun(self: fontstring) : number
---@field SetMaxLetters fun(self: fontstring, maxLetters: number)
---@field GetMaxLetters fun(self: fontstring) : number
---@field SetTextInsets fun(self: fontstring, left: number, right: number, top: number, bottom: number)
---@field GetTextInsets fun(self: fontstring) : number, number, number, number
---@field SetTextJustification fun(self: fontstring, justifyH: string, justifyV: string)
---@field GetTextJustification fun(self: fontstring) : string, string
---@field SetTextShadowColor fun(self: fontstring, r: red|number, g: green|number, b: blue|number, a: alpha|number)
---@field GetTextShadowColor fun(self: fontstring) : number, number, number, number
---@field SetTextShadowOffset fun(self: fontstring, offsetX: number, offsetY: number)
---@field GetTextShadowOffset fun(self: fontstring) : number, number
---@field SetTextShadow fun(self: fontstring, offsetX: number, offsetY: number, r: red|number, g: green|number, b: blue|number, a: alpha|number)
---@field SetTextTruncate fun(self: fontstring, truncate: string)
---@field GetTextTruncate fun(self: fontstring) : string
---@field SetTextTruncateWidth fun(self: fontstring, width: number)
---@field GetTextTruncateWidth fun(self: fontstring) : number
---@field SetTextTruncateLines fun(self: fontstring, lines: number)
---@field GetTextTruncateLines fun(self: fontstring) : number
---@class texture : region
---@field SetDrawLayer fun(self: texture, layer: drawlayer, subLayer: number|nil)
---@field SetTexture fun(self: texture, path: string)
---@field SetAtlas fun(self: texture, atlas: string)
---@field SetColorTexture fun(self: texture, r: red|number, g: green|number, b: blue|number, a: alpha|number|nil)
---@field SetDesaturated fun(self: texture, desaturate: boolean)
---@field SetBlendMode fun(self: texture, mode: "ADD"|"BLEND"|"DISABLE"|"MOD"|"MOD2X"|"OVERLAY"|"REPLACE"|"SUBTRACT")
---@field SetVertexColor fun(self: texture, r: red|number, g: green|number, b: blue|number, a: alpha|number|nil)
---@field GetPoint fun(self: texture, index: number) : string, table, string, number, number
---@field SetShown fun(self: texture, state: boolean)
---@field IsShown fun(self: texture) : boolean
---@field GetParent fun(self: texture) : table
---@field SetTexCoord fun(self: texture, left: number, right: number, top: number, bottom: number)
---@field GetTexCoord fun(self: texture) : number, number, number, number
---@field SetRotation fun(self: texture, rotation: number)
---@field GetRotation fun(self: texture) : number
---@field SetRotationRadians fun(self: texture, rotation: number)
---@field GetRotationRadians fun(self: texture) : number
---@field SetRotationDegrees fun(self: texture, rotation: number)
---@field GetRotationDegrees fun(self: texture) : number
---@field SetMask fun(self: texture, mask: table)
---@field GetMask fun(self: texture) : table
---@field SetMaskTexture fun(self: texture, maskTexture: table)
---@field GetMaskTexture fun(self: texture) : table
---@field GetDesaturated fun(self: texture) : boolean
---@field SetGradient fun(self: texture, gradient: string)
---@field GetGradient fun(self: texture) : string
---@field SetGradientAlpha fun(self: texture, gradient: string)
---@field GetGradientAlpha fun(self: texture) : string
---@field SetGradientRotation fun(self: texture, rotation: number)
---@field GetGradientRotation fun(self: texture) : number
---@field SetGradientRotationRadians fun(self: texture, rotation: number)
---@field GetGradientRotationRadians fun(self: texture) : number
---@field SetGradientRotationDegrees fun(self: texture, rotation: number)
---@field GetGradientRotationDegrees fun(self: texture) : number
---@field SetGradientColors fun(self: texture, ...)
---@field GetGradientColors fun(self: texture) : number, number, number, number, number, number, number, number, number, number, number, number
---@field GetBlendMode fun(self: texture) : string
---@field GetVertexColor fun(self: texture) : number, number, number, number
@@ -1,6 +1,3 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
..\FrameXML\UI.xsd">
<Script file="Libs\LibStub\LibStub.lua"/>
<Include file="Libs\AceLocale-3.0\AceLocale-3.0.xml" />
</Ui>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,419 @@
local addonId, edTable = ...
local Details = _G._detalhes
local AceLocale = LibStub("AceLocale-3.0")
local Loc = AceLocale:GetLocale("Details_EncounterDetails")
local Graphics = LibStub:GetLibrary("LibGraph-2.0")
local ipairs = ipairs
local _GetSpellInfo = Details.getspellinfo
local unpack = unpack
local detailsFramework = DetailsFramework
local CreateFrame = CreateFrame
local GameCooltip = GameCooltip
local GameTooltip = GameTooltip
local wipe = table.wipe
local _
local encounterDetails = _G.EncounterDetailsGlobal
local edFrame = encounterDetails.Frame
edFrame.EnemySpellsWidgets = {}
local CONST_MAX_AURA_LINES = 21
local aurasButtonTemplate = {
backdrop = {edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true},
backdropcolor = {.3, .3, .3, .5},
onentercolor = {1, 1, 1, .5},
backdropbordercolor = {0, 0, 0, 1},
}
local spell_blocks = {}
local bossmods_blocks = {}
local on_focus_gain = function(self)
self:HighlightText()
end
local on_focus_lost = function(self)
self:HighlightText(0, 0)
end
local on_enter_spell = function(self)
if (self.MyObject._spellid) then
GameTooltip:SetOwner(self, "ANCHOR_TOPLEFT")
if (type(self.MyObject._spellid) == "string") then
local spellId = self.MyObject._spellid:gsub("%a", "")
spellId = tonumber(spellId)
if (spellId) then
GameTooltip:SetSpellByID(spellId)
end
else
GameTooltip:SetSpellByID(self.MyObject._spellid)
end
GameTooltip:Show()
self:SetBackdropColor(1, 1, 1, .5)
self:SetBackdropBorderColor(0, 0, 0, 1)
return true
end
end
local on_leave_spell = function(self, capsule)
GameTooltip:Hide()
self:SetBackdropColor(.3, .3, .3, .5)
end
local create_aura_func = function(self, button, spellid, encounter_id)
local name, _, icon = encounterDetails.getspellinfo(spellid)
encounterDetails:OpenAuraPanel(spellid, name, self and self.MyObject._icon.texture, encounter_id)
end
local info_onenter = function(self)
local spellid = self._spellid
local info = encounterDetails.EnemySpellPool[spellid]
if (info) then
Details:CooltipPreset(2)
GameCooltip:SetOption("FixedWidth", false)
for token, _ in pairs(info.token) do
GameCooltip:AddLine("event:", token, 1, nil, "white")
end
GameCooltip:AddLine("source:", info.source, 1, nil, "white")
GameCooltip:AddLine("school:", encounterDetails:GetSpellSchoolFormatedName(info.school), 1, nil, "white")
if (info.type) then
GameCooltip:AddLine("aura type:", info.type, 1, nil, "white")
end
GameCooltip:ShowCooltip(self, "tooltip")
end
self:SetBackdropColor(1, 1, 1, .5)
end
local info_onleave = function(self)
GameCooltip:Hide()
self:SetBackdropColor(.3, .3, .3, .5)
end
local bossModsTitle = detailsFramework:CreateLabel(edFrame, "Boss Mods Time Bars:", 12, "orange")
bossModsTitle:SetPoint(10, -85)
table.insert(edFrame.EnemySpellsWidgets, bossModsTitle)
bossModsTitle:Hide()
local bossSpellsTitle = detailsFramework:CreateLabel(edFrame, "Boss Spells and Auras:", 12, "orange")
bossSpellsTitle:SetPoint(444, -85)
table.insert(edFrame.EnemySpellsWidgets, bossSpellsTitle)
bossSpellsTitle:Hide()
--create boss mods list
for i = 1, CONST_MAX_AURA_LINES do
local anchor_frame = CreateFrame("frame", "BossFrameBossModsAnchor" .. i, edFrame, "BackdropTemplate")
local spellicon = detailsFramework:NewImage(anchor_frame, [[Interface\ICONS\TEMP]], 19, 19, "background", nil, "icon", "$parentIcon")
--timerId
local spellid = detailsFramework:CreateTextEntry(anchor_frame, encounterDetails.empty_function, 80, 20, nil, "$parentSpellId")
spellid:SetTemplate(aurasButtonTemplate)
spellid:SetHook("OnEditFocusGained", on_focus_gain)
spellid:SetHook("OnEditFocusLost", on_focus_lost)
spellid:SetHook("OnEnter", on_enter_spell)
spellid:SetHook("OnLeave", on_leave_spell)
--ability name
local spellname = detailsFramework:CreateTextEntry(anchor_frame, encounterDetails.empty_function, 180, 20, nil, "$parentSpellName")
spellname:SetTemplate(aurasButtonTemplate)
spellname:SetHook("OnEditFocusGained", on_focus_gain)
spellname:SetHook("OnEditFocusLost", on_focus_lost)
spellname:SetHook("OnEnter", on_enter_spell)
spellname:SetHook("OnLeave", on_leave_spell)
local create_aura = detailsFramework:NewButton(anchor_frame, nil, "$parentCreateAuraButton", "AuraButton", 90, 18, create_aura_func, nil, nil, nil, "Make Aura")
create_aura:SetTemplate(aurasButtonTemplate)
spellicon:SetPoint("topleft", edFrame, "topleft", 10, -85 +(i * 21 * -1))
spellid:SetPoint("left", spellicon, "right", 4, 0)
spellname:SetPoint("left", spellid, "right", 4, 0)
create_aura:SetPoint("left", spellname, "right", 4, 0)
spellid:SetBackdropBorderColor(0, 0, 0)
spellname:SetBackdropBorderColor(0, 0, 0)
anchor_frame.icon = spellicon
anchor_frame.spellid = spellid
anchor_frame.spellname = spellname
anchor_frame.aurabutton = create_aura
anchor_frame.aurabutton._icon = spellicon
table.insert(bossmods_blocks, anchor_frame)
table.insert(edFrame.EnemySpellsWidgets, anchor_frame)
anchor_frame:Hide()
end
--create buff list
for i = 1, CONST_MAX_AURA_LINES do
local anchor_frame = CreateFrame("frame", "BossFrameSpellAnchor" .. i, edFrame, "BackdropTemplate")
local spellicon = detailsFramework:NewImage(anchor_frame, [[Interface\ICONS\TEMP]], 19, 19, "background", nil, "icon", "$parentIcon")
local spellid = detailsFramework:CreateTextEntry(anchor_frame, encounterDetails.empty_function, 80, 20)
spellid:SetTemplate(aurasButtonTemplate)
spellid:SetHook("OnEditFocusGained", on_focus_gain)
spellid:SetHook("OnEditFocusLost", on_focus_lost)
spellid:SetHook("OnEnter", on_enter_spell)
spellid:SetHook("OnLeave", on_leave_spell)
local spellname = detailsFramework:CreateTextEntry(anchor_frame, encounterDetails.empty_function, 160, 20)
spellname:SetTemplate(aurasButtonTemplate)
spellname:SetHook("OnEditFocusGained", on_focus_gain)
spellname:SetHook("OnEditFocusLost", on_focus_lost)
spellname:SetHook("OnEnter", on_enter_spell)
spellname:SetHook("OnLeave", on_leave_spell)
--spellicon_button:SetPoint("topleft", BossFrame, "topleft", 255, -65 +(i * 21 * -1))
spellicon:SetPoint("topleft", edFrame, "topleft", 443, -85 +(i * 21 * -1))
spellid:SetPoint("left", spellicon, "right", 4, 0)
spellname:SetPoint("left", spellid, "right", 4, 0)
local spellinfo = CreateFrame("frame", nil, anchor_frame,"BackdropTemplate")
spellinfo:SetBackdrop({edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true})
spellinfo:SetBackdropColor(.3, .3, .3, .5)
spellinfo:SetBackdropBorderColor(0, 0, 0, 1)
spellinfo:SetSize(80, 20)
spellinfo:SetScript("OnEnter", info_onenter)
spellinfo:SetScript("OnLeave", info_onleave)
local spellinfotext = spellinfo:CreateFontString(nil, "overlay", "GameFontNormal")
spellinfotext:SetPoint("center", spellinfo, "center")
spellinfotext:SetText("info")
spellinfo:SetPoint("left", spellname.widget, "right", 4, 0)
local create_aura = detailsFramework:NewButton(anchor_frame, nil, "$parentCreateAuraButton", "AuraButton", 90, 18, create_aura_func, nil, nil, nil, "Make Aura")
create_aura:SetPoint("left", spellinfo, "right", 4, 0)
create_aura:SetTemplate(aurasButtonTemplate)
anchor_frame.icon = spellicon
anchor_frame.spellid = spellid
anchor_frame.spellname = spellname
anchor_frame.aurabutton = create_aura
anchor_frame.aurabutton._icon = spellicon
anchor_frame.info = spellinfo
table.insert(spell_blocks, anchor_frame)
table.insert(edFrame.EnemySpellsWidgets, anchor_frame)
anchor_frame:Hide()
end
local update_enemy_spells = function()
local combat = encounterDetails:GetCombat(encounterDetails._segment)
local spell_list = {}
if (combat) then
for i, npc in combat[1]:ListActors() do
--damage
if (npc:IsNeutralOrEnemy()) then
for spellid, spell in pairs(npc.spells._ActorTable) do
if (spellid > 10) then
local name, _, icon = encounterDetails.getspellinfo(spellid)
table.insert(spell_list, {spellid, name, icon, nil, npc.serial})
end
end
end
end
for i, npc in combat[2]:ListActors() do
--heal
if (npc:IsNeutralOrEnemy()) then
for spellid, spell in pairs(npc.spells._ActorTable) do
if (spellid > 10) then
local name, _, icon = encounterDetails.getspellinfo(spellid)
table.insert(spell_list, {spellid, name, icon, true, npc.serial})
end
end
end
end
table.sort(spell_list, function(t1, t2)
return t1[2] < t2[2]
end)
encounterDetails.SpellScrollframe.spell_pool = spell_list
encounterDetails.SpellScrollframe.encounter_id = combat.is_boss and combat.is_boss.id
encounterDetails.SpellScrollframe:Update()
end
end
local refresh_bossmods_timers = function(self)
local combat = encounterDetails:GetCombat(encounterDetails._segment)
local offset = FauxScrollFrame_GetOffset(self)
local already_added = {}
local db = Details.boss_mods_timers
local encounter_id = combat.is_boss and combat.is_boss.id
if (db) then
wipe(already_added)
local timersToAdd = {}
for timerId, timerTable in pairs(db.encounter_timers_dbm) do
if (timerTable.id == encounter_id) then
local spellId = timerTable [7]
local spellIcon = timerTable [5]
local spellName
local spell = timerId
spell = spell:gsub("ej", "")
spell = tonumber(spell)
if (spell and not already_added[spell]) then
if (spell > 40000) then
local spellname, _, spellicon = _GetSpellInfo(spell)
table.insert(timersToAdd, {label = spellname, value = {timerTable[2], spellname, spellIcon or spellicon, timerTable.id, timerTable[7]}, icon = spellIcon or spellicon})
else
--local title, description, depth, abilityIcon, displayInfo, siblingID, nextSectionID, filteredByDifficulty, link, startsOpen, flag1, flag2, flag3, flag4 = C_EncounterJournal.GetSectionInfo(spell)
local sectionInfo = C_EncounterJournal.GetSectionInfo(spell)
table.insert(timersToAdd, {label = sectionInfo.title, value = {timerTable[2], sectionInfo.title, spellIcon or sectionInfo.abilityIcon, timerTable.id, timerTable[7]}, icon = spellIcon or sectionInfo.abilityIcon})
end
already_added[spell] = true
end
end
end
table.sort(timersToAdd, function(t1, t2)
return t1.label < t2.label
end)
local offset = FauxScrollFrame_GetOffset(self)
for barIndex = 1, CONST_MAX_AURA_LINES do
local data = timersToAdd[barIndex + offset]
local bar = bossmods_blocks[barIndex]
if (data) then
bar:Show()
bar.icon.texture = data.icon
bar.icon:SetTexCoord(.1, .9, .1, .9)
bar.spellid.text = data.value[1] or "--x--x--"
bar.spellname.text = data.label or "--x--x--"
bar.spellid._spellid = data.value[1]
bar.spellname._spellid = data.value[1]
local func = function()
local timerId, spellname, spellicon, encounterid, spellid = unpack(data.value)
encounterDetails:OpenAuraPanel(timerId, spellname, spellicon, encounterid, DETAILS_WA_TRIGGER_DBM_TIMER, DETAILS_WA_AURATYPE_TEXT, {dbm_timer_id = timerId, spellid = spellid, text = "Next " .. spellname .. " In", text_size = 72, icon = spellicon})
end
bar.aurabutton:SetClickFunction(func)
else
bar:Hide()
end
end
FauxScrollFrame_Update(self, #timersToAdd, CONST_MAX_AURA_LINES, 20)
if (#timersToAdd > 0) then
self:Show()
end
end
end
local refresh_spellauras = function(self)
local pool = encounterDetails.SpellScrollframe.spell_pool
local encounter_id = encounterDetails.SpellScrollframe.encounter_id
local offset = FauxScrollFrame_GetOffset(self)
for bar_index = 1, CONST_MAX_AURA_LINES do
local data = pool[bar_index + offset]
local bar = spell_blocks[bar_index]
if (data) then
bar:Show()
bar.icon.texture = data[3]
bar.icon:SetTexCoord(.1, .9, .1, .9)
bar.spellid.text = data[1]
bar.spellname.text = data[2]
bar.spellid._spellid = data[1]
bar.spellname._spellid = data[1]
bar.info._spellid = data[1]
local is_heal = data[4]
if (is_heal) then
bar.spellid:SetBackdropBorderColor(0, 1, 0)
bar.spellname:SetBackdropBorderColor(0, 1, 0)
else
bar.spellid:SetBackdropBorderColor(0, 0, 0)
bar.spellname:SetBackdropBorderColor(0, 0, 0)
end
bar.aurabutton:SetClickFunction(create_aura_func, data [1], encounter_id)
else
bar:Hide()
end
end
FauxScrollFrame_Update(self, #pool, CONST_MAX_AURA_LINES, 20)
end
local spellScrollFrame = CreateFrame("ScrollFrame", "EncounterDetails_SpellAurasScroll", edFrame, "FauxScrollFrameTemplate, BackdropTemplate")
spellScrollFrame:SetScript("OnVerticalScroll", function(self, offset) FauxScrollFrame_OnVerticalScroll(self, offset, 14, refresh_spellauras) end)
spellScrollFrame:SetPoint("topleft", edFrame, "topleft", 200, -75)
spellScrollFrame:SetPoint("bottomright", edFrame, "bottomright", -33, 42)
spellScrollFrame.Update = refresh_spellauras
spellScrollFrame:Hide()
encounterDetails.SpellScrollframe = spellScrollFrame
detailsFramework:ReskinSlider(spellScrollFrame)
table.insert(edFrame.EnemySpellsWidgets, spellScrollFrame)
encounterDetails.update_enemy_spells = update_enemy_spells
local bossmodsScrollFrame = CreateFrame("ScrollFrame", "EncounterDetails_BossModsScroll", edFrame, "FauxScrollFrameTemplate, BackdropTemplate")
bossmodsScrollFrame:SetScript("OnVerticalScroll", function(self, offset) FauxScrollFrame_OnVerticalScroll(self, offset, 14, refresh_bossmods_timers) end)
bossmodsScrollFrame:SetPoint("topleft", edFrame, "topleft", 10, -75)
bossmodsScrollFrame:SetPoint("bottomleft", edFrame, "bottomleft", 250, 42)
bossmodsScrollFrame.Update = refresh_bossmods_timers
bossmodsScrollFrame:Hide()
encounterDetails.BossModsScrollframe = bossmodsScrollFrame
table.insert(edFrame.EnemySpellsWidgets, bossmodsScrollFrame)
encounterDetails.update_bossmods = function() bossmodsScrollFrame:Update() end
local build_bigwigs_bars = function()
local t = {}
local db = Details.boss_mods_timers
if (db) then
wipe(already_added)
local encounter_id = encounterDetails.SpellScrollframe.encounter_id
for timer_id, timer_table in pairs(db.encounter_timers_bw) do
if (timer_table.id == encounter_id) then
local spell = timer_id
if (spell and not already_added [spell]) then
local int_spell = tonumber(spell)
if (not int_spell) then
local spellname = timer_table [2]:gsub(" %(.%)", "")
table.insert(t, {label = spellname, value = {timer_table [2], spellname, timer_table [5], timer_table.id}, icon = timer_table [5], onclick = on_select_bw_bar})
elseif (int_spell < 0) then
local title, description, depth, abilityIcon, displayInfo, siblingID, nextSectionID, filteredByDifficulty, link, startsOpen, flag1, flag2, flag3, flag4 = C_EncounterJournal.GetSectionInfo(abs(int_spell))
table.insert(t, {label = title, value = {timer_table [2], title, timer_table [5] or abilityIcon, timer_table.id}, icon = timer_table [5] or abilityIcon, onclick = on_select_bw_bar})
else
local spellname, _, spellicon = _GetSpellInfo(int_spell)
table.insert(t, {label = spellname, value = {timer_table [2], spellname, timer_table [5] or spellicon, timer_table.id}, icon = timer_table [5] or spellicon, onclick = on_select_bw_bar})
end
already_added [spell] = true
end
end
end
end
return t
end
@@ -0,0 +1,698 @@
local addonId, edTable = ...
local Details = _G._detalhes
local AceLocale = LibStub("AceLocale-3.0")
local Loc = AceLocale:GetLocale("Details_EncounterDetails")
local Graphics = LibStub:GetLibrary("LibGraph-2.0")
local ipairs = ipairs
local _GetSpellInfo = Details.getspellinfo
local unpack = unpack
local detailsFramework = DetailsFramework
local CreateFrame = CreateFrame
local GameCooltip = GameCooltip
local wipe = table.wipe
local _
--VerticalLines são os indicatores de onde aconteceram mortes, precisa ser renomeados e criar uma classe pra eles
--precisa fazer um indicator genérico na classe df_chart para ser usado como indicador de bloodlust ou qualquer coisa que indica um evento por x tempo
---@class ed_phaseframe : frame
---@field texture texture
local encounterDetails = _G.EncounterDetailsGlobal
local edFrame = encounterDetails.Frame
--an auxiliary table to store things related to df_chartmulti but can't be stored in 'chartPanel'
encounterDetails.chartFrameAux = {}
local CONST_CHART_WIDTH = 921
local CONST_CHART_HEIGHT = 524
local CONST_CHART_LENGTH = 810
local CONST_CHART_TIMELINE_Y_POSITION = -540
local CONST_CHART_MAX_DEATHS_ICONS = 6
local CONST_PHASE_PANEL_WIDTH = 451
local CONST_PHASE_BAR_HEIGHT = 16
local DETAILS_ATTRIBUTE_DAMAGE = 1
local phaseAlpha = 0.5
local lastBoss = nil
local chartLineColors = {{1, 1, 1, 1}, {1, 0.5, 0.3, 1}, {0.75, 0.7, 0.1, 1}, {0.2, 0.9, 0.2, 1}, {0.2, 0.5, 0.9, 1}}
encounterDetails.CombatsAlreadyDrew = {}
---create a multi chart frame
---@return df_chartmulti
local createMultiChartFrame = function()
---@type df_chartmulti
local chartPanel = detailsFramework:CreateGraphicMultiLineFrame(edFrame, "EncounterDetailsChartPanel")
chartPanel.xAxisLabelsYOffset = -9
chartPanel:CreateAxesLines(48, 28, "left", 1, 10, 10, 1, 1, 1, 1)
chartPanel:SetXAxisDataType("time")
chartPanel:SetSize(CONST_CHART_WIDTH, CONST_CHART_HEIGHT)
chartPanel:SetPoint("topleft", encounterDetails.Frame, "topleft", 2, -76)
chartPanel:SetLineThickness(3)
encounterDetails.chartPanel = chartPanel
detailsFramework:ApplyStandardBackdrop(chartPanel)
---@type ed_phaseframe[]
encounterDetails.chartFrameAux.PhaseFrames = {}
encounterDetails.chartFrameAux.VerticalLines = {}
local phaseTooltip = encounterDetails:CreatePhaseTooltip(chartPanel)
encounterDetails:CreatePhaseIndicators(chartPanel, phaseTooltip)
detailsFramework:NewLabel(chartPanel, chartPanel, nil, "phases_string", "phases:", "GameFontHighlightSmall")
chartPanel["phases_string"]:SetPoint("bottomleft", chartPanel, "bottomleft", 5, 10)
chartPanel:SetScript("OnShow", function()
chartPanel["phases_string"]:Show()
end)
chartPanel:SetScript("OnHide", function()
chartPanel["phases_string"]:Hide()
end)
return chartPanel
end
function encounterDetails:ShowChartFrame()
local segment = encounterDetails._segment
if (not segment) then
return
end
---@type df_chartmulti
local multiChartPanel = encounterDetails.chartPanel
if (not multiChartPanel) then
---@type df_chartmulti
multiChartPanel = createMultiChartFrame()
end
multiChartPanel:Reset()
---@type combat
local combatObject = encounterDetails:GetCombat(segment)
--elapsed combat time
if (combatObject:GetCombatTime() < 12) then
return
end
local uniqueCombatId = combatObject:GetCombatUID()
local chartData = EncounterDetailsDB.chartData[uniqueCombatId]
local currentChartData = chartData and chartData["Raid Damage Done"]
if (not currentChartData or not combatObject.start_time or not combatObject.end_time) then
encounterDetails:Msg("This segment doesn't have chart data.")
return
elseif (currentChartData.max_value and currentChartData.max_value == 0) then
return
end
encounterDetails.Frame.linhas = 1 --can't find references to this variable
if (encounterDetails.Frame.linhas > 5) then
encounterDetails.Frame.linhas = 1
end
for _, line in ipairs(encounterDetails.chartFrameAux.VerticalLines) do
line:Hide()
end
local encounterId = combatObject.is_boss and combatObject.is_boss.id
local chartIndex = 2
local smoothnessLevel = 3
---draw the damage line from the 5th combat to 2nd combat
for i = segment + 4, segment + 1, -1 do
---@type combat
local thisCombatObject = encounterDetails:GetCombat(i)
if (thisCombatObject) then
local elapsedTime = thisCombatObject:GetCombatTime()
if (elapsedTime > 12 and thisCombatObject.is_boss and thisCombatObject.is_boss.id == encounterId) then --is the same boss
local thisUniqueCombatId = thisCombatObject:GetCombatUID()
local thisChartData = EncounterDetailsDB.chartData[thisUniqueCombatId] and EncounterDetailsDB.chartData[thisUniqueCombatId]["Raid Damage Done"]
--check if this is a valid chart data
if (thisChartData and thisChartData.max_value and thisChartData.max_value > 0) then
local tryNumber = thisCombatObject.is_boss.try_number or i
multiChartPanel:AddData(thisChartData, smoothnessLevel, "Try #" .. tryNumber, chartLineColors[chartIndex])
multiChartPanel:SetXAxisData(elapsedTime)
chartIndex = chartIndex + 1
end
end
end
end
---@type number[]
local bloodLustTimers = combatObject.bloodlust or {}
for index, bloodlustCombatTime in ipairs(bloodLustTimers) do
multiChartPanel:AddBackdropIndicator("Bloodlust #" .. index, bloodlustCombatTime, bloodlustCombatTime + 40, {0, 0, 1, 0.2})
end
encounterDetails:UpdatePhaseIndicators(multiChartPanel, combatObject)
multiChartPanel:AddData(currentChartData, smoothnessLevel, "current", chartLineColors[1])
multiChartPanel:SetXAxisData(combatObject:GetCombatTime())
multiChartPanel:Plot()
multiChartPanel:Show()
end
function encounterDetails:UpdatePhaseIndicators(chartPanel, combatObject)
encounterDetails:ClearPhaseIndicators()
--update phase indicators
local phaseData = combatObject.PhaseData
local plotFrameWidth = chartPanel.plotFrame:GetWidth()
local scale = (plotFrameWidth) / combatObject:GetCombatTime()
for i = 1, #phaseData do
local phase = phaseData[i][1]
local phaseStartAt = phaseData[i][2]
local phaseIndicator = encounterDetails:GetPhaseIndicator(i, phase)
if (phaseStartAt == 1) then
phaseStartAt = 0
end
phaseIndicator:SetPoint("topleft", chartPanel.plotFrame, "bottomleft", (phaseStartAt * scale), -6)
phaseIndicator.phase = phase
phaseIndicator.start_at = phaseStartAt
local nextPhase = phaseData[i+1]
if (nextPhase) then
local duration = nextPhase[2] - phaseStartAt
phaseIndicator:SetWidth(scale * duration)
phaseIndicator.elapsed = duration
else
local duration = combatObject:GetCombatTime() - phaseStartAt
phaseIndicator:SetWidth(scale * duration)
phaseIndicator.elapsed = duration
end
end
end
---tooltip frame on hovering over
---@param chartPanel df_chartmulti
---@return frame
function encounterDetails:CreatePhaseTooltip(chartPanel)
---@type frame
local phaseTooltip = CreateFrame("frame", "EncounterDetailsPhasePanel", chartPanel, "BackdropTemplate")
phaseTooltip:SetFrameStrata("TOOLTIP")
phaseTooltip:SetFrameLevel(1000)
phaseTooltip:SetWidth(450)
detailsFramework:ApplyStandardBackdrop(phaseTooltip)
local damageTexture = detailsFramework:CreateImage(phaseTooltip,[[Interface\AddOns\Details\images\skins\classic_skin_v1]], 16, 16, "overlay", {11/1024, 24/1024, 376/1024, 390/1024})
local damageLabel = detailsFramework:CreateLabel(phaseTooltip, "Damage Done:")
damageTexture:SetPoint("topleft", phaseTooltip, "topleft", 10, -10)
damageLabel:SetPoint("left", damageTexture, "right", 4, 0)
local healingTexture = detailsFramework:CreateImage(phaseTooltip,[[Interface\AddOns\Details\images\skins\classic_skin_v1]], 16, 16, "overlay", {43/1024, 57/1024, 376/1024, 390/1024})
local healingLabel = detailsFramework:CreateLabel(phaseTooltip, "Healing Done:")
healingTexture:SetPoint("topleft", phaseTooltip, "topleft", 250, -10)
healingLabel:SetPoint("left", healingTexture, "right", 4, 0)
phaseTooltip.phase_label = detailsFramework:CreateLabel(phaseTooltip, "")
phaseTooltip.phase_label.fontsize = 10
phaseTooltip.time_label = detailsFramework:CreateLabel(phaseTooltip, "")
phaseTooltip.time_label.fontsize = 10
phaseTooltip.report_label = detailsFramework:CreateLabel(phaseTooltip, "|cFFffb400Left Click|r: Report Damage |cFFffb400Right Click|r: Report Heal")
phaseTooltip.report_label.fontsize = 10
phaseTooltip.phase_label:SetPoint("bottomleft", phaseTooltip, "bottomleft", 10, 5)
phaseTooltip.time_label:SetPoint("left", phaseTooltip.phase_label, "right", 5, 0)
phaseTooltip.report_label:SetPoint("bottomright", phaseTooltip, "bottomright", -10, 5)
local phaseFrameBackgroundTexture = detailsFramework:CreateImage(phaseTooltip,[[Interface\Tooltips\UI-Tooltip-Background]], nil, nil, "artwork")
phaseFrameBackgroundTexture:SetPoint("left", phaseTooltip.phase_label, "left")
phaseFrameBackgroundTexture.height = 16
phaseFrameBackgroundTexture:SetPoint("right", phaseTooltip.report_label, "right")
phaseFrameBackgroundTexture:SetVertexColor(0, 0, 0, 1)
phaseTooltip.damage_labels = {}
phaseTooltip.heal_labels = {}
function phaseTooltip:ClearLabels()
for i, tooltipBar in ipairs(phaseTooltip.damage_labels) do
tooltipBar:Hide()
end
for i, tooltipBar in ipairs(phaseTooltip.heal_labels) do
tooltipBar:Hide()
end
end
local createTooltipBar = function(index, xOffset)
---@type statusbar
local newtooltipBar = CreateFrame("statusbar", nil, phaseTooltip, "BackdropTemplate")
newtooltipBar:SetSize(200, 16)
newtooltipBar:SetFrameLevel(phaseTooltip:GetFrameLevel() + 50 - index)
newtooltipBar:SetMinMaxValues(0, 1)
newtooltipBar:SetPoint("topleft", phaseTooltip, "topleft", 5 + xOffset, ((index * 16) * -1) - 30)
local playerName = detailsFramework:CreateLabel(newtooltipBar, "", 11, "white", nil, nil, nil, "overlay")
local amountLabel = detailsFramework:CreateLabel(newtooltipBar, "", 11, nil, nil, nil, nil, "overlay")
amountLabel:SetJustifyH("right")
local iconTexture = detailsFramework:CreateImage(newtooltipBar, "", 16, 16, "overlay")
local backgroundTexture = detailsFramework:CreateImage(newtooltipBar, [[Interface\AddOns\Details\images\bar_serenity]], nil, nil, "border")
backgroundTexture.height = 16
backgroundTexture:SetVertexColor(.1, .1, .1, 0.834)
local statusBarTexture = newtooltipBar:CreateTexture(nil, "artwork")
statusBarTexture:SetTexture([[Interface\AddOns\Details\images\bar_serenity]])
statusBarTexture:SetVertexColor(.3, .3, .3, 1)
statusBarTexture:SetAllPoints()
newtooltipBar:SetStatusBarTexture(statusBarTexture)
backgroundTexture:SetAllPoints()
iconTexture:SetPoint("left", newtooltipBar, "left", 0, 0)
playerName:SetPoint("left", iconTexture, "right", 2, 0)
amountLabel:SetPoint("right", newtooltipBar, "right", -2, 0)
newtooltipBar.lefttext = playerName
newtooltipBar.righttext = amountLabel
newtooltipBar.icon = iconTexture
newtooltipBar.bg = backgroundTexture
newtooltipBar.statusBarTexture = statusBarTexture
return newtooltipBar
end
function phaseTooltip:GetTooltipBar(index, barType)
local thisBar
if (barType == "damage") then
thisBar = phaseTooltip.damage_labels[index]
if (not thisBar) then
thisBar = createTooltipBar(index, 0)
phaseTooltip.damage_labels[index] = thisBar
end
elseif (barType == "healing") then
thisBar = phaseTooltip.heal_labels[index]
if (not thisBar) then
thisBar = createTooltipBar(index, 235)
phaseTooltip.heal_labels[index] = thisBar
end
end
thisBar:Show()
return thisBar
end
return phaseTooltip
end
---phase indicators below the x axis
---@param chartPanel df_chartmulti
function encounterDetails:CreatePhaseIndicators(chartPanel, phaseTooltip)
local sparkContainer = {}
local phaseColors = {{0.2, 1, 0.2, phaseAlpha}, {1, 1, 0.2, phaseAlpha}, {1, 0.2, 0.2, phaseAlpha}, {0.2, 1, 1, phaseAlpha}, {0.2, 0.2, 1, phaseAlpha},
[1.5] = {0.25, 0.95, 0.25, phaseAlpha},[2.5] = {0.95, 0.95, 0.25, phaseAlpha},[3.5] = {0.95, 0.25, 0.25, phaseAlpha}
}
local createSpark = function()
local newSpark = phaseTooltip:CreateTexture(nil, "overlay")
newSpark:SetTexture([[Interface\CastingBar\UI-CastingBar-Spark]])
newSpark:SetBlendMode("ADD")
newSpark:Hide()
table.insert(sparkContainer, newSpark)
end
local getSpark = function(index)
local spark = sparkContainer[index]
if (not spark) then
createSpark()
spark = sparkContainer[index]
end
spark:ClearAllPoints()
return spark
end
local hideSparks = function()
for _, spark in ipairs(sparkContainer) do
spark:Hide()
end
end
local onClickPhase = function(self, button)
---@type combat
local combatObject = encounterDetails:GetCombat(encounterDetails._segment)
if (button == "LeftButton") then
local result = {}
local reportFunc = function(IsCurrent, IsReverse, AmtLines)
AmtLines = AmtLines + 1
if (#result > AmtLines) then
for i = #result, AmtLines+1, -1 do
table.remove(result, i)
end
end
encounterDetails:SendReportLines(result)
end
--need to build here because the mouse will leave the block to click in the send button
table.insert(result, "Details!: Damage for Phase " .. self.phase .. " of " .. (combatObject and combatObject.is_boss and combatObject.is_boss.name or "Unknown") .. ":")
for i = 1, #self.damage_actors do
table.insert(result, encounterDetails:GetOnlyName(self.damage_actors[i][1]) .. ": " .. Details:ToK(math.floor(self.damage_actors[i][2])))
end
encounterDetails:SendReportWindow(reportFunc, nil, nil, true)
elseif (button == "RightButton") then
local result = {}
local reportFunc = function(IsCurrent, IsReverse, AmtLines)
AmtLines = AmtLines + 1
if (#result > AmtLines) then
for i = #result, AmtLines+1, -1 do
table.remove(result, i)
end
end
encounterDetails:SendReportLines(result)
end
table.insert(result, "Details!: Healing for Phase " .. self.phase .. " of " ..(combatObject and combatObject.is_boss and combatObject.is_boss.name or "Unknown") .. ":")
for i = 1, #self.heal_actors do
table.insert(result, encounterDetails:GetOnlyName(self.heal_actors[i][1]) .. ": " .. Details:ToK(math.floor(self.heal_actors[i][2])))
end
encounterDetails:SendReportWindow(reportFunc, nil, nil, true)
end
end
local onEnterPhase = function(self)
local leftSpark = getSpark(1)
local rightSpark = getSpark(2)
leftSpark:SetPoint("left", self.texture, "left", -16, 0)
rightSpark:SetPoint("right", self.texture, "right", 16, 0)
leftSpark:Show()
rightSpark:Show()
self.texture:SetBlendMode("ADD")
local phase = self.phase
local sparkIndex = 3
self.texture:SetVertexColor(1, 1, 1)
for _, thisPhaseFrame in ipairs(encounterDetails.chartFrameAux.PhaseFrames) do
if (thisPhaseFrame ~= self and thisPhaseFrame.phase == phase) then
local thisPhaseLeftSpark = getSpark(sparkIndex)
local thisPhaseRightSpark = getSpark(sparkIndex+1)
thisPhaseLeftSpark:SetPoint("left", thisPhaseFrame.texture, "left", -16, 0)
thisPhaseRightSpark:SetPoint("right", thisPhaseFrame.texture, "right", 16, 0)
thisPhaseLeftSpark:Show()
thisPhaseRightSpark:Show()
thisPhaseFrame.texture:SetBlendMode("ADD")
thisPhaseFrame.texture:SetVertexColor(1, 1, 1)
sparkIndex = sparkIndex + 2
end
end
---@type combat
local combatObject = encounterDetails:GetCombat(encounterDetails._segment)
if (combatObject) then
phaseTooltip:ClearLabels()
--damage
---@type table<number, table<string, number>>
local listOfPlayers = {}
for playerName, damageDone in pairs(combatObject.PhaseData.damage[self.phase]) do
table.insert(listOfPlayers, {playerName, damageDone})
end
table.sort(listOfPlayers, Details.Sort2)
local topDamage = listOfPlayers[1] and listOfPlayers[1][2]
for index, playerTable in ipairs(listOfPlayers) do
local playerName = playerTable[1]
local damageDone = playerTable[2]
local tooltipBar = phaseTooltip:GetTooltipBar(index, "damage")
tooltipBar:SetValue(damageDone / topDamage)
tooltipBar.lefttext.text = encounterDetails:GetOnlyName(playerName)
tooltipBar.righttext.text = Details:ToK(math.floor(damageDone))
---@type actor
local actor = combatObject:GetActor(DETAILS_ATTRIBUTE_DAMAGE, playerName)
local class = encounterDetails:GetClass(playerName)
local spec = encounterDetails:GetSpec(playerName) or actor and actor.spec
--get the class color for the actor
local red, green, blue = Details:GetClassColor(class)
tooltipBar:SetStatusBarColor(red, green, blue)
if (spec) then
tooltipBar.icon.texture = [[Interface\AddOns\Details\images\spec_icons_normal]]
tooltipBar.icon.texcoord = encounterDetails.class_specs_coords[spec]
elseif (class) then
tooltipBar.icon.texture = [[Interface\AddOns\Details\images\classes_small_alpha]]
tooltipBar.icon.texcoord = Details.class_coords[class]
else
tooltipBar.icon.texture = [[Interface\LFGFRAME\LFGROLE_BW]]
tooltipBar.icon:SetTexCoord(.25, .5, 0, 1)
end
tooltipBar:Show()
end
local damage_players = #listOfPlayers
self.damage_actors = listOfPlayers
--healing
---@type table<number, table<string, number>>
local listOfPlayersHeal = {}
for playerName, heal in pairs(combatObject.PhaseData.heal[self.phase]) do
table.insert(listOfPlayersHeal, {playerName, heal})
end
table.sort(listOfPlayersHeal, Details.Sort2)
local topHealing = listOfPlayersHeal[1] and listOfPlayersHeal[1][2]
for index, playerTable in ipairs(listOfPlayersHeal) do
local playerName = playerTable[1]
local healingDone = playerTable[2]
local tooltipBar = phaseTooltip:GetTooltipBar(index, "healing")
tooltipBar:SetValue(healingDone / topHealing)
tooltipBar.lefttext.text = encounterDetails:GetOnlyName(playerName)
tooltipBar.righttext.text = Details:ToK(math.floor(healingDone))
---@type actor
local actor = combatObject:GetActor(DETAILS_ATTRIBUTE_DAMAGE, playerName)
local class = encounterDetails:GetClass(playerName)
local spec = encounterDetails:GetSpec(playerName) or actor and actor.spec
--get the class color for the actor
local red, green, blue = Details:GetClassColor(class)
tooltipBar:SetStatusBarColor(red, green, blue)
if (spec) then
tooltipBar.icon.texture = [[Interface\AddOns\Details\images\spec_icons_normal]]
tooltipBar.icon.texcoord = encounterDetails.class_specs_coords[spec]
elseif (class) then
tooltipBar.icon:SetTexture([[Interface\AddOns\Details\images\classes_small_alpha]])
tooltipBar.icon:SetTexCoord(unpack(Details.class_coords[class]))
else
tooltipBar.icon:SetTexture([[Interface\LFGFRAME\LFGROLE_BW]])
tooltipBar.icon:SetTexCoord(.25, .5, 0, 1)
end
tooltipBar:Show()
end
local heal_players = #listOfPlayersHeal
self.heal_actors = listOfPlayersHeal
--show the panel
phaseTooltip:SetHeight((math.max(damage_players, heal_players) * 16) + 60)
phaseTooltip:SetPoint("bottom", self, "top", 0, 10)
phaseTooltip:Show()
phaseTooltip.phase_label.text = "|cFFffb400Phase|r: " .. self.phase
local m, s = math.floor(self.elapsed / 60), math.floor(self.elapsed % 60)
phaseTooltip.time_label.text = "|cFFffb400Elapsed|r: " .. m .. "m " .. s .. "s"
end
end
local onLeavePhase = function(self)
wipe(self.damage_actors)
wipe(self.heal_actors)
for _, phaseTextureFrame in ipairs(encounterDetails.chartFrameAux.PhaseFrames) do
phaseTextureFrame.texture:SetBlendMode("BLEND")
phaseTextureFrame.texture:SetVertexColor(unpack(phaseTextureFrame.texture.original_color))
end
hideSparks()
phaseTooltip:Hide()
end
function encounterDetails:GetPhaseIndicator(index, phase)
local phaseIndicatorFrame = encounterDetails.chartFrameAux.PhaseFrames[index]
if (not phaseIndicatorFrame) then
---@type ed_phaseframe
phaseIndicatorFrame = CreateFrame("frame", "EncounterDetailsPhaseTexture" .. index, chartPanel, "BackdropTemplate")
phaseIndicatorFrame:SetHeight(CONST_PHASE_BAR_HEIGHT)
local phaseTexture = phaseIndicatorFrame:CreateTexture(nil, "artwork")
phaseTexture:SetAllPoints()
phaseTexture:SetColorTexture(1, 1, 1, phaseAlpha)
phaseTexture.original_color = {1, 1, 1}
phaseIndicatorFrame.texture = phaseTexture
phaseIndicatorFrame:SetScript("OnEnter", onEnterPhase)
phaseIndicatorFrame:SetScript("OnLeave", onLeavePhase)
phaseIndicatorFrame:SetScript("OnMouseUp", onClickPhase)
phaseIndicatorFrame = phaseIndicatorFrame
table.insert(encounterDetails.chartFrameAux.PhaseFrames, phaseIndicatorFrame)
end
phaseIndicatorFrame:ClearAllPoints()
phase = math.min(phase, 5)
if (not phaseColors[phase]) then
Details:Msg("Phase out of range:", phase)
phase = math.max(phase, 1)
end
local phaseColor = phaseColors[phase]
if (not phaseColor) then
phaseColor = {1, 1, 1}
end
phaseIndicatorFrame.texture:SetVertexColor(unpack(phaseColor))
local originalColor = phaseIndicatorFrame.texture.original_color
originalColor[1], originalColor[2], originalColor[3] = unpack(phaseColors[phase])
phaseIndicatorFrame:Show()
return phaseIndicatorFrame
end
function encounterDetails:ClearPhaseIndicators()
for i, texture in pairs(encounterDetails.chartFrameAux.PhaseFrames) do
texture:Hide()
end
end
end
---not in use at the moment
---@param self any
---@param chartPanel df_chart
---@param detailsGraphicData any
---@param combatObject combat
---@param drawDeathsCombat combat
function encounterDetails:DrawSegmentGraphic(chartPanel, detailsGraphicData, combatObject, drawDeathsCombat)
--> add death icons for the first deaths in the segment
if (drawDeathsCombat) then
local mortes = drawDeathsCombat.last_events_tables
local scaleG = CONST_CHART_LENGTH / drawDeathsCombat:GetCombatTime()
for _, row in ipairs(encounterDetails.chartFrameAux.VerticalLines) do
row:Hide()
end
for i = 1, math.min(CONST_CHART_MAX_DEATHS_ICONS, #mortes) do
local vRowFrame = encounterDetails.chartFrameAux.VerticalLines[i]
if (not vRowFrame) then
vRowFrame = CreateFrame("frame", "DetailsEncountersVerticalLine"..i, chartPanel, "BackdropTemplate")
vRowFrame:SetWidth(20)
vRowFrame:SetHeight(43)
vRowFrame:SetFrameLevel(chartPanel:GetFrameLevel()+2)
vRowFrame:SetScript("OnEnter", function(frame)
if (vRowFrame.dead[1] and vRowFrame.dead[1][3] and vRowFrame.dead[1][3][2]) then
GameCooltip:Reset()
--time of death and player name
GameCooltip:AddLine(vRowFrame.dead[6].." "..vRowFrame.dead[3])
local class, l, r, t, b = Details:GetClass(vRowFrame.dead[3])
if (class) then
GameCooltip:AddIcon([[Interface\AddOns\Details\images\classes_small]], 1, 1, 12, 12, l, r, t, b)
end
GameCooltip:AddLine("")
--last hits:
local death = vRowFrame.dead
local amt = 0
for i = #death[1], 1, -1 do
local this_hit = death[1][i]
if (type(this_hit[1]) == "boolean" and this_hit[1]) then
local spellname, _, spellicon = _GetSpellInfo(this_hit[2])
local t = death[2] - this_hit[4]
GameCooltip:AddLine("-" .. string.format("%.1f", t) .. " " .. spellname .. "(" .. this_hit[6] .. ")", encounterDetails:comma_value(this_hit[3]))
GameCooltip:AddIcon(spellicon, 1, 1, 12, 12, 0.1, 0.9, 0.1, 0.9)
amt = amt + 1
if (amt == 3) then
break
end
end
end
GameCooltip:SetOption("TextSize", 9.5)
GameCooltip:SetOption("HeightAnchorMod", -15)
GameCooltip:SetWallpaper(1,[[Interface\SPELLBOOK\Spellbook-Page-1]], {.6, 0.1, 0, 0.64453125}, {1, 1, 1, 0.1}, true)
GameCooltip:ShowCooltip(frame, "tooltip")
end
end)
vRowFrame:SetScript("OnLeave", function(frame)
Details.popup:ShowMe(false)
end)
vRowFrame.texture = vRowFrame:CreateTexture(nil, "overlay")
vRowFrame.texture:SetTexture("Interface\\AddOns\\Details\\images\\verticalline")
vRowFrame.texture:SetWidth(3)
vRowFrame.texture:SetHeight(20)
vRowFrame.texture:SetPoint("center", "DetailsEncountersVerticalLine"..i, "center")
vRowFrame.texture:SetPoint("bottom", "DetailsEncountersVerticalLine"..i, "bottom", 0, 0)
vRowFrame.texture:SetVertexColor(1, 1, 1, .5)
vRowFrame.icon = vRowFrame:CreateTexture(nil, "overlay")
vRowFrame.icon:SetTexture("Interface\\WorldStateFrame\\SkullBones")
vRowFrame.icon:SetTexCoord(0.046875, 0.453125, 0.046875, 0.46875)
vRowFrame.icon:SetWidth(16)
vRowFrame.icon:SetHeight(16)
vRowFrame.icon:SetPoint("center", "DetailsEncountersVerticalLine"..i, "center")
vRowFrame.icon:SetPoint("bottom", "DetailsEncountersVerticalLine"..i, "bottom", 0, 20)
encounterDetails.chartFrameAux.VerticalLines[i] = vRowFrame
end
local deadTime = mortes[i].dead_at
vRowFrame:SetPoint("topleft", encounterDetails.Frame, "topleft",(deadTime*scaleG)+70, -CONST_CHART_HEIGHT-22)
vRowFrame.dead = mortes[i]
vRowFrame:Show()
end
end
end
function encounterDetails:CreateGraphPanel() --not in use
--bloodlust indicators
chartPanel.bloodlustIndicators = {}
for i = 1, 5 do
local bloodlustTexture = chartPanel:CreateTexture(nil, "overlay")
bloodlustTexture:SetColorTexture(0, 1, 0, 0,6)
chartPanel.bloodlustIndicators[#chartPanel.bloodlustIndicators+1] = bloodlustTexture
end
end
@@ -0,0 +1,361 @@
local addonId, edTable = ...
local Details = _G._detalhes
local AceLocale = LibStub("AceLocale-3.0")
local Loc = AceLocale:GetLocale("Details_EncounterDetails")
local Graphics = LibStub:GetLibrary("LibGraph-2.0")
local ipairs = ipairs
local _GetSpellInfo = Details.getspellinfo
local unpack = unpack
local detailsFramework = DetailsFramework
local CreateFrame = CreateFrame
local GameCooltip = GameCooltip
local _
local encounterDetails = _G.EncounterDetailsGlobal
local edFrame = encounterDetails.Frame
local emote_segment_index = 1
local searching
local emoteLines = {}
local emoteSearchTable = {}
local CONST_EMOTES_MAX_LINES = 32
encounterDetails.emoteSegmentIndex = emote_segment_index
--emotes frame
local emoteFrame = CreateFrame("frame", "DetailsEncountersEmoteFrame", UIParent, "BackdropTemplate")
emoteFrame:RegisterEvent("CHAT_MSG_RAID_BOSS_EMOTE")
emoteFrame:RegisterEvent("CHAT_MSG_RAID_BOSS_WHISPER")
emoteFrame:RegisterEvent("CHAT_MSG_MONSTER_EMOTE")
emoteFrame:RegisterEvent("CHAT_MSG_MONSTER_SAY")
emoteFrame:RegisterEvent("CHAT_MSG_MONSTER_WHISPER")
emoteFrame:RegisterEvent("CHAT_MSG_MONSTER_PARTY")
emoteFrame:RegisterEvent("CHAT_MSG_MONSTER_YELL")
encounterDetails.EmoteFrame = emoteFrame
local emoteTable = {
["CHAT_MSG_RAID_BOSS_EMOTE"] = 1,
["CHAT_MSG_RAID_BOSS_WHISPER"] = 2,
["CHAT_MSG_MONSTER_EMOTE"] = 3,
["CHAT_MSG_MONSTER_SAY"] = 4,
["CHAT_MSG_MONSTER_WHISPER"] = 5,
["CHAT_MSG_MONSTER_PARTY"] = 6,
["CHAT_MSG_MONSTER_YELL"] = 7,
}
emoteFrame:SetScript("OnEvent", function(...)
if (not encounterDetails.current_whisper_table) then
return
end
local combat = encounterDetails:GetCombat("current")
--local arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 = ...
--print("2 =", arg2, "3 =", arg3, "4 =", arg4, "5 =", arg5, "6 =", arg6, "7 =", arg7, "8 =", arg8, "9 =", arg9)
if (combat and encounterDetails:IsInCombat() and encounterDetails:GetZoneType() == "raid") then
local arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 = ...
table.insert(encounterDetails.current_whisper_table, {combat:GetCombatTime(), arg3, arg4, emoteTable [arg2]})
end
end)
local refresh_emotes = function(self)
local offset = FauxScrollFrame_GetOffset(self)
local emotePool = encounterDetails.charsaved.emotes[emote_segment_index]
if (searching) then
local i = 0
local lower = string.lower
for index, data in ipairs(emotePool) do
if (lower(data[2]):find(lower(searching))) then
i = i + 1
emoteSearchTable[i] = data
end
for o = #emoteSearchTable, i + 1, -1 do
emoteSearchTable[o] = nil
end
emotePool = emoteSearchTable
end
edFrame.SearchResults:Show()
edFrame.SearchResults:SetText("Found " .. i .. " matches")
if (i > 0) then
edFrame.ReportEmoteButton:Enable()
elseif (i == 0) then
edFrame.ReportEmoteButton:Disable()
end
else
edFrame.SearchResults:Hide()
end
if (emotePool) then
for barIndex = 1, CONST_EMOTES_MAX_LINES do
local data = emotePool[barIndex + offset]
local bar = emoteLines[barIndex]
if (data) then
bar:Show()
local min, sec = math.floor(data[1] / 60), math.floor(data[1] % 60)
bar.leftText:SetText(min .. "m" .. sec .. "s:")
if (data[2] == "") then
bar.rightText:SetText("--x--x--")
else
bar.rightText:SetText(string.format(data[2], data[3]))
end
local colorString = encounterDetails.BossWhispColors[data[4]]
local colorTable = _G.ChatTypeInfo[colorString]
bar.rightText:SetTextColor(colorTable.r, colorTable.g, colorTable.b)
bar.icon:SetTexture([[Interface\CHARACTERFRAME\UI-StateIcon]])
bar.icon:SetTexCoord(0, 0.5, 0.5, 1)
bar.icon:SetBlendMode("ADD")
else
bar:Hide()
end
end
FauxScrollFrame_Update(self, #emotePool, CONST_EMOTES_MAX_LINES, 15)
else
for barIndex = 1, CONST_EMOTES_MAX_LINES do
local bar = emoteLines[barIndex]
bar:Hide()
end
end
end
edFrame.EmoteWidgets = {}
--~emotes ~whispers
local barDivEmotes = detailsFramework:CreateImage(edFrame, "Interface\\AddOns\\Details_EncounterDetails\\images\\boss_bg", 4, 480, "artwork", {724/1024, 728/1024, 0, 245/512})
barDivEmotes:SetPoint("TOPLEFT", edFrame, "TOPLEFT", 244, -74)
barDivEmotes:Hide()
table.insert(edFrame.EmoteWidgets, barDivEmotes)
local emoteScrollFrame = CreateFrame("ScrollFrame", "EncounterDetails_EmoteScroll", edFrame, "FauxScrollFrameTemplate, BackdropTemplate")
emoteScrollFrame:SetScript("OnVerticalScroll", function(self, offset) FauxScrollFrame_OnVerticalScroll(self, offset, 14, refresh_emotes) end)
emoteScrollFrame:SetPoint("topleft", edFrame, "topleft", 249, -75)
emoteScrollFrame:SetPoint("bottomright", edFrame, "bottomright", -33, 42)
emoteScrollFrame.Update = refresh_emotes
emoteScrollFrame:Hide()
detailsFramework:ReskinSlider(emoteScrollFrame, 3)
encounterDetails.EmoteScrollFrame = emoteScrollFrame
table.insert(edFrame.EmoteWidgets, emoteScrollFrame)
local onEnterRow = function(self)
self:SetBackdrop({bgFile = [[Interface\AddOns\Details\images\background]], tile = true, tileSize = 16})
self:SetBackdropColor(1, 1, 1, .6)
if (self.rightText:IsTruncated()) then
GameCooltip:Reset()
GameCooltip:AddLine(self.rightText:GetText())
GameCooltip:SetOwner(self, "bottomleft", "topleft", 42, -9)
GameCooltip:Show()
end
end
local onLeaveRow = function(self)
self:SetBackdrop({bgFile = [[Interface\AddOns\Details\images\background]], tile = true, tileSize = 16})
self:SetBackdropColor(1, 1, 1, .3)
GameCooltip:Hide()
end
local onMouseUpRow = function(self)
--report
local text = self.rightText:GetText()
local time = self.leftText:GetText()
local reportFunc = function()
-- remove textures
text = text:gsub("(|T).*(|t)", "")
-- remove colors
text = text:gsub("|c%x?%x?%x?%x?%x?%x?%x?%x?", "")
text = text:gsub("|r", "")
-- replace links
for _, spellid in text:gmatch("(|Hspell:)(.-)(|h)") do
local spell = tonumber(spellid)
local link = GetSpellLink(spell)
text = text:gsub("(|Hspell).*(|h)", link)
end
-- remove unit links
text = text:gsub("(|Hunit).-(|h)", "")
-- remove the left space
text = text:gsub("^%s$", "")
encounterDetails:SendReportLines({"Details! Encounter Emote at " .. time, "\"" .. text .. "\""})
end
encounterDetails:SendReportWindow(reportFunc)
end
for i = 1, CONST_EMOTES_MAX_LINES do
local line = CreateFrame("frame", nil, edFrame,"BackdropTemplate")
local y = (i-1) * 15 * -1
line:SetPoint("topleft", emoteScrollFrame, "topleft", 0, y)
line:SetPoint("topright", emoteScrollFrame, "topright", 0, y)
line:SetHeight(14)
line:SetBackdrop({bgFile = [[Interface\AddOns\Details\images\background]], tile = true, tileSize = 16})
line:SetBackdropColor(1, 1, 1, .3)
line.icon = line:CreateTexture(nil, "overlay")
line.icon:SetPoint("left", line, "left", 2, 0)
line.icon:SetSize(14, 14)
line.leftText = line:CreateFontString(nil, "overlay", "GameFontHighlightSmall")
line.leftText:SetPoint("left", line.icon, "right", 2, 0)
line.leftText:SetHeight(14)
line.leftText:SetJustifyH("left")
line.rightText = line:CreateFontString(nil, "overlay", "GameFontHighlightSmall")
line.rightText:SetPoint("left", line.icon, "right", 46, 0)
line.rightText:SetHeight(14)
line.rightText:SetJustifyH("left")
line:SetFrameLevel(emoteScrollFrame:GetFrameLevel()+1)
line:SetScript("OnEnter", onEnterRow)
line:SetScript("OnLeave", onLeaveRow)
line:SetScript("OnMouseUp", onMouseUpRow)
table.insert(emoteLines, line)
table.insert(edFrame.EmoteWidgets, line)
line:Hide()
end
--select emote segment
local emotesSegmentLabel = detailsFramework:CreateLabel(edFrame, "Segment:", 11, nil, "GameFontHighlightSmall")
emotesSegmentLabel:SetPoint("topleft", edFrame, "topleft", 10, -85)
local onEmoteSegmentSelected = function(_, _, segment)
FauxScrollFrame_SetOffset(emoteScrollFrame, 0)
emote_segment_index = segment
encounterDetails.emoteSegmentIndex = segment
emoteScrollFrame:Update()
end
function encounterDetails:SetEmoteSegment(segment)
emote_segment_index = segment
encounterDetails.emoteSegmentIndex = segment
end
local segmentIcon = [[Interface\AddOns\Details\images\icons]]
local segmentIconCoords = {0.7373046875, 0.9912109375, 0.6416015625, 0.7978515625}
local segmentIconColor = {1, 1, 1, 0.5}
local buildEmoteSementsList = function()
local resultTable = {}
if (not encounterDetails.charsaved) then
return resultTable
end
for index, segment in ipairs(encounterDetails.charsaved.emotes) do
local bossIcon, iconWidth, iconHeight, iconL, iconR, iconT, iconB = Details:GetBossEncounterTexture(segment.boss or "unknown")
table.insert(resultTable, {label = "#" .. index .. " " ..(segment.boss or "unknown"), value = index, icon = bossIcon, iconsize = {iconWidth, iconHeight}, texcoord = {iconL, iconR, iconT, iconB}, onclick = onEmoteSegmentSelected, iconcolor = segmentIconColor})
end
return resultTable
end
local emoteSegmentsDropdown = detailsFramework:NewDropDown(edFrame, _, "$parentEmotesSegmentDropdown", "EmotesSegment", 180, 20, buildEmoteSementsList, 1)
emoteSegmentsDropdown:SetPoint("topleft", emotesSegmentLabel, "bottomleft", -1, -2)
emoteSegmentsDropdown:SetTemplate(detailsFramework:GetTemplate("dropdown", "OPTIONS_DROPDOWN_TEMPLATE"))
encounterDetails.emoteSegmentsDropdown = emoteSegmentsDropdown
table.insert(edFrame.EmoteWidgets, emoteSegmentsDropdown)
table.insert(edFrame.EmoteWidgets, emotesSegmentLabel)
--search box
local emotesSearchLabel = detailsFramework:CreateLabel(edFrame, "Search:", 11, nil, "GameFontHighlightSmall")
emotesSearchLabel:SetPoint("topleft", edFrame, "topleft", 10, -130)
local emotesSearchResultsLabel = detailsFramework:CreateLabel(edFrame, "", 11, nil, "GameFontNormal", "SearchResults")
emotesSearchResultsLabel:SetPoint("topleft", edFrame, "topleft", 10, -190)
local searchTextEntry = detailsFramework:NewTextEntry(edFrame, nil, "$parentEmoteSearchBox", nil, 180, 20)
searchTextEntry:SetTemplate(detailsFramework:GetTemplate("dropdown", "OPTIONS_DROPDOWN_TEMPLATE"))
searchTextEntry:SetPoint("topleft",emotesSearchLabel, "bottomleft", -1, -2)
searchTextEntry:SetJustifyH("left")
searchTextEntry:SetAsSearchBox()
searchTextEntry:SetHook("OnTextChanged", function()
searching = searchTextEntry:GetText()
if (searching == "") then
searching = nil
FauxScrollFrame_SetOffset(emoteScrollFrame, 0)
edFrame.ReportEmoteButton:Disable()
emoteScrollFrame:Update()
else
FauxScrollFrame_SetOffset(emoteScrollFrame, 0)
edFrame.ReportEmoteButton:Enable()
emoteScrollFrame:Update()
end
end)
table.insert(edFrame.EmoteWidgets, searchTextEntry)
table.insert(edFrame.EmoteWidgets, emotesSearchLabel)
-- report button
local reportEmoteButton = detailsFramework:NewButton(edFrame, nil, "$parentReportEmoteButton", "ReportEmoteButton", 180, 20, function()
local reportFunc = function(IsCurrent, IsReverse, AmtLines)
local segment = encounterDetails.charsaved.emotes and encounterDetails.charsaved.emotes[emote_segment_index]
if (segment) then
encounterDetails.report_lines = {"Details!: Emotes for " .. segment.boss}
local added = 0
for index = 1, 16 do
local bar = emoteLines[index]
if (bar:IsShown() and added < AmtLines) then
local time = bar.leftText:GetText()
local text = bar.rightText:GetText()
--"|Hunit:77182:Oregorger|hOregorger prepares to cast |cFFFF0000|Hspell:156879|h[Blackrock Barrage]|h|r."
-- remove textures
text = text:gsub("(|T).*(|t)", "")
-- remove colors
text = text:gsub("|c%x?%x?%x?%x?%x?%x?%x?%x?", "")
text = text:gsub("|r", "")
-- replace links
for _, spellid in text:gmatch("(|Hspell:)(.-)(|h)") do
local spell = tonumber(spellid)
local link = GetSpellLink(spell)
text = text:gsub("(|Hspell).*(|h)", link)
end
-- remove unit links
text = text:gsub("(|Hunit).-(|h)", "")
-- remove the left space
text = text:gsub("^%s$", "")
table.insert(encounterDetails.report_lines, time .. " " .. text)
added = added + 1
if (added == AmtLines) then
break
end
end
end
encounterDetails:SendReportLines(encounterDetails.report_lines)
else
encounterDetails:Msg("There is nothing to report.")
end
end
local use_slider = true
encounterDetails:SendReportWindow(reportFunc, nil, nil, use_slider)
end, nil, nil, nil, "Report Results")
reportEmoteButton:SetIcon([[Interface\AddOns\Details\images\report_button]], 8, 14, nil, {0, 1, 0, 1}, nil, 4, 2)
reportEmoteButton:SetTemplate(detailsFramework:GetTemplate("dropdown", "OPTIONS_DROPDOWN_TEMPLATE"))
reportEmoteButton:SetPoint("topleft", searchTextEntry, "bottomleft", 0, -4)
reportEmoteButton:Disable()
table.insert(edFrame.EmoteWidgets, reportEmoteButton)
for _, widget in pairs(edFrame.EmoteWidgets) do
widget:Hide()
end
local emoteReportLabel = detailsFramework:NewLabel(searchTextEntry.widget, searchTextEntry.widget, nil, "report_click", "|cFFffb400Left Click|r: Report Line")
emoteReportLabel:SetPoint("topleft", searchTextEntry.widget, "bottomleft", 1, -61)
@@ -0,0 +1,471 @@
local addonId, edTable = ...
local Details = _G._detalhes
local AceLocale = LibStub("AceLocale-3.0")
local Loc = AceLocale:GetLocale("Details_EncounterDetails")
local Graphics = LibStub:GetLibrary("LibGraph-2.0")
local ipairs = ipairs
local _GetSpellInfo = Details.getspellinfo
local unpack = unpack
local detailsFramework = DetailsFramework
local CreateFrame = CreateFrame
local GameCooltip = GameCooltip
local wipe = table.wipe
local _
local encounterDetails = _G.EncounterDetailsGlobal
local edFrame = encounterDetails.Frame
local phaseFrame = CreateFrame("frame", "EncounterDetailsPhaseFrame", edFrame, "BackdropTemplate")
phaseFrame:SetAllPoints()
phaseFrame:SetFrameLevel(edFrame:GetFrameLevel()+1)
phaseFrame.DamageTable = {}
phaseFrame.HealingTable = {}
phaseFrame.LastPhaseSelected = 1
phaseFrame.CurrentSegment = {}
phaseFrame.PhaseButtons = {}
EncounterDetailsPhaseFrame:Hide()
local phaseButtonTemplateHighlight = {
backdrop = {edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true},
backdropcolor = {.7, .7, .7, .5},
onentercolor = {1, 1, 1, .5},
backdropbordercolor = {.70, .70, .70, 1},
}
local phaseButtonTemplate = {
backdrop = {edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true},
backdropcolor = {.7, .7, .7, .5},
onentercolor = {1, 1, 1, .5},
backdropbordercolor = {0, 0, 0, 1},
}
local scrollWidth, scrollHeight, scrollLineAmount, scrollLineHeight = 250, 420, 20, 20
local phasesY = -88
local anchorY = -120
phaseFrame:SetScript("OnShow", function()
phaseFrame.OnSelectPhase(1)
end)
function phaseFrame:ClearAll()
--disable all buttons
for i = 1, #phaseFrame.PhaseButtons do
phaseFrame.PhaseButtons[i]:SetTemplate(phaseButtonTemplate)
phaseFrame.PhaseButtons[i]:Disable()
end
--update damage and healing scrolls
wipe(phaseFrame.DamageTable)
wipe(phaseFrame.HealingTable)
--refresh the scroll
phaseFrame.Damage_Scroll:Refresh()
phaseFrame.Heal_Scroll:Refresh()
--clear phase bars
phaseFrame:ClearPhaseBars()
end
local selectSegment = function(_, _, phaseSelected)
phaseFrame["OnSelectPhase"](phaseSelected)
end
function phaseFrame.OnSelectPhase(phaseSelected)
phaseFrame:ClearAll()
--get the selected segment
phaseFrame.CurrentSegment = encounterDetails:GetCombat(encounterDetails._segment)
if (not phaseFrame.CurrentSegment) then
return
end
--get the heal and damage for phase selected
local phaseData = phaseFrame.CurrentSegment.PhaseData
if (not phaseData) then
return
end
phaseSelected = phaseSelected or phaseFrame.LastPhaseSelected
phaseFrame.LastPhaseSelected = phaseSelected
local phases = phaseFrame:GetPhaseTimers(phaseFrame.CurrentSegment, true)
for buttonIndex, phase in ipairs(phases) do
local button = phaseFrame.PhaseButtons[buttonIndex]
if (phase == phaseSelected) then
button:SetTemplate(phaseButtonTemplateHighlight)
else
button:SetTemplate(phaseButtonTemplate)
if (phaseFrame.CurrentSegment.PhaseData.damage[phase]) then
button:Enable()
else
button:Disable()
end
end
button:SetText(phase)
button:SetClickFunction(selectSegment, phase)
end
if (not phaseData.damage[phaseSelected]) then
phaseFrame:ClearAll()
return
end
--update damage and healing scrolls
wipe(phaseFrame.DamageTable)
for charName, amount in pairs(phaseData.damage[phaseSelected]) do
table.insert(phaseFrame.DamageTable, {charName, amount})
end
table.sort(phaseFrame.DamageTable, function(a, b) return a[2] > b[2] end)
wipe(phaseFrame.HealingTable)
for charName, amount in pairs(phaseData.heal[phaseSelected]) do
table.insert(phaseFrame.HealingTable, {charName, amount})
end
table.sort(phaseFrame.HealingTable, function(a, b) return a[2] > b[2] end)
--refresh the scroll
phaseFrame.Damage_Scroll:Refresh()
phaseFrame.Heal_Scroll:Refresh()
phaseFrame:UpdatePhaseBars()
end
local PhaseSelectLabel = detailsFramework:CreateLabel(phaseFrame, "Select Phase:", 12, "orange")
local DamageLabel = detailsFramework:CreateLabel(phaseFrame, "Damage Done")
local HealLabel = detailsFramework:CreateLabel(phaseFrame, "Healing Done")
local PhaseTimersLabel = detailsFramework:CreateLabel(phaseFrame, "Time Spent on Each Phase")
local report_damage = function(IsCurrent, IsReverse, AmtLines)
local result = {}
local reportFunc = function(IsCurrent, IsReverse, AmtLines)
AmtLines = AmtLines + 1
if (#result > AmtLines) then
for i = #result, AmtLines+1, -1 do
table.remove(result, i)
end
end
encounterDetails:SendReportLines(result)
end
table.insert(result, "Details!: Damage for Phase " .. phaseFrame.LastPhaseSelected .. " of " ..(phaseFrame.CurrentSegment and phaseFrame.CurrentSegment.is_boss and phaseFrame.CurrentSegment.is_boss.name or "Unknown") .. ":")
for i = 1, #phaseFrame.DamageTable do
table.insert(result, encounterDetails:GetOnlyName(phaseFrame.DamageTable[i][1]) .. ": " .. Details:ToK(math.floor(phaseFrame.DamageTable[i][2])))
end
encounterDetails:SendReportWindow(reportFunc, nil, nil, true)
end
local Report_DamageButton = detailsFramework:CreateButton(phaseFrame, report_damage, 16, 16, "report")
Report_DamageButton:SetPoint("left", DamageLabel, "left", scrollWidth-44, 0)
Report_DamageButton.textcolor = "gray"
Report_DamageButton.textsize = 9
local report_healing = function()
local result = {}
local reportFunc = function(IsCurrent, IsReverse, AmtLines)
AmtLines = AmtLines + 1
if (#result > AmtLines) then
for i = #result, AmtLines+1, -1 do
table.remove(result, i)
end
end
encounterDetails:SendReportLines(result)
end
table.insert(result, "Details!: Healing for Phase " .. phaseFrame.LastPhaseSelected .. " of " ..(phaseFrame.CurrentSegment and phaseFrame.CurrentSegment.is_boss and phaseFrame.CurrentSegment.is_boss.name or "Unknown") .. ":")
for i = 1, #phaseFrame.HealingTable do
table.insert(result, encounterDetails:GetOnlyName(phaseFrame.HealingTable[i][1]) .. ": " .. Details:ToK(math.floor(phaseFrame.HealingTable[i][2])))
end
encounterDetails:SendReportWindow(reportFunc, nil, nil, true)
end
local Report_HealingButton = detailsFramework:CreateButton(phaseFrame, report_healing, 16, 16, "report")
Report_HealingButton:SetPoint("left", HealLabel, "left", scrollWidth-44, 0)
Report_HealingButton.textcolor = "gray"
Report_HealingButton.textsize = 9
PhaseSelectLabel:SetPoint("topleft", phaseFrame, "topleft", 10, phasesY)
DamageLabel:SetPoint("topleft", phaseFrame, "topleft", 10, anchorY)
HealLabel:SetPoint("topleft", phaseFrame, "topleft", scrollWidth + 40, anchorY)
PhaseTimersLabel:SetPoint("topleft", phaseFrame, "topleft",(scrollWidth * 2) +(40*2), anchorY)
for i = 1, 10 do
local button = detailsFramework:CreateButton(phaseFrame, phaseFrame.OnSelectPhase, 60, 20, "", i)
button:SetPoint("left", PhaseSelectLabel, "right", 8 +((i-1) * 61), 0)
table.insert(phaseFrame.PhaseButtons, button)
end
local ScrollRefresh = function(self, data, offset, total_lines)
local formatToK = Details:GetCurrentToKFunction()
local removeRealm = Details.GetOnlyName
local topValue = data[1] and data[1][2]
for i = 1, scrollLineAmount do
local index = i + offset
local player = data[index]
if (player) then
local line = self:GetLine(i)
local texture, L, R, T, B = Details:GetPlayerIcon(player[1], phaseFrame.CurrentSegment)
line.icon:SetTexture(texture)
line.icon:SetTexCoord(L, R, T, B)
line.name:SetText(index .. ". " .. removeRealm(_, player[1]))
line.done:SetText(formatToK(_, player[2]) .. " (" .. string.format("%.1f", player[2] / topValue * 100) .. "%)")
line.statusbar:SetValue(player[2] / topValue * 100)
local actorClass = Details:GetClass(player[1])
if (actorClass) then
line.statusbar:SetColor(actorClass)
else
line.statusbar:SetColor("silver")
end
end
end
end
local onEnterLine = function(self)
self:SetBackdropColor(unpack(edTable.defaultBackgroundColor_OnEnter))
end
local onLeaveLine = function(self)
self:SetBackdropColor(unpack(edTable.defaultBackgroundColor))
end
local scrollCreateLine = function(self, index)
local line = CreateFrame("button", "$parentLine" .. index, self,"BackdropTemplate")
line:SetPoint("topleft", self, "topleft", 0, -((index-1)*(scrollLineHeight+1)))
line:SetSize(scrollWidth, scrollLineHeight)
line:SetScript("OnEnter", onEnterLine)
line:SetScript("OnLeave", onLeaveLine)
line:SetScript("OnClick", line_onclick)
line:SetBackdrop({bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true})
line:SetBackdropColor(unpack(edTable.defaultBackgroundColor))
local statusBar = detailsFramework:CreateBar(line, encounterDetails.Frame.DefaultBarTexture, 1, 1, 100)
statusBar:SetAllPoints(line)
statusBar.backgroundtexture = encounterDetails.Frame.DefaultBarTexture
statusBar.backgroundcolor = {.3, .3, .3, .3}
local icon = statusBar:CreateTexture("$parentIcon", "overlay")
icon:SetSize(scrollLineHeight, scrollLineHeight)
local name = statusBar:CreateFontString("$parentName", "overlay", "GameFontNormal")
detailsFramework:SetFontSize(name, 10)
icon:SetPoint("left", line, "left", 2, 0)
name:SetPoint("left", icon, "right", 2, 0)
detailsFramework:SetFontColor(name, "white")
local done = statusBar:CreateFontString("$parentDone", "overlay", "GameFontNormal")
detailsFramework:SetFontSize(done, 10)
detailsFramework:SetFontColor(done, "white")
done:SetPoint("right", line, "right", -2, 0)
line.icon = icon
line.name = name
line.done = done
line.statusbar = statusBar
name:SetHeight(10)
name:SetJustifyH("left")
return line
end
local damageScroll = detailsFramework:CreateScrollBox(phaseFrame, "$parentDamageScroll", ScrollRefresh, phaseFrame.DamageTable, scrollWidth, scrollHeight, scrollLineAmount, scrollLineHeight)
local healScroll = detailsFramework:CreateScrollBox(phaseFrame, "$parentHealScroll", ScrollRefresh, phaseFrame.HealingTable, scrollWidth, scrollHeight, scrollLineAmount, scrollLineHeight)
damageScroll:SetPoint("topleft", DamageLabel.widget, "bottomleft", 0, -4)
healScroll:SetPoint("topleft", HealLabel.widget, "bottomleft", 0, -4)
detailsFramework:ReskinSlider(damageScroll, 4)
detailsFramework:ReskinSlider(healScroll, 4)
for i = 1, scrollLineAmount do
damageScroll:CreateLine(scrollCreateLine)
end
phaseFrame.Damage_Scroll = damageScroll
damageScroll:SetBackdrop({bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", tile = true, tileSize = 16})
damageScroll:SetBackdropColor(0, 0, 0, .4)
for i = 1, scrollLineAmount do
healScroll:CreateLine(scrollCreateLine)
end
phaseFrame.Heal_Scroll = healScroll
healScroll:SetBackdrop({bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", tile = true, tileSize = 16})
healScroll:SetBackdropColor(0, 0, 0, .4)
phaseFrame.PhasesBars = {}
phaseFrame.PhasesSegmentCompare = {}
local PhaseBarOnEnter = function(self)
phaseFrame:UpdateSegmentCompareBars(self.phase)
self:SetBackdropColor(unpack(edTable.defaultBackgroundColor_OnEnter))
end
local PhaseBarOnLeave = function(self)
phaseFrame:ClearSegmentCompareBars()
self:SetBackdropColor(unpack(edTable.defaultBackgroundColor))
end
local PhaseBarOnClick = function(self)
--report
end
--cria as linhas mostrando o tempo decorride de cada phase
for i = 1, 10 do
local line = CreateFrame("button", "$parentPhaseBar" .. i, phaseFrame,"BackdropTemplate")
line:SetPoint("topleft", PhaseTimersLabel.widget, "bottomleft", 0, -((i-1)*(31)) - 4)
line:SetSize(175, 30)
line:SetScript("OnEnter", PhaseBarOnEnter)
line:SetScript("OnLeave", PhaseBarOnLeave)
line:SetScript("OnClick", PhaseBarOnClick)
line:SetBackdrop({bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true})
line:SetBackdropColor(unpack(edTable.defaultBackgroundColor))
local icon = line:CreateTexture("$parentIcon", "overlay")
icon:SetSize(16, 16)
icon:SetTexture([[Interface\AddOns\Details\images\clock]])
local name = line:CreateFontString("$parentName", "overlay", "GameFontNormal")
detailsFramework:SetFontSize(name, 10)
local done = line:CreateFontString("$parentDone", "overlay", "GameFontNormal")
detailsFramework:SetFontSize(done, 10)
icon:SetPoint("left", line, "left", 2, 0)
name:SetPoint("left", icon, "right", 2, 0)
done:SetPoint("right", line, "right", -3, 0)
line.icon = icon
line.name = name
line.done = done
name:SetHeight(10)
name:SetJustifyH("left")
table.insert(phaseFrame.PhasesBars, line)
end
--cria a linha do segmento para a comparao, o que fica na parte direita da tela
--ele acessado para mostrar quando passar o mouse sobre uma das barras de phase
for i = 1, 20 do
local line = CreateFrame("button", "$parentSegmentCompareBar" .. i, phaseFrame,"BackdropTemplate")
line:SetPoint("topleft", PhaseTimersLabel.widget, "bottomleft", 175+10, -((i-1)*(scrollLineHeight+1)) - 4)
line:SetSize(150, scrollLineHeight)
line:SetBackdrop({bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true})
line:SetBackdropColor(unpack(edTable.defaultBackgroundColor))
line:Hide()
local name = line:CreateFontString("$parentName", "overlay", "GameFontNormal")
detailsFramework:SetFontSize(name, 9)
name:SetPoint("left", line, "left", 2, 0)
local done = line:CreateFontString("$parentDone", "overlay", "GameFontNormal")
detailsFramework:SetFontSize(done, 9)
done:SetPoint("right", line, "right", -2, 0)
line.name = name
line.done = done
name:SetHeight(10)
name:SetJustifyH("left")
table.insert(phaseFrame.PhasesSegmentCompare, line)
end
function phaseFrame:ClearPhaseBars()
for i = 1, #phaseFrame.PhasesBars do
local bar = phaseFrame.PhasesBars[i]
bar.name:SetText("")
bar.done:SetText("")
bar.phase = nil
bar:Hide()
end
end
function phaseFrame:ClearSegmentCompareBars()
for i = 1, #phaseFrame.PhasesSegmentCompare do
phaseFrame.PhasesSegmentCompare[i]:Hide()
end
end
function phaseFrame:GetPhaseTimers(segment, ordered)
local t = {}
segment = segment or phaseFrame.CurrentSegment
for phaseIT = 1, #segment.PhaseData do
local phase, startAt = unpack(segment.PhaseData[phaseIT]) --phase iterator
local endAt = segment.PhaseData[phaseIT+1] and segment.PhaseData[phaseIT+1][2] or segment:GetCombatTime()
local elapsed = endAt - startAt
t[phase] = (t[phase] or 0) + elapsed
end
if (ordered) then
local order = {}
for phase, _ in pairs(t) do
table.insert(order, phase)
end
table.sort(order, function(a, b) return a < b end)
return order, t
end
return t
end
--executa quando atualizar o segment geral
function phaseFrame:UpdatePhaseBars()
local timers, hash = phaseFrame:GetPhaseTimers(phaseFrame.CurrentSegment, true)
local i = 1
for index, phase in ipairs(timers) do
local timer = hash[phase]
phaseFrame.PhasesBars[i].name:SetText("|cFFC0C0C0Phase:|r |cFFFFFFFF" .. phase)
phaseFrame.PhasesBars[i].done:SetText(detailsFramework:IntegerToTimer(timer))
phaseFrame.PhasesBars[i].phase = phase
phaseFrame.PhasesBars[i]:Show()
i = i + 1
end
end
--executa quando passar o mouse sobre uma bnarra de phase
function phaseFrame:UpdateSegmentCompareBars(phase)
--segmento atual(numero)
local segmentNumber = encounterDetails._segment
local segmentTable = phaseFrame.CurrentSegment
local bossID = segmentTable:GetBossInfo() and segmentTable:GetBossInfo().id
local index = 1
for i, segment in ipairs(Details:GetCombatSegments()) do
if (segment:GetBossInfo() and segment:GetBossInfo().id == bossID) then
local bar = phaseFrame.PhasesSegmentCompare [index]
local timers = phaseFrame:GetPhaseTimers(segment)
if (timers [phase]) then
if (segment ~= segmentTable) then
bar.name:SetText("Segment " .. i .. ":")
detailsFramework:SetFontColor(bar.name, "orange")
bar.done:SetText(detailsFramework:IntegerToTimer(timers [phase]))
detailsFramework:SetFontColor(bar.done, "orange")
else
bar.name:SetText("Segment " .. i .. ":")
detailsFramework:SetFontColor(bar.name, "white")
bar.done:SetText(detailsFramework:IntegerToTimer(timers [phase]))
detailsFramework:SetFontColor(bar.done, "white")
end
else
bar.name:SetText("Segment " .. i .. ":")
detailsFramework:SetFontColor(bar.name, "red")
bar.done:SetText("--x--x--")
end
bar:Show()
index = index + 1
end
end
end
File diff suppressed because it is too large Load Diff
@@ -1,4 +1,4 @@
## Interface: 100100
## Interface: 100105
## Title: Details!: Raid Check (plugin)
## Notes: Show talents and item level for all members in your group, also shows food and flask state.
## RequiredDeps: Details
@@ -1,4 +1,4 @@
## Interface: 100100
## Interface: 100105
## Title: Details!: Streamer (plugin)
## Notes: Show which spells you are casting, viewers can see what are you doing and follow your steps.
## RequiredDeps: Details
@@ -1,9 +1,8 @@
## Interface: 100100
## Interface: 100105
## Title: Details!: Tiny Threat (plugin)
## Notes: Threat meter plugin, show threat for group members in the window. Select it from the Plugin menu in the Orange Cogwheel.
## RequiredDeps: Details
## OptionalDeps: Ace3
## IconTexture: Interface\AddOns\Details\images\minimap
## X-Wago-ID: Rn6VJW6d
@@ -1,4 +1,4 @@
## Interface: 100100
## Interface: 100105
## Title: Details!: Vanguard (plugin)
## Notes: Show the health and debuffs for tanks in your group.
## SavedVariablesPerCharacter: _detalhes_databaseVanguard