Class TextModeThemeRemapper
Recolors content that is already on screen when the theme changes, by rewriting any color that came from the outgoing theme to the matching color in the incoming one.
Inherited Members
Namespace: Scylla.TextMode
Assembly: ScyllaTextMode.dll
Syntax
public static class TextModeThemeRemapper
Remarks
A theme change cannot recolor text on its own. Every Cell stores its own foreground and background as packed RGBA8, and every scrollback line stores its own color, so changing what a view writes with from now on leaves everything already written exactly as it was. This class is the pass that closes that gap.
Correspondence between the two themes comes from ColorPaletteMapping, which
resolves each entry by name, then by semantic role, then by nearest color. So a cell
painted with the old theme's error red lands on the new theme's error red even when the
two palettes order their entries differently or one of them is missing an entry.
The match is on exact RGBA, which has two consequences worth knowing. A cell that happens to hold precisely the outgoing theme's error red, but was written from a truecolor escape sequence or by game code that never consulted the theme, is recolored along with the rest. Conversely a color from the 256-color cube or the gray ramp is left alone unless its fixed RGBA happens to coincide with a palette entry, because those are generated rather than palette entries. No such coincidence exists among the shipped themes, so xterm-256 output survives a theme change unchanged. There is no way to distinguish the two cases without a per-cell role index, and there is nowhere to put one: Cell is a full 32 bytes and CellAttributes has a single free bit.
Methods
BuildBackgroundMap(ColorPalette, ColorPalette, Dictionary<uint, uint>)
Builds the packed-color translation table for cell backgrounds between two themes.
Declaration
public static int BuildBackgroundMap(ColorPalette from, ColorPalette to, Dictionary<uint, uint> map)
Parameters
| Type | Name | Description |
|---|---|---|
| ColorPalette | from | The outgoing theme's palette. |
| ColorPalette | to | The incoming theme's palette. |
| Dictionary<uint, uint> | map | The dictionary to fill. Cleared first. Must not be null. |
Returns
| Type | Description |
|---|---|
| int | The number of entries written, which is zero when the two themes look identical. |
Remarks
This is BuildMap(ColorPalette, ColorPalette, Dictionary<uint, uint>) with a different entry resolved first: the outgoing theme's ENTRY_BACKGROUND, so it wins over any other entry that holds the same color. Most dark themes use one color for both ANSI black and the background. In BuildMap(ColorPalette, ColorPalette, Dictionary<uint, uint>) ANSI black wins that tie, so blank cells would take the incoming theme's black instead of its background, which shows as soon as the two differ, as they do in the WordPerfect theme.
Foreground colors keep using BuildMap(ColorPalette, ColorPalette, Dictionary<uint, uint>), so text drawn in black stays black.
BuildMap(ColorPalette, ColorPalette, Dictionary<uint, uint>)
Builds the packed-color translation table between two themes.
Declaration
public static int BuildMap(ColorPalette from, ColorPalette to, Dictionary<uint, uint> map)
Parameters
| Type | Name | Description |
|---|---|---|
| ColorPalette | from | The outgoing theme's palette. |
| ColorPalette | to | The incoming theme's palette. |
| Dictionary<uint, uint> | map | The dictionary to fill. Cleared first. Must not be null. |
Returns
| Type | Description |
|---|---|
| int | The number of entries written, which is zero when the two themes look identical. |
Remarks
Pairs that map a color to itself are left out, so switching between two themes that share most of their colors produces a small table and cannot generate a false positive on the colors they agree about.
The outgoing theme's ENTRY_FOREGROUND and ENTRY_BRIGHT_FOREGROUND entries are resolved first, so default text keeps following the default text color even when a theme uses the same color for ANSI white. Beyond those two, where two entries of the outgoing palette hold the same color, the lower index wins. That puts the ANSI block ahead of the semantic block, which is the useful way round: in a monochrome theme where several ANSI slots share one green, that green follows the ANSI mapping rather than whichever semantic entry happened to reuse it.
The same rule applies within the ANSI block, and there it loses information. A theme that collapses several ANSI slots onto one identical color, which is what a monochrome theme does, cannot be told apart on the way out: every cell holding that shared color maps to whatever the lowest-index slot resolves to, so text that was semantically red, green and blue all arrives as red. Nothing can recover the distinction, because the outgoing theme never drew it. Keep the interpolation levels of a monochrome theme pairwise distinct after quantization and this does not arise.
Cell backgrounds use BuildBackgroundMap(ColorPalette, ColorPalette, Dictionary<uint, uint>) instead, where the theme's background wins over ANSI black.
Remap(CellGrid, IReadOnlyDictionary<uint, uint>)
Rewrites every cell color in a grid that the map has an entry for.
Declaration
public static int Remap(CellGrid grid, IReadOnlyDictionary<uint, uint> map)
Parameters
| Type | Name | Description |
|---|---|---|
| CellGrid | grid | The grid to recolor. Null is ignored. |
| IReadOnlyDictionary<uint, uint> | map | The translation table from BuildMap(ColorPalette, ColorPalette, Dictionary<uint, uint>). Null or empty is a no-op. |
Returns
| Type | Description |
|---|---|
| int | The number of colors changed, counting foreground and background separately. |
Remarks
The grid is marked fully dirty when anything changed, rather than per cell. A theme switch typically touches cells scattered across the whole grid, and the grid coalesces its dirty cells into one flat range regardless, so there is nothing to be gained by being surgical about it.
Remap(CellGrid, IReadOnlyDictionary<uint, uint>, IReadOnlyDictionary<uint, uint>)
Rewrites every cell color in a grid, with one table for the foreground colors and another for the background colors.
Declaration
public static int Remap(CellGrid grid, IReadOnlyDictionary<uint, uint> foregroundMap, IReadOnlyDictionary<uint, uint> backgroundMap)
Parameters
| Type | Name | Description |
|---|---|---|
| CellGrid | grid | The grid to recolor. Null is ignored. |
| IReadOnlyDictionary<uint, uint> | foregroundMap | The table for foreground colors, from BuildMap(ColorPalette, ColorPalette, Dictionary<uint, uint>). Null or empty leaves them alone. |
| IReadOnlyDictionary<uint, uint> | backgroundMap | The table for background colors, from BuildBackgroundMap(ColorPalette, ColorPalette, Dictionary<uint, uint>). Null or empty leaves them alone. |
Returns
| Type | Description |
|---|---|
| int | The number of colors changed, counting foreground and background separately. |
Remap(Cell[], IReadOnlyDictionary<uint, uint>, IReadOnlyDictionary<uint, uint>)
Rewrites every cell color in a row of cells, with one table for the foreground colors and another for the background colors.
Declaration
public static int Remap(Cell[] cells, IReadOnlyDictionary<uint, uint> foregroundMap, IReadOnlyDictionary<uint, uint> backgroundMap)
Parameters
| Type | Name | Description |
|---|---|---|
| Cell[] | cells | The cells to recolor, such as a line from Scrollback. Null is ignored. |
| IReadOnlyDictionary<uint, uint> | foregroundMap | The table for foreground colors, from BuildMap(ColorPalette, ColorPalette, Dictionary<uint, uint>). Null or empty leaves them alone. |
| IReadOnlyDictionary<uint, uint> | backgroundMap | The table for background colors, from BuildBackgroundMap(ColorPalette, ColorPalette, Dictionary<uint, uint>). Null or empty leaves them alone. |
Returns
| Type | Description |
|---|---|
| int | The number of colors changed, counting foreground and background separately. |
Remarks
This is the overload for cells that live outside a grid, which is where the terminal emulator keeps the lines that scrolled off its screen. Nothing is marked dirty, since loose cells are not uploaded to the GPU directly.