commit 3a21eba1783e1d29204d54e006fe9f5efb016fc3
parent 185426535af1a1f3ba649607d5346f59fa687803
Author: lumidify <nobody@lumidify.org>
Date: Thu, 2 Jun 2022 08:48:16 +0200
Improve theme handling
Diffstat:
18 files changed, 509 insertions(+), 406 deletions(-)
diff --git a/.ltk/theme.ini b/.ltk/theme.ini
@@ -1,32 +1,32 @@
[window]
-font_size = 15
-border_width = 0
+font-size = 15
+border-width = 0
bg = #000000
fg = #FFFFFF
font = Liberation Mono
[button]
-border_width = 2
-text_color = #FFFFFF
+border-width = 2
+text-color = #FFFFFF
pad = 5
border = #339999
fill = #113355
-border_pressed = #FFFFFF
-fill_pressed = #113355
-border_active = #FFFFFF
-fill_active = #738194
-border_disabled = #FFFFFF
-fill_disabled = #292929
+border-pressed = #FFFFFF
+fill-pressed = #113355
+border-active = #FFFFFF
+fill-active = #738194
+border-disabled = #FFFFFF
+fill-disabled = #292929
[label]
-text_color = #FFFFFF
+text-color = #FFFFFF
pad = 5
[scrollbar]
size = 15
bg = #000000
-bg_disabled = #555555
+bg-disabled = #555555
fg = #113355
-fg_pressed = #113355
-fg_active = #738194
-fg_disabled = #292929
+fg-pressed = #113355
+fg-active = #738194
+fg-disabled = #292929
diff --git a/Makefile b/Makefile
@@ -50,6 +50,7 @@ OBJ = \
src/button.o \
src/label.o \
src/menu.o \
+ src/theme.o \
src/graphics_xlib.o \
src/surface_cache.o \
$(EXTRA_OBJ)
@@ -75,6 +76,7 @@ HDR = \
src/text.h \
src/util.h \
src/menu.h \
+ src/theme.h \
src/graphics.h \
src/surface_cache.h \
src/macros.h
diff --git a/src/button.c b/src/button.c
@@ -33,6 +33,7 @@
#include "button.h"
#include "graphics.h"
#include "surface_cache.h"
+#include "theme.h"
#define MAX_BUTTON_BORDER_WIDTH 100
#define MAX_BUTTON_PADDING 500
@@ -83,64 +84,34 @@ static struct {
ltk_color fill_disabled;
} theme;
-/* FIXME: do this more efficiently (don't create color twice if
- the color is again defined in the theme file) */
-void
-ltk_button_setup_theme_defaults(ltk_window *window) {
- theme.border_width = 2;
- theme.pad = 5;
- ltk_color_create(window->dpy, window->screen, window->cm, "#FFFFFF", &theme.text_color);
- ltk_color_create(window->dpy, window->screen, window->cm, "#339999", &theme.border);
- ltk_color_create(window->dpy, window->screen, window->cm, "#113355", &theme.fill);
- ltk_color_create(window->dpy, window->screen, window->cm, "#FFFFFF", &theme.border_pressed);
- ltk_color_create(window->dpy, window->screen, window->cm, "#113355", &theme.fill_pressed);
- ltk_color_create(window->dpy, window->screen, window->cm, "#FFFFFF", &theme.border_active);
- ltk_color_create(window->dpy, window->screen, window->cm, "#738194", &theme.fill_active);
- ltk_color_create(window->dpy, window->screen, window->cm, "#FFFFFF", &theme.border_disabled);
- ltk_color_create(window->dpy, window->screen, window->cm, "#292929", &theme.fill_disabled);
+static ltk_theme_parseinfo parseinfo[] = {
+ {"border", THEME_COLOR, {.color = &theme.border}, {.color = "#339999"}, 0, 0, 0},
+ {"border-active", THEME_COLOR, {.color = &theme.border_active}, {.color = "#FFFFFF"}, 0, 0, 0},
+ {"border-disabled", THEME_COLOR, {.color = &theme.border_disabled}, {.color = "#FFFFFF"}, 0, 0, 0},
+ {"border-pressed", THEME_COLOR, {.color = &theme.border_pressed}, {.color = "#FFFFFF"}, 0, 0, 0},
+ {"border-width", THEME_INT, {.i = &theme.border_width}, {.i = 2}, 0, MAX_BUTTON_BORDER_WIDTH, 0},
+ {"fill", THEME_COLOR, {.color = &theme.fill}, {.color = "#113355"}, 0, 0, 0},
+ {"fill-active", THEME_COLOR, {.color = &theme.fill_active}, {.color = "#738194"}, 0, 0, 0},
+ {"fill-disabled", THEME_COLOR, {.color = &theme.fill_disabled}, {.color = "#292929"}, 0, 0, 0},
+ {"fill-pressed", THEME_COLOR, {.color = &theme.fill_pressed}, {.color = "#113355"}, 0, 0, 0},
+ {"pad", THEME_INT, {.i = &theme.pad}, {.i = 5}, 0, MAX_BUTTON_PADDING, 0},
+ {"text-color", THEME_COLOR, {.color = &theme.text_color}, {.color = "#FFFFFF"}, 0, 0, 0},
+};
+static int parseinfo_sorted = 0;
+
+int
+ltk_button_ini_handler(ltk_window *window, const char *prop, const char *value) {
+ return ltk_theme_handle_value(window, "button", prop, value, parseinfo, LENGTH(parseinfo), &parseinfo_sorted);
+}
+
+int
+ltk_button_fill_theme_defaults(ltk_window *window) {
+ return ltk_theme_fill_defaults(window, "button", parseinfo, LENGTH(parseinfo));
}
void
-ltk_button_ini_handler(ltk_window *window, const char *prop, const char *value) {
- const char *errstr;
- if (strcmp(prop, "border_width") == 0) {
- theme.border_width = ltk_strtonum(value, 0, MAX_BUTTON_BORDER_WIDTH, &errstr);
- if (errstr)
- ltk_warn("Invalid button border width '%s': %s.\n", value, errstr);
- } else if (strcmp(prop, "pad") == 0) {
- theme.pad = ltk_strtonum(value, 0, MAX_BUTTON_PADDING, &errstr);
- if (errstr)
- ltk_warn("Invalid button padding '%s': %s.\n", value, errstr);
- } else if (strcmp(prop, "border") == 0) {
- if (ltk_color_create(window->dpy, window->screen, window->cm, value, &theme.border))
- ltk_warn("Error setting button border color to '%s'.\n", value);
- } else if (strcmp(prop, "fill") == 0) {
- if (ltk_color_create(window->dpy, window->screen, window->cm, value, &theme.fill))
- ltk_warn("Error setting button fill color to '%s'.\n", value);
- } else if (strcmp(prop, "border_pressed") == 0) {
- if (ltk_color_create(window->dpy, window->screen, window->cm, value, &theme.border_pressed))
- ltk_warn("Error setting button pressed border color to '%s'.\n", value);
- } else if (strcmp(prop, "fill_pressed") == 0) {
- if (ltk_color_create(window->dpy, window->screen, window->cm, value, &theme.fill_pressed))
- ltk_warn("Error setting button pressed fill color to '%s'.\n", value);
- } else if (strcmp(prop, "border_active") == 0) {
- if (ltk_color_create(window->dpy, window->screen, window->cm, value, &theme.border_active))
- ltk_warn("Error setting button active border color to '%s'.\n", value);
- } else if (strcmp(prop, "fill_active") == 0) {
- if (ltk_color_create(window->dpy, window->screen, window->cm, value, &theme.fill_active))
- ltk_warn("Error setting button active fill color to '%s'.\n", value);
- } else if (strcmp(prop, "border_disabled") == 0) {
- if (ltk_color_create(window->dpy, window->screen, window->cm, value, &theme.border_disabled))
- ltk_warn("Error setting button disabled border color to '%s'.\n", value);
- } else if (strcmp(prop, "fill_disabled") == 0) {
- if (ltk_color_create(window->dpy, window->screen, window->cm, value, &theme.fill_disabled))
- ltk_warn("Error setting button disabled fill color to '%s'.\n", value);
- } else if (strcmp(prop, "text_color") == 0) {
- if (ltk_color_create(window->dpy, window->screen, window->cm, value, &theme.text_color))
- ltk_warn("Error setting button text color to '%s'.\n", value);
- } else {
- ltk_warn("Unknown property '%s' for button style.\n", prop);
- }
+ltk_button_uninitialize_theme(ltk_window *window) {
+ ltk_theme_uninitialize(window, parseinfo, LENGTH(parseinfo));
}
static void
@@ -213,7 +184,7 @@ static ltk_button *
ltk_button_create(ltk_window *window, const char *id, char *text) {
ltk_button *button = ltk_malloc(sizeof(ltk_button));
- uint16_t font_size = window->theme.font_size;
+ uint16_t font_size = window->theme->font_size;
button->tl = ltk_text_line_create(window->text_context, font_size, text, 0, -1);
int text_w, text_h;
ltk_text_line_get_size(button->tl, &text_w, &text_h);
diff --git a/src/button.h b/src/button.h
@@ -24,8 +24,9 @@ typedef struct {
ltk_text_line *tl;
} ltk_button;
-void ltk_button_setup_theme_defaults(ltk_window *window);
-void ltk_button_ini_handler(ltk_window *window, const char *prop, const char *value);
+int ltk_button_ini_handler(ltk_window *window, const char *prop, const char *value);
+int ltk_button_fill_theme_defaults(ltk_window *window);
+void ltk_button_uninitialize_theme(ltk_window *window);
int ltk_button_cmd(
ltk_window *window,
diff --git a/src/color.c b/src/color.c
@@ -26,17 +26,28 @@
/* FIXME: better error codes */
/* FIXME: I think xcolor is unneeded when xft is enabled */
int
-ltk_color_create(Display *dpy, int screen, Colormap cm, const char *hex, ltk_color *col) {
+ltk_color_create(Display *dpy, Visual *vis, Colormap cm, const char *hex, ltk_color *col) {
if (!XParseColor(dpy, cm, hex, &col->xcolor))
return 1;
if (!XAllocColor(dpy, cm, &col->xcolor))
return 1;
/* FIXME: replace with XftColorAllocValue */
#if USE_XFT == 1
- if (!XftColorAllocName(dpy, DefaultVisual(dpy, screen), cm, hex, &col->xftcolor))
+ if (!XftColorAllocName(dpy, vis, cm, hex, &col->xftcolor))
return 1;
#else
- (void)screen;
+ (void)vis;
#endif
return 0;
}
+
+void
+ltk_color_destroy(Display *dpy, Visual *vis, Colormap cm, ltk_color *col) {
+ /* FIXME: what should the 'planes' argument be? */
+ XFreeColors(dpy, cm, &col->xcolor.pixel, 1, 0);
+ #if USE_XFT == 1
+ XftColorFree(dpy, vis, cm, &col->xftcolor);
+ #else
+ (void)vis;
+ #endif
+}
diff --git a/src/color.h b/src/color.h
@@ -31,6 +31,7 @@ typedef struct {
} ltk_color;
/* returns 1 on failure, 0 on success */
-int ltk_color_create(Display *dpy, int screen, Colormap cm, const char *hex, ltk_color *col);
+int ltk_color_create(Display *dpy, Visual *vis, Colormap cm, const char *hex, ltk_color *col);
+void ltk_color_destroy(Display *dpy, Visual *vis, Colormap cm, ltk_color *col);
#endif /* _LTK_COLOR_H_ */
diff --git a/src/label.c b/src/label.c
@@ -33,6 +33,7 @@
#include "label.h"
#include "graphics.h"
#include "surface_cache.h"
+#include "theme.h"
#define MAX_LABEL_PADDING 500
@@ -68,30 +69,27 @@ static struct {
int pad;
} theme;
-void
-ltk_label_setup_theme_defaults(ltk_window *window) {
- theme.pad = 5;
- ltk_color_create(window->dpy, window->screen, window->cm, "#FFFFFF", &theme.text_color);
- ltk_color_create(window->dpy, window->screen, window->cm, "#000000", &theme.bg_color);
+int parseinfo_sorted = 0;
+
+static ltk_theme_parseinfo parseinfo[] = {
+ {"bg-color", THEME_COLOR, {.color = &theme.bg_color}, {.color = "#000000"}, 0, 0, 0},
+ {"pad", THEME_INT, {.i = &theme.pad}, {.i = 5}, 0, MAX_LABEL_PADDING, 0},
+ {"text-color", THEME_COLOR, {.color = &theme.text_color}, {.color = "#FFFFFF"}, 0, 0, 0},
+};
+
+int
+ltk_label_ini_handler(ltk_window *window, const char *prop, const char *value) {
+ return ltk_theme_handle_value(window, "label", prop, value, parseinfo, LENGTH(parseinfo), &parseinfo_sorted);
+}
+
+int
+ltk_label_fill_theme_defaults(ltk_window *window) {
+ return ltk_theme_fill_defaults(window, "label", parseinfo, LENGTH(parseinfo));
}
void
-ltk_label_ini_handler(ltk_window *window, const char *prop, const char *value) {
- const char *errstr;
- /* FIXME: store generic max padding somewhere for all widgets? */
- if (strcmp(prop, "pad") == 0) {
- theme.pad = ltk_strtonum(value, 0, MAX_LABEL_PADDING, &errstr);
- if (errstr)
- ltk_warn("Invalid label padding '%s': %s.\n", value, errstr);
- } else if (strcmp(prop, "text_color") == 0) {
- if (ltk_color_create(window->dpy, window->screen, window->cm, value, &theme.text_color))
- ltk_warn("Error setting label text color to '%s'.\n", value);
- } else if (strcmp(prop, "bg_color") == 0) {
- if (ltk_color_create(window->dpy, window->screen, window->cm, value, &theme.bg_color))
- ltk_warn("Error setting label background color to '%s'.\n", value);
- } else {
- ltk_warn("Unknown property '%s' for label style.\n", prop);
- }
+ltk_label_uninitialize_theme(ltk_window *window) {
+ ltk_theme_uninitialize(window, parseinfo, LENGTH(parseinfo));
}
static void
@@ -123,7 +121,7 @@ static ltk_label *
ltk_label_create(ltk_window *window, const char *id, char *text) {
ltk_label *label = ltk_malloc(sizeof(ltk_label));
- uint16_t font_size = window->theme.font_size;
+ uint16_t font_size = window->theme->font_size;
label->tl = ltk_text_line_create(window->text_context, font_size, text, 0, -1);
int text_w, text_h;
ltk_text_line_get_size(label->tl, &text_w, &text_h);
diff --git a/src/label.h b/src/label.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2021, 2022 lumidify <nobody@lumidify.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -24,8 +24,9 @@ typedef struct {
ltk_text_line *tl;
} ltk_label;
-void ltk_label_setup_theme_defaults(ltk_window *window);
-void ltk_label_ini_handler(ltk_window *window, const char *prop, const char *value);
+int ltk_label_ini_handler(ltk_window *window, const char *prop, const char *value);
+int ltk_label_fill_theme_defaults(ltk_window *window);
+void ltk_label_uninitialize_theme(ltk_window *window);
int ltk_label_cmd(
ltk_window *window,
diff --git a/src/ltk.h b/src/ltk.h
@@ -22,8 +22,6 @@
#include <stdint.h>
#include <X11/extensions/Xdbe.h>
#include "color.h"
-#include "widget.h"
-#include "surface_cache.h"
typedef enum {
LTK_EVENT_RESIZE = 1 << 0,
@@ -34,7 +32,7 @@ typedef enum {
typedef struct {
int border_width;
- uint16_t font_size;
+ int font_size;
char *font;
ltk_color fg;
ltk_color bg;
@@ -58,10 +56,14 @@ struct ltk_event_queue {
*/
/* FIXME: fix this ugliness; remove circular dependencies */
+typedef struct ltk_window ltk_window;
typedef struct ltk_text_context ltk_text_context;
typedef struct ltk_surface ltk_surface;
-typedef struct ltk_window {
+#include "widget.h"
+#include "surface_cache.h"
+
+struct ltk_window {
Display *dpy;
Visual *vis;
ltk_surface_cache *surface_cache;
@@ -80,7 +82,7 @@ typedef struct ltk_window {
ltk_widget *pressed_widget;
void (*other_event) (struct ltk_window *, XEvent event);
ltk_rect rect;
- ltk_window_theme theme;
+ ltk_window_theme *theme;
ltk_rect dirty_rect;
struct ltk_event_queue *first_event;
struct ltk_event_queue *last_event;
@@ -92,7 +94,7 @@ typedef struct ltk_window {
call hide for all popup widgets even if the hide function
already calls ltk_window_unregister_popup */
char popups_locked;
-} ltk_window;
+};
void ltk_window_invalidate_rect(ltk_window *window, ltk_rect rect);
int ltk_create_xcolor(ltk_window *window, const char *hex, XColor *col);
diff --git a/src/ltkd.c b/src/ltkd.c
@@ -42,6 +42,7 @@
#include "ini.h"
#include "khash.h"
+#include "theme.h"
#include "memory.h"
#include "color.h"
#include "rect.h"
@@ -59,7 +60,7 @@
#include "macros.h"
#define MAX_WINDOW_BORDER_WIDTH 100
-#define MAX_FONT_SIZE 200
+#define MAX_WINDOW_FONT_SIZE 200
#define MAX_SOCK_CONNS 20
#define READ_BLK_SIZE 128
@@ -114,7 +115,13 @@ static void ltk_destroy_window(ltk_window *window);
static void ltk_redraw_window(ltk_window *window);
static void ltk_window_other_event(ltk_window *window, XEvent event);
static void ltk_handle_event(ltk_window *window, XEvent event);
+
static void ltk_load_theme(ltk_window *window, const char *path);
+static void ltk_uninitialize_theme(ltk_window *window);
+static int ltk_ini_handler(void *window, const char *widget, const char *prop, const char *value);
+static int ltk_window_fill_theme_defaults(ltk_window *window);
+static int ltk_window_ini_handler(ltk_window *window, const char *prop, const char *value);
+
static int read_sock(struct ltk_sock_info *sock);
static int push_token(struct token_list *tl, char *token);
static int read_sock(struct ltk_sock_info *sock);
@@ -428,8 +435,10 @@ ltk_cleanup(void) {
}
ltk_widgets_cleanup();
- if (main_window)
+ if (main_window) {
+ ltk_uninitialize_theme(main_window);
ltk_destroy_window(main_window);
+ }
main_window = NULL;
}
@@ -526,7 +535,7 @@ ltk_redraw_window(ltk_window *window) {
window->dirty_rect.w -= window->dirty_rect.x + window->dirty_rect.w - window->rect.w;
if (window->dirty_rect.y + window->dirty_rect.h > window->rect.h)
window->dirty_rect.h -= window->dirty_rect.y + window->dirty_rect.h - window->rect.h;
- XSetForeground(window->dpy, window->gc, window->theme.bg.xcolor.pixel);
+ XSetForeground(window->dpy, window->gc, window->theme->bg.xcolor.pixel);
/* FIXME: this should use window->dirty_rect, but that doesn't work
properly with double buffering */
XFillRectangle(
@@ -703,6 +712,75 @@ ltk_window_unregister_all_popups(ltk_window *window) {
ltk_window_invalidate_rect(window, window->rect);
}
+ltk_window_theme window_theme;
+static ltk_theme_parseinfo theme_parseinfo[] = {
+ {"border-width", THEME_INT, {.i = &window_theme.border_width}, {.i = 0}, 0, MAX_WINDOW_BORDER_WIDTH, 0},
+ {"font-size", THEME_INT, {.i = &window_theme.font_size}, {.i = 15}, 0, MAX_WINDOW_FONT_SIZE, 0},
+ {"font", THEME_STRING, {.str = &window_theme.font}, {.str = "Liberation Mono"}, 0, 0, 0},
+ {"bg", THEME_COLOR, {.color = &window_theme.bg}, {.color = "#000000"}, 0, 0, 0},
+ {"fg", THEME_COLOR, {.color = &window_theme.fg}, {.color = "#FFFFFF"}, 0, 0, 0},
+};
+static int theme_parseinfo_sorted = 0;
+
+static int
+ltk_window_fill_theme_defaults(ltk_window *window) {
+ return ltk_theme_fill_defaults(window, "window", theme_parseinfo, LENGTH(theme_parseinfo));
+}
+
+static int
+ltk_window_ini_handler(ltk_window *window, const char *prop, const char *value) {
+ return ltk_theme_handle_value(window, "window", prop, value, theme_parseinfo, LENGTH(theme_parseinfo), &theme_parseinfo_sorted);
+}
+
+/* FIXME: standardize return codes - usually, 0 is returned on success, but ini.h
+ uses 1 on success, so this is all a bit confusing */
+static int
+ltk_ini_handler(void *window, const char *widget, const char *prop, const char *value) {
+ if (strcmp(widget, "window") == 0) {
+ ltk_window_ini_handler(window, prop, value);
+ } else if (strcmp(widget, "button") == 0) {
+ ltk_button_ini_handler(window, prop, value);
+ } else if (strcmp(widget, "label") == 0) {
+ ltk_label_ini_handler(window, prop, value);
+ } else if (strcmp(widget, "scrollbar") == 0) {
+ ltk_scrollbar_ini_handler(window, prop, value);
+ } else if (strcmp(widget, "menu") == 0) {
+ ltk_menu_ini_handler(window, prop, value);
+ } else if (strcmp(widget, "submenu") == 0) {
+ ltk_submenu_ini_handler(window, prop, value);
+ } else {
+ return 0;
+ }
+ return 1;
+}
+
+static void
+ltk_load_theme(ltk_window *window, const char *path) {
+ /* FIXME: give line number in error message */
+ if (ini_parse(path, ltk_ini_handler, window) != 0) {
+ ltk_warn("Unable to load theme.\n");
+ }
+ if (ltk_window_fill_theme_defaults(window) ||
+ ltk_button_fill_theme_defaults(window) ||
+ ltk_label_fill_theme_defaults(window) ||
+ ltk_scrollbar_fill_theme_defaults(window) ||
+ ltk_menu_fill_theme_defaults(window) ||
+ ltk_submenu_fill_theme_defaults(window)) {
+ ltk_uninitialize_theme(window);
+ ltk_fatal("Unable to load theme defaults.\n");
+ }
+}
+
+static void
+ltk_uninitialize_theme(ltk_window *window) {
+ ltk_theme_uninitialize(window, theme_parseinfo, LENGTH(theme_parseinfo));
+ ltk_button_uninitialize_theme(window);
+ ltk_label_uninitialize_theme(window);
+ ltk_scrollbar_uninitialize_theme(window);
+ ltk_menu_uninitialize_theme(window);
+ ltk_submenu_uninitialize_theme(window);
+}
+
static ltk_window *
ltk_create_window(const char *title, int x, int y, unsigned int w, unsigned int h) {
char *theme_path;
@@ -754,17 +832,16 @@ ltk_create_window(const char *title, int x, int y, unsigned int w, unsigned int
//printf("%d %d\n", WidthOfScreen(XDefaultScreenOfDisplay(window->dpy)), WidthMMOfScreen(XDefaultScreenOfDisplay(window->dpy)));
window->cm = DefaultColormap(window->dpy, window->screen);
theme_path = ltk_strcat_useful(ltk_dir, "/theme.ini");
- window->theme.font = NULL;
- if (!theme_path)
- ltk_fatal_errno("Not enough memory for theme path.\n");
+ window->theme = &window_theme;
ltk_load_theme(window, theme_path);
ltk_free(theme_path);
+ /* FIXME: fix theme memory leaks when exit happens between here and the end of this function */
window->wm_delete_msg = XInternAtom(window->dpy, "WM_DELETE_WINDOW", False);
memset(&attrs, 0, sizeof(attrs));
- attrs.background_pixel = window->theme.bg.xcolor.pixel;
+ attrs.background_pixel = window->theme->bg.xcolor.pixel;
attrs.colormap = window->cm;
- attrs.border_pixel = window->theme.fg.xcolor.pixel;
+ attrs.border_pixel = window->theme->fg.xcolor.pixel;
/* this causes the window contents to be kept
* when it is resized, leading to less flicker */
attrs.bit_gravity = NorthWestGravity;
@@ -775,7 +852,7 @@ ltk_create_window(const char *title, int x, int y, unsigned int w, unsigned int
window->depth = DefaultDepth(window->dpy, window->screen);
window->xwindow = XCreateWindow(
window->dpy, DefaultRootWindow(window->dpy), x, y,
- w, h, window->theme.border_width, window->depth,
+ w, h, window->theme->border_width, window->depth,
InputOutput, window->vis,
CWBackPixel | CWColormap | CWBitGravity | CWEventMask | CWBorderPixel, &attrs
);
@@ -789,8 +866,8 @@ ltk_create_window(const char *title, int x, int y, unsigned int w, unsigned int
}
window->drawable = window->back_buf;
window->gc = XCreateGC(window->dpy, window->xwindow, 0, 0);
- XSetForeground(window->dpy, window->gc, window->theme.fg.xcolor.pixel);
- XSetBackground(window->dpy, window->gc, window->theme.bg.xcolor.pixel);
+ XSetForeground(window->dpy, window->gc, window->theme->fg.xcolor.pixel);
+ XSetBackground(window->dpy, window->gc, window->theme->bg.xcolor.pixel);
XSetStandardProperties(
window->dpy, window->xwindow,
title, NULL, None, NULL, 0, NULL
@@ -815,7 +892,7 @@ ltk_create_window(const char *title, int x, int y, unsigned int w, unsigned int
window->dirty_rect.y = 0;
window->surface = ltk_surface_from_window(window);
- window->text_context = ltk_text_context_create(window, window->theme.font);
+ window->text_context = ltk_text_context_create(window, window->theme->font);
XClearWindow(window->dpy, window->xwindow);
XMapRaised(window->dpy, window->xwindow);
@@ -833,76 +910,10 @@ ltk_destroy_window(ltk_window *window) {
XCloseDisplay(window->dpy);
ltk_surface_destroy(window->surface);
ltk_surface_cache_destroy(window->surface_cache);
- if (window->theme.font)
- ltk_free(window->theme.font);
ltk_free(window);
}
void
-ltk_window_ini_handler(ltk_window *window, const char *prop, const char *value) {
- const char *errstr;
- if (strcmp(prop, "border_width") == 0) {
- window->theme.border_width = ltk_strtonum(value, 0, MAX_WINDOW_BORDER_WIDTH, &errstr);
- if (errstr)
- ltk_warn("Invalid window border width '%s': %s.\n", value, errstr);
- } else if (strcmp(prop, "bg") == 0) {
- if (ltk_color_create(window->dpy, window->screen, window->cm, value, &window->theme.bg))
- ltk_warn("Error setting window background color to '%s'.\n", value);
- } else if (strcmp(prop, "fg") == 0) {
- if (ltk_color_create(window->dpy, window->screen, window->cm, value, &window->theme.fg))
- ltk_warn("Error setting window foreground color to '%s'.\n", value);
- } else if (strcmp(prop, "font") == 0) {
- window->theme.font = ltk_strdup(value);
- } else if (strcmp(prop, "font_size") == 0) {
- window->theme.font_size = ltk_strtonum(value, 0, MAX_FONT_SIZE, &errstr);
- if (errstr)
- ltk_warn("Invalid window font size '%s': %s.\n", value, errstr);
- }
-}
-
-int
-ltk_ini_handler(void *window, const char *widget, const char *prop, const char *value) {
- if (strcmp(widget, "window") == 0) {
- ltk_window_ini_handler(window, prop, value);
- } else if (strcmp(widget, "button") == 0) {
- ltk_button_ini_handler(window, prop, value);
- } else if (strcmp(widget, "label") == 0) {
- ltk_label_ini_handler(window, prop, value);
- } else if (strcmp(widget, "scrollbar") == 0) {
- ltk_scrollbar_ini_handler(window, prop, value);
- } else if (strcmp(widget, "menu") == 0) {
- ltk_menu_ini_handler(window, prop, value);
- } else if (strcmp(widget, "submenu") == 0) {
- ltk_submenu_ini_handler(window, prop, value);
- } else {
- return 0;
- }
- return 1;
-}
-
-static void
-ltk_window_setup_theme_defaults(ltk_window *window) {
- window->theme.border_width = 0;
- window->theme.font_size = 15;
- window->theme.font = ltk_strdup("Liberation Mono");
- ltk_color_create(window->dpy, window->screen, window->cm, "#000000", &window->theme.bg);
- ltk_color_create(window->dpy, window->screen, window->cm, "#FFFFFF", &window->theme.fg);
-}
-
-static void
-ltk_load_theme(ltk_window *window, const char *path) {
- /* FIXME: Error checking, especially when creating colors! */
- ltk_window_setup_theme_defaults(window);
- ltk_button_setup_theme_defaults(window);
- ltk_label_setup_theme_defaults(window);
- ltk_scrollbar_setup_theme_defaults(window);
- ltk_menu_setup_theme_defaults(window);
- if (ini_parse(path, ltk_ini_handler, window) < 0) {
- ltk_warn("Can't load theme.\n");
- }
-}
-
-void
ltk_window_set_active_widget(ltk_window *window, ltk_widget *widget) {
if (window->active_widget == widget)
return;
diff --git a/src/menu.c b/src/menu.c
@@ -34,6 +34,7 @@
#include "menu.h"
#include "graphics.h"
#include "surface_cache.h"
+#include "theme.h"
#define MAX_MENU_BORDER_WIDTH 100
#define MAX_MENU_PAD 500
@@ -77,7 +78,6 @@ static struct theme {
ltk_color fill_disabled;
} menu_theme, submenu_theme;
-static void ini_handler(ltk_window *window, struct theme *t, const char *prop, const char *value);
static void ltk_menu_resize(ltk_widget *self);
static void ltk_menu_change_state(ltk_widget *self);
static void ltk_menu_draw(ltk_widget *self, ltk_rect clip);
@@ -140,189 +140,90 @@ static struct ltk_widget_vtable vtable = {
.needs_surface = 1
};
-/* FIXME: maybe just store colors as pointers and check after
- ini handling if any are null */
+static ltk_theme_parseinfo menu_parseinfo[] = {
+ {"border-width", THEME_INT, {.i = &menu_theme.border_width}, {.i = 2}, 0, MAX_MENU_BORDER_WIDTH, 0},
+ {"pad", THEME_INT, {.i = &menu_theme.pad}, {.i = 0}, 0, MAX_MENU_PAD, 0},
+ {"text-pad", THEME_INT, {.i = &menu_theme.text_pad}, {.i = 5}, 0, MAX_MENU_PAD, 0},
+ {"arrow-size", THEME_INT, {.i = &menu_theme.arrow_size}, {.i = 10}, 0, MAX_MENU_ARROW_SIZE, 0},
+ {"arrow-pad", THEME_INT, {.i = &menu_theme.arrow_pad}, {.i = 5}, 0, MAX_MENU_PAD, 0},
+ {"compress-borders", THEME_BOOL, {.b = &menu_theme.compress_borders}, {.b = 1}, 0, 0, 0},
+ {"border-sides", THEME_BORDERSIDES, {.border = &menu_theme.border_sides}, {.border = LTK_BORDER_ALL}, 0, 0, 0},
+ {"menu-border-width", THEME_INT, {.i = &menu_theme.menu_border_width}, {.i = 0}, 0, MAX_MENU_BORDER_WIDTH, 0},
+ {"background", THEME_COLOR, {.color = &menu_theme.background}, {.color = "#555555"}, 0, 0, 0},
+ {"scroll-background", THEME_COLOR, {.color = &menu_theme.scroll_background}, {.color = "#333333"}, 0, 0, 0},
+ {"scroll-arrow-color", THEME_COLOR, {.color = &menu_theme.scroll_arrow_color}, {.color = "#000000"}, 0, 0, 0},
+ {"text", THEME_COLOR, {.color = &menu_theme.text}, {.color = "#FFFFFF"}, 0, 0, 0},
+ {"border", THEME_COLOR, {.color = &menu_theme.border}, {.color = "#339999"}, 0, 0, 0},
+ {"fill", THEME_COLOR, {.color = &menu_theme.fill}, {.color = "#113355"}, 0, 0, 0},
+ {"text-pressed", THEME_COLOR, {.color = &menu_theme.text_pressed}, {.color = "#FFFFFF"}, 0, 0, 0},
+ {"border-pressed", THEME_COLOR, {.color = &menu_theme.border_pressed}, {.color = "#FFFFFF"}, 0, 0, 0},
+ {"fill-pressed", THEME_COLOR, {.color = &menu_theme.fill_pressed}, {.color = "#113355"}, 0, 0, 0},
+ {"text-active", THEME_COLOR, {.color = &menu_theme.text_active}, {.color = "#FFFFFF"}, 0, 0, 0},
+ {"border-active", THEME_COLOR, {.color = &menu_theme.border_active}, {.color = "#FFFFFF"}, 0, 0, 0},
+ {"fill-active", THEME_COLOR, {.color = &menu_theme.fill_active}, {.color = "#738194"}, 0, 0, 0},
+ {"text-disabled", THEME_COLOR, {.color = &menu_theme.text_disabled}, {.color = "#FFFFFF"}, 0, 0, 0},
+ {"border-disabled", THEME_COLOR, {.color = &menu_theme.border_disabled}, {.color = "#FFFFFF"}, 0, 0, 0},
+ {"fill-disabled", THEME_COLOR, {.color = &menu_theme.fill_disabled}, {.color = "#292929"}, 0, 0, 0},
+ {"menu-border", THEME_COLOR, {.color = &menu_theme.menu_border}, {.color = "#000000"}, 0, 0, 0},
+};
+static int menu_parseinfo_sorted = 0;
-void
-ltk_menu_setup_theme_defaults(ltk_window *window) {
- menu_theme.border_width = 2;
- menu_theme.pad = 0;
- menu_theme.text_pad = 5;
- menu_theme.arrow_size = 10;
- menu_theme.arrow_pad = 5;
- menu_theme.compress_borders = 1;
- menu_theme.border_sides = LTK_BORDER_ALL;
- menu_theme.menu_border_width = 0;
- ltk_color_create(window->dpy, window->screen, window->cm, "#555555", &menu_theme.background);
- ltk_color_create(window->dpy, window->screen, window->cm, "#333333", &menu_theme.scroll_background);
- ltk_color_create(window->dpy, window->screen, window->cm, "#000000", &menu_theme.scroll_arrow_color);
- ltk_color_create(window->dpy, window->screen, window->cm, "#FFFFFF", &menu_theme.text);
- ltk_color_create(window->dpy, window->screen, window->cm, "#339999", &menu_theme.border);
- ltk_color_create(window->dpy, window->screen, window->cm, "#113355", &menu_theme.fill);
- ltk_color_create(window->dpy, window->screen, window->cm, "#FFFFFF", &menu_theme.text_pressed);
- ltk_color_create(window->dpy, window->screen, window->cm, "#FFFFFF", &menu_theme.border_pressed);
- ltk_color_create(window->dpy, window->screen, window->cm, "#113355", &menu_theme.fill_pressed);
- ltk_color_create(window->dpy, window->screen, window->cm, "#FFFFFF", &menu_theme.text_active);
- ltk_color_create(window->dpy, window->screen, window->cm, "#FFFFFF", &menu_theme.border_active);
- ltk_color_create(window->dpy, window->screen, window->cm, "#738194", &menu_theme.fill_active);
- ltk_color_create(window->dpy, window->screen, window->cm, "#FFFFFF", &menu_theme.text_disabled);
- ltk_color_create(window->dpy, window->screen, window->cm, "#FFFFFF", &menu_theme.border_disabled);
- ltk_color_create(window->dpy, window->screen, window->cm, "#292929", &menu_theme.fill_disabled);
-
- /* FIXME: actually unnecessary since border width is 0 */
- ltk_color_create(window->dpy, window->screen, window->cm, "#000000", &menu_theme.menu_border);
-
- submenu_theme.border_width = 0;
- submenu_theme.pad = 5;
- submenu_theme.text_pad = 5;
- submenu_theme.arrow_size = 10;
- submenu_theme.arrow_pad = 5;
- submenu_theme.compress_borders = 0;
- submenu_theme.border_sides = LTK_BORDER_NONE;
- submenu_theme.menu_border_width = 1;
- ltk_color_create(window->dpy, window->screen, window->cm, "#555555", &submenu_theme.background);
- ltk_color_create(window->dpy, window->screen, window->cm, "#333333", &submenu_theme.scroll_background);
- ltk_color_create(window->dpy, window->screen, window->cm, "#000000", &submenu_theme.scroll_arrow_color);
- ltk_color_create(window->dpy, window->screen, window->cm, "#FFFFFF", &submenu_theme.menu_border);
- ltk_color_create(window->dpy, window->screen, window->cm, "#FFFFFF", &submenu_theme.text);
- ltk_color_create(window->dpy, window->screen, window->cm, "#113355", &submenu_theme.fill);
- ltk_color_create(window->dpy, window->screen, window->cm, "#000000", &submenu_theme.text_pressed);
- ltk_color_create(window->dpy, window->screen, window->cm, "#113355", &submenu_theme.fill_pressed);
- ltk_color_create(window->dpy, window->screen, window->cm, "#000000", &submenu_theme.text_active);
- ltk_color_create(window->dpy, window->screen, window->cm, "#113355", &submenu_theme.fill_active);
- ltk_color_create(window->dpy, window->screen, window->cm, "#FFFFFF", &submenu_theme.text_disabled);
- ltk_color_create(window->dpy, window->screen, window->cm, "#292929", &submenu_theme.fill_disabled);
-
- /* FIXME: make this unnecessary if border width is 0 */
- ltk_color_create(window->dpy, window->screen, window->cm, "#FFFFFF", &submenu_theme.border);
- ltk_color_create(window->dpy, window->screen, window->cm, "#FFFFFF", &submenu_theme.border_pressed);
- ltk_color_create(window->dpy, window->screen, window->cm, "#FFFFFF", &submenu_theme.border_active);
- ltk_color_create(window->dpy, window->screen, window->cm, "#FFFFFF", &submenu_theme.border_disabled);
+int
+ltk_menu_ini_handler(ltk_window *window, const char *prop, const char *value) {
+ return ltk_theme_handle_value(window, "menu", prop, value, menu_parseinfo, LENGTH(menu_parseinfo), &menu_parseinfo_sorted);
}
-/* FIXME: use border-width, etc. */
-/* FIXME: make theme parsing more convenient */
-/* FIXME: DEALLOCATE COLORS INSTEAD OF OVERWRITING DEFAULTS! */
-static void
-ini_handler(ltk_window *window, struct theme *t, const char *prop, const char *value) {
- const char *errstr;
- if (strcmp(prop, "border_width") == 0) {
- t->border_width = ltk_strtonum(value, 0, MAX_MENU_BORDER_WIDTH, &errstr);
- if (errstr)
- ltk_warn("Invalid menu border width '%s': %s.\n", value, errstr);
- } else if (strcmp(prop, "menu_border_width") == 0) {
- t->menu_border_width = ltk_strtonum(value, 0, MAX_MENU_BORDER_WIDTH, &errstr);
- /* FIXME: clarify different types of border width in error message */
- if (errstr)
- ltk_warn("Invalid menu border width '%s': %s.\n", value, errstr);
- } else if (strcmp(prop, "pad") == 0) {
- t->pad = ltk_strtonum(value, 0, MAX_MENU_PAD, &errstr);
- if (errstr)
- ltk_warn("Invalid menu pad '%s': %s.\n", value, errstr);
- } else if (strcmp(prop, "text_pad") == 0) {
- t->text_pad = ltk_strtonum(value, 0, MAX_MENU_PAD, &errstr);
- if (errstr)
- ltk_warn("Invalid menu text pad '%s': %s.\n", value, errstr);
- } else if (strcmp(prop, "arrow_size") == 0) {
- /* FIXME: should be error when used for menu instead of submenu */
- t->arrow_size = ltk_strtonum(value, 0, MAX_MENU_ARROW_SIZE, &errstr);
- if (errstr)
- ltk_warn("Invalid menu arrow size '%s': %s.\n", value, errstr);
- } else if (strcmp(prop, "arrow_pad") == 0) {
- /* FIXME: should be error when used for menu instead of submenu */
- t->arrow_pad = ltk_strtonum(value, 0, MAX_MENU_PAD, &errstr);
- if (errstr)
- ltk_warn("Invalid menu arrow pad '%s': %s.\n", value, errstr);
- } else if (strcmp(prop, "compress_borders") == 0) {
- if (strcmp(value, "true") == 0)
- t->compress_borders = 1;
- else if (strcmp(value, "false") == 0)
- t->compress_borders = 0;
- else
- ltk_warn("Invalid menu compress_borders '%s'.\n", value);
- } else if (strcmp(prop, "border_sides") == 0) {
- t->border_sides = LTK_BORDER_NONE;
- for (const char *c = value; *c != '\0'; c++) {
- switch (*c) {
- case 't':
- t->border_sides |= LTK_BORDER_TOP;
- break;
- case 'b':
- t->border_sides |= LTK_BORDER_BOTTOM;
- break;
- case 'l':
- t->border_sides |= LTK_BORDER_LEFT;
- break;
- case 'r':
- t->border_sides |= LTK_BORDER_RIGHT;
- break;
- default:
- ltk_warn("Invalid menu border_sides '%s'.\n", value);
- return;
- }
- }
- } else if (strcmp(prop, "background") == 0) {
- if (ltk_color_create(window->dpy, window->screen, window->cm, value, &t->background))
- ltk_warn("Error setting menu background color to '%s'.\n", value);
- } else if (strcmp(prop, "menu_border") == 0) {
- /* FIXME: clarify different type of menu border color */
- if (ltk_color_create(window->dpy, window->screen, window->cm, value, &t->menu_border))
- ltk_warn("Error setting menu border color to '%s'.\n", value);
- } else if (strcmp(prop, "scroll_background") == 0) {
- if (ltk_color_create(window->dpy, window->screen, window->cm, value, &t->scroll_background))
- ltk_warn("Error setting menu scroll background color to '%s'.\n", value);
- } else if (strcmp(prop, "scroll_arrow_color") == 0) {
- if (ltk_color_create(window->dpy, window->screen, window->cm, value, &t->scroll_arrow_color))
- ltk_warn("Error setting menu scroll arrow color to '%s'.\n", value);
- } else if (strcmp(prop, "text") == 0) {
- if (ltk_color_create(window->dpy, window->screen, window->cm, value, &t->text))
- ltk_warn("Error setting menu text color to '%s'.\n", value);
- } else if (strcmp(prop, "border") == 0) {
- if (ltk_color_create(window->dpy, window->screen, window->cm, value, &t->border))
- ltk_warn("Error setting menu border color to '%s'.\n", value);
- } else if (strcmp(prop, "fill") == 0) {
- if (ltk_color_create(window->dpy, window->screen, window->cm, value, &t->fill))
- ltk_warn("Error setting menu fill color to '%s'.\n", value);
- } else if (strcmp(prop, "text_pressed") == 0) {
- if (ltk_color_create(window->dpy, window->screen, window->cm, value, &t->text_pressed))
- ltk_warn("Error setting menu pressed text color to '%s'.\n", value);
- } else if (strcmp(prop, "border_pressed") == 0) {
- if (ltk_color_create(window->dpy, window->screen, window->cm, value, &t->border_pressed))
- ltk_warn("Error setting menu pressed border color to '%s'.\n", value);
- } else if (strcmp(prop, "fill_pressed") == 0) {
- if (ltk_color_create(window->dpy, window->screen, window->cm, value, &t->fill_pressed))
- ltk_warn("Error setting menu pressed fill color to '%s'.\n", value);
- } else if (strcmp(prop, "text_active") == 0) {
- if (ltk_color_create(window->dpy, window->screen, window->cm, value, &t->text_active))
- ltk_warn("Error setting menu active text color to '%s'.\n", value);
- } else if (strcmp(prop, "border_active") == 0) {
- if (ltk_color_create(window->dpy, window->screen, window->cm, value, &t->border_active))
- ltk_warn("Error setting menu active border color to '%s'.\n", value);
- } else if (strcmp(prop, "fill_active") == 0) {
- if (ltk_color_create(window->dpy, window->screen, window->cm, value, &t->fill_active))
- ltk_warn("Error setting menu active fill color to '%s'.\n", value);
- } else if (strcmp(prop, "text_disabled") == 0) {
- if (ltk_color_create(window->dpy, window->screen, window->cm, value, &t->text_disabled))
- ltk_warn("Error setting menu disabled text color to '%s'.\n", value);
- } else if (strcmp(prop, "border_disabled") == 0) {
- if (ltk_color_create(window->dpy, window->screen, window->cm, value, &t->border_disabled))
- ltk_warn("Error setting menu disabled border color to '%s'.\n", value);
- } else if (strcmp(prop, "fill_disabled") == 0) {
- if (ltk_color_create(window->dpy, window->screen, window->cm, value, &t->fill_disabled))
- ltk_warn("Error setting menu disabled fill color to '%s'.\n", value);
- } else {
- ltk_warn("Unknown property '%s' for button style.\n", prop);
- }
+int
+ltk_menu_fill_theme_defaults(ltk_window *window) {
+ return ltk_theme_fill_defaults(window, "menu", menu_parseinfo, LENGTH(menu_parseinfo));
}
void
-ltk_menu_ini_handler(ltk_window *window, const char *prop, const char *value) {
- ini_handler(window, &menu_theme, prop, value);
+ltk_menu_uninitialize_theme(ltk_window *window) {
+ ltk_theme_uninitialize(window, menu_parseinfo, LENGTH(menu_parseinfo));
}
-void
+static ltk_theme_parseinfo submenu_parseinfo[] = {
+ {"border-width", THEME_INT, {.i = &submenu_theme.border_width}, {.i = 0}, 0, MAX_MENU_BORDER_WIDTH, 0},
+ {"pad", THEME_INT, {.i = &submenu_theme.pad}, {.i = 5}, 0, MAX_MENU_PAD, 0},
+ {"text-pad", THEME_INT, {.i = &submenu_theme.text_pad}, {.i = 5}, 0, MAX_MENU_PAD, 0},
+ {"arrow-size", THEME_INT, {.i = &submenu_theme.arrow_size}, {.i = 10}, 0, MAX_MENU_ARROW_SIZE, 0},
+ {"arrow-pad", THEME_INT, {.i = &submenu_theme.arrow_pad}, {.i = 5}, 0, MAX_MENU_PAD, 0},
+ {"compress-borders", THEME_BOOL, {.b = &submenu_theme.compress_borders}, {.b = 0}, 0, 0, 0},
+ {"border-sides", THEME_BORDERSIDES, {.border = &submenu_theme.border_sides}, {.border = LTK_BORDER_NONE}, 0, 0, 0},
+ {"menu-border-width", THEME_INT, {.i = &submenu_theme.menu_border_width}, {.i = 1}, 0, MAX_MENU_BORDER_WIDTH, 0},
+ {"background", THEME_COLOR, {.color = &submenu_theme.background}, {.color = "#555555"}, 0, 0, 0},
+ {"scroll-background", THEME_COLOR, {.color = &submenu_theme.scroll_background}, {.color = "#333333"}, 0, 0, 0},
+ {"scroll-arrow-color", THEME_COLOR, {.color = &submenu_theme.scroll_arrow_color}, {.color = "#000000"}, 0, 0, 0},
+ {"text", THEME_COLOR, {.color = &submenu_theme.text}, {.color = "#FFFFFF"}, 0, 0, 0},
+ {"border", THEME_COLOR, {.color = &submenu_theme.border}, {.color = "#FFFFFF"}, 0, 0, 0},
+ {"fill", THEME_COLOR, {.color = &submenu_theme.fill}, {.color = "#113355"}, 0, 0, 0},
+ {"text-pressed", THEME_COLOR, {.color = &submenu_theme.text_pressed}, {.color = "#000000"}, 0, 0, 0},
+ {"border-pressed", THEME_COLOR, {.color = &submenu_theme.border_pressed}, {.color = "#FFFFFF"}, 0, 0, 0},
+ {"fill-pressed", THEME_COLOR, {.color = &submenu_theme.fill_pressed}, {.color = "#113355"}, 0, 0, 0},
+ {"text-active", THEME_COLOR, {.color = &submenu_theme.text_active}, {.color = "#000000"}, 0, 0, 0},
+ {"border-active", THEME_COLOR, {.color = &submenu_theme.border_active}, {.color = "#FFFFFF"}, 0, 0, 0},
+ {"fill-active", THEME_COLOR, {.color = &submenu_theme.fill_active}, {.color = "#113355"}, 0, 0, 0},
+ {"text-disabled", THEME_COLOR, {.color = &submenu_theme.text_disabled}, {.color = "#FFFFFF"}, 0, 0, 0},
+ {"border-disabled", THEME_COLOR, {.color = &submenu_theme.border_disabled}, {.color = "#FFFFFF"}, 0, 0, 0},
+ {"fill-disabled", THEME_COLOR, {.color = &submenu_theme.fill_disabled}, {.color = "#292929"}, 0, 0, 0},
+ {"menu-border", THEME_COLOR, {.color = &submenu_theme.menu_border}, {.color = "#FFFFFF"}, 0, 0, 0},
+};
+static int submenu_parseinfo_sorted = 0;
+
+int
ltk_submenu_ini_handler(ltk_window *window, const char *prop, const char *value) {
- ini_handler(window, &submenu_theme, prop, value);
+ return ltk_theme_handle_value(window, "submenu", prop, value, submenu_parseinfo, LENGTH(submenu_parseinfo), &submenu_parseinfo_sorted);
+}
+
+int
+ltk_submenu_fill_theme_defaults(ltk_window *window) {
+ return ltk_theme_fill_defaults(window, "submenu", submenu_parseinfo, LENGTH(submenu_parseinfo));
+}
+
+void
+ltk_submenu_uninitialize_theme(ltk_window *window) {
+ ltk_theme_uninitialize(window, submenu_parseinfo, LENGTH(submenu_parseinfo));
}
static void
@@ -1018,7 +919,7 @@ ltk_menu_insert_entry(ltk_menu *menu, const char *id, const char *text, ltk_menu
e->id = ltk_strdup(id);
ltk_window *w = menu->widget.window;
/* FIXME: pass const text */
- e->text = ltk_text_line_create(w->text_context, w->theme.font_size, (char *)text, 0, -1);
+ e->text = ltk_text_line_create(w->text_context, w->theme->font_size, (char *)text, 0, -1);
e->submenu = submenu;
if (submenu)
submenu->widget.parent = (ltk_widget *)menu;
diff --git a/src/menu.h b/src/menu.h
@@ -50,9 +50,12 @@ struct ltk_menuentry {
int disabled;
};
-void ltk_menu_setup_theme_defaults(ltk_window *window);
-void ltk_menu_ini_handler(ltk_window *window, const char *prop, const char *value);
-void ltk_submenu_ini_handler(ltk_window *window, const char *prop, const char *value);
+int ltk_menu_ini_handler(ltk_window *window, const char *prop, const char *value);
+int ltk_menu_fill_theme_defaults(ltk_window *window);
+void ltk_menu_uninitialize_theme(ltk_window *window);
+int ltk_submenu_ini_handler(ltk_window *window, const char *prop, const char *value);
+int ltk_submenu_fill_theme_defaults(ltk_window *window);
+void ltk_submenu_uninitialize_theme(ltk_window *window);
int ltk_menu_cmd(
ltk_window *window,
diff --git a/src/scrollbar.c b/src/scrollbar.c
@@ -31,6 +31,7 @@
#include "ltk.h"
#include "util.h"
#include "scrollbar.h"
+#include "theme.h"
#define MAX_SCROLLBAR_WIDTH 100 /* completely arbitrary */
@@ -67,47 +68,30 @@ static struct {
ltk_color fg_disabled;
} theme;
-void
-ltk_scrollbar_setup_theme_defaults(ltk_window *window) {
- theme.size = 15;
- /* FIXME: error checking - but if these fail, there is probably a bigger
- problem, so it might be best to just die completely */
- ltk_color_create(window->dpy, window->screen, window->cm, "#000000", &theme.bg_normal);
- ltk_color_create(window->dpy, window->screen, window->cm, "#555555", &theme.bg_disabled);
- ltk_color_create(window->dpy, window->screen, window->cm, "#113355", &theme.fg_normal);
- ltk_color_create(window->dpy, window->screen, window->cm, "#738194", &theme.fg_active);
- ltk_color_create(window->dpy, window->screen, window->cm, "#113355", &theme.fg_pressed);
- ltk_color_create(window->dpy, window->screen, window->cm, "#292929", &theme.fg_disabled);
+static ltk_theme_parseinfo parseinfo[] = {
+ {"size", THEME_INT, {.i = &theme.size}, {.i = 15}, 0, MAX_SCROLLBAR_WIDTH, 0},
+ {"bg", THEME_COLOR, {.color = &theme.bg_normal}, {.color = "#000000"}, 0, 0, 0},
+ {"bg-disabled", THEME_COLOR, {.color = &theme.bg_disabled}, {.color = "#555555"}, 0, 0, 0},
+ {"fg", THEME_COLOR, {.color = &theme.fg_normal}, {.color = "#113355"}, 0, 0, 0},
+ {"fg-active", THEME_COLOR, {.color = &theme.fg_active}, {.color = "#738194"}, 0, 0, 0},
+ {"fg-pressed", THEME_COLOR, {.color = &theme.fg_pressed}, {.color = "#113355"}, 0, 0, 0},
+ {"fg-disabled", THEME_COLOR, {.color = &theme.fg_disabled}, {.color = "#292929"}, 0, 0, 0},
+};
+static int parseinfo_sorted = 0;
+
+int
+ltk_scrollbar_ini_handler(ltk_window *window, const char *prop, const char *value) {
+ return ltk_theme_handle_value(window, "scrollbar", prop, value, parseinfo, LENGTH(parseinfo), &parseinfo_sorted);
+}
+
+int
+ltk_scrollbar_fill_theme_defaults(ltk_window *window) {
+ return ltk_theme_fill_defaults(window, "scrollbar", parseinfo, LENGTH(parseinfo));
}
void
-ltk_scrollbar_ini_handler(ltk_window *window, const char *prop, const char *value) {
- const char *errstr;
- if (strcmp(prop, "size") == 0) {
- theme.size = ltk_strtonum(value, 1, MAX_SCROLLBAR_WIDTH, &errstr);
- if (errstr)
- ltk_warn("Invalid scrollbar size '%s': %s.\n", value, errstr);
- } else if (strcmp(prop, "bg") == 0) {
- if (ltk_color_create(window->dpy, window->screen, window->cm, value, &theme.bg_normal))
- ltk_warn("Error setting scrollbar background color to '%s'.\n", value);
- } else if (strcmp(prop, "bg_disabled") == 0) {
- if (ltk_color_create(window->dpy, window->screen, window->cm, value, &theme.bg_disabled))
- ltk_warn("Error setting scrollbar disabled background color to '%s'.\n", value);
- } else if (strcmp(prop, "fg") == 0) {
- if (ltk_color_create(window->dpy, window->screen, window->cm, value, &theme.fg_normal))
- ltk_warn("Error setting scrollbar foreground color to '%s'.\n", value);
- } else if (strcmp(prop, "fg_active") == 0) {
- if (ltk_color_create(window->dpy, window->screen, window->cm, value, &theme.fg_active))
- ltk_warn("Error setting scrollbar active foreground color to '%s'.\n", value);
- } else if (strcmp(prop, "fg_pressed") == 0) {
- if (ltk_color_create(window->dpy, window->screen, window->cm, value, &theme.fg_pressed))
- ltk_warn("Error setting scrollbar pressed foreground color to '%s'.\n", value);
- } else if (strcmp(prop, "fg_disabled") == 0) {
- if (ltk_color_create(window->dpy, window->screen, window->cm, value, &theme.fg_disabled))
- ltk_warn("Error setting scrollbar disabled foreground color to '%s'.\n", value);
- } else {
- ltk_warn("Unknown property '%s' for scrollbar style.\n", prop);
- }
+ltk_scrollbar_uninitialize_theme(ltk_window *window) {
+ ltk_theme_uninitialize(window, parseinfo, LENGTH(parseinfo));
}
void
diff --git a/src/scrollbar.h b/src/scrollbar.h
@@ -31,9 +31,11 @@ typedef struct {
} ltk_scrollbar;
void ltk_scrollbar_set_virtual_size(ltk_scrollbar *scrollbar, int virtual_size);
-void ltk_scrollbar_setup_theme_defaults(ltk_window *window);
-void ltk_scrollbar_ini_handler(ltk_window *window, const char *prop, const char *value);
ltk_scrollbar *ltk_scrollbar_create(ltk_window *window, ltk_orientation orient, void (*callback)(ltk_widget *), void *data);
void ltk_scrollbar_scroll(ltk_widget *self, int delta, int scaled);
+int ltk_scrollbar_ini_handler(ltk_window *window, const char *prop, const char *value);
+int ltk_scrollbar_fill_theme_defaults(ltk_window *window);
+void ltk_scrollbar_uninitialize_theme(ltk_window *window);
+
#endif /* _LTK_SCROLLBAR_H_ */
diff --git a/src/text.h b/src/text.h
@@ -30,6 +30,7 @@ ltk_text_context *ltk_text_context_create(ltk_window *window, char *default_font
void ltk_text_context_destroy(ltk_text_context *ctx);
/* FIXME: allow to give length of text */
+/* FIXME: uint16_t as size is kind of ugly (also see window theme) */
ltk_text_line *ltk_text_line_create(ltk_text_context *ctx, uint16_t font_size, char *text, int take_over_text, int width);
void ltk_text_line_set_width(ltk_text_line *tl, int width);
void ltk_text_line_get_size(ltk_text_line *tl, int *w, int *h);
diff --git a/src/theme.c b/src/theme.c
@@ -0,0 +1,161 @@
+#include "util.h"
+#include "theme.h"
+#include "memory.h"
+
+/* FIXME: handle '#' or no '#' in color specification */
+static int
+search_helper(const void *keyv, const void *entryv) {
+ char *key = (char *)keyv;
+ ltk_theme_parseinfo *entry = (ltk_theme_parseinfo *)entryv;
+ return strcmp(key, entry->key);
+}
+
+static int
+sort_helper(const void *entry1v, const void *entry2v) {
+ ltk_theme_parseinfo *entry1 = (ltk_theme_parseinfo *)entry1v;
+ ltk_theme_parseinfo *entry2 = (ltk_theme_parseinfo *)entry2v;
+ return strcmp(entry1->key, entry2->key);
+}
+
+/* FIXME: more information for errors */
+int
+ltk_theme_handle_value(ltk_window *window, char *debug_name, const char *prop, const char *value, ltk_theme_parseinfo *parseinfo, size_t len, int *sorted) {
+ if (!*sorted) {
+ qsort(parseinfo, len, sizeof(ltk_theme_parseinfo), &sort_helper);
+ *sorted = 1;
+ }
+ ltk_theme_parseinfo *entry = bsearch(prop, parseinfo, len, sizeof(ltk_theme_parseinfo), &search_helper);
+ if (!entry) {
+ ltk_warn("Invalid property '%s:%s'.\n", debug_name, prop);
+ return 1;
+ } else if (entry->initialized) {
+ ltk_warn("Duplicate setting for property '%s:%s'.\n", debug_name, prop);
+ return 1;
+ }
+ const char *errstr = NULL;
+ switch (entry->type) {
+ case THEME_INT:
+ *(entry->ptr.i) = ltk_strtonum(value, entry->min, entry->max, &errstr);
+ if (errstr) {
+ ltk_warn("Invalid value '%s' for property '%s:%s'.\n", value, debug_name, prop);
+ return 1;
+ } else {
+ entry->initialized = 1;
+ }
+ break;
+ case THEME_STRING:
+ *(entry->ptr.str) = ltk_strdup(value);
+ entry->initialized = 1;
+ break;
+ case THEME_COLOR:
+ if (ltk_color_create(window->dpy, window->vis, window->cm, value, entry->ptr.color)) {
+ ltk_warn("Unable to create color '%s' for property '%s:%s'.\n", value, debug_name, prop);
+ return 1;
+ } else {
+ entry->initialized = 1;
+ }
+ break;
+ case THEME_BOOL:
+ if (strcmp(value, "true") == 0) {
+ *(entry->ptr.b) = 1;
+ } else if (strcmp(value, "false") == 0) {
+ *(entry->ptr.b) = 0;
+ } else {
+ ltk_warn("Invalid value '%s' for property '%s:%s'.\n", value, debug_name, prop);
+ return 1;
+ }
+ entry->initialized = 1;
+ break;
+ case THEME_BORDERSIDES:
+ *(entry->ptr.border) = LTK_BORDER_NONE;
+ for (const char *c = value; *c != '\0'; c++) {
+ switch (*c) {
+ case 't':
+ *(entry->ptr.border) |= LTK_BORDER_TOP;
+ break;
+ case 'b':
+ *(entry->ptr.border) |= LTK_BORDER_BOTTOM;
+ break;
+ case 'l':
+ *(entry->ptr.border) |= LTK_BORDER_LEFT;
+ break;
+ case 'r':
+ *(entry->ptr.border) |= LTK_BORDER_RIGHT;
+ break;
+ default:
+ ltk_warn("Invalid value '%s' for property '%s:%s'.\n", value, debug_name, prop);
+ return 1;
+ }
+ }
+ entry->initialized = 1;
+ break;
+ default:
+ ltk_fatal("Invalid theme setting type. This should not happen.\n");
+ /* TODO: ltk_assert(0); */
+ }
+ return 0;
+}
+
+int
+ltk_theme_fill_defaults(ltk_window *window, char *debug_name, ltk_theme_parseinfo *parseinfo, size_t len) {
+ for (size_t i = 0; i < len; i++) {
+ ltk_theme_parseinfo *e = &parseinfo[i];
+ switch (e->type) {
+ case THEME_INT:
+ *(e->ptr.i) = e->defaultval.i;
+ e->initialized = 1;
+ break;
+ case THEME_STRING:
+ *(e->ptr.str) = ltk_strdup(e->defaultval.str);
+ e->initialized = 1;
+ break;
+ case THEME_COLOR:
+ if (ltk_color_create(window->dpy, window->vis, window->cm, e->defaultval.color, e->ptr.color)) {
+ ltk_warn("Unable to create default color '%s' for property '%s:%s'.\n", e->defaultval.color, debug_name, e->key);
+ return 1;
+ } else {
+ e->initialized = 1;
+ }
+ break;
+ case THEME_BOOL:
+ *(e->ptr.b) = e->defaultval.b;
+ e->initialized = 1;
+ break;
+ case THEME_BORDERSIDES:
+ *(e->ptr.border) = e->defaultval.border;
+ e->initialized = 1;
+ break;
+ default:
+ ltk_fatal("Invalid theme setting type. This should not happen.\n");
+ /* TODO: ltk_assert(0); */
+ }
+ }
+ return 0;
+}
+
+void
+ltk_theme_uninitialize(ltk_window *window, ltk_theme_parseinfo *parseinfo, size_t len) {
+ for (size_t i = 0; i < len; i++) {
+ ltk_theme_parseinfo *e = &parseinfo[i];
+ if (!e->initialized)
+ continue;
+ switch (e->type) {
+ case THEME_STRING:
+ free(*(e->ptr.str));
+ e->initialized = 0;
+ break;
+ case THEME_COLOR:
+ ltk_color_destroy(window->dpy, window->vis, window->cm, e->ptr.color);
+ e->initialized = 0;
+ break;
+ case THEME_INT:
+ case THEME_BOOL:
+ case THEME_BORDERSIDES:
+ e->initialized = 0;
+ break;
+ default:
+ ltk_fatal("Invalid theme setting type. This should not happen.\n");
+ /* TODO: ltk_assert(0); */
+ }
+ }
+}
diff --git a/src/theme.h b/src/theme.h
@@ -0,0 +1,48 @@
+#ifndef _LTK_THEME_H_
+#define _LTK_THEME_H_
+
+#include "ltk.h"
+
+typedef enum {
+ THEME_STRING,
+ THEME_COLOR,
+ THEME_INT,
+ THEME_BOOL,
+ THEME_BORDERSIDES
+} ltk_theme_datatype;
+
+typedef struct {
+ char *key;
+ ltk_theme_datatype type;
+ /* Note: Bool and int are both integers, but they are
+ separate just to make it a bit clearer */
+ union {
+ char **str;
+ ltk_color *color;
+ int *i;
+ int *b;
+ ltk_border_sides *border;
+ } ptr;
+ /* Note: The default color is also given as a string
+ because it has to be allocated first (it is only a
+ different entry in the union in order to make it
+ a bit clearer) */
+ union {
+ char *str;
+ char *color;
+ int i;
+ int b;
+ ltk_border_sides border;
+ } defaultval;
+ int min, max; /* only for integers */
+ int initialized;
+} ltk_theme_parseinfo;
+
+/* Both return 1 on error, 0 on success */
+int ltk_theme_handle_value(ltk_window *window, char *debug_name, const char *prop, const char *value, ltk_theme_parseinfo *parseinfo, size_t len, int *sorted);
+int ltk_theme_fill_defaults(ltk_window *window, char *debug_name, ltk_theme_parseinfo *parseinfo, size_t len);
+void ltk_theme_uninitialize(ltk_window *window, ltk_theme_parseinfo *parseinfo, size_t len);
+
+#define LENGTH(X) (sizeof(X) / sizeof(X[0]))
+
+#endif /* _LTK_THEME_H_ */
diff --git a/src/util.h b/src/util.h
@@ -14,7 +14,10 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-/* Requires: <stdarg.h> */
+#ifndef _LTK_UTIL_H_
+#define _LTK_UTIL_H_
+
+#include <stdarg.h>
long long ltk_strtonum(
const char *numstr, long long minval,
@@ -35,3 +38,5 @@ void ltk_fatal_errno(const char *format, ...);
void ltk_warn_errno(const char *format, ...);
void ltk_fatal(const char *format, ...);
void ltk_warn(const char *format, ...);
+
+#endif /* _LTK_UTIL_H_ */