ltk

GUI toolkit for X11 (WIP)
git clone git://lumidify.org/ltk.git (fast, but not encrypted)
git clone https://lumidify.org/ltk.git (encrypted, but very slow)
git clone git://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/ltk.git (over tor)
Log | Files | Refs | README | LICENSE

commit 07a20345dc4d1ff7a311dc1359c36394dfbc3769
parent b44497a0e20b249923f01284f85163b61b7e7609
Author: lumidify <nobody@lumidify.org>
Date:   Thu,  2 May 2024 22:03:08 +0200

Implement theme inheritance

Diffstat:
Msrc/ltk/button.c | 14+++++++++-----
Msrc/ltk/button.h | 2+-
Msrc/ltk/color.h | 1+
Msrc/ltk/color_xlib.c | 19+++++++++++++++++++
Msrc/ltk/config.c | 145+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Msrc/ltk/entry.c | 13+++++++++----
Msrc/ltk/entry.h | 2+-
Msrc/ltk/label.c | 13+++++++++----
Msrc/ltk/label.h | 2+-
Msrc/ltk/ltk.c | 12++++++++----
Msrc/ltk/ltk.h | 3++-
Msrc/ltk/menu.c | 39++++++++++++++++++++++++++++++++-------
Msrc/ltk/text.h | 10++++++----
Msrc/ltk/text_pango.c | 39+++++++++++++++++++++++++++++----------
Msrc/ltk/window.c | 18+++++++++---------
Msrc/ltk/window.h | 11-----------
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", &ltk_general_get_theme_parseinfo, NULL}, - {"theme:window", &ltk_window_get_theme_parseinfo, NULL}, - {"theme:button", &ltk_button_get_theme_parseinfo, "window"}, - {"theme:entry", &ltk_entry_get_theme_parseinfo, "window"}, - {"theme:label", &ltk_label_get_theme_parseinfo, "window"}, - {"theme:scrollbar", &ltk_scrollbar_get_theme_parseinfo, "window"}, - {"theme:menu", &ltk_menu_get_theme_parseinfo, "window"}, - {"theme:menuentry", &ltk_menuentry_get_theme_parseinfo, "window"}, - {"theme:submenu", &ltk_submenu_get_theme_parseinfo, "window"}, - {"theme:submenuentry", &ltk_submenuentry_get_theme_parseinfo, "window"}, + {"general", &ltk_general_get_theme_parseinfo, NULL, 0}, + {"theme:window", &ltk_window_get_theme_parseinfo, NULL, 0}, + {"theme:button", &ltk_button_get_theme_parseinfo, "theme:window", 0}, + {"theme:entry", &ltk_entry_get_theme_parseinfo, "theme:window", 0}, + {"theme:label", &ltk_label_get_theme_parseinfo, "theme:window", 0}, + {"theme:scrollbar", &ltk_scrollbar_get_theme_parseinfo, "theme:window", 0}, + {"theme:menu", &ltk_menu_get_theme_parseinfo, "theme:window", 0}, + {"theme:menuentry", &ltk_menuentry_get_theme_parseinfo, "theme:window", 0}, + {"theme:submenu", &ltk_submenu_get_theme_parseinfo, "theme:window", 0}, + {"theme:submenuentry", &ltk_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 */