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:
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.
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 visiblelet 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 framelet mut frame = Frame::new(250,100,100,100, "Test");
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();
}
Changing the frame's position to be inside the Pack didn't have an effect.
#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();
}
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.
[..] 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();