palette: better uniformization of builtin vs community

This commit is contained in:
Lemmy
2026-05-03 10:51:37 -04:00
parent 55c2753cbe
commit 3f0441ffb9
7 changed files with 1230 additions and 1040 deletions
File diff suppressed because it is too large Load Diff
+7 -4
View File
@@ -7,12 +7,15 @@
namespace noctalia::theme {
struct FixedPaletteMode {
Palette palette;
TerminalPalette terminal;
};
struct BuiltinPalette {
std::string_view name;
Palette dark;
Palette light;
TerminalPalette darkTerminal;
TerminalPalette lightTerminal;
FixedPaletteMode dark;
FixedPaletteMode light;
};
std::span<const BuiltinPalette> builtinPalettes();
+9 -16
View File
@@ -116,28 +116,21 @@ namespace noctalia::theme {
}
void injectTerminalColors(TokenMap& dst, const nlohmann::json& modeJson) {
if (!modeJson.contains("terminal") || !modeJson["terminal"].is_object())
if (!modeJson.contains(kTerminalJsonKey) || !modeJson[kTerminalJsonKey].is_object())
return;
const auto& terminal = modeJson["terminal"];
static constexpr std::pair<const char*, const char*> directKeys[] = {
{"foreground", "terminal_foreground"},
{"background", "terminal_background"},
{"cursor", "terminal_cursor"},
{"cursorText", "terminal_cursor_text"},
{"selectionFg", "terminal_selection_fg"},
{"selectionBg", "terminal_selection_bg"},
};
for (const auto& [jsonKey, flatKey] : directKeys) {
const auto& terminal = modeJson[kTerminalJsonKey];
for (const auto& [jsonKey, flatKey] : kTerminalDirectColorTokenKeys) {
if (terminal.contains(jsonKey) && terminal[jsonKey].is_string())
setToken(dst, flatKey, terminal[jsonKey].get<std::string>());
}
for (const char* group : {"normal", "bright"}) {
if (!terminal.contains(group) || !terminal[group].is_object())
for (const auto& group : kTerminalAnsiGroupTokenKeys) {
if (!terminal.contains(group.jsonKey) || !terminal[group.jsonKey].is_object())
continue;
for (auto it = terminal[group].begin(); it != terminal[group].end(); ++it) {
if (!it.value().is_string())
for (const auto key : kTerminalAnsiColorJsonKeys) {
const auto& groupJson = terminal[group.jsonKey];
if (!groupJson.contains(key) || !groupJson[key].is_string())
continue;
setToken(dst, std::string("terminal_") + group + "_" + it.key(), it.value().get<std::string>());
setToken(dst, std::string(group.tokenPrefix) + "_" + std::string(key), groupJson[key].get<std::string>());
}
}
}
+48 -16
View File
@@ -54,15 +54,49 @@ namespace noctalia::theme {
}
}
const ::Color& ansiColorForKey(const TerminalAnsiColors& colors, std::string_view key) {
if (key == kTerminalBlackJsonKey)
return colors.black;
if (key == kTerminalRedJsonKey)
return colors.red;
if (key == kTerminalGreenJsonKey)
return colors.green;
if (key == kTerminalYellowJsonKey)
return colors.yellow;
if (key == kTerminalBlueJsonKey)
return colors.blue;
if (key == kTerminalMagentaJsonKey)
return colors.magenta;
if (key == kTerminalCyanJsonKey)
return colors.cyan;
return colors.white;
}
const ::Color& directColorForKey(const TerminalPalette& terminal, std::string_view key) {
if (key == kTerminalForegroundJsonKey)
return terminal.foreground;
if (key == kTerminalBackgroundJsonKey)
return terminal.background;
if (key == kTerminalCursorJsonKey)
return terminal.cursor;
if (key == kTerminalCursorTextJsonKey)
return terminal.cursorText;
if (key == kTerminalSelectionFgJsonKey)
return terminal.selectionFg;
return terminal.selectionBg;
}
const TerminalAnsiColors& ansiGroupForKey(const TerminalPalette& terminal, std::string_view key) {
return key == kTerminalBrightJsonKey ? terminal.bright : terminal.normal;
}
void applyAnsiColors(TokenMap& tokens, std::string_view prefix, const TerminalAnsiColors& colors) {
setToken(tokens, std::string(prefix) + "_black", colors.black);
setToken(tokens, std::string(prefix) + "_red", colors.red);
setToken(tokens, std::string(prefix) + "_green", colors.green);
setToken(tokens, std::string(prefix) + "_yellow", colors.yellow);
setToken(tokens, std::string(prefix) + "_blue", colors.blue);
setToken(tokens, std::string(prefix) + "_magenta", colors.magenta);
setToken(tokens, std::string(prefix) + "_cyan", colors.cyan);
setToken(tokens, std::string(prefix) + "_white", colors.white);
for (const auto key : kTerminalAnsiColorJsonKeys) {
std::string tokenKey(prefix);
tokenKey += "_";
tokenKey += key;
setToken(tokens, tokenKey, ansiColorForKey(colors, key));
}
}
Color interpolateColor(const Color& a, const Color& b, double t) {
@@ -73,14 +107,12 @@ namespace noctalia::theme {
} // namespace
void applyTerminalPalette(TokenMap& tokens, const TerminalPalette& terminal) {
applyAnsiColors(tokens, "terminal_normal", terminal.normal);
applyAnsiColors(tokens, "terminal_bright", terminal.bright);
setToken(tokens, "terminal_foreground", terminal.foreground);
setToken(tokens, "terminal_background", terminal.background);
setToken(tokens, "terminal_selection_fg", terminal.selectionFg);
setToken(tokens, "terminal_selection_bg", terminal.selectionBg);
setToken(tokens, "terminal_cursor_text", terminal.cursorText);
setToken(tokens, "terminal_cursor", terminal.cursor);
for (const auto& group : kTerminalAnsiGroupTokenKeys) {
applyAnsiColors(tokens, group.tokenPrefix, ansiGroupForKey(terminal, group.jsonKey));
}
for (const auto& key : kTerminalDirectColorTokenKeys) {
setToken(tokens, key.tokenKey, directColorForKey(terminal, key.jsonKey));
}
}
void synthesizeTerminalPaletteTokens(TokenMap& tokens) {
+55
View File
@@ -3,12 +3,67 @@
#include "theme/palette.h"
#include "ui/palette.h"
#include <array>
#include <string_view>
#include <unordered_map>
namespace noctalia::theme {
using TokenMap = std::unordered_map<std::string, uint32_t>;
inline constexpr std::string_view kTerminalJsonKey = "terminal";
inline constexpr std::string_view kTerminalNormalJsonKey = "normal";
inline constexpr std::string_view kTerminalBrightJsonKey = "bright";
inline constexpr std::string_view kTerminalBlackJsonKey = "black";
inline constexpr std::string_view kTerminalRedJsonKey = "red";
inline constexpr std::string_view kTerminalGreenJsonKey = "green";
inline constexpr std::string_view kTerminalYellowJsonKey = "yellow";
inline constexpr std::string_view kTerminalBlueJsonKey = "blue";
inline constexpr std::string_view kTerminalMagentaJsonKey = "magenta";
inline constexpr std::string_view kTerminalCyanJsonKey = "cyan";
inline constexpr std::string_view kTerminalWhiteJsonKey = "white";
inline constexpr std::string_view kTerminalForegroundJsonKey = "foreground";
inline constexpr std::string_view kTerminalBackgroundJsonKey = "background";
inline constexpr std::string_view kTerminalCursorJsonKey = "cursor";
inline constexpr std::string_view kTerminalCursorTextJsonKey = "cursorText";
inline constexpr std::string_view kTerminalSelectionFgJsonKey = "selectionFg";
inline constexpr std::string_view kTerminalSelectionBgJsonKey = "selectionBg";
struct TerminalColorTokenKey {
std::string_view jsonKey;
std::string_view tokenKey;
};
inline constexpr std::array<TerminalColorTokenKey, 6> kTerminalDirectColorTokenKeys = {{
{kTerminalForegroundJsonKey, "terminal_foreground"},
{kTerminalBackgroundJsonKey, "terminal_background"},
{kTerminalCursorJsonKey, "terminal_cursor"},
{kTerminalCursorTextJsonKey, "terminal_cursor_text"},
{kTerminalSelectionFgJsonKey, "terminal_selection_fg"},
{kTerminalSelectionBgJsonKey, "terminal_selection_bg"},
}};
inline constexpr std::array<std::string_view, 8> kTerminalAnsiColorJsonKeys = {{
kTerminalBlackJsonKey,
kTerminalRedJsonKey,
kTerminalGreenJsonKey,
kTerminalYellowJsonKey,
kTerminalBlueJsonKey,
kTerminalMagentaJsonKey,
kTerminalCyanJsonKey,
kTerminalWhiteJsonKey,
}};
struct TerminalAnsiGroupTokenKey {
std::string_view jsonKey;
std::string_view tokenPrefix;
};
inline constexpr std::array<TerminalAnsiGroupTokenKey, 2> kTerminalAnsiGroupTokenKeys = {{
{kTerminalNormalJsonKey, "terminal_normal"},
{kTerminalBrightJsonKey, "terminal_bright"},
}};
struct TerminalAnsiColors {
::Color black;
::Color red;
+37 -30
View File
@@ -132,39 +132,44 @@ namespace noctalia::theme {
TerminalAnsiColors readAnsiJson(const nlohmann::json& obj) {
return TerminalAnsiColors{
.black = readColorField(obj, "black"),
.red = readColorField(obj, "red"),
.green = readColorField(obj, "green"),
.yellow = readColorField(obj, "yellow"),
.blue = readColorField(obj, "blue"),
.magenta = readColorField(obj, "magenta"),
.cyan = readColorField(obj, "cyan"),
.white = readColorField(obj, "white"),
.black = readColorField(obj, kTerminalBlackJsonKey),
.red = readColorField(obj, kTerminalRedJsonKey),
.green = readColorField(obj, kTerminalGreenJsonKey),
.yellow = readColorField(obj, kTerminalYellowJsonKey),
.blue = readColorField(obj, kTerminalBlueJsonKey),
.magenta = readColorField(obj, kTerminalMagentaJsonKey),
.cyan = readColorField(obj, kTerminalCyanJsonKey),
.white = readColorField(obj, kTerminalWhiteJsonKey),
};
}
TerminalPalette readTerminalJson(const nlohmann::json& obj) {
TerminalPalette tp{};
if (auto it = obj.find("normal"); it != obj.end() && it->is_object()) {
if (auto it = obj.find(kTerminalNormalJsonKey); it != obj.end() && it->is_object()) {
tp.normal = readAnsiJson(*it);
}
if (auto it = obj.find("bright"); it != obj.end() && it->is_object()) {
if (auto it = obj.find(kTerminalBrightJsonKey); it != obj.end() && it->is_object()) {
tp.bright = readAnsiJson(*it);
}
tp.foreground = readColorField(obj, "foreground");
tp.background = readColorField(obj, "background");
tp.selectionFg = readColorField(obj, "selectionFg");
tp.selectionBg = readColorField(obj, "selectionBg");
tp.cursorText = readColorField(obj, "cursorText");
tp.cursor = readColorField(obj, "cursor");
tp.foreground = readColorField(obj, kTerminalForegroundJsonKey);
tp.background = readColorField(obj, kTerminalBackgroundJsonKey);
tp.selectionFg = readColorField(obj, kTerminalSelectionFgJsonKey);
tp.selectionBg = readColorField(obj, kTerminalSelectionBgJsonKey);
tp.cursorText = readColorField(obj, kTerminalCursorTextJsonKey);
tp.cursor = readColorField(obj, kTerminalCursorJsonKey);
return tp;
}
std::optional<TerminalPalette> readModeTerminalJson(const nlohmann::json& obj) {
auto it = obj.find(kTerminalJsonKey);
if (it == obj.end() || !it->is_object())
return std::nullopt;
return readTerminalJson(*it);
}
struct ParsedCommunityPalette {
Palette dark;
Palette light;
TerminalPalette darkTerminal;
TerminalPalette lightTerminal;
FixedPaletteMode dark;
FixedPaletteMode light;
};
std::optional<ParsedCommunityPalette> parseCommunityPaletteJson(const std::filesystem::path& path) {
@@ -179,21 +184,25 @@ namespace noctalia::theme {
return std::nullopt;
ParsedCommunityPalette out{};
if (auto it = root.find("dark"); it != root.end() && it->is_object()) {
out.dark = readPaletteJson(*it);
out.dark.palette = readPaletteJson(*it);
if (auto terminal = readModeTerminalJson(*it)) {
out.dark.terminal = *terminal;
} else {
return std::nullopt;
}
} else {
return std::nullopt;
}
if (auto it = root.find("light"); it != root.end() && it->is_object()) {
out.light = readPaletteJson(*it);
out.light.palette = readPaletteJson(*it);
if (auto terminal = readModeTerminalJson(*it)) {
out.light.terminal = *terminal;
} else {
return std::nullopt;
}
} else {
out.light = out.dark;
}
if (auto it = root.find("darkTerminal"); it != root.end() && it->is_object()) {
out.darkTerminal = readTerminalJson(*it);
}
if (auto it = root.find("lightTerminal"); it != root.end() && it->is_object()) {
out.lightTerminal = readTerminalJson(*it);
}
return out;
} catch (const std::exception& e) {
kLog.warn("failed to parse community palette '{}': {}", path.string(), e.what());
@@ -206,8 +215,6 @@ namespace noctalia::theme {
.name = "community",
.dark = parsed.dark,
.light = parsed.light,
.darkTerminal = parsed.darkTerminal,
.lightTerminal = parsed.lightTerminal,
};
const std::string mode = resolvedModeName(cfg);
const GeneratedPalette generated = expandBuiltinPalette(bp);
+1 -1
View File
@@ -5,7 +5,7 @@
#include <string>
Palette palette = noctalia::theme::findBuiltinPalette("Noctalia")->dark;
Palette palette = noctalia::theme::findBuiltinPalette("Noctalia")->dark.palette;
namespace {