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 +#include + +/** 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 + \ 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 + #include + #include + #include + + 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 Binary files /dev/null and b/documentation/src/Fl_Grid.png 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 +#include +#include +#include + +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 +#include + +// 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 #include #include // demo printing - +#include // grid layout #include #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 @@ -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 - -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 *) { - done = 1; -} - -// print screen demo -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 ); - 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 . -// . 350 100 : 350 . -// . |<------------------->| |<-------->| |<------------------->| . -// .................................................................... -// : ....................... ............ ....................... : -// : : : : : : : : -// : : A : : B : : C : : -// : : : : : : : : -// : :.....................: :..........: :.....................: : __ -// :..................................................................: __ MARGIN -// -// | | -// MARGIN -// - -#define MENUBAR_H 25 // menubar height -#define MARGIN 20 // fixed margin around widgets -#define MARGIN2 (MARGIN*2) -#define MARGIN3 (MARGIN*3) - +// 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 *w, void *) { + done = 1; + w->window()->hide(); // necessary if built w/o GL +} + +// print screen demo +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); + printer.end_page(); + printer.end_job(); +} + +// Create a form that allows resizing for A and C (GL windows) with B fixed size/centered: +// +// |<--------------------------------------->|<---------------------->| +// . lt_cube center : rt_cube . +// . 350 100 : 350 . +// . |<------------------->| |<-------->| |<------------------->| . +// .................................................................... +// : ....................... ............ ....................... : __ +// : : : : : : : : +// : : A : : B : : C : : h = 350 +// : : : : : : : : +// : :.....................: :..........: :.....................: : __ +// :..................................................................: __ MARGIN +// +// | | | | | | +// MARGIN GAP GAP + +#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); - 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(); + // 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); - // 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); + // 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 + + // left GL window + lt_cube = new cube_box(0, 0, 350, 350); + + // 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 +#include +#include +#include +#include +#include + +// 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] [About] [Help] [Quit] + +#include +#include +#include +#include + +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 +#include +#include +#include +#include +#include + +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