URI: 
       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 = &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 }