Introduce Fl_Flex::need_layout() to optimize layout calculation

This is intended to reduce layout calculation and resizing of child
widgets until necessary before the Fl_Flex widget and all its
children are drawn in Fl_Flex::draw().

With this commit users no longer need to call layout() to layout the
Fl_Flex widget and its children properly unless they change widget
sizes or show/hide children.
This commit is contained in:
Albrecht Schlosser 2023-09-04 16:16:40 +02:00
parent d7dc491b5a
commit b2a41e08c3
2 changed files with 126 additions and 45 deletions

View File

@ -2,7 +2,7 @@
// Fl_Flex widget header file 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
@ -113,14 +113,15 @@
*/
class FL_EXPORT Fl_Flex : public Fl_Group {
int margin_left_;
int margin_top_;
int margin_right_;
int margin_bottom_;
int gap_;
int fixed_size_size_;
int fixed_size_alloc_;
Fl_Widget **fixed_size_;
int margin_left_; // left margin
int margin_top_; // top margin
int margin_right_; // right margin
int margin_bottom_; // bottom margin
int gap_; // gap between widgets
int fixed_size_size_; // number of fixed size widgets in array
int fixed_size_alloc_; // allocated size of fixed size array
Fl_Widget **fixed_size_; // array of fixed size widgets
bool need_layout_; // true if layout needs to be calculated
public:
@ -135,7 +136,7 @@ public:
Fl_Flex(int X, int Y, int W, int H, const char *L = 0);
// original Fl_Flex constructors:
// backwards compatible if direction *names* { ROW | COLUMN } are used
// backwards compatible if direction enums { ROW | COLUMN } are used
Fl_Flex(int direction);
Fl_Flex(int w, int h, int direction);
@ -149,7 +150,7 @@ public:
/**
Set the horizontal or vertical size of a child widget.
\param[in] w widget to be affected
\param[in] w widget to be affected
\param[in] size width (Fl_Flex::HORIZONTAL) or height (Fl_Flex::VERTICAL)
\see fixed(Fl_Widget *w, int size)
@ -168,9 +169,37 @@ protected:
virtual int alloc_size(int size) const;
void on_remove(int) FL_OVERRIDE;
void draw() FL_OVERRIDE;
public:
/**
Set or reset the request to calculate the layout of children.
This is intended for internal use but can also be used by user
code to request layout calculation before the widget is drawn.
Call this if you changed attributes or sizes of children to ensure
that the layout is calculated properly. Changing other Fl_Flex
attributes or resizing the widget does this automatically.
\note Never call this with '\c set == 0' because this would defeat its
purpose to recalculate the layout before the widget is drawn.
*/
void need_layout(int set) {
if (set) need_layout_ = true;
else need_layout_ = false;
}
/**
Returns whether layout calculation is required.
This should rarely be needed by user code. Used internally in draw().
*/
bool need_layout() const {
return need_layout_;
}
/** Returns the left margin size of the widget.
This returns the \b left margin of the widget which is not necessarily
@ -233,6 +262,7 @@ public:
margin_left_ = margin_top_ = margin_right_ = margin_bottom_ = m;
if (g >= 0)
gap_ = g;
need_layout(1);
}
/** Set the margin sizes at all four edges of the Fl_Flex widget.
@ -254,6 +284,7 @@ public:
margin_top_ = top < 0 ? 0 : top;
margin_right_ = right < 0 ? 0 : right;
margin_bottom_ = bottom < 0 ? 0 : bottom;
need_layout(1);
}
/** Return the gap size of the widget.
@ -273,6 +304,7 @@ public:
*/
void gap(int g) {
gap_ = g < 0 ? 0 : g;
need_layout(1);
}
/** Returns non-zero (true) if Fl_Flex alignment is horizontal (row mode).
@ -287,19 +319,8 @@ public:
return type() == Fl_Flex::HORIZONTAL ? 1 : 0;
}
/**
Calculates the layout of the widget and redraws it.
If you change widgets in the Fl_Flex container you should call this method
to force recalculation of child widget sizes and positions. This can be
useful (necessary) if you hide(), show(), add() or remove() children.
This method also calls redraw() on the Fl_Flex widget.
*/
void layout() {
resize(x(), y(), w(), h());
redraw();
}
// Calculate the layout of the widget and redraw it.
void layout();
/**
Gets the number of extra pixels of blank space that are added
@ -325,6 +346,7 @@ public:
*/
void spacing(int i) {
gap(i);
need_layout(1);
}
};

View File

@ -2,7 +2,7 @@
// Fl_Flex widget implementation 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
@ -16,7 +16,7 @@
//
#include <FL/Fl_Flex.H>
#include <stdlib.h>
#include <stdlib.h> // malloc, free, ...
/**
Construct a new Fl_Flex widget with the given position, size, and label.
@ -114,14 +114,16 @@ Fl_Flex::Fl_Flex(int x, int y, int w, int h, int direction)
}
void Fl_Flex::init(int t) {
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
fixed_size_ = NULL; // array of fixed size widgets
gap_ = 0; // default gap size
fixed_size_size_ = 0; // number of fixed size widgets
fixed_size_alloc_ = 0; // allocated size of array of fixed size widgets
fixed_size_ = NULL; // array of fixed size widgets
need_layout_ = false; // no need to calculate layout yet
type(HORIZONTAL);
if (t == VERTICAL)
type(VERTICAL);
@ -138,13 +140,62 @@ Fl_Flex::~Fl_Flex() {
*/
void Fl_Flex::on_remove(int index) {
fixed(child(index), 0);
need_layout(1);
}
/**
Draw the widget.
This will finally calculate the layout of the widget and of all its
children if necessary and draw the widget.
Some changes of included children may require a new layout to be
calculated. If this is the case the user may need to call layout()
to make sure everything is calculated properly.
\see layout()
*/
void Fl_Flex::draw() {
if (need_layout()) {
layout();
}
need_layout(0);
Fl_Group::draw();
}
/**
Resize the container and calculate all child positions and sizes.
\param[in] x,y position
\param[in] w,h width and height
*/
void Fl_Flex::resize(int x, int y, int w, int h) {
Fl_Widget::resize(x, y, w, h);
need_layout(1);
int cc = children();
} // resize()
/**
Calculates the layout of the widget and redraws it.
If you change widgets in the Fl_Flex container you should call this method
to force recalculation of child widget sizes and positions. This can be
useful (necessary) if you hide(), show(), add() or remove() children.
Call this method if you need to recalculate widget positions for usage in
an algorithm that places widgets at certain positions or when you need to
display (show) or hide one or more children depending on the current layout
(for instance a side bar).
This method also calls redraw() on the Fl_Flex widget.
*/
void Fl_Flex::layout() {
resize(x(), y(), w(), h());
const int nc = children();
int dx = Fl::box_dx(box());
int dy = Fl::box_dy(box());
@ -152,22 +203,22 @@ void Fl_Flex::resize(int x, int y, int w, int h) {
int dh = Fl::box_dh(box());
// Calculate total space minus gaps
int gaps = cc > 1 ? cc - 1 : 0;
int gaps = nc > 1 ? nc - 1 : 0;
int hori = horizontal();
int space = hori ? (w - dw - margin_left_ - margin_right_)
: (h - dh - margin_top_ - margin_bottom_);
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_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 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
int fw = nc; // number of flexible widgets
// Precalculate remaining space that can be distributed
for (int i = 0; i < cc; i++) {
for (int i = 0; i < nc; i++) {
Fl_Widget *c = child(i);
if (c->visible()) {
if (fixed(c)) {
@ -194,7 +245,7 @@ void Fl_Flex::resize(int x, int y, int w, int h) {
sp++;
}
for (int i = 0; i < cc; i++) {
for (int i = 0; i < nc; i++) {
Fl_Widget *c = child(i);
if (!c->visible())
continue;
@ -218,17 +269,23 @@ void Fl_Flex::resize(int x, int y, int w, int h) {
}
}
} // resize()
need_layout(0); // layout done, no need to do it again when drawing
redraw();
}
/**
Ends automatic child addition and resizes all children.
This calculates the layout depending on all children and whether
they have been assigned fix sizes or not.
This marks the Fl_Flex widget as changed (need_layout(1)) which forces the
widget to calculate its layout depending on all children and whether
they have been assigned fix sizes or not right before it is drawn.
\see layout()
\see draw()
*/
void Fl_Flex::end() {
Fl_Group::end();
resize(x(), y(), w(), h());
need_layout(1);
}
/**
@ -237,7 +294,7 @@ void Fl_Flex::end() {
This sets either the width or height of a child widget, depending on the
type() of the Fl_Flex container (Fl_Flex::HORIZONTAL or Fl_Flex::VERTICAL).
The other dimension is set to the full width or height of the Fl_Flex widget
minus margin sizes.
minus border and margin sizes.
This can be used to set a fixed widget width or height of children
of Fl_Flex so they are not resized dynamically.
@ -266,10 +323,11 @@ void Fl_Flex::fixed(Fl_Widget *child, int size) {
fixed_size_[i] = fixed_size_[i+1];
}
fixed_size_size_--;
need_layout(1);
return;
}
// if w is meant to be flexible, we are done now
// if w is meant to be flexible and we didn't find it, we are done now
if (size == 0)
return;
@ -288,6 +346,7 @@ void Fl_Flex::fixed(Fl_Widget *child, int size) {
child->size(size, h()-margin_top_-margin_bottom_-Fl::box_dh(box()));
else
child->size(w()-margin_left_-margin_right_-Fl::box_dw(box()), size);
need_layout(1);
}
/**