ltk

Socket-based GUI for X11 (WIP)
git clone git://lumidify.org/ltk.git (fast, but not encrypted)
git clone https://lumidify.org/git/ltk.git (encrypted, but very slow)
Log | Files | Refs | README | LICENSE

button.c (8299B)


      1 /*
      2  * Copyright (c) 2016-2023 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 "proto_types.h"
     24 #include "event.h"
     25 #include "memory.h"
     26 #include "color.h"
     27 #include "rect.h"
     28 #include "widget.h"
     29 #include "ltk.h"
     30 #include "util.h"
     31 #include "text.h"
     32 #include "button.h"
     33 #include "graphics.h"
     34 #include "surface_cache.h"
     35 #include "theme.h"
     36 #include "cmd.h"
     37 
     38 #define MAX_BUTTON_BORDER_WIDTH 100
     39 #define MAX_BUTTON_PADDING 500
     40 
     41 static void ltk_button_draw(ltk_widget *self, ltk_surface *draw_surf, int x, int y, ltk_rect clip);
     42 static int ltk_button_release(ltk_widget *self);
     43 static ltk_button *ltk_button_create(ltk_window *window,
     44     const char *id, char *text);
     45 static void ltk_button_destroy(ltk_widget *self, int shallow);
     46 static void ltk_button_redraw_surface(ltk_button *button, ltk_surface *s);
     47 
     48 static struct ltk_widget_vtable vtable = {
     49 	.key_press = NULL,
     50 	.key_release = NULL,
     51 	.mouse_press = NULL,
     52 	.mouse_release = NULL,
     53 	.release = &ltk_button_release,
     54 	.motion_notify = NULL,
     55 	.mouse_leave = NULL,
     56 	.mouse_enter = NULL,
     57 	.change_state = NULL,
     58 	.get_child_at_pos = NULL,
     59 	.resize = NULL,
     60 	.hide = NULL,
     61 	.draw = &ltk_button_draw,
     62 	.destroy = &ltk_button_destroy,
     63 	.child_size_change = NULL,
     64 	.remove_child = NULL,
     65 	.type = LTK_WIDGET_BUTTON,
     66 	.flags = LTK_NEEDS_REDRAW | LTK_ACTIVATABLE_ALWAYS,
     67 };
     68 
     69 static struct {
     70 	int border_width;
     71 	ltk_color text_color;
     72 	int pad;
     73 
     74 	ltk_color border;
     75 	ltk_color fill;
     76 
     77 	ltk_color border_pressed;
     78 	ltk_color fill_pressed;
     79 
     80 	ltk_color border_hover;
     81 	ltk_color fill_hover;
     82 
     83 	ltk_color border_active;
     84 	ltk_color fill_active;
     85 
     86 	ltk_color border_disabled;
     87 	ltk_color fill_disabled;
     88 } theme;
     89 
     90 static ltk_theme_parseinfo parseinfo[] = {
     91 	{"border", THEME_COLOR, {.color = &theme.border}, {.color = "#339999"}, 0, 0, 0},
     92 	{"border-hover", THEME_COLOR, {.color = &theme.border_hover}, {.color = "#FFFFFF"}, 0, 0, 0},
     93 	{"border-active", THEME_COLOR, {.color = &theme.border_active}, {.color = "#FFFFFF"}, 0, 0, 0},
     94 	{"border-disabled", THEME_COLOR, {.color = &theme.border_disabled}, {.color = "#FFFFFF"}, 0, 0, 0},
     95 	{"border-pressed", THEME_COLOR, {.color = &theme.border_pressed}, {.color = "#FFFFFF"}, 0, 0, 0},
     96 	{"border-width", THEME_INT, {.i = &theme.border_width}, {.i = 2}, 0, MAX_BUTTON_BORDER_WIDTH, 0},
     97 	{"fill", THEME_COLOR, {.color = &theme.fill}, {.color = "#113355"}, 0, 0, 0},
     98 	{"fill-hover", THEME_COLOR, {.color = &theme.fill_hover}, {.color = "#738194"}, 0, 0, 0},
     99 	{"fill-active", THEME_COLOR, {.color = &theme.fill_active}, {.color = "#113355"}, 0, 0, 0},
    100 	{"fill-disabled", THEME_COLOR, {.color = &theme.fill_disabled}, {.color = "#292929"}, 0, 0, 0},
    101 	{"fill-pressed", THEME_COLOR, {.color = &theme.fill_pressed}, {.color = "#113355"}, 0, 0, 0},
    102 	{"pad", THEME_INT, {.i = &theme.pad}, {.i = 5}, 0, MAX_BUTTON_PADDING, 0},
    103 	{"text-color", THEME_COLOR, {.color = &theme.text_color}, {.color = "#FFFFFF"}, 0, 0, 0},
    104 };
    105 static int parseinfo_sorted = 0;
    106 
    107 int
    108 ltk_button_ini_handler(ltk_window *window, const char *prop, const char *value) {
    109 	return ltk_theme_handle_value(window, "button", prop, value, parseinfo, LENGTH(parseinfo), &parseinfo_sorted);
    110 }
    111 
    112 int
    113 ltk_button_fill_theme_defaults(ltk_window *window) {
    114 	return ltk_theme_fill_defaults(window, "button", parseinfo, LENGTH(parseinfo));
    115 }
    116 
    117 void
    118 ltk_button_uninitialize_theme(ltk_window *window) {
    119 	ltk_theme_uninitialize(window, parseinfo, LENGTH(parseinfo));
    120 }
    121 
    122 /* FIXME: only keep text in surface to avoid large surface */
    123 static void
    124 ltk_button_draw(ltk_widget *self, ltk_surface *draw_surf, int x, int y, ltk_rect clip) {
    125 	ltk_button *button = (ltk_button *)self;
    126 	ltk_rect lrect = self->lrect;
    127 	ltk_rect clip_final = ltk_rect_intersect(clip, (ltk_rect){0, 0, lrect.w, lrect.h});
    128 	if (clip_final.w <= 0 || clip_final.h <= 0)
    129 		return;
    130 	ltk_surface *s;
    131 	ltk_surface_cache_request_surface_size(button->key, lrect.w, lrect.h);
    132 	if (!ltk_surface_cache_get_surface(button->key, &s) || self->dirty)
    133 		ltk_button_redraw_surface(button, s);
    134 	ltk_surface_copy(s, draw_surf, clip_final, x + clip_final.x, y + clip_final.y);
    135 }
    136 
    137 static void
    138 ltk_button_redraw_surface(ltk_button *button, ltk_surface *s) {
    139 	ltk_rect rect = button->widget.lrect;
    140 	int bw = theme.border_width;
    141 	ltk_color *border = NULL, *fill = NULL;
    142 	/* FIXME: HOVERACTIVE STATE */
    143 	if (button->widget.state & LTK_DISABLED) {
    144 		border = &theme.border_disabled;
    145 		fill = &theme.fill_disabled;
    146 	} else if (button->widget.state & LTK_PRESSED) {
    147 		border = &theme.border_pressed;
    148 		fill = &theme.fill_pressed;
    149 	} else if (button->widget.state & LTK_HOVER) {
    150 		border = &theme.border_hover;
    151 		fill = &theme.fill_hover;
    152 	} else if (button->widget.state & LTK_ACTIVE) {
    153 		border = &theme.border_active;
    154 		fill = &theme.fill_active;
    155 	} else {
    156 		border = &theme.border;
    157 		fill = &theme.fill;
    158 	}
    159 	rect.x = 0;
    160 	rect.y = 0;
    161 	ltk_surface_fill_rect(s, fill, rect);
    162 	if (bw > 0)
    163 		ltk_surface_draw_rect(s, border, (ltk_rect){bw / 2, bw / 2, rect.w - bw, rect.h - bw}, bw);
    164 
    165 	int text_w, text_h;
    166 	ltk_text_line_get_size(button->tl, &text_w, &text_h);
    167 	int text_x = (rect.w - text_w) / 2;
    168 	int text_y = (rect.h - text_h) / 2;
    169 	ltk_text_line_draw(button->tl, s, &theme.text_color, text_x, text_y);
    170 	button->widget.dirty = 0;
    171 }
    172 
    173 static int
    174 ltk_button_release(ltk_widget *self) {
    175 	ltk_queue_specific_event(self, "button", LTK_PWEVENTMASK_BUTTON_PRESS, "press");
    176 	return 1;
    177 }
    178 
    179 static ltk_button *
    180 ltk_button_create(ltk_window *window, const char *id, char *text) {
    181 	ltk_button *button = ltk_malloc(sizeof(ltk_button));
    182 
    183 	uint16_t font_size = window->theme->font_size;
    184 	button->tl = ltk_text_line_create(window->text_context, font_size, text, 0, -1);
    185 	int text_w, text_h;
    186 	ltk_text_line_get_size(button->tl, &text_w, &text_h);
    187 	ltk_fill_widget_defaults(&button->widget, id, window, &vtable, button->widget.ideal_w, button->widget.ideal_h);
    188 	button->widget.ideal_w = text_w + theme.border_width * 2 + theme.pad * 2;
    189 	button->widget.ideal_h = text_h + theme.border_width * 2 + theme.pad * 2;
    190 	button->key = ltk_surface_cache_get_unnamed_key(window->surface_cache, button->widget.ideal_w, button->widget.ideal_h);
    191 	button->widget.dirty = 1;
    192 
    193 	return button;
    194 }
    195 
    196 static void
    197 ltk_button_destroy(ltk_widget *self, int shallow) {
    198 	(void)shallow;
    199 	ltk_button *button = (ltk_button *)self;
    200 	if (!button) {
    201 		ltk_warn("Tried to destroy NULL button.\n");
    202 		return;
    203 	}
    204 	ltk_surface_cache_release_key(button->key);
    205 	ltk_text_line_destroy(button->tl);
    206 	ltk_free(button);
    207 }
    208 
    209 /* button <button id> create <text> */
    210 static int
    211 ltk_button_cmd_create(
    212     ltk_window *window,
    213     ltk_button *button_unneeded,
    214     ltk_cmd_token *tokens,
    215     size_t num_tokens,
    216     ltk_error *err) {
    217 	(void)button_unneeded;
    218 	ltk_cmdarg_parseinfo cmd[] = {
    219 		{.type = CMDARG_IGNORE, .optional = 0},
    220 		{.type = CMDARG_STRING, .optional = 0},
    221 		{.type = CMDARG_IGNORE, .optional = 0},
    222 		{.type = CMDARG_STRING, .optional = 0},
    223 	};
    224 	if (ltk_parse_cmd(window, tokens, num_tokens, cmd, LENGTH(cmd), err))
    225 		return 1;
    226 	if (!ltk_widget_id_free(cmd[1].val.str)) {
    227 		err->type = ERR_WIDGET_ID_IN_USE;
    228 		err->arg = 1;
    229 		return 1;
    230 	}
    231 	ltk_button *button = ltk_button_create(window, cmd[1].val.str, cmd[3].val.str);
    232 	ltk_set_widget((ltk_widget *)button, cmd[1].val.str);
    233 
    234 	return 0;
    235 }
    236 
    237 static struct button_cmd {
    238 	char *name;
    239 	int (*func)(ltk_window *, ltk_button *, ltk_cmd_token *, size_t, ltk_error *);
    240 	int needs_all;
    241 } button_cmds[] = {
    242 	{"create", &ltk_button_cmd_create, 1},
    243 };
    244 
    245 GEN_CMD_HELPERS(ltk_button_cmd, LTK_WIDGET_BUTTON, ltk_button, button_cmds, struct button_cmd)