tlabel.c - ltk - Socket-based GUI for X11 (WIP)
HTML git clone git://lumidify.org/ltk.git (fast, but not encrypted)
HTML git clone https://lumidify.org/git/ltk.git (encrypted, but very slow)
DIR Log
DIR Files
DIR Refs
DIR README
DIR LICENSE
---
tlabel.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)