grid.c - ltkx - GUI toolkit for X11 (old)
HTML git clone git://lumidify.org/ltkx.git (fast, but not encrypted)
HTML git clone https://lumidify.org/ltkx.git (encrypted, but very slow)
HTML git clone git://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/ltkx.git (over tor)
DIR Log
DIR Files
DIR Refs
DIR README
DIR LICENSE
---
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 }