Background Fill for Group

212 views
Skip to first unread message

Webb Hinton

unread,
Jun 16, 2021, 3:07:35 PM6/16/21
to fltk.general
What's a good approach to creating a Group that has a "filled" background?  I.e when a widget moves inside of it's parent group, the background is "cleared", thus avoiding the "tracing" effect you get otherwise:

tracing_issue.gif

Code: (sorry it is in rust)
use fltk::{app, draw::*, enums::*, frame::Frame, group::Group, prelude::*, window::Window, button::*};

fn main() {
    let app = app::App::default();
    let mut wind = Window::default()
        .with_size(400, 300)
        .with_label("Group Background Color");
    wind.make_resizable(true);
    let mut group = Group::new(0, 0, 400, 300, None);
    let mut button= Button::new(100,200,50,20,"animate");
    //frame we want to see  
    let mut frame = Frame::new(0, 0, 100, 100, "redraw parent");
    frame.set_color(Color::Magenta);
    frame.set_frame(FrameType::BorderFrame);

    group.end();
    // group.draw(move |widg| {
    //     //background fill
    //     draw_rect_fill(widg.x(), widg.y(), widg.width(), widg.height(), Color::Red);
    //     //draw the children after drawing the background fill color
    //     widg.draw_children();
    // });
    wind.end();
    wind.show();
    
    button.set_callback(move |_| {
        let mut frame = frame.clone();
        std::thread::spawn(move || {
            for i in 0..1000 {
                app::sleep(0.05);
                    frame.resize(frame.x()+10, frame.y(), frame.width(), frame.height());
                    dbg!(frame.x(), frame.y(), frame.width(), frame.height());
                    //Only Redraws the Widget, frame does not show
                    // frame.redraw();
                    //Redraws as expected, but redraws all children
                    frame.parent().unwrap().redraw();
                    dbg!("thread working!");
            }
        });
    });

    app.run().unwrap();
}

imm

unread,
Jun 16, 2021, 3:13:49 PM6/16/21
to General FLTK
On Wed, 16 Jun 2021, 20:07 Webb Hinton wrote:
What's a good approach to creating a Group that has a "filled" background?  I.e when a widget moves inside of it's parent group, the background is "cleared", thus avoiding the "tracing" effect you get otherwise:


If your group has a box type set, you will not get that tracing effect.

In fltk, "frame" types are only responsible for drawing the border, whereas "box" types draw the border and the background.

Note that Mo made some naming choices around the use of the word Frame in the rust port that are "unfortunate" and confusing here...
--
Ian
From my Fairphone FP3

Greg Ercolano

unread,
Jun 16, 2021, 3:30:36 PM6/16/21
to fltkg...@googlegroups.com


On 6/16/21 12:13 PM, imm wrote:
On Wed, 16 Jun 2021, 20:07 Webb Hinton wrote:
What's a good approach to creating a Group that has a "filled" background?  I.e when a widget moves inside of it's parent group, the background is "cleared", thus avoiding the "tracing" effect you get otherwise:


If your group has a box type set, you will not get that tracing effect.

    ..and to be specific, group->box(FL_FLAT_BOX); would be an example of that (in C++).
    I'm not sure the equivalent in rust, but it's probably similar.

Ian MacArthur

unread,
Jun 16, 2021, 3:46:56 PM6/16/21
to Fltk General
I think (without actually bothering to check properly, that is, so proceed with caution!) that in this case it would be something like:


frame.set_frame(FrameType::BorderBox);

Which is partly what I was alluding to about the confusing use of the word Frame (and frame) in the rust port...



Mo_Al_

unread,
Jun 16, 2021, 8:19:55 PM6/16/21
to fltk.general
A translation of erco's answer to Rust would be group.set_frame(FrameType::FlatBox);
Indeed that should fix the issue. 

Webb Hinton

unread,
Jul 14, 2021, 2:30:49 PM7/14/21
to fltk.general
It look like this works for a Group, but not for a Pack.  When I try it with Pack, the child widget does not show up in front of the background.

group_vs_pacK_fill.PNG


fn main(){
    let app = App::default();
    let mut win = Window::new(200, 200, 500, 300, "Pack vs Group Overdraw");
    //child not visible
    let mut my_pack= Pack::new(50,50,100,200,"mypack");
    my_pack.set_frame(FrameType::FlatBox);
    my_pack.set_color(Color::Blue);
    //should see a yellow frame
    let mut frame = Frame::new(250,100,100,100, "Test");
    frame.set_frame(FrameType::FlatBox);
    frame.set_color(Color::Yellow);
    my_pack.end();

    //child shows up ontop of group correctly
    let mut my_group= Group::new(200,50,100,200,"my group");
    my_group.set_frame(FrameType::FlatBox);
    my_group.set_color(Color::Red);

    let mut frame = Frame::new(200,100,100,100, "Test");
    frame.set_frame(FrameType::FlatBox);
    frame.set_color(Color::Yellow);
    my_group.end();

    win.end();
    win.show();

    app.run().unwrap();
}

Albrecht Schlosser

unread,
Jul 14, 2021, 4:03:46 PM7/14/21
to fltkg...@googlegroups.com
On 7/14/21 8:06 PM Webb Hinton wrote:
It look like this works for a Group, but not for a Pack.  When I try it with Pack, the child widget does not show up in front of the background.
fn main(){
    let app = App::default();
    let mut win = Window::new(200, 200, 500, 300, "Pack vs Group Overdraw");
    //child not visible
    let mut my_pack= Pack::new(50,50,100,200,"mypack");
    my_pack.set_frame(FrameType::FlatBox);
    my_pack.set_color(Color::Blue);
    //should see a yellow frame
    let mut frame = Frame::new(250,100,100,100, "Test");

This "frame" is horizontally far outside the bounds of the (Fl_)Pack widget. What happens if you change this to

    let mut frame = Frame::new(50,100,100,100, "Test");

Note also that (Fl_)Pack does its own calculations of widget sizes (the default is IIRC vertical packing, hence the widget might appear at the top, or ...?). I'm not sure what the result of this test *should* be...

Greg Ercolano

unread,
Jul 14, 2021, 5:02:06 PM7/14/21
to fltkg...@googlegroups.com

On 7/14/21 11:06 AM, Webb Hinton wrote:

It look like this works for a Group, but not for a Pack.  When I try it with Pack, the child widget does not show up in front of the background.

    TL;DR: Fl_Pack is I think really designed ONLY to be parented to an Fl_Scroll, due to how
    its meant to always shrinkwrap itself (resized) exactly around its packed children.
    (Perhaps there's other possible ways it can be used, but really that's its best use)

    Fl_Pack seems designed such that as children are added or removed, it's supposed to
    grow/shrink itself to exactly fit around the children so that its background is never seen.
    But its calculation for this is done at draw() time, where it's a little late.

    If you want to use Fl_Pack in a way where a background is drawn over a fixed xywh area,
    you can't depend on Fl_Pack being a fixed size because it resizes itself, so create an Fl_Scroll
    with the xywh area, set the box() type for the scroll, and then put the pack inside the scroll,
    as the docs recommend:

        [..] Then the Fl_Pack resizes itself to surround the child widgets.
        [..] You may want to put the Fl_Pack inside an Fl_Scroll.

    When Fl_Pack resizes itself to surround the child widgets (or 'shrinkwraps'),
    the parent Fl_Scroll's background would be seen behind them, and if the children
    exceed the size of the scroll widget, scrollbars appear allowing you to scroll
    to see them. (Which is why Fl_Scroll, and not Fl_Group should be the parent,
    as Fl_Group cannot handle children that get larger than the Fl_Group's xywh,
    only Fl_Scroll handles that properly)

    The Fl_Pack docs should really be improved a bit, as Fl_Pack is in many ways unlike
    other FLTK widgets, and is really more like a low level widget intended to be coupled
    with e.g. Fl_Scroll to work properly.

    Using Fl_Pack alone can be confusing, because it's odd in several ways:

        o It's probably the only widget that only draws its background if it's set to a FL_XXX_FRAME type (very odd)

        o FL_XXX_BOX types are not supported because it's draw() code draws the pack's box() last,
           which would over draw its children(!) (which is what's happening in your example program)

        o It is I think the only widget that resizes itself (shrink wraps) to fit its children, and doesn't
           actually stay at the width/height you initially set it to.

        o The resize calculation for shrink wrapping is unfortunately in the draw() code, where it can only
           be triggered when an actual redraw occurs.  (this can and should be fixed like it was when
           recalc_scrollbars() were added to Fl_Scroll, IIRC)


    Looking at how Fl_Pack::draw() is implemented, it appears FL_XXX_BOX types were not
    intended to be supported at all:

        When there's a box type other than FL_NO_BOX (e.g. FL_XXX_FRAME or FL_XXX_BOX),
        it tries to draw any area of its background /around/ the children while it's drawing them
        presumably to prevent 'flicker' for a single buffered window.

        And then, after the children are drawn, the Fl_Pack::box() is drawn last, presumably
        to draw any frame and label over the children. Which means FL_XXX_BOX types can't
        be supported /at all/, because that would just draw right over the children it just finished drawing.
        (as you example shows).

        The intention here is obviously only to support box() types that draw frames, and due
        to how Fl_Pack 'shrinkwraps', its background should never be seen anyway.

    A problem is due to how Fl_Pack does its resize calculation inside its draw() code, where the app
    can't call it directly after changing children because draw() can only be called in a draw context.
    So the app can't force a recalc the way Fl_Pack is written today.

    You can see this in the attached program below, where I ported your code to C++, and changed
    the pack's box from FL_FLAT_BOX to FL_THIN_UP_FRAME so that it draws its BLUE background
    around the children, but only does this for the first draw(). As soon as it draws again (e.g. if the
    window is exposed from under another window, or the window is resized even slightly) the widget
    has shrinkwrapped itself around the children, causing the BLUE area to disappear.

    Fl_Pack should really be modified to provide a recalc method the app can call after changing
    its children, so the  'shrink wrap' behavior can happen BEFORE draw() is called. Precidents exist
    in other widgets already, e.g.  Fl_Group::init_sizes(), and Fl_Scroll::recalc_scrollbars().

    In short, I believe Fl_Pack is only intended to be used in special cases, such as a child of Fl_Scroll,
    and the pack's own background should not be depended on at all due to how its meant to always
    be 'shrink wrapped'.

    Perhaps Fl_Pack can be used in other situations, such as parented to an Fl_Group to get
    similar 'predictable' behavior, but an Fl_Group would be bad if the Fl_Pack grew larger than
    the Fl_Group, as Fl_Group isn't designed to handle children that go beyond its bounds, which is
    why Fl_Scroll is preferred.

    These odd behaviors seem specific to Fl_Pack, and should probably be documented in
    Fl_Pack's class description, because as it stands it's not really clear, as many folks try to
    use Fl_Pack like an Fl_Group, but it's really not meant to be used alone I think.

    What follows is my C++ port of your (rust?) code, with a small mod changing the box type
    to a thin frame, which briefly shows the BLUE area, which then disappears on subsequent
    redraws when the pack resizes itself to fit the one child.


#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Group.H>
#include <FL/Fl_Pack.H>
#include <FL/Fl_Box.H>

int main() {
    Fl_Window win(200, 200, 500, 300, "Pack vs Group Overdraw");
    //child not visible
    Fl_Pack my_pack(50,50,100,200,"mypack");
      //my_pack.box(FL_FLAT_BOX);         // <-- not supported: any FL_XXX_BOX causes bg to overdraw children
      my_pack.box(FL_THIN_UP_FRAME);      // <-- this causes pack's bg to draw around children, but only..
                                          //     until the next redraw, causing the shrink wrap behavior to occur

      my_pack.color(FL_BLUE);


      //should see a yellow frame

      Fl_Box frame(200,50,100,100, "Test1");
      frame.box(FL_FLAT_BOX);
      frame.color(FL_YELLOW);


    my_pack.end();
    //child shows up ontop of group correctly

    Fl_Group my_group(200,50,100,200,"my group");
      my_group.box(FL_FLAT_BOX);
      my_group.color(FL_RED);  
      Fl_Box frame2(200,100,100,100, "Test2");
      frame2.box(FL_FLAT_BOX); 
      frame2.color(FL_YELLOW); 
    my_group.end();
    win.end();
    win.resizable(win);
    win.show();
    return Fl::run();
}


Webb Hinton

unread,
Jul 14, 2021, 5:04:49 PM7/14/21
to fltk.general
Changing the frame's position to be inside the Pack didn't have an effect. I'd always though that it doesn't really matter what x/y coords you provide to a widget that's going to be inside of a Pack because the Pack repositions them automatically. 

fn main(){
    let app = App::default();
    let mut win = Window::new(200, 200, 500, 300, "Pack vs Group Overdraw");
    //child not visible
    let mut my_pack= Pack::new(50,50,100,200,"mypack");
    my_pack.set_frame(FrameType::FlatBox);
    my_pack.set_color(Color::Blue);
    //should see a yellow frame
    //inside pack
    let mut frame = Frame::new(50,100,100,100, "Test");
    // let mut frame = Frame::new(250,100,100,100, "Test");

    frame.set_frame(FrameType::FlatBox);
    frame.set_color(Color::Yellow);
    my_pack.end();

    //child shows up ontop of group correctly
    let mut my_group= Group::new(200,50,100,200,"my group");
    my_group.set_frame(FrameType::FlatBox);
    my_group.set_color(Color::Red);

    let mut frame = Frame::new(200,100,100,100, "Test");
    frame.set_frame(FrameType::FlatBox);
    frame.set_color(Color::Yellow);
    my_group.end();

    win.end();
    win.show();

    app.run().unwrap();
}

Webb Hinton

unread,
Jul 14, 2021, 5:11:05 PM7/14/21
to fltk.general
I see. Thanks for the detailed breakdown. 

If FL_Pack isn't meant to be used outside the context of an FL_Scroll, wouldn't it make sense for FLTK to have some kind of Flexbox type container that's useful in a more general context? Having to use the Pack inside of a Scroll is not idiomatic for situations where we won't need to scroll the content, and manually implementing resizing for a custom Group-type widget is also verbose. 

Greg Ercolano

unread,
Jul 14, 2021, 5:14:31 PM7/14/21
to fltkg...@googlegroups.com


On 7/14/21 1:15 PM, Webb Hinton wrote:
Changing the frame's position to be inside the Pack didn't have an effect.

    Right, what you want is to add a parent Fl_Scroll to the pack, and set your
    colors+box type for the scroll, not the pack. (in green below)

    For reasons why this is, see my previous reply.



#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Group.H>

#include <FL/Fl_Scroll.H>


#include <FL/Fl_Pack.H>
#include <FL/Fl_Box.H>

int main() {

    Fl_Window win(200, 200, 500, 300, "Pack vs Group Overdraw");
    Fl_Scroll my_scroll(50,50,100,200,"myscroll");
      my_scroll.color(FL_BLUE);                // set bgcolor for scroll, not pack
      my_scroll.box(FL_FLAT_BOX);              // set box() for scroll, not pack
      Fl_Pack my_pack(50,50,100,200,"mypack");


        Fl_Box frame(200,50,100,100, "Test1");
          frame.box(FL_FLAT_BOX);
          frame.color(FL_YELLOW);
      my_pack.end();

    my_scroll.end();

    Fl_Group my_group(200,50,100,200,"my group");
      my_group.box(FL_FLAT_BOX);
      my_group.color(FL_RED);
      Fl_Box frame2(200,100,100,100, "Test2");
      frame2.box(FL_FLAT_BOX);
      frame2.color(FL_YELLOW);
    my_group.end();
    win.end();
    win.resizable(win);
    win.show();
    return Fl::run();
}



    Note that if you want window resizing to affect the width of the pack to follow the size
    of the scroll, you'd have to derive a class from Fl_Scroll and implement a resize() method
    for that class that changes the child pack's width to match, as by default changes to
    an Fl_Scroll doesn't change its children.


Greg Ercolano

unread,
Jul 14, 2021, 5:22:18 PM7/14/21
to fltkg...@googlegroups.com


On 7/14/21 2:11 PM, Webb Hinton wrote:
If FL_Pack isn't meant to be used outside the context of an FL_Scroll, wouldn't it make sense for FLTK to have some kind of Flexbox type container that's useful in a more general context?

    I'd agree, and am not sure why such a higher level widget wasn't provided.
    Probably has to do with the history of FLTK's roots in forms/xforms?

    I don't think there's a reason FLTK couldn't provide such a thing, e.g. an
    "Fl_Scroll_Pack" or some such, and have it do optional desireable behaviors
    that e.g. cause the pack to stretch to fit the size of the scroll, instead of the
    scroll sizing around the fixed size of the pack.


Having to use the Pack inside of a Scroll is not idiomatic for situations where we won't need to scroll the content, and manually implementing resizing for a custom Group-type widget is also verbose.

    Having a scroll is mainly a safety feature so that if, for some reason, the pack's
    contents exceeds the size of its parent, scrollbars appear. Even if they never appear,
    it's best to have the parent be a scroll in case they do in some unexpected case
    (such as when the window size is shrunk).

    If an Fl_Group were the parent, Bad Things (TM) would happen, e.g. with
    event delivery for children drawn outside the Fl_Group's xywh.

Gmail Ercolano

unread,
Jul 14, 2021, 5:42:00 PM7/14/21
to fltkg...@googlegroups.com


On 7/14/21 2:14 PM, Greg Ercolano wrote:
    [..] Note that if you want window resizing to affect the width of the pack to follow the size

    of the scroll, you'd have to derive a class from Fl_Scroll and implement a resize() method
    for that class that changes the child pack's width to match, as by default changes to
    an Fl_Scroll doesn't change its children.


    ..and here's an implementation showing how one might do that, and perhaps
    a good starting point for someone wanting to implement the higher level widget
    discussed previously:



#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Group.H>
#include <FL/Fl_Scroll.H>
#include <FL/Fl_Pack.H>
#include <FL/Fl_Box.H>

class MyScrollPack : public Fl_Scroll {
    Fl_Pack *pack;
public:
    MyScrollPack(int X,int Y,int W,int H,const char *L=0):Fl_Scroll(X,Y,W,H,L) {
        pack = new Fl_Pack(X,Y,W,H);
    }
    // Allow begin()/end()/add()/etc to affect pack, not scroll
    void begin() { pack->begin(); }
    void end() { pack->end(); Fl_Scroll::end(); }
    void add(Fl_Widget& o) { pack->add(o); }
    void add(Fl_Widget* o) {add(*o);}
    void type(int val) { pack->type(val); }

    // TODO: other methods..!

    // Track pack's size during resizing
    void resize(int X, int Y, int W, int H) {
        Fl_Scroll::resize(X,Y,W,H);
        if ( pack->horizontal() ) pack->size(pack->w(), H); // Horiz pack? follow height
        else                      pack->size(W, pack->h()); // Vertical pack? follow width
        init_sizes();
    }
};



int main() {
    Fl_Window win(200, 200, 500, 300, "Pack vs Group Overdraw");

    MyScrollPack my_pack(50,50,100,200,"myscrollpack");
      my_pack.color(FL_BLUE);
      my_pack.box(FL_FLAT_BOX);

      Fl_Box frame(200,50,100,100, "Test1");
      frame.box(FL_FLAT_BOX);
      frame.color(FL_YELLOW);
    my_pack.end();

Webb Hinton

unread,
Jul 14, 2021, 9:52:13 PM7/14/21
to fltk.general
Looks like someone may have already done the work for us?

Albrecht Schlosser

unread,
Jul 15, 2021, 8:14:27 AM7/15/21
to fltkg...@googlegroups.com
On 7/15/21 3:52 AM Webb Hinton wrote:
> Looks like someone may have already done the work for us?
>
> https://github.com/osen/FL_Flex <https://github.com/osen/FL_Flex>

FTR: I opened an issue with a statement to consider Fl_Flex for
inclusion in FLTK:
"Add a flexible row/column container widget"
https://github.com/fltk/fltk/issues/255

I also opened a separate issue "Consider to add a simple grid container
widget"
https://github.com/fltk/fltk/issues/256

The latter is meant as a reminder to think about this. I don't know if
this is really useful as an additional widget.

Mohammed

unread,
Jul 21, 2021, 5:23:00 PM7/21/21
to fltkg...@googlegroups.com
I published a Rust crate based on Greg’s example:

Each file defines a single theme. Themes can also be defined inline as data (an array or vec of ColorMap structs)

Webb Hinton

unread,
Jul 27, 2021, 4:04:49 PM7/27/21
to fltk.general
Wonderful work !
Reply all
Reply to author
Forward
0 new messages