[Fl_Flow] A new layout manager for FLTK

146 views
Skip to first unread message

Karsten Pedersen

unread,
Oct 28, 2021, 8:37:56 PM10/28/21
to fltk.general
Hi all,

I have had a little bit of time to port a layout manager we use for an internal UI system to FLTK. It may seem a little weird to use at first but it allows for extremely quick layout and prototyping. It was originally designed to quickly hack together UIs for ever-changing scientific research tools. However it started to become a preferred layout system for everything because it was quick easy and maintainable.

So please do give it a go if you have time. Or at the very least have a skim through the tutorial on the main project page to see if it may be useful to you.


A random list of features:

- Single .h file (include/FL/Fl_Flow.H, mostly to make it easy to try out)
- Works via instructions rather than absolute coordinates
- Resizes well due to the instruction nature
- Very flexible, it is rare you need nesting to achieve any desired layout
- Supports centering content
- No tools needed
- Well integrated into FLTK. Can mix and match with other layouts

I hope someone finds it useful. Personally I feel it makes GUI development fun again.

Karsten

Greg Ercolano

unread,
Oct 28, 2021, 9:58:13 PM10/28/21
to fltkg...@googlegroups.com

Wow, this looks neat.

Scroll down on the project's main github main page and you're
sure to understand how it works right away.

Nice use of ascii art for positioning hints.. love it.

Didn't look at the code, but if it's a single .H file,
I can guess it's "light" and simple.. Nice!

Assuming the other devs agree (we'll give it a look over),
is it OK to add it to FLTK itself and its LGPL license?

duncan

unread,
Oct 29, 2021, 2:50:44 AM10/29/21
to fltk.general
I have had a little bit of time to port a layout manager we use for an internal UI system to FLTK. It may seem a little weird to use at first but it allows for extremely quick layout and prototyping. It was originally designed to quickly hack together UIs for ever-changing scientific research tools. However it started to become a preferred layout system for everything because it was quick easy and maintainable.

So please do give it a go if you have time. Or at the very least have a skim through the tutorial on the main project page to see if it may be useful to you.


I've looked through the tutorial and it looks like a really useful addition to the FLTK ecosphere,
especially for quick prototying of a new interface.

However, I notice that the code uses exceptions and templates, which might be a problem for
inclusion within the core of FLTK because the CMP currently prohibits their use, but there's
probably nothing to stop Fl_Flow being offered as part of an add-on system. FLTK++ perhaps?

Karsten Pedersen

unread,
Oct 29, 2021, 5:09:32 AM10/29/21
to fltk.general
Hello,

> Assuming the other devs agree (we'll give it a look over),
> is it OK to add it to FLTK itself and its LGPL license?

> However, I notice that the code uses exceptions and templates,

Oh, I didn't expect there to perhaps be interest to add it into FLTK itself. Otherwise i probably would have avoided the templates. Again, I used them to make it as easy as possible for others to drop into a project. But if Fl_Flow is added to FLTK anyway then this avoids that problem entirely.

To be fair, the nice thing about this layout system is that it isn't too complex or large to write. If people do decide that they would like to add it into FLTK itself, I can happily de-templatize it. I do use std::string, std::exception and std::vector but these can also be replaced if necessary. Their use is fairly minor.

I haven't added a license yet. Depending on what people want to do with it I am happy with GPL, BSD, etc.

Karsten

Ian MacArthur

unread,
Oct 29, 2021, 6:28:43 AM10/29/21
to fltk.general
On Friday, 29 October 2021 at 07:50:44 UTC+1 duncan wrote:

However, I notice that the code uses exceptions and templates, which might be a problem for
inclusion within the core of FLTK because the CMP currently prohibits their use, but there's
probably nothing to stop Fl_Flow being offered as part of an add-on system. FLTK++ perhaps?

As far as this aspect of it goes, I am less concerned about template code than about exceptions - template code is basically resolved at compile time, so doesn't have an impact on the runtime.
Exceptions are trickier because they have a runtime aspect to them, and require the linking in of support that fltk generally eschews in favour of being fast and light.

So, from my perspective, an updated CMP would probably permit template code (but perhaps suggest that it's use be minimized for backwards compatibility) but might still deprecate the use of exceptions...

Ian MacArthur

unread,
Oct 29, 2021, 6:43:47 AM10/29/21
to fltk.general
On Friday, 29 October 2021 at 10:09:32 UTC+1 karsten wrote:
Hello,

> Assuming the other devs agree (we'll give it a look over),
> is it OK to add it to FLTK itself and its LGPL license?

> However, I notice that the code uses exceptions and templates,

Oh, I didn't expect there to perhaps be interest to add it into FLTK itself. Otherwise i probably would have avoided the templates. Again, I used them to make it as easy as possible for others to drop into a project. But if Fl_Flow is added to FLTK anyway then this avoids that problem entirely.

As I noted in another post, I (personally, others may disagree!) am less bothered about template code than about the use of exceptions, as fltk has "traditionally" excluded exception handling from the build.

That said, some implementations of the STL make use of exception handling internally and can therefore cause exception support to be pulled into the build anyway, which is a problem I've hit a few times. FWIW, the MS version of STL seems to be OK in this regard, others less so - though I am led to believe (i.e. have not checked recently!) that more recent gcc STL code is now exception-free, as it were... This was certainly not always the case.
 

To be fair, the nice thing about this layout system is that it isn't too complex or large to write. If people do decide that they would like to add it into FLTK itself, I can happily de-templatize it. I do use std::string, std::exception and std::vector but these can also be replaced if necessary. Their use is fairly minor.

It might be more a "de-STL-ize" thing I suspect... other uses of templates that are "local" are probably fine, I'd guess, And "de-exception-ize" too. 
Inventing new words!
 

I haven't added a license yet. Depending on what people want to do with it I am happy with GPL, BSD, etc.

The fltk license is basically LGPL but with a specific exemption to permit static linking (which the LGPL would typically disallow) since static linking is "the norm" for fltk.
If this proves to be the way forward, we'd need you to "formally" say that the code could be released under the fltk license, I think, and then that should be it... IANAL etc...

 

Philip Rose

unread,
Oct 29, 2021, 8:05:03 AM10/29/21
to fltkg...@googlegroups.com

From: Ian MacArthur
Sent: 29 October 2021 11:28
To: fltk.general
Subject: [fltk.general] Re: [Fl_Flow] A new layout manager for FLTK

 

 

As a matter of interest what is the issue with exceptions?

 

Regards Phil.

Karsten Pedersen

unread,
Oct 29, 2021, 8:57:28 AM10/29/21
to fltk.general
Hello,

Removing STL and templates is fine. Actually the internal version of this system was originally written entirely in ANSI C89. I do have a very (1 year) old version which is still public. Perhaps people might find the uncanny similarity interesting (especially the adapted tutorial). The entire C project was meant to remain open-source but it became a bit butchered to work on an entirely different widget system.

[ OLD C VERSION ]
https://osen.github.io/flow.html  <-- C-style tutorial

Had I known there was a potential for inclusion with FLTK I would have brushed up on the coding conventions and stuck to them. But either way, if people have a play and think that they will still be interested, let me know and I can spend a bit of time making it more suitable for integration.

Thanks,

Karsten

Albrecht Schlosser

unread,
Oct 29, 2021, 12:43:20 PM10/29/21
to fltkg...@googlegroups.com
On 10/29/21 2:57 PM Karsten Pedersen wrote:
Removing STL and templates is fine. Actually the internal version of this system was originally written entirely in ANSI C89. I do have a very (1 year) old version which is still public. Perhaps people might find the uncanny similarity interesting (especially the adapted tutorial). The entire C project was meant to remain open-source but it became a bit butchered to work on an entirely different widget system.

[ OLD C VERSION ]
https://osen.github.io/flow.html  <-- C-style tutorial

Thanks, I'll look into this as well.


Had I known there was a potential for inclusion with FLTK I would have brushed up on the coding conventions and stuck to them. But either way, if people have a play and think that they will still be interested, let me know and I can spend a bit of time making it more suitable for integration.

I'd really be interested (and in this case I assume I'm also speaking for the FLTK Team) to integrate more layout capabilities in FLTK as has been requested multiple times and especially recently. Currently we have at least one layout proposal (Fl_Flex) which can be used similar to Fl_Pack in a horizontal (row) and a vertical (column) mode. There are also thoughts about adding a grid widget. Unfortunately Fl_Flex uses std::vector (IIRC, or something similar) which is incompatible with the FLTK coding style and constraints.

I created two GitHub Issues to reflect this situation:

"Add a flexible row/column container widget"
https://github.com/fltk/fltk/issues/255

"Consider to add a simple grid container widget"
https://github.com/fltk/fltk/issues/256

Your proposal would be able to fill another gap (or maybe replace one or both?).

That said, thank you very much for offering your code. I looked into it and I tested it. Here are my results (I apologize for the long message):

(1) All the template code can be removed easily (I did it in a minute). It's useless in the code as it is now. Has there been a reason in another version (maybe in your program)?

(2) I also removed exceptions, but this was more like a quick fix (I replaced throw with printf). I'd appreciate if you could do this in your code base (better than my quick fix).

(3) I believe std::vector and std::string are likely harder to remove, but since you have an old (C89) version it's probably doable. I'm looking forward to seeing a version that is compatible with FLTK.

(4) Coding style: there's not much "wrong" with your code. To simplify a potential conversion I suggest to use `clang-format -i filename` if your code is inside the FLTK source tree or to copy our '.clang-format' file to the root of your code tree and use clang-format as above. This is not "perfect" yet, still work in progress but pretty well usable.

I noticed that the constructors were rewritten by clang-format in a questionable way. You may keep yours but I suggest to use clang-format for everything else, particularly the opening braces and to assert the correct indenting. That's just a suggestion though and if you don't have clang-format installed we can certainly do this before integration into FLTK.

(5) As others said already we need the FLTK license which is LGPL with exceptions. If you agree we can add our license to your files for integration.

(6) There are a lot of additional (maybe internal) Fl_Something classes (structs) in your code. Having everything in one header file makes these structs publicly available. In the final version this could probably be avoided by splitting the code in header + implementation and having the local classes/structs only in the implementation (.cxx). But that's something to consider later, it's not important for now.

(7) I'm not sure about the (correct) usage of Fl_Widget_Tracker though. This class was intended to be used only inside event handling code that called callbacks so we could know if the user (callback) code deleted the widget. The class was designed to be a short-lived local (stack) variable that would automatically be deleted when it went out of scope.

The reason for this ("short-lived") restriction is that widget tracking uses a simple list and thus can be very inefficient if many widgets are watched. I think your code requires something like this (I didn't actually understand everything yet, I'm still looking and testing) but before I try to find out all the fine details myself, can you please explain the exact reasons? Would there be a potential to access deleted widgets if you didn't use it?

(8) A minor issue in my tests (using FLTK 1.4 on Linux, BTW) was that your code seems to need at least one actual resize() to be effective. If you run the (demo) programs as given they don't position the widgets correctly after the first show(). I used code like the following to work around this:

win.size(win.w(), win.h()+1);
win.size(win.w(), win.h()-1);
win.show();

Did you see this as well and what is your workaround?

(9) I also noticed strange effects when the window is resized to a smaller size than required to hold all widgets. Are there any ideas how to deal with this situation? The following small demo shows the effect if you resize the window width to less than the button width or both the width and height to something smaller than necessary. Suddenly all buttons "jump" to the bottom of the window or hide behind other buttons. One obvious "solution" is to limit resizing by setting a size_range().

Example code + screenshot:

#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Flow.H>
int main() {
char buf[20];
Fl_Double_Window win(640, 480);
Fl_Flow flow(0, 0, win.w(), win.h());
for (int i = 0; i < 10; i++) {
Fl_Button *but = new Fl_Button(0, 0, 100, 30);
sprintf(buf, "Button %d", i+1);
but->copy_label(buf);
flow.rule(but, "^<");
}
win.resizable(flow);
win.size(win.w(), win.h()+1); // make sure that the "flow"
win.size(win.w(), win.h()-1); // is resized initially
win.show();
return Fl::run();
}




Summary: the functionality is great and could be a real enhancement for FLTK. If the coding style issues can be resolved I'd like to include it, i.e. my vote would most likely be +1. Thank you very much!

Karsten Pedersen

unread,
Oct 29, 2021, 1:27:37 PM10/29/21
to fltk.general
Hi Albrecht,

I am very glad that people are liking this system. In response to your queries;

(0) Fl_Flex is actually my project too ;)
In the past I have certainly been looking into ways to make GUI management more fun. Fl_Flex was good and works pretty well but it isn't nearly as fun. I do find Fl_Flow much faster.

(1) The template code is purely so that the entire system could be collated into a single header file and used from multiple .cpp files without causing duplicate symbols. The is kind of the C++ alternative to how this is done with C libraries such as stb_image. GLM (C++ maths library) for example does this. As you have noticed, it is very easy to strip out. It isn't depended upon by the library in any real way.

(2) Very happy to. Though do I need to look up at how FLTK deals with operator new() throwing exceptions on OOM situations.

(3) std::string is easy, I barely use it. I could just use standard C-Style strings. I will have a look through FLTK and see what you guys use for containers. If not I can put together some C-style linked list, etc.

(4) Fl_Flow isn't very large. I will have a look through FLTK codebase and mimic what you guys do.

(5) OK, I will push to my repo on GitHub an LGPL LICENSE file. I can perhaps grab one with the exceptions in it from the FLTK project repo.

(6) There is some amount of sharing going on between the structures. I will have a look at putting them private. Perhaps FLTK already has some private headers?

(7) Ah I am using it basically in place of std::weak_ptr<T>. I get a free "observer pattern" and it means I don't get any dangling pointers if Widgets get destroyed.

... however, I do have logic in there that checks if the widget has been removed so technically I don't need it. I just like that warm fuzzy feeling of avoiding (some) memory issues and use-after-free. I can possibly remove it. Or at least conditionally compile it in as debug builds?

(8) Ah. This could well be my window managers (spectrwm and dwm) that cause the initial resize event as it gets tiled. Darn, embarrassingly I didn't think of that. I will fix that today. I basically just need to get it to reposition all children on the initial show() (and add() if already shown).

(9) In some way this works really well and if the window is too small, it will drop down to the next line. You can get really far just by changing the order of the flow instructions.

... unfortunately if the window is simply too small, Widgets will stay at the bottom right because they collide immediately with other widgets or the bounds. One workaround is to start the components really small and then expand later on. Personally I just set the minimum window size to something a little larger and in practice this avoids these issues. But I will have a further think on this.

Many thanks,

Karsten

Bill Spitzak

unread,
Oct 29, 2021, 1:33:59 PM10/29/21
to fltkg...@googlegroups.com
This looks pretty good and does allow a lot more useful layouts that require nesting of other widgets. I do feel it would be useful to add to FLTK.

I don't think the implementation should use any exceptions as it sounds like you are using them for coding errors. Should just ignore any unexpected characters in the command strings. You can also assert and crash if you would prefer people to not put garbage in the strings.

std::string: to fit with FLTK style the commands themselves can be stored as raw char* pointers on the assumption that the source is a string constant. It may also make sense to have the command "parse" the command string at construction and store the result in a small array that is part of the object.

I don't have any problem with using std::vector, it was not used back in the day because it used much more memory and was slower than naive C implementations, but this is not true any more.

One thing I would like to see is support for "outside" labels to produce nice columns. If a whole bunch of widgets with outside-left labels are pushed to the left, they should instead be positioned so the label fits, and the maximum size of all the labels is used for this.

It would be nice to compute a minimum size to fix the layout issue with #9 above.




--
You received this message because you are subscribed to the Google Groups "fltk.general" group.
To unsubscribe from this group and stop receiving emails from it, send an email to fltkgeneral...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/fltkgeneral/60dcdaf6-af88-785b-bbae-1ebcdb71c2eb%40online.de.

Albrecht Schlosser

unread,
Oct 29, 2021, 2:32:23 PM10/29/21
to fltkg...@googlegroups.com
On 10/29/21 7:27 PM Karsten Pedersen wrote:
> Hi Albrecht,
>
> I am very glad that people are liking this system. In response to your
> queries;
>
> (0) Fl_Flex is actually my project too ;)

Why did I not check this? ;-)

My previous tests of Fl_Flex (some time ago) indicated that it was worth
considering for inclusion but it had also some positioning issues and
the usage of std::containers which we can't use. I mentioned it in one
of the issues (see my previous message).

> In the past I have certainly been looking into ways to make GUI
> management more fun. Fl_Flex was good and works pretty well but it
> isn't nearly as fun. I do find Fl_Flow much faster.

Would you recommend not to use Fl_Flex at all in FLTK in favor of
Fl_Flow? Or do they both have their (different) usage patterns?

> (1) The template code is purely so that the entire system could be
> collated into a single header file and used from multiple .cpp files
> without causing duplicate symbols. The is kind of the C++ alternative
> to how this is done with C libraries such as stb_image. GLM (C++ maths
> library) for example does this. As you have noticed, it is very easy
> to strip out. It isn't depended upon by the library in any real way.

Ah yes, thanks for the explanation. My tests were all with only one C++
file.

> (2) Very happy to. Though do I need to look up at how FLTK deals with
> operator new() throwing exceptions on OOM situations.

We don't. ;-)

> (3) std::string is easy, I barely use it. I could just use standard
> C-Style strings. I will have a look through FLTK and see what you guys
> use for containers. If not I can put together some C-style linked
> list, etc.

We don't use any containers yet (officially) although there are some
(inofficial, i.e. not public) containers used in some implementations,
for instance Fl_Table::IntVector. Picking existing container types,
putting them in classes etc., making them kinda "protected" or "public"
is on my todo (ehm, wish) list though. With "protected" I mean in this
context that they could be inside the library code base but not in the
public header files. Public would mean that they'd be in the FL/
directory for public usage.

Bill wrote in his comment "I don't have any problem with using
std::vector, it was not used back in the day because it used much more
memory and was slower than naive C implementations, but this is not true
any more." We *might* reconsider using std::vector in the future which
would simplify some, particularly new code.

OTOH the strings used could likely be simple char* arrays as Bill
mentioned as well.

> (4) Fl_Flow isn't very large. I will have a look through FLTK codebase
> and mimic what you guys do.

Caution, there's some code that doesn't comply with the "rules" which
are in https://www.fltk.org/cmp.php and particularly the "Coding
Standards" section: https://www.fltk.org/cmp.php#CODING_STANDARDS .
However, some parts are ancient and maybe outdated as well.

> (5) OK, I will push to my repo on GitHub an LGPL LICENSE file. I can
> perhaps grab one with the exceptions in it from the FLTK project repo.

Great.

> (6) There is some amount of sharing going on between the structures. I
> will have a look at putting them private. Perhaps FLTK already has
> some private headers?

If we want to use "private" headers we put them into the 'src'
directory. These headers will not be installed. The problem is that they
must not be used in the public header FL/Fl_Flow.H unless they can be
simple forward references like 'class Fl_xyz;'.

> (7) Ah I am using it basically in place of std::weak_ptr<T>. I get a
> free "observer pattern" and it means I don't get any dangling pointers
> if Widgets get destroyed.
>
> ... however, I do have logic in there that checks if the widget has
> been removed so technically I don't need it. I just like that warm
> fuzzy feeling of avoiding (some) memory issues and use-after-free. I
> can possibly remove it. Or at least conditionally compile it in as
> debug builds?

As I said already I'm not sure that you used Fl_Widget_Tracker correctly
in all cases and the second point is its inefficiency. After reading
more of your code I'm coming to the conclusion that it's very likely not
necessary to use it at all. If an Fl_Widget is deleted it removes itself
from its parent group. Your prepare() method checks if a widget is still
a member of the group and adds "missing" widgets to the internal states.
This can all be done by examining the children() array of Fl_Group, and
since Fl_Flow is derived from Fl_Group you get this for free. If you
have questions I can assist.

> (8) Ah. This could well be my window managers (spectrwm and dwm) that
> cause the initial resize event as it gets tiled. Darn, embarrassingly
> I didn't think of that. I will fix that today. I basically just need
> to get it to reposition all children on the initial show() (and add()
> if already shown).

Great if you can do that. I just didn't want to dive too deep into the code.

> (9) In some way this works really well and if the window is too small,
> it will drop down to the next line. You can get really far just by
> changing the order of the flow instructions.

Yes, I saw that it works pretty well. Until ...

> ... unfortunately if the window is simply too small, Widgets will stay
> at the bottom right because they collide immediately with other
> widgets or the bounds. One workaround is to start the components
> really small and then expand later on. Personally I just set the
> minimum window size to something a little larger and in practice this
> avoids these issues. But I will have a further think on this.

I'm not sure what the Fl_Flow implementation could do about this. Maybe
it could let widgets overlap as a better visual indication that
something is wrong which could help the author testing (and the user
would "know" they need to increase the window size). The minimum window
size is something you need to set for all GUI's anyway, thus I don't
think this is a big issue. We can simply document it. If the window is
too small, it's too small. ;-)

> Many thanks,
>
> Karsten

Thanks for your contribution!

PS: Can you please avoid top-posting in this group? This makes reading
the threads easier with their context (keep the context as small as
possible by trimming unnecessary parts). Thanks.
https://en.wikipedia.org/wiki/Posting_style#Top-posting

Albrecht Schlosser

unread,
Oct 29, 2021, 2:38:19 PM10/29/21
to fltkg...@googlegroups.com
On 10/29/21 7:33 PM Bill Spitzak wrote:
> I don't have any problem with using std::vector, it was not used back
> in the day because it used much more memory and was slower than naive
> C implementations, but this is not true any more.

Bill, would you recommend to use std::vector inside the FLTK library? I
mean: inside, not in the public API.

Doing so could simplify code development for some new features where we
often need to maintain dynamic arrays. I know we discussed this several
times and there were some concerns (doubts) regarding compiler
compatibility and such.

As Ian mentioned in this thread, some implementations of containers
might pull in exceptions which we don't want. Do you have any insights
on this?

Bill Spitzak

unread,
Oct 29, 2021, 3:02:04 PM10/29/21
to fltkg...@googlegroups.com
I don't think there is a problem with modern systems with using std::vector (and also std::unique_ptr which will help with making arrays of pointers).

Schemes to hide them from the header files I think add unnecessary overhead, if an object uses a std::vector it will have to be declared in the header file.

Certainly the implementations are using exceptions as a way to encode "unlikely" branches of if statements. I think this is much more common with shared_ptr and with multithreading stuff, not sure how helpful it is for std::vector. Just because so many system libraries use them I'm not sure if disabling exceptions works now or does anything to the compile. IMHO we should be supporting exceptions, in particular a callback should be able to throw one and it causes the Fl::run() or similar function to throw it, rather than crashing. This was done in fltk2.0, not thoroughly tested but a button that threw an exception worked.


--
You received this message because you are subscribed to the Google Groups "fltk.general" group.
To unsubscribe from this group and stop receiving emails from it, send an email to fltkgeneral...@googlegroups.com.

Greg Ercolano

unread,
Oct 29, 2021, 3:27:19 PM10/29/21
to fltkg...@googlegroups.com


On 10/29/21 11:32 AM, Albrecht Schlosser wrote:
On 10/29/21 7:27 PM Karsten Pedersen wrote:

(2) Very happy to. Though do I need to look up at how FLTK deals with operator new() throwing exceptions on OOM situations.

We don't. ;-)

    Right -- it's up to the app to trap such exceptions if interested in them, but FLTK
    does not throw or catch exceptions itself.

    For handling new in my daemon application, I used a hook (I forget the details)
    to hook into both new and malloc() so that out of memory conditions basically
    retry with a sleep until it succeeds, preventing a crash due to the machine being
    overloaded by, presumably, some other application that will eventually be killed
    by the OOM killer or a user.

    I don't ever try to handle out of memory conditions in the app; the best thing IMHO
    is to wait a little and try again so that a memory allocation never fails.


(3) std::string is easy, I barely use it. I could just use standard C-Style strings. I will have a look through FLTK and see what you guys use for containers. If not I can put together some C-style linked list, etc.

We don't use any containers yet (officially) although there are some (inofficial, i.e. not public) containers used in some implementations, for instance Fl_Table::IntVector. Picking existing container types, putting them in classes etc., making them kinda "protected" or "public" is on my todo (ehm, wish) list though. With "protected" I mean in this context that they could be inside the library code base but not in the public header files. Public would mean that they'd be in the FL/ directory for public usage.

Bill wrote in his comment "I don't have any problem with using std::vector, it was not used back in the day because it used much more memory and was slower than naive C implementations, but this is not true any more." We *might* reconsider using std::vector in the future which would simplify some, particularly new code.

OTOH the strings used could likely be simple char* arrays as Bill mentioned as well.

    Our main concern with the std:: stuff is old compilers that support C++ but not templates.
    These days that falls into embedded hardware with limited abilities, and/or really old
    operating systems, I think someone was able to get FLTK to run under DOS even (!),
    using some kind of custom graphics that talks directly to hardware without a window manager.

    "Back in the day" in the 90's that Bill was talking about, STL was a separate package
    and it didn't even work with most of the SGI compilers we were using back then, as
    they didn't even support templates at all until much later. As far as I can recall, my
    most recent SGI native compiler config from the 2000's did not support templates ("CC"),
    but I think if you used gnu c++ you could use STL.

    IIRC, STL was eventually brought into one of the C++ standards and now the compilers
    "come with STL" as part of the standard. While I am not at all a fan of STL's API, I use it
    in new applications.. but I'm glad FLTK doesn't use it. Errors from the template library
    are horribly ugly, and the API makes me barf. Vectors should have been called arrays,
    'push_back()' instead of 'append()', and 'back()' instead of 'last()', and lots of simple
    use cases (like deleting an element) are horribly byzantine.. oof, there I go again on a rant.



(5) OK, I will push to my repo on GitHub an LGPL LICENSE file. I can perhaps grab one with the exceptions in it from the FLTK project repo.

Great.

    Yes, I did that with all my donations to FLTK as well Fl_Input_Choice, Fl_Table, Fl_Tree;
    FLTK's license is the standard LGPL 2 with a static compile exception, and some recommendations
    for handling credits that are I think optional IIRC.

Karsten Pedersen

unread,
Oct 29, 2021, 3:55:43 PM10/29/21
to fltk.general
Firstly,


> PS: Can you please avoid top-posting in this group?

Ah geez, my apologies. I didn't notice that the Google Groups web
form was hiding all that content inside a tiny collapsed ellipsis and
sending it all after my message. I will remember that in future!


> Would you recommend not to use Fl_Flex at all in FLTK in favor of
> Fl_Flow? Or do they both have their (different) usage patterns?

I suppose they are quite different and some guys might prefer Fl_Flex
because it is more similar to what they already use coming from
Gtk, wxWidgets, Java, etc.


> Great if you can do that. I just didn't want to dive too deep
> into the code.

OK, I have made a change that should fix this. Interestingly I
couldn't recreate it in MWM and TWM. However I could in CWM. I
believe the fix pushed to my repo is OK. I didn't expect resize()
to not be called before draw but as mentioned in a few of the
Fl_Group comments, many window managers do overly send resize events.

> it could let widgets overlap as a better visual indication that  
> something is wrong which could help the author testing (and the user
> would "know" they need to increase the window size).

Yes, that isn't a bad idea to do. We could potentially even hide
them if they don't fit. That way as in your example you will simply
see the excess buttons disappear if they don't fit rather than
giving any clue that things are a bit squashed. Perhaps I could
even recalculate the entire layout again but with every initial
widget size halved.


> We don't use any containers yet (officially) although there are some
> (inofficial, i.e. not public) containers used in some implementations

As an aside, is there any interest in a custom lighter STL containing
only the stuff FLTK needs? I have a project here:


and a more robust one in my private CVS repo. This provides
containers, smart pointers, etc but with some additional safety. For
example it locks on -> and [] access avoiding almost any chance of
getting a dangling pointer, dangling reference or dangling "this".

Thanks,

Karsten

Karsten Pedersen

unread,
Oct 29, 2021, 4:05:43 PM10/29/21
to fltk.general
Hi,

One thing I would like to see is support for "outside" labels to produce nice columns. If a whole bunch of widgets with outside-left labels are pushed to the left, they should instead be positioned so the label fits, and the maximum size of all the labels is used for this.

Ah, good point, I didn't think of this one. I will see if I can include these outside labels as part of the widget bounds when I process / transform them.

It would be nice to compute a minimum size to fix the layout issue with #9 above.

As far as I can tell, FLTK widgets don't have a minimum size? I.e Fl_Buttons, etc. Potentially if they all had a default constructor that set this size would make it even more convenient for Fl_Flow because it wouldn't need to be set in i.e an initializer list.

Though in general, whatever size they have been given before they get added to Fl_Flow should be their minimum size since there is no "flow" instruction to shrink, only grow. Unfortunately as you may have seen they don't really do much more with that information other than just get stuck at the bottom corner with their minimum size :/

Thanks,

Karsten
 

Ian MacArthur

unread,
Oct 29, 2021, 4:48:16 PM10/29/21
to Fltk General
On 29 Oct 2021, at 13:05, 'Philip Rose' wrote:
>
> As a matter of interest what is the issue with exceptions?


Hi Phil,

I’ve moved this into another thread, as I guess it’s OT for the Fl_Flow discussion of the most part.

Anyway, the answer is “it depends...” to some extent.
The crux is that (and this depends somewhat on the system on which you are running, compiler, etc.) enabling exception support will *generally* increase the memory footprint as “every function” (not actually every function in practice) gets some additional structure to handle the exception events - in some cases there may be additional exception stacks allocated and so forth. The “cost” may be small, but it can mount up. There can also be some runtime cost as the function call/return may have additional steps to handle the exception state and so on.
On a modern desktop system, this probably doesn't much matter, but it can make a difference in constrained systems.
In the early days of fltk, it was felt it wasn’t worth the cost - that was before my time, but I would generally concur with that decision.



That said, I may not be the best judge since a lot of my work is in embedded systems, so I’m not keen on C++ exceptions anyway; in an embedded system, there’s nowhere to go, in effect, so you need to handle errors at source, as far as possible - shrugging ‘em off to deal with later won’t fly! (Literally in many cases - you try to persuade a DO-178 assessor that exceptions represent valid control flow and see how far that gets you...)


Ian MacArthur

unread,
Oct 29, 2021, 5:03:32 PM10/29/21
to Fltk General
On 29 Oct 2021, at 18:27, Karsten Pedersen wrote:
>
> (2) Very happy to. Though do I need to look up at how FLTK deals with operator new() throwing exceptions on OOM situations.

Others are coming in with actually useful feedback, but I’ll just pick this up: “operator new() throwing exceptions on OOM situations”

Question: Have you ever seen that happen?

I have, but never on a modern desktop system. The memory allocators in all the “main” OS these days are, by design, *very* optimistic in their allocation strategy and pretty much never fail to allocate memory for a “small” request, and they depend on the MMU to sort that out when the allocation is actually needed and referenced. The consequence of that is that calls to new or malloc or etc. basically never fail.
A nasty side effect of that if you do seriously go OOM and the allocation can not be honoured by the MMU at point of use, you can get tricky (and hard to debug) faults “elsewhere” in the program at runtime.

A few years back, a colleague hit basically this issue, and we all stared at it for days before we figured out what was going on. Oddly, “the same” code running on vxWorks-5.something-ancient did OOM at the point the new was made, because it’s allocator was not optimistic and was mapping actual RAM at the point it was called... If we’d tried that first we’d have spotted it sooner!







Albrecht Schlosser

unread,
Oct 29, 2021, 5:36:14 PM10/29/21
to fltkg...@googlegroups.com
On 10/29/21 10:05 PM Karsten Pedersen wrote:

As far as I can tell, FLTK widgets don't have a minimum size? I.e Fl_Buttons, etc. Potentially if they all had a default constructor that set this size would make it even more convenient for Fl_Flow because it wouldn't need to be set in i.e an initializer list.

I think what Bill meant was the *maximum* (not minimum) size of all outside labels (and I may add: in the same "column"). That would mean that you reserve space for all labels at the left side of the Fl_Flow widget to place the outside labels and align the widgets right of the labels in one "column".

Honestly, I can't imagine how this could be done while taking into account that widgets "flow" from one row to the next. If you know what I mean. I wouldn't spend much time on this "alignment issue", however leaving enough space for outside labels would be useful.

Karsten Pedersen

unread,
Oct 29, 2021, 5:45:17 PM10/29/21
to fltk.general
On Friday, October 29, 2021 at 9:03:32 PM UTC Ian MacArthur wrote:
Question: Have you ever seen that happen?

On OpenBSD I do see it every so often because they have a particularly spartan ulimit. However what sets it off is usually fringe software such as debug allocators, VMs and very large things like web browsers. In my day to day software, if I was to hit OOM, it usually means I am doing something wrong admittedly.

In C, I either sleep until memory gets released elsewhere (admittedly this doesn't help the limits set by ulimit) or I simply abort().

However, my slight concern is that operator new just fires out an exception and the stack unwinds as it propagates. I would personally prefer an abort().
 
can not be honoured by the MMU at point of use, you can get tricky (and hard to debug) faults “elsewhere” in the program at runtime.

This is true. Non-deterministic things like this make me shudder ;)

Karsten Pedersen

unread,
Oct 29, 2021, 5:55:46 PM10/29/21
to fltk.general
On Friday, October 29, 2021 at 9:36:14 PM UTC Albrecht Schlosser wrote:
I think what Bill meant was the *maximum* (not minimum) size of all outside labels (and I may add: in the same "column"). That would mean that you reserve space for all labels at the left side of the Fl_Flow widget to place the outside labels and align the widgets right of the labels in one "column".

Yep, I understand this one. A specific example is the Fl_Input widget with a label. I will see if I can get this to work. If I can calculate a bounds of the widget and label, I don't see this as an issue.

I was referring to this line earlier from Bill:
> It would be nice to compute a minimum size to fix the layout issue with #9 above.
 
I think perhaps this was to address your (9), widgets sticking to bottom right if the window is too small. Perhaps if we could calculate the minimum size we could make the Fl_Flow widget from its contained widgets, we could do something with that. Perhaps we could even compute the minimum window size from that.

Obviously one complexity arises because depending on one dimension the window may not collide in the other dimension. You could end up with a smaller window, but one that is shaped in a way that no widgets collide.

Albrecht Schlosser

unread,
Oct 29, 2021, 5:58:13 PM10/29/21
to fltkg...@googlegroups.com
On 10/29/21 9:55 PM Karsten Pedersen wrote:
>
> > Would you recommend not to use Fl_Flex at all in FLTK in favor of
> > Fl_Flow? Or do they both have their (different) usage patterns?
>
> I suppose they are quite different and some guys might prefer Fl_Flex
> because it is more similar to what they already use coming from
> Gtk, wxWidgets, Java, etc.

OK, then we should consider adding both.

[ ... initial resize ... ]
> > Great if you can do that. I just didn't want to dive too deep
> > into the code.
>
> OK, I have made a change that should fix this. Interestingly I
> couldn't recreate it in MWM and TWM. However I could in CWM. I
> believe the fix pushed to my repo is OK.

Thanks, the fix worked for me.

> > it could let widgets overlap as a better visual indication that
> > something is wrong which could help the author testing (and the user
> > would "know" they need to increase the window size).
>
> Yes, that isn't a bad idea to do. We could potentially even hide
> them if they don't fit. That way as in your example you will simply
> see the excess buttons disappear if they don't fit rather than
> giving any clue that things are a bit squashed. Perhaps I could
> even recalculate the entire layout again but with every initial
> widget size halved.

Don't try too much, this effort would only make it more complicated for
no real effect. My intention was only to let the developer and end user
know when something goes awry. Often you don't know that there *should*
be another widget which has been hidden behind the other widgets.
Therefore I would suggest not to keep them all at the bottom but present
them somehow overlapping such that you can see that there is something
behind the other widgets. But, as I said before, that's not very
important. We should concentrate on the real important things first.

> > We don't use any containers yet (officially) although there are some
> > (inofficial, i.e. not public) containers used in some implementations
>
> As an aside, is there any interest in a custom lighter STL containing
> only the stuff FLTK needs? I have a project here:
>
> https://github.com/osen/sr1

Basically yes, but a first look at this didn't give me evidence that
this would be something useful for us. But I can be wrong...

FWIW, I removed Fl_Widget_Tracker in my personal test version and
uploaded the result to my FLTK fork in branch feature-Fl_Flow:
https://github.com/Albrecht-S/fltk/tree/feature-Fl_Flow

Note that this is mostly untested code but I hope I got everything
right. Your prepare() method could be adjusted to use the parent/child
relationship and I basically replaced every Fl_Widget_Tracker with an
'Fl_Widget *'. This seems to work fine. I also tried the demo program
(below) with valgrind and didn't find any invalid memory errors (and no
leaks).

I extended the (no longer minimal) "test/flow_minimal.cxx" demo program
to delete and add buttons dynamically. Click on any button to delete it
and use the button at the bottom of the window to add new buttons to the
Fl_Flow widget.

Here's the Git statistics of my changes:

$ git diff --stat master..
 FL/Fl_Flow.H          | 486 ++++++++++++++++++++++++++++++++++++++++++
 test/CMakeLists.txt   |   2 +-
 test/flow.cxx         |  36 ++++
 test/flow_center.cxx  |  22 ++
 test/flow_minimal.cxx |  46 ++++
 5 files changed, 591 insertions(+), 1 deletion(-)

The three demo programs are functional and all demos are built if you
use CMake.

Note that I reformatted 'FL/Fl_Flow.H' and used this single header file
rather than your original sources for my modifications.

Commit 40e23c163584f is the one with all the necessary modifications to
remove Fl_Widget_Tracker. Feel free to use my changes in your process to
make it more FLTK compatible. TIA.

PS: I encourage everybody to download from my FLTK fork for easier
testing. The demo programs would be readily available.

Albrecht Schlosser

unread,
Oct 29, 2021, 6:08:18 PM10/29/21
to fltkg...@googlegroups.com
On 10/29/21 11:55 PM Karsten Pedersen wrote:
I was referring to this line earlier from Bill:
> It would be nice to compute a minimum size to fix the layout issue with #9 above.
 
I think perhaps this was to address your (9), widgets sticking to bottom right if the window is too small. Perhaps if we could calculate the minimum size we could make the Fl_Flow widget from its contained widgets, we could do something with that. Perhaps we could even compute the minimum window size from that.

Ah, yes, I probably misunderstood. But anyway, I would definitely not let the Fl_Flow widget make child widgets smaller than the programmer designed. This would never work. Think of a still empty input widget. What would be its minimal size? Only the program author may have a clue. No, no, as I said before: if it's too small, it's too small. The only thing we could do is not to hide widgets if they don't fit entirely.


Obviously one complexity arises because depending on one dimension the window may not collide in the other dimension. You could end up with a smaller window, but one that is shaped in a way that no widgets collide.

Let's not try to do the program designer's work. This is going to fail. KISS (https://www.acronymfinder.com/Keep-It-Simple%2c-Stupid-(KISS).html)

Karsten Pedersen

unread,
Oct 29, 2021, 6:26:47 PM10/29/21
to fltk.general
On Friday, October 29, 2021 at 9:58:13 PM UTC Albrecht Schlosser wrote:
Commit 40e23c163584f is the one with all the necessary modifications to
remove Fl_Widget_Tracker. Feel free to use my changes in your process to
make it more FLTK compatible. TIA.

This looks good. OK so my next steps will be to:

1) Fork and get FLTK building from your feature-Fl_Flow branch.
2) Move implementation from header into src/Fl_Flow.cxx
3) Move private structures (that can be just forward declared) from .h to .cpp
4) Replace vector, string with alternatives
5) Send pull request on GitHub

Let me know if I have missed anything but I will make a start.

Karsten

Albrecht Schlosser

unread,
Oct 29, 2021, 6:58:11 PM10/29/21
to fltkg...@googlegroups.com
Looks good.

1) Please fork from the original fltk/fltk (so you can later make a PR) and pull in my branch from my fork if you like.

However, my branch was just a quick hack for testing and the demo programs will likely not all go into the final commit but one good demo program should definitely be added.

2) Yes, absolutely.
3) Yep (but .cpp -> .cxx)

4) Yes, but KISS. Do we need a string *alternative* or can it just be char * ?

5) Before you send a PR we should discuss the progress in fltk.coredev. I suggest that you start a topic in fltk.coredev (where this belongs) so we can decide how to proceed. It's IMHO easier while the development is still going on than in the context of an official PR and we're used to discussing such issues in fltk.coredev. But I'm confident that we can make this work.

Before the final PR the commits should be rebased and squashed so we have only the relevant files in a single commit.

Looking forward to your progress. Thanks a lot!

Philip Rose

unread,
Oct 30, 2021, 3:39:59 AM10/30/21
to fltkg...@googlegroups.com

rom: Ian MacArthur
Sent: 29 October 2021 21:48
To: Fltk General
Subject: OT: thread drift Re: [fltk.general] [Fl_Flow] A new layout managerfor FLTK

Thanks Ian,

 

Makes sense. As not a professional programmer, I have acquired a few bad habits.

 

Phil.

Albrecht Schlosser

unread,
Oct 30, 2021, 4:27:05 PM10/30/21
to fltkg...@googlegroups.com
On 10/30/21 12:58 AM Albrecht Schlosser wrote:
On 10/30/21 12:26 AM Karsten Pedersen wrote:
On Friday, October 29, 2021 at 9:58:13 PM UTC Albrecht Schlosser wrote:
Commit 40e23c163584f is the one with all the necessary modifications to
remove Fl_Widget_Tracker. Feel free to use my changes in your process to
make it more FLTK compatible. TIA.

FTR: I implemented some new features and fixes in my fork/branch. It's all experimental code and should not stop you, Karsten, from continuing your work.

Main new features:

1) Ability to set a 'resize_callback' which is called after the Fl_Flow widget size has been changed but before the widgets are layed out in the widget area.

2) A method to set the (minimal) widget sizes for the widget layout at runtime, for instance in a resize_callback.

3) Updated demo program which utilizes the resize_callback and changes button sizes to "fit better" in the available area.

I pushed my changes to my fork but, as said above, you (Karsten) can ignore these for now. I intend to merge my changes later when we have Fl_Flow in FLTK or at least in a PR.

Ian MacArthur

unread,
Oct 30, 2021, 4:59:55 PM10/30/21
to Fltk General
On 30 Oct 2021, at 08:39, 'Philip Rose' wrote:
 
Makes sense. As not a professional programmer, I have acquired a few bad habits.


I don't think there’s anything bad about using exceptions, per se, and in a desktop-type situation they are probably a sensible approach in many cases.

Further, if they actually encourage folks to handle errors at all, then that’s a Good Thing, as a lot of code is just weak in that regard.

But it is a horses for courses thing, they have their uses but they are not always the right solution.
For a library, they may be a poor solution, since propagating exceptions from the library to the calling context may not always be a credible option.


Some examples (and this is probably wandering into personal opinion, possibly even a rant, now...) but a lot of folks seem to use exceptions to capture *defined* error conditions. IMHO this is wrong - if it is a defined error state then it is, by that definition, not exceptional, so not an exception, and therefore should be captured explicitly in the regular code flow.

I recognise that most folks aren’t expected to code to DAL-C or DAL-B (or, God forbid, DAL-A) but in those cases you would be expected to have explicit requirements detailing the possible errors and the handling thereof, and woe betide you if you don’t capture them explicitly in the code - shrugging them off an as exception won’t please the external assessors...

In another time and place, I worked with a bloke who was of the ranting “never use goto” type - but his code was littered with duff looking exception handlers, because it was “the proper C++ way”. Bollocks. That’s just another form of control flow transfer - a “goto” in all but name... His code was rubbish anyway...

I shut up now, before it becomes libellous...


Bill Spitzak

unread,
Oct 30, 2021, 5:37:32 PM10/30/21
to fltkg...@googlegroups.com
FLTK probably should not use exceptions itself, either for any purpose of operating correctly, or to report something wrong back to the calling program.

However an FLTK program must be able to call a library that uses exceptions (at least from user-defined callback functions), and FLTK must be usable by a program that is using exceptions. I think this is true in modern compilers no matter how FLTK itself is compiled.

I believe that is the current state, but any callbacks that call code that may throw an exception has to catch them. It would be nice if FLTK managed to cause such exceptions to cleanly be thrown to the calling program, as though the Fl::run() function threw them. The code to support this can be conditionally compiled in so that it does not interfere with the ability to compile FLTK with exception support turned off.


--
You received this message because you are subscribed to the Google Groups "fltk.general" group.
To unsubscribe from this group and stop receiving emails from it, send an email to fltkgeneral...@googlegroups.com.

Karsten Pedersen

unread,
Nov 1, 2021, 8:03:38 AM11/1/21
to fltk.general
Just an FYI, I have now separated out the header and .cxx in my branch here:


One thing that came up due to new code and the removal of Fl_Widget_Tracker is here:

This comparison might be done on a dangling pointer (to a Widget that had just been removed / deleted). The comparison itself isn't unsafe as such and the code after it doesn't access the invalid memory. Perhaps the worst that could happen is a new widget being added that happens to reclaim the same pointer so gets set an incorrect size because it reuses the original set by the deleted widget before it.
I am tempted to call prepare() at the beginning of this function to wipe obsolete pointers but that would be needlessly costly at this point. It is something to keep note of.

I see std::string is already replaced which is good. My next task will be to replace the std::vector<T> with a linked list for both Instructions and States.

Finally, you will see in my original repository here, I have added the FLTK LGPL license file that I am happy to release my code under:

I am not massively keen on that resize_callback stuff. Purely because the sample test which utilizes it is looking too complex for what I would expect a developer to implement each time. I am thinking maybe something instead like:

m_flow.rule(button, "^<=>[33%]");

could be a better choice to "move up, then left then expand right to 33%" of the remaining screen space.
It would require a more substantial interpreter but *should* allow for nice dynamic column resizing.

However an FLTK program must be able to call a library that uses exceptions (at least from user-defined callback functions), and FLTK must be usable by a program that is using exceptions. I think this is true in modern compilers no matter how FLTK itself is compiled.
 
I am not sure that  FLTK can currently handle an exception propagating from a user callback function all the way through the internals. The Fl_Widget_Tracker is probably a good start on leveraging the RAII maybe.
Potentially an easy solution is to wrap any function calling directly into a user callback function pointer could be wrapped in a try { } catch (...) { } with a default handler to just output some exception information. Perhaps also catching std::exception is useful. wxWidgets does this and comes up with an error alert dialog box.
Another solution could be to make some more use of a custom reference counting smart (shared) pointer (in order to keep C++98 compatibility). I wrote one for Watcom C++ on PC-DOS a while back during a "games jam" so potentially we don't need to lose support for aging platforms either.

Karsten Pedersen

unread,
Nov 1, 2021, 11:22:28 AM11/1/21
to fltk.general
I have also replaced both vector<T> with linked lists in the latest version from my branch.
Naturally the code is fairly "unsafe" and much more fiddly IMO than a vector container but I believe I got it right ;)

Valgrind and AddressSanitizer don't work on OpenBSD yet but the debug libc didn't flag anything. Testing is obviously appreciated at this point.

Karsten

Albrecht Schlosser

unread,
Nov 1, 2021, 11:27:03 AM11/1/21
to fltkg...@googlegroups.com
On 11/1/21 4:22 PM Karsten Pedersen wrote:
> I have also replaced both vector<T> with linked lists in the latest
> version from my branch.
> Naturally the code is fairly "unsafe" and much more fiddly IMO than a
> vector container but I believe I got it right ;)

Thanks very much, I'll look into it.

> Valgrind and AddressSanitizer don't work on OpenBSD yet but the debug
> libc didn't flag anything. Testing is obviously appreciated at this point.

I can and will test with valgrind on Linux.

I've been busy all day hence I noticed your first mail just a few
minutes ago. I wanted to reply but now I'll look into your new code
first to see what you changed actually and reply later.

Greg Ercolano

unread,
Nov 1, 2021, 12:27:58 PM11/1/21
to fltkg...@googlegroups.com

Some suggs:


    I think the forward declarations for Fl_Instruction and Fl_State could be moved to the private:
    section of Fl_Flow.H without ill effect.  They don't seem to be exposed in the public API, and doing
    so would make the names available for use elsewhere.

    I wasn't sure how to "fork a fork", so I made a patch against the "flow" branch in github.com/osen/fltk.git
    and put the patch file here that implements a possible way to do the above:
    http://seriss.com/people/erco/tmp/patch-private-structs-and-add-example.patch

    Also, would be good to include an example in the fork as examples/howto-flow.cxx
    which I think I included in the patch as well.

    Made a small mod to the Makefile so that Fl_Flow would be built into the library
    using the default 'make' (using configure) so that the example would compile on linux.
    Other mods would be needed for cmake.


Karsten Pedersen

unread,
Nov 1, 2021, 1:27:26 PM11/1/21
to fltk.general
Great. Thanks.

Patch has been applied and committed. I also have added another test of a "login screen" (these things are suitably awkward to make and I want to compare it with the Fl_Flex approach).

I also fixed a bug where I was not setting m_states to NULL. This luckily was causing random crashes when nesting multiple Fl_Flow widgets within one another so I noticed it.

Karsten

Albrecht Schlosser

unread,
Nov 1, 2021, 1:53:22 PM11/1/21
to fltkg...@googlegroups.com
On 11/1/21 6:27 PM Karsten Pedersen wrote:
> Great. Thanks.
>
> Patch has been applied and committed. I also have added another test
> of a "login screen" (these things are suitably awkward to make and I
> want to compare it with the Fl_Flex approach).

Looks nice. I still need to learn how all the "rules" work. I mean, I
understand it, but "reading" other person's code is a little different.
It looks simple in the tutorial but it can be pretty complex even in
such a "small" login window. But the results are great.

> I also fixed a bug where I was not setting m_states to NULL. This
> luckily was causing random crashes when nesting multiple Fl_Flow
> widgets within one another so I noticed it.

I'm attaching another tiny contribution. A simple comment "fix" (there's
no longer a vector) and the other change addresses the issue you
mentioned before: "One thing that came up due to new code and the
removal of Fl_Widget_Tracker is here:
https://github.com/osen/fltk/blob/flow/src/Fl_Flow.cxx#L314"

That particular line number (and the link) is no longer valid but I
think I found it. My "fix" calls find() for the widget. The "null"
comparison might even be omitted but anyway. If we're not only looking
for correctness and safety another question arises: which code (old or
new) would be more efficient? The new code searches the widget list once
and the old code would have searched through all Fl_State's. The latter
could find a stale state, change it, and this doesn't affect anything.
Perhaps it's better to leave it as-is? Users shouldn't do this but it
doesn't do any harm.

flow.patch

Greg Ercolano

unread,
Nov 1, 2021, 2:02:40 PM11/1/21
to fltkg...@googlegroups.com

On 11/1/21 10:53 AM, Albrecht Schlosser wrote:

It looks simple in the tutorial [..] the results are great.

    Ya, here here.

    I definitely want to try this out in one of my more complicated apps,
    but it looks like it does what I'd want.

    Seems like a really neat way of doing this, too.

Albrecht Schlosser

unread,
Nov 1, 2021, 2:51:02 PM11/1/21
to fltkg...@googlegroups.com
OK, now replying to the older message ...

I apologize in advance for the long message. I hope it helps.


On 11/1/21 1:03 PM Karsten Pedersen wrote:
Just an FYI, I have now separated out the header and .cxx in my branch here:


Thanks, looks good!


One thing that came up due to new code and the removal of Fl_Widget_Tracker is here:

Please see my other message with a potential fix.


This comparison might be done on a dangling pointer (to a Widget that had just been removed / deleted). The comparison itself isn't unsafe as such and the code after it doesn't access the invalid memory. Perhaps the worst that could happen is a new widget being added that happens to reclaim the same pointer so gets set an incorrect size because it reuses the original set by the deleted widget before it.

Good catch, I didn't think of this "same address" issue. This is a potential problem anyway if users change the children of the Fl_Flow widget after it has been created and rules have been applied.


I am tempted to call prepare() at the beginning of this function to wipe obsolete pointers but that would be needlessly costly at this point. It is something to keep note of.

Maybe my "fix" is enough or we can at least live with this issue. I agree that calling prepare() would be wasteful.


I see std::string is already replaced which is good. My next task will be to replace the std::vector<T> with a linked list for both Instructions and States.

Yes, this was a quick and easy change, hence I did it in my fork.


Finally, you will see in my original repository here, I have added the FLTK LGPL license file that I am happy to release my code under:

Great, thanks! If we're using your code I suggest to add the official FLTK boiler plate with a copyright statement of your choice (or, as you like, just the normal "Bill Spitzak and others" copyright.


I am not massively keen on that resize_callback stuff. Purely because the sample test which utilizes it is looking too complex for what I would expect a developer to implement each time.

The resize_callback stuff is entirely optional for the user. It was an attempt to find a way to let the user change widget sizes

(a) after the Fl_Flow widget and all children have been initialized during runtime whenever useful and
(b) when the Fl_Flow widget is being resized.

The method 'Fl_Flow::min_size(Fl_Widget *widget, int w, int h)' can be used at any time for (a) and the resize callback is intended to be used whenever the Fl_Flow widget is resized and the normal mechanism is not sufficient.

In the original code I didn't see a way to change a widget's size at runtime. Size and layout changes beyond the normal positioning by the user should be possible in one or the other way.

Another option would be that users derive their own class and override the resize() method but I believe that a resize callback is easier to implement (requires less code) in a user program. Just like we have button callbacks.

Of course I'm open for (better) suggestions. We're in a process to find the best solution, aren't we?


I am thinking maybe something instead like:

m_flow.rule(button, "^<=>[33%]");

could be a better choice to "move up, then left then expand right to 33%" of the remaining screen space.
It would require a more substantial interpreter but *should* allow for nice dynamic column resizing.

Yes, something like that would be helpful as well. We have something similar with the '@ symbol' orientation and size. Maybe it's worth to take a look at that for similar "instructions".

I was also looking for a way to center, say, two or three objects in one row such that the space left and right of the objects is equal. Generally maybe an instuction to center all objects in one "row" after filing the available space. For instance if I move 5 buttons as in the example and only 3 buttons fit per row, is it possible to lay them out that 3 buttons in the first row are centered and the other two in the last row too? I'm not talking about changing the padding, just moving them to the center of each row?

One of your demos shows how the text input widget can be expanded to take the full space left above the (one) button at the bottom. What if I want to add more than one row of buttons at the bottom? Is there a way to achieve this while still expanding the text input field above to full size?

I think all or some of these questions can be resolved by nesting Fl_Flow widgets but maybe there's a better way?

Please don't understand me wrong, this is not to criticize your work which is really great. I'm asking these questions as a "normal user", anticipating that users would have such questions too.

That said, there's one more technical aspect: the Fl_Flow widget should be a "class" rather than a struct, as all other FLTK widgets are. The helper structs can IMHO remain structs or can become classes as well, as it fits.

Regarding Greg's change, moving the helper structs into the class: +1. But I'm not sure if some of the structs and/or variables would need to be protected just in case users want to derive their own classes.

One last suggestion for now: I see that you are parsing (decoding) the rules and combining characters, e.g. "=>" into one Fl_Instruction. That's OK. But I wonder why we need one Fl_Instruction object for each such decoded part of the rule. This adds a lot of overhead that can maybe be reduced.

Just a thought: decoding the rule leads to a number of int's that are stored each in one Fl_Instruction. First of all, each int is small and could be a char or unsigned char as well. Then we could maybe store all int's of one rule in a char array (aka string) and have only one Fl_Instruction object per rule, like this:

struct Fl_Flow::Fl_Instruction { // or would this be Fl_Rule then?
Fl_Instruction *m_next; // do we still need this?
Fl_Widget *m_widget;
unsigned char *m_instructions; // all decoded int's of one "rule"
};

m_instructions would have to be allocated but there's a natural upper bound when it's created which is strlen(rule_text). Or something like that.

As I wrote, just a thought...

Karsten Pedersen

unread,
Nov 1, 2021, 6:08:56 PM11/1/21
to fltk.general
On Monday, November 1, 2021 at 6:51:02 PM UTC Albrecht Schlosser wrote:
but "reading" other person's code is a little different.
It looks simple in the tutorial but it can be pretty complex even in
such a "small" login window.

Yeah, I do understand this. I look at some of my test forms and do get a little confused. What I find helps is to just follow the instructions through from the start and try to visualize it. I think some comments next to each one can help, even just the names of the instructions, "up, left, etc". Sometimes, even though you can get away without, nesting Fl_Flow containers can be preferable.
 
Please see my other message with a potential fix.

OK, I am happy with this fix and have applied it. It is indeed more efficient than calling prepare(). I also looked at the code behind Fl_Widget_Tracker and can see that scans through a list of children. Possibly a future solution will be something like std::weak_ptr<T> which stores a reference count and only when the last reference (strong *and* weak) goes out, does it clean up. That way its speed is mostly O(1) rather than O(n). It will also solve the issue of address reuse.
 
If we're using your code I suggest to add the official FLTK boiler plate with a copyright statement of your choice (or, as you like, just the normal "Bill Spitzak and others" copyright.

OK, I have added my name "and others" because a few of us here have worked on Fl_Flow so far in the last couple of days.
 
I was also looking for a way to center, say, two or three objects in one row such that the space left and right of the objects is equal. Generally maybe an instuction to center all objects in one "row" after filing the available space.

Indeed, you may have spotted my login example only has one text box (username) rather than password, etc. Basically, I couldn't yet think of a way to do it "easily" without nesting. So I agree that some sort of solution here could be good. Of course you could position on in the center (or [25%] if we implement that) and then simply stack the others below it. However a way to group a bunch of them conveniently centered could indeed be nice and reduce typing and complexity.
 
One of your demos shows how the text input widget can be expanded to take the full space left above the (one) button at the bottom. What if I want to add more than one row of buttons at the bottom? Is there a way to achieve this while still expanding the text input field above to full size?
If you add a new button, move it up. It will hit the text input and stop. Then give it a down instruction and it will hit the top of the original button. This will then be the start of your second row. Move the button left and repeat for the next one. This could be dome in a little more of a controlled manner with a (hidden?) separator. Or of course a nested Fl_Flow.


I think all or some of these questions can be resolved by nesting Fl_Flow widgets but maybe there's a better way?

Please don't understand me wrong, this is not to criticize your work which is really great. I'm asking these questions as a "normal user", anticipating that users would have such questions too.
 
Yes, I absolutely understand. This whole layout system is fairly experimental and evolving so I am very happy to hear your insights as we explore it and including any potential issues. What I do want to avoid is someone coming up to maintain a GUI program, only to be greeted with:

m_flow.rule(m_content, "<^/>^/>/^<v=^><=>^v</>/>/^"); // Position the content

 
That said, there's one more technical aspect: the Fl_Flow widget should be a "class" rather than a struct, as all other FLTK widgets are. The helper structs can IMHO remain structs or can become classes as well, as it fits.
 
Good spot, I will change that.

Regarding Greg's change, moving the helper structs into the class: +1. But I'm not sure if some of the structs and/or variables would need to be protected just in case users want to derive their own classes.

Whilst setting the destructor virtual, I was thinking to myself what inheriting from this widget would be like. Personally I feel it would be very difficult as it stands because I would assume the user is trying to add new instructions (or they would simply inherit from Fl_Group instead, in which case they would really want to tap into the process() function itself, rather than override something like resize(), etc.
 
One last suggestion for now: I see that you are parsing (decoding) the rules and combining characters, e.g. "=>" into one Fl_Instruction.

True. I think there certainly is room for optimising this part. In particular I was thinking bitmasks such as:

if (instruction & EXPAND) { ... }

I am also going to start looking at adding some doxygen comments too (I will clone the syntax used in other units).

Karsten

Albrecht Schlosser

unread,
Nov 1, 2021, 7:18:42 PM11/1/21
to fltkg...@googlegroups.com
On 11/1/21 11:08 PM Karsten Pedersen wrote:

On Monday, November 1, 2021 at 6:51:02 PM UTC Albrecht Schlosser wrote:
but "reading" other person's code is a little different.
It looks simple in the tutorial but it can be pretty complex even in
such a "small" login window.

Yeah, I do understand this. I look at some of my test forms and do get a little confused. What I find helps is to just follow the instructions through from the start and try to visualize it. I think some comments next to each one can help, even just the names of the instructions, "up, left, etc". Sometimes, even though you can get away without, nesting Fl_Flow containers can be preferable.

Sure, I see that as well. The "right" combination of complex rules and nesting (and even other Fl_Group types) needs to be found for each individual task.


 
Please see my other message with a potential fix.

OK, I am happy with this fix and have applied it. It is indeed more efficient than calling prepare(). I also looked at the code behind Fl_Widget_Tracker and can see that scans through a list of children.

Yeah, and if you're adding lots of watches to this list it's getting even worse. That's why I wanted to remove it from this widget and why I wrote that it was only intended for short term watches, mainly while callbacks are executed. Sure, we could improve this with hashes or whatever, but that's OT for now.


Possibly a future solution will be something like std::weak_ptr<T> which stores a reference count and only when the last reference (strong *and* weak) goes out, does it clean up. That way its speed is mostly O(1) rather than O(n). It will also solve the issue of address reuse.

Yes it would, but we don't use such "modern" stuff. ;-)

 
If we're using your code I suggest to add the official FLTK boiler plate with a copyright statement of your choice (or, as you like, just the normal "Bill Spitzak and others" copyright.

OK, I have added my name "and others" because a few of us here have worked on Fl_Flow so far in the last couple of days.

That's fine.


I was also looking for a way to center, say, two or three objects in one row such that the space left and right of the objects is equal. Generally maybe an instuction to center all objects in one "row" after filing the available space.

Indeed, you may have spotted my login example only has one text box (username) rather than password, etc. Basically, I couldn't yet think of a way to do it "easily" without nesting. So I agree that some sort of solution here could be good. Of course you could position on in the center (or [25%] if we implement that) and then simply stack the others below it. However a way to group a bunch of them conveniently centered could indeed be nice and reduce typing and complexity.

I'm sure we'll find a way...


One of your demos shows how the text input widget can be expanded to take the full space left above the (one) button at the bottom. What if I want to add more than one row of buttons at the bottom? Is there a way to achieve this while still expanding the text input field above to full size?
If you add a new button, move it up. It will hit the text input and stop. Then give it a down instruction and it will hit the top of the original button. This will then be the start of your second row. Move the button left and repeat for the next one. This could be dome in a little more of a controlled manner with a (hidden?) separator. Or of course a nested Fl_Flow.

I see. But such instructions tend to be "write-only": once you've written it and read it again you don't know what's going on and why you did that.

With a little experience there will be some ideas ...


I think all or some of these questions can be resolved by nesting Fl_Flow widgets but maybe there's a better way?

Please don't understand me wrong, this is not to criticize your work which is really great. I'm asking these questions as a "normal user", anticipating that users would have such questions too.
 
Yes, I absolutely understand. This whole layout system is fairly experimental and evolving so I am very happy to hear your insights as we explore it and including any potential issues. What I do want to avoid is someone coming up to maintain a GUI program, only to be greeted with:

m_flow.rule(m_content, "<^/>^/>/^<v=^><=>^v</>/>/^"); // Position the content

;-)


Whilst setting the destructor virtual,

I noticed it. Something I had forgotten to mention, but this should always be done for FLTK widgets.


I was thinking to myself what inheriting from this widget would be like. Personally I feel it would be very difficult as it stands because I would assume the user is trying to add new instructions (or they would simply inherit from Fl_Group instead, in which case they would really want to tap into the process() function itself, rather than override something like resize(), etc.

You never know what users would do. I know from my own experience that I derived a class from Fl_Group just to draw another border. The top border was like

-----------  here is the group label label  -------------

This wouldn't need any protected (rather than private) members and functions but, as I said, you never know.


 
One last suggestion for now: I see that you are parsing (decoding) the rules and combining characters, e.g. "=>" into one Fl_Instruction.

True. I think there certainly is room for optimising this part. In particular I was thinking bitmasks such as:

if (instruction & EXPAND) { ... }

Absolutely. We're often using bit masks (see all these Fl_Align flags). This can make the entire system much easier (particularly x_direction() and y_direction()).


I am also going to start looking at adding some doxygen comments too (I will clone the syntax used in other units).

That would be great. Before you start, there's one rule: add the doxygen comments to the file where the implementation is. Generally short inline methods must be documented in the header, other methods are documented in the .cxx file.

I was thinking about copying your tutorial, but this should not go into the Doxygen docs in the source files. Short code examples, yes, but not the tutorial. We can probably find a place in the documentation chapters (a new "Layout" chapter, maybe) where we can add something like your tutorial. Maybe in chapter "How does resizing work?". Layout (Fl_Pack, Fl_Flow, Fl_Flex (?), Fl_Grid (?) and others) and resizing with FLTK standard means can all be grouped together in one chapter. But that's something we can attack later.

Greg Ercolano

unread,
Nov 1, 2021, 8:00:01 PM11/1/21
to fltkg...@googlegroups.com
    If you think the Fl_Instruction / Fl_State might be useful for deriving classes,
    then perhaps naming the classes Fl_Flow_Instruction + Fl_Flow_State so that
    it's clear they're related.

Albrecht Schlosser

unread,
Nov 1, 2021, 8:04:26 PM11/1/21
to fltkg...@googlegroups.com
Since they're now in the Fl_Flow:: namespace they could also be Fl_Flow::Instruction and Fl_Flow::State.

Albrecht Schlosser

unread,
Nov 2, 2021, 9:33:54 AM11/2/21
to fltkg...@googlegroups.com
On 11/1/21 11:08 PM Karsten Pedersen wrote:
Indeed, you may have spotted my login example only has one text box (username) rather than password, etc. Basically, I couldn't yet think of a way to do it "easily" without nesting. So I agree that some sort of solution here could be good. Of course you could position on in the center (or [25%] if we implement that) and then simply stack the others below it. However a way to group a bunch of them conveniently centered could indeed be nice and reduce typing and complexity.

I'm attaching my modified version of the flow_login.cxx example program with two text boxes (username and password). The boxes are not really centered but appear in the right place. I added comments to explain what is going on.

@Karsten: I extended your mechanism by making a larger invisible box that reserves space for the labels at the left side. It does also serve as an anchor for the input widgets. The vertical "centering" is done by adding another invisible box (sep) below the input fields which replaces the visible line you used.

The "invisible box" mechanism is not new, it can also used for standard Fl_Group resizing and is well known and documented.

It's kinda complex though because you add invisible widgets for nothing but positioning of other widgets but the advantage is that you can work with relative positions by defining the free space (distance) from the group borders.

The Fl_Flow widget is certainly a good new feature.

PS: Please feel free to update your branch with my modified version if you like it.

flow_login.cxx

Albrecht Schlosser

unread,
Nov 2, 2021, 10:03:39 AM11/2/21
to fltkg...@googlegroups.com
On 11/2/21 2:33 PM Albrecht Schlosser wrote:
On 11/1/21 11:08 PM Karsten Pedersen wrote:
Indeed, you may have spotted my login example only has one text box (username) rather than password, etc. Basically, I couldn't yet think of a way to do it "easily" without nesting. So I agree that some sort of solution here could be good. Of course you could position on in the center (or [25%] if we implement that) and then simply stack the others below it. However a way to group a bunch of them conveniently centered could indeed be nice and reduce typing and complexity.

I'm attaching my modified version of the flow_login.cxx example program with two text boxes (username and password). The boxes are not really centered but appear in the right place. I added comments to explain what is going on.

Here is another, slightly simplified version. Instead of moving the input boxes up and down I added an invisible box *above* the input widgets which saved a few rules.

flow_login.cxx

Karsten Pedersen

unread,
Nov 3, 2021, 11:31:38 AM11/3/21
to fltk.general
Yes it would, but we don't use such "modern" stuff. ;-)

To be fair, I specifically chose FLTK for Fl_Flow because I liked the clean and maintainable codebase. Adding complexity like smart pointers might detract from this. I do have a custom shared/weak pointer implementation that also works on C++98 but after some reconsidering, it is not something I really want to push for. 

I see. But such instructions tend to be "write-only": once you've written it and read it again you don't know what's going on and why you did that.
 
Write-only is a really good way to summarize it. It is quite easy to get into a mindset where the instructions make perfect sense. But once you are done and you read it back, it makes almost zero sense!
 
Layout (Fl_Pack, Fl_Flow, Fl_Flex (?), Fl_Grid (?) and others) and resizing with FLTK standard means can all be grouped together in one chapter. But that's something we can attack later.

 Yes absolutely. It will be really nice to have information on all the different layout systems. I have seen similar pages for Motif, Qt and javax.swing and it provides good insight as to where one might be more suitable than another. Ultimately I would also like to provide a tutorial program (test or sample) that takes the user through the same tutorial but as part of an interactive GUI program.

As an aside, I have committed your changes to flow_login.cxx. With the comments it is much more understandable. We are lucky in this specific example that the centered panel never changes size, so we can use a hard coded value of '100' for the topPad. However if we did want this to resize at some point, then we might need to look at adding percentile instructions like:

center.rule(topPad, "=^[10%]^=<");   // Resize to 10% of height, reserve space above input boxes

Karsten


Albrecht Schlosser

unread,
Nov 3, 2021, 7:16:03 PM11/3/21
to fltkg...@googlegroups.com
On 11/3/21 4:31 PM Karsten Pedersen wrote:
...  once you are done and you read it back, it makes almost zero sense!

Like writing regex's. Writing is "easy" but understanding someone else's long regex is hard to do.


Layout (Fl_Pack, Fl_Flow, Fl_Flex (?), Fl_Grid (?) and others) and resizing with FLTK standard means can all be grouped together in one chapter. But that's something we can attack later.

 Yes absolutely. It will be really nice to have information on all the different layout systems. I have seen similar pages for Motif, Qt and javax.swing and it provides good insight as to where one might be more suitable than another. Ultimately I would also like to provide a tutorial program (test or sample) that takes the user through the same tutorial but as part of an interactive GUI program.

What we need is one test program in our 'test' directory which can be used mainly by developers for testing the functionality. Not really a unit test but something like that. We can also have some (small) "good example programs" in our 'examples' directory mainly for users to see how it can be used.

Documentation is also required. But although an interactive tutorial program might be a nice challenge I believe that this would be too much to maintain later. It might be useful as an external project if you like to provide it and we can put links on your pages but we should try not to add too much stuff for one widget.


As an aside, I have committed your changes to flow_login.cxx.

Thanks.


With the comments it is much more understandable. We are lucky in this specific example that the centered panel never changes size, so we can use a hard coded value of '100' for the topPad.

Yep, that's why I did it this way. I knew that this wouldn't help with centering but it's another way to layout a widget.


However if we did want this to resize at some point, then we might need to look at adding percentile instructions like:

center.rule(topPad, "=^[10%]^=<");   // Resize to 10% of height, reserve space above input boxes

Yes, that would be an option. But it wouldn't help for *exact* centering, otherwise the input widgets and buttons would have to resize as well. I don't know how centering could be achieved (yet). Anyway, if we wanted to add such percent values our "instructions" would naturally become larger. My suggestion to put all instructions of one rule in one 'char *' array could probably no longer apply.

Here's another thought: What if we offered an optional feature (per Fl_Flow widget, not per child) to resize every widget inside the Fl_Flow with the same factor as the Fl_Flow widget since its creation? This would be similar to the way resizing works in Fl_Group widgets but likely w/o a resizable() widget. We could utilize Fl_Group::init_sizes() and the related internals to save the original widget sizes. The user would give absolute sizes related to the initial size of the Fl_Flow widget. Then, when a resize occurs, all widget sizes are adjusted to (initial_size * factor) before the layout engine (prepare() + process()) starts. In fact the resizing of individual widgets could be the first step in prepare(). Everything else would be the same. It might look a little strange if buttons changed their sizes but it could help making things smoother.

If you think of the flow_login example: everything inside the main Fl_Flow widget would resize with the same factor.

Reply all
Reply to author
Forward
0 new messages