Live skin switching: no restart when changing skins #200

Open
opened 2026-09-30 05:30:34 +00:00 by sky_line · 0 comments
Owner

Changing the skin (Preferences > Skins) currently pops the ChangeSkin notification and only applies after a restart (LLFloaterPreference::onApplySkin / callbackApplySkin). Goal: apply a skin change completely and immediately. No restart, and no "some windows update later".

What a skin changes, and why it needs a restart today

Each part is read once, when the UI is built at startup:

  1. Colours (skins/<skin>/colors.xml): loaded into LLUIColorTable at startup (llappviewer.cpp ~2789, loadFromSettings).
  2. Textures (skins/<skin>/textures, 9-21 overrides per skin: buttons, frames, icons): LLUIImageList caches them by name; widgets hold LLUIImagePtr.
  3. Widget defaults (xui/en/widgets/*.xml, e.g. heretic floater.xml / tool_tip.xml / location_input.xml, alchemy line_editor.xml / chat_editor.xml): applied only when a widget is constructed.
  4. Layouts (XUI overrides: heretic panel_toolbar_view, panel_chiclet_bar, panel_login, panel_progress, panel_instant_message, floater_about, floater_region_restarting; alchemy panel_toolbar_view; ionic panel_progress): every floater, panel, menu and toolbar is built from XUI once. Code keeps getChild pointers, and there's no rebuild path.
  5. skin_settings.xml per-skin settings (e.g. LegacyProfile, LegacyNotificationWell) and gDirUtilp->setSkinFolder (search paths for everything above).

Plan

1. Switch the skin search path at runtime

gDirUtilp->setSkinFolder(new, language), reload skin_settings.xml, then fire one LLUI "skin changed" signal that the steps below hang off.

2. Colours: in place, instant

LLUIColor holds a pointer into the table (mColorPtr), not a copy. Reload LLUIColorTable from the new skin into the existing entries: no erase/reinsert, so pointers stay valid, and keys the new skin lacks fall back to the default skin's value. Everything recolours on the next frame.

3. Textures: in place, instant

For every cached LLUIImage, point its image at the new skin's texture of the same name, falling back to the default skin's file. Same pattern as the live font switch (3a2b25e021): keep the objects widgets point to and swap what's inside. Release the old images through the VK texture path (destruction is already deferred past in-flight frames). Also check scale-9 margins / LLUIImage params from textures.xml, which can differ per skin.

4. Widget defaults + layouts: rebuild the UI tree

A full UI rebuild, not "reopen later":

  • Floaters: record every open floater (instance key, rect, docked/torn-off, minimized, focus). Destroy all LLFloaterReg instances and rebuild them from the new XUI + widget defaults, then restore their state. Floaters with transient content (IM sessions, inventory views, profiles, edit tools) need their instance keys re-applied. Audit the ones that can't simply reconstruct and give them a save/restore hook.
  • Always-present UI: rebuild the toolbars (LLToolBarView + button commands), chat bar / nearby chat, status bar, navigation/location bar, menus (init_menus / cleanup_menus, incl. the pie menus), notification well/chiclets and the login panel. Rebuild them in dependency order.
  • Dangling pointers: code holding getChild pointers or LLHandles across the rebuild must re-fetch. Grep for cached widget pointers in singletons, e.g. gMenuHolder children, LLNavigationBar, LLStatusBar, the chiclet bar and IM containers.
  • Keyboard focus, drag-and-drop and tooltips: reset cleanly before the rebuild.

5. Preferences UI

Apply switches immediately, like fonts do now. Remove the ChangeSkin restart notification, and also remove the "restart required" tool tip on the skin list.

6. Verify

Switch through default → alchemy → heretic → ionic → user skins with many floaters open (IMs, inventory, build tools, profiles, prefs itself). There should be no crashes or leaks (ASan build), state should be restored, and the result should look identical to a fresh start with that skin.

Related: live fonts (#193, 3a2b25e021). The font switch should share the same "UI changed" relayout path.

Changing the skin (Preferences > Skins) currently pops the `ChangeSkin` notification and only applies after a restart (`LLFloaterPreference::onApplySkin` / `callbackApplySkin`). Goal: apply a skin change **completely and immediately**. No restart, and no "some windows update later". ## What a skin changes, and why it needs a restart today Each part is read once, when the UI is built at startup: 1. **Colours** (`skins/<skin>/colors.xml`): loaded into `LLUIColorTable` at startup (`llappviewer.cpp` ~2789, `loadFromSettings`). 2. **Textures** (`skins/<skin>/textures`, 9-21 overrides per skin: buttons, frames, icons): `LLUIImageList` caches them by name; widgets hold `LLUIImagePtr`. 3. **Widget defaults** (`xui/en/widgets/*.xml`, e.g. heretic `floater.xml` / `tool_tip.xml` / `location_input.xml`, alchemy `line_editor.xml` / `chat_editor.xml`): applied only when a widget is constructed. 4. **Layouts** (XUI overrides: heretic `panel_toolbar_view`, `panel_chiclet_bar`, `panel_login`, `panel_progress`, `panel_instant_message`, `floater_about`, `floater_region_restarting`; alchemy `panel_toolbar_view`; ionic `panel_progress`): every floater, panel, menu and toolbar is built from XUI once. Code keeps `getChild` pointers, and there's no rebuild path. 5. **`skin_settings.xml`** per-skin settings (e.g. `LegacyProfile`, `LegacyNotificationWell`) and `gDirUtilp->setSkinFolder` (search paths for everything above). ## Plan ### 1. Switch the skin search path at runtime `gDirUtilp->setSkinFolder(new, language)`, reload `skin_settings.xml`, then fire one `LLUI` "skin changed" signal that the steps below hang off. ### 2. Colours: in place, instant `LLUIColor` holds a pointer into the table (`mColorPtr`), not a copy. Reload `LLUIColorTable` from the new skin **into the existing entries**: no erase/reinsert, so pointers stay valid, and keys the new skin lacks fall back to the default skin's value. Everything recolours on the next frame. ### 3. Textures: in place, instant For every cached `LLUIImage`, point its image at the new skin's texture of the same name, falling back to the default skin's file. Same pattern as the live font switch (3a2b25e021): keep the objects widgets point to and swap what's inside. Release the old images through the VK texture path (destruction is already deferred past in-flight frames). Also check scale-9 margins / `LLUIImage` params from `textures.xml`, which can differ per skin. ### 4. Widget defaults + layouts: rebuild the UI tree A full UI rebuild, not "reopen later": - **Floaters:** record every open floater (instance key, rect, docked/torn-off, minimized, focus). Destroy all `LLFloaterReg` instances and rebuild them from the new XUI + widget defaults, then restore their state. Floaters with transient content (IM sessions, inventory views, profiles, edit tools) need their instance keys re-applied. Audit the ones that can't simply reconstruct and give them a save/restore hook. - **Always-present UI:** rebuild the toolbars (`LLToolBarView` + button commands), chat bar / nearby chat, status bar, navigation/location bar, menus (`init_menus` / `cleanup_menus`, incl. the pie menus), notification well/chiclets and the login panel. Rebuild them in dependency order. - **Dangling pointers:** code holding `getChild` pointers or `LLHandle`s across the rebuild must re-fetch. Grep for cached widget pointers in singletons, e.g. `gMenuHolder` children, `LLNavigationBar`, `LLStatusBar`, the chiclet bar and IM containers. - **Keyboard focus, drag-and-drop and tooltips:** reset cleanly before the rebuild. ### 5. Preferences UI Apply switches immediately, like fonts do now. Remove the `ChangeSkin` restart notification, and also remove the "restart required" tool tip on the skin list. ### 6. Verify Switch through default → alchemy → heretic → ionic → user skins with many floaters open (IMs, inventory, build tools, profiles, prefs itself). There should be no crashes or leaks (ASan build), state should be restored, and the result should look identical to a fresh start with that skin. Related: live fonts (#193, 3a2b25e021). The font switch should share the same "UI changed" relayout path.
sky_line added this to the Slipstream project 2026-10-06 15:04:15 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
sky_line/Slipstream#200
No description provided.