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

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 = &ltk_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 = &ltk_button_draw,
     52 	.destroy = &ltk_button_destroy,
     53 	.child_size_change = NULL,
     54 	.remove_child = NULL,
     55 	.recalc_ideal_size = &ltk_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 }