diff --git a/FL/Fl_Flex.H b/FL/Fl_Flex.H index 6d8a76fbd..20b4f50f5 100644 --- a/FL/Fl_Flex.H +++ b/FL/Fl_Flex.H @@ -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); } }; diff --git a/src/Fl_Flex.cxx b/src/Fl_Flex.cxx index 5167bc4d1..81f200b14 100644 --- a/src/Fl_Flex.cxx +++ b/src/Fl_Flex.cxx @@ -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 -#include +#include // 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); } /**