Commit Graph
1056 Commits
Author SHA1 Message Date
Brues 3e50a0b34f Turn on countdownForCooldowns when migrating the old cooldown settings
pfUI drew its own cooldown numbers before; the native countdown shows
them only with the countdownForCooldowns cvar on, and only the first-run
setup set it. Upgraded installs showed no numbers at all, so the
cooldown settings migration turns the cvar on as it removes
appearance.cd.
2026-11-01 00:23:39 -05:00
Brues 3faeafdfc5 Migrate the old cooldown settings into C.cooldown
The shared appearance.cd values (font, size, threshold, tenths, colors)
move to the cooldown table. "Enable Durations On Foreign Frames" and
"Hide Foreign Cooldown Animations" become the Other Addons numbers and
sweep; the spellbook and character slots take that sweep, and the
foreign numbers when "Enable Durations On Blizzard Frames" was off.

Each module's old options move to its kind: the bars, bags and totems
reverse, the buffs sweep and reverse, and the nameplate debuff text,
sweep and reverse, with "Enable Debuff Timers" off turning off both
numbers and sweep. Unit frames had one set per frame and now share one:
the target frame's timer options and the raid frames' "Show Time Left"
stand for the rest. The per-frame keys, appearance.cd and bars.cd_size
are removed.

The migration is keyed on the old keys rather than the version, so it
also runs on dev installs, and runs once.
2026-10-31 23:52:28 -05:00
Brues 5476a28982 Skin menu dividers: CreateMenuDivider draws a 1px border-colored line; use it in the radar menu 2026-10-31 21:04:11 -05:00
Brues 987c244591 Load only the client's locale strings; abbreviate numbers through ClassicAPI's numeric formatter; build the module environment once instead of on every load 2026-10-31 20:01:05 -05:00
Brues 14a26b8301 Replace saved-handler wrappers with HookScript; use intrinsic pre/postcall bindings for barbershop and SetHighlight 2026-10-31 19:47:41 -05:00
Brues b2e89b461c Hook OnClick/OnEnter/OnLeave instead of wrapping saved handlers; drop dead TBC bank-slot OnUpdate 2026-10-31 19:40:51 -05:00
Brues 907d282a68 Centralize cooldown setup and settings
Every pfUI cooldown is now made by CreateCooldown(parent, kind) in the
new api/cooldown.lua, which replaces the cooldown module. A kind names
what the cooldown times: actionbar, bags, blizzard (spellbook and
character), unitframe, indicator, nameplate, buff, tooltip, totem or
lossofcontrol. The pfCooldownType, pfCooldownStyleText,
pfCooldownStyleAnimation and pfCooldownSize fields are gone. The new
file owns everything the cooldowns share:
- the numbers' font and size
- the formatter
- each kind's numbers, sweep and sweep direction
- the GCD threshold for spell kinds
- the CooldownFrame_SetTimer hook for other addons' Cooldown frames
UpdateCooldowns applies changed settings to every cooldown at once.
GetAuraTimer moves here from the unit frames.

All settings live in one C.cooldown table, on one page: Settings ->
Cooldown. It has the shared font, size, threshold, tenths and colors,
then "Show Numbers On", "Show Sweep On" and "Reverse Sweep On" lists
with a checkbox per kind. The per-module options are removed:
- each unit frame's timer text, animation and reverse, and the
  indicators' "Show Time Left"
- the nameplate debuff timer options
- the Blizzard and foreign font sizes and toggles
- Action Bars' cooldown text size
- the reverse options of bars, bags, buffs and totems
The old appearance.cd settings are not migrated.

The player buff frame and the tooltip aura icons now show the native
countdown too, which replaces their own text and OnUpdate ticking. The
buff frame keeps its inside or below-the-icon placement. Weapon
enchants report only their time left, so they start through
SetCooldownTimeLeft: numbers, no sweep. A slim poll remains for their
charges and renewals, since no event reports those. Buff indicators
now take the aura's real duration, which gives them a correct sweep.
The spellbook and character slots now hide GCD numbers like the other
spell cooldowns.
2026-10-31 19:17:14 -05:00
Brues 93ab07a662 Show cooldown text through ClassicAPI's native countdown
The cooldown module no longer builds its own text frame with a 10 Hz
OnUpdate per cooldown. The CooldownFrame_SetTimer hook now configures
each Cooldown frame's own countdown instead: SetHideCountdownNumbers
for the per-frame text toggles and omnicc's noCooldownCount,
SetMinimumCountdownDuration for the GCD threshold, and
GetCountdownFontString for the pfUI font and size. The engine ticks
the numbers, gated by the countdownForCooldowns cvar. The cvar gets a
checkbox in the Cooldown settings, and pfUI.SetupCVars turns it on.

The text comes from one shared C_StringUtil numeric rule formatter,
given to each frame with SetCountdownFormatter. Its breakpoints keep
pfUI's format: the number in its unit's cooldown color, a unit (m/h/d)
once the unit below passes 99, and tenths in the low color when
milliseconds are on. Values now round up. GetColoredTimeString formats
through the same formatter, so the buff, buffwatch and tooltip timers
match. A config:changed callback rebuilds the breakpoints, and every
frame picks them up on its next update.

The paper-doll slots swap their indicator Models for Cooldown frames,
as the spellbook and bags already do. They and the spellbook take the
BLIZZARD tag when they are created, replacing the per-update hooks in
the skin module. Models get no numbers. Hide Foreign Cooldown
Animations clears the draw flags instead of setting alpha to 0, so the
numbers stay visible. The buff module's sweep hides its numbers
because buff.timer carries the text.
2026-10-31 18:45:17 -05:00
Brues d2cefe69cf Add a DBM-style range radar for party and raid members
/radar toggles it and /radar <yards> sets the range; the right-click
menu and the mouse wheel step through 5-40 yd. The map turns with
GetPlayerFacing so ahead is up, and ClassicAPI.UnitPosition places
class-colored dots (the mirrored name, since SuperWoW takes the global
with x, y, z order).

The range ring is two stacked TempPortraitAlphaMask discs: a ring-color
disc under a slightly smaller background-color one. It turns red when a
member is inside the range, and a list under the radar shows the five
nearest with their raid markers inline. Raid markers come from the
markN tokens, so marked mobs show too and a marked member shows the
marker instead of a dot.

Units behind terrain (UnitInLineOfSight, traced 4x a second) are
dimmed. Units on another floor, over 5 yd up or down and steeper than
45 degrees so hills don't count, get an arrow above or below them.
Hovering a blip shows its name, distance and both states; clicking a
blip or a list row targets it.
2026-10-31 17:17:35 -05:00
Brues 2b7febf617 Replace pfUI's clamp/isempty/modf with ClassicAPI's Clamp/TableIsEmpty
The world map's shift/ctrl wheel alpha and scale use Clamp, and the
fresh-install profile check uses TableIsEmpty (pfUI_init and pfUI_config
are always tables by then). modf only wrapped math.modf and had no callers.
2026-10-31 15:47:52 -05:00
Brues 3b64bfa98b Show the target LoS indicator through ClassicAPI's UnitInLineOfSight
The indicator moves from the unitxp module to the target frame, so it no
longer needs UnitXP_SP3. Its checkbox and the shared font size move to a
Target Indicators header; unitxp_font_size becomes indicator_font_size.
The unused pfUI.api.UnitInLineOfSight wrapper, which shadowed the
ClassicAPI global inside the pfUI environment, is removed.
2026-10-31 15:41:15 -05:00
Brues 217b00e9a4 Use ClassicAPI.strsplit for pfUI's strsplit 2026-10-31 15:33:13 -05:00
Brues 2d5cf94ae1 Fold librange into pfUI.api.UnitInRange 2026-10-31 15:23:13 -05:00
Brues 7ba0fedd2b Make the inspect window movable by dropping EnableMovable's addon wait 2026-10-31 15:21:18 -05:00
Brues f81ea09d1c Choose 3D, 2D or round/square class icon portraits 2026-10-31 14:43:43 -05:00
Brues e5a78c8d54 Animate unit frame bars with ClassicAPI's SmoothStatusBarMixin
Health and power bars take SmoothStatusBarMixin instead of pfUI's own OnUpdate animation, so the animation no longer runs faster at higher frame rates. The speed dropdown becomes an on/off checkbox, since the mixin's rate is fixed. A new range keeps the bar's filled share, so it doesn't jump before the animation moves on.
2026-10-31 14:20:20 -05:00
Brues 54e66fea11 Drive range, offline, aggro and combat state from events
Every unit frame re-checked its range fade, offline state, combat and
aggro glow and indicator icons on a 0.2-0.5s tick, and infight polled
aggro every 0.3s. Each now comes from an event.

Aggro: the 270-token scan and its cache are gone. UNIT_TARGET fires for
every unit in view (SuperWoW registers them all), so a table of which
hostile unit attacks which friendly one, by GUID, follows every attacker,
including ones no token reaches. A unit that comes into view mid-fight
is noted when a token first reaches it (target, focus, mouseover, a
nameplate, a member's target). A read drops attackers that left view,
where nothing fires, and a loading screen resets the table. Changes go
out as "aggro:changed" with the old and the new victim, so only the
frames showing those redraw; infight listens too.

Combat square and glow follow UNIT_FLAGS. Range fade follows ClassicAPI's
UNIT_IN_RANGE_UPDATE, registered only by frames that fade, by GUID for a
chained unit; with the 40y check off, frames fade by sight through
UNIT_DISTANCE_CHECK_UPDATE, which also swaps a portrait's question mark
for the model. A UnitXP threshold has no event and keeps its poll.
UNIT_CONNECTION refreshes a member who goes offline or comes back; the
PARTY_MEMBER_ENABLE/DISABLE branches were never registered.

A full refresh now draws the indicator icons and combat state, so a new
unit brings its own. UNIT_FACTION redraws the PvP icon and reaction
colour; its "pvp" refresh step drew nothing. Unlock mode and /pftest
re-apply the fade on entry and exit.

What stays on the tick: the pet frames and the UnitXP range check.
2026-10-31 13:10:46 -05:00
Brues 480889cb05 Drive power from UNIT_POWER_UPDATE and UNIT_MAXPOWER
1.12's UNIT_MANA, UNIT_RAGE and the rest fire for every power a unit
has, not only the one it shows, so a druid in cat or bear form redrew
every frame showing them, health colours and texts included, on each
mana tick. ClassicAPI's generic power events name the power in arg2.

Unit frames register them on their own token and redraw only for the
power their bar shows, or for mana under a druid mana bar. They fire
for tokens only, so the GUID-followed target-of-X frames keep the
per-type events; those now share one list with UNIT_AURA, so a frame
swaps auras and power on the same check.

energytick counts gains on the power it tracks alone, so a cat druid's
mana regen is no longer read as energy. It forgets the power while
hidden, so a bear's mana ticks are not measured against rage, and
re-baselines on a shift, so leaving cat form is not a fake mana gain.

The equipment manager's dining trigger wakes on mana changes only.
2026-10-31 11:55:54 -05:00
Brues b6f58b0e04 Drive auras from UNIT_AURA_DELTA
UNIT_AURA says nothing about what changed and stays silent when a buff
is refreshed or its duration packet lands, so a recast HoT kept its old
timer until something else rebuilt the row. UNIT_AURA_DELTA reports
added, updated and removed aura instances, refreshes included.

Unit frames register it on their own token. An add, a removal or a full
update rebuilds the auras as before; an update-only delta re-reads the
slot behind each shown icon for its stacks and timer and redraws the
buff indicators, without a relayout or a dispel scan. Frames that
follow a GUID (the target-of-X chain) stay on UNIT_AURA, since the
delta only fires for tokens. A finished aura layout announces itself
as "unitframe:auras".

buffwatch drops its 0.4s rescan: bars rebuild on the delta, on login
and on a target change, and a timer wakes the frame when the next aura
crosses the bar threshold. Bars re-anchor when the player or target
frame lays out its aura rows, and a whitelist or blacklist click
applies at once.

Nameplates, the loss of control alert and the equipment manager's
mount/stealth/dining check listen to the delta as well.
2026-10-31 11:32:47 -05:00
Brues 8b5a27f9be Expire cached aggro on UNIT_TARGET
UnitHasAggro caches a positive answer for 1s and a negative one for
0.3s, so the aggro glow lagged a taunt or a mob turning on a healer by
up to that long. A retarget now expires the entries it can affect: every
unit that had aggro and the retargeting unit's new target. The rest stay
cached, so the scan runs no more often. Mobs without a token of their
own fire it with their GUID, and "<guid>target" composes.
2026-10-31 06:36:42 -05:00
Brues a9542bd467 Follow target-of-target frames with UNIT_TARGET
A frame whose unit is another unit's target (targettarget,
targettargettarget, pettarget, focustarget, partyNtarget) has no token
that unit events name, so it polled: bars every 0.2s, a full aura and
portrait rescan every second, and a GUID compare to notice swaps.

Such a frame now registers its unit events by the unit's GUID, which
every unit event also fires with, and UNIT_TARGET by the GUID of every
link of its chain. A link retargeting re-points the frame at once and
queues one full refresh; the ticks are gone. The 0.2s visibility timer
stays for them, since nothing reports a unit going out of sight.

Nameplates register UNIT_TARGET on their own token, so a mob turning to
someone else recolours its combat state immediately instead of after
the colour cache and data tick run out.

Drop pfScanActive and the namebuf delay: both guarded against the old
target-swapping range scan, which no longer exists. focus.lua's
instantRefresh was never read.
2026-10-31 06:27:21 -05:00
Brues f5c08822f8 Keep empty fields and literal delimiters in strsplit
pfUI.api.strsplit (also the bare strsplit in module code) built a
"([^%s]+)" pattern from the delimiter. That dropped empty fields
("a,,b" gave "a", "b") despite the comment, and a delimiter that is a
pattern character (% ] ^ -) broke the pattern. It now scans for an
escaped character class and keeps every field, like WoW's strsplit.

The custom buff indicator list skips the empty entries this now yields
for "" and the list editor's leading "#", so an empty list no longer
scans auras.
2026-10-31 02:49:46 -05:00
Brues 6f5c225ec3 Drive unit frame visibility from events
Every unit frame re-checked its visibility five times a second. Now
group, target and pet events re-check it in OnEvent, which also runs
while a frame is hidden, and the focus module already re-checks focus
and focustarget on PLAYER_FOCUS_CHANGED.

Only the frames with a tick keep the 0.2 second poll. They show another
unit's target or a pet that can go out of sight, and no event reports
either. The focus target frame now gets the same tick as targettarget,
because no event reports the focus changing its target.

Unlock mode and /pftest have no event, so they re-check every frame
when they change. RegisterUnitWatch does not fit here. It polls on the
same 0.2 second timer, and it only shows a unit that exists, without
the pet range, raid grid and group hiding rules.
2026-10-31 02:04:20 -05:00
Brues a8d805626d Replace QueueFunction and RunOOC with RunNextFrame
RunOOC never waited for combat to end; it ran its function on the next
frame. QueueFunction ran one function every 0.2 seconds. Each caller now
defers with ClassicAPI's RunNextFrame.

The loot roll announcement sends its name lists and the roll directly.
The 0.2 second spacing did not help with the server's chat flood limit
(10 messages less than a second apart), and an announcement is at most
four messages.
2026-10-31 01:57:10 -05:00
Brues b64ae7dd53 Replace HookAddonOrVariable with an EventUtil-based HookAddon
The polling lurker frame is gone. HookAddon gates on
C_AddOns.DoesAddOnExist, waits for PLAYER_LOGIN with
EventUtil.ContinueOnPlayerLogin, then runs on
EventUtil.ContinueOnAddOnLoaded. Addons loaded at startup still hook
only after every pfUI module and skin exists and after the addon's
own VARIABLES_LOADED setup. Load-on-demand addons still hook on their
ADDON_LOADED.

The "or variable" form is gone. Callers that passed a global now pass
the addon that defines it: TheoryCraft, SuperMacro, DPSMate and
Turtle_GroupUI. DPSMate names its windows from its settings, so its
hook now checks for DPSMate_DPSMate itself, as the variable lookup
used to. eqcompare's Guda check uses HookAddon too.
2026-10-31 00:28:30 -05:00
Brues b9c260b8e1 Confirm clipboard copies with fading screen text instead of chat 2026-10-30 23:50:42 -05:00
Brues ff5deac0ec Copy links with CopyToClipboard instead of a popup
Clicking a chat URL and the GUI's Donate/GitHub/Website buttons now put the address straight on the clipboard and say so in chat. The url link goes through ClassicAPI's LinkUtil handler registry, so SetItemRef is no longer replaced. Exporting a profile also copies the string.
2026-10-30 23:45:08 -05:00
Brues 6a670ab668 Tint swing timer bars while the target is out of range
C_SwingTimer checks the range for each shown bar, and
PLAYER_SWING_RANGE_UPDATE turns a bar the new Out Of Range Color (grey by
default, since the main-hand bar and the hunter dead zone are red) while
its swing can't reach the current target. The melee check is the
server's 2D reach; the ranged one covers the hunter's minimum range too.
No check (no target, nothing to attack, no weapon) keeps the normal
color.
2026-10-30 23:31:29 -05:00
Brues 70cc994a97 Zoom, pan and per-slot undress in the dressing room
EnableClickRotate becomes EnableModelControls, the controls of 4.3.4's
ModelWithControlsTemplate: left-drag turns the model without ClassicAPI's
step animation, the mouse wheel moves the camera toward the face with
SetPortraitZoom, and right-drag pans with 4.3.4's per-race pan speed and
bounds. Showing the frame resets zoom and pan. Every model frame pfUI
skins gets them.

The dressing room gains an Undress button and a column of slot buttons
on each side, in the paperdoll's order; clicking one calls UndressSlot.
2026-10-30 23:11:09 -05:00
Brues f7680ded0b Fill textures with SetColorTexture instead of color-form SetTexture
ClassicAPI's SetColorTexture is the same engine call as SetTexture(r, g, b [, a]) under the name that says what it does. 83 calls across 25 files, renamed only.
2026-10-30 19:48:48 -05:00
Brues 6469a557ff Reuse one question dialog, backfill chat history, dim arrow icons on events
CreateQuestionDialog builds its frame once and sizes it right away from
the text's unbounded width. A frame per call broke the one-dialog guard
after the first dialog, since 1.12 binds a frame name only once. An
onclose that opens the next question runs on the next frame.

Chat history goes in with BackFillMessage, above the lines the frame
already has. SkinArrowButton dims its icon from OnEnable/OnDisable
instead of an OnUpdate per button.
2026-10-30 19:48:02 -05:00
Brues af9edb215d Show pfUI's dropdowns and menus with ClassicAPI's menu system
pfUIDropdownTemplate (a DropdownButton) replaces CreateDropDownButton in
the config GUI, addon profiles, the trigger editor and the icon picker
filter. The equipment set gear menu and the tracking menu open
MenuUtil context menus. pfUIMenuStyleMixin draws pfUI's backdrop behind
them, and the skin module makes it the default style and draws the
checkboxes and radios as pfUI boxes.
2026-10-30 19:33:40 -05:00
Brues a721f788a9 Let each cooldown frame apply its own sweep settings
The sweep options reached the frames only through the cooldown module's
CooldownFrame_SetTimer hook, which set alpha 0 for a hidden animation and
SetReverse(true) for a reversed one. With that module disabled, Show Timer
Animation and Reverse did nothing, and reverse never turned back off
without a reload. The player buff sweeps had no other path at all.

Each owner now configures its own frame. Player buffs drive their
Cooldown with SetCooldown and Clear, and only while Show Timer Animation
is on. Unit frame auras, raid aura indicators and nameplate debuffs hide
the sweep with SetDrawSwipe and SetDrawBling. Every frame with a reverse
option calls SetReverse. The hook keeps the timer text and Hide Foreign
Cooldown Animations.

Nameplate debuffs hid the sweep with alpha, which the plate's SetAlpha
copied down on every target change (ClassicAPI keeps 1.12's alpha under
default plates), so hidden sweeps came back. Their settings now come from
UpdateDebuffConfig alone.

Bag slots get a native Cooldown like bank slots, in place of the
template's indicator Model, so Reverse Cooldown Animation covers bags and
Hide Foreign Cooldown Animations no longer hides them.

The buff, unit frame, nameplate and totem options apply without a reload.

Fixes #72.
2026-10-30 10:46:00 -05:00
Brues 393b37b8c3 Add an option to show timestamps in chat copy
"Show Timestamps In Chat Copy" (chat text settings, on by default) is read
each time chat copy opens. With it off, chat copy takes each line through
pfUI.chat.StripTimestamp, which removes the exact timestamp AddMessage
prepends: the configured color and brackets, so other bracketed text
stays.

Whispers are now recolored before the timestamp goes on, not after. The
recolor turned the stamp's closing "|r " into "|r" plus the whisper color,
so whisper lines did not match. The shown text and the saved history come
out the same.
2026-10-30 01:25:36 -05:00
Brues 5d8314b2d4 Scroll the config panels with ClassicAPI's scroll bar
pfUIScrollFrameTemplate (api/ui-widgets.xml) is a ScrollFrame with
pfUIScrollBarTemplate inside its right edge and a Content scroll child,
driven by ScrollUtil.InitScrollFrameWithScrollBar. The panels keep a
ScrollFrame because the engine measures its range from the shown frames
inside the content, which the hand-placed config rows rely on. The bar
follows OnScrollRangeChanged instead of an OnUpdate poll per panel.

A panel measures its range once its rows are built, and the search
results measure theirs with UpdateScrollChildRect instead of hiding,
re-attaching and scrolling a frame later. Clearing the search measures
again too.

CreateScrollFrame and CreateScrollChild have no users left, and nothing
called CreateTabFrame or CreateTabChild; all four are gone. pfQuest's
compat layer brings its own copies of the scroll helpers.
2026-10-30 00:51:16 -05:00
Brues 3987cfb8a0 Show loot history and the icon picker in ClassicAPI ScrollBoxes
pfUIScrollBoxListTemplate (api/ui-widgets.xml) is a WowScrollBoxList with
pfUIScrollBarTemplate beside it, 5px inside the frame, so a list's
backdrop goes on the frame itself. The addon list moves onto it and drops
the sibling backdrop frame.

Loot history builds a flat list of item rows and, under an expanded item,
player rows; the view sizes and indents them. The item frame array, the
player pool, the hand-placed rows and the scroll child re-attach are gone,
and the scroll position holds across roll updates.

The icon picker's FauxScrollFrame and 80 fixed buttons become a grid view
over the provider's icon indices. Picking an icon reinitializes the shown
buttons instead of redrawing the grid, and closing the popup drops the
grid's entries along with the provider.
2026-10-30 00:47:07 -05:00
Brues 796f20eadf Show chat copy and the share box in ClassicAPI's scrolling edit box
pfUIScrollingEditBoxTemplate (api/ui-widgets.xml) is ScrollingEditBoxTemplate
with pfUIScrollBarTemplate beside the text. Its mixin's SetFont gives the
box a font object of its own, since the edit box sets its font by object on
every SetText and pfUI fonts are paths. ui-widgets.lua now loads before the
XML, which needs the mixin.

The share box no longer recomputes its scroll child on every text change.

Chat copy reads each chat frame's messages with ClassicAPI's
GetMessageInfo when it opens, so the AddMessage hook, its 100-line copy of
the history and the skip of combat frames are gone. It shows every line
the frame keeps, lines from before login included. It puts the caret at
the end to scroll to the newest line instead of three OnUpdate passes, and
Escape now leaves copy mode on every chat frame.
2026-10-30 00:38:54 -05:00
Brues 26c55bddb0 Drive the rez icon from ClassicAPI's incoming resurrections
ClassicAPI's UnitHasIncomingResurrection sees every resurrection cast in
view from SMSG_SPELL_START and ends it with the cast, and
INCOMING_RESURRECT_CHANGED signals each change. libpredict's own
tracking goes: the ress tables and 60s timers, parsing Resurrection and
RES/RESNO messages, clearing on UNIT_HEALTH, and the rez branches of the
cast handlers, along with UNIT_SPELLCAST_STOP and the foreign cast
targets only rez used. libpredict still announces the player's own
resurrections to HealComm and CTRA for clients without ClassicAPI.

The native state ends with the cast, so a rez that landed would vanish
before the target accepts it. libpredict records the SPELL_RESURRECT
combat log event per GUID and keeps the offer until the target is alive
again or the 60s accept popup runs out, unlike Grid2, which cannot tell
a landed rez from a cancelled one. UnitHasResurrection answers both.

The unit frames show the icon in RefreshIndicators, refreshed by
INCOMING_RESURRECT_CHANGED on the frame's own unit and by libpredict for
offers, instead of polling libpredict every 0.1s.
2026-10-29 23:49:28 -05:00
Brues 8c22fdb850 Show the addon list in a ClassicAPI ScrollBox
The list built one row per addon in a ScrollFrame, hid the rows out of
view by hand and drove pfUI's own slider. A WowScrollBoxList keeps rows
only for the addons in view and recycles them while scrolling.

pfUIScrollBarTemplate (api/ui-widgets.xml) is a ScrollBarMixin bar with
the old slider's look: a thin track and a flat teal thumb that hides when
nothing scrolls. Its steppers exist for the mixin but stay hidden.

Rows read the addon's enabled state, so profiles enable and disable
addons directly instead of clicking every row's checkbox. A checkbox
now shows whether an addon is enabled; it showed whether it was loaded.
2026-10-29 23:04:47 -05:00
Brues 1ccf87c61a Drop CreateGoldString's fallback for clients without coin icons
ClassicAPI always provides GetCoinTextureString, so the presence check
and the hand-built "g/s/c" string after it never ran.
2026-10-29 22:39:01 -05:00
Brues 94dcfb3dad Compute bar layout options instead of tabulating pfGridmath
pfGridmath listed every exact cols x rows grid per bar size; the only
live use was seeding the layout dropdown, which a divisor loop now does.

Everything else that read it was unreachable: every layout option is a
"cols x rows" string, so ResolveBarLayout's BarLayoutFormfactor lookup
never matched, and the actionbar module resets a stored layout that
isn't one before BarLayoutSize/BarButtonAnchor run, so their fallbacks
never fired. BarLayoutFormfactor and env/tables.lua go with it.
2026-10-29 14:16:43 -05:00
Brues 0890905eb6 Drop named-unit frames and the pfValidUnits global
CreateUnitFrame and CreateCastbar could take a character name instead of
a unit token, but every caller passes a token, so that mode and the
empty-label guards around it were dead. The OnClick TargetByName branch
could never run anyway: label was always a string, and "" is truthy.

With that gone, the aggro scan was the only reader of pfValidUnits, so
its unit list now lives in unitframes.lua and is built once at load.

/focusname resolves the name with UnitTokenFromName (exact first, then
prefix) instead of swapping the target and muting UIErrorsFrame.
2026-10-29 14:00:54 -05:00
Brues 6c76a88467 Draw unit frame bars with the native StatusBar
The custom bar sized its textures in Lua from GetWidth(), so it had to
track how ClassicAPI reports the UI scale (#71), and it stretched the bar
texture instead of cropping it. The engine's StatusBar does that layout
itself.

The background of the missing part is a second StatusBar that fills
from the other end (ClassicAPI's SetReverseFill), one frame level below
the fill, so the incoming-heal texture still draws between the two.
SetFrameLevel moves both, and the health and power bars now sit two
levels above their backdrops. SetStatusBarBackgroundTexture becomes
SetStatusBarBackgroundColor, as it only ever took a color.
2026-10-29 11:06:02 -05:00
Brues 35c32f365e Remove scaledsize fix 2026-10-29 10:38:28 -05:00
Brues 6dcb5162be bags: use ClassicAPI's native item search
C_Container.SetItemSearch drives the search box; slots dim from isFiltered on INVENTORY_SEARCH_UPDATE and on each slot update. Matches name, subclass, equip slot and equipment set names. Drops the tooltip-scanning full-text option.
2026-10-29 10:35:28 -05:00
Brues 5ab5337bbc Keep status bars true to their value on ClassicAPI 1.18.0
ClassicAPI 1.18.0 divides the UI scale out of an anchored frame's
GetWidth/GetHeight, as 4.3.4 does. The status bar still divided it out
itself, so under a UI scale below 1 the bar emptied early (at 29% health
on a 0.71 scale). Only divide on older ClassicAPI.

Fixes #71
2026-10-29 10:33:55 -05:00
0cabf2ec29 Add equipment manager auto-switching and "Unused" items panel (#70)
* Add equipment manager auto-switching and "Unused" items panel

Equipment manager (modules/equipmentmanager.lua):
- Add automatic set switching: ordered triggers (mounted, swimming,
  eating/drinking, shapeshift form/stance, zone, instance type, time
  of day, resting, stealthed, targeting a boss) equip a set; topmost
  matching trigger wins, with a 0.5s debounce
- Never swap gear in combat, while dead or casting; optionally restore
  the previous gear via a hidden "pfUI Auto-Restore" set
- Add an "Unused" side panel listing equippable items in bags, bank
  and paperdoll that no set uses, with an ignore list (tools and quest
  disguises pre-ignored), bank snapshot and bag highlighting
- Persist triggers, ignore list and bank snapshot in new per-character
  SavedVariable pfUI_eqmgr (all three .toc files)
- Add Character > Equipment Manager options (autoswitch, announce,
  minimum item quality, include bank) in gui.lua and config.lua
- Hide ignored-slot overlays when the manager frame is closed

Add frFR translations for the new equipment manager strings.

* equipmentmanager: lean on ClassicAPI for unused scan and autoswitch

- Unused panel: list items via GetInventoryItemsForSlot instead of
  hand-packing locations and walking the bags.
- Bank snapshot: record set membership while the bank is open.
  GetItemLocations reports bank items as missing once it closes, so set
  items in the bank were listed as unused.
- Autoswitch: check CanUseEquipmentSets and the UseEquipmentSet result
  before marking a trigger applied, and retry when the swap is blocked.
- Drive the engine from events (aura, form, zone, resting, target,
  regen) instead of a 4 Hz ticker; poll only swimming, time and eating.
- Detect eating/drinking by the localized names of the Food (433) and
  Drink (430) auras instead of hardcoded locale strings and icon paths.
- Drop presence guards on APIs that always exist; use strsplittable and
  strtrim for trigger values.

* equipmentmanager: wake the dining trigger on player health/mana events

Drop dining from the poll: listen to UNIT_HEALTH/UNIT_MANA for the player
while a dining trigger is enabled, so taking the set off once full is
event-driven. Register UNIT_AURA for the player only.

* equipmentmanager: split trigger values with strsplit

strsplittable needs ClassicAPI 1.17.11; strsplit has been there since
1.7.3, so this keeps the minimum where it is.

* equipmentmanager: let the trigger hint wrap

---------

Co-authored-by: Duciel <336374+Duciel@users.noreply.github.com>
Co-authored-by: Brues <5278969+brues-code@users.noreply.github.com>
2026-10-27 11:02:44 -05:00
Brues e326cea05e Add radial sweeps to buff/debuff icon display 2026-10-25 20:26:45 -05:00
Brues d9e74b3bc5 Add per-frame reverse toggle for cooldown sweeps 2026-10-25 20:24:12 -05:00
Brues bd7bf3f39f Size aura cooldown text to its laid-out icon
Unit frame aura icons only get their size from the layout pass, but
RefreshUnit started their timers before it ran. A cooldown text created
on a never-laid-out icon saw a height of 0 and fell back to a 16pt font,
so some buff and debuff timers overflowed their icons. (Closes #64)

- RefreshUnit stores each aura's timer and starts them after
  LayoutAuras, once every icon has its final size.
- With dynamic size on, the cooldown text re-sizes its font whenever
  its frame's height changed since the last SetTimer, so icons that
  switch between the normal and own aura size keep a matching font.
2026-10-25 11:48:24 -05:00