Add unified Xresources theme engine, Default.xth export, and xthemes manager plugin

This commit is contained in:
Luxferre
2026-10-21 20:36:27 +03:00
parent f615c6ddbe
commit a31595070c
60 changed files with 3692 additions and 419 deletions
+49 -56
View File
@@ -236,10 +236,12 @@ int runFilesApp(int argc, char** argv) {
Visual* visual = DefaultVisual(dpy, screen);
Colormap cmap = DefaultColormap(dpy, screen);
ThemeSpec initSpec = loadThemeSpec(dpy, screen);
XSetWindowAttributes swa {};
swa.override_redirect = True;
swa.background_pixel = 0x222222;
swa.border_pixel = 0x005577;
swa.background_pixel = initSpec.bgMain;
swa.border_pixel = initSpec.selBg;
swa.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask |
VisibilityChangeMask | StructureNotifyMask;
@@ -261,38 +263,19 @@ int runFilesApp(int argc, char** argv) {
GC gc = XCreateGC(dpy, pix, 0, nullptr);
XftDraw* xftDraw = XftDrawCreate(dpy, pix, visual, cmap);
PangoFontMap* fontMap = pango_xft_get_font_map(dpy, screen);
PangoContext* pangoCtx = pango_font_map_create_context(fontMap);
PangoLayout* layoutMain = pango_layout_new(pangoCtx);
PangoFontDescription* descMain =
pango_font_description_from_string("Fira Code, DejaVuSansM Nerd Font 15px");
pango_layout_set_font_description(layoutMain, descMain);
PangoLayout* layoutSmall = pango_layout_new(pangoCtx);
PangoFontDescription* descSmall =
pango_font_description_from_string("Fira Code, DejaVuSansM Nerd Font 12px");
pango_layout_set_font_description(layoutSmall, descSmall);
PangoLayout* layoutIcon = pango_layout_new(pangoCtx);
PangoFontDescription* descIcon =
pango_font_description_from_string("DejaVuSansM Nerd Font, Fira Code 16px");
pango_layout_set_font_description(layoutIcon, descIcon);
auto allocColor = [&](const char* hex) {
XftColor col {};
XftColorAllocName(dpy, visual, cmap, hex, &col);
return col;
};
XftColor colWhite = allocColor("#ffffff");
XftColor colLight = allocColor("#dddddd");
XftColor colMuted = allocColor("#999999");
XftColor colYellow = allocColor("#f4d03f");
XftColor colGreen = allocColor("#2ecc71");
XftColor colBlue = allocColor("#5dade2");
XftColor colOrange = allocColor("#e67e22");
XftColor colRed = allocColor("#e74c3c");
PluginTheme pt;
pt.init(dpy, screen, visual, cmap, win);
auto& layoutMain = pt.layoutMain;
auto& layoutSmall = pt.layoutSmall;
auto& layoutIcon = pt.layoutIcon;
auto& colWhite = pt.colWhite;
auto& colLight = pt.colLight;
auto& colMuted = pt.colMuted;
auto& colYellow = pt.colYellow;
auto& colGreen = pt.colGreen;
auto& colBlue = pt.colBlue;
auto& colOrange = pt.colOrange;
auto& colRed = pt.colRed;
const char* homeEnv = std::getenv("HOME");
std::string homeDir = (homeEnv && homeEnv[0]) ? homeEnv : "/home/user";
@@ -599,9 +582,9 @@ int runFilesApp(int argc, char** argv) {
const std::string& centerLabel,
const std::string& rightLabel) {
const int barY = kWinHeight - 26;
XSetForeground(dpy, gc, 0x141414);
XSetForeground(dpy, gc, pt.spec.bgFooter);
XFillRectangle(dpy, pix, gc, 0, barY, kWinWidth, 26);
XSetForeground(dpy, gc, 0x2d2d2d);
XSetForeground(dpy, gc, pt.spec.divider);
XDrawLine(dpy, pix, gc, 0, barY, kWinWidth, barY);
drawPango(layoutMain, &colBlue, 6, barY, 26, leftLabel);
@@ -624,14 +607,15 @@ int runFilesApp(int argc, char** argv) {
auto drawUI = [&]() {
if (!visible) return;
pt.checkAndReload();
XSetForeground(dpy, gc, 0x222222);
XSetForeground(dpy, gc, pt.spec.bgMain);
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, kWinHeight);
if (mode == ViewMode::Browser) {
XSetForeground(dpy, gc, 0x161616);
XSetForeground(dpy, gc, pt.spec.bgHeader);
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, 38);
XSetForeground(dpy, gc, 0x333333);
XSetForeground(dpy, gc, pt.spec.border);
XDrawLine(dpy, pix, gc, 0, 37, kWinWidth, 37);
if (searchQuery.empty()) {
@@ -681,12 +665,13 @@ int runFilesApp(int argc, char** argv) {
bool isSel = (idx == selected);
if (isSel) {
XSetForeground(dpy, gc, 0x005577);
XSetForeground(dpy, gc, pt.spec.selBg);
XFillRectangle(dpy, pix, gc, 0, ry, kWinWidth, rowH - 1);
XSetForeground(dpy, gc, 0x0088bb);
XSetForeground(dpy, gc, pt.spec.accentAlt);
XDrawLine(dpy, pix, gc, 0, ry, kWinWidth, ry);
} else {
XSetForeground(dpy, gc, (i % 2 == 0) ? 0x222222 : 0x1d1d1d);
XSetForeground(dpy, gc,
(i % 2 == 0) ? pt.spec.bgAltEven : pt.spec.bgAltOdd);
XFillRectangle(dpy, pix, gc, 0, ry, kWinWidth, rowH - 1);
}
@@ -738,7 +723,7 @@ int runFilesApp(int argc, char** argv) {
searchQuery.empty() ? "Close" : "Clear");
} else if (mode == ViewMode::Actions) {
XSetForeground(dpy, gc, 0x161616);
XSetForeground(dpy, gc, pt.spec.bgHeader);
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, 34);
drawPango(layoutIcon, &colBlue, 6, 0, 34, kIconList);
drawPango(layoutMain, &colWhite, 28, 0, 34, "File Actions", 200);
@@ -755,7 +740,7 @@ int runFilesApp(int argc, char** argv) {
if (idx >= static_cast<int>(actionsList.size())) break;
int ry = listY + i * rowH;
bool isSel = (idx == actionSelected);
XSetForeground(dpy, gc, isSel ? 0x005577 : 0x222222);
XSetForeground(dpy, gc, isSel ? pt.spec.selBg : pt.spec.bgMain);
XFillRectangle(dpy, pix, gc, 0, ry, kWinWidth, rowH - 1);
drawPango(layoutIcon, isSel ? &colYellow : &colBlue, 8, ry, rowH,
actionsList[idx].icon);
@@ -765,7 +750,7 @@ int runFilesApp(int argc, char** argv) {
drawSoftkeyBar("Select", "Select", "Back");
} else if (mode == ViewMode::TextView) {
XSetForeground(dpy, gc, 0x161616);
XSetForeground(dpy, gc, pt.spec.bgHeader);
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, 32);
drawPango(layoutIcon, &colBlue, 6, 0, 32, kIconFileText);
drawPango(layoutMain, &colWhite, 28, 0, 32, viewingFileName, 150);
@@ -791,7 +776,7 @@ int runFilesApp(int argc, char** argv) {
drawSoftkeyBar("Edit", "Down", "Back");
} else if (mode == ViewMode::ImageView) {
XSetForeground(dpy, gc, 0x161616);
XSetForeground(dpy, gc, pt.spec.bgHeader);
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, 36);
drawPango(layoutIcon, &colGreen, 6, 1, 18, kIconImage);
drawPango(layoutMain, &colWhite, 28, 0, 20, viewingFileName, 145);
@@ -807,7 +792,7 @@ int runFilesApp(int argc, char** argv) {
} else if (mode == ViewMode::PropsView && !filteredItems.empty()) {
const auto& it = filteredItems[selected];
XSetForeground(dpy, gc, 0x161616);
XSetForeground(dpy, gc, pt.spec.bgHeader);
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, 34);
drawPango(layoutIcon, &colBlue, 6, 0, 34, kIconInfo);
drawPango(layoutMain, &colWhite, 28, 0, 34, "Properties", 200);
@@ -838,7 +823,7 @@ int runFilesApp(int argc, char** argv) {
drawSoftkeyBar("Back", "Back", "Back");
} else if (mode == ViewMode::InputModal) {
XSetForeground(dpy, gc, 0x161616);
XSetForeground(dpy, gc, pt.spec.bgHeader);
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, 36);
drawPango(layoutIcon, &colBlue, 6, 0, 36, kIconEdit);
drawPango(layoutMain, &colWhite, 28, 0, 36, inputTitle, 145);
@@ -849,9 +834,9 @@ int runFilesApp(int argc, char** argv) {
else if (caseMode == TextCaseMode::Num) mBadge = "[123]";
drawPango(layoutSmall, &colYellow, kWinWidth - 46, 0, 36, mBadge);
XSetForeground(dpy, gc, 0x182630);
XSetForeground(dpy, gc, pt.spec.bgInputFocus);
XFillRectangle(dpy, pix, gc, 10, 74, kWinWidth - 20, 38);
XSetForeground(dpy, gc, 0x0088bb);
XSetForeground(dpy, gc, pt.spec.accentAlt);
XDrawRectangle(dpy, pix, gc, 10, 74, kWinWidth - 21, 37);
drawPango(layoutMain, &colWhite, 16, 74, 38, inputValue + "|",
kWinWidth - 32);
@@ -863,7 +848,7 @@ int runFilesApp(int argc, char** argv) {
} else if (mode == ViewMode::ConfirmDelete && !filteredItems.empty()) {
const auto& it = filteredItems[selected];
XSetForeground(dpy, gc, 0x161616);
XSetForeground(dpy, gc, pt.spec.bgHeader);
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, 36);
drawPango(layoutIcon, &colRed, 8, 0, 36, kIconTrash);
drawPango(layoutMain, &colWhite, 30, 0, 36, "Delete Item?", 200);
@@ -878,8 +863,10 @@ int runFilesApp(int argc, char** argv) {
for (int i = 0; i < 2; ++i) {
int ry = 164 + i * 40;
bool isSel = (confirmSelected == i);
XSetForeground(dpy, gc,
isSel ? (i == 1 ? 0x922b21 : 0x005577) : 0x2a2a2a);
XSetForeground(
dpy, gc,
isSel ? (i == 1 ? pt.spec.dangerBg : pt.spec.selBg)
: pt.spec.bgButton);
XFillRectangle(dpy, pix, gc, 16, ry, kWinWidth - 32, 34);
drawPango(layoutMain, &colWhite, 28, ry, 34,
i == 0 ? "No, Keep Item" : "Yes, Delete", 184);
@@ -889,9 +876,9 @@ int runFilesApp(int argc, char** argv) {
int64_t tNow = nowMs();
if (!statusBanner.empty() && tNow < statusBannerUntilMs) {
XSetForeground(dpy, gc, 0x102535);
XSetForeground(dpy, gc, pt.spec.bgToast);
XFillRectangle(dpy, pix, gc, 8, kWinHeight - 58, kWinWidth - 16, 28);
XSetForeground(dpy, gc, 0x5dade2);
XSetForeground(dpy, gc, pt.spec.info);
XDrawRectangle(dpy, pix, gc, 8, kWinHeight - 58, kWinWidth - 17, 27);
drawPango(layoutSmall, &colWhite, 14, kWinHeight - 58, 28, statusBanner,
kWinWidth - 28);
@@ -1151,6 +1138,9 @@ int runFilesApp(int argc, char** argv) {
for (const auto& cmd : readFifoLines(fifoFd)) {
if (cmd == "NOP") {
continue;
} else if (cmd == "RELOAD_THEME") {
pt.reload();
if (visible) drawUI();
} else if (cmd == "CLOSE") {
hideWindow();
} else if (cmd == "OPEN") {
@@ -1384,7 +1374,10 @@ int runFilesApp(int argc, char** argv) {
int pr = ::poll(pfds, nfds, timeoutMs);
if (pr > 0 && fifoIdx >= 0 && (pfds[fifoIdx].revents & POLLIN)) {
for (const auto& cmd : readFifoLines(fifoFd)) {
if (cmd == "CLOSE") {
if (cmd == "RELOAD_THEME") {
pt.reload();
if (visible) drawUI();
} else if (cmd == "CLOSE") {
hideWindow();
} else if (cmd == "OPEN") {
showWindow("");