LibAceGUIWidgets
LibAceGUIWidgets is a shared AceGUI-3.0 widget and styling library — the common UI toolkit behind the TOG suite of addons. It registers reusable, drop-in AceGUI widget types and helpers so addons share one consistent look instead of each rebuilding the same frames.
This is a library, not a standalone addon. It has no UI or settings of its own — you only need it because another addon lists it as a dependency, which CurseForge installs automatically. Embeddable via LibStub.
What it provides
- ClearFrame — a movable, resizable main window with a themed title bar, saved size/position, and a bottom status bar (status text, Close button, info “i” tooltip, and an optional settings gear).
- GroupFrame — a lightweight borderless container.
- TLabel — a label + icon widget with tooltip support.
- RowList — a virtualized scrolling data list (value-aware sortable headers, alt-row banding, class-color columns, stateful action icons, per-column header tooltips, row-hover callbacks, slim scrollbar).
- Menus & scrolling — an anchored dropdown menu with cascading submenus and multi-select (check-marked rows that stay open and update live), placement control, click-outside to close, drawn above the window — plus a slim-scrollbar scroll box for content that overflows.
- Form helpers — a labelled dropdown box, a prompt/confirm dialog, and a search box (with a tokenised any-field matcher) — the pickers and inputs consumers were each hand-rolling.
- Expandable list — a scrolling datasheet of collapsible
label + valuegroups; a +/− toggle opens each to its indented child rows. Pooled rows, persistent per-group expand state — for EP breakdowns, statsheets, and any drill-down tree. - Version & update helpers — a one-call version resolver for a status-bar / about line (the TOC version with the packager’s git-tag prefix stripped) and a one-call VersionCheck-1.0 hook-up (the “a guildmate is running a newer build” nudge) — so every suite addon wires these the same way instead of hand-rolling them.
- Resize framework — drag grips, minimum and maximum bounds, a live re-layout callback while the drag is happening, and saved size, for any window — not just the library’s own frame types.
- Plus brand/style tokens and small helpers (branded labels, auto-flipping tooltips, resize bounds).
The widgets are addon-agnostic: each consumer supplies its own accent color, class-color table, and tooltip owner, so the library owns structure and styling while your addon keeps its identity. It composes with LibLocaleOverride for localized tab and dropdown fonts.
Requirements
- Ace3 (for AceGUI-3.0) — required.
- VersionCheck-1.0 — optional; only the update-reminder helper uses it, and the library works fine without it.
Recent Updates
v0.1.9 — any window can be resized, and a tooltip bug that spammed errors on hover
- Windows that could not be resized now can be. Dragging a corner to resize was something only the library’s own main-window type could do — any other panel an addon built was stuck at whatever size it opened at. Resizing is now something any window can be given, so expect more of them to have a grip in the bottom-right corner.
- A resized window re-flows while you drag it, not after you let go. The game only tells an addon about a resize once the drag ends, which is why a list would sit half-drawn until you released the mouse. Windows built on this can update as you move, and the update is capped to once per frame so dragging stays smooth rather than stuttering.
- Windows can remember their size. Resize a panel, log out, come back to it the way you left it — addons no longer have to build that themselves for each window.
- Fixed: a stream of red Lua errors when hovering some buttons. A tooltip could report “bad argument #1 to SetText” every time your cursor crossed the control, sometimes a dozen at once. Two separate faults, both in the shared tooltip helper: it accepted text it could not actually draw and then failed noisily inside the hover itself, and it stacked a fresh handler every time an addon refreshed a tooltip’s wording — so a tooltip that updates as you change a setting would fire more and more work each time you hovered it, and could show the old wording on the way past. Both fixed; a control with unusable text now simply shows no tooltip instead of erroring.
- Under the hood. The main window type now builds its resize grips through the same shared code every other window uses, so there is one implementation rather than two that happen to agree. The test suite grew from 122 checks to 160, and it now checks positions and pixels for the first time — a column header that drifts out of line with the values underneath it is the kind of fault that looks like correct data, and nothing was watching for it before.
v0.1.8 — lists can open already sorted, searchable dropdowns, named cooldowns & a peer-review round
- A list can be given its sort up front. Until now the only way a RowList got sorted was you clicking a header — so a tab that was meant to open “newest first” opened in whatever order its data happened to be in. Addons can now set the sort before the first draw, and one that told you it sorted by date really will.
- Dropdowns can carry a search box. A picker with several hundred entries (a consumable list, say) opens on the most relevant few and filters as you type, instead of being a wall you scroll. The addon decides what a query means — name, stat, item level — so each picker searches the way that makes sense for it.
- Named cooldowns. A rate-limited button now counts itself down in its own caption, dims, and refuses the click while it waits, rather than firing and being rejected somewhere you cannot see. The remaining time is computed from the clock, so a loading screen or a dropped frame cannot leave it stuck.
- Columns can show one thing and sort on another. A cell can render an icon and an item link while the column still sorts on the plain name — previously the sort arrow on such a column visibly did nothing.
- Out-of-date flags fixed on Wrath, Cataclysm and Mists. The library declared older interface versions than those clients run. That is not cosmetic for a library: addons list it as a required dependency, so a user who has not ticked “Load out of date AddOns” could find the addon itself simply missing, with nothing saying why.
- Robustness, from a peer-review round. Conditional row action icons no longer break the list they are on; a mis-declared column now says so at once instead of silently not existing; and the widgets are versioned so an addon that depends on this library reliably gets this library’s widgets. Nine review findings closed, with the test suite grown from 76 to 122 checks.
v0.1.7 — per-row action visibility & the tab glow actually shows
- Row action icons can hide on rows they don’t apply to. A RowList action may now supply a
show(entry)test, so an icon that’s only meaningful on a handful of rows — “request this from the guild bank”, say — appears on those rows alone instead of sitting greyed-out on every line. Existing actions are unaffected: leaveshowout and the icon always shows, exactly as before. - The selected-tab highlight now actually appears. The accent band added in v0.1.6 never rendered on any client — it anchored to the wrong thing and drew underneath the tab’s own graphic. Both faults are fixed; consumers need no code change, just the newer library.
v0.1.6 — RowList icon columns & branded tab glow
- RowList icon columns. A RowList column can render a small per-row texture instead of text — a wishlist coin that’s gold when the item is on your list and grey when it isn’t, a won/standby/priority marker, and the like — while the column still sorts on its value, so marked rows group together. Consumers stop hand-rolling per-cell icon textures.
- Selected-tab highlight. A one-call helper brands an AceGUI
TabGroupso the selected tab shows a soft accent highlight band behind its label, with a fainter accent band on mouse-over — the stock AceGUI tab only greys out when active, which is easy to miss. Pure styling, applied once after the tab group is created.
v0.1.5 — stateful action icons, nils-last sort & an offline test suite
- Stateful row action icons. A RowList row’s action icon can now reflect that row’s state instead of a fixed image — e.g. a wishlist coin that shows gold when the item is on your list and greyed when it isn’t — from a single texture.
- “Blanks at the bottom” sort option. A numeric column can opt into
nilsLast, keeping rows with no value pinned to the bottom in both sort directions while the real numbers sort by value — handy for an EP column where some rows have no score. - An offline test suite. The library’s frame-free logic (sorting, matching, tooltips, the version resolver, config) is now covered by a runnerless local test harness — no game client and no CI, just
lua Tests/wowapi/run.lua. No shipped API changed.
v0.1.4 — version resolver, VersionCheck hook-up & sort/tooltip fixes
- Version & update helpers. A shared version resolver for the status-bar / about line (reads the TOC version and strips the packager’s git-tag prefix) and a one-call VersionCheck-1.0 hook-up for the “a guildmate has a newer build” nudge — VersionCheck is now an optional dependency, so an addon can ship without it and simply have no reminder.
- Numeric columns sort by value. A RowList column of numbers (item level, EP, …) now sorts numerically even when the addon feeds it pre-formatted text — no more
“3”landing after“12”. - Tooltips no longer cover the control. Widget tooltips auto-flip to sit beside or below the thing you’re hovering instead of centering on top of it.
- Dropdown-box labels stay on one line instead of wrapping and spilling out of the box.
v0.1.3 — multi-select menus, settings gear & menu polish
- Multi-select dropdowns. A menu can stay open on each pick and update its checkmark live — so class/spec/tag filters work as a true multi-select instead of closing after every choice — and can be positioned from a chosen corner.
- Settings gear in the status bar. Windows can add a native gear button beside the info “i” icon (same look/spacing across the suite), lifted clear of the resize strips so it’s always clickable — a managed settings button instead of each addon hand-rolling one.
- Row action → menu anchor. A row's action button now hands itself to its click handler, so a per-row menu (e.g. an edit gear) can drop from that button instead of a fixed corner.
- Menu polish. Rows no longer crowd the top/bottom border; clicking outside now dismisses a menu from its very first open; and an open menu now closes with its window/tab instead of floating over the next one you open.
Older versions and full developer notes are in the library's CHANGELOG.md (ships with the library).
For addon authors
Everything below is for people building against the library. If you are a player, you can stop at Requirements — there is nothing here to configure.
local W = LibStub("LibAceGUIWidgets-1.0", true) -- silent: nil if not installed
if not W then return end
local minor = LibStub.minors["LibAceGUIWidgets-1.0"] or 0
if minor >= 25 then rl:SetSort("when", true) end -- open the list already sorted
-- MINOR 26: give any frame the grips, bounds, live callbacks and size persistence
if W.MakeResizable then
local h = W:MakeResizable(myWindow, {
minW = 400, minH = 200, maxW = 1200, maxH = 900,
grips = "SE S E", -- a SET of grips, not one corner; `false` for none
status = MyDB.window, -- width/height/left/top, written when a drag ends
onResize = function(f, w, ht) list:Refresh() end, -- LIVE, once per draw
onResizeStop = function(f, w, ht) rebuildSomethingSlow() end,
})
h:SetEnabled(false) -- a locked window: grips hidden, drags refused
end
- Embed by dependency, never by copying. The widget types register with AceGUI under shared names, and AceGUI resolves a duplicate registration as “keep whichever version is not lower” — so two copies at the same version are decided by load order, which neither addon controls. This library registers its types at version 29 for exactly that reason, and a test pins it. The three types are raised together: the last time one moved alone, the other two sat tied with a competing copy for an unknown length of time and nothing said so.
- Feature-gate on
LibStub.minors["LibAceGUIWidgets-1.0"]. Additions are made so an older embedded copy degrades quietly rather than erroring, so a plainif W.SomeHelper thenis enough for most helpers. The number matters when behaviour changed rather than appeared. - Theming is yours, structure is the library’s.
W:Configure{ accent, classColors, tooltipOwner }— the library never reaches into your addon for state, and hardcodes no brand colour. onResizeis the reason to useMakeResizablerather than hand-rollingStartSizing. The client only reports a resize when the drag ends, so a window whose contents must re-lay-out during the drag has nothing to hook. It fires live, throttled to at most one call per frame draw — un-throttled means a full re-render per mouse pixel, which is why “just hook OnSizeChanged” is not the answer. CallingMakeResizabletwice on one frame is idempotent: same handle back, reconfigured, never a second set of grips. If the window is also movable, its mover must writeleft/topinto the same status table — this never sees a move.W:AttachTooltipis idempotent as of MINOR 26, and that changed behaviour. It used to install a freshHookScriptpair per call, so re-attaching to refresh a tooltip’s wording stacked one handler per refresh and the first call’s stale text still drew. The text now lives on the frame and a later call updates it. It also takes only strings and numbers as text: anything else draws no line rather than raisingbad argument #1 to 'SetText'inside yourOnEnter, where the traceback names the library and not your call site.- Three RowList contracts that used to fail confusingly and are now absorbed or enforced: a conditional entry in
actions(cond and action or nil) is compacted away rather than dropping an icon or raising mid-draw; at most one column may omitwidthand a second is now a construction-time error naming it; and the sort is deterministic but not stable — exactly-equal cells may land in any order, so sort your model on a unique key if that matters to you. - An offline test suite ships with the source (not in the packaged addon):
lua Tests/wowapi/run.lua, no game client and no build step. 160 checks. It is built on the shared WoWAPITesting harness, so a consumer can write specs against this library’s real files. As of MINOR 26 it asserts real geometry — grip rects, and that every RowList header column sits over its own cells to the pixel. - The library carries two standing boards, both append-only.
docs/AUDIT.mdis the peer-review record — findings and answers, each with the concrete failure it came from; worth reading before changing RowList.docs/LIBRARY_CONTRACTS.mdis where you ask for something: consumers request, this library authors. Do not add a widget or helper by editing this library from your own repo — a shared library changed from inside whichever consumer happened to be open is a change to every consumer, decided by accident. Write the request there, stage whatever unblocks you inside your own addon, and it gets implemented, spec’d and versioned here.
Full developer notes for every release are in CHANGELOG.md, and the README.md carries the complete API tour.
Credits
Pimptasty — author. Widget code extracted and decoupled from FastGuildInvite.
Community
Bug reports, feature requests, or questions: Join the Discord.

