URI: 
       tbutton.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
       ---
       tbutton.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)