Skip to content

iconbrowser

CyanideX edited this page Aug 5, 2026 · 1 revision

Icon Browser

Browsable, searchable icon picker for CET mods. Renders a filterable grid of all IconGlyphs with category filtering, search, and an optional preview panel.

The module is exposed as wu.IconBrowser.

Quick Start

local wu = GetMod("WindowUtils")
local iconbrowser = wu.IconBrowser

-- Inline icon picker (in onDraw)
selected = iconbrowser.draw("my_picker", selected, function(name, glyph)
    print("Selected:", name, glyph)
end)

The browser indexes all IconGlyphs keys at load time, assigns categories via prefix matching, and renders a virtualized grid that handles 7000+ icons at 60fps.

How It Works

  1. On first use, all IconGlyphs keys are indexed and categorized by prefix matching against a built-in prefix-to-category map
  2. The prefix map is sorted longest-first so the most specific match wins (e.g. "AccountMultiple" before "Account")
  3. Icons that don't match any prefix are assigned to "Other"
  4. The grid uses manual row-skipping (only visible rows are rendered) for performance with large icon sets
  5. Per-instance state is tracked by ID, so multiple browsers can coexist independently

draw(id, selected, onSelect, opts?)

Render an inline icon browser. Creates/retrieves per-instance state keyed by id.

Parameter Type Default Description
id string - Unique instance ID
selected string|nil nil Currently selected icon name
onSelect function|nil nil Callback: onSelect(name, glyph)
opts table|nil nil Configuration overrides

Returns: string|nil, string|nil, boolean - selected icon name, resolved glyph string, whether selection changed this frame

If the icon index is not built yet (IconGlyphs empty at that point), draw renders nothing and returns selected, nil, false.

Passing a non-nil selected overwrites the instance's stored selection every frame and re-resolves the glyph from IconGlyphs, so pass nil if you want the browser to own the selection.

The three return values let you track selection changes and use the glyph directly:

local selected, glyph, changed = iconbrowser.draw("my_picker", selected)
if changed then
    myEntry.icon = selected
    myEntry.glyph = glyph
    save()
end

The onSelect callback is still available for event-driven usage:

iconbrowser.draw("my_picker", selected, function(name, glyph)
    myEntry.icon = name
    myEntry.glyph = glyph
    save()
end)

Options

Option Type Default Description
cellSize number 28 Icon cell size (1080p baseline pixels, clamped to at least 12)
showSearch boolean true Show the search bar
showCategory boolean true Show the category dropdown
showPreview boolean false Show the preview panel below the grid
layout string "fill" "fixed" for a resizable panel, anything else fills available space
gridHeight number 300 Grid height when layout is "fixed"
comboHeight number 10 Category dropdown height in items
defaultCategory string|nil nil Pre-select a category on first creation
elementIds table|nil nil Bounds reporting ids for tutorials and hints (see below)

That is the full set. There is no icon count option: the grid shows "No icons match" when the filter is empty and nothing else.

elementIds

Each sub-key registers bounds for one part of the browser so tutorials and hints can target it. All are optional.

Sub-key Targets
search The search bar (reported as a group)
combo The category dropdown
grid The icon grid panel or fill child
preview The preview panel (only when showPreview is true)
previewButton The large icon button inside the preview panel
iconbrowser.draw("picker", selected, onSelect, {
    showPreview = true,
    elementIds = {
        search = "icon_search",
        combo = "icon_category",
        grid = "icon_grid",
        preview = "icon_preview",
        previewButton = "icon_preview_btn",
    },
})

Layout Modes

fill (default): The grid expands to fill all remaining vertical space, reserving room for the preview panel if enabled.

fixed: The grid renders inside a resizable panel with a configurable height. Useful for embedding in a larger layout.

-- Fixed-height embedded browser
iconbrowser.draw("embedded", selected, onSelect, {
    layout = "fixed",
    gridHeight = 200,
    showPreview = false,
})

Preview Panel

When showPreview = true, a panel below the grid shows the selected icon's glyph, name, code reference, and category. Left-click or middle-click the preview icon or the code row to copy IconGlyphs.<Name> to the clipboard; a toast confirms the copy. Right-click does nothing. With no selection the panel shows "No icon selected".

iconbrowser.draw("picker", selected, onSelect, {
    showPreview = true,
})

Search and Filtering

The search bar filters icons by name and category (case-insensitive substring match). The whole query is treated as one literal substring, spaces included, so multi-word queries only match if the icon name or category contains that exact run of characters. The category dropdown filters to a single category, with "All" as the first entry. Both can be used together.

-- Browser with search but no category dropdown
iconbrowser.draw("simple", selected, onSelect, {
    showCategory = false,
})

Category Queries

getCategories()

Get sorted list of all category names.

Returns: table - sorted array of category name strings

This is the module's live table, not a copy. Every caller gets the same array the browser itself uses for the category dropdown and index lookups. Treat it as read-only. Sorting, inserting, or clearing it corrupts the category filter for every icon browser instance. Copy it first if you need to modify anything.

The list is empty until the index is built. The index is built at require time and retried on each call and each draw(), so an early call before IconGlyphs is populated can return an empty array.

getCategory(name)

Get the category for a given icon name.

Parameter Type Description
name string Icon name (PascalCase key from IconGlyphs)

Returns: string - category name, or "Other" if unknown

local cat = iconbrowser.getCategory("AccountCircle")  -- "Account / User"

Instance Management

reset(id?)

Reset an instance's search query, category filter, and selection. Call with no argument to reset all instances. Useful when the picker lives inside a modal that opens and closes. Unknown ids are ignored.

Reset also clears the "default category already applied" flag, so a defaultCategory in opts is applied again on the next draw().

Parameter Type Default Description
id string|nil nil Instance ID, or nil to reset all
-- Reset a specific instance
iconbrowser.reset("cat_picker")

-- Reset all instances
iconbrowser.reset()

destroy(id)

Remove all internal state for an icon browser instance, including its nested search state. Call when dynamically created browsers are no longer needed.

Parameter Type Description
id string Instance ID to clean up

Categories

Icons are categorized by prefix matching against a built-in map of ~60 categories. Categories include:

Account / User, Agriculture, Alert / Error, Alpha / Numeric, Animal, Arrange, Arrow, Audio, Automotive, Banking, Battery, Brand / Logo, Cellphone / Phone, Cloud, Clothing, Color, Currency, Database, Date / Time, Developer / Languages, Device / Tech, Drawing / Art, Edit / Modify, Emoji, Files / Folders, Food / Drink, Form, Gaming / RPG, Geographic Information System, Hardware / Tools, Health / Beauty, Holiday, Home Automation, Lock, Math, Medical / Hospital, Music, Nature, Navigation, Notification, People / Family, Photography, Places, Printer, Religion, Science, Settings, Shape, Shopping, Social Media, Sport, Text / Content / Format, Tooltip, Transportation, Vector, Video / Movie, View, Weather, and Other (fallback).

Clone this wiki locally