diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/Fl_Grid.cxx | 1108 | ||||
| -rw-r--r-- | src/Makefile | 1 | ||||
| -rw-r--r-- | src/makedepend | 5 |
4 files changed, 1115 insertions, 0 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 693726a13..ea4f89cc1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -41,6 +41,7 @@ set (CPPFILES Fl_File_Input.cxx Fl_Flex.cxx Fl_Graphics_Driver.cxx + Fl_Grid.cxx Fl_Group.cxx Fl_Help_View.cxx Fl_Image.cxx diff --git a/src/Fl_Grid.cxx b/src/Fl_Grid.cxx new file mode 100644 index 000000000..66991345d --- /dev/null +++ b/src/Fl_Grid.cxx @@ -0,0 +1,1108 @@ +// +// Fl_Grid widget for the Fast Light Tool Kit (FLTK). +// +// Copyright 2021-2022 by Albrecht Schlosser. +// Copyright 2022-2023 by Bill Spitzak and others. +// +// This library is free software. Distribution and use rights are outlined in +// the file "COPYING" which should have been included with this file. If this +// file is missing or damaged, see the license at: +// +// https://www.fltk.org/COPYING.php +// +// Please see the following page on how to report bugs and issues: +// +// https://www.fltk.org/bugs.php +// + +/** \file src/Fl_Grid.cxx + + Implements the Fl_Grid container widget. + + \since 1.4.0 +*/ + +#include <FL/Fl_Grid.H> +#include <FL/fl_draw.H> + +// private class Col for column management + +class Fl_Grid::Col { + friend class Fl_Grid; + int minw_; // minimal size (width) + int w_; // calculated size (width) + short weight_; // weight used to allocate extra space + short gap_; // gap to the right of the column + Col() { + minw_ = 0; + w_ = 0; + weight_ = 50; + gap_ = -1; + } + ~Col() {}; +}; + +// private class Row for row management + +class Fl_Grid::Row { + friend class Fl_Grid; + + Cell *cells_; // cells of this row + int minh_; // minimal size (height) + int h_; // calculated size (height) + short weight_; // weight used to allocate extra space + short gap_; // gap below the row (-1 = use default) + + Row() { + cells_ = NULL; + minh_ = 0; + h_ = 0; + weight_ = 50; + gap_ = -1; + } + + ~Row() { + free_cells(); + }; + + // Fl_Grid::Row::free_cells() - free all cells of a row + + void free_cells() { + Cell *cel = cells_; + while (cel) { + Cell *next = cel->next_; + delete cel; + cel = next; + } // free_cells() + cells_ = 0; + } + + // Fl_Grid::Row::remove_cell() - remove all cells of column col from list of cells + + void remove_cell(int col) { // + Cell *cel = cells_; + Cell *prev = 0; + while (cel) { + Cell *next = cel->next_; + if (cel->col_ == col) { + if (prev) { + prev->next_ = next; + } else { + cells_ = next; + } + delete cel; + return; + } else { + prev = cel; + cel = next; + } + } // while (cel) + } // Row::remove_cell(col) + +}; // class Row + +/** + Create a new Fl_Grid widget. + + \todo More documentation of Fl_Grid constructor? +*/ +Fl_Grid::Fl_Grid(int X, int Y, int W, int H, const char *L) + : Fl_Group(X, Y, W, H, L) { + + init(); + box(FL_FLAT_BOX); +} + +// private: init vars + +void Fl_Grid::init() { + rows_ = 0; + cols_ = 0; + margin_left_ = 0; + margin_top_ = 0; + margin_right_ = 0; + margin_bottom_ = 0; + gap_row_ = 0; + gap_col_ = 0; + Cols_ = 0; + Rows_ = 0; + old_size = Fl_Rect(0, 0, 0, 0); + need_layout_ = false; // no need to calculate layout + grid_color = (Fl_Color)0xbbeebb00; // light green + draw_grid_ = false; // don't draw grid helper lines + if (fl_getenv("FLTK_GRID_DEBUG")) + draw_grid_ = true; +} + +Fl_Grid::~Fl_Grid() { + delete[] Cols_; + delete[] Rows_; +} + +/** + Set the basic layout parameters of the Fl_Grid widget. + + You need to specify at least \p rows and \p cols to define a layout + before you can add widgets to the grid. + + Parameters \p margin and \p gap are optional. + + You can call layout(int rows, int cols, int margin, int gap) again + to change the layout but this is inefficient since all cells are + reallocated if the layout changed. + + Calling this with the same values of \p rows and \p cols is fast and + can be used to change \p margin and \p gap w/o reallocating the cells. + + \p margin sets all margins (left, top, right, bottom) to the same value. + Negative values (e.g. -1) don't change the established margins. + The default value set by the constructor is 0. + + \p gap sets row and column gaps to the same value. Negative values (e.g. -1) + do not affect the established gaps. The default value set by the constructor + is 0. + + After you added all widgets you must call layout() once without arguments + to calculate the actual layout and to position and resize all widgets. + + \todo Document when and why to call layout() w/o args. See Fl_Flex::layout() + + \param[in] rows number of rows + \param[in] cols number of columns + \param[in] margin margin size inside the Fl_Grid's border + \param[in] gap gap size between cells + + \see Fl_Grid::layout() +*/ +void Fl_Grid::layout(int rows, int cols, int margin, int gap) { + + if (margin >= 0) + margin_left_ = margin_top_ = margin_right_ = margin_bottom_ = margin; + + if (gap >= 0) + gap_row_ = gap_col_ = gap; + + if (cols == cols_ && rows == rows_) // same size, nothing to do + return; + + // release allocated memory if either rows <= 0 or cols <= 0 + + if (rows <= 0 || cols <= 0) { + clear_layout(); + return; + } + + // allocate new cells, rows, and columns + + // Cell *new_cells = new Cell[rows * cols]; + + // reallocate and copy old columns + + if (cols != cols_) { + Col *new_cols = new Col[cols]; + for (int c = 0; c < cols; c++) { + if (c < cols_) + new_cols[c] = Cols_[c]; + else + break; + } + delete[] Cols_; + Cols_ = new_cols; + } + + // reallocate and copy old rows + + if (rows != rows_) { + Row *new_rows = new Row[rows]; + Row *row = Rows_; + for (int r = 0; r < rows; r++, row++) { + if (r < rows_) { + new_rows[r] = *row; + row->cells_ = 0; + } else { + break; + } + } + delete[] Rows_; + Rows_ = new_rows; + } + + // store new layout and cells + + cols_ = cols; + rows_ = rows; + need_layout(1); + +} // layout(int, int, int, int) + + +/** + Draws the grid helper lines for design and debugging purposes. + + This method is protected so it can be modified in subclasses. +*/ +void Fl_Grid::draw_grid() { + + int x0 = x() + Fl::box_dx(box()) + margin_left_; + int y0 = y() + Fl::box_dy(box()) + margin_top_; + int x1 = x() + w() - Fl::box_dx(box()) - margin_right_; + int y1 = y() + h() - Fl::box_dy(box()) - margin_bottom_; + + fl_line_style(FL_SOLID, 1); + fl_color(grid_color); + + // draw total layout frame + + fl_rect(x0, y0, x1 - x0, y1 - y0); + + // draw horizontal lines (gap = 0) or rectangles (gap > 0) + + for (int r = 0; r < rows_ - 1; r++) { + + int gap = Rows_[r].gap_ >= 0 ? Rows_[r].gap_ : gap_row_; + y0 += Rows_[r].h_; + if (gap == 0) { + fl_xyline(x0, y0, x1); + } else { + fl_rectf(x0, y0, x1 - x0, gap); + } + y0 += gap; + } + + // draw vertical lines (gap = 0) or rectangles (gap > 0) + + x0 = x() + Fl::box_dx(box()) + margin_left_; + y0 = y() + Fl::box_dy(box()) + margin_top_; + + for (int c = 0; c < cols_ - 1; c++) { + + int gap = Cols_[c].gap_ >= 0 ? Cols_[c].gap_ : gap_row_; + x0 += Cols_[c].w_; + if (gap == 0) { + fl_yxline(x0, y0, y1); + } else { + fl_rectf(x0, y0, gap, y1 - y0); + } + x0 += gap; + } + + fl_line_style(FL_SOLID, 0); + fl_color(FL_BLACK); + +} // Fl_Grid::draw_grid() + + +/** + Draws the Fl_Grid widget and all children. + + If the layout has been changed layout() is called before the widget + is drawn so all children are arranged as designed. + + \see layout() + \see need_layout() +*/ +void Fl_Grid::draw() { + + if (need_layout()) + layout(); + + if (damage() & ~FL_DAMAGE_CHILD) { // draw the entire widget + draw_box(); + if (draw_grid_) + draw_grid(); + draw_label(); + } + draw_children(); + +} // Fl_Grid::draw() + +/** + Calculate the grid layout and resize and position all widgets. + + This is called automatically when the Fl_Grid is resized. You need to + call it once after you added widgets or moved widgets between cells. + + Calling it once after all modfications are completed is enough. + + \todo Document when and why to call layout() w/o args. See Fl_Flex::layout() + + \see Fl_Grid::layout(int rows, int cols, int margin, int gap) +*/ +void Fl_Grid::layout() { + + if (rows_ == 0 || cols_ == 0) // empty grid + return; + + Row *row; + Col *col; + Cell *cel; + + // calculate the total available space w/o borders and margins + + int tw = w() - Fl::box_dw(box()) - margin_left_ - margin_right_; + int th = h() - Fl::box_dh(box()) - margin_top_ - margin_bottom_; + + // initialize column widths and row heights + + col = Cols_; + for (int c = 0; c < cols_; c++, col++) { + col->w_ = col->minw_; + } + + row = Rows_; + for (int r = 0; r < rows_; r++, row++) { + row->h_ = row->minh_; + } + + // calculate minimal column widths and row heights (in one loop) + + row = Rows_; + for (int r = 0; r < rows_; r++, row++) { + col = Cols_; + for (int c = 0; c < cols_; c++, col++) { + cel = cell(r, c); + if (cel) { + Fl_Widget *wi = cel->widget_; + if (wi && wi->visible()) { + if (cel->colspan_ == 1 && cel->w_ > col->w_) col->w_ = cel->w_; + if (cel->rowspan_ == 1 && cel->h_ > row->h_) row->h_ = cel->h_; + } // widget + } // colspan && rowspan + } // cols + } // rows + + // calculate total space occupied by rows and columns including gaps + + int tcwi = 0; // total column width incl. gaps + int tcwe = 0; // total column weight + int hcwe = 0; // highest column weight + int icwe = 0; // index of column with highest weight + + int trhe = 0; // total row height incl. gaps + int trwe = 0; // total row weight + int hrwe = 0; // highest row weight + int irwe = 0; // index of row with highest weight + + col = Cols_; + for (int c = 0; c < cols_; c++, col++) { + tcwi += col->w_; + tcwe += col->weight_; + if (c < cols_ - 1) + tcwi += ((col->gap_ >= 0) ? col->gap_ : gap_col_); + if (col->weight_ > hcwe) { + hcwe = col->weight_; + icwe = c; + } + } + + row = Rows_; + for (int r = 0; r < rows_; r++, row++) { + trhe += row->h_; + trwe += row->weight_; + if (r < rows_ - 1) + trhe += ((row->gap_ >= 0) ? row->gap_ : gap_row_); + if (row->weight_ > hrwe) { + hrwe = row->weight_; + irwe = r; + } + } + + // Add extra space to columns and rows to fill the entire grid, using relative weights. + // Rounding differences are added to or subtracted from the col/row with the highest weight. + + int space = tw - tcwi; // additional space for columns + int add_space = 0; // used for calculation + int remaining = 0; // remaining space + + if (space > 0 && tcwe > 0) { + remaining = space; + col = Cols_; + for (int c = 0; c < cols_; c++, col++) { + if (col->weight_ > 0) { + add_space = int(float(space * col->weight_) / tcwe + 0.5); + col->w_ += add_space; + remaining -= add_space; + } + } + if (remaining != 0) + Cols_[icwe].w_ += remaining; + } + + space = th - trhe; // additional space for rows + + if (space > 0 && trwe > 0) { + remaining = space; + row = Rows_; + for (int r = 0; r < rows_; r++, row++) { + if (row->weight_ > 0) { + add_space = int(float(space * row->weight_) / trwe + 0.5); + row->h_ += add_space; + remaining -= add_space; + } + } + if (remaining != 0) + Rows_[irwe].h_ += remaining; + } + + // calculate and assign widget positions and sizes + + int x0, y0; // starting x/y positions + + y0 = y() + Fl::box_dy(box()) + margin_top_; + + row = Rows_; + for (int r = 0; r < rows_; r++, row++) { + x0 = x() + Fl::box_dx(box()) + margin_left_; + col = Cols_; + for (int c = 0; c < cols_; c++, col++) { + int wx = x0; // widget's x + int wy = y0; // widget's y + cel = cell(r, c); + if (cel) { + Fl_Widget *wi = cel->widget_; + if (wi && wi->visible()) { + + // calculate the cell's position and size, take cell spanning into account + + int ww = col->w_; + int wh = row->h_; + + for (int i = 0; i < cel->colspan_ - 1; i++) { + ww += (Cols_[c + i].gap_ >= 0) ? Cols_[c + i].gap_ : gap_col_; + ww += Cols_[c + i + 1].w_; + } + + for (int i = 0; i < cel->rowspan_ - 1; i++) { + wh += (Rows_[r + i].gap_ >= 0) ? Rows_[r + i].gap_ : gap_row_; + wh += Rows_[r + i + 1].h_; + } + + // horizontal alignment: left + right => stretch + + Fl_Grid_Align ali = cel->align_; + Fl_Grid_Align mask; + + mask = FL_GRID_LEFT | FL_GRID_RIGHT | FL_GRID_HORIZONTAL; + if ((ali & mask) == 0) { + wx += (ww - cel->w_) / 2; + ww = cel->w_; + } else if ((ali & mask) == FL_GRID_LEFT) { + ww = cel->w_; + } else if ((ali & mask) == FL_GRID_RIGHT) { + wx += ww - cel->w_; + ww = cel->w_; + } + + // vertical alignment: top + bottom => stretch + + mask = FL_GRID_TOP | FL_GRID_BOTTOM | FL_GRID_VERTICAL; + if ((ali & mask) == 0) { + wy += (wh - cel->h_) / 2; + wh = cel->h_; + } else if ((ali & mask) == FL_GRID_TOP) { + wh = cel->h_; + } else if ((ali & mask) == FL_GRID_BOTTOM) { + wy += wh - cel->h_; + wh = cel->h_; + } + + wi->resize(wx, wy, ww, wh); + + } // widget is visible + + } // cell + + x0 += (col->w_ + ((col->gap_ >= 0) ? col->gap_ : gap_col_)); + + } // cols + + y0 += ( row->h_ + ((row->gap_ >= 0) ? row->gap_ : gap_row_) ); + + } // rows + + need_layout(0); + redraw(); + +} // layout() + + +/** + Fl_Group calls this method when a child widget is about to be removed. + + Make sure that the widget is also removed from our internal list of children. +*/ +void Fl_Grid::on_remove(int index) { + + Fl_Widget *wi = child(index); + Cell *c = cell(wi); // find the cell of this child + if (c) { + remove_cell(c->row_, c->col_); + } +} + +// private: add a new cell to the grid + +Fl_Grid::Cell *Fl_Grid::add_cell(int row, int col) { + Cell *c = new Cell(row, col); + Row *r = &Rows_[row]; + Cell* cel = r->cells_; // "current" cell + Cell* prev = 0; // "previous" cell + while (cel) { // existing cells + if (cel->col_ > col) { // found spot ... + break; + } + prev = cel; + cel = cel->next_; + } + + // insertion point: prev => last cell or NULL, cel == next cell or NULL + + if (prev) + prev->next_ = c; + else + r->cells_ = c; + c->next_ = cel; + + need_layout(1); + return c; +} + +// private: remove a cell from the grid + +void Fl_Grid::remove_cell(int row, int col) { + Row *r = &Rows_[row]; + r->remove_cell(col); + need_layout(1); +} + +/** + Recalculate the layout and position and resize all widgets. + + This method overrides Fl_Group::resize() and calculates all positions and + sizes of its children according to its own rules. + + \param[in] X,Y new widget position + \param[in] W,H new widget size +*/ +void Fl_Grid::resize(int X, int Y, int W, int H) { + old_size = Fl_Rect(x(), y(), w(), h()); + Fl_Widget::resize(X, Y, W, H); + need_layout(1); +} + +/** + Reset the layout w/o removing widgets. + + Removes all cells and sets rows and cols to zero. Existing widgets are + kept as children of the Fl_Group (base class) but are hidden. + + This method should be rarely used. You may want to call Fl_Grid::clear() + to remove all widgets and reset the layout to zero rows and columns. + + You must call layout(int rows, int cols, ...) to set a new layout, + allocate new cells, and assign widgets to new cells. + + \todo Fl_Grid::clear() needs to be implemented as documented above! +*/ +void Fl_Grid::clear_layout() { + + delete[] Cols_; + delete[] Rows_; + init(); + for (int i = 0; i < children(); i++) { + child(i)->hide(); + } + need_layout(1); + return; +} + +/** + Set all margins (left, top, right, bottom). + + All margins are measured in pixels inside the box borders. You need + to specify at least one argument, all other arguments are optional. + If you don't specify an argument or use a negative value (e.g. -1) + then that particular margin is not affected. + + \param[in] left left margin + \param[in] top top margin + \param[in] right right margin + \param[in] bottom bottom margin +*/ +void Fl_Grid::margin(int left, int top, int right, int bottom) { + if (left >= 0) + margin_left_ = left; + if (top >= 0) + margin_top_ = top; + if (right >= 0) + margin_right_ = right; + if (bottom >= 0) + margin_bottom_ = bottom; + need_layout(1); +} + +/** + Set default gaps for rows and columns. + + All gaps are positioned below the rows and right of their columns. + + The bottom row and the right-most column don't have a gap, i.e. the gap + sizes of these columns and rows are ignored. You can use a right or + bottom margin instead. + + You have to specify at least one argument, \p col_gap is optional. + If you don't specify an argument or use a negative value (e.g. -1) + then that margin is not affected. + + You can also initialize the default gaps with layout(int, int, int, int). + + \param[in] row_gap default gap for all rows + \param[in] col_gap default gap for all columns + + \see Fl_Grid::layout(int rows, int cols, int margin, int gap) +*/ +void Fl_Grid::gap(int row_gap, int col_gap) { + if (row_gap >= 0) + gap_row_ = row_gap; + if (col_gap >= 0) + gap_col_ = col_gap; + need_layout(1); +} + +/** + Get the grid cell of row \p row and column \p col. + + Widgets and other attributes are organized in cells (Fl_Grid::Cell). + + This cell is an opaque structure (class) with some public methods. + \b Don't assume anything about grid cell sizes and ordering in memory. + These are implementation details that can be changed without notice. + + The validity of an Fl_Grid::Cell pointer is limited. It will definitely be + invalidated when the overall grid layout is changed, for instance by calling + layout(int, int). + + Adding new cells beyond the current layout limits will also invalidate + cell pointers but this is not (yet) implemented. Attempts to assign widgets + to out-of-bounds cells are currently ignored. + + The only well-defined usage of cell pointers is to set one or more properties + like widget alignment of a cell after retrieving the cell pointer. Don't + store cell pointers in your program for later reference. + + \param[in] row row index + \param[in] col column index + + \returns pointer to cell + \retval NULL if \p row or \p col is out of bounds or no widget was assigned +*/ +Fl_Grid::Cell* Fl_Grid::cell(int row, int col) const { + if (row < 0 || row >= rows_ || col < 0 || col >= cols_) + return 0; + Row *r = &Rows_[row]; + Cell *cel = r->cells_; + while (cel) { + if (cel->col_ > col) + return 0; + if (cel->col_ == col) + return cel; + cel = cel->next_; + } + return 0; +} + +/** + Get the grid cell of widget \p widget. + + The pointer to the cell can be used for further assignment of properties + like alignment etc. + + Hint: If you know the row and column index of the cell you should use + Fl_Grid::cell(int row, int col) instead because it is \b much faster. + + Please see Fl_Grid::cell(int row, int col) for details and the + validity of cell pointers. + + \param[in] widget widget whose cell is requested + \retval NULL if \p widget is not assigned to a cell +*/ +Fl_Grid::Cell* Fl_Grid::cell(Fl_Widget *widget) const { + Row *row = Rows_; + for (int r = 0; r < rows_; r++, row++) { + Cell *cel = row->cells_; + while (cel) { + if (cel->widget_ == widget) + return cel; + cel = cel->next_; + } + } + return 0; +} + +/** + Assign a widget to a grid cell and set its alignment. + + This short form sets row and column spanning attributes to (1, 1). + + For more information see + Fl_Grid::widget(Fl_Widget *wi, int row, int col, int rowspan, int colspan, Fl_Grid_Align align) + + \param[in] wi widget to be assigned to the cell + \param[in] row row + \param[in] col column + \param[in] align widget alignment inside the cell + + \return assigned cell + \retval NULL if \p row or \p col is out of bounds + + \see Fl_Grid::widget(Fl_Widget *wi, int row, int col, int rowspan, int colspan, Fl_Grid_Align align) +*/ +Fl_Grid::Cell *Fl_Grid::widget(Fl_Widget *wi, int row, int col, Fl_Grid_Align align) { + return widget(wi, row, col, 1, 1, align); +} + +/** + Assign a widget to a grid cell and set cell spanning and alignment. + + Default alignment is \c FL_GRID_FILL which stretches the widget in + horizontal and vertical directions to fill the whole cell(s) given + by \p colspan and \p rowspan. + + You can use this method to move a widget from one cell to another; it + is automatically removed from its old cell. If the new cell is already + assigned to another widget that widget is deassigned but kept as a + child of the group. + + \param[in] wi widget to be assigned to the cell + \param[in] row row + \param[in] col column + \param[in] rowspan vertical span in cells, default 1 + \param[in] colspan horizontal span in cells, default 1 + \param[in] align widget alignment inside the cell + + \return assigned cell + \retval NULL if \p row or \p col is out of bounds +*/ +Fl_Grid::Cell *Fl_Grid::widget(Fl_Widget *wi, int row, int col, int rowspan, int colspan, Fl_Grid_Align align) { + + if (row < 0 || row > rows_) + return 0; + if (col < 0 || col > cols_) + return 0; + + Cell *c = cell(row, col); + if (!c) { + c = add_cell(row, col); + } + + // check if the widget is already assigned to another cell and deassign it + + if (c->widget_ != wi) { + Cell *oc = cell(wi); // search cells for the same widget + if (oc) { // if found: deassign and remove cell + remove_cell(oc->row_, oc->col_); + } + } + + // assign the widget to this cell + + c->widget_ = wi; + c->align_ = align; + + c->w_ = wi->w(); + c->h_ = wi->h(); + + if (rowspan > 0) + c->rowspan_ = rowspan; + if (colspan > 0) + c->colspan_ = colspan; + + need_layout(1); + return c; +} + +/** + Set the minimal width of a column. + + Column widths are calculated by using the maximum of all widget widths + in that column and the given column width. After calculating the width + additional space is added when resizing according to the \c weight of + the column. + + You can set one or more column widths in one call by using + Fl_Grid::col_width(const int *value, size_t size). + + \param[in] col column number (counting from 0) + \param[in] value minimal column width, must be \>= 0 + + \see Fl_Grid::col_width(const int *value, size_t size) +*/ + +void Fl_Grid::col_width(int col, int value) { + if (col >= 0 && col < cols_) { + if (Cols_[col].minw_ != value) { + Cols_[col].minw_ = value; + need_layout(1); + } + } +} + +/** + Set minimal widths of more than one column. + + The values are taken from the array \p value and assigned sequentially + to columns, starting from column 0. If the array \p size is too large + extraneous values are ignored. + + Negative values in the \p array are not assigned to their columns, + i.e. the existing value for the corresponding column is not changed. + + Example: + \code + int widths[] = { 0, 0, 50, -1, -1, 50, 0 }; + grid->col_width(widths, sizeof(width)/sizeof(width[0])); + \endcode + + \param[in] value an array of column widths + \param[in] size the size of the array (number of values) +*/ +void Fl_Grid::col_width(const int *value, size_t size) { + Col *c = Cols_; + for (int i = 0; i < cols_; i++, value++, c++) { + if (i >= (int)size) break; + if (*value >= 0) + c->minw_ = *value; + } + need_layout(1); +} + +/** + Set the weight of a column. + + Column and row weights are used to distribute additional space when the + grid is resized beyond its defined (minimal) size. All weight values are + relative and can be chosen freely. Suggested weights are in the range + {0 .. 100}, 0 (zero) disables resizing of the column. + + How does it work? + + Whenever additional space (say: \c SPACE in pixels) is to be distributed + to a set of columns the weights of all columns are added to a value \c SUM, + then every single column width is increased by the value (in pseudo code): + \code + col.width += SPACE * col.weight / SUM + \endcode + Resulting pixel values are rounded to the next integer and rounding + differences are added to or subtracted from the column with the highest + weight. If more columns have the same weight one of them is chosen. + + \note If none of the columns considered for resizing have weights \> 0 + then Fl_Grid assigns the remaining space to an arbitrary column or to + all considered columns evenly. This is implementation defined and can + be changed without notice. You can avoid this situation by designing + your grid with sensible sizes and weights. + + \param[in] col column number (counting from 0) + \param[in] value weight, must be \>= 0 +*/ +void Fl_Grid::col_weight(int col, int value) { + if (col >= 0 && col < cols_) + Cols_[col].weight_ = value; + need_layout(1); +} + + +/** + Set the weight of more than one column. + + The values are taken from the array \p value and assigned sequentially + to columns, starting from column 0. If the array \p size is too large + extraneous values are ignored. + + Negative values in the \p array are not assigned to their columns, + i.e. the existing value for the corresponding column is not changed. + + Example: + \code + int val[] = { 0, 0, 50, -1, -1, 50, 0 }; + grid->col_weight(val, sizeof(val)/sizeof(val[0])); + \endcode + + \param[in] value an array of column weights + \param[in] size the size of the array (number of values) +*/ +void Fl_Grid::col_weight(const int *value, size_t size) { + Col *c = Cols_; + for (int i = 0; i < cols_; i++, value++, c++) { + if (i >= (int)size) break; + if (*value >= 0) + c->weight_ = *value; + } + need_layout(1); +} + +/** + Set the gap of column \c col. + + Note that the gap is right of each column except the last one + which is ignored. Use margin() for the right most column. + + \param[in] col column + \param[in] value gap size after the column +*/ +void Fl_Grid::col_gap(int col, int value) { + if (col >= 0 && col < cols_) + Cols_[col].gap_ = value; + need_layout(1); +} + +/** + Set more than one column gaps at once. + + \see Fl_Grid::col_weight(const int *value, size_t size) for + handling of the value array and \p size. +*/ +void Fl_Grid::col_gap(const int *value, size_t size) { + Col *c = Cols_; + for (int i = 0; i < cols_; i++, value++, c++) { + if (i >= (int)size) break; + if (*value >= 0) + c->gap_ = *value; + } + need_layout(1); +} + +/** + Set the minimal row height of row \c row. + + \param[in] row row + \param[in] value minimal height of the row +*/ +void Fl_Grid::row_height(int row, int value) { + if (row >= 0 && row < rows_) + Rows_[row].minh_ = value; + need_layout(1); +} + + +/** + Set the minimal row height of more than one row. + + \param[in] value array of height values + \param[in] size size of array \p value + + \see Fl_Grid::col_weight(const int *value, size_t size) for + handling of the value array and \p size. +*/ +void Fl_Grid::row_height(const int *value, size_t size) { + Row *r = Rows_; + for (int i = 0; i < rows_; i++, value++, r++) { + if (i >= (int)size) break; + if (*value >= 0) + r->minh_ = *value; + } + need_layout(1); +} + +/** + Set the row weight of row \c row. + + \param[in] row row + \param[in] value weight of the row +*/ +void Fl_Grid::row_weight(int row, int value) { + if (row >= 0 && row < rows_) + Rows_[row].weight_ = value; + need_layout(1); +} + +/** + Set the weight of more than one row. + + \param[in] value array of height values + \param[in] size size of array \p value + + \see Fl_Grid::col_weight(const int *value, size_t size) for + handling of the \p value array and \p size. +*/ + +void Fl_Grid::row_weight(const int *value, size_t size) { + Row *r = Rows_; + for (int i = 0; i < rows_; i++, value++, r++) { + if (i >= (int)size) break; + if (*value >= 0) + r->weight_ = *value; + } + need_layout(1); +} + +/** + Set the gap of row \c row. + + Note that the gap is below each row except the last one + which is ignored. Use margin() for the bottom row. + + \param[in] row row + \param[in] value gap size below the row +*/ +void Fl_Grid::row_gap(int row, int value) { + if (row >= 0 && row < rows_) + Rows_[row].gap_ = value; + need_layout(1); +} + + +/** + Set more than one row gaps at once. + + \see Fl_Grid::col_weight(const int *value, size_t size) for + handling of the value array and \p size. +*/ +void Fl_Grid::row_gap(const int *value, size_t size) { + Row *r = Rows_; + for (int i = 0; i < rows_; i++, value++, r++) { + if (i >= (int)size) break; + if (*value >= 0) + r->gap_ = *value; + } + need_layout(1); +} + +/** + Output layout information of this Fl_Grid to stderr. + + Parameter \p level will be used to define the amount of output. + - 0 = nothing + - 127 = everything + - other values not yet defined + + \note It is not yet defined which kind of values \p level will have, + either a numerical value (127 = maximum, 0 = nothing) or a bit mask + that determines what to output. + + \todo Add more information about cells and children. + \todo Control output by using \p level. + + \param[in] level not yet used (0-127, default = 127) +*/ +void Fl_Grid::debug(int level) { + if (level <= 0) + return; + fprintf(stderr, "Fl_Grid::layout(%d, %d) at (%d, %d, %d, %d)\n", + rows_, cols_, x(), y(), w(), h()); + fprintf(stderr, " margins: (%2d, %2d, %2d, %2d)\n", + margin_left_, margin_top_, margin_right_, margin_bottom_); + fprintf(stderr, " gaps: (%2d, %2d)\n", + gap_row_, gap_col_); + Row *row = Rows_; + for (int r = 0; r < rows_; r++, row++) { + fprintf(stderr, "Row %2d: minh = %d, weight = %d, gap = %d, h = %d\n", + r, row->minh_, row->weight_, row->gap_, row->h_); + Cell *cel = row->cells_; + while (cel) { + fprintf(stderr, " Cell(%2d, %2d)\n", cel->row_, cel->col_); + cel = cel->next_; + } + } + fflush(stderr); // necessary for Windows +} diff --git a/src/Makefile b/src/Makefile index c025ff1d3..de1174e16 100644 --- a/src/Makefile +++ b/src/Makefile @@ -45,6 +45,7 @@ CPPFILES = \ Fl_File_Input.cxx \ Fl_Flex.cxx \ Fl_Graphics_Driver.cxx \ + Fl_Grid.cxx \ Fl_Group.cxx \ Fl_Help_View.cxx \ Fl_Image.cxx \ diff --git a/src/makedepend b/src/makedepend index 9bd465409..0f973b847 100644 --- a/src/makedepend +++ b/src/makedepend @@ -2238,6 +2238,11 @@ Fl_Graphics_Driver.o: ../FL/platform.H Fl_Graphics_Driver.o: ../FL/platform_types.h Fl_Graphics_Driver.o: ../FL/x11.H Fl_Graphics_Driver.o: Fl_Screen_Driver.H +Fl_Grid.o: ../FL/fl_draw.H +Fl_Grid.o: ../FL/Fl_Grid.H +Fl_Grid.o: ../FL/Fl_Group.H +Fl_Grid.o: ../FL/Fl_Rect.H +Fl_Grid.o: ../FL/Fl_Widget.H Fl_Group.o: ../FL/Enumerations.H Fl_Group.o: ../FL/Fl.H Fl_Group.o: ../FL/fl_attr.h |
