label.c (6148B)
1 /* 2 * Copyright (c) 2021, 2022 lumidify <nobody@lumidify.org> 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 #include <stdio.h> 18 #include <stdlib.h> 19 #include <stdint.h> 20 #include <string.h> 21 #include <stdarg.h> 22 23 #include "event.h" 24 #include "memory.h" 25 #include "color.h" 26 #include "rect.h" 27 #include "widget.h" 28 #include "ltk.h" 29 #include "util.h" 30 #include "text.h" 31 #include "label.h" 32 #include "graphics.h" 33 #include "surface_cache.h" 34 #include "theme.h" 35 #include "cmd.h" 36 37 #define MAX_LABEL_PADDING 500 38 39 static void ltk_label_draw(ltk_widget *self, ltk_surface *draw_surf, int x, int y, ltk_rect clip); 40 static ltk_label *ltk_label_create(ltk_window *window, 41 const char *id, char *text); 42 static void ltk_label_destroy(ltk_widget *self, int shallow); 43 static void ltk_label_redraw_surface(ltk_label *label, ltk_surface *s); 44 45 static struct ltk_widget_vtable vtable = { 46 .draw = <k_label_draw, 47 .destroy = <k_label_destroy, 48 .hide = NULL, 49 .resize = NULL, 50 .change_state = NULL, 51 .child_size_change = NULL, 52 .remove_child = NULL, 53 .get_child_at_pos = NULL, 54 .key_press = NULL, 55 .key_release = NULL, 56 .mouse_press = NULL, 57 .mouse_release = NULL, 58 .motion_notify = NULL, 59 .mouse_leave = NULL, 60 .mouse_enter = NULL, 61 .type = LTK_WIDGET_LABEL, 62 .flags = LTK_NEEDS_REDRAW | LTK_ACTIVATABLE_SPECIAL, 63 }; 64 65 static struct { 66 ltk_color text_color; 67 ltk_color bg_color; 68 ltk_color bg_color_active; 69 int pad; 70 } theme; 71 72 int parseinfo_sorted = 0; 73 74 static ltk_theme_parseinfo parseinfo[] = { 75 {"bg-color", THEME_COLOR, {.color = &theme.bg_color}, {.color = "#000000"}, 0, 0, 0}, 76 {"bg-color-active", THEME_COLOR, {.color = &theme.bg_color_active}, {.color = "#222222"}, 0, 0, 0}, 77 {"pad", THEME_INT, {.i = &theme.pad}, {.i = 5}, 0, MAX_LABEL_PADDING, 0}, 78 {"text-color", THEME_COLOR, {.color = &theme.text_color}, {.color = "#FFFFFF"}, 0, 0, 0}, 79 }; 80 81 int 82 ltk_label_ini_handler(ltk_window *window, const char *prop, const char *value) { 83 return ltk_theme_handle_value(window, "label", prop, value, parseinfo, LENGTH(parseinfo), &parseinfo_sorted); 84 } 85 86 int 87 ltk_label_fill_theme_defaults(ltk_window *window) { 88 return ltk_theme_fill_defaults(window, "label", parseinfo, LENGTH(parseinfo)); 89 } 90 91 void 92 ltk_label_uninitialize_theme(ltk_window *window) { 93 ltk_theme_uninitialize(window, parseinfo, LENGTH(parseinfo)); 94 } 95 96 static void 97 ltk_label_draw(ltk_widget *self, ltk_surface *draw_surf, int x, int y, ltk_rect clip) { 98 ltk_label *label = (ltk_label *)self; 99 ltk_rect lrect = self->lrect; 100 ltk_rect clip_final = ltk_rect_intersect(clip, (ltk_rect){0, 0, lrect.w, lrect.h}); 101 if (clip_final.w <= 0 || clip_final.h <= 0) 102 return; 103 ltk_surface *s; 104 ltk_surface_cache_request_surface_size(label->key, lrect.w, lrect.h); 105 if (!ltk_surface_cache_get_surface(label->key, &s) || self->dirty) 106 ltk_label_redraw_surface(label, s); 107 ltk_surface_copy(s, draw_surf, clip_final, x + clip_final.x, y + clip_final.y); 108 } 109 110 static void 111 ltk_label_redraw_surface(ltk_label *label, ltk_surface *s) { 112 ltk_rect r = label->widget.lrect; 113 r.x = 0; 114 r.y = 0; 115 ltk_surface_fill_rect(s, (label->widget.state & LTK_ACTIVE) ? &theme.bg_color_active : &theme.bg_color, r); 116 117 int text_w, text_h; 118 ltk_text_line_get_size(label->tl, &text_w, &text_h); 119 int text_x = (r.w - text_w) / 2; 120 int text_y = (r.h - text_h) / 2; 121 ltk_text_line_draw(label->tl, s, &theme.text_color, text_x, text_y); 122 } 123 124 static ltk_label * 125 ltk_label_create(ltk_window *window, const char *id, char *text) { 126 ltk_label *label = ltk_malloc(sizeof(ltk_label)); 127 128 uint16_t font_size = window->theme->font_size; 129 label->tl = ltk_text_line_create(window->text_context, font_size, text, 0, -1); 130 int text_w, text_h; 131 ltk_text_line_get_size(label->tl, &text_w, &text_h); 132 /* FIXME: what was I even thinking here? label->widget.ideal_{w,h} isn't even initialized here */ 133 ltk_fill_widget_defaults(&label->widget, id, window, &vtable, label->widget.ideal_w, label->widget.ideal_h); 134 label->widget.ideal_w = text_w + theme.pad * 2; 135 label->widget.ideal_h = text_h + theme.pad * 2; 136 label->key = ltk_surface_cache_get_unnamed_key(window->surface_cache, label->widget.ideal_w, label->widget.ideal_h); 137 138 return label; 139 } 140 141 static void 142 ltk_label_destroy(ltk_widget *self, int shallow) { 143 (void)shallow; 144 ltk_label *label = (ltk_label *)self; 145 if (!label) { 146 ltk_warn("Tried to destroy NULL label.\n"); 147 return; 148 } 149 ltk_surface_cache_release_key(label->key); 150 ltk_text_line_destroy(label->tl); 151 ltk_free(label); 152 } 153 154 /* label <label id> create <text> */ 155 static int 156 ltk_label_cmd_create( 157 ltk_window *window, 158 ltk_label *label_unneeded, 159 ltk_cmd_token *tokens, 160 size_t num_tokens, 161 ltk_error *err) { 162 (void)label_unneeded; 163 ltk_cmdarg_parseinfo cmd[] = { 164 {.type = CMDARG_IGNORE, .optional = 0}, 165 {.type = CMDARG_STRING, .optional = 0}, 166 {.type = CMDARG_IGNORE, .optional = 0}, 167 {.type = CMDARG_STRING, .optional = 0}, 168 }; 169 if (ltk_parse_cmd(window, tokens, num_tokens, cmd, LENGTH(cmd), err)) 170 return 1; 171 if (!ltk_widget_id_free(cmd[1].val.str)) { 172 err->type = ERR_WIDGET_ID_IN_USE; 173 err->arg = 1; 174 return 1; 175 } 176 ltk_label *label = ltk_label_create(window, cmd[1].val.str, cmd[3].val.str); 177 ltk_set_widget((ltk_widget *)label, cmd[1].val.str); 178 179 return 0; 180 } 181 182 static struct label_cmd { 183 char *name; 184 int (*func)(ltk_window *, ltk_label *, ltk_cmd_token *, size_t, ltk_error *); 185 int needs_all; 186 } label_cmds[] = { 187 {"create", <k_label_cmd_create, 1}, 188 }; 189 190 GEN_CMD_HELPERS(ltk_label_cmd, LTK_WIDGET_LABEL, ltk_label, label_cmds, struct label_cmd)