source: mainline/uspace/lib/gui/grid.c@ 9c4cf0d

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 9c4cf0d was 10cb47e, checked in by Martin Decky <martin@…>, 9 years ago

add support for custom client data into widgets

  • Property mode set to 100644
File size: 9.7 KB
Line 
1/*
2 * Copyright (c) 2012 Petr Koupy
3 * Copyright (c) 2013 Martin Decky
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup gui
31 * @{
32 */
33/**
34 * @file
35 */
36
37#include <assert.h>
38#include <mem.h>
39#include <malloc.h>
40#include <surface.h>
41#include "window.h"
42#include "grid.h"
43
44typedef struct {
45 sysarg_t min;
46 sysarg_t max;
47 sysarg_t val;
48} constraints_t;
49
50static void paint_internal(widget_t *widget)
51{
52 grid_t *grid = (grid_t *) widget;
53
54 surface_t *surface = window_claim(grid->widget.window);
55 if (!surface) {
56 window_yield(grid->widget.window);
57 return;
58 }
59
60 // FIXME: Replace with (accelerated) rectangle fill
61 for (sysarg_t y = widget->vpos; y < widget->vpos + widget->height; y++) {
62 for (sysarg_t x = widget->hpos; x < widget->hpos + widget->width; x++)
63 surface_put_pixel(surface, x, y, grid->background);
64 }
65
66 window_yield(grid->widget.window);
67}
68
69static grid_cell_t *grid_cell_at(grid_t *grid, size_t col, size_t row)
70{
71 if ((col < grid->cols) && (row < grid->rows))
72 return grid->layout + (row * grid->cols + col);
73
74 return NULL;
75}
76
77static grid_cell_t *grid_coords_at(grid_t *grid, sysarg_t hpos, sysarg_t vpos)
78{
79 for (size_t c = 0; c < grid->cols; c++) {
80 for (size_t r = 0; r < grid->rows; r++) {
81 grid_cell_t *cell = grid_cell_at(grid, c, r);
82 if (cell) {
83 widget_t *widget = cell->widget;
84
85 if ((widget) && (hpos >= widget->hpos) &&
86 (vpos >= widget->vpos) &&
87 (hpos < widget->hpos + widget->width) &&
88 (vpos < widget->vpos + widget->height))
89 return cell;
90 }
91 }
92 }
93
94 return NULL;
95}
96
97void deinit_grid(grid_t *grid)
98{
99 widget_deinit(&grid->widget);
100 free(grid->layout);
101}
102
103static void grid_destroy(widget_t *widget)
104{
105 grid_t *grid = (grid_t *) widget;
106
107 deinit_grid(grid);
108 free(grid);
109}
110
111static void grid_reconfigure(widget_t *widget)
112{
113 /* No-op */
114}
115
116static void adjust_constraints(constraints_t *cons, size_t run,
117 sysarg_t dim_min, sysarg_t dim_max)
118{
119 assert(run > 0);
120
121 sysarg_t dim_min_part = dim_min / run;
122 sysarg_t dim_min_rem = dim_min % run;
123
124 sysarg_t dim_max_part = dim_max / run;
125 sysarg_t dim_max_rem = dim_max % run;
126
127 for (size_t i = 0; i < run; i++) {
128 sysarg_t dim_min_cur = dim_min_part;
129 sysarg_t dim_max_cur = dim_max_part;
130
131 if (i == run - 1) {
132 dim_min_cur += dim_min_rem;
133 dim_max_cur += dim_max_rem;
134 }
135
136 /*
137 * We want the strongest constraint
138 * for the minimum.
139 */
140 if (cons[i].min < dim_min_cur)
141 cons[i].min = dim_min_cur;
142
143 /*
144 * The comparison is correct, we want
145 * the weakest constraint for the
146 * maximum.
147 */
148 if (cons[i].max < dim_max_cur)
149 cons[i].max = dim_max_cur;
150 }
151}
152
153static void solve_constraints(constraints_t *cons, size_t run, sysarg_t sum)
154{
155 /* Initial solution */
156 sysarg_t cur_sum = 0;
157
158 for (size_t i = 0; i < run; i++) {
159 cons[i].val = cons[i].min;
160 cur_sum += cons[i].val;
161 }
162
163 /* Iterative improvement */
164 while (cur_sum < sum) {
165 sysarg_t delta = (sum - cur_sum) / run;
166 if (delta == 0)
167 break;
168
169 cur_sum = 0;
170
171 for (size_t i = 0; i < run; i++) {
172 if (cons[i].val + delta < cons[i].max)
173 cons[i].val += delta;
174
175 cur_sum += cons[i].val;
176 }
177 }
178}
179
180static void grid_rearrange(widget_t *widget, sysarg_t hpos, sysarg_t vpos,
181 sysarg_t width, sysarg_t height)
182{
183 grid_t *grid = (grid_t *) widget;
184
185 widget_modify(widget, hpos, vpos, width, height);
186 paint_internal(widget);
187
188 /* Compute column widths */
189 constraints_t *widths =
190 (constraints_t *) calloc(grid->cols, sizeof(constraints_t));
191 if (widths) {
192 /* Constrain widths */
193 for (size_t c = 0; c < grid->cols; c++) {
194 widths[c].min = 0;
195
196 for (size_t r = 0; r < grid->rows; r++) {
197 grid_cell_t *cell = grid_cell_at(grid, c, r);
198 if (!cell)
199 continue;
200
201 widget_t *widget = cell->widget;
202 if (widget)
203 adjust_constraints(&widths[c], cell->cols,
204 widget->width_min, widget->width_max);
205 }
206 }
207
208 solve_constraints(widths, grid->cols, width);
209 }
210
211 /* Compute row heights */
212 constraints_t *heights =
213 (constraints_t *) calloc(grid->rows, sizeof(constraints_t));
214 if (heights) {
215 /* Constrain heights */
216 for (size_t r = 0; r < grid->rows; r++) {
217 heights[r].min = 0;
218
219 for (size_t c = 0; c < grid->cols; c++) {
220 grid_cell_t *cell = grid_cell_at(grid, c, r);
221 if (!cell)
222 continue;
223
224 widget_t *widget = cell->widget;
225 if (widget) {
226 adjust_constraints(&heights[r], cell->rows,
227 widget->height_min, widget->height_max);
228 }
229 }
230 }
231
232 solve_constraints(heights, grid->rows, height);
233 }
234
235 /* Rearrange widgets */
236 if ((widths) && (heights)) {
237 sysarg_t cur_vpos = vpos;
238
239 for (size_t r = 0; r < grid->rows; r++) {
240 sysarg_t cur_hpos = hpos;
241
242 for (size_t c = 0; c < grid->cols; c++) {
243 grid_cell_t *cell = grid_cell_at(grid, c, r);
244 if (!cell)
245 continue;
246
247 widget_t *widget = cell->widget;
248 if (widget) {
249 sysarg_t cur_width = 0;
250 sysarg_t cur_height = 0;
251
252 for (size_t cd = 0; cd < cell->cols; cd++)
253 cur_width += widths[c + cd].val;
254
255 for (size_t rd = 0; rd < cell->rows; rd++)
256 cur_height += heights[r + rd].val;
257
258 if ((cur_width > 0) && (cur_height > 0)) {
259 sysarg_t wwidth = cur_width;
260 sysarg_t wheight = cur_height;
261
262 /*
263 * Make sure the widget is respects its
264 * maximal constrains.
265 */
266
267 if ((widget->width_max > 0) &&
268 (wwidth > widget->width_max))
269 wwidth = widget->width_max;
270
271 if ((widget->height_max > 0) &&
272 (wheight > widget->height_max))
273 wheight = widget->height_max;
274
275 widget->rearrange(widget, cur_hpos, cur_vpos,
276 wwidth, wheight);
277 }
278
279
280 }
281
282 cur_hpos += widths[c].val;
283 }
284
285 cur_vpos += heights[r].val;
286 }
287 }
288
289 if (widths)
290 free(widths);
291
292 if (heights)
293 free(heights);
294}
295
296static void grid_repaint(widget_t *widget)
297{
298 paint_internal(widget);
299
300 list_foreach(widget->children, link, widget_t, child) {
301 child->repaint(child);
302 }
303
304 window_damage(widget->window);
305}
306
307static void grid_handle_keyboard_event(widget_t *widget, kbd_event_t event)
308{
309 /* No-op */
310}
311
312static void grid_handle_position_event(widget_t *widget, pos_event_t event)
313{
314 grid_t *grid = (grid_t *) widget;
315
316 grid_cell_t *cell = grid_coords_at(grid, event.hpos, event.vpos);
317 if ((cell) && (cell->widget))
318 cell->widget->handle_position_event(cell->widget, event);
319}
320
321static bool grid_add(struct grid *grid, widget_t *widget, size_t col,
322 size_t row, size_t cols, size_t rows)
323{
324 if ((cols == 0) || (rows == 0) || (col + cols > grid->cols) ||
325 (row + rows > grid->rows))
326 return false;
327
328 grid_cell_t *cell = grid_cell_at(grid, col, row);
329 if (!cell)
330 return false;
331
332 /*
333 * Check whether the cell is not occupied by an
334 * extension of a different cell.
335 */
336 if ((!cell->widget) && (cell->cols > 0) && (cell->rows > 0))
337 return false;
338
339 widget->parent = (widget_t *) grid;
340
341 list_append(&widget->link, &grid->widget.children);
342 widget->window = grid->widget.window;
343
344 /* Mark cells in layout */
345 for (size_t r = row; r < row + rows; r++) {
346 for (size_t c = col; c < col + cols; c++) {
347 if ((r == row) && (c == col)) {
348 cell->widget = widget;
349 cell->cols = cols;
350 cell->rows = rows;
351 } else {
352 grid_cell_t *extension = grid_cell_at(grid, c, r);
353 if (extension) {
354 extension->widget = NULL;
355 extension->cols = 1;
356 extension->rows = 1;
357 }
358 }
359 }
360 }
361
362 return true;
363}
364
365bool init_grid(grid_t *grid, widget_t *parent, const void *data, size_t cols,
366 size_t rows, pixel_t background)
367{
368 if ((cols == 0) || (rows == 0))
369 return false;
370
371 grid->layout =
372 (grid_cell_t *) calloc(cols * rows, sizeof(grid_cell_t));
373 if (!grid->layout)
374 return false;
375
376 memset(grid->layout, 0, cols * rows * sizeof(grid_cell_t));
377
378 widget_init(&grid->widget, parent, data);
379
380 grid->widget.destroy = grid_destroy;
381 grid->widget.reconfigure = grid_reconfigure;
382 grid->widget.rearrange = grid_rearrange;
383 grid->widget.repaint = grid_repaint;
384 grid->widget.handle_keyboard_event = grid_handle_keyboard_event;
385 grid->widget.handle_position_event = grid_handle_position_event;
386
387 grid->add = grid_add;
388 grid->background = background;
389 grid->cols = cols;
390 grid->rows = rows;
391
392 return true;
393}
394
395grid_t *create_grid(widget_t *parent, const void *data, size_t cols,
396 size_t rows, pixel_t background)
397{
398 grid_t *grid = (grid_t *) malloc(sizeof(grid_t));
399 if (!grid)
400 return NULL;
401
402 if (init_grid(grid, parent, data, cols, rows, background))
403 return grid;
404
405 free(grid);
406 return NULL;
407}
408
409/** @}
410 */
Note: See TracBrowser for help on using the repository browser.