ltkx

GUI toolkit for X11 (old)
git clone git://lumidify.org/ltkx.git (fast, but not encrypted)
git clone https://lumidify.org/ltkx.git (encrypted, but very slow)
git clone git://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/ltkx.git (over tor)
Log | Files | Refs | README | 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, &ltk_draw_grid, &ltk_destroy_grid, 0);
     60 	grid->widget.mouse_press = &ltk_grid_mouse_press;
     61 	grid->widget.mouse_release = &ltk_grid_mouse_release;
     62 	grid->widget.motion_notify = &ltk_grid_motion_notify;
     63 	grid->widget.resize = &ltk_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 }