commit 07a20345dc4d1ff7a311dc1359c36394dfbc3769
parent b44497a0e20b249923f01284f85163b61b7e7609
Author: lumidify <nobody@lumidify.org>
Date: Thu, 2 May 2024 22:03:08 +0200
Implement theme inheritance
Diffstat:
16 files changed, 226 insertions(+), 117 deletions(-)
diff --git a/src/ltk/button.c b/src/ltk/button.c
@@ -32,7 +32,6 @@
static void ltk_button_draw(ltk_widget *self, ltk_surface *draw_surf, int x, int y, ltk_rect clip);
static int ltk_button_release(ltk_widget *self);
-ltk_button *ltk_button_create(ltk_window *window, char *text);
static void ltk_button_destroy(ltk_widget *self, int shallow);
static void ltk_button_recalc_ideal_size(ltk_widget *self);
@@ -77,8 +76,10 @@ static struct {
ltk_color *border_disabled;
ltk_color *fill_disabled;
+ char *font;
ltk_size border_width;
ltk_size pad;
+ ltk_size font_size;
} theme;
static ltk_theme_parseinfo parseinfo[] = {
@@ -88,6 +89,7 @@ static ltk_theme_parseinfo parseinfo[] = {
{"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_SIZE, {.size = &theme.border_width}, {.size = {.val = 50, .unit = LTK_UNIT_MM}}, 0, MAX_BUTTON_BORDER_WIDTH, 0},
+ {"font-size", THEME_SIZE, {.size = &theme.font_size}, {.size = {.val = 1200, .unit = LTK_UNIT_PT}}, 0, 20000, 0},
{"fill", THEME_COLOR, {.color = &theme.fill}, {.color = "#113355"}, 0, 0, 0},
{"fill-hover", THEME_COLOR, {.color = &theme.fill_hover}, {.color = "#738194"}, 0, 0, 0},
{"fill-active", THEME_COLOR, {.color = &theme.fill_active}, {.color = "#113355"}, 0, 0, 0},
@@ -95,6 +97,7 @@ static ltk_theme_parseinfo parseinfo[] = {
{"fill-pressed", THEME_COLOR, {.color = &theme.fill_pressed}, {.color = "#113355"}, 0, 0, 0},
{"pad", THEME_SIZE, {.size = &theme.pad}, {.size = {.val = 100, .unit = LTK_UNIT_MM}}, 0, MAX_BUTTON_PADDING, 0},
{"text-color", THEME_COLOR, {.color = &theme.text_color}, {.color = "#FFFFFF"}, 0, 0, 0},
+ {"font", THEME_STRING, {.str = &theme.font}, {.str = "Monospace"}, 0, 0, 0},
};
void
@@ -169,18 +172,19 @@ recalc_ideal_size(ltk_button *button) {
static void
ltk_button_recalc_ideal_size(ltk_widget *self) {
ltk_button *button = LTK_CAST_BUTTON(self);
- int font_size = ltk_size_to_pixel(self->window->theme->font_size, self->last_dpi);
+ int font_size = ltk_size_to_pixel(theme.font_size, self->last_dpi);
ltk_text_line_set_font_size(button->tl, font_size);
recalc_ideal_size(button);
}
ltk_button *
-ltk_button_create(ltk_window *window, char *text) {
+ltk_button_create(ltk_window *window, const char *text) {
ltk_button *button = ltk_malloc(sizeof(ltk_button));
ltk_fill_widget_defaults(LTK_CAST_WIDGET(button), window, &vtable, 0, 0);
- ltk_size font_size = window->theme->font_size;
- button->tl = ltk_text_line_create_default(ltk_size_to_pixel(font_size, button->widget.last_dpi), text, 0, -1);
+ button->tl = ltk_text_line_create_const_text_default(
+ theme.font, ltk_size_to_pixel(theme.font_size, button->widget.last_dpi), text, -1
+ );
recalc_ideal_size(button);
button->widget.dirty = 1;
diff --git a/src/ltk/button.h b/src/ltk/button.h
@@ -30,6 +30,6 @@ typedef struct {
ltk_text_line *tl;
} ltk_button;
-ltk_button *ltk_button_create(ltk_window *window, char *text);
+ltk_button *ltk_button_create(ltk_window *window, const char *text);
#endif /* LTK_BUTTON_H */
diff --git a/src/ltk/color.h b/src/ltk/color.h
@@ -22,6 +22,7 @@ typedef struct ltk_color ltk_color;
#include "graphics.h"
ltk_color *ltk_color_create(ltk_renderdata *renderdata, const char *hex);
+ltk_color *ltk_color_copy(ltk_renderdata *renderdata, const ltk_color *color);
void ltk_color_destroy(ltk_renderdata *renderdata, ltk_color *col);
#endif /* LTK_COLOR_H */
diff --git a/src/ltk/color_xlib.c b/src/ltk/color_xlib.c
@@ -46,6 +46,25 @@ ltk_color_create(ltk_renderdata *renderdata, const char *hex) {
return col;
}
+/* FIXME: does this actually work the way I think it does? */
+ltk_color *
+ltk_color_copy(ltk_renderdata *renderdata, const ltk_color *color) {
+ ltk_color *col = ltk_malloc(sizeof(ltk_color));
+ col->xcolor = color->xcolor;
+ if (!XAllocColor(renderdata->dpy, renderdata->cm, &col->xcolor)) {
+ ltk_free(col);
+ return NULL;
+ }
+ #if USE_XFT == 1
+ if (!XftColorAllocValue(renderdata->dpy, renderdata->vis, renderdata->cm, &color->xftcolor.color, &col->xftcolor)) {
+ XFreeColors(renderdata->dpy, renderdata->cm, &col->xcolor.pixel, 1, 0);
+ ltk_free(col);
+ return NULL;
+ }
+ #endif
+ return col;
+}
+
void
ltk_color_destroy(ltk_renderdata *renderdata, ltk_color *col) {
/* FIXME: what should the 'planes' argument be? */
diff --git a/src/ltk/config.c b/src/ltk/config.c
@@ -72,17 +72,18 @@ static struct theme_handlerinfo {
const char *name;
void (*get_parseinfo)(ltk_theme_parseinfo **parseinfo, size_t *len);
const char *parent;
+ int finished;
} theme_handlers[] = {
- {"general", <k_general_get_theme_parseinfo, NULL},
- {"theme:window", <k_window_get_theme_parseinfo, NULL},
- {"theme:button", <k_button_get_theme_parseinfo, "window"},
- {"theme:entry", <k_entry_get_theme_parseinfo, "window"},
- {"theme:label", <k_label_get_theme_parseinfo, "window"},
- {"theme:scrollbar", <k_scrollbar_get_theme_parseinfo, "window"},
- {"theme:menu", <k_menu_get_theme_parseinfo, "window"},
- {"theme:menuentry", <k_menuentry_get_theme_parseinfo, "window"},
- {"theme:submenu", <k_submenu_get_theme_parseinfo, "window"},
- {"theme:submenuentry", <k_submenuentry_get_theme_parseinfo, "window"},
+ {"general", <k_general_get_theme_parseinfo, NULL, 0},
+ {"theme:window", <k_window_get_theme_parseinfo, NULL, 0},
+ {"theme:button", <k_button_get_theme_parseinfo, "theme:window", 0},
+ {"theme:entry", <k_entry_get_theme_parseinfo, "theme:window", 0},
+ {"theme:label", <k_label_get_theme_parseinfo, "theme:window", 0},
+ {"theme:scrollbar", <k_scrollbar_get_theme_parseinfo, "theme:window", 0},
+ {"theme:menu", <k_menu_get_theme_parseinfo, "theme:window", 0},
+ {"theme:menuentry", <k_menuentry_get_theme_parseinfo, "theme:window", 0},
+ {"theme:submenu", <k_submenu_get_theme_parseinfo, "theme:window", 0},
+ {"theme:submenuentry", <k_submenuentry_get_theme_parseinfo, "theme:window", 0},
};
GEN_SORT_SEARCH_HELPERS(themehandler, struct theme_handlerinfo, name)
@@ -206,57 +207,90 @@ handle_theme_setting(ltk_renderdata *renderdata, ltk_theme_parseinfo *entry, con
}
static int
-fill_theme_defaults(ltk_renderdata *renderdata) {
+fill_single_theme_defaults(ltk_renderdata *renderdata, struct theme_handlerinfo *handler) {
ltk_theme_parseinfo *parseinfo;
size_t len;
- for (size_t j = 0; j < LENGTH(theme_handlers); j++) {
- theme_handlers[j].get_parseinfo(&parseinfo, &len);
- for (size_t i = 0; i < len; i++) {
- ltk_theme_parseinfo *e = &parseinfo[i];
- if (e->initialized)
- continue;
- switch (e->type) {
- case THEME_INT:
- *(e->ptr.i) = e->defaultval.i;
- e->initialized = 1;
- break;
- case THEME_UINT:
- *(e->ptr.u) = e->defaultval.u;
- e->initialized = 1;
- break;
- case THEME_DOUBLE:
- *(e->ptr.d) = e->defaultval.d;
- e->initialized = 1;
- break;
- case THEME_SIZE:
- *(e->ptr.size) = e->defaultval.size;
- e->initialized = 1;
- break;
- case THEME_STRING:
- if (e->defaultval.str)
- *(e->ptr.str) = ltk_strdup(e->defaultval.str);
- else
- *(e->ptr.str) = NULL;
- e->initialized = 1;
- break;
- case THEME_COLOR:
- if (!(*(e->ptr.color) = ltk_color_create(renderdata, e->defaultval.color)))
+ if (handler->finished)
+ return 0;
+ handler->get_parseinfo(&parseinfo, &len);
+ ltk_theme_parseinfo *parent_parseinfo = NULL;
+ size_t parent_len = 0;
+ if (handler->parent) {
+ struct theme_handlerinfo *parent_handler = themehandler_get_entry(
+ theme_handlers, LENGTH(theme_handlers), handler->parent, strlen(handler->parent)
+ );
+ /* Yes, this will cause an infinite loop if there's a cycle in the parent
+ relationship. However, there is absolutely no reason to construct such a cycle. */
+ /* FIXME: warning if not found */
+ if (parent_handler) {
+ if (fill_single_theme_defaults(renderdata, parent_handler))
+ return 1;
+ parent_handler->get_parseinfo(&parent_parseinfo, &parent_len);
+ }
+ }
+ for (size_t i = 0; i < len; i++) {
+ ltk_theme_parseinfo *e = &parseinfo[i];
+ if (e->initialized)
+ continue;
+ ltk_theme_parseinfo *ep = parent_parseinfo ?
+ theme_get_entry(parent_parseinfo, parent_len, e->key, strlen(e->key)) : NULL;
+ switch (e->type) {
+ case THEME_INT:
+ *(e->ptr.i) = ep ? *(ep->ptr.i) : e->defaultval.i;
+ e->initialized = 1;
+ break;
+ case THEME_UINT:
+ *(e->ptr.u) = ep ? *(ep->ptr.u) : e->defaultval.u;
+ e->initialized = 1;
+ break;
+ case THEME_DOUBLE:
+ *(e->ptr.d) = ep ? *(ep->ptr.d) : e->defaultval.d;
+ e->initialized = 1;
+ break;
+ case THEME_SIZE:
+ *(e->ptr.size) = ep ? *(ep->ptr.size) : e->defaultval.size;
+ e->initialized = 1;
+ break;
+ case THEME_STRING:
+ if (ep)
+ *(e->ptr.str) = *(ep->ptr.str) ? ltk_strdup(*(ep->ptr.str)) : NULL;
+ else if (e->defaultval.str)
+ *(e->ptr.str) = ltk_strdup(e->defaultval.str);
+ else
+ *(e->ptr.str) = NULL;
+ e->initialized = 1;
+ break;
+ case THEME_COLOR:
+ if (ep) {
+ if (!(*(e->ptr.color) = ltk_color_copy(renderdata, *(ep->ptr.color))))
return 1;
- 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");
+ } else if (!(*(e->ptr.color) = ltk_color_create(renderdata, e->defaultval.color))) {
+ return 1;
}
+ e->initialized = 1;
+ break;
+ case THEME_BOOL:
+ *(e->ptr.b) = ep ? *(ep->ptr.b) : e->defaultval.b;
+ e->initialized = 1;
+ break;
+ case THEME_BORDERSIDES:
+ *(e->ptr.border) = ep ? *(ep->ptr.border) : e->defaultval.border;
+ e->initialized = 1;
+ break;
+ default:
+ ltk_fatal("Invalid theme setting type. This should not happen.\n");
}
}
+ handler->finished = 1;
+ return 0;
+}
+
+static int
+fill_theme_defaults(ltk_renderdata *renderdata) {
+ for (size_t j = 0; j < LENGTH(theme_handlers); j++) {
+ if (fill_single_theme_defaults(renderdata, &theme_handlers[j]))
+ return 1;
+ }
return 0;
}
@@ -266,6 +300,7 @@ uninitialize_theme(ltk_renderdata *renderdata) {
size_t len;
for (size_t j = 0; j < LENGTH(theme_handlers); j++) {
theme_handlers[j].get_parseinfo(&parseinfo, &len);
+ theme_handlers[j].finished = 0;
for (size_t i = 0; i < len; i++) {
ltk_theme_parseinfo *e = &parseinfo[i];
if (!e->initialized)
diff --git a/src/ltk/entry.c b/src/ltk/entry.c
@@ -168,8 +168,10 @@ static struct {
ltk_color *border_disabled;
ltk_color *fill_disabled;
+ char *font;
ltk_size border_width;
ltk_size pad;
+ ltk_size font_size;
} theme;
/* FIXME:
@@ -191,6 +193,8 @@ static ltk_theme_parseinfo parseinfo[] = {
{"pad", THEME_SIZE, {.size = &theme.pad}, {.size = {.val = 100, .unit = LTK_UNIT_MM}}, 0, MAX_ENTRY_PADDING, 0},
{"text-color", THEME_COLOR, {.color = &theme.text_color}, {.color = "#FFFFFF"}, 0, 0, 0},
{"selection-color", THEME_COLOR, {.color = &theme.selection_color}, {.color = "#000000"}, 0, 0, 0},
+ {"font-size", THEME_SIZE, {.size = &theme.font_size}, {.size = {.val = 1200, .unit = LTK_UNIT_PT}}, 0, 20000, 0},
+ {"font", THEME_STRING, {.str = &theme.font}, {.str = "Monospace"}, 0, 0, 0},
};
void
@@ -747,18 +751,19 @@ recalc_ideal_size(ltk_entry *entry) {
static void
ltk_entry_recalc_ideal_size(ltk_widget *self) {
ltk_entry *entry = LTK_CAST_ENTRY(self);
- int font_size = ltk_size_to_pixel(self->window->theme->font_size, self->last_dpi);
+ int font_size = ltk_size_to_pixel(theme.font_size, self->last_dpi);
ltk_text_line_set_font_size(entry->tl, font_size);
recalc_ideal_size(entry);
}
ltk_entry *
-ltk_entry_create(ltk_window *window, char *text) {
+ltk_entry_create(ltk_window *window, const char *text) {
ltk_entry *entry = ltk_malloc(sizeof(ltk_entry));
ltk_fill_widget_defaults(LTK_CAST_WIDGET(entry), window, &vtable, 0, 0);
- ltk_size font_size = window->theme->font_size;
- entry->tl = ltk_text_line_create_default(ltk_size_to_pixel(font_size, entry->widget.last_dpi), text, 0, -1);
+ entry->tl = ltk_text_line_create_const_text_default(
+ theme.font, ltk_size_to_pixel(theme.font_size, entry->widget.last_dpi), text, -1
+ );
recalc_ideal_size(entry);
entry->cur_offset = 0;
diff --git a/src/ltk/entry.h b/src/ltk/entry.h
@@ -38,6 +38,6 @@ typedef struct {
char selecting;
} ltk_entry;
-ltk_entry *ltk_entry_create(ltk_window *window, char *text);
+ltk_entry *ltk_entry_create(ltk_window *window, const char *text);
#endif /* LTK_ENTRY_H */
diff --git a/src/ltk/label.c b/src/ltk/label.c
@@ -59,7 +59,9 @@ static struct {
ltk_color *text_color;
ltk_color *bg_color;
ltk_color *bg_color_active;
+ char *font;
ltk_size pad;
+ ltk_size font_size;
} theme;
static ltk_theme_parseinfo parseinfo[] = {
@@ -67,6 +69,8 @@ static ltk_theme_parseinfo parseinfo[] = {
{"bg-color-active", THEME_COLOR, {.color = &theme.bg_color_active}, {.color = "#222222"}, 0, 0, 0},
{"pad", THEME_SIZE, {.size = &theme.pad}, {.size = {.val = 100, .unit = LTK_UNIT_MM}}, 0, MAX_LABEL_PADDING, 0},
{"text-color", THEME_COLOR, {.color = &theme.text_color}, {.color = "#FFFFFF"}, 0, 0, 0},
+ {"font-size", THEME_SIZE, {.size = &theme.font_size}, {.size = {.val = 1200, .unit = LTK_UNIT_PT}}, 0, 20000, 0},
+ {"font", THEME_STRING, {.str = &theme.font}, {.str = "Monospace"}, 0, 0, 0},
};
void
@@ -104,18 +108,19 @@ recalc_ideal_size(ltk_label *label) {
static void
ltk_label_recalc_ideal_size(ltk_widget *self) {
ltk_label *label = LTK_CAST_LABEL(self);
- int font_size = ltk_size_to_pixel(self->window->theme->font_size, self->last_dpi);
+ int font_size = ltk_size_to_pixel(theme.font_size, self->last_dpi);
ltk_text_line_set_font_size(label->tl, font_size);
recalc_ideal_size(label);
}
ltk_label *
-ltk_label_create(ltk_window *window, char *text) {
+ltk_label_create(ltk_window *window, const char *text) {
ltk_label *label = ltk_malloc(sizeof(ltk_label));
ltk_fill_widget_defaults(LTK_CAST_WIDGET(label), window, &vtable, 0, 0);
- ltk_size font_size = window->theme->font_size;
- label->tl = ltk_text_line_create_default(ltk_size_to_pixel(font_size, label->widget.last_dpi), text, 0, -1);
+ label->tl = ltk_text_line_create_const_text_default(
+ theme.font, ltk_size_to_pixel(theme.font_size, label->widget.last_dpi), text, -1
+ );
recalc_ideal_size(label);
return label;
diff --git a/src/ltk/label.h b/src/ltk/label.h
@@ -29,6 +29,6 @@ typedef struct {
ltk_text_line *tl;
} ltk_label;
-ltk_label *ltk_label_create(ltk_window *window, char *text);
+ltk_label *ltk_label_create(ltk_window *window, const char *text);
#endif /* LTK_LABEL_H */
diff --git a/src/ltk/ltk.c b/src/ltk/ltk.c
@@ -196,8 +196,7 @@ ltk_init(void) {
ltk_free0(config_path);
ltk_events_init(shared_data.renderdata);
- ltk_window_theme *window_theme = ltk_window_get_theme();
- shared_data.text_context = ltk_text_context_create(shared_data.renderdata, window_theme->font);
+ shared_data.text_context = ltk_text_context_create(shared_data.renderdata);
shared_data.clipboard = ltk_clipboard_create(shared_data.renderdata);
/* FIXME: configure cache size; check for overflow */
ltk_image_init(shared_data.renderdata, 1024 * 1024 * 4);
@@ -514,6 +513,11 @@ ltk_handle_event(ltk_event *event) {
}
ltk_text_line *
-ltk_text_line_create_default(uint16_t font_size, char *text, int take_over_text, int width) {
- return ltk_text_line_create(shared_data.text_context, font_size, text, take_over_text, width);
+ltk_text_line_create_default(const char *font, int font_size, char *text, int take_over_text, int width) {
+ return ltk_text_line_create(shared_data.text_context, font, font_size, text, take_over_text, width);
+}
+
+ltk_text_line *
+ltk_text_line_create_const_text_default(const char *font, int font_size, const char *text, int width) {
+ return ltk_text_line_create_const_text(shared_data.text_context, font, font_size, text, width);
}
diff --git a/src/ltk/ltk.h b/src/ltk/ltk.h
@@ -50,7 +50,8 @@ void ltk_window_destroy(ltk_widget *self, int shallow);
int ltk_call_cmd(ltk_widget *caller, const char *cmd, size_t cmdlen, const char *text, size_t textlen);
/* convenience function to use the default text context */
-ltk_text_line *ltk_text_line_create_default(uint16_t font_size, char *text, int take_over_text, int width);
+ltk_text_line *ltk_text_line_create_default(const char *font, int font_size, char *text, int take_over_text, int width);
+ltk_text_line *ltk_text_line_create_const_text_default(const char *font, int font_size, const char *text, int width);
ltk_clipboard *ltk_get_clipboard(void);
ltk_renderdata *ltk_get_renderer(void);
diff --git a/src/ltk/menu.c b/src/ltk/menu.c
@@ -79,10 +79,12 @@ static struct entry_theme {
ltk_color *border_disabled;
ltk_color *fill_disabled;
+ char *font;
ltk_size text_pad;
ltk_size arrow_pad;
ltk_size arrow_size;
ltk_size border_width;
+ ltk_size font_size;
} menu_entry_theme, submenu_entry_theme;
static void ltk_menu_ensure_rect_shown(ltk_widget *self, ltk_rect r);
@@ -130,6 +132,7 @@ static ltk_widget *ltk_menuentry_get_child(ltk_widget *self);
/* FIXME: these functions are named really badly */
static void recalc_ideal_menu_size_with_notification(ltk_widget *self, ltk_widget *widget);
static void recalc_ideal_menu_size(ltk_menu *menu);
+static void ltk_menuentry_set_font(ltk_menuentry *entry);
static void ltk_menu_recalc_ideal_size(ltk_widget *self);
static void ltk_menuentry_recalc_ideal_size(ltk_widget *self);
static void ltk_menuentry_recalc_ideal_size_with_notification(ltk_menuentry *entry);
@@ -227,6 +230,8 @@ static ltk_theme_parseinfo menu_entry_parseinfo[] = {
{"text-disabled", THEME_COLOR, {.color = &menu_entry_theme.text_disabled}, {.color = "#FFFFFF"}, 0, 0, 0},
{"border-disabled", THEME_COLOR, {.color = &menu_entry_theme.border_disabled}, {.color = "#FFFFFF"}, 0, 0, 0},
{"fill-disabled", THEME_COLOR, {.color = &menu_entry_theme.fill_disabled}, {.color = "#292929"}, 0, 0, 0},
+ {"font-size", THEME_SIZE, {.size = &menu_entry_theme.font_size}, {.size = {.val = 1200, .unit = LTK_UNIT_PT}}, 0, 20000, 0},
+ {"font", THEME_STRING, {.str = &menu_entry_theme.font}, {.str = "Monospace"}, 0, 0, 0},
};
static ltk_theme_parseinfo submenu_parseinfo[] = {
@@ -260,6 +265,8 @@ static ltk_theme_parseinfo submenu_entry_parseinfo[] = {
{"text-disabled", THEME_COLOR, {.color = &submenu_entry_theme.text_disabled}, {.color = "#FFFFFF"}, 0, 0, 0},
{"border-disabled", THEME_COLOR, {.color = &submenu_entry_theme.border_disabled}, {.color = "#FFFFFF"}, 0, 0, 0},
{"fill-disabled", THEME_COLOR, {.color = &submenu_entry_theme.fill_disabled}, {.color = "#292929"}, 0, 0, 0},
+ {"font-size", THEME_SIZE, {.size = &submenu_entry_theme.font_size}, {.size = {.val = 1200, .unit = LTK_UNIT_PT}}, 0, 20000, 0},
+ {"font", THEME_STRING, {.str = &submenu_entry_theme.font}, {.str = "Monospace"}, 0, 0, 0},
};
void
ltk_menu_get_theme_parseinfo(ltk_theme_parseinfo **p, size_t *len) {
@@ -1003,12 +1010,20 @@ recalc_ideal_menuentry_size(ltk_menuentry *entry) {
static void
ltk_menuentry_recalc_ideal_size(ltk_widget *self) {
ltk_menuentry *entry = LTK_CAST_MENUENTRY(self);
- int font_size = ltk_size_to_pixel(self->window->theme->font_size, self->last_dpi);
+ struct entry_theme *t = IN_SUBMENU(entry) ? &submenu_entry_theme : &menu_entry_theme;
+ int font_size = ltk_size_to_pixel(t->font_size, self->last_dpi);
ltk_text_line_set_font_size(entry->text_line, font_size);
recalc_ideal_menuentry_size(entry);
}
static void
+ltk_menuentry_set_font(ltk_menuentry *entry) {
+ struct entry_theme *t = IN_SUBMENU(entry) ? &submenu_entry_theme : &menu_entry_theme;
+ int font_size = ltk_size_to_pixel(t->font_size, LTK_CAST_WIDGET(entry)->last_dpi);
+ ltk_text_line_set_font(entry->text_line, t->font, font_size);
+}
+
+static void
ltk_menuentry_recalc_ideal_size_with_notification(ltk_menuentry *entry) {
recalc_ideal_menuentry_size(entry);
/* FIXME: only call if something changed */
@@ -1021,9 +1036,10 @@ ltk_menuentry *
ltk_menuentry_create(ltk_window *window, const char *text) {
ltk_menuentry *e = ltk_malloc(sizeof(ltk_menuentry));
ltk_fill_widget_defaults(&e->widget, window, &entry_vtable, 0, 0);
- /* FIXME: this should be split up into two versions with const or not const (one for taking over text) */
- e->text_line = ltk_text_line_create_default(
- ltk_size_to_pixel(window->theme->font_size, e->widget.last_dpi), (char *)text, 0, -1
+ e->text_line = ltk_text_line_create_const_text_default(
+ menu_entry_theme.font,
+ ltk_size_to_pixel(menu_entry_theme.font_size, e->widget.last_dpi),
+ text, -1
);
e->submenu = NULL;
/* Note: This is only set as a dummy value! The actual ideal size can't
@@ -1037,7 +1053,7 @@ ltk_menuentry_create(ltk_window *window, const char *text) {
static int
ltk_menuentry_remove_child(ltk_widget *self, ltk_widget *widget) {
ltk_menuentry *e = LTK_CAST_MENUENTRY(self);
- if (widget != &e->submenu->widget)
+ if (widget != LTK_CAST_WIDGET(e->submenu))
return 1;
widget->parent = NULL;
e->submenu = NULL;
@@ -1067,6 +1083,8 @@ ltk_menu_insert_entry(ltk_menu *menu, ltk_menuentry *entry, size_t idx) {
if (insert_entry(menu, entry, idx))
return 2; /* invalid index */
entry->widget.parent = &menu->widget;
+ /* the theme may have changed if the entry switched between menu and submenu */
+ ltk_menuentry_set_font(entry);
ltk_menuentry_recalc_ideal_size_with_notification(entry);
recalc_ideal_menu_size_with_notification(LTK_CAST_WIDGET(menu), NULL);
menu->widget.dirty = 1;
@@ -1113,7 +1131,10 @@ ltk_menu_remove_entry_index(ltk_menu *menu, size_t idx) {
if (idx >= menu->num_entries)
return 1; /* invalid index */
menu->entries[idx]->widget.parent = NULL;
- ltk_menuentry_recalc_ideal_size_with_notification(menu->entries[idx]);
+ /* I don't think this is needed because the entry isn't shown
+ anywhere. Its size will be recalculated once it is added
+ to a menu again. */
+ /* ltk_menuentry_recalc_ideal_size_with_notification(menu->entries[idx]); */
memmove(
menu->entries + idx,
menu->entries + idx + 1,
@@ -1151,11 +1172,15 @@ ltk_menu_remove_child(ltk_widget *self, ltk_widget *child) {
return ltk_menu_remove_entry(LTK_CAST_MENU(self), LTK_CAST_MENUENTRY(child));
}
+/* TODO: add function to also destroy the entries when removing them */
void
ltk_menu_remove_all_entries(ltk_menu *menu) {
for (size_t i = 0; i < menu->num_entries; i++) {
menu->entries[i]->widget.parent = NULL;
- ltk_menuentry_recalc_ideal_size_with_notification(menu->entries[i]);
+ /* I don't think this is needed because the entry isn't shown
+ anywhere. Its size will be recalculated once it is added
+ to a menu again. */
+ /* ltk_menuentry_recalc_ideal_size_with_notification(menu->entries[i]); */
}
menu->num_entries = menu->num_alloc = 0;
ltk_free0(menu->entries);
diff --git a/src/ltk/text.h b/src/ltk/text.h
@@ -26,13 +26,15 @@
typedef struct ltk_text_line ltk_text_line;
typedef struct ltk_text_context ltk_text_context;
-ltk_text_context *ltk_text_context_create(ltk_renderdata *data, char *default_font);
+ltk_text_context *ltk_text_context_create(ltk_renderdata *data);
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_font_size(ltk_text_line *tl, uint16_t font_size);
+/* FIXME: font string format is currently defined by implementation, maybe standardize that? */
+ltk_text_line *ltk_text_line_create(ltk_text_context *ctx, const char *font, int font_size, char *text, int take_over_text, int width);
+ltk_text_line *ltk_text_line_create_const_text(ltk_text_context *ctx, const char *font, int font_size, const char *text, int width);
+void ltk_text_line_set_font(ltk_text_line *tl, const char *font, int font_size);
+void ltk_text_line_set_font_size(ltk_text_line *tl, int font_size);
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);
void ltk_text_line_destroy(ltk_text_line *tl);
diff --git a/src/ltk/text_pango.c b/src/ltk/text_pango.c
@@ -39,7 +39,8 @@ struct ltk_text_line {
char *text;
size_t len;
PangoLayout *layout;
- uint16_t font_size;
+ PangoFontDescription *font_desc;
+ int font_size;
PangoAttrList *attrs;
};
@@ -47,16 +48,14 @@ struct ltk_text_context {
ltk_renderdata *data;
PangoFontMap *fontmap;
PangoContext *context;
- char *default_font;
};
ltk_text_context *
-ltk_text_context_create(ltk_renderdata *data, char *default_font) {
+ltk_text_context_create(ltk_renderdata *data) {
ltk_text_context *ctx = ltk_malloc(sizeof(ltk_text_context));
ctx->data = data;
ctx->fontmap = NULL;
ctx->context = NULL;
- ctx->default_font = ltk_strdup(default_font);
return ctx;
}
@@ -74,7 +73,6 @@ ltk_text_context_init(ltk_text_context *ctx) {
void
ltk_text_context_destroy(ltk_text_context *ctx) {
- ltk_free(ctx->default_font);
/* FIXME: if both are unref'd, there is a segfault - what is
the normal thing to do here? */
if (ctx->fontmap)
@@ -104,15 +102,29 @@ ltk_text_line_set_text(ltk_text_line *tl, char *text, int take_over_text) {
}
void
-ltk_text_line_set_font_size(ltk_text_line *tl, uint16_t font_size) {
- PangoFontDescription *desc = pango_font_description_from_string(tl->ctx->default_font);
+ltk_text_line_set_font_size(ltk_text_line *tl, int font_size) {
+ if (font_size == tl->font_size)
+ return;
+ pango_font_description_set_absolute_size(tl->font_desc, font_size * PANGO_SCALE);
+ pango_layout_set_font_description(tl->layout, tl->font_desc);
+ tl->font_size = font_size;
+}
+
+void
+ltk_text_line_set_font(ltk_text_line *tl, const char *font, int font_size) {
+ /* it doesn't seem as if there's a way to parse a font string without
+ createing a new font description (but I might have missed something) */
+ PangoFontDescription *desc = pango_font_description_from_string(font);
pango_font_description_set_absolute_size(desc, font_size * PANGO_SCALE);
pango_layout_set_font_description(tl->layout, desc);
- pango_font_description_free(desc);
+ if (tl->font_desc)
+ pango_font_description_free(tl->font_desc);
+ tl->font_desc = desc;
+ tl->font_size = font_size;
}
ltk_text_line *
-ltk_text_line_create(ltk_text_context *ctx, uint16_t font_size, char *text, int take_over_text, int width) {
+ltk_text_line_create(ltk_text_context *ctx, const char *font, int font_size, char *text, int take_over_text, int width) {
ltk_text_context_init(ctx);
ltk_text_line *tl = ltk_malloc(sizeof(ltk_text_line));
if (take_over_text)
@@ -131,11 +143,17 @@ ltk_text_line_create(ltk_text_context *ctx, uint16_t font_size, char *text, int
ltk_text_line_set_width(tl, width * PANGO_SCALE);
tl->attrs = NULL;
ltk_text_line_clear_attrs(tl);
- ltk_text_line_set_font_size(tl, font_size);
+ tl->font_desc = NULL;
+ ltk_text_line_set_font(tl, font, font_size);
return tl;
}
+ltk_text_line *
+ltk_text_line_create_const_text(ltk_text_context *ctx, const char *font, int font_size, const char *text, int width) {
+ return ltk_text_line_create(ctx, font, font_size, ltk_strdup(text), 1, width);
+}
+
void
ltk_text_line_draw(ltk_text_line *tl, ltk_surface *s, ltk_color *color, int x, int y) {
XftDraw *d = ltk_surface_get_xft_draw(s);
@@ -375,6 +393,7 @@ ltk_text_line_move_cursor_visually(ltk_text_line *tl, size_t pos, int movement,
void
ltk_text_line_destroy(ltk_text_line *tl) {
+ pango_font_description_free(tl->font_desc);
if (tl->attrs)
pango_attr_list_unref(tl->attrs);
g_object_unref(tl->layout);
diff --git a/src/ltk/window.c b/src/ltk/window.c
@@ -118,7 +118,14 @@ static ltk_widget **widget_stack = NULL;
static size_t widget_stack_alloc = 0;
static size_t widget_stack_len = 0;
-static ltk_window_theme theme;
+static struct {
+ int border_width;
+ ltk_size font_size;
+ char *font;
+ ltk_color *fg;
+ ltk_color *bg;
+} theme;
+
static ltk_theme_parseinfo theme_parseinfo[] = {
{"bg", THEME_COLOR, {.color = &theme.bg}, {.color = "#000000"}, 0, 0, 0},
{"fg", THEME_COLOR, {.color = &theme.fg}, {.color = "#FFFFFF"}, 0, 0, 0},
@@ -132,12 +139,6 @@ ltk_window_get_theme_parseinfo(ltk_theme_parseinfo **p, size_t *len) {
*len = LENGTH(theme_parseinfo);
}
-/* FIXME: maybe ltk_fatal if ltk not initialized? */
-ltk_window_theme *
-ltk_window_get_theme(void) {
- return &theme;
-}
-
void
ltk_window_cleanup(void) {
ltk_keypress_bindings_destroy(keypresses);
@@ -489,7 +490,7 @@ ltk_window_redraw(ltk_widget *self, ltk_surface *draw_surf, int x, int y, ltk_re
window->dirty_rect.h -= window->dirty_rect.y + window->dirty_rect.h - window->rect.h;
/* FIXME: this should use window->dirty_rect, but that doesn't work
properly with double buffering */
- ltk_surface_fill_rect(window->surface, window->theme->bg, (ltk_rect){0, 0, window->rect.w, window->rect.h});
+ ltk_surface_fill_rect(window->surface, theme.bg, (ltk_rect){0, 0, window->rect.w, window->rect.h});
if (window->root_widget) {
ptr = window->root_widget;
ltk_widget_draw(ptr, window->surface, 0, 0, window->rect);
@@ -624,7 +625,6 @@ ltk_window_create_intern(ltk_renderdata *data, const char *title, int x, int y,
unsigned int dpi = (unsigned int)round(config->dpi_scale * config->fixed_dpi * 5);
window->renderwindow = ltk_renderer_create_window(data, title, x, y, w, h, dpi);
ltk_renderer_set_window_properties(window->renderwindow, theme.bg);
- window->theme = &theme;
window->root_widget = NULL;
window->hover_widget = NULL;
diff --git a/src/ltk/window.h b/src/ltk/window.h
@@ -29,14 +29,6 @@
#define LTK_WINDOW_SIGNAL_CLOSE -1
#define LTK_WINDOW_SIGNAL_INVALID -2
-typedef struct {
- int border_width;
- ltk_size font_size;
- char *font;
- ltk_color *fg;
- ltk_color *bg;
-} ltk_window_theme;
-
typedef struct ltk_window {
ltk_widget widget;
ltk_renderwindow *renderwindow;
@@ -49,7 +41,6 @@ typedef struct ltk_window {
ltk_widget *pressed_widget;
ltk_rect rect;
- ltk_window_theme *theme;
ltk_rect dirty_rect;
/* FIXME: generic array */
ltk_widget **popups;
@@ -81,6 +72,4 @@ void ltk_window_register_popup(ltk_window *window, ltk_widget *popup);
void ltk_window_unregister_popup(ltk_window *window, ltk_widget *popup);
void ltk_window_unregister_all_popups(ltk_window *window);
-ltk_window_theme *ltk_window_get_theme(void);
-
#endif /* LTK_WINDOW_H */