summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlbrecht Schlosser <albrechts.fltk@online.de>2023-10-16 22:11:33 +0200
committerAlbrecht Schlosser <albrechts.fltk@online.de>2023-10-16 22:18:24 +0200
commit38871c5b3192bccd508f3686413d4e56041ab091 (patch)
tree26db3ef4a73e93f23a1d1bcb037652aafc26dbed
parente7b790ae31b3c5d5e819f35f5fa8bd3619f7103f (diff)
Add Fl_Grid widget and test and demo programs
- FL/Fl_Grid.H: header file - src/Fl_Grid.cxx: implementation - examples/grid-simple.cxx: simple example program - test/cube.cxx: use Fl_Grid for layout - test/grid_alignment.cxx: test cell alignment and other functions - test/grid_buttons.cxx: demo program as discussed in fltk.general - test/grid_login.cxx: like test/flex_login.cxx but with Fl_Grid - test/flex_login.cxx: modified to match test/grid_login.cxx
-rw-r--r--FL/Fl_Grid.H333
-rw-r--r--documentation/src/Fl_Grid.pngbin0 -> 10544 bytes
-rw-r--r--examples/.gitignore1
-rw-r--r--examples/CMakeLists.txt9
-rw-r--r--examples/Makefile1
-rw-r--r--examples/grid-simple.cxx49
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/Fl_Grid.cxx1108
-rw-r--r--src/Makefile1
-rw-r--r--src/makedepend5
-rw-r--r--test/.gitignore3
-rw-r--r--test/CMakeLists.txt4
-rw-r--r--test/Makefile12
-rw-r--r--test/cube.cxx189
-rw-r--r--test/flex_login.cxx27
-rw-r--r--test/grid_alignment.cxx203
-rw-r--r--test/grid_buttons.cxx75
-rw-r--r--test/grid_login.cxx94
-rw-r--r--test/makedepend2
19 files changed, 2008 insertions, 109 deletions
diff --git a/FL/Fl_Grid.H b/FL/Fl_Grid.H
new file mode 100644
index 000000000..d7782ee5e
--- /dev/null
+++ b/FL/Fl_Grid.H
@@ -0,0 +1,333 @@
+//
+// Fl_Grid widget header 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
+//
+
+#ifndef _FL_FL_GRID_H_
+#define _FL_FL_GRID_H_
+
+/** \file FL/Fl_Grid.H
+ Fl_Grid container widget.
+*/
+
+#include <FL/Fl_Group.H>
+#include <FL/Fl_Rect.H>
+
+/** Fl_Grid type for child widget alignment control. */
+typedef unsigned short Fl_Grid_Align;
+
+/** Align the widget in the middle of the cell (default). */
+const Fl_Grid_Align FL_GRID_CENTER = 0x0000;
+
+/** Align the widget at the top of the cell. */
+const Fl_Grid_Align FL_GRID_TOP = 0x0001;
+
+/** Align the widget at the bottom of the cell. */
+const Fl_Grid_Align FL_GRID_BOTTOM = 0x0002;
+
+/** Align the widget at the left side of the cell. */
+const Fl_Grid_Align FL_GRID_LEFT = 0x0004;
+
+/** Align the widget at the right side of the cell. */
+const Fl_Grid_Align FL_GRID_RIGHT = 0x0008;
+
+/** Stretch the widget horizontally to fill the cell. */
+const Fl_Grid_Align FL_GRID_HORIZONTAL = 0x0010;
+
+/** Stretch the widget vertically to fill the cell. */
+const Fl_Grid_Align FL_GRID_VERTICAL = 0x0020;
+
+/** Stretch the widget in both directions to fill the cell. */
+const Fl_Grid_Align FL_GRID_FILL = 0x0030;
+
+/** Stretch the widget proportionally. */
+const Fl_Grid_Align FL_GRID_PROPORTIONAL = 0x0040;
+
+const Fl_Grid_Align FL_GRID_TOP_LEFT = FL_GRID_TOP | FL_GRID_LEFT;
+const Fl_Grid_Align FL_GRID_TOP_RIGHT = FL_GRID_TOP | FL_GRID_RIGHT;
+const Fl_Grid_Align FL_GRID_BOTTOM_LEFT = FL_GRID_BOTTOM | FL_GRID_LEFT;
+const Fl_Grid_Align FL_GRID_BOTTOM_RIGHT = FL_GRID_BOTTOM | FL_GRID_RIGHT;
+
+/**
+ Fl_Grid is a container (layout) widget with multiple columns and rows.
+
+ This container widget features very flexible layouts in columns and rows
+ w/o the need to position each child widget in x/y coordinates.
+
+ Widgets are assigned to grid cells (column, row) with their minimal sizes
+ in \p w() and \p h(). The \p x() and \p y() positions are ignored and can
+ be (0, 0). Fl_Grid calculates widget positions and resizes the widgets to
+ fit into the grid. It is possible to create a single row or column of
+ widgets with Fl_Grid.
+
+ You should design your grid with the smallest possible sizes of all widgets
+ in mind. Fl_Grid will automatically assign additional space to cells
+ according to some rules (described later) when resizing the Fl_Grid widget.
+
+ \b Hint: You should set a minimum window size to make sure the Fl_Grid is
+ never resized below its minimal sizes. Resizing below the given widget
+ sizes results in undefined behavior.
+
+ Fl_Grid and other container widgets (e.g. Fl_Group) can be nested. One main
+ advantage of this usage is that widget coordinates in embedded Fl_Group
+ widgets become relative to the group and will be positioned as expected.
+ \todo This (relative group coordinates of nested groups of Fl_Grid)
+ needs explanation and maybe an example.
+
+ Fl_Grid child widgets are handled by its base class Fl_Group but Fl_Grid
+ stores additional data corresponding to each widget in internal grid cells.
+
+ Fl_Grid children are allowed to span multiple columns and rows like HTML
+ \<table\> cells. Individual children can have fixed sizes or be aligned
+ inside their cells (left, right, top, bottom, and more) and/or follow
+ their cell sizes when the Fl_Grid container is resized.
+
+ Note to resizing: since Fl_Grid uses its own layout algorithm the normal
+ Fl_Group::resizable() widget is ignored (if set). Calling init_sizes()
+ is not necessary.
+
+ \note Fl_Grid is, as of FLTK 1.4.0, still in experimental state and
+ should be used with caution. The API can still be changed although it is
+ assumed to be almost stable - as stable as possible for a first release.
+
+ Example: Simple 3x3 Fl_Grid with five buttons:
+ \n
+ \code
+ #include <FL/Fl.H>
+ #include <FL/Fl_Double_Window.H>
+ #include <FL/Fl_Grid.H>
+ #include <FL/Fl_Button.H>
+
+ int main(int argc, char **argv) {
+ Fl_Double_Window *win = new Fl_Double_Window(320, 180, "3x3 Fl_Grid with Buttons");
+ // create the Fl_Grid container with five buttons
+ Fl_Grid *grid = new Fl_Grid(0, 0, win->w(), win->h());
+ grid->layout(3, 3, 10, 10);
+ grid->color(FL_WHITE);
+ Fl_Button *b0 = new Fl_Button(0, 0, 0, 0, "New");
+ Fl_Button *b1 = new Fl_Button(0, 0, 0, 0, "Options");
+ Fl_Button *b3 = new Fl_Button(0, 0, 0, 0, "About");
+ Fl_Button *b4 = new Fl_Button(0, 0, 0, 0, "Help");
+ Fl_Button *b6 = new Fl_Button(0, 0, 0, 0, "Quit");
+ // assign buttons to grid positions
+ grid->widget(b0, 0, 0);
+ grid->widget(b1, 0, 2);
+ grid->widget(b3, 1, 1);
+ grid->widget(b4, 2, 0);
+ grid->widget(b6, 2, 2);
+ grid->show_grid(0); // 1 to display grid helper lines
+ win->end();
+ win->resizable(grid);
+ win->size_range(300, 100);
+ win->show(argc, argv);
+ return Fl::run();
+ }
+ \endcode
+
+ \image html Fl_Grid.png
+ \image latex Fl_Grid.png "Simple 3x3 Fl_Grid" width=7cm
+*/
+class Fl_Grid : public Fl_Group {
+
+public:
+ class Cell {
+ friend class Fl_Grid;
+ private:
+ Cell *next_; // next cell in row
+ short row_; // row number
+ short col_; // column number
+ short rowspan_; // row span (1 - n)
+ short colspan_; // column span (1 - n)
+ Fl_Grid_Align align_; // widget alignment in its cell
+ Fl_Widget *widget_; // assigned widget
+ int w_; // minimal widget width
+ int h_; // minimal widget height
+
+ public:
+
+ Cell(int row, int col) {
+ next_ = NULL;
+ row_ = row;
+ col_ = col;
+ rowspan_ = 1;
+ colspan_ = 1;
+ widget_ = NULL;
+ w_ = 0;
+ h_ = 0;
+ align_ = 0;
+ }
+
+ ~Cell() {}
+
+ Fl_Widget *widget() { return widget_; }
+ void align(Fl_Grid_Align align) {
+ align_ = align;
+ }
+ }; // class Cell
+
+private:
+ class Row;
+ class Col;
+ short rows_;
+ short cols_;
+
+ short margin_left_; // left margin
+ short margin_top_; // top margin
+ short margin_right_; // right margin
+ short margin_bottom_; // bottom margin
+ short gap_row_; // gap between rows
+ short gap_col_; // gap between columns
+ Fl_Rect old_size; // only for resize callback (TBD)
+ Col *Cols_; // array of columns
+ Row *Rows_; // array of rows
+ bool need_layout_; // true if layout needs to be calculated
+
+protected:
+ Fl_Color grid_color; // color for drawing the grid lines (design helper)
+ bool draw_grid_; // draw the grid for testing / design
+
+private:
+ void init();
+ Cell *add_cell(int row, int col);
+ void remove_cell(int row, int col);
+
+public:
+ Fl_Grid(int X, int Y, int W, int H, const char *L = 0);
+ virtual ~Fl_Grid();
+
+ // define and manage the layout and resizing
+
+ virtual void layout(int rows, int cols, int margin = -1, int gap = -1);
+ virtual void layout();
+ virtual void clear_layout();
+ virtual void resize(int X, int Y, int W, int H) FL_OVERRIDE;
+
+ /**
+ Request or reset the request to calculate the layout of children.
+
+ If called with \p true (1) this calls redraw() to schedule a
+ full draw(). When draw is eventually called, the layout is
+ (re)calculated before actually drawing the widget.
+
+ \param[in] set 1 to request layout calculation,\n
+ 0 to reset the request
+ */
+ void need_layout(int set) {
+ if (set) {
+ need_layout_ = true;
+ redraw();
+ }
+ else {
+ need_layout_ = false;
+ }
+ }
+
+ /**
+ Return whether layout calculation is required.
+ */
+ bool need_layout() const {
+ return need_layout_;
+ }
+
+protected:
+ virtual void draw() FL_OVERRIDE;
+ void on_remove(int) FL_OVERRIDE;
+ virtual void draw_grid(); // draw grid lines for debugging
+
+public:
+
+ // set individual margins
+
+ virtual void margin(int left, int top = -1, int right = -1, int bottom = -1);
+
+ // set default row and column gaps for all rows and columns, respectively
+
+ virtual void gap(int row_gap, int col_gap = -1); // set default row and column gap(s)
+
+ // find cells, get cell pointers
+
+ Fl_Grid::Cell* cell(int row, int col) const;
+ Fl_Grid::Cell* cell(Fl_Widget *widget) const;
+
+ // assign a widget to a cell
+
+ Fl_Grid::Cell* widget(Fl_Widget *wi, int row, int col, Fl_Grid_Align align = FL_GRID_FILL);
+ Fl_Grid::Cell* widget(Fl_Widget *wi, int row, int col, int rowspan, int colspan, Fl_Grid_Align align = FL_GRID_FILL);
+
+ // set minimal column and row sizes (widths and heights, respectively),
+ // set row and column specific gaps and weights
+
+ void col_width(int col, int value);
+ void col_width(const int *value, size_t size);
+
+ void col_weight(int col, int value);
+ void col_weight(const int *value, size_t size);
+
+ void col_gap(int col, int value);
+ void col_gap(const int *value, size_t size);
+
+ void row_height(int row, int value);
+ void row_height(const int *value, size_t size);
+
+ void row_weight(int row, int value);
+ void row_weight(const int *value, size_t size);
+
+ void row_gap(int row, int value);
+ void row_gap(const int *value, size_t size);
+
+ /**
+ Enable or disable drawing of the grid helper lines for visualization.
+
+ Use this method during the design stage of your Fl_Grid widget or
+ for debugging if widgets are not positioned as intended.
+
+ The default is a light green color but you can change it for better
+ contrast if needed, see show_grid(int set, Fl_Color col).
+
+ \note You can define the environment variable \c FLTK_GRID_DEBUG=1
+ to set show_grid(1) for all Fl_Grid widgets at construction time.
+ This enables you to debug the grid layout w/o changing code.
+
+ \param[in] set 1 (true) = draw, 0 = don't draw the grid
+
+ \see show_grid(int set, Fl_Color col)
+ */
+ void show_grid(int set) {
+ draw_grid_ = set ? true : false;
+ }
+
+ /**
+ Enable or disable drawing of the grid helper lines for visualization.
+
+ This method also sets the color used for the helper lines.
+
+ The default is a light green color but you can change it to any color
+ for better contrast if needed.
+
+ \param[in] set 1 (true) = draw, 0 = don't draw the grid
+ \param[in] col color to use for the grid helper lines
+
+ \see show_grid(int set)
+ */
+ void show_grid(int set, Fl_Color col) {
+ draw_grid_ = set ? true : false;
+ grid_color = col;
+ }
+
+ void debug(int level = 127);
+
+}; // class Fl_Grid
+
+#endif // _FL_FL_GRID_H_
diff --git a/documentation/src/Fl_Grid.png b/documentation/src/Fl_Grid.png
new file mode 100644
index 000000000..62341b63d
--- /dev/null
+++ b/documentation/src/Fl_Grid.png
Binary files differ
diff --git a/examples/.gitignore b/examples/.gitignore
index f62a5e4db..d4a21b441 100644
--- a/examples/.gitignore
+++ b/examples/.gitignore
@@ -12,6 +12,7 @@ callbacks
cairo-draw-x
chart-simple
draggable-group
+grid-simple
howto-add_fd-and-popen
howto-browser-with-icons
howto-drag-and-drop
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index b1409d12e..ea0b8487a 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -1,7 +1,7 @@
#
# CMakeLists.txt used to build example apps by the CMake build system
#
-# Copyright 2020-2022 by Bill Spitzak and others.
+# Copyright 2020-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
@@ -31,10 +31,11 @@ file (MAKE_DIRECTORY ${EXECUTABLE_OUTPUT_PATH})
############################################################
set (SIMPLE_SOURCES
- chart-simple
- callbacks
browser-simple
+ callbacks
+ chart-simple
draggable-group
+ grid-simple
howto-add_fd-and-popen
howto-browser-with-icons
howto-drag-and-drop
@@ -45,8 +46,8 @@ set (SIMPLE_SOURCES
howto-remap-numpad-keyboard-keys
howto-text-over-image-button
menubar-add
- nativefilechooser-simple-app
nativefilechooser-simple
+ nativefilechooser-simple-app
progress-simple
shapedwindow
simple-terminal
diff --git a/examples/Makefile b/examples/Makefile
index 57d87de39..43f1d3119 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -31,6 +31,7 @@ ALL = animgifimage$(EXEEXT) \
callbacks$(EXEEXT) \
chart-simple$(EXEEXT) \
draggable-group$(EXEEXT) \
+ grid-simple$(EXEEXT) \
howto-add_fd-and-popen$(EXEEXT) \
howto-browser-with-icons$(EXEEXT) \
howto-drag-and-drop$(EXEEXT) \
diff --git a/examples/grid-simple.cxx b/examples/grid-simple.cxx
new file mode 100644
index 000000000..f08a5957b
--- /dev/null
+++ b/examples/grid-simple.cxx
@@ -0,0 +1,49 @@
+//
+// Fl_Grid Example Program 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
+//
+
+// This example program is also included in the documentation.
+// See FL/Fl_Grid.H
+
+#include <FL/Fl.H>
+#include <FL/Fl_Double_Window.H>
+#include <FL/Fl_Grid.H>
+#include <FL/Fl_Button.H>
+
+int main(int argc, char **argv) {
+ Fl_Double_Window *win = new Fl_Double_Window(320, 180, "3x3 Fl_Grid with Buttons");
+ // create the Fl_Grid container with five buttons
+ Fl_Grid *grid = new Fl_Grid(0, 0, win->w(), win->h());
+ grid->layout(3, 3, 10, 10);
+ grid->color(FL_WHITE);
+ Fl_Button *b0 = new Fl_Button(0, 0, 0, 0, "New");
+ Fl_Button *b1 = new Fl_Button(0, 0, 0, 0, "Options");
+ Fl_Button *b3 = new Fl_Button(0, 0, 0, 0, "About");
+ Fl_Button *b4 = new Fl_Button(0, 0, 0, 0, "Help");
+ Fl_Button *b6 = new Fl_Button(0, 0, 0, 0, "Quit");
+ // assign buttons to grid positions
+ grid->widget(b0, 0, 0);
+ grid->widget(b1, 0, 2);
+ grid->widget(b3, 1, 1);
+ grid->widget(b4, 2, 0);
+ grid->widget(b6, 2, 2);
+ // grid->show_grid(1); // enable to display grid helper lines
+ win->end();
+ win->resizable(grid);
+ win->size_range(300, 100);
+ win->show(argc, argv);
+ return Fl::run();
+}
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
diff --git a/test/.gitignore b/test/.gitignore
index 493ff3a75..cd29e2fca 100644
--- a/test/.gitignore
+++ b/test/.gitignore
@@ -52,6 +52,9 @@ fullscreen
gl_overlay
glpuzzle
glut_test
+grid_alignment
+grid_buttons
+grid_login
handle_events
hello
help_dialog
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index a44498c4d..e693399a4 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -92,6 +92,10 @@ if (OPENGL_FOUND)
CREATE_EXAMPLE (glut_test glut_test.cxx "fltk_gl;fltk")
endif()
+CREATE_EXAMPLE (grid_alignment grid_alignment.cxx fltk)
+CREATE_EXAMPLE (grid_buttons grid_buttons.cxx fltk)
+CREATE_EXAMPLE (grid_login grid_login.cxx fltk)
+
if (OPENGL_FOUND)
CREATE_EXAMPLE (handle_events handle_events.cxx "fltk_gl;fltk") # opt. Fl_Gl_Window
else()
diff --git a/test/Makefile b/test/Makefile
index e73431b03..e69b8a9fd 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -76,6 +76,9 @@ CPPFILES =\
gl_overlay.cxx \
glpuzzle.cxx \
glut_test.cxx \
+ grid_alignment \
+ grid_buttons \
+ grid_login \
hello.cxx \
help_dialog.cxx \
icon.cxx \
@@ -161,6 +164,9 @@ ALL = \
fltk-versions$(EXEEXT) \
fonts$(EXEEXT) \
forms$(EXEEXT) \
+ grid_alignment$(EXEEXT) \
+ grid_buttons$(EXEEXT) \
+ grid_login$(EXEEXT) \
hello$(EXEEXT) \
help_dialog$(EXEEXT) \
icon$(EXEEXT) \
@@ -437,6 +443,12 @@ forms$(EXEEXT): forms.o
$(CXX) $(ARCHFLAGS) $(CXXFLAGS) $(LDFLAGS) -o $@ forms.o $(LINKFLTKFORMS) $(LDLIBS)
$(OSX_ONLY) ../fltk-config --post $@
+grid_alignment$(EXEEXT): grid_alignment.o
+
+grid_buttons$(EXEEXT): grid_buttons.o
+
+grid_login$(EXEEXT): grid_login.o
+
hello$(EXEEXT): hello.o
help_dialog$(EXEEXT): help_dialog.o $(IMGLIBNAME)
diff --git a/test/cube.cxx b/test/cube.cxx
index 2dacb5976..bd69bc42c 100644
--- a/test/cube.cxx
+++ b/test/cube.cxx
@@ -3,7 +3,7 @@
//
// Modified to have 2 cubes to test multiple OpenGL contexts
//
-// Copyright 1998-2021 by Bill Spitzak and others.
+// Copyright 1998-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
@@ -26,7 +26,7 @@
#include <FL/Fl_Slider.H>
#include <FL/Fl_Sys_Menu_Bar.H>
#include <FL/Fl_Printer.H> // demo printing
-
+#include <FL/Fl_Grid.H> // grid layout
#include <stdlib.h>
#if !HAVE_GL
@@ -39,8 +39,6 @@ public:
cube_box(int x,int y,int w,int h,const char *l=0) :Fl_Box(FL_DOWN_BOX,x,y,w,h,l) {
label("This demo does\nnot work without GL");
}
- void begin() {}
- void end() {}
};
#else
#include <FL/Fl_Gl_Window.H>
@@ -120,11 +118,8 @@ void cube_box::draw() {
gl_draw(wire ? "Cube: wire" : "Cube: flat", -4.5f, -4.5f );
glEnable(GL_DEPTH_TEST);
- // if an OpenGL graphics driver is installed, give it a chance
- // to draw additional graphics
-#if HAVE_GL
+ // draw additional FLTK widgets and graphics
Fl_Gl_Window::draw();
-#endif
}
int cube_box::handle(int e) {
@@ -135,121 +130,128 @@ int cube_box::handle(int e) {
return Fl_Gl_Window::handle(e);
}
-#endif
+// callback for overlay button (Fl_Button on OpenGL scene)
+void show_info_cb(Fl_Widget*, void*) {
+ fl_message("This is an example of using FLTK widgets inside OpenGL windows.\n"
+ "Multiple widgets can be added to Fl_Gl_Windows. They will be\n"
+ "rendered as overlays over the scene.");
+}
+
+// overlay a button onto an OpenGL window (cube_box)
+void overlay_button(cube_box *cube) {
+ cube->begin();
+ Fl_Widget *w = new Fl_Button(10, 10, 120, 30, "FLTK over GL");
+ w->color(FL_FREE_COLOR);
+ w->box(FL_BORDER_BOX);
+ w->callback(show_info_cb);
+ cube->end();
+}
+
+#endif // HAVE_GL
Fl_Window *form;
Fl_Slider *speed, *size;
Fl_Button *exit_button, *wire, *flat;
cube_box *lt_cube, *rt_cube;
-
int done = 0; // set to 1 in exit button callback
// exit button callback
-void exit_cb(Fl_Widget *, void *) {
+void exit_cb(Fl_Widget *w, void *) {
done = 1;
+ w->window()->hide(); // necessary if built w/o GL
}
// print screen demo
-void print_cb(Fl_Widget *w, void *data)
-{
+void print_cb(Fl_Widget *w, void *data) {
Fl_Printer printer;
Fl_Window *win = Fl::first_window();
- if(!win) return;
- if( printer.start_job(1) ) return;
- if( printer.start_page() ) return;
- printer.scale(0.5,0.5);
- printer.print_widget( win );
+ if (!win) return;
+ if (printer.start_job(1)) return;
+ if (printer.start_page()) return;
+ printer.scale(0.5, 0.5);
+ printer.print_widget(win);
printer.end_page();
printer.end_job();
}
// Create a form that allows resizing for A and C (GL windows) with B fixed size/centered:
//
-// lt_grp rt_grp
// |<--------------------------------------->|<---------------------->|
-// . lt_cube ct_grp : rt_cube .
+// . lt_cube center : rt_cube .
// . 350 100 : 350 .
// . |<------------------->| |<-------->| |<------------------->| .
// ....................................................................
-// : ....................... ............ ....................... :
+// : ....................... ............ ....................... : __
// : : : : : : : :
-// : : A : : B : : C : :
+// : : A : : B : : C : : h = 350
// : : : : : : : :
// : :.....................: :..........: :.....................: : __
// :..................................................................: __ MARGIN
//
-// | |
-// MARGIN
-//
+// | | | | | |
+// MARGIN GAP GAP
-#define MENUBAR_H 25 // menubar height
-#define MARGIN 20 // fixed margin around widgets
-#define MARGIN2 (MARGIN*2)
-#define MARGIN3 (MARGIN*3)
-
-void show_info_cb(Fl_Widget*, void*) {
- fl_message("This is an example of using FLTK widgets inside OpenGL windows.\n"
- "Multiple widgets can be added to Fl_Gl_Windows. They will be\n"
- "rendered as overlays over the scene.");
-}
+#define MENUBAR_H 25 // menubar height
+#define MARGIN 20 // fixed margin around widgets
+#define GAP 20 // fixed gap between widgets
void makeform(const char *name) {
// Widget's XYWH's
- int form_w = 800 + 4 * MARGIN; // main window width
+ int form_w = 800 + 2 * MARGIN + 2 * GAP; // main window width
int form_h = 350 + MENUBAR_H + 2 * MARGIN; // main window height
- int me_bar_x=0, me_bar_y=0, me_bar_w=form_w, me_bar_h=MENUBAR_H; // menubar
- int lt_grp_x=0, lt_grp_y=MENUBAR_H+MARGIN, lt_grp_w=350+100+MARGIN3, lt_grp_h=form_h-MENUBAR_H-MARGIN2; // left group
- int lt_cub_x=lt_grp_x+MARGIN, lt_cub_y=lt_grp_y, lt_cub_w=350, lt_cub_h=lt_grp_h; // left cube box (GL)
- int ct_grp_x=lt_grp_x+350+MARGIN2, ct_grp_y=lt_grp_y, ct_grp_w=100, ct_grp_h=lt_grp_h; // center group
- int rt_grp_x=lt_grp_x+lt_grp_w, rt_grp_y=lt_grp_y, rt_grp_w=350+MARGIN, rt_grp_h=lt_grp_h; // right group
- int rt_cub_x=rt_grp_x, rt_cub_y=lt_grp_y, rt_cub_w=350, rt_cub_h=lt_grp_h; // right cube box (GL)
// main window
form = new Fl_Window(form_w, form_h, name);
- form->begin();
- // menu bar
- Fl_Sys_Menu_Bar *menubar = new Fl_Sys_Menu_Bar(me_bar_x, me_bar_y, me_bar_w, me_bar_h);
- menubar->add("File/Print window", FL_COMMAND+'p', print_cb);
- menubar->add("File/Quit", FL_COMMAND+'q', exit_cb);
- // left group
- Fl_Group *lt_grp = new Fl_Group(lt_grp_x, lt_grp_y, lt_grp_w, lt_grp_h);
- lt_grp->begin();
- // left GL window
- lt_cube = new cube_box(lt_cub_x, lt_cub_y, lt_cub_w, lt_cub_h, 0);
+ // menu bar
+ Fl_Sys_Menu_Bar *menubar = new Fl_Sys_Menu_Bar(0, 0, form_w, MENUBAR_H);
+ menubar->add("File/Print window", FL_COMMAND+'p', print_cb);
+ menubar->add("File/Quit", FL_COMMAND+'q', exit_cb);
+
+ // Fl_Grid (layout)
+ Fl_Grid *grid = new Fl_Grid(0, MENUBAR_H, form_w, 350 + 2 * MARGIN);
+ grid->layout(4, 4, MARGIN, GAP);
+ grid->box(FL_FLAT_BOX);
+
+ // set column and row weights to control resizing behavior
+ int cwe[] = {50, 0, 0, 50}; // column weights
+ int rwe[] = { 0, 0, 50, 0}; // row weights
+ grid->col_weight(cwe, 4); // set weights for resizing
+ grid->row_weight(rwe, 4); // set weights for resizing
+
+ // set non-default gaps for special layout purposes and labels
+ grid->row_gap(0, 0); // no gap below wire button
+ grid->row_gap(2, 50); // gap below sliders for labels
- lt_cube->begin();
- Fl_Widget *w = new Fl_Button(10, 10, 120, 30, "FLTK over GL");
- w->color(FL_FREE_COLOR);
- w->box(FL_BORDER_BOX );
- w->callback(show_info_cb);
- lt_cube->end();
+ // left GL window
+ lt_cube = new cube_box(0, 0, 350, 350);
- // center group
- Fl_Group *ct_grp = new Fl_Group(ct_grp_x, ct_grp_y, ct_grp_w, ct_grp_h);
- ct_grp->begin();
- wire = new Fl_Radio_Light_Button(ct_grp_x, ct_grp_y, 100, 25, "Wire");
- flat = new Fl_Radio_Light_Button(ct_grp_x, wire->y()+wire->h(), 100, 25, "Flat");
- speed = new Fl_Slider(FL_VERT_SLIDER, ct_grp_x, flat->y()+flat->h()+MARGIN, 40, 200, "Speed");
- size = new Fl_Slider(FL_VERT_SLIDER, ct_grp_x+40+MARGIN, flat->y()+flat->h()+MARGIN, 40, 200, "Size");
- exit_button = new Fl_Button(ct_grp_x, form_h-MARGIN-25, 100, 25, "Exit");
- exit_button->callback(exit_cb);
- ct_grp->end();
- ct_grp->resizable(speed); // only sliders resize vertically, not buttons
- lt_grp->end();
- lt_grp->resizable(lt_cube);
- // right group
- Fl_Group *rt_grp = new Fl_Group(rt_grp_x, rt_grp_y, rt_grp_w, rt_grp_h);
- rt_grp->begin();
- // right GL window
- rt_cube = new cube_box(rt_cub_x, rt_cub_y, rt_cub_w, rt_cub_h, 0);
- rt_grp->end();
- rt_grp->resizable(rt_cube);
- // right resizer
- Fl_Box *rt_resizer = new Fl_Box(rt_grp_x-5, rt_grp_y, 10, rt_grp_h);
- rt_resizer->box(FL_NO_BOX);
+ // center group
+ wire = new Fl_Radio_Light_Button( 0, 0, 100, 25, "Wire");
+ flat = new Fl_Radio_Light_Button( 0, 0, 100, 25, "Flat");
+ speed = new Fl_Slider(FL_VERT_SLIDER, 0, 0, 40, 90, "Speed");
+ size = new Fl_Slider(FL_VERT_SLIDER, 0, 0, 40, 90, "Size");
+ exit_button = new Fl_Button( 0, 0, 100, 25, "Exit");
+ exit_button->callback(exit_cb);
+
+ // right GL window
+ rt_cube = new cube_box(0, 0, 350, 350);
+
+ // assign widgets to grid positions (R=row, C=col) and sizes
+ // RS=rowspan, CS=colspan: R, C, RS, CS, optional alignment
+ grid->widget(lt_cube, 0, 0, 4, 1);
+ grid->widget(wire, 0, 1, 1, 2);
+ grid->widget(flat, 1, 1, 1, 2);
+ grid->widget(speed, 2, 1, 1, 1, FL_GRID_VERTICAL);
+ grid->widget(size, 2, 2, 1, 1, FL_GRID_VERTICAL);
+ grid->widget(exit_button, 3, 1, 1, 2);
+ grid->widget(rt_cube, 0, 3, 4, 1);
+
+#if HAVE_GL
+ overlay_button(lt_cube); // overlay a button onto the OpenGL window
+#endif // HAVE_GL
form->end();
- form->resizable(rt_resizer);
+ form->resizable(grid);
form->size_range(form->w(), form->h()); // minimum window size
}
@@ -257,29 +259,27 @@ int main(int argc, char **argv) {
Fl::use_high_res_GL(1);
Fl::set_color(FL_FREE_COLOR, 255, 255, 0, 75);
makeform(argv[0]);
- speed->bounds(4,0);
-#if HAVE_GL
+ speed->bounds(4, 0);
speed->value(lt_cube->speed = rt_cube->speed = 1.0);
-#else
- speed->value(lt_cube->speed = rt_cube->speed = 0.0);
-#endif
- size->bounds(4,0.01);
- size->value(lt_cube->size = rt_cube->size = 3.0);
+ size->bounds(4, 0.2);
+ size->value(lt_cube->size = rt_cube->size = 2.0);
flat->value(1); lt_cube->wire = 0; rt_cube->wire = 1;
- form->label("cube");
+ form->label("Cube Demo");
form->show(argc,argv);
lt_cube->show();
rt_cube->show();
+
#if 0
// This demonstrates how to manipulate OpenGL contexts.
// In this case the same context is used by multiple windows (I'm not
// sure if this is allowed on Win32, can somebody check?).
- // This fixes a bug on the XFree86 3.0 OpenGL where only one context
- // per program seems to work, but there are probably better uses for
- // this!
+ // This fixes a bug on the XFree86 3.0 OpenGL where only one context per
+ // program seems to work, but there are probably better uses for this!
lt_cube->make_current(); // causes context to be created
rt_cube->context(lt_cube->context()); // share the contexts
#endif
+
+#if HAVE_GL
for (;;) {
if (form->visible() && speed->value()) {
if (!Fl::check()) break; // returns immediately
@@ -295,4 +295,7 @@ int main(int argc, char **argv) {
if (done) break; // exit button was clicked
}
return 0;
+#else
+ return Fl::run();
+#endif
}
diff --git a/test/flex_login.cxx b/test/flex_login.cxx
index be72e6c8b..3f6dc77d8 100644
--- a/test/flex_login.cxx
+++ b/test/flex_login.cxx
@@ -2,7 +2,7 @@
// Fl_Flex demo program for the Fast Light Tool Kit (FLTK).
//
// Copyright 2020 by Karsten Pedersen
-// Copyright 2022 by Bill Spitzak and others.
+// 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
@@ -32,7 +32,10 @@ Fl_Button *create_button(const char *caption) {
void buttons_panel(Fl_Flex *parent) {
new Fl_Box(0, 0, 0, 0, "");
- Fl_Box *w = new Fl_Box(0, 0, 0, 0, "Welcome to Flex Login");
+ Fl_Box *title = new Fl_Box(0, 0, 0, 0, "Welcome to Fl_Flex");
+ title->align(FL_ALIGN_CENTER);
+ title->labelfont(FL_BOLD + FL_ITALIC);
+ title->labelsize(16);
Fl_Flex *urow = new Fl_Flex(Fl_Flex::ROW);
{
@@ -71,7 +74,7 @@ void buttons_panel(Fl_Flex *parent) {
Fl_Box *b = new Fl_Box(0, 0, 0, 0, "");
- parent->fixed(w, 60);
+ parent->fixed(title, 60);
parent->fixed(urow, 30);
parent->fixed(prow, 30);
parent->fixed(pad, 1);
@@ -95,7 +98,7 @@ void middle_panel(Fl_Flex *parent) {
new Fl_Box(0, 0, 0, 0, "");
- parent->fixed(box, 200);
+ parent->fixed(box, 150);
parent->fixed(spacer, 10);
parent->fixed(bp, 300);
}
@@ -119,21 +122,21 @@ void mainPanel(Fl_Flex *parent) {
int main(int argc, char **argv) {
- Fl_Window *window = new Fl_Double_Window(100, 100, "Simple GUI Example");
+ Fl_Window *win = new Fl_Double_Window(100, 100, "Fl_Flex \"Login\" Layout");
Fl_Flex *col = new Fl_Flex(5, 5, 90, 90, Fl_Flex::COLUMN);
mainPanel(col);
col->end();
- window->resizable(col);
- window->color(fl_rgb_color(250, 250, 250));
- window->end();
+ win->resizable(col);
+ win->color(fl_rgb_color(250, 250, 250));
+ win->end();
- window->resize(0, 0, 640, 480);
- window->size_range(550, 250);
- window->show(argc, argv);
+ win->resize(0, 0, 600, 300); // same size as grid_login
+ win->size_range(550, 250);
+ win->show(argc, argv);
int ret = Fl::run();
- delete window; // not necessary but useful to test for memory leaks
+ delete win; // not necessary but useful to test for memory leaks
return ret;
}
diff --git a/test/grid_alignment.cxx b/test/grid_alignment.cxx
new file mode 100644
index 000000000..59a5368c3
--- /dev/null
+++ b/test/grid_alignment.cxx
@@ -0,0 +1,203 @@
+//
+// Fl_Grid demo program for the Fast Light Tool Kit (FLTK).
+//
+// Copyright 2021 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
+//
+
+//
+// This program tests several different alignment features of Fl_Grid.
+//
+
+#include <FL/Fl.H>
+#include <FL/Fl_Double_Window.H>
+#include <FL/Fl_Grid.H>
+#include <FL/Fl_Button.H>
+#include <FL/Fl_Input.H>
+#include <FL/Fl_Box.H>
+
+// Test function to change the layout (executed by timer callback)
+
+#define TEST_RELAYOUT (0)
+#define TEST_REMOVE_NOTIFY (0)
+
+#if (TEST_RELAYOUT)
+void relayout_cb(void *v) {
+ Fl_Grid *grid = (Fl_Grid *)v;
+ grid->layout(5, 5, 8, 4);
+ grid->margin(10, 20, 30, 40);
+ grid->layout();
+ grid->redraw();
+}
+#endif
+
+#if TEST_REMOVE_NOTIFY
+
+void remove_cb(void *v) {
+ static int n = 10;
+ n--;
+ Fl_Grid *grid = (Fl_Grid *)v;
+#if (0) // test 1: remove() the widget -- leaks memory (!)
+ grid->remove(n);
+#else // test 2: delete the widget -- no leak
+ delete grid->child(0);
+#endif
+ if (n > 0)
+ Fl::repeat_timeout(0.3, remove_cb, v);
+}
+
+#endif
+
+int main(int argc, char **argv) {
+ Fl_Grid::Cell *c;
+ Fl_Box *b;
+ Fl_Double_Window *win = new Fl_Double_Window(440, 350, "FLTK 1.4.0 - Fl_Grid Alignment Test");
+ Fl_Grid *grid = new Fl_Grid(10, 10, 420, 330);
+ grid->layout(7, 7, 8, 4); // cols, rows, margin, gap
+ grid->box(FL_FLAT_BOX);
+ grid->color(FL_WHITE);
+
+ // add boxes (top and bottom rows)
+
+ for (int col = 0; col < 7; col++) {
+ grid->col_width(col, 50);
+ b = new Fl_Box(0, 0, 20, 20); // variable size
+ if (col == 5) {
+ b->size(4, 20); // reduce width
+ grid->col_width(col, 4); // new min. width
+ grid->col_weight(col, 0); // no hor. resizing
+ }
+ b->box(FL_FLAT_BOX);
+ b->color(FL_BLUE);
+ grid->widget(b, 0, col);
+
+ if (col == 5)
+ b = new Fl_Box(0, 0, 4, 20); // variable size
+ else
+ b = new Fl_Box(0, 0, 20, 20); // variable size
+ b->box(FL_FLAT_BOX);
+ b->color(FL_RED);
+ grid->widget(b, 6, col);
+ }
+
+ // add boxes (left and right columns)
+
+ grid->row_height(0, 40);
+ grid->row_height(6, 40);
+
+ for (int row = 1; row < 6; row++) {
+ grid->row_height(row, 40);
+ b = new Fl_Box(0, 0, 20, 20); // fixed size, see alignment below
+ b->box(FL_FLAT_BOX);
+ b->color(FL_RED);
+ switch(row) {
+ case 1: grid->widget(b, row, 0, FL_GRID_FILL); break;
+ case 2: grid->widget(b, row, 0, FL_ALIGN_CENTER); break;
+ case 3: grid->widget(b, row, 0, FL_ALIGN_BOTTOM_RIGHT); break;
+ case 4: grid->widget(b, row, 0, FL_ALIGN_BOTTOM_RIGHT); break;
+ case 5: grid->widget(b, row, 0, FL_ALIGN_TOP_RIGHT); break;
+ default: break;
+ }
+
+ b = new Fl_Box(0, 0, 20, 20);
+ b->box(FL_FLAT_BOX);
+ b->color(FL_GREEN);
+ c = grid->widget(b, row, 6, FL_ALIGN_CENTER);
+ }
+
+ // two more boxes to demonstrate widget alignment inside the cell
+
+ for (int row = 4; row < 6; row++) {
+ b = new Fl_Box(0, 0, 20, 20); // fixed size, see alignment below
+ b->box(FL_FLAT_BOX);
+ b->color(FL_MAGENTA);
+ c = grid->widget(b, row, 1); // default alignment: FL_GRID_FILL
+ if (row == 4)
+ c->align(FL_ALIGN_BOTTOM_LEFT); // alignment uses widget size
+ if (row == 5)
+ c->align(FL_ALIGN_TOP_LEFT); // alignment uses widget size
+ }
+
+ // one vertical box (line), spanning 5 rows
+
+ b = new Fl_Box(0, 0, 2, 2); // extends vertically
+ b->box(FL_FLAT_BOX);
+ b->color(FL_BLACK);
+ grid->widget(b, 1, 5, 5, 1, FL_GRID_VERTICAL | FL_ALIGN_RIGHT);
+
+ // add a textbox with label or title, spanning 5 cells, centered
+
+ b = new Fl_Box(0, 0, 1, 1); // variable size
+ b->label("Hello, Fl_Grid !");
+ b->labelfont(FL_BOLD + FL_ITALIC);
+ b->labelsize(30);
+ b->labeltype(FL_SHADOW_LABEL);
+ grid->widget(b, 1, 1, 1, 5); // rowspan = 1, colspan = 5
+
+ // add a footer textbox, spanning 3 cells, right aligned
+
+ b = new Fl_Box(0, 0, 1, 10); // variable size
+ b->label("FLTK/test/grid_alignment.cxx");
+ b->labelfont(FL_COURIER);
+ b->labelsize(11);
+ b->align(FL_ALIGN_INSIDE | FL_ALIGN_RIGHT);
+ grid->widget(b, 5, 2, 1, 3, FL_GRID_HORIZONTAL | FL_ALIGN_BOTTOM);
+
+ // input widgets with fixed size and alignment inside the cell
+
+ Fl_Input *i1 = new Fl_Input(0, 0, 100, 30, "Username:");
+ c = grid->widget(i1, 2, 3, 1, 2); // widget, col, row, colspan, rowspan
+ c->align(FL_GRID_HORIZONTAL); // widget alignment in cell
+
+ Fl_Input *i2 = new Fl_Input(0, 0, 100, 30, "Password:");
+ c = grid->widget(i2, 3, 3, 1, 2); // widget, col, row, colspan, rowspan
+ c->align(FL_GRID_HORIZONTAL); // widget alignment in cell
+
+ // the login button spans 2 columns
+
+ Fl_Button *bt = new Fl_Button(0, 0, 10, 30, "Login");
+ grid->widget(bt, 4, 3, 1, 2, FL_GRID_HORIZONTAL); // widget, col, row, colspan, rowspan, alignment
+
+ grid->row_weight(1, 90);
+ grid->row_weight(2, 0);
+ grid->row_weight(3, 0);
+ grid->row_weight(4, 0);
+
+ grid->col_weight(0, 30);
+ grid->col_weight(4, 90);
+
+ grid->row_gap(5, 12);
+
+ grid->end();
+ grid->layout();
+ grid->debug(0);
+ // grid->show_grid(1); // enable to display grid helper lines
+ win->end();
+ win->resizable(grid);
+ win->size_range(440, 350);
+ win->show(argc, argv);
+
+#if (TEST_RELAYOUT)
+ Fl::add_timeout(5.0, relayout_cb, grid);
+#endif
+
+#if (TEST_REMOVE_NOTIFY)
+ Fl::add_timeout(3.0, remove_cb, grid);
+#endif
+
+ // return Fl::run();
+ int ret = Fl::run();
+ grid->clear_layout();
+ delete win;
+ return ret;
+}
diff --git a/test/grid_buttons.cxx b/test/grid_buttons.cxx
new file mode 100644
index 000000000..a00a69a64
--- /dev/null
+++ b/test/grid_buttons.cxx
@@ -0,0 +1,75 @@
+//
+// Fl_Grid demo program for the Fast Light Tool Kit (FLTK).
+//
+// Copyright 2021 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
+//
+
+// Q: How to achieve a spaced out layout?
+// https://groups.google.com/g/fltkgeneral/c/haet7hOQR0g
+
+// A: We use an Fl_Grid with 1 x 7 cells (5 buttons) as requested:
+// [New] [Options] <gap> [About] [Help] <gap> [Quit]
+
+#include <FL/Fl.H>
+#include <FL/Fl_Double_Window.H>
+#include <FL/Fl_Grid.H>
+#include <FL/Fl_Button.H>
+
+int main(int argc, char **argv) {
+
+ Fl_Double_Window *win = new Fl_Double_Window(460, 200, "Fl_Grid Row with 5 Buttons");
+
+ Fl_Grid *grid = new Fl_Grid(0, 0, win->w(), 50);
+ grid->layout(1, 7, 10, 10);
+
+ // create the buttons
+
+ Fl_Button *b0 = new Fl_Button(0, 0, 80, 30, "New");
+ Fl_Button *b1 = new Fl_Button(0, 0, 80, 30, "Options");
+ Fl_Button *b3 = new Fl_Button(0, 0, 80, 30, "About");
+ Fl_Button *b4 = new Fl_Button(0, 0, 80, 30, "Help");
+ Fl_Button *b6 = new Fl_Button(0, 0, 80, 30, "Quit");
+
+ grid->end();
+
+ // assign buttons to grid positions
+
+ grid->widget(b0, 0, 0);
+ grid->widget(b1, 0, 1); grid->col_gap(1, 0);
+ grid->widget(b3, 0, 3);
+ grid->widget(b4, 0, 4); grid->col_gap(4, 0);
+ grid->widget(b6, 0, 6);
+
+ // set column weights for resizing (only empty columns resize)
+
+ int weight[] = { 0, 0, 50, 0, 0, 50, 0 };
+ grid->col_weight(weight, 7);
+
+ grid->end();
+ // grid->show_grid(1); // enable to display grid helper lines
+
+ // add content ...
+
+ Fl_Group *g1 = new Fl_Group(0, 50, win->w(), win->h() - 50);
+ // add more widgets ...
+
+ win->end();
+ win->resizable(g1);
+ win->size_range(win->w(), 100);
+ win->show(argc, argv);
+
+ int ret = Fl::run();
+ delete win; // not necessary but useful to test for memory leaks
+ return ret;
+}
diff --git a/test/grid_login.cxx b/test/grid_login.cxx
new file mode 100644
index 000000000..fb10bb649
--- /dev/null
+++ b/test/grid_login.cxx
@@ -0,0 +1,94 @@
+//
+// Fl_Grid demo program for the Fast Light Tool Kit (FLTK).
+//
+// Copyright 2021 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
+//
+
+#include <FL/Fl.H>
+#include <FL/Fl_Double_Window.H>
+#include <FL/Fl_Grid.H>
+#include <FL/Fl_Button.H>
+#include <FL/Fl_Input.H>
+#include <FL/Fl_Box.H>
+
+int main(int argc, char **argv) {
+
+ Fl_Double_Window *win = new Fl_Double_Window(480, 200, "Fl_Grid \"Login\" Layout");
+
+ // Fl_Grid of 6 x 6 cells, margin 2 and gap 2
+
+ Fl_Grid *grid = new Fl_Grid(5, 5, 470, 190);
+ grid->layout(6, 6, 2, 2); // 6 rows, 6 columns, margin 2, gap 2
+
+ // image (150x200) in left column
+
+ Fl_Box *ibox = new Fl_Box(0, 0, 150, 200, "Image");
+ ibox->box(FL_BORDER_BOX);
+ ibox->color(fl_rgb_color(0, 200, 0));
+ grid->widget(ibox, 1, 1, 4, 1, FL_GRID_CENTER);
+
+ // the title spans 2 columns (3 - 4)
+
+ Fl_Box *title = new Fl_Box(0, 0, 200, 60);
+ title->label("Welcome to Fl_Grid");
+ title->align(FL_ALIGN_CENTER);
+ title->labelfont(FL_BOLD + FL_ITALIC);
+ title->labelsize(16);
+ grid->widget(title, 1, 3, 1, 2, FL_GRID_HORIZONTAL | FL_GRID_CENTER);
+
+ grid->col_width(2, 90); // placeholder for labels
+
+ // input widgets with fixed height and horizontal stretching
+
+ Fl_Input *i1 = new Fl_Input(0, 0, 150, 30, "Username:");
+ grid->widget(i1, 2, 3, 1, 2, FL_GRID_HORIZONTAL);
+ grid->row_gap(2, 10); // gap below username
+
+ Fl_Input *i2 = new Fl_Input(0, 0, 150, 30, "Password:");
+ grid->widget(i2, 3, 3, 1, 2, FL_GRID_HORIZONTAL);
+ grid->row_gap(3, 10); // gap below password
+
+ // register and login buttons
+
+ Fl_Button *btr = new Fl_Button(0, 0, 80, 30, "Register");
+ grid->widget(btr, 4, 3, 1, 1, FL_GRID_HORIZONTAL);
+ grid->col_gap(3, 20); // gap right of the register button
+
+ Fl_Button *btl = new Fl_Button(0, 0, 80, 30, "Login");
+ grid->widget(btl, 4, 4, 1, 1, FL_GRID_HORIZONTAL);
+
+ // set column and row weights for resizing behavior (optional)
+
+ int cw[] = { 20, 0, 0, 10, 10, 20}; // column weights
+ int rw[] = { 10, 0, 0, 0, 0, 10}; // row weights
+ grid->col_weight(cw, 6);
+ grid->row_weight(rw, 6);
+
+ grid->end();
+ grid->layout();
+ // grid->debug(1);
+ // grid->show_grid(1); // enable to display grid helper lines
+ win->end();
+ grid->color(fl_rgb_color(250, 250, 250));
+ win->color(fl_rgb_color(250, 250, 250));
+
+ win->resizable(grid);
+ win->resize(0, 0, 600, 300); // same size as flex_login
+ win->size_range(550, 250);
+ win->show(argc, argv);
+
+ int ret = Fl::run();
+ delete win; // not necessary but useful to test for memory leaks
+ return ret;
+}
diff --git a/test/makedepend b/test/makedepend
index 6d57bc9a2..dfd430bf3 100644
--- a/test/makedepend
+++ b/test/makedepend
@@ -539,6 +539,7 @@ cube.o: ../FL/fl_config.h
cube.o: ../FL/Fl_Device.H
cube.o: ../FL/Fl_Export.H
cube.o: ../FL/Fl_Gl_Window.H
+cube.o: ../FL/Fl_Grid.H
cube.o: ../FL/Fl_Group.H
cube.o: ../FL/Fl_Image.H
cube.o: ../FL/Fl_Light_Button.H
@@ -550,6 +551,7 @@ cube.o: ../FL/Fl_Plugin.H
cube.o: ../FL/Fl_Preferences.H
cube.o: ../FL/Fl_Printer.H
cube.o: ../FL/Fl_Radio_Light_Button.H
+cube.o: ../FL/Fl_Rect.H
cube.o: ../FL/Fl_Slider.H
cube.o: ../FL/Fl_String.H
cube.o: ../FL/Fl_Sys_Menu_Bar.H