button.c (7256B)
1 /* 2 * This file is part of the Lumidify ToolKit (LTK) 3 * Copyright (c) 2016, 2017, 2018 lumidify <nobody@lumidify.org> 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a copy 6 * of this software and associated documentation files (the "Software"), to deal 7 * in the Software without restriction, including without limitation the rights 8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 * copies of the Software, and to permit persons to whom the Software is 10 * furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice shall be included in all 13 * copies or substantial portions of the Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 * SOFTWARE. 22 */ 23 24 #include <stdio.h> 25 #include <stdint.h> 26 #include <X11/Xlib.h> 27 #include <X11/Xutil.h> 28 #include "khash.h" 29 #include "ltk.h" 30 #include "stb_truetype.h" 31 #include <fribidi.h> 32 #include <harfbuzz/hb.h> 33 #include <fontconfig/fontconfig.h> 34 #include "text_common.h" 35 #include "array.h" 36 #include "text_buffer.h" 37 #include "button.h" 38 39 void 40 ltk_button_ini_handler(LtkTheme *theme, const char *prop, const char *value) { 41 if (theme->button == NULL) { 42 theme->button = malloc(sizeof(LtkButtonTheme)); 43 } 44 if (strcmp(prop, "border_width") == 0) { 45 theme->button->border_width = atoi(value); 46 } else if (strcmp(prop, "font_size") == 0) { 47 theme->button->font_size = atoi(value); 48 } else if (strcmp(prop, "pad") == 0) { 49 theme->button->pad = atoi(value); 50 } else if (strcmp(prop, "border") == 0) { 51 theme->button->border = ltk_create_xcolor(value); 52 } else if (strcmp(prop, "fill") == 0) { 53 theme->button->fill = ltk_create_xcolor(value); 54 } else if (strcmp(prop, "border_hover") == 0) { 55 theme->button->border_hover = ltk_create_xcolor(value); 56 } else if (strcmp(prop, "fill_hover") == 0) { 57 theme->button->fill_hover = ltk_create_xcolor(value); 58 } else if (strcmp(prop, "border_pressed") == 0) { 59 theme->button->border_pressed = ltk_create_xcolor(value); 60 } else if (strcmp(prop, "fill_pressed") == 0) { 61 theme->button->fill_pressed = ltk_create_xcolor(value); 62 } else if (strcmp(prop, "border_active") == 0) { 63 theme->button->border_active = ltk_create_xcolor(value); 64 } else if (strcmp(prop, "fill_active") == 0) { 65 theme->button->fill_active = ltk_create_xcolor(value); 66 } else if (strcmp(prop, "border_disabled") == 0) { 67 theme->button->border_disabled = ltk_create_xcolor(value); 68 } else if (strcmp(prop, "fill_disabled") == 0) { 69 theme->button->fill_disabled = ltk_create_xcolor(value); 70 } else if (strcmp(prop, "text_color") == 0) { 71 theme->button->text_color = ltk_create_xcolor(value); 72 } else { 73 (void)printf("WARNING: Unknown property \"%s\" for button style.\n"); 74 } 75 } 76 77 void 78 ltk_button_draw(LtkButton *button) { 79 LtkTheme *global_theme = ltk_get_theme(); 80 LtkButtonTheme *theme = global_theme->button; 81 LtkWindow *window = button->widget.window; 82 LtkRect rect = button->widget.rect; 83 int bw = theme->border_width; 84 XColor border; 85 XColor fill; 86 XImage *img; 87 int text_x, text_y; 88 switch (button->widget.state) { 89 case LTK_NORMAL: 90 border = theme->border; 91 fill = theme->fill; 92 img = button->text; 93 break; 94 case LTK_HOVERACTIVE: 95 case LTK_HOVER: 96 border = theme->border_hover; 97 fill = theme->fill_hover; 98 img = button->text_hover; 99 break; 100 case LTK_PRESSED: 101 border = theme->border_pressed; 102 fill = theme->fill_pressed; 103 img = button->text_pressed; 104 break; 105 case LTK_ACTIVE: 106 border = theme->border_active; 107 fill = theme->fill_active; 108 img = button->text_active; 109 break; 110 case LTK_DISABLED: 111 border = theme->border_disabled; 112 fill = theme->fill_disabled; 113 img = button->text_disabled; 114 break; 115 default: 116 ltk_fatal("No style found for button!\n"); 117 } 118 Display *display = ltk_get_display(); 119 Colormap colormap = ltk_get_colormap(); 120 XSetForeground(display, window->gc, fill.pixel); 121 XFillRectangle(display, window->xwindow, window->gc, rect.x, rect.y, rect.w, rect.h); 122 /* FIXME: Why did I do this? */ 123 if (bw < 1) return; 124 XSetForeground(display, window->gc, border.pixel); 125 XSetLineAttributes(display, window->gc, bw, LineSolid, CapButt, JoinMiter); 126 XDrawRectangle(display, window->xwindow, window->gc, rect.x + bw / 2, rect.y + bw / 2, rect.w - bw, rect.h - bw); 127 if (!img) { 128 img = ltk_text_line_render(button->tl, display, window->xwindow, window->gc, colormap, theme->text_color, fill); 129 /* FIXME: any nicer way to do this? */ 130 switch (button->widget.state) { 131 case LTK_NORMAL: 132 button->text = img; 133 break; 134 case LTK_HOVERACTIVE: 135 case LTK_HOVER: 136 button->text_hover = img; 137 break; 138 case LTK_PRESSED: 139 button->text_pressed = img; 140 break; 141 case LTK_ACTIVE: 142 button->text_active = img; 143 break; 144 case LTK_DISABLED: 145 button->text_disabled = img; 146 break; 147 } 148 } 149 text_x = rect.x + (rect.w - button->tl->w) / 2; 150 text_y = rect.y + (rect.h - button->tl->h) / 2; 151 XPutImage(display, window->xwindow, window->gc, img, 0, 0, text_x, text_y, button->tl->w, button->tl->h); 152 } 153 154 LtkButton * 155 ltk_button_create(LtkWindow *window, const char *text, void (*callback) (void *, XEvent, void *), void *data) { 156 LtkButton *button = malloc(sizeof(LtkButton)); 157 158 if (button == NULL) { 159 ltk_fatal("ERROR: Unable to allocate memory for LtkButton.\n"); 160 } 161 162 ltk_fill_widget_defaults(&button->widget, window, <k_button_draw, <k_button_destroy, 1); 163 button->widget.mouse_release = <k_button_mouse_release; 164 165 button->callback = callback; 166 button->data = data; 167 LtkTheme *theme = ltk_get_theme(); 168 button->tl = ltk_text_line_create(theme->button->font_size); 169 /* FIXME: support font size */ 170 ltk_text_line_insert_utf8(button->tl, 0, text); 171 ltk_text_line_wrap(button->tl, -1); 172 button->widget.rect.w = button->tl->w + (theme->button->border_width + theme->button->pad) * 2; 173 button->widget.rect.h = button->tl->h + (theme->button->border_width + theme->button->pad) * 2; 174 button->text = NULL; 175 button->text_pressed = NULL; 176 button->text_hover = NULL; 177 button->text_disabled = NULL; 178 button->text_active = NULL; 179 180 return button; 181 } 182 183 void 184 ltk_button_destroy(LtkButton *button) { 185 if (!button) { 186 (void)printf("WARNING: Tried to destroy NULL button.\n"); 187 } 188 if (button->text) XDestroyImage(button->text); 189 if (button->text_hover) XDestroyImage(button->text_hover); 190 if (button->text_pressed) XDestroyImage(button->text_pressed); 191 if (button->text_active) XDestroyImage(button->text_active); 192 if (button->text_disabled) XDestroyImage(button->text_disabled); 193 ltk_text_line_destroy(button->tl); 194 free(button); 195 } 196 197 /* FIXME: is the fixme below supposed to be for the function above? */ 198 /* FIXME: ungrid button if gridded */ 199 void 200 ltk_button_mouse_release(LtkButton *button, XEvent event) { 201 if (button->widget.state == LTK_HOVERACTIVE && button->callback) { 202 button->callback(button, event, button->data); 203 } 204 }