Fl_Flex: support different margin sizes, improve docs

Support different margin sizes on all four edges. Default margin
  and gap size is now 0 (compatible with Fl_Pack).

Doxygen: move the description from the constructor to the class
  declaration which constitutes a "description".

Make some methods virtual and/or 'const'.

Clarify demo programs, make them even more "FLTK style".
This commit is contained in:
Albrecht Schlosser 2022-08-07 15:08:40 +02:00
parent f5b14397dd
commit 713d276b1c
5 changed files with 304 additions and 130 deletions

View File

@ -20,9 +20,104 @@
#include <FL/Fl_Group.H>
/**
Fl_Flex is a container (layout) widget for one row or one column of widgets.
It provides flexible positioning of its children either in one row or in one column.
Fl_Flex is designed to be as simple as possible. You can set individual widget
sizes or let Fl_Flex position and size the widgets to fit in the container.
All "flexible" (i.e. non-fixed size) widgets are assigned the same width or
height, respectively. For details see below.
You can set the margins \b around all children at the inner side the box frame
(if any). Fl_Flex supports setting different margin sizes on top, bottom, left
and right sides.
The default margin size is 0 on all edges of the container.
You can set the gap size \b between all children. The gap size is always the
same between all of its children. This is similar to the 'spacing' of Fl_Pack.
The default gap size is 0.
Fl_Flex can either consist of a single row, i.e. \p type(Fl_Flex::HORIZONTAL)
or a single column, i.e. \p type(Fl_Flex::VERTICAL). The default value is
Fl_Flex::VERTICAL for consistency with Fl_Pack but you can use \p type()
to assign a row (Fl_Flex::HORIZONTAL) layout.
If type() == Fl_Flex::HORIZONTAL widgets are resized horizontally to fit in
the container and their height is the full Fl_Flex height minus border size
and margins. You can set a fixed widget width by using set_size().
If type() == Fl_Flex::VERTICAL widgets are resized vertically to fit in
the container and their width is the full Fl_Flex width minus border size
and margins. You can set a fixed widget height by using set_size().
To create arbitrary spacing you can use invisible boxes of flexible or
fixed sizes (see example below).
Alternate constructors let you specify the layout as Fl_Flex::HORIZONTAL or
Fl_Flex::VERTICAL directly. Fl_Flex::ROW is an alias of Fl_Flex::HORIZONTAL
and Fl_Flex::COLUMN is an alias of Fl_Flex::VERTICAL.
The default box type is FL_NO_BOX as inherited from Fl_Group. You \b may
need to set a box type with a solid background depending on your layout.
\b Important: You should always make sure that the Fl_Flex container cannot
be resized smaller than its designed minimal size. This can usually be done by
setting a size_range() on the window as shown in the example below. Fl_Flex
does not take care of sensible sizes. If it is resized too small the behavior
is undefined, i.e. widgets may overlap and/or shrink to zero size.
\b Hint: In many cases Fl_Flex can be used as a drop-in replacement
for Fl_Pack. This is the recommended single row/column container since
FLTK 1.4.0. Its resizing behavior is much more predictable (as expected)
than that of Fl_Pack which "resizes itself to shrink-wrap itself around
all of the children".
Fl_Flex containers can be nested so you can create flexible layouts with
multiple columns and rows. However, if your UI design is more complex you
may want to use Fl_Grid instead.
At the time of this writing (Aug 7, 2022) Fl_Grid is not yet available
but will be added before FLTK 1.4.0 gets released.
Example:
\image html Fl_Flex_simple.png
\image latex Fl_Flex_simple.png "Fl_Flex" width=6cm
Example code:
\code
#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Flex.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Button.H>
int main(int argc, char **argv) {
Fl_Double_Window window(410, 40, "Simple Fl_Flex Demo");
Fl_Flex flex(5, 5, 400, 30, Fl_Flex::HORIZONTAL);
Fl_Button b1(0, 0, 0, 0, "File");
Fl_Button b2(0, 0, 0, 0, "Save");
Fl_Box bx(0, 0, 0, 0);
Fl_Button b3(0, 0, 0, 0, "Exit");
flex.set_size(bx, 60); // set fix width of invisible box
flex.gap(10);
flex.end();
window.resizable(flex);
window.end();
window.size_range(300, 30);
window.show(argc, argv);
return Fl::run();
}
\endcode
\since 1.4.0
*/
class FL_EXPORT Fl_Flex : public Fl_Group {
int margin_;
int margin_left_;
int margin_top_;
int margin_right_;
int margin_bottom_;
int gap_;
int set_size_size_;
int set_size_alloc_;
@ -52,21 +147,65 @@ public:
virtual void end();
virtual void resize(int x, int y, int w, int h);
/**
Set the horizontal or vertical size of a child widget.
\param[in] w widget to be affected
\param[in] size width (Fl_Flex::HORIZONTAL) or height (Fl_Flex::VERTICAL)
\see set_size(Fl_Widget *w, int size)
*/
void set_size(Fl_Widget &w, int size) {
set_size(&w, size);
}
void set_size(Fl_Widget *w, int size);
int set_size(Fl_Widget *w);
int set_size(Fl_Widget *w) const;
protected:
void init(int t = VERTICAL);
int alloc_size(int size);
virtual int alloc_size(int size) const;
public:
/** Return the margin size of the widget.
\return margin size.
/** Returns the left margin size of the widget.
This returns the \b left margin of the widget which is not necessarily
the same as all other margins.
\note This method is useful if you never set different margin sizes.
\see int margins(int *left, int *top, int *right, int *bottom)
to get all four margin values.
\return size of left margin.
*/
int margin() { return margin_; }
int margin() const { return margin_left_; }
/** Returns all (four) margin sizes of the widget.
All margin sizes are returned in the given arguments. If any argument
is \p NULL the respective value is not returned.
\param[in] left returns left margin if not \p NULL
\param[in] top returns top margin if not \p NULL
\param[in] right returns right margin if not \p NULL
\param[in] bottom returns bottom margin if not \p NULL
\return whether all margins are equal
\retval 1 all margins have the same size
\retval 0 at least one margin has a different size
*/
int margins(int *left, int *top, int *right, int *bottom) const {
if (left) *left = margin_left_;
if (top) *top = margin_top_;
if (right) *right = margin_right_;
if (bottom) *bottom = margin_bottom_;
if (margin_left_ == margin_top_ && margin_top_ == margin_right_ && margin_right_ == margin_bottom_)
return 1;
return 0;
}
/** Set the margin and optionally the gap size of the widget.
This method can be used to set both the margin and the gap size.
@ -74,27 +213,56 @@ public:
If you don't use the second parameter \p g or supply a negative value
the gap size is not changed.
The margin is some free space inside the widget border \b around all child
widgets. It has the same size at all four edges of the Fl_Flex widget.
The margin is some free space inside the widget border \b around all child widgets.
The gap size \p g is some free space \b between child widgets.
This method sets the margin to the same size at all four edges of the Fl_Flex widget.
The gap size \p g is some free space \b between child widgets. Negative values
(the default if this argument is omitted) do not change the gap value.
\param[in] m margin size, must be \>= 0
\param[in] g gap size, must be \>= 0, or will be ignored (if negative)
\param[in] g gap size, ignored (if negative)
\see gap(int)
*/
void margin(int m, int g = -1) {
margin_ = m < 0 ? 0 : m;
if (m < 0)
m = 0;
margin_left_ = margin_top_ = margin_right_ = margin_bottom_ = m;
if (g >= 0)
gap_ = g;
}
/** Set the margin sizes at all four edges of the Fl_Flex widget.
The margin is the free space inside the widget border \b around all child widgets.
You must use all four parameters of this method to set the four margins in the
order \p left, \p top, \p right, \p bottom. Negative values are set to 0 (zero).
To set all margins to equal sizes, use margin(int m)
This method sets the margin to the same size at all four edges of the widget.
\param[in] left,top,right,bottom margin sizes, must be \>= 0
\see margin(int, int)
*/
void margin(int left, int top, int right, int bottom) {
margin_left_ = left < 0 ? 0 : left;
margin_top_ = top < 0 ? 0 : top;
margin_right_ = right < 0 ? 0 : right;
margin_bottom_ = bottom < 0 ? 0 : bottom;
}
/** Return the gap size of the widget.
\return gap size between all child widgets.
*/
int gap() { return gap_; }
int gap() const {
return gap_;
}
/**
Set the gap size of the widget.
@ -134,6 +302,32 @@ public:
redraw();
}
/**
Gets the number of extra pixels of blank space that are added
between the children.
This method is the same as 'int gap()' and is defined to enable
using Fl_Flex as a drop-in replacement of Fl_Pack.
\see int gap()
*/
int spacing() const {
return gap_;
}
/**
Sets the number of extra pixels of blank space that are added
between the children.
This method is the same as 'gap(int)' and is defined to enable
using Fl_Flex as a drop-in replacement of Fl_Pack.
\see void gap(int)
*/
void spacing(int i) {
gap(i);
}
#if (1)
// Additional methods for backwards compatibility with "original" Fl_Flex widget
@ -141,14 +335,18 @@ public:
/**
Deprecated.
\deprecated Please use set_size(Fl_Widget *) instead.
\see int set_size(Fl_Widget *)
*/
bool isSetSize(Fl_Widget *w) {
bool isSetSize(Fl_Widget *w) const {
return (bool)set_size(w);
}
/**
Set the horizontal or vertical size of a child widget.
\deprecated Please use set_size(Fl_Widget *, int) instead.
\see set_size(Fl_Widget *, int)
*/
void setSize(Fl_Widget *w, int size) {
set_size(w, size);

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -21,42 +21,13 @@
/**
Construct a new Fl_Flex widget with the given position, size, and label.
Fl_Flex is a container (layout) widget that provides flexible positioning
of its children either in one row or in one column. Fl_Flex containers can
be nested so you can create flexible layouts with multiple columns and rows.
Fl_Flex is designed to be as simple as possible. You can set particular
widget sizes or let Fl_Flex position and size the widgets to fit the container.
You can set the margin \b around all children at the inner side the box frame
(if any). This margin has the same size on top, bottom, and both sides.
The default margin size is 3.
You can set the gap size \b between all children. The gap size is the same between
all children. This is similar to the 'spacing' of Fl_Pack.
The default gap size is 3.
Fl_Flex can either consist of a single row, i.e. type() = Fl_Flex::HORIZONTAL,
or a single column, i.e. type() = Fl_Flex::VERTICAL. The default value is
\p VERTICAL for consistency with Fl_Pack but you can use \p type() to assign
a row (\p HORIZONTAL) layout.
If type() == Fl_Flex::HORIZONTAL widgets are resized horizontally to fit in
the container and their height is the full Fl_Flex height minus border size
and margin. You can set a fixed widget width by using set_size().
If type() == Fl_Flex::VERTICAL the above description applies equivalently
to the row layout, i.e. widget heights can be set etc.
You can set \p type(Fl_Flex::HORIZONTAL) or \p type(Fl_Flex::VERTICAL).
The default is \p type(Fl_Flex::VERTICAL).
Alternate constructors let you specify the layout as Fl_Flex::HORIZONTAL or
Fl_Flex::VERTICAL directly. Fl_Flex::ROW is an alias of Fl_Flex::HORIZONTAL
and Fl_Flex::COLUMN is an alias of Fl_Flex::VERTICAL.
\note Please use the names (enum) rather than numerical values for
the type() argument and the \p direction parameter of the alternate
constructors. These constructors are backwards compatible with older
versions of Fl_Flex.
\param[in] X,Y position
\param[in] W,H size (width and height)
\param[in] L label (optional)
@ -74,7 +45,7 @@ Fl_Flex::Fl_Flex(int X, int Y, int W, int H, const char *L)
// special Fl_Flex constructors w/o label (backwards compatible with original Fl_Flex widget)
/**
Construct a new Fl_Flex object specifying its layout.
Construct a new Fl_Flex widget specifying its layout.
Use Fl_Flex::HORIZONTAL (aka Fl_Flex::ROW) or Fl_Flex::VERTICAL
(aka Fl_Flex::COLUMN) as the \p direction argument.
@ -95,7 +66,7 @@ Fl_Flex::Fl_Flex(int direction)
}
/**
Construct a new Fl_Flex object specifying its layout and size.
Construct a new Fl_Flex widget specifying its layout and size.
Use Fl_Flex::HORIZONTAL (aka Fl_Flex::ROW) or Fl_Flex::VERTICAL
(aka Fl_Flex::COLUMN) as the \p direction argument.
@ -118,16 +89,15 @@ Fl_Flex::Fl_Flex(int w, int h, int direction)
init(direction);
}
/**
Construct a new Fl_Flex object specifying its layout, position, and size.
Construct a new Fl_Flex widget specifying its layout, position, and size.
Use Fl_Flex::HORIZONTAL (aka Fl_Flex::ROW) or Fl_Flex::VERTICAL
(aka Fl_Flex::COLUMN) as the \p direction argument.
This constructor sets the position and size of the widget which is suitable
for outer Fl_Flex widgets but does not set a widget label.
Use Fl_Widget::label() to set one if required.
for top level Fl_Flex widgets but does not set a widget label.
Use Fl_Widget::label() to set one if desired.
\param[in] x,y widget position
\param[in] w,h widget size
@ -145,11 +115,14 @@ Fl_Flex::Fl_Flex(int x, int y, int w, int h, int direction)
}
void Fl_Flex::init(int t) {
gap_ = 3; // default gap size
margin_ = 3; // default margin size
set_size_ = 0; // array of fixed size widgets
set_size_size_ = 0; // number of fixed size widgets
set_size_alloc_ = 0; // allocated size of array of fixed size widgets
gap_ = 0; // default gap size
margin_left_ = 0; // default margin size
margin_top_ = 0; // default margin size
margin_right_ = 0; // default margin size
margin_bottom_ = 0; // default margin size
set_size_ = NULL; // array of fixed size widgets
set_size_size_ = 0; // number of fixed size widgets
set_size_alloc_ = 0; // allocated size of array of fixed size widgets
type(HORIZONTAL);
if (t == VERTICAL)
type(VERTICAL);
@ -174,13 +147,14 @@ void Fl_Flex::resize(int x, int y, int w, int h) {
// Calculate total space minus gaps
int gaps = cc > 1 ? cc - 1 : 0;
int hori = horizontal();
int space = (hori ? w - dw : h - dh) - 2 * margin_;
int space = hori ? (w - dw - margin_left_ - margin_right_)
: (h - dh - margin_top_ - margin_bottom_);
// set x and y (start) position, calculate widget sizes
int xp = x + dx + margin_;
int yp = y + dy + margin_;
int hh = h - dh - margin_ * 2; // if horizontal: constant height of widgets
int vw = w - dw - margin_ * 2; // if vertical: constant width of widgets
int xp = x + dx + margin_left_;
int yp = y + dy + margin_top_;
int hh = h - dh - margin_top_ - margin_bottom_; // if horizontal: constant height of widgets
int vw = w - dw - margin_left_ - margin_right_; // if vertical: constant width of widgets
int fw = cc; // number of flexible widgets
@ -307,7 +281,7 @@ void Fl_Flex::set_size(Fl_Widget *w, int size) {
\retval 1 the widget has a fixed size
\retval 0 the widget resizes dynamically
*/
int Fl_Flex::set_size(Fl_Widget *w) {
int Fl_Flex::set_size(Fl_Widget *w) const {
for (int i = 0; i < set_size_size_; i++) {
if (w == set_size_[i]) {
return 1;
@ -330,6 +304,6 @@ int Fl_Flex::set_size(Fl_Widget *w) {
\param[in] size current size
\return int new size (to be allocated)
*/
int Fl_Flex::alloc_size(int size) {
int Fl_Flex::alloc_size(int size) const {
return size + 8;
}

View File

@ -36,7 +36,7 @@ void debug_group(Fl_Group *g) {
#endif
} // debug_group
Fl_Button *createButton(const char *caption) {
Fl_Button *create_button(const char *caption) {
Fl_Button *rtn = new Fl_Button(0, 0, 120, 30, caption);
rtn->color(fl_rgb_color(225, 225, 225));
return rtn;
@ -74,21 +74,21 @@ void toggle_cb(Fl_Widget *w, void *v) {
debug_group(flex);
}
Fl_Flex *createRow() {
Fl_Flex *create_row() {
Fl_Flex *row = new Fl_Flex(Fl_Flex::ROW);
{
Fl_Button *toggle = createButton("hide OK button");
Fl_Button *toggle = create_button("hide OK button");
toggle->tooltip("hide() or show() OK button");
Fl_Box *box2 = new Fl_Box(0, 0, 120, 10, "Box2");
Fl_Button * okay = createButton("OK");
Fl_Button * okay = create_button("OK");
new Fl_Input(0, 0, 120, 10, "");
toggle->callback(toggle_cb, okay);
Fl_Flex *col2 = new Fl_Flex(Fl_Flex::COLUMN);
{
createButton("Top2");
createButton("Bottom2");
create_button("Top2");
create_button("Bottom2");
col2->end();
col2->margin(0, 5);
col2->box(FL_FLAT_BOX);
@ -110,59 +110,53 @@ Fl_Flex *createRow() {
int main(int argc, char **argv) {
Fl_Window *window = new Fl_Double_Window(100, 100, "Simple GUI Example");
{
Fl_Flex *col = new Fl_Flex(5, 5, 90, 90, Fl_Flex::COLUMN);
{
Fl_Flex *row = new Fl_Flex(Fl_Flex::ROW);
row->color(FL_YELLOW);
row->box(FL_FLAT_BOX);
{
createButton("Cancel");
new Fl_Box(0, 0, 120, 10, "Box1");
createButton("OK");
new Fl_Input(0, 0, 120, 10, "");
Fl_Flex *col = new Fl_Flex(5, 5, 90, 90, Fl_Flex::COLUMN);
Fl_Flex *row1 = new Fl_Flex(Fl_Flex::ROW);
row1->color(FL_YELLOW);
row1->box(FL_FLAT_BOX);
create_button("Cancel");
new Fl_Box(0, 0, 120, 10, "Box1");
create_button("OK");
new Fl_Input(0, 0, 120, 10, "");
Fl_Flex *col1 = new Fl_Flex(Fl_Flex::COLUMN);
{
createButton("Top1");
createButton("Bottom1");
col1->end();
col1->box(FL_FLAT_BOX);
col1->color(fl_rgb_color(255, 128, 128));
col1->margin(5, 5);
}
Fl_Flex *col1 = new Fl_Flex(Fl_Flex::COLUMN);
create_button("Top1");
create_button("Bottom1");
col1->box(FL_FLAT_BOX);
col1->color(fl_rgb_color(255, 128, 128));
col1->margin(5, 5);
col1->end();
row1->end();
row->end();
}
col->set_size(createRow(), 90);
createButton("Something1");
row = new Fl_Flex(Fl_Flex::ROW);
{
Fl_Button *cancel = createButton("Cancel");
Fl_Button *ok = createButton("OK");
new Fl_Input(0, 0, 120, 10, "");
col->set_size(create_row(), 90); // sets height of created (anonymous) row #2
row->set_size(cancel, 100);
row->set_size(ok, 100);
row->end();
}
createButton("Something2");
create_button("Something1"); // "row" #3
col->set_size(row, 30);
col->margin(0, 6);
col->end();
}
window->resizable(col);
window->color(fl_rgb_color(160, 180, 240));
window->box(FL_FLAT_BOX);
window->end();
}
Fl_Flex *row4 = new Fl_Flex(Fl_Flex::ROW);
Fl_Button *cancel = create_button("Cancel");
Fl_Button *ok = create_button("OK");
new Fl_Input(0, 0, 120, 10, "");
row4->set_size(cancel, 100);
row4->set_size(ok, 100);
row4->end();
create_button("Something2"); // "row" #5
col->set_size(row4, 30);
col->margin(6, 10, 6, 10);
col->gap(6);
col->end();
window->resizable(col);
window->color(fl_rgb_color(160, 180, 240));
window->box(FL_FLAT_BOX);
window->end();
window->size_range(550, 330);
window->resize(0, 0, 640, 480);
window->show(argc, argv);
int ret = Fl::run();
delete window;
delete window; // not necessary but useful to test for memory leaks
return ret;
}

View File

@ -22,13 +22,15 @@
#include <FL/Fl_Button.H>
#include <FL/Fl_Input.H>
Fl_Button *createButton(const char *caption) {
Fl_Button *create_button(const char *caption) {
Fl_Button *rtn = new Fl_Button(0, 0, 100, 25, caption);
rtn->color(fl_rgb_color(225, 225, 225));
return rtn;
}
void buttonsPanel(Fl_Flex *parent) {
// create widgets inside a column, i.e. parent is type(COLUMN)
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");
@ -57,8 +59,8 @@ void buttonsPanel(Fl_Flex *parent) {
Fl_Flex *brow = new Fl_Flex(Fl_Flex::ROW);
{
new Fl_Box(0, 0, 0, 0, "");
Fl_Button *reg = createButton("Register");
Fl_Button *login = createButton("Login");
Fl_Button *reg = create_button("Register");
Fl_Button *login = create_button("Login");
brow->set_size(reg, 80);
brow->set_size(login, 80);
@ -77,7 +79,9 @@ void buttonsPanel(Fl_Flex *parent) {
parent->set_size(b, 30);
}
void middlePanel(Fl_Flex *parent) {
// create widgets inside a row, i.e. parent is type(ROW)
void middle_panel(Fl_Flex *parent) {
new Fl_Box(0, 0, 0, 0, "");
Fl_Box *box = new Fl_Box(0, 0, 0, 0, "Image");
@ -86,7 +90,7 @@ void middlePanel(Fl_Flex *parent) {
Fl_Box *spacer = new Fl_Box(0, 0, 0, 0, "");
Fl_Flex *bp = new Fl_Flex(Fl_Flex::COLUMN);
buttonsPanel(bp);
buttons_panel(bp);
bp->end();
new Fl_Box(0, 0, 0, 0, "");
@ -96,14 +100,19 @@ void middlePanel(Fl_Flex *parent) {
parent->set_size(bp, 300);
}
// The main panel consists of three "rows" inside a column, i.e. parent is
// type(COLUMN). The middle panel has a fixed size (200) such that the two
// boxes take the remaining space and middle_panel has all widgets.
void mainPanel(Fl_Flex *parent) {
new Fl_Box(0, 0, 0, 0, "");
new Fl_Box(0, 0, 0, 0, ""); // flexible separator
Fl_Flex *mp = new Fl_Flex(Fl_Flex::ROW);
middlePanel(mp);
middle_panel(mp);
mp->end();
new Fl_Box(0, 0, 0, 0, "");
new Fl_Box(0, 0, 0, 0, ""); // flexible separator
parent->set_size(mp, 200);
}
@ -111,21 +120,20 @@ void mainPanel(Fl_Flex *parent) {
int main(int argc, char **argv) {
Fl_Window *window = new Fl_Double_Window(100, 100, "Simple GUI Example");
{
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();
}
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();
window->resize(0, 0, 640, 480);
window->size_range(550, 250);
window->show(argc, argv);
int ret = Fl::run();
delete window;
delete window; // not necessary but useful to test for memory leaks
return ret;
}