Changeset e63c424f in mainline for uspace/lib/gui/grid.c


Ignore:
Timestamp:
2013-04-17T16:24:45Z (11 years ago)
Author:
Martin Decky <martin@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
395ca2e
Parents:
077bc931
Message:

reimplement the grid widget to respect children widgets size constrains

File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/lib/gui/grid.c

    r077bc931 re63c424f  
    11/*
    22 * Copyright (c) 2012 Petr Koupy
     3 * Copyright (c) 2013 Martin Decky
    34 * All rights reserved.
    45 *
     
    3839#include <malloc.h>
    3940#include <surface.h>
    40 
    4141#include "window.h"
    4242#include "grid.h"
    4343
    44 static void paint_internal(widget_t *w)
    45 {
    46         grid_t *grid = (grid_t *) w;
    47 
     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       
    4854        surface_t *surface = window_claim(grid->widget.window);
    4955        if (!surface) {
    5056                window_yield(grid->widget.window);
    51         }
    52 
    53         for (sysarg_t y = w->vpos; y <  w->vpos + w->height; ++y) {
    54                 for (sysarg_t x = w->hpos; x < w->hpos + w->width; ++x) {
     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++)
    5563                        surface_put_pixel(surface, x, y, grid->background);
    56                 }
    57         }
    58 
     64        }
     65       
    5966        window_yield(grid->widget.window);
    6067}
    6168
    62 static widget_t **widget_at(grid_t *grid, size_t row, size_t col)
    63 {
    64         if (row < grid->rows && col < grid->cols) {
     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))
    6572                return grid->layout + (row * grid->cols + col);
    66         } else {
    67                 return NULL;
    68         }
     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;
    6995}
    7096
     
    78104{
    79105        grid_t *grid = (grid_t *) widget;
    80 
     106       
    81107        deinit_grid(grid);
    82 
    83108        free(grid);
    84109}
     
    86111static void grid_reconfigure(widget_t *widget)
    87112{
    88         /* no-op */
     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        }
    89178}
    90179
     
    93182{
    94183        grid_t *grid = (grid_t *) widget;
    95 
     184       
    96185        widget_modify(widget, hpos, vpos, width, height);
    97186        paint_internal(widget);
    98 
    99         sysarg_t cell_width = width / grid->cols;
    100         sysarg_t cell_height = height / grid->rows;
    101 
    102         list_foreach(widget->children, link) {
    103                 widget_t *child = list_get_instance(link, widget_t, link);
    104 
    105                 sysarg_t widget_hpos = 0;
    106                 sysarg_t widget_vpos = 0;
    107                 sysarg_t widget_width = 0;
    108                 sysarg_t widget_height = 0;
    109 
    110                 size_t r = 0;
    111                 size_t c = 0;
    112                 for (r = 0; r < grid->rows; ++r) {
    113                         bool found = false;
    114                         for (c = 0; c < grid->cols; ++c) {
    115                                 widget_t **cell = widget_at(grid, r, c);
    116                                 if (cell && *cell == child) {
    117                                         found = true;
    118                                         break;
     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);
    119228                                }
    120229                        }
    121                         if (found) {
    122                                 break;
     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;
    123283                        }
    124                 }
    125 
    126                 widget_hpos = cell_width * c + hpos;
    127                 widget_vpos = cell_height * r + vpos;
    128 
    129                 for (size_t _c = c; _c < grid->cols; ++_c) {
    130                         widget_t **cell = widget_at(grid, r, _c);
    131                         if (cell && *cell == child) {
    132                                 widget_width += cell_width;
    133                         } else {
    134                                 break;
    135                         }
    136                 }
    137 
    138                 for (size_t _r = r; _r < grid->rows; ++_r) {
    139                         widget_t **cell = widget_at(grid, _r, c);
    140                         if (cell && *cell == child) {
    141                                 widget_height += cell_height;
    142                         } else {
    143                                 break;
    144                         }
    145                 }
    146 
    147                 if (widget_width > 0 && widget_height > 0) {
    148                         child->rearrange(child,
    149                             widget_hpos, widget_vpos, widget_width, widget_height);
    150                 }
    151         }
     284                       
     285                        cur_vpos += heights[r].val;
     286                }
     287        }
     288       
     289        if (widths)
     290                free(widths);
     291       
     292        if (heights)
     293                free(heights);
    152294}
    153295
     
    155297{
    156298        paint_internal(widget);
     299       
    157300        list_foreach(widget->children, link) {
    158301                widget_t *child = list_get_instance(link, widget_t, link);
    159302                child->repaint(child);
    160303        }
     304       
    161305        window_damage(widget->window);
    162306}
     
    164308static void grid_handle_keyboard_event(widget_t *widget, kbd_event_t event)
    165309{
    166         /* no-op */
     310        /* No-op */
    167311}
    168312
     
    170314{
    171315        grid_t *grid = (grid_t *) widget;
    172 
    173         if ((widget->height / grid->rows) == 0) {
    174                 return;
    175         }
    176         if ((widget->width / grid->cols) == 0) {
    177                 return;
    178         }
    179 
    180         sysarg_t row = (event.vpos - widget->vpos) / (widget->height / grid->rows);
    181         sysarg_t col = (event.hpos - widget->hpos) / (widget->width / grid->cols);
    182 
    183         widget_t **cell = widget_at(grid, row, col);
    184         if (cell && *cell) {
    185                 (*cell)->handle_position_event(*cell, event);
    186         }
    187 }
    188 
    189 static void grid_add(grid_t *grid, widget_t *widget,
    190     size_t row, size_t col, size_t rows, size_t cols)
    191 {
    192         assert(row + rows <= grid->rows);
    193         assert(col + cols <= grid->cols);
    194 
     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);
     320}
     321
     322static bool grid_add(struct grid *grid, widget_t *widget, size_t col,
     323    size_t row, size_t cols, size_t rows)
     324{
     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       
    195340        widget->parent = (widget_t *) grid;
     341       
    196342        list_append(&widget->link, &grid->widget.children);
    197343        widget->window = grid->widget.window;
    198 
    199         for (size_t r = row; r < row + rows; ++r) {
    200                 for (size_t c = col; c < col + cols; ++c) {
    201                         widget_t **cell = widget_at(grid, r, c);
    202                         if (cell) {
    203                                 *cell = widget;
     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                                }
    204359                        }
    205360                }
    206361        }
    207 }
    208 
    209 bool init_grid(grid_t *grid,
    210     widget_t *parent, size_t rows, size_t cols, pixel_t background)
    211 {
    212         assert(rows > 0);
    213         assert(cols > 0);
    214 
    215         widget_t **layout = (widget_t **) malloc(rows * cols * sizeof(widget_t *));
    216         if (!layout) {
     362       
     363        return true;
     364}
     365
     366bool init_grid(grid_t *grid, widget_t *parent, size_t cols, size_t rows,
     367    pixel_t background)
     368{
     369        if ((cols == 0) || (rows == 0))
    217370                return false;
    218         }
    219         memset(layout, 0, rows * cols * sizeof(widget_t *));
    220 
     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       
    221379        widget_init(&grid->widget, parent);
    222 
     380       
    223381        grid->widget.destroy = grid_destroy;
    224382        grid->widget.reconfigure = grid_reconfigure;
     
    227385        grid->widget.handle_keyboard_event = grid_handle_keyboard_event;
    228386        grid->widget.handle_position_event = grid_handle_position_event;
    229 
     387       
    230388        grid->add = grid_add;
    231389        grid->background = background;
     390        grid->cols = cols;
    232391        grid->rows = rows;
    233         grid->cols = cols;
    234         grid->layout = layout;
    235 
     392       
    236393        return true;
    237394}
    238395
    239 grid_t *create_grid(widget_t *parent, size_t rows, size_t cols, pixel_t background)
     396grid_t *create_grid(widget_t *parent, size_t cols, size_t rows, pixel_t background)
    240397{
    241398        grid_t *grid = (grid_t *) malloc(sizeof(grid_t));
    242         if (!grid) {
     399        if (!grid)
    243400                return NULL;
    244         }
    245 
    246         if (init_grid(grid, parent, rows, cols, background)) {
     401       
     402        if (init_grid(grid, parent, cols, rows, background))
    247403                return grid;
    248         } else {
    249                 free(grid);
    250                 return NULL;
    251         }
     404       
     405        free(grid);
     406        return NULL;
    252407}
    253408
Note: See TracChangeset for help on using the changeset viewer.