forked from DanderBot/DandersFrames
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAPI.lua
More file actions
749 lines (637 loc) · 24 KB
/
API.lua
File metadata and controls
749 lines (637 loc) · 24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
local addonName, DF = ...
-- ============================================================
-- EXTERNAL API
-- Global functions for external addon integration (Wago UI Packs, etc.)
-- ============================================================
-- These functions are exposed globally so other addons can import/export
-- DandersFrames profiles programmatically.
-- ============================================================
-- FRAME SETTINGS (Party + Raid)
-- ============================================================
-- Export both party and raid frame settings as an encoded string
-- Parameters:
-- profileKey: (optional) specific profile to export, defaults to current profile
-- Returns: string (encoded profile) or nil on error
function DandersFrames_Export(profileKey)
if not DF or not DF.ExportProfile then
return nil
end
-- If a specific profile is requested, temporarily use that profile's data
if profileKey and profileKey ~= "" then
-- Check if the profile exists
if not DandersFramesDB_v2 or not DandersFramesDB_v2.profiles or not DandersFramesDB_v2.profiles[profileKey] then
return nil -- Profile doesn't exist
end
-- Save current db reference
local originalDB = DF.db
-- Temporarily switch to the requested profile's data
DF.db = DandersFramesDB_v2.profiles[profileKey]
-- Export with the profile name
local str = DF:ExportProfile(nil, {party = true, raid = true}, profileKey)
-- Restore original db reference
DF.db = originalDB
return str
end
-- Default: export current profile
local str = DF:ExportProfile(nil, {party = true, raid = true})
return str
end
-- Import frame settings from an encoded string
-- Parameters:
-- str: encoded profile string (from DandersFrames_Export or manual export)
-- profileKey: (optional) exact name for the profile - if exists, overwrites it
-- Returns: boolean success, string errorMessage/profileName
function DandersFrames_Import(str, profileKey)
if not DF then
return false, "DandersFrames not loaded"
end
if not str or str == "" then
return false, "Empty import string"
end
-- Validate the import string first
if not DF.ValidateImportString then
return false, "Import function not available"
end
local importData, errMsg = DF:ValidateImportString(str)
if not importData then
return false, errMsg or "Invalid import string"
end
-- Use ApplyImportedProfile to create/overwrite a profile
if DF.ApplyImportedProfile then
-- Use provided profileKey, or fall back to name from import data
local targetName = profileKey
if not targetName or targetName == "" then
targetName = importData.profileName or "Imported Profile"
end
-- If profileKey is provided and profile exists, we overwrite it
-- If profileKey is provided and doesn't exist, we create it
-- If no profileKey, create new profile (may auto-rename for uniqueness)
local createNew = not profileKey or profileKey == ""
local success = DF:ApplyImportedProfile(importData, nil, nil, targetName, createNew)
if success then
-- If profileKey was provided, switch to that profile
if profileKey and profileKey ~= "" then
if DandersFramesDB_v2 then
DandersFramesDB_v2.profiles[profileKey] = DF:DeepCopy(DF.db)
DandersFramesDB_v2.currentProfile = profileKey
end
end
-- Return the actual profile name
local actualName = DandersFramesDB_v2 and DandersFramesDB_v2.currentProfile or targetName
-- Refresh the GUI if it's open
if DF.GUIFrame and DF.GUIFrame:IsShown() and DF.GUI and DF.GUI.RefreshCurrentPage then
DF.GUI:RefreshCurrentPage()
end
return true, actualName
else
return false, "Failed to apply profile"
end
end
-- Fallback: direct apply (shouldn't reach here normally)
if importData.party and DF.db then
DF.db.party = importData.party
end
if importData.raid and DF.db then
DF.db.raid = importData.raid
end
-- Update frames
if DF.UpdateAll then
DF:UpdateAll()
end
return true
end
-- ============================================================
-- CLICK CASTING
-- ============================================================
-- Export click casting profile as an encoded string
-- Parameters:
-- profileKey: (optional) specific profile to export, defaults to current profile
-- Returns: string (encoded profile) or nil on error
function DandersFrames_ClickCast_Export(profileKey)
local CC = DF and DF.ClickCast
if not CC then
return nil
end
-- If a specific profile is requested
if profileKey and profileKey ~= "" then
local classData = CC:GetClassData()
if not classData or not classData.profiles or not classData.profiles[profileKey] then
return nil -- Profile doesn't exist
end
-- Export the specific profile
local profile = classData.profiles[profileKey]
local exportData = {
version = 1,
profileName = profileKey,
profile = CopyTable(profile),
exportedAt = date("%Y-%m-%d %H:%M"),
class = select(2, UnitClass("player")),
}
-- Serialize
local LibSerialize = LibStub and LibStub("LibSerialize", true)
local LibDeflate = LibStub and LibStub("LibDeflate", true)
if not LibSerialize or not LibDeflate then
return nil
end
local serialized = LibSerialize:Serialize(exportData)
local compressed = LibDeflate:CompressDeflate(serialized)
local encoded = LibDeflate:EncodeForPrint(compressed)
return "!DFC1!" .. encoded
end
-- Default: export current profile
if not CC.ExportProfile then
return nil
end
return CC:ExportProfile()
end
-- Import click casting profile from an encoded string
-- Parameters:
-- str: encoded profile string
-- profileKey: (optional) exact name for the profile - if exists, overwrites it
-- importAll: (optional) if true, imports ALL bindings including invalid ones
-- Returns: boolean success, string errorMessage/profileName
function DandersFrames_ClickCast_Import(str, profileKey, importAll)
local CC = DF and DF.ClickCast
if not CC then
return false, "ClickCasting module not loaded"
end
if not str or str == "" then
return false, "Empty import string"
end
-- Backwards compatibility: if profileKey is a boolean, treat it as importAll
if type(profileKey) == "boolean" then
importAll = profileKey
profileKey = nil
end
-- Decode the string
local data
if string.sub(str, 1, 6) == "!DFC1!" then
local payload = string.sub(str, 7)
if CC.DeserializeString then
data = CC:DeserializeString(payload)
end
elseif string.sub(str, 1, 5) == "DF01:" then
local payload = string.sub(str, 6)
if CC.DeserializeStringLegacy then
data = CC:DeserializeStringLegacy(payload)
end
else
return false, "Invalid format (expected !DFC1! or DF01: header)"
end
if not data then
return false, "Failed to decode import data"
end
if not data.profile then
return false, "Invalid profile data"
end
-- Analyze bindings for compatibility
local bindingsToImport = {}
if data.profile.bindings then
for _, binding in ipairs(data.profile.bindings) do
local isValid = true
-- Check spell bindings for class compatibility (unless importing all)
if not importAll and binding.actionType == "spell" and binding.spellName then
if CC.GetSpellValidityStatus then
local status = CC:GetSpellValidityStatus(binding.spellName)
isValid = (status ~= "invalid")
end
end
if isValid or importAll then
table.insert(bindingsToImport, binding)
end
end
end
-- Get class data
local classData = CC:GetClassData()
if not classData then
return false, "Failed to get class data"
end
-- Determine profile name
local targetProfileName
if profileKey and profileKey ~= "" then
-- Use exact profileKey (will overwrite if exists)
targetProfileName = profileKey
else
-- Generate unique name
targetProfileName = data.profileName or "Imported"
local baseName = targetProfileName
local counter = 1
while classData.profiles[targetProfileName] do
counter = counter + 1
targetProfileName = baseName .. " " .. counter
end
end
-- Create the profile
local profile = CopyTable(data.profile)
profile.bindings = bindingsToImport
-- Import the profile (overwrites if exists)
classData.profiles[targetProfileName] = profile
-- Ensure all required fields exist
if not classData.profiles[targetProfileName].bindings then
classData.profiles[targetProfileName].bindings = {}
end
if not classData.profiles[targetProfileName].customMacros then
classData.profiles[targetProfileName].customMacros = {}
end
if not classData.profiles[targetProfileName].options then
-- Use default options
classData.profiles[targetProfileName].options = {
enableMouseover = true,
enableOnUnitFrames = true,
enableOnRaidFrames = true,
enableOnPartyFrames = true,
}
end
-- Switch to the imported profile if profileKey was specified
if profileKey and profileKey ~= "" and CC.SwitchProfile then
CC:SwitchProfile(targetProfileName)
end
-- Refresh the UI if open
if CC.UpdateProfileDropdown then
CC.UpdateProfileDropdown()
end
if CC.RefreshClickCastingUI then
CC:RefreshClickCastingUI()
end
return true, targetProfileName
end
-- ============================================================
-- PROFILE UTILITIES (Frame Settings)
-- ============================================================
-- Get a list of all available frame settings profile names
-- Returns: table of profile names (keys), or empty table if none
function DandersFrames_GetProfiles()
local profiles = {}
if DandersFramesDB_v2 and DandersFramesDB_v2.profiles then
for name, _ in pairs(DandersFramesDB_v2.profiles) do
table.insert(profiles, name)
end
end
return profiles
end
-- Get the current active frame settings profile name
-- Returns: string profile name, or nil if not available
function DandersFrames_GetCurrentProfile()
if DandersFramesDB_v2 and DandersFramesDB_v2.currentProfile then
return DandersFramesDB_v2.currentProfile
end
return nil
end
-- ============================================================
-- PROFILE UTILITIES (Click Casting)
-- ============================================================
-- Get a list of all available click casting profile names for current class
-- Returns: table of profile names (keys), or empty table if none
function DandersFrames_ClickCast_GetProfiles()
local profiles = {}
local CC = DF and DF.ClickCast
if CC and CC.GetClassData then
local classData = CC:GetClassData()
if classData and classData.profiles then
for name, _ in pairs(classData.profiles) do
table.insert(profiles, name)
end
end
end
return profiles
end
-- Get the current active click casting profile name
-- Returns: string profile name, or nil if not available
function DandersFrames_ClickCast_GetCurrentProfile()
local CC = DF and DF.ClickCast
if CC and CC.GetActiveProfileName then
return CC:GetActiveProfileName()
end
return nil
end
-- ============================================================
-- VERSION INFO
-- ============================================================
-- Get addon version (useful for compatibility checks)
-- Returns: string version
function DandersFrames_GetVersion()
return DF and DF.VERSION or "Unknown"
end
-- Check if the API is ready (addon fully loaded)
-- Returns: boolean
function DandersFrames_IsReady()
return DF and DF.db and true or false
end
-- ============================================================
-- FRAME ACCESS
-- ============================================================
-- Get the player frame
-- Returns: frame or nil
function DandersFrames_GetPlayerFrame()
if not DF then return nil end
return DF.playerFrame
end
-- Get a specific party frame by index (1-4)
-- Returns: frame or nil
function DandersFrames_GetPartyFrame(index)
if not DF or not DF.GetPartyFrame then return nil end
return DF:GetPartyFrame(index)
end
-- Get a specific raid frame by index (1-40)
-- Returns: frame or nil
function DandersFrames_GetRaidFrame(index)
if not DF or not DF.GetRaidFrame then return nil end
return DF:GetRaidFrame(index)
end
-- Get a frame by unit ID (e.g. "player", "party2", "raid15")
-- Returns: frame or nil
function DandersFrames_GetFrameForUnit(unit)
if not DF or not DF.GetFrameForUnit then return nil end
return DF:GetFrameForUnit(unit)
end
-- Get all visible frames as a table
-- Returns: table of frames (may be empty)
function DandersFrames_GetAllFrames()
if not DF or not DF.GetAllFrames then return {} end
return DF:GetAllFrames()
end
-- Iterate all frames with a callback
-- If callback is provided: calls callback(frame) for each frame, returns nil
-- If no callback: returns an iterator function for use in for-loops
-- Usage: DandersFrames_IterateFrames(function(frame) ... end)
-- Usage: for frame in DandersFrames_IterateFrames() do ... end
function DandersFrames_IterateFrames(callback)
if not DF or not DF.IterateCompactFrames then
if callback then return end
return function() return nil end
end
return DF:IterateCompactFrames(callback)
end
-- ============================================================
-- FRAME CONTAINERS & HEADERS
-- These return the actual SecureGroupHeaderTemplate frames and
-- container frames. Useful for anchoring to the group as a whole
-- rather than individual unit frames.
-- ============================================================
-- Get the party header (SecureGroupHeaderTemplate that manages party frames)
-- Returns: frame or nil
function DandersFrames_GetPartyHeader()
return DF and DF.partyHeader or nil
end
-- Get the party container frame (parent of the party header, movable anchor)
-- Returns: frame or nil
function DandersFrames_GetPartyContainer()
return DF and DF.partyContainer or nil
end
-- Get the raid container frame (parent of all raid headers, movable anchor)
-- Returns: frame or nil
function DandersFrames_GetRaidContainer()
return DF and DF.raidContainer or nil
end
-- Get a specific raid group header by group number (1-8)
-- Only populated in separated/grouped raid mode (raidUseGroups = true)
-- Returns: frame or nil
function DandersFrames_GetRaidGroupHeader(group)
if not DF or not DF.raidSeparatedHeaders then return nil end
return DF.raidSeparatedHeaders[group]
end
-- Get all raid group headers as a table (1-8)
-- Only populated in separated/grouped raid mode
-- Returns: table (keys 1-8, values are frames or nil)
function DandersFrames_GetRaidGroupHeaders()
if not DF or not DF.raidSeparatedHeaders then return {} end
local headers = {}
for i = 1, 8 do
headers[i] = DF.raidSeparatedHeaders[i]
end
return headers
end
-- Get the flat raid header (single SecureGroupHeaderTemplate for all 40 slots)
-- Only populated in flat raid mode (raidUseGroups = false)
-- Returns: frame or nil
function DandersFrames_GetFlatRaidHeader()
if not DF or not DF.FlatRaidFrames then return nil end
return DF.FlatRaidFrames.header
end
-- Get the arena header
-- Returns: frame or nil
function DandersFrames_GetArenaHeader()
return DF and DF.arenaHeader or nil
end
-- Check if raid is using grouped/separated mode vs flat mode
-- Returns: boolean (true = grouped, false = flat), or nil if not available
function DandersFrames_IsRaidGrouped()
if not DF or not DF.GetRaidDB then return nil end
local db = DF:GetRaidDB()
return db and db.raidUseGroups or false
end
-- ============================================================
-- PINNED FRAMES
-- ============================================================
-- Get a pinned frame header by set index (1 or 2)
-- Returns: SecureGroupHeaderTemplate frame or nil
function DandersFrames_GetPinnedHeader(setIndex)
if not DF or not DF.PinnedFrames or not DF.PinnedFrames.initialized then return nil end
return DF.PinnedFrames.headers and DF.PinnedFrames.headers[setIndex] or nil
end
-- Get a pinned frame container by set index (1 or 2)
-- Returns: container frame or nil
function DandersFrames_GetPinnedContainer(setIndex)
if not DF or not DF.PinnedFrames or not DF.PinnedFrames.initialized then return nil end
return DF.PinnedFrames.containers and DF.PinnedFrames.containers[setIndex] or nil
end
-- Get all visible pinned frames as a table
-- Returns: table of frames (may be empty)
function DandersFrames_GetPinnedFrames()
local frames = {}
if not DF or not DF.PinnedFrames or not DF.PinnedFrames.initialized or not DF.PinnedFrames.headers then
return frames
end
for setIndex = 1, 2 do
local header = DF.PinnedFrames.headers[setIndex]
if header and header:IsShown() then
for i = 1, 40 do
local child = header:GetAttribute("child" .. i)
if child and child:IsVisible() then
table.insert(frames, child)
end
end
end
end
return frames
end
-- Find a pinned frame for a specific unit
-- Parameters:
-- unit: string unit ID (e.g. "party1", "raid5")
-- Returns: frame or nil
function DandersFrames_GetPinnedFrameForUnit(unit)
if not DF or not unit or not DF.PinnedFrames or not DF.PinnedFrames.initialized or not DF.PinnedFrames.headers then
return nil
end
for setIndex = 1, 2 do
local header = DF.PinnedFrames.headers[setIndex]
if header and header:IsShown() then
for i = 1, 40 do
local child = header:GetAttribute("child" .. i)
if child and child:IsVisible() and child.unit == unit then
return child
end
end
end
end
return nil
end
-- Check if pinned frames are initialized and active
-- Returns: boolean
function DandersFrames_IsPinnedActive()
if not DF or not DF.PinnedFrames then return false end
return DF.PinnedFrames.initialized == true
end
-- Get the current pinned frames mode
-- Returns: "party" or "raid" or nil
function DandersFrames_GetPinnedMode()
if not DF or not DF.PinnedFrames or not DF.PinnedFrames.initialized then return nil end
return DF.PinnedFrames.currentMode
end
-- ============================================================
-- EXTERNAL HIGHLIGHTS
-- Allows external addons to highlight specific unit frames with
-- a colored border overlay. These are separate from DF's internal
-- selection/aggro/hover highlights and will not conflict.
-- ============================================================
local externalHighlights = {}
local function GetOrCreateExternalHighlight(frame)
if frame.dfExternalHighlight then
return frame.dfExternalHighlight
end
local hl = CreateFrame("Frame", nil, frame, "BackdropTemplate")
hl:SetAllPoints()
hl:SetFrameLevel(frame:GetFrameLevel() + 20)
hl:SetBackdrop({
edgeFile = "Interface\\Buttons\\WHITE8x8",
edgeSize = 2,
})
hl:Hide()
frame.dfExternalHighlight = hl
return hl
end
-- Highlight a specific unit's frame
-- Parameters:
-- unit: string unit ID (e.g. "player", "party1", "raid5")
-- r, g, b: color (0-1), defaults to yellow (1, 1, 0)
-- a: alpha (0-1), defaults to 1
-- duration: seconds, nil/0 = permanent until cleared
-- Returns: boolean success
function DandersFrames_HighlightUnit(unit, r, g, b, a, duration)
if not DF or not unit then return false end
local frame = DF:GetFrameForUnit(unit)
if not frame then return false end
r = r or 1
g = g or 1
b = b or 0
a = a or 1
local hl = GetOrCreateExternalHighlight(frame)
hl:SetBackdropBorderColor(r, g, b, a)
hl:Show()
externalHighlights[unit] = hl
if duration and duration > 0 then
C_Timer.After(duration, function()
if hl:IsShown() and externalHighlights[unit] == hl then
hl:Hide()
externalHighlights[unit] = nil
end
end)
end
return true
end
-- Highlight a frame directly (for external addons that already have a frame ref)
-- Parameters:
-- frame: the frame to highlight
-- r, g, b, a, duration: same as HighlightUnit
-- key: optional string key for tracking (used by ClearHighlight)
-- Returns: boolean success
function DandersFrames_HighlightFrame(frame, r, g, b, a, duration, key)
if not frame then return false end
r = r or 1
g = g or 1
b = b or 0
a = a or 1
local hl = GetOrCreateExternalHighlight(frame)
hl:SetBackdropBorderColor(r, g, b, a)
hl:Show()
local trackKey = key or tostring(frame)
externalHighlights[trackKey] = hl
if duration and duration > 0 then
C_Timer.After(duration, function()
if hl:IsShown() and externalHighlights[trackKey] == hl then
hl:Hide()
externalHighlights[trackKey] = nil
end
end)
end
return true
end
-- Highlight all visible frames
-- Parameters:
-- r, g, b: color (0-1), defaults to yellow
-- a: alpha (0-1), defaults to 1
-- duration: seconds, nil/0 = permanent until cleared
-- Returns: number of frames highlighted
function DandersFrames_HighlightAll(r, g, b, a, duration)
if not DF or not DF.GetAllFrames then return 0 end
local frames = DF:GetAllFrames()
local count = 0
for _, frame in ipairs(frames) do
if frame:IsVisible() then
local unit = frame.unit or tostring(frame)
r = r or 1
g = g or 1
b = b or 0
a = a or 1
local hl = GetOrCreateExternalHighlight(frame)
hl:SetBackdropBorderColor(r, g, b, a)
hl:Show()
externalHighlights[unit] = hl
count = count + 1
if duration and duration > 0 then
C_Timer.After(duration, function()
if hl:IsShown() and externalHighlights[unit] == hl then
hl:Hide()
externalHighlights[unit] = nil
end
end)
end
end
end
return count
end
-- Clear highlight on a specific unit
-- Parameters:
-- unit: string unit ID
-- Returns: boolean (true if highlight was removed)
function DandersFrames_ClearHighlight(unit)
if not unit then return false end
local hl = externalHighlights[unit]
if hl then
hl:Hide()
externalHighlights[unit] = nil
return true
end
-- Also try to find the frame and clear directly
if DF and DF.GetFrameForUnit then
local frame = DF:GetFrameForUnit(unit)
if frame and frame.dfExternalHighlight then
frame.dfExternalHighlight:Hide()
return true
end
end
return false
end
-- Clear all external highlights
-- Returns: number of highlights cleared
function DandersFrames_ClearAllHighlights()
local count = 0
for key, hl in pairs(externalHighlights) do
if hl and hl.Hide then
hl:Hide()
count = count + 1
end
end
wipe(externalHighlights)
return count
end