grid.c (8959B)
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 /* TODO: remove_widget function that also adjusts static width */ 25 26 #include <X11/Xlib.h> 27 #include <X11/Xutil.h> 28 #include "khash.h" 29 #include "ltk.h" 30 #include "grid.h" 31 32 void ltk_set_row_weight(LtkGrid * grid, int row, int weight) 33 { 34 grid->row_weights[row] = weight; 35 ltk_recalculate_grid(grid); 36 } 37 38 void ltk_set_column_weight(LtkGrid * grid, int column, int weight) 39 { 40 grid->column_weights[column] = weight; 41 ltk_recalculate_grid(grid); 42 } 43 44 void ltk_draw_grid(LtkGrid *grid) 45 { 46 int i; 47 for (i = 0; i < grid->rows * grid->columns; i++) { 48 if (!grid->widget_grid[i]) 49 continue; 50 LtkWidget *ptr = grid->widget_grid[i]; 51 ptr->draw(ptr); 52 } 53 } 54 55 LtkGrid *ltk_create_grid(LtkWindow *window, int rows, int columns) 56 { 57 LtkGrid *grid = malloc(sizeof(LtkGrid)); 58 59 ltk_fill_widget_defaults(&grid->widget, window, <k_draw_grid, <k_destroy_grid, 0); 60 grid->widget.mouse_press = <k_grid_mouse_press; 61 grid->widget.mouse_release = <k_grid_mouse_release; 62 grid->widget.motion_notify = <k_grid_motion_notify; 63 grid->widget.resize = <k_recalculate_grid; 64 65 grid->rows = rows; 66 grid->columns = columns; 67 grid->widget_grid = malloc(rows * columns * sizeof(LtkWidget)); 68 grid->row_heights = malloc(rows * sizeof(int)); 69 grid->column_widths = malloc(rows * sizeof(int)); 70 grid->row_weights = malloc(rows * sizeof(int)); 71 grid->column_weights = malloc(columns * sizeof(int)); 72 /* Positions have one extra for the end */ 73 grid->row_pos = malloc((rows + 1) * sizeof(int)); 74 grid->column_pos = malloc((columns + 1) * sizeof(int)); 75 int i; 76 for (i = 0; i < rows; i++) { 77 grid->row_heights[i] = 0; 78 grid->row_weights[i] = 0; 79 grid->row_pos[i] = 0; 80 } 81 grid->row_pos[rows] = 0; 82 for (i = 0; i < columns; i++) { 83 grid->column_widths[i] = 0; 84 grid->column_weights[i] = 0; 85 grid->column_pos[i] = 0; 86 } 87 grid->column_pos[columns] = 0; 88 for (i = 0; i < rows * columns; i++) { 89 grid->widget_grid[i] = NULL; 90 } 91 92 ltk_recalculate_grid(grid); 93 return grid; 94 } 95 96 void ltk_destroy_grid(LtkGrid *grid) 97 { 98 LtkWidget *ptr; 99 int i; 100 for (i = 0; i < grid->rows * grid->columns; i++) { 101 if (grid->widget_grid[i]) { 102 ptr = grid->widget_grid[i]; 103 ptr->destroy(ptr); 104 } 105 } 106 free(grid->widget_grid); 107 free(grid->row_heights); 108 free(grid->column_widths); 109 free(grid->row_weights); 110 free(grid->column_weights); 111 free(grid->row_pos); 112 free(grid->column_pos); 113 free(grid); 114 } 115 116 void ltk_recalculate_grid(LtkGrid *grid) 117 { 118 unsigned int height_static = 0, width_static = 0; 119 unsigned int total_row_weight = 0, total_column_weight = 0; 120 float height_unit = 0, width_unit = 0; 121 unsigned int currentx = 0, currenty = 0; 122 int i, j; 123 for (i = 0; i < grid->rows; i++) { 124 total_row_weight += grid->row_weights[i]; 125 if (grid->row_weights[i] == 0) { 126 height_static += grid->row_heights[i]; 127 } 128 } 129 for (i = 0; i < grid->columns; i++) { 130 total_column_weight += grid->column_weights[i]; 131 if (grid->column_weights[i] == 0) { 132 width_static += grid->column_widths[i]; 133 } 134 } 135 if (total_row_weight > 0) { 136 height_unit = (float) (grid->widget.rect.h - height_static) / (float) total_row_weight; 137 } 138 if (total_column_weight > 0) { 139 width_unit = (float) (grid->widget.rect.w - width_static) / (float) total_column_weight; 140 } 141 for (i = 0; i < grid->rows; i++) { 142 grid->row_pos[i] = currenty; 143 if (grid->row_weights[i] > 0) { 144 grid->row_heights[i] = grid->row_weights[i] * height_unit; 145 } 146 currenty += grid->row_heights[i]; 147 } 148 grid->row_pos[grid->rows] = currenty; 149 for (i = 0; i < grid->columns; i++) { 150 grid->column_pos[i] = currentx; 151 if (grid->column_weights[i] > 0) { 152 grid->column_widths[i] = grid->column_weights[i] * width_unit; 153 } 154 currentx += grid->column_widths[i]; 155 } 156 grid->column_pos[grid->columns] = currentx; 157 int orig_width, orig_height; 158 int end_column, end_row; 159 for (i = 0; i < grid->rows; i++) { 160 for (j = 0; j < grid->columns; j++) { 161 if (!grid->widget_grid[i * grid->columns + j]) { 162 continue; 163 } 164 LtkWidget *ptr = grid->widget_grid[i * grid->columns + j]; 165 orig_width = ptr->rect.w; 166 orig_height = ptr->rect.h; 167 end_row = i + ptr->row_span; 168 end_column = j + ptr->column_span; 169 if (ptr->sticky & LTK_STICKY_LEFT && ptr->sticky & LTK_STICKY_RIGHT) { 170 ptr->rect.w = grid->column_pos[end_column] - grid->column_pos[j]; 171 } 172 if (ptr->sticky & LTK_STICKY_TOP && ptr->sticky & LTK_STICKY_BOTTOM) { 173 ptr->rect.h = grid->row_pos[end_row] - grid->row_pos[i]; 174 } 175 if (orig_width != ptr->rect.w || orig_height != ptr->rect.h) { 176 if (ptr->resize) { 177 ptr->resize(ptr, orig_width, orig_height); 178 } 179 } 180 181 if (ptr->sticky & LTK_STICKY_RIGHT) { 182 ptr->rect.x = grid->column_pos[end_column] - ptr->rect.w; 183 } else if (ptr->sticky & LTK_STICKY_LEFT) { 184 ptr->rect.x = grid->column_pos[j]; 185 } else { 186 ptr->rect.x = grid->column_pos[j] + ((grid->column_pos[end_column] - grid->column_pos[j]) / 2 - ptr->rect.w / 2); 187 } 188 189 if (ptr->sticky & LTK_STICKY_BOTTOM) { 190 ptr->rect.y = grid->row_pos[end_row] - ptr->rect.h; 191 } else if (ptr->sticky & LTK_STICKY_TOP) { 192 ptr->rect.y = grid->row_pos[i]; 193 } else { 194 ptr->rect.y = grid->row_pos[i] + ((grid->row_pos[end_row] - grid->row_pos[i]) / 2 - ptr->rect.h / 2); 195 } 196 } 197 } 198 } 199 200 void ltk_grid_widget(LtkWidget *widget, LtkGrid *grid, int row, int column, int row_span, int column_span, unsigned short sticky) 201 { 202 widget->sticky = sticky; 203 widget->row = row; 204 widget->column = column; 205 widget->row_span = row_span; 206 widget->column_span = column_span; 207 if (grid->column_weights[column] == 0 && widget->rect.w > grid->column_widths[column]) { 208 grid->column_widths[column] = widget->rect.w; 209 } 210 if (grid->row_weights[row] == 0 && widget->rect.h > grid->row_heights[row]) { 211 grid->row_heights[row] = widget->rect.h; 212 } 213 grid->widget_grid[widget->row * grid->columns + widget->column] = widget; 214 widget->parent = grid; 215 ltk_recalculate_grid(grid); 216 } 217 218 static int ltk_grid_find_nearest_column(LtkGrid *grid, int x) 219 { 220 int i; 221 for (i = 0; i < grid->columns; i++) { 222 if (grid->column_pos[i] <= x && grid->column_pos[i + 1] >= x) { 223 return i; 224 } 225 } 226 return -1; 227 } 228 229 static int ltk_grid_find_nearest_row(LtkGrid *grid, int y) 230 { 231 int i; 232 for (i = 0; i < grid->rows; i++) { 233 if (grid->row_pos[i] <= y && grid->row_pos[i + 1] >= y) { 234 return i; 235 } 236 } 237 return -1; 238 } 239 240 void ltk_grid_mouse_press(LtkGrid *grid, XEvent event) 241 { 242 int x = event.xbutton.x; 243 int y = event.xbutton.y; 244 int row = ltk_grid_find_nearest_row(grid, y); 245 int column = ltk_grid_find_nearest_column(grid, x); 246 if (row == -1 || column == -1) 247 return; 248 LtkWidget *ptr = grid->widget_grid[row * grid->columns + column]; 249 if (ptr && ltk_collide_rect(ptr->rect, x, y)) { 250 ltk_widget_mouse_press_event(ptr, event); 251 } 252 } 253 254 void ltk_grid_mouse_release(LtkGrid *grid, XEvent event) 255 { 256 int x = event.xbutton.x; 257 int y = event.xbutton.y; 258 int row = ltk_grid_find_nearest_row(grid, y); 259 int column = ltk_grid_find_nearest_column(grid, x); 260 if (row == -1 || column == -1) 261 return; 262 LtkWidget *ptr = grid->widget_grid[row * grid->columns + column]; 263 if (ptr) { 264 if (ltk_collide_rect(ptr->rect, x, y)) { 265 ltk_widget_mouse_release_event(ptr, event); 266 } else { 267 ltk_remove_hover_widget(grid); 268 ltk_change_active_widget_state(grid, LTK_ACTIVE); 269 } 270 } 271 } 272 273 void ltk_grid_motion_notify(LtkGrid *grid, XEvent event) 274 { 275 short pressed = (event.xmotion.state & Button1Mask) == Button1Mask; 276 if (pressed) 277 return; 278 int x = event.xbutton.x; 279 int y = event.xbutton.y; 280 int row = ltk_grid_find_nearest_row(grid, y); 281 int column = ltk_grid_find_nearest_column(grid, x); 282 if (row == -1 || column == -1) 283 return; 284 LtkWidget *ptr = grid->widget_grid[row * grid->columns + column]; 285 if (ptr) { 286 if (ltk_collide_rect(ptr->rect, x, y)) 287 ltk_widget_motion_notify_event(ptr, event); 288 else if (!pressed) 289 ltk_remove_hover_widget(grid); 290 } 291 }