## What - Adds additional theming controls for the chat box and stats area - Adds `ThemeSkipChatBoxFrame()` to complement the existing `ThemeSkipStatsAreaFrame` (added in #1430) - Adds a customizable border color, `ThemeBorderColor`, that can be used to draw a border around the chat box and/or the stats area in place of the skipped ornament - Dark theme now uses the above to skip the chat box and stats area ornamental frames and to draw a border specific to the dark theme - Default theme is unchanged - related to #1425 ## Why - #1430 removed the stats area ornamental frame in dark theme, but that left the stats area floating without a border - The chat box had a border, but it used ornamental frame resources that did not look good in dark theme - The replacement border should be configurable so future themes can set their own color ## How ### New functions - `module/merintr/theme.c` - `ThemeSkipChatBoxFrame` - returns true when the active theme skips the `B*` chat box ornament. Same shape as the existing `ThemeSkipStatsAreaFrame` - `clientd3d/color.c` - `ThemeBorderColor` - per-theme border color for the stats area and chat box, or `CLR_INVALID` for the default border. Exported via `client.def` so both the client and the interface module read it. One shared color so the two borders match ### Borders Both borders use the existing `DrawBorder`, converting the color to the nearest palette index with `GetClosestPaletteIndex`. - Chat box: drawn by `TextInputDrawBorder` in `clientd3d/textin.c`. No new drawing code. The chat box already drew its own border, it just uses `ThemeBorderColor` now and keeps its focus highlight (highlight color while focused, theme color while not) - Stats area: drawn by `InterfaceDrawElements` in `module/merintr/drawint.c`. New code, since the stats area had no border before ## Notes - The border color is not a `COLOR_*` table entry. The table is for user-customizable colors. This border is a fixed per-theme decision. - The frame and the border are independent. Setting a border color without skipping the frame draws both, which may not look good. Whether to combine them is the theme author's choice. - The chat box border sits a couple of pixels outside the text area, leaving a thin gap that shows the window background. This is the existing border geometry, not new to this change. In the default theme the ornament filled that region, so the gap was hidden. With the ornament skipped it is faintly visible. It is minor so I left it out of scope for this change. ### Examples #### Chat Box (before) <img width="954" height="516" alt="image" src="https://github.com/user-attachments/assets/99e270f7-9b9e-41b6-9077-3f55d65d4243" /> - default border clashes with dark theme and is not configurable per-theme #### Chat Box (after) <img width="953" height="516" alt="image" src="https://github.com/user-attachments/assets/48207cf5-be7b-4a3a-af3a-845f56f1b795" /> #### Stat Area (before) <img width="363" height="600" alt="image" src="https://github.com/user-attachments/assets/06b31f14-d1ee-47eb-b495-84cef38e7e0d" /> - floats without a border (see lower left) #### Stat Area (after) <img width="368" height="600" alt="image" src="https://github.com/user-attachments/assets/a560acbd-afc9-42e3-b28a-677623e3115d" />
5.4 KiB
Themes
The client supports swappable UI themes selected from the Settings dialog under Interface Features. Theme swaps take effect without a restart.
Two themes ship today: Theme::Default (the original Meridian 59 palette) and Theme::Dark (a dark theme).
A theme covers two kinds of surface: colors and bitmaps.
Color theming
Theme definitions and the abstraction that hides them live in clientd3d/color.c and clientd3d/srvrstr.c. The rest of the client asks for a color or brush by name (GetColor(COLOR_X), GetBrush(COLOR_X)) and is theme-blind.
The active theme is stored in Config::theme (the Theme enum in clientd3d/config.h). Each theme keeps its customized colors in its own INI section (e.g. [Colors], [ColorsDark]) so switching themes does not clobber the other theme's saved values.
Bitmap surfaces
Bitmap theming is per-surface. A surface is themed when a _DARK (or other theme-specific) variant exists and the code path routes the ID through a resolver. Surfaces without a variant render the default art under every theme.
Three kinds of themeable bitmap surface exist:
- Tiled backgrounds: the main window background and the inventory texture.
- Wrapper ornaments: decorative corner + edge-repeater bitmaps drawn around the major UI regions.
- Button and icon art: toolbar buttons, stat-tab buttons, mailbox icon.
The wrapper ornaments live in module/merintr/drawint.c. Five groups exist today:
| Group | Wraps | Dark theme handling |
|---|---|---|
E* |
Outer client-window edge | Yes |
M* |
Minimap | Yes |
S* |
Stats area outer panel | No (solid-line border) |
B* |
Chat edit box | No (solid-line border) |
I* |
Inventory area | No (skipped for every theme) |
Optional solid-line border for stats area and chat box
A theme can skip the S* and B* ornaments and draw a thin solid-line border in their place, for when the ornament artwork does not read well against the theme's sidebar fill.
Three theme switches control this: one to skip the stats area frame, one to skip the chat box frame, and a shared border color used for both. Returning no color keeps the default border.
The frame and the border are independent. Setting a border color without skipping the frame draws both, which may not look good. Whether to combine them is the theme author's choice.
Sidebar fill
The right sidebar (enchantments, portrait, stat bars) sits inside the main window. Two fill strategies exist for the sidebar area:
- Show the main window background through any gaps between drawn elements.
- Paint the sidebar with the inventory texture so it has its own fill.
Each theme picks one. The choice depends on whether the main window background reads well behind the portrait and stat bars. For example, the dark theme paints the inventory texture across the sidebar because the dark main window background lacks contrast against the portrait; the default theme leaves the main window background visible.
flowchart LR
A[Caller paints<br/>a sidebar region] --> Q{active theme uses<br/>inventory fill?}
Q -->|yes| F[Fill with inventory texture]
Q -->|no| D[Return without painting]
style A fill:#1971c2,color:#fff
style Q fill:#b08000,color:#fff
style F fill:#333,color:#fff
style D fill:#333,color:#fff
Per-module bitmap resolvers
Each module owns its own bitmap IDs in its own resource.h. The ID values are not shared across modules, so each module ships its own resolver:
| Module | Resolver | Themed bitmaps today |
|---|---|---|
clientd3d |
MainThemeResourceId |
Main window background |
module/merintr |
InterfaceThemeResourceId |
Inventory texture; window-edge and minimap wrapper ornaments |
Other client modules (admin, char, chess, dm, mailnews) contain bitmaps but have no themed variants today.
A resolver takes a default-theme bitmap ID and returns the variant for the active theme. If the active theme has no variant for that ID, the resolver returns the input unchanged.
flowchart LR
A[Caller in clientd3d] --> M[MainThemeResourceId]
B[Caller in merintr] --> R[InterfaceThemeResourceId]
M --> Q{variant for<br/>active theme?}
R --> Q
Q -->|yes| V[return themed ID]
Q -->|no| D[return input ID]
style A fill:#1971c2,color:#fff
style B fill:#1971c2,color:#fff
style M fill:#2f9e44,color:#fff
style R fill:#2f9e44,color:#fff
style Q fill:#b08000,color:#fff
style V fill:#333,color:#fff
style D fill:#333,color:#fff
Adding a new theme
The major components to touch:
- The
Themeenum inclientd3d/config.h. - The color tables and INI machinery in
clientd3d/color.c. - The per-theme menu bar color in
ThemeMenuBarColorinclientd3d/color.c(optional). Skip to keep the system default. - The per-theme title bar style in
ThemeUsesDarkTitleBarinclientd3d/color.c(optional). Returntruefor light text on a dark title bar,falsefor dark text on a light title bar. Match this to the menu bar color for the most consistent look. - Server message colors in
clientd3d/srvrstr.c(optional). - The Settings UI: localized string in
clientd3d/client.rcand combo entry inclientd3d/preferences.c. - Bitmap variants (optional): author
_<NAME>BMP files and extend the per-module bitmap resolvers inclientd3d/color.candmodule/merintr/theme.c. - Theme capability switches in
module/merintr/theme.c.