source: mainline/uspace/lib/gui/grid.c@ e63c424f

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

reimplement the grid widget to respect children widgets size constrains

  • Property mode set to 100644
File size: 9.7 KB
RevLine 
[6d5e378]1/*
2 * Copyright (c) 2012 Petr Koupy
[e63c424f]3 * Copyright (c) 2013 Martin Decky
[6d5e378]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
[e63c424f]44typedef struct {
45 sysarg_t min;
46 sysarg_t max;
47 sysarg_t val;
48} constraints_t;
[6d5e378]49
[e63c424f]50static void paint_internal(widget_t *widget)
51{
52 grid_t *grid = (grid_t *) widget;
53
[6d5e378]54 surface_t *surface = window_claim(grid->widget.window);
55 if (!surface) {
56 window_yield(grid->widget.window);
[e63c424f]57 return;
[6d5e378]58 }
[e63c424f]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++)
[6d5e378]63 surface_put_pixel(surface, x, y, grid->background);
64 }
[e63c424f]65
[6d5e378]66 window_yield(grid->widget.window);
67}
68
[e63c424f]69static grid_cell_t *grid_cell_at(grid_t *grid, size_t col, size_t row)
[6d5e378]70{
[e63c424f]71 if ((col < grid->cols) && (row < grid->rows))
[6d5e378]72 return grid->layout + (row * grid->cols + col);
[e63c424f]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 }
[6d5e378]92 }
[e63c424f]93
94 return NULL;
[6d5e378]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;
[e63c424f]106
[6d5e378]107 deinit_grid(grid);
108 free(grid);
109}
110
111static void grid_reconfigure(widget_t *widget)
112{
[e63c424f]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 }
[6d5e378]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;
[e63c424f]184
[6d5e378]185 widget_modify(widget, hpos, vpos, width, height);
186 paint_internal(widget);
[e63c424f]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);
[6d5e378]205 }
206 }
[e63c424f]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 }
[6d5e378]229 }
230 }
[e63c424f]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;
[6d5e378]283 }
[e63c424f]284
285 cur_vpos += heights[r].val;
[6d5e378]286 }
287 }
[e63c424f]288
289 if (widths)
290 free(widths);
291
292 if (heights)
293 free(heights);
[6d5e378]294}
295
296static void grid_repaint(widget_t *widget)
297{
298 paint_internal(widget);
[e63c424f]299
[6d5e378]300 list_foreach(widget->children, link) {
301 widget_t *child = list_get_instance(link, widget_t, link);
302 child->repaint(child);
303 }
[e63c424f]304
[6d5e378]305 window_damage(widget->window);
306}
307
308static void grid_handle_keyboard_event(widget_t *widget, kbd_event_t event)
309{
[e63c424f]310 /* No-op */
[6d5e378]311}
312
313static void grid_handle_position_event(widget_t *widget, pos_event_t event)
314{
315 grid_t *grid = (grid_t *) widget;
[e63c424f]316
317 grid_cell_t *cell = grid_coords_at(grid, event.hpos, event.vpos);
318 if ((cell) && (cell->widget))
319 cell->widget->handle_position_event(cell->widget, event);
[6d5e378]320}
321
[e63c424f]322static bool grid_add(struct grid *grid, widget_t *widget, size_t col,
323 size_t row, size_t cols, size_t rows)
[6d5e378]324{
[e63c424f]325 if ((cols == 0) || (rows == 0) || (col + cols > grid->cols) ||
326 (row + rows > grid->rows))
327 return false;
328
329 grid_cell_t *cell = grid_cell_at(grid, col, row);
330 if (!cell)
331 return false;
332
333 /*
334 * Check whether the cell is not occupied by an
335 * extension of a different cell.
336 */
337 if ((!cell->widget) && (cell->cols > 0) && (cell->rows > 0))
338 return false;
339
[6d5e378]340 widget->parent = (widget_t *) grid;
[e63c424f]341
[6d5e378]342 list_append(&widget->link, &grid->widget.children);
343 widget->window = grid->widget.window;
[e63c424f]344
345 /* Mark cells in layout */
346 for (size_t r = row; r < row + rows; r++) {
347 for (size_t c = col; c < col + cols; c++) {
348 if ((r == row) && (c == col)) {
349 cell->widget = widget;
350 cell->cols = cols;
351 cell->rows = rows;
352 } else {
353 grid_cell_t *extension = grid_cell_at(grid, c, r);
354 if (extension) {
355 extension->widget = NULL;
356 extension->cols = 1;
357 extension->rows = 1;
358 }
[6d5e378]359 }
360 }
361 }
[e63c424f]362
363 return true;
[6d5e378]364}
365
[e63c424f]366bool init_grid(grid_t *grid, widget_t *parent, size_t cols, size_t rows,
367 pixel_t background)
[6d5e378]368{
[e63c424f]369 if ((cols == 0) || (rows == 0))
[6d5e378]370 return false;
[e63c424f]371
372 grid->layout =
373 (grid_cell_t *) calloc(cols * rows, sizeof(grid_cell_t));
374 if (!grid->layout)
375 return false;
376
377 memset(grid->layout, 0, cols * rows * sizeof(grid_cell_t));
378
[6d5e378]379 widget_init(&grid->widget, parent);
[e63c424f]380
[6d5e378]381 grid->widget.destroy = grid_destroy;
382 grid->widget.reconfigure = grid_reconfigure;
383 grid->widget.rearrange = grid_rearrange;
384 grid->widget.repaint = grid_repaint;
385 grid->widget.handle_keyboard_event = grid_handle_keyboard_event;
386 grid->widget.handle_position_event = grid_handle_position_event;
[e63c424f]387
[6d5e378]388 grid->add = grid_add;
389 grid->background = background;
390 grid->cols = cols;
[e63c424f]391 grid->rows = rows;
392
[6d5e378]393 return true;
394}
395
[e63c424f]396grid_t *create_grid(widget_t *parent, size_t cols, size_t rows, pixel_t background)
[6d5e378]397{
398 grid_t *grid = (grid_t *) malloc(sizeof(grid_t));
[e63c424f]399 if (!grid)
[6d5e378]400 return NULL;
[e63c424f]401
402 if (init_grid(grid, parent, cols, rows, background))
[6d5e378]403 return grid;
[e63c424f]404
405 free(grid);
406 return NULL;
[6d5e378]407}
408
409/** @}
410 */
Note: See TracBrowser for help on using the repository browser.