Skip to content

feat: let widgets replace device icons with icons from the user's icon packs - #33

Merged
PyFlat-JR merged 1 commit into
mainfrom
feature/custom-device-icons
Oct 18, 2026
Merged

PyFlat-JR merged 1 commit into
mainfrom
feature/custom-device-icons

Conversation

@PyFlat-JR

Copy link
Copy Markdown
Member

What does this PR do?

Lets each widget replace a device's built-in icon with an icon from the user's Macro Deck icon packs, including packs imported from their own images. Requested on Discord, where the tablet icon read as an empty square and a G502 showed the generic battery icon.

  • Icons tab: a new tab between Devices and Appearance. It has one icon picker for each device the widget shows, in the widget's order.
    • It follows the device selection live. An empty selection shows this plugin's own devices, the same as the widget.
    • A device that is unticked keeps its icon.
    • The pickers are a UiRepeat inside a UiObjectInput keyed icons. A repeat opens no id scope, so each picker stays icons.<key>.
  • Storage: the widget data holds icons.<row id> = { "type": "icon-pack", "reference": "<icon id>" }.
    • The row id is the slot id, or ExternalBatteryDevices.RowKey for another plugin's device, because an input key takes no colon.
    • A bare icon id is accepted too: the host's picker writes one when the stored value was already a string.
    • The data schema allows icons.
  • Loading: each widget session resolves its icons in the background through UiResources.GetIconAsync, then re-renders.
    • This happens per session because a replaced icon keeps its id and gets a new hash.
    • A missing icon is logged, and the built-in glyph stays.
    • HostCallRetry now also retries a rate-limited lookup.
  • Rendering: the image replaces the glyph in the ring, the list and the tile, in its own colours. The ring, bar and percentage still carry the state colour.
    • It needs an explicit Size that fills the glyph's box. Without one, the reader draws it at its pixel size.
  • One device list: WidgetDevice (source id, icon key, label, own or external) now feeds both the device multi-select and the Icons tab. It replaces DeviceChoices.
  • Tablet glyph: now has a camera dot and a home bar.
  • Tests: new WidgetIconTests:
    • both stored shapes, and invalid ones ignored;
    • the untinted, sized image in place of the glyph;
    • the tab showing only the shown devices, in their order, and updating live when a device is ticked;
    • icons on this plugin's rows and on other providers' rows;
    • a live widget session loading an icon from the fake icon store;
    • the glyph fallback;
    • the rate-limit retry.
  • Docs: README and AGENTS.md.

Not implemented: a per-device default icon in the config flow. Macro Deck's config-flow dialog has no renderer for Icon fields (an ActionParameter.Icon shows as a plain text box), so that waits on the host. Renaming a device drops its icon, the same as the widget's device selection, because the slot id follows the name.

Checklist

  • I have read AGENTS.md and CONTRIBUTING.md.
  • dotnet build and dotnet test pass locally (0 warnings, 368 tests).
  • I updated README.md / AGENTS.md if this changes what they describe.
  • If this touches a device backend: I tested it against the real hardware (see below).

Device/hardware tested against (if applicable)

Not applicable: no device backend changes.

@PyFlat-JR
PyFlat-JR merged commit 52cd5e8 into main Oct 18, 2026
3 checks passed
@PyFlat-JR
PyFlat-JR deleted the feature/custom-device-icons branch October 18, 2026 20:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant