button.c - ltk - GUI toolkit for X11 (WIP)
HTML git clone git://lumidify.org/ltk.git (fast, but not encrypted)
HTML git clone https://lumidify.org/ltk.git (encrypted, but very slow)
HTML git clone git://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/ltk.git (over tor)
DIR Log
DIR Files
DIR Refs
DIR README
DIR LICENSE
---
button.c (7200B)
---
1 /*
2 * Copyright (c) 2016-2024 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
19 #include "config.h"
20 #include "button.h"
21 #include "color.h"
22 #include "graphics.h"
23 #include "ltk.h"
24 #include "memory.h"
25 #include "rect.h"
26 #include "text.h"
27 #include "util.h"
28 #include "widget.h"
29
30 #define MAX_BUTTON_BORDER_WIDTH 10000
31 #define MAX_BUTTON_PADDING 50000
32
33 static void ltk_button_draw(ltk_widget *self, ltk_surface *draw_surf, int x, int y, ltk_rect clip);
34 static int ltk_button_release(ltk_widget *self);
35 static void ltk_button_destroy(ltk_widget *self, int shallow);
36 static void ltk_button_recalc_ideal_size(ltk_widget *self);
37
38 static struct ltk_widget_vtable vtable = {
39 .key_press = NULL,
40 .key_release = NULL,
41 .mouse_press = NULL,
42 .mouse_release = NULL,
43 .release = <k_button_release,
44 .motion_notify = NULL,
45 .mouse_leave = NULL,
46 .mouse_enter = NULL,
47 .change_state = NULL,
48 .get_child_at_pos = NULL,
49 .resize = NULL,
50 .hide = NULL,
51 .draw = <k_button_draw,
52 .destroy = <k_button_destroy,
53 .child_size_change = NULL,
54 .remove_child = NULL,
55 .recalc_ideal_size = <k_button_recalc_ideal_size,
56 .type = LTK_WIDGET_BUTTON,
57 .flags = LTK_NEEDS_REDRAW | LTK_ACTIVATABLE_ALWAYS,
58 .invalid_signal = LTK_BUTTON_SIGNAL_INVALID,
59 };
60
61 static struct {
62 ltk_color *text_color;
63
64 ltk_color *border;
65 ltk_color *fill;
66
67 ltk_color *border_pressed;
68 ltk_color *fill_pressed;
69
70 ltk_color *border_hover;
71 ltk_color *fill_hover;
72
73 ltk_color *border_active;
74 ltk_color *fill_active;
75
76 ltk_color *border_disabled;
77 ltk_color *fill_disabled;
78
79 char *font;
80 ltk_size border_width;
81 ltk_size pad;
82 ltk_size font_size;
83 } theme;
84
85 static ltk_theme_parseinfo parseinfo[] = {
86 {"border", THEME_COLOR, {.color = &theme.border}, {.color = "#339999"}, 0, 0, 0},
87 {"border-hover", THEME_COLOR, {.color = &theme.border_hover}, {.color = "#FFFFFF"}, 0, 0, 0},
88 {"border-active", THEME_COLOR, {.color = &theme.border_active}, {.color = "#FFFFFF"}, 0, 0, 0},
89 {"border-disabled", THEME_COLOR, {.color = &theme.border_disabled}, {.color = "#FFFFFF"}, 0, 0, 0},
90 {"border-pressed", THEME_COLOR, {.color = &theme.border_pressed}, {.color = "#FFFFFF"}, 0, 0, 0},
91 {"border-width", THEME_SIZE, {.size = &theme.border_width}, {.size = {.val = 50, .unit = LTK_UNIT_MM}}, 0, MAX_BUTTON_BORDER_WIDTH, 0},
92 {"font-size", THEME_SIZE, {.size = &theme.font_size}, {.size = {.val = 1200, .unit = LTK_UNIT_PT}}, 0, 20000, 0},
93 {"fill", THEME_COLOR, {.color = &theme.fill}, {.color = "#113355"}, 0, 0, 0},
94 {"fill-hover", THEME_COLOR, {.color = &theme.fill_hover}, {.color = "#738194"}, 0, 0, 0},
95 {"fill-active", THEME_COLOR, {.color = &theme.fill_active}, {.color = "#113355"}, 0, 0, 0},
96 {"fill-disabled", THEME_COLOR, {.color = &theme.fill_disabled}, {.color = "#292929"}, 0, 0, 0},
97 {"fill-pressed", THEME_COLOR, {.color = &theme.fill_pressed}, {.color = "#113355"}, 0, 0, 0},
98 {"pad", THEME_SIZE, {.size = &theme.pad}, {.size = {.val = 100, .unit = LTK_UNIT_MM}}, 0, MAX_BUTTON_PADDING, 0},
99 {"text-color", THEME_COLOR, {.color = &theme.text_color}, {.color = "#FFFFFF"}, 0, 0, 0},
100 {"font", THEME_STRING, {.str = &theme.font}, {.str = "Monospace"}, 0, 0, 0},
101 };
102
103 void
104 ltk_button_get_theme_parseinfo(ltk_theme_parseinfo **p, size_t *len) {
105 *p = parseinfo;
106 *len = LENGTH(parseinfo);
107 }
108
109 static void
110 ltk_button_draw(ltk_widget *self, ltk_surface *draw_surf, int x, int y, ltk_rect clip) {
111 ltk_button *button = LTK_CAST_BUTTON(self);
112 ltk_rect lrect = self->lrect;
113 ltk_rect clip_final = ltk_rect_intersect(clip, (ltk_rect){0, 0, lrect.w, lrect.h});
114 if (clip_final.w <= 0 || clip_final.h <= 0)
115 return;
116
117 int bw = ltk_size_to_pixel(theme.border_width, self->last_dpi);
118 ltk_color *border = NULL, *fill = NULL;
119 /* FIXME: HOVERACTIVE STATE */
120 if (self->state & LTK_DISABLED) {
121 border = theme.border_disabled;
122 fill = theme.fill_disabled;
123 } else if (self->state & LTK_PRESSED) {
124 border = theme.border_pressed;
125 fill = theme.fill_pressed;
126 } else if (self->state & LTK_HOVER) {
127 border = theme.border_hover;
128 fill = theme.fill_hover;
129 } else if (self->state & LTK_ACTIVE) {
130 border = theme.border_active;
131 fill = theme.fill_active;
132 } else {
133 border = theme.border;
134 fill = theme.fill;
135 }
136 /* FIXME: helper functions for these common rect calculations */
137 ltk_rect draw_rect = {x, y, lrect.w, lrect.h};
138 ltk_rect draw_clip = {x + clip_final.x, y + clip_final.y, clip_final.w, clip_final.h};
139 ltk_surface_fill_rect(draw_surf, fill, draw_clip);
140 /* FIXME: support theme setting for border sides */
141 if (bw > 0) {
142 ltk_surface_draw_border_clipped(
143 draw_surf, border, draw_rect, bw, LTK_BORDER_ALL, draw_clip
144 );
145 }
146 int text_w, text_h;
147 ltk_text_line_get_size(button->tl, &text_w, &text_h);
148 int text_x = x + (lrect.w - text_w) / 2;
149 int text_y = y + (lrect.h - text_h) / 2;
150 ltk_text_line_draw_clipped(button->tl, draw_surf, theme.text_color, text_x, text_y, draw_clip);
151 /* FIXME: only redraw if dirty (needs to be handled higher-up to only
152 call draw when dirty or window rect invalidated */
153 self->dirty = 0;
154 }
155
156 static int
157 ltk_button_release(ltk_widget *self) {
158 ltk_widget_emit_signal(self, LTK_BUTTON_SIGNAL_PRESSED, LTK_EMPTY_ARGLIST);
159 return 1;
160 }
161
162 static void
163 recalc_ideal_size(ltk_button *button) {
164 int text_w, text_h;
165 ltk_text_line_get_size(button->tl, &text_w, &text_h);
166 int bw = ltk_size_to_pixel(theme.border_width, LTK_CAST_WIDGET(button)->last_dpi);
167 int pad = ltk_size_to_pixel(theme.pad, LTK_CAST_WIDGET(button)->last_dpi);
168 button->widget.ideal_w = text_w + bw * 2 + pad * 2;
169 button->widget.ideal_h = text_h + bw * 2 + pad * 2;
170 }
171
172 static void
173 ltk_button_recalc_ideal_size(ltk_widget *self) {
174 ltk_button *button = LTK_CAST_BUTTON(self);
175 int font_size = ltk_size_to_pixel(theme.font_size, self->last_dpi);
176 ltk_text_line_set_font_size(button->tl, font_size);
177 recalc_ideal_size(button);
178 }
179
180 ltk_button *
181 ltk_button_create(ltk_window *window, const char *text) {
182 ltk_button *button = ltk_malloc(sizeof(ltk_button));
183 ltk_fill_widget_defaults(LTK_CAST_WIDGET(button), window, &vtable, 0, 0);
184
185 button->tl = ltk_text_line_create_const_text_default(
186 theme.font, ltk_size_to_pixel(theme.font_size, button->widget.last_dpi), text, -1
187 );
188 recalc_ideal_size(button);
189 button->widget.dirty = 1;
190
191 return button;
192 }
193
194 static void
195 ltk_button_destroy(ltk_widget *self, int shallow) {
196 (void)shallow;
197 ltk_button *button = LTK_CAST_BUTTON(self);
198 if (!button) {
199 ltk_warn("Tried to destroy NULL button.\n");
200 return;
201 }
202 ltk_text_line_destroy(button->tl);
203 ltk_free(button);
204 }