This change updates several text setters to use `SetFormattedText` instead of passing `string.format(...)` into `SetText`. It also switches a few regex captures from `string.find` to `string.match` for cleaner capture handling, and replaces explicit width/height setters with `SetSize` in the first-run dialog for consistency.
* Add pfUI-native macro icon picker
Replace Blizzard's MacroPopupFrame (the name + icon dialog) with a
custom pfUI picker driven by IconDataProviderMixin, so the full spell +
item + loose icon set is available instead of the stock spell-only list.
It has a searchable, filterable icon grid and saves through
C_Macro.CreateMacro / C_Macro.EditMacro, which take the icon as a texture
string -- so arbitrary icons (including index-less INV_* item icons)
persist. The macro-frame API is identical on the modern (Turtle/Octo)
and stock-vanilla macro UIs, so the picker works on both with no client
detection.
The new macroicons module takes over the New / Change-Icon buttons and
retires the stock popup; the blizzard macro skin now only skins
MacroPopupFrame when that module is disabled.
* Gate the macro icon hook behind a config flag
Run the Blizzard_MacroUI hook only when C.disabled["macroicons"] is
"1".
* Add search to the icon pickers and fix set editing
Equipment manager:
- Move the OK button next to Cancel at the bottom-right.
- Add an icon search box in the freed bottom-left space.
- Shift the popup contents up and shrink the window after the title
was removed.
- Show the current icon when you edit a set. Keep a full icon path as
is instead of adding a second prefix, and match grid icons by name.
- Close the name/icon popup when the manager frame closes. Remove the
duplicate OnHide hook.
Both pickers now use Blizzard global strings for the labels and the
icon filters.
* Replace the row hover poll with OnLeave handlers
The gear button sits inside the set row's rectangle, so MouseIsOver(row)
is true for both. Show the gear on row enter. Hide it from a shared
OnLeave on the row and the gear when the cursor leaves the row. This
removes the per-frame OnUpdate poll.
* Handle missing C_Macro.CreateMacro API
Add guards to detect if C_Macro.CreateMacro is unavailable. The macroicons module now returns early if the API doesn't exist, and turtle-wow.lua uses the alternative Blizzard_MacroUI fallback path in such cases.
* Remove the macro picker title and shrink the window
Delete the title font string and its SetText calls. Move every
top-anchored element up 14px and shrink the window height from 498 to
484. The OK, Cancel, and search controls are bottom-anchored, so they
keep their spacing to the grid.
* bump ClassicAPI min version to 1.9.3
* Extract a shared icon picker widget
The macro icon picker and the equipment manager's name/icon popup carried
near-identical copies of the icon grid, All/Spells/Items filter, name search,
and the IconDataProvider state machine. Pull that into a single
pfUI.api.CreateIconPicker helper; each module keeps only its own frame shell,
buttons, and save flow.
Also fix the grid tooltip: pass white to GameTooltip:SetText (it defaults to
red) and derive the icon name with the case-insensitive basename key so
mixed-case provider paths show a clean name instead of the full path.
Replace separate GetWidth/GetHeight and SetWidth/SetHeight calls with GetSize/SetSize for consistency and brevity. Updated modules: addonbuttons (store size via GetSize), chatcopy (use SetSize), easteregg (use SetSize for explosions), farmmode (use GetSize/SetSize when adjusting map size), and game_menu (use GetSize/SetSize when resizing frame).
commit 70b1e66c2d
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Sun Jul 26 18:30:18 2026 -0500
Use SetSize/SetShown and simplify minimap/map
commit 7b6bfe9975
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Sat Jul 25 11:27:14 2026 -0500
Include raidpet frames in /pftest test mode
The unit-frame test toggle (showall) only previews frames that exist and
are positioned. Raidpet frames are created when raidpet.visible == "1",
but LayoutPets' collapse mode only positions pets whose raidpet<N> unit
actually exists, so solo/in test mode they stayed hidden.
Add a showall branch to LayoutPets that mirror-lays every pet cell and
shows it (bypassing collapse and the roster gate), still guarded by the
existing showpets check so nothing happens when raidpet is disabled. Call
LayoutPets from the /pftest handler so the grid updates immediately on
toggle-on and restores to the normal layout on toggle-off.
commit 4c65c38647
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Sat Jul 25 10:34:33 2026 -0500
Remove unused RangeCache local in UnitInRange
commit 6c96bbbe6c
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Sat Jul 25 10:34:33 2026 -0500
Use Clamp() for the two-sided clamps in ui-widgets
Replace the hand-rolled math.min/math.max and if/elseif clamp idioms in the
status bar and scroll frame with Clamp(). Equivalent for every value seen
(min <= max always holds), just clearer.
commit 830e0a0be1
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Sat Jul 25 10:34:33 2026 -0500
Drop vanilla GetItemInfo shim for C_Item.GetItemInfo
The compat/vanilla.lua GetItemInfo override reshaped vanilla's tuple into
retail order by inserting nil at slot 4 (itemLevel), but truncated at 8
fields, dropping itemEquipLoc/itemTexture. ClassicAPI's global GetItemInfo
keeps the vanilla shape (its hook only warms the item cache), so the shim
stayed necessary but incomplete.
Remove it and point the three callers at ClassicAPI's C_Item.GetItemInfo,
which returns the full 18-field retail tuple. Positions 1-8 are identical
to the old shim output, so bags/roll are unchanged; character.lua's
itemSlot (equipLoc, field 9) was always nil under the truncating shim and
now resolves correctly for ShaguScore.
commit c0da63657d
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Sat Jul 25 10:09:34 2026 -0500
Various cleanup
Removes version compatibility checks for TBC/Vanilla, consolidating code to target a single WoW version. Refactors repetitive SetWidth/SetHeight calls to SetSize for cleaner code. Removes duplicate function definitions from unitxp module and simplifies conditional show/hide operations using SetShown.
commit 11a6302c2d
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Sat Jul 25 09:53:37 2026 -0500
Delegate pfUI.api.strsplit to ClassicAPI's strsplit
Replace the Lua pattern-based implementation with a thin wrapper around
ClassicAPI's C-level strsplit. Keeps the pfUI.api.strsplit entry point for
backwards compatibility with addons that call it, while dropping the
redundant reimplementation.
Behavioral note: the old version used ([^delim]+) which silently collapsed
empty fields; delegating to real strsplit now preserves them
("a,,b" -> "a", "", "b"). All in-repo callers split numeric color tuples,
version strings, or build name-keyed lookup tables, none of which are
affected by empty-field preservation.
commit 67c126eac8
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Sat Jul 25 01:01:02 2026 -0500
Bump ClassicAPI minimum version to 1.8.0
commit a6cf157518
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Sat Jul 25 00:49:50 2026 -0500
read spell rank via C_Spell.GetSpellSubtext
The rank string comes from the spell subtext ("Rank N"), which ClassicAPI's
C_Spell.GetSpellSubtext returns directly -- so drop nampower's
GetSpellRecField(spellId, "rank") here. The existing "Rank " gsub parse is
unchanged.
commit 4b1ba99b4f
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Sat Jul 25 00:33:20 2026 -0500
Move player info overlay onto ClassicAPI (drop Nampower)
The haste/spell-power overlay read both values through Nampower and disabled
itself entirely without it. Move both to ClassicAPI so it works on any
ClassicAPI client:
- Spell power: GetSpellPower("net") (nampower) -> GetSpellBonusDamage(i)
(ClassicAPI, same per-school field). Merged the build+scan into one loop;
the default school seeds the tiebreak so equal schools still favor it.
- Haste: GetUnitField("player", "modCastSpeed") -> UnitSpellHaste("player"),
which returns the haste percentage directly (the (1/modCastSpeed-1)*100
conversion is now baked in, off the same UNIT_MOD_CAST_SPEED field, signed).
- Dropped the "if not GetUnitField then return" gate -- UpdateInfoText no
longer touches Nampower, so the overlay runs everywhere.
commit 7df4aa6d50
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Sat Jul 25 00:32:44 2026 -0500
Hook the real global _G.UnitHealth for feign death
Inside a RegisterModule body, `function UnitHealth(...)` defines UnitHealth on
the pfUI environment, not the real global -- so the feign-death real-HP fix
only reached callers that resolve UnitHealth through pfUI's env, and missed
_G consumers (Blizzard frames, other addons). Hook _G.UnitHealth explicitly
(and capture oldUnitHealth from _G) so the un-gate applies everywhere.
commit b0bf2fd869
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Fri Jul 24 21:10:58 2026 -0500
Refactor nampower module
Simplify reactive spell storage by using spell IDs instead of texture/name tables. Update to use modern C_Spell APIs (GetSpellTexture, GetSpellName) instead of manual texture paths. Consolidate SetWidth/SetHeight calls to SetSize and use print() instead of DEFAULT_CHAT_FRAME:AddMessage().
commit 0b06961333
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Fri Jul 24 20:54:23 2026 -0500
Use GetNamePlateForUnit for target lookups; drop dead ScanGuid block
- Replace the three GetNamePlateForGUID(UnitGUID("target")) round-trips with
GetNamePlateForUnit("target"), which resolves the token to a GUID internally
-- no UnitGUID string detour, and it returns nil for no-target/no-plate so
the UnitExists guards collapse. GetNamePlateForGUID is left for raw-GUID cases.
- Remove the dead libunitscan.ScanGuid nameplate block (ScanGuid was deleted
from libunitscan long ago, so the guard was never true) -- it carried the
last GetUnitField("npcFlags") read.
commit 3e6210086e
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Fri Jul 24 20:54:23 2026 -0500
Drop Nampower stats system and polling from unit frames
GetUnitStats now reads health/power straight from UnitHealth/UnitPower
(the descriptor fields the server broadcasts) -- for a real unit token
GetUnitField read nothing different, so the whole Nampower-vs-fallback
apparatus was measuring a distinction that no longer exists. Removed:
- The stats system: pfUI.uf.stats, pfUIStatsFrame + UpdateStatsDisplay, the
lastUnitStats change-cache, the /pfuistats slash command, and every counter
increment (event/heartbeat/earlyReturn/throttledSkip/nampower/fallback).
- All GetUnitField health reads (GetUnitStats, heal-prediction, health-gradient
color) -- collapsed to the UnitHealth/UnitHealthMax they already fell back to.
- The heartbeat-polling backstop and its fallbackThrottle/lastEventUpdate deps.
Frames now refresh on events only; eventless frames still use their own tick,
and range/aggro still run in the eventless-actions block.
commit 63001b7b0c
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Fri Jul 24 20:09:42 2026 -0500
Move nameplates onto ClassicAPI stable nameplate tokens
ClassicAPI now assigns retail-exact, per-plate-stable "nameplateN" tokens and
fires vanilla UNIT_* events for them, so the nameplate module can key off the
token instead of Nampower/SuperWoW GUID primitives:
- Events: UNIT_AURA_GUID/UNIT_FLAGS_GUID -> UNIT_AURA/UNIT_FLAGS, matched by
the "nameplateN" token (guarded on the token prefix, since these also fire
for target/party/raid). Registered unconditionally -- no GetUnitField gate.
- Health: GetUnitField(guid, "health"/"maxHealth") -> UnitHealth/UnitHealthMax
on the plate's cached token. Same UNIT_FIELD_HEALTH the server broadcasts
(real HP on Turtle; the ~= 100 scaled-vs-real guard is unchanged).
- Combat/target in GetCombatStateColor: GetUnitField("flags") + HasFlag ->
UnitAffectingCombat(token); GetUnitField("target") + SuperWoW "<guid>target"
-> the "nameplateNtarget" chain. Dropped the now-dead HasFlag,
UNIT_FLAG_IN_COMBAT, and guidTargetTokenCache.
- GetCastInfo now takes the token directly; every caller already holds it, so
the UnitTokenFromGUID(guid) round-trip is gone (unused module-wide now).
NAME_PLATE_UNIT_ADDED caches the token as plate.nameplate.unit alongside the
GUID; REMOVED clears both. GUID is retained only as the stable cache key
(debuffCache/threatMemory/combatColorCache). Only GetUnitField("npcFlags")
remains -- it has no token/vanilla equivalent.
commit ce1c49fcbb
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Fri Jul 24 18:20:06 2026 -0500
Make focus/focustarget event-driven via ClassicAPI unit events
ClassicAPI now fires UNIT_* (health/mana/aura/...) with arg1 == "focus" and
arg1 == "focustarget", observed per-unit like target/party/raid. Both frames
already registered those events (focus/focustarget are in pfValidUnits) and
their OnEvent already matches arg1 == label, so the 0.2s polling ticks were
pure workarounds for the missing events. Drop both ticks; the frames now
refresh on-event like target, with range/glow still on the shared 0.5s state
pass and PLAYER_FOCUS_CHANGED still driving assign/clear.
Replace the previous RunNextFrame conflict check with per-addon EventUtil.ContinueOnAddOnLoaded calls in modules/macrotweak.lua so macrotweak is disabled as soon as known conflicting addons load. Simplify variable naming and ensure the disabled flag is set correctly. In skins/blizzard/macro.lua replace separate SetHeight/SetWidth calls with a single SetSize(150,22) for MacroEditButton to tidy UI sizing.
commit d63057474083aa67fda2aa28ee6301334ffc4fdf
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Mon Jul 6 00:59:17 2026 -0500
Use C_Map.GetMapOverlays instead of hardcoded pfMapOverlayData
ClassicAPI's C_Map.GetMapOverlays reads WorldMapOverlay.dbc directly and
returns the full overlay list for a zone (explored + unexplored) — the
data vanilla's GetMapOverlayInfo withholds. mapreveal now iterates it
straight (named fields: textureName/texturePath/width/height/offsets),
dropping unpack_hash and the pfMapOverlayData tables entirely.
Also fixes the explored-check: it compared the full texture path against
GetMapOverlayInfo's bare-name keys, so the magnifying glass never
suppressed on explored overlays. Now matches on the bare name.
Removes ~870 lines of hand-measured overlay data (base + Turtle).
commit ad12c8209a80cd1211fe08e83f7013d24b5efd2d
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Sun Jul 5 23:45:09 2026 -0500
utilize HookScript from ClassicAPI
pfUI's Lua hooksecurefunc lived in pfUI.env and shadowed ClassicAPI's C
global for all pfUI code. Replace it with a thin pfUI.hooksecurefunc shim
that keeps the missing-target no-op our call sites rely on (ClassicAPI
errors on a nil target) and delegates the actual hook to _G.hooksecurefunc.
Migrated all 70 internal call sites (modules/libs/skins) to
pfUI.hooksecurefunc; bare hooksecurefunc now resolves to ClassicAPI's C
version everywhere. Dropped the unused prepend path and the orphaned
pfUI.hooks table.
Adds a Fizzle-style durability label to each character pane slot.
Renders at the bottom of the slot icon as "N%" colored by the existing
DURABILITY_THRESHOLD_COLORS table (red < orange < yellow < invasion <
green), or hidden when the item has no durability stat (necks, rings,
trinkets) or the slot is empty.
api/api.lua: new pfUI.api.CreateFontString(f, key, layer, size, flags,
font) helper following the CreateBackdrop pattern — idempotent
get-or-create that attaches the FontString to the parent frame as the
named field. Defaults to pfUI.font_default at C.global.font_size with
OUTLINE on the OVERLAY layer.
skins/blizzard/character.lua: scoreText migrated from the inline
"if not frame.scoreText then ..." block to the new helper as a working
example; new durabilityText created the same way at BOTTOM/size 10.
Durability render added to RefreshCharacterSlot — runs on every
PaperDollItemSlotButton_Update so equip/unequip/repair/damage all
refresh automatically.
api/config.lua + modules/gui.lua: new C.character.inventory.durability
toggle (default "1"). GUI lives under a new top-level "Character" tab
→ "Inventory" sub-tab, leaving room for future "Reputation" / "Skills"
sub-tabs at the same level.
Drop the now-vestigial expansion plumbing.
- Delete modules/thirdparty-tbc.lua + its xml Include
- Strip 10 tbc-tagged CreateConfig calls in modules/gui.lua
- Drop the expansion arg from CreateConfig() signature + the disabled-
entry rendering path that depended on it
- Drop the showdisabled GUI toggle + its default
- Simplify pfUI:RegisterModule / pfUI:RegisterSkin to (name, func) only
- Strip the leading version arg ("vanilla:tbc", etc.) from all 114
Register call sites
- Delete the pfUI.expansion variable
Bugfixes:
- Fixed 40-yard range check not working for raid/party frames
- Fixed aggro indicator not displaying properly on raid/party frames
- Improved aggro cache to only cache positive results for instant detection
- Fixed HP/Mana not updating with "Use Raid Frames for group" enabled
- Added SuperWoW nil-check for SpellInfo
- Added missing events: PARTY_MEMBER_ENABLE, PARTY_MEMBER_DISABLE, PLAYER_UPDATE_RESTING
UI Improvements:
- Share/Hoverbind buttons now show warning when module is disabled