Understanding Fl_Pack.

55 views
Skip to first unread message

Gonzalo Garramuno

unread,
Oct 2, 2022, 6:01:55 PM10/2/22
to fltkg...@googlegroups.com
I have used Fl_Pack in the past and I always struggle with its behavior.  

In the sample attached demo program I have an Fl_Pack that contains an Fl_Gl_Window widget and a normal Fl_Group inside an Fl_Pack.  

I would like the Fl_Gl_Window widget to stretch and fill the Fl_Pack whenever the Fl_Group is hidden when the Hide button is clicked.  How can I do that?


#include <iostream>

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Pack.H>
#include <FL/Fl_Group.H>
#include <FL/math.h>

#include <FL/gl.h>
#include <FL/Fl_Gl_Window.H>



class shape_window : public Fl_Gl_Window {
  void draw();
public:
  int sides;
  shape_window(int x,int y,int w,int h,const char *l=0);
    void resize( int X,int Y,int W,int H )
        {
            std::cerr << "win resize W=" << W << std::endl;
            Fl_Gl_Window::resize( X, Y, W, H );
        }
};

shape_window::shape_window(int x,int y,int w,int h,const char *l) :
Fl_Gl_Window(x,y,w,h,l) {
  sides = 3;
}

void shape_window::draw() {
  if (!valid()) {
    valid(1);
    glLoadIdentity();
    glViewport(0,0,pixel_w(),pixel_h());
  }
// draw an amazing but slow graphic:
  glClear(GL_COLOR_BUFFER_BIT);
  glColor3f( 0.5, 0.5, 0.75 );

  glBegin(GL_POLYGON);
  for (int j=0; j<sides; j++) {
      double ang = j*2*M_PI/sides;
      glVertex3f((GLfloat)cos(ang), (GLfloat)sin(ang), 0);
  }
  glEnd();
}

// when you change the data, as in this callback, you must call redraw():
void hide_cb(Fl_Widget *o, void *p) {
  Fl_Group *d = (Fl_Group *)p;
  if ( d->visible() ) {
      std::cerr << "hide grp= " << d << std::endl;
      d->hide();
  }
  else {
      std::cerr << "show grp= " << d << std::endl;
      d->show();
  }
  Fl_Pack* pack = (Fl_Pack*)d->parent();
  std::cerr << "pack= " << pack << std::endl;
  pack->init_sizes();
  pack->redraw();
}

int main(int argc, char **argv) {

  Fl::use_high_res_GL(1);
  Fl_Window window(300, 370);
  window.begin();

  Fl_Pack pack(10, 75, 280, 280);
  std::cerr << "pack= " << &pack << std::endl;
  pack.type( Fl_Pack::HORIZONTAL );
  pack.color( FL_CYAN );
  //pack.box( FL_FLAT_BOX );
  pack.begin();

  int H = window.h()-90;
  int W = pack.w()/2;

  shape_window sw(10, 75, W, H );
  sw.end();

  Fl_Group     grp( 0, 75, W, H );
  std::cerr << "grp= " << &grp << std::endl;
  grp.color( FL_RED );
  grp.box( FL_FLAT_BOX );
  grp.end();



  pack.resizable(&sw);
  pack.end();



  Fl_Button button(30, 5, W, 30, "Hide");
  button.callback( hide_cb, &grp );

  window.resizable( &pack );

  window.end();
  window.show(argc,argv);


  return Fl::run();
}



Gonzalo Garramuno
ggar...@gmail.com




Greg Ercolano

unread,
Oct 2, 2022, 7:22:36 PM10/2/22
to fltkg...@googlegroups.com

On 10/2/22 15:01, Gonzalo Garramuno wrote:

I have used Fl_Pack in the past and I always struggle with its behavior. 

    It's probably not the right fit for what you want, due to how it shrink-wraps
    itself around the gl group after you hide the red group, causing the gl group
    to remain its previous size, instead of expanding to fit.

    From the Fl_Pack docs, I believe it's the red text that is I think what's defeating you here:

This widget was designed to add the functionality of compressing and aligning widgets.
If type() is Fl_Pack::HORIZONTAL all the children are resized to the height of the Fl_Pack,
and are moved next to each other horizontally. [..]
Then the Fl_Pack resizes itself to surround the child widgets.

    ..which is why when you hide the red group, the gl group doesn't enlarge,
    but instead Fl_Pack shrinks around it.

    The reason for that behavior is the intended use inside an Fl_Scroll, where the
    pack would be the immediate child, so that the scrollbars adjust automatically
    when a widget in the pack is removed/hidden.

    I'm thinking for your simple widget arrangement, it might be easiest to not use
    an Fl_Pack group at all, just use a regular group, using your existing show/hide
    toggle for the red box, and just add a line to resize() the gl window to either full
    or 1/2 it's parent's group size to get the expansion behavior you want.

    You might be able to use Albrecht's new Fl_Flex widget (new in 1.4.x) for this,
    I'm not sure. He can probably advise.

Greg Ercolano

unread,
Oct 2, 2022, 8:00:00 PM10/2/22
to fltkg...@googlegroups.com
I would like the Fl_Gl_Window widget to stretch and fill the Fl_Pack whenever the Fl_Group is hidden when the Hide button is clicked.  How can I do that?

    If for some reason you really need to use Fl_Pack, I only made a few
    small changes here to keep its use, the important changes highlighted in green.
    Had to make some widgets global (shown in magenta).



#include <iostream>

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Pack.H>
#include <FL/Fl_Group.H>
#include <FL/math.h>

#include <FL/gl.h>
#include <FL/Fl_Gl_Window.H>

Fl_Pack *G_pack = 0;    // global pack (since parenting of red grp changes)



class shape_window : public Fl_Gl_Window {
  void draw();
public:
  int sides;
  shape_window(int x,int y,int w,int h,const char *l=0);

};

shape_window *G_sw = 0;



shape_window::shape_window(int x,int y,int w,int h,const char *l) :
Fl_Gl_Window(x,y,w,h,l) {
  sides = 3;
}

void shape_window::draw() {
  if (!valid()) {
    valid(1);
    glLoadIdentity();
    glViewport(0,0,pixel_w(),pixel_h());
  }
  // draw an amazing but slow graphic:
  glClear(GL_COLOR_BUFFER_BIT);
  glColor3f( 0.5, 0.5, 0.75 );

  glBegin(GL_POLYGON);
  for (int j=0; j<sides; j++) {
      double ang = j*2*M_PI/sides;
      glVertex3f((GLfloat)cos(ang), (GLfloat)sin(ang), 0);
  }
  glEnd();
}

// when you change the data, as in this callback, you must call redraw():
void hide_cb(Fl_Widget *o, void *p) {
  Fl_Group *d = (Fl_Group *)p;

  Fl_Window *win = d->top_window();



  if ( d->visible() ) {

      std::cerr << "hide red= " << d << std::endl;
      win->add(d);    // move out of pack -> window group
      d->hide();      // and hide() until needed
  } else {
      std::cerr << "show red= " << d << std::endl;
      d->show();      // show and..
      G_pack->add(d); // move out of window group and back into G_pack
      G_sw->size(G_pack->w()/2, G_sw->h());   // force gl back to 1/2 size

  }
}

int main(int argc, char **argv) {

  Fl::use_high_res_GL(1);
  Fl_Window window(300, 370);
  window.begin();

  G_pack = new Fl_Pack(10, 75, 280, 280);
  std::cerr << "G_pack= " << G_pack << std::endl;
  G_pack->type( Fl_Pack::HORIZONTAL );
  G_pack->color( FL_CYAN );
  //G_pack->box( FL_FLAT_BOX );
  G_pack->begin();

  int H = window.h()-90;
  int W = G_pack->w()/2;

  G_sw = new shape_window(10, 75, W, H );
  G_sw->end();

  Fl_Group red( 0, 75, W, H );
  std::cerr << "red= " << &red << std::endl;
  red.color( FL_RED );
  red.box( FL_FLAT_BOX );
  red.end();

  G_pack->resizable(G_sw);
  G_pack->end();



  Fl_Button button(30, 5, W, 30, "Hide");

  button.callback( hide_cb, &red );

  window.resizable( G_pack );

Greg Ercolano

unread,
Oct 2, 2022, 8:31:12 PM10/2/22
to fltkg...@googlegroups.com

On 10/2/22 15:01, Gonzalo Garramuno wrote:

I have used Fl_Pack in the past and I always struggle with its behavior. 

    One last followup:

    I noticed this bit in the Fl_Pack docs:
The 'resizable()' for Fl_Pack is set to NULL by default. Its behavior is slightly different
than in a normal
Fl_Group widget: only if the resizable() widget is the last widget
in the group it is extended to take the full available width or height, respectively,
of the
Fl_Pack group.
    ..which means if your gl window were added to the pack last, instead of first,
   
you'd get the behavior you want with the stretchy GL window. Then your show()/hide()
    stuff works unmodified. See the below code for proof of concept.

    This change in child creation order does, however, put your GL window
    to the right of the pack instead of the left, which may be bad for your UX.
    Perhaps Fl_Pack can be modified (an RFE) to allow an option flag that would
    allow this stretch behavior with the pack's resizable() regardless of what order
    it appears in the group's hierarchy.




#include <iostream>

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Pack.H>
#include <FL/Fl_Group.H>
#include <FL/math.h>

#include <FL/gl.h>
#include <FL/Fl_Gl_Window.H>

Fl_Pack *G_pack = 0;    // global pack (since parenting of red grp changes)

class shape_window : public Fl_Gl_Window {
  void draw();
public:
  int sides;
  shape_window(int x,int y,int w,int h,const char *l=0);
};

shape_window *G_sw = 0;


shape_window::shape_window(int x,int y,int w,int h,const char *l) :
Fl_Gl_Window(x,y,w,h,l) {
  sides = 3;
}

void shape_window::draw() {
  if (!valid()) {
    valid(1);
    glLoadIdentity();
    glViewport(0,0,pixel_w(),pixel_h());
  }
  // draw an amazing but slow graphic:
  glClear(GL_COLOR_BUFFER_BIT);
  glColor3f( 0.5, 0.5, 0.75 );

  glBegin(GL_POLYGON);
  for (int j=0; j<sides; j++) {
      double ang = j*2*M_PI/sides;
      glVertex3f((GLfloat)cos(ang), (GLfloat)sin(ang), 0);
  }
  glEnd();
}

// when you change the data, as in this callback, you must call redraw():
void hide_cb(Fl_Widget *o, void *p) {
  Fl_Group *d = (Fl_Group *)p;
  Fl_Window *win = d->top_window();

  if ( d->visible() ) {
      std::cerr << "hide red= " << d << std::endl;
      d->hide();      // simple hide() of red group
  } else {
      std::cerr << "show red= " << d << std::endl;
      d->show();      // simple show() of red group
  }
  G_pack->init_sizes();

}

int main(int argc, char **argv) {

  Fl::use_high_res_GL(1);
  Fl_Window window(300, 370);
  window.begin();

  G_pack = new Fl_Pack(10, 75, 280, 280);
  std::cerr << "G_pack= " << G_pack << std::endl;
  G_pack->type( Fl_Pack::HORIZONTAL );
  G_pack->color( FL_CYAN );
  //G_pack->box( FL_FLAT_BOX );
  G_pack->begin();

  int H = window.h()-90;
  int W = G_pack->w()/2;


  Fl_Group red( 0, 75, W, H );
  std::cerr << "red= " << &red << std::endl;
  red.color( FL_RED );
  red.box( FL_FLAT_BOX );
  red.end();

  // SHAPE_WINDOW MOVED /AFTER/ RED GROUP

  G_sw = new shape_window(10, 75, W, H );
  G_sw->end();

  G_pack->resizable(G_sw);
  G_pack->end();


  Fl_Button button(30, 5, W, 30, "Hide");
  button.callback( hide_cb, &red );

  window.resizable( G_pack );

Albrecht Schlosser

unread,
Oct 3, 2022, 5:52:21 AM10/3/22
to fltkg...@googlegroups.com
On 10/3/22 01:22 Greg Ercolano wrote:

On 10/2/22 15:01, Gonzalo Garramuno wrote:

I have used Fl_Pack in the past and I always struggle with its behavior.

Fl_Pack has some "very special" properties and should only be used if it's exactly what you need. See also Greg's replies in this thread.

IMHO it is often be easier to implement specialized resizing behavior like yours in your own container widget derived from Fl_Group using the resize() method, particularly if you can control addition/removal or hiding/showing of child widgets.

[Greg's reply mostly stripped]



    You might be able to use Albrecht's new Fl_Flex widget (new in 1.4.x) for this,
    I'm not sure. He can probably advise.

Yes, probably. I didn't try Gonzalo's demo program though. But Gonzalo's statement "I would like the Fl_Gl_Window widget to stretch and fill the Fl_Pack whenever the Fl_Group is hidden when the Hide button is clicked" sounds like a good reason to use Fl_Flex.

Anyway, some facts:

As Greg wrote, the Fl_Pack widget "shrink-wraps" around its children and uses a special handling of the resizable() child widget. It applies these special features mostly inside the draw() method which makes its behavior even less predictable (and IMHO usable).

Fl_Flex is designed to be used as a drop-in replacement of Fl_Pack (if you don't use some special Fl_Pack properties and methods). It's mostly source compatible and you can try/view/test this in test/pack.cxx which has been modified to use either an Fl_Pack or an Fl_Flex container inside an Fl_Scroll widget or as a main container.

Fl_Flex's resize behavior is kinda the opposite of Fl_Pack. Fl_Flex uses all the space assigned to it (w, h) and resizes its children to fit according to some rules. If you hide one child widget (of a total of two children) the other one will take the entire space of the parent Fl_Flex container. But beware: Fl_Flex is already functional but not yet 100% complete.

That said, another container (Fl_Grid) is work in progress and will provide a grid (aka matrix) of widgets similar to an HTML <table>. Fl_Grid will be included in 1.4.0 if nothing really bad happens, I'm pretty confident. Fl_Grid may not be what you want though but you can construct an Fl_Grid widget with only one row or one column if you want its particular resizing features which are much more versatile than Fl_Flex.

Fl_Flex and Fl_Grid can be children of another Fl_Group (or derived, even Fl_Flex or Fl_Grid) widget, hence you can achieve complex nested resizing behavior.

imm

unread,
Oct 3, 2022, 5:52:33 AM10/3/22
to fltkg...@googlegroups.com
On Sun, 2 Oct 2022 at 23:01, Gonzalo wrote:
>
> I have used Fl_Pack in the past and I always struggle with its behavior.
>
> In the sample attached demo program I have an Fl_Pack that contains an Fl_Gl_Window widget and a normal Fl_Group inside an Fl_Pack.
>
> I would like the Fl_Gl_Window widget to stretch and fill the Fl_Pack whenever the Fl_Group is hidden when the Hide button is clicked. How can I do that?
>

Hi Gonzalo,
FWIW, I've never been able to get along with the default behaviour of
Fl_Pack, so tend to go with something like Greg's suggestions of
putting things in a group and managing the resize directly as widgets
get added / remove.

Though TBH I never know about the "special" treatment for the "Last"
pack widget - if I had that might have helped a few times!

Albrecht Schlosser

unread,
Oct 3, 2022, 4:37:22 PM10/3/22
to fltkg...@googlegroups.com
On 10/3/22 11:52 Albrecht Schlosser wrote:
On 10/3/22 01:22 Greg Ercolano wrote:

    You might be able to use Albrecht's new Fl_Flex widget (new in 1.4.x) for this,
    I'm not sure. He can probably advise.

Yes, probably. I didn't try Gonzalo's demo program though. But Gonzalo's statement "I would like the Fl_Gl_Window widget to stretch and fill the Fl_Pack whenever the Fl_Group is hidden when the Hide button is clicked" sounds like a good reason to use Fl_Flex.

OK, I had some spare time and tried Gonzalo's demo. I replaced Fl_Pack with Fl_Flex everywhere and changed:

// pack->init_sizes();
pack->layout();
// pack->redraw();

With these minimal changes Gonzalo's program does what I think he wanted to achieve.

Full demo program attached as 'understanding_fl_pack.cxx'.
understanding_fl_pack.cxx

Greg Ercolano

unread,
Oct 3, 2022, 4:43:37 PM10/3/22
to fltkg...@googlegroups.com

On 10/3/22 13:37, Albrecht Schlosser wrote:

OK, I had some spare time and tried Gonzalo's demo. I replaced Fl_Pack with Fl_Flex everywhere and changed:

// pack->init_sizes();
pack->layout();
// pack->redraw();
With these minimal changes Gonzalo's program does what I think he wanted to achieve.
Full demo program attached as 'understanding_fl_pack.cxx'.

    Yep, can confirm that behavior is probably what he wants.

Gonzalo Garramuno

unread,
Oct 17, 2022, 4:41:15 AM10/17/22
to fltkg...@googlegroups.com
Thank you guys for pointing me to Fl_Flex!!! It works great!!! The docs of Fl_Pack should mention Fl_Flex as an alternative I think.


Gonzalo Garramuno
ggar...@gmail.com




Reply all
Reply to author
Forward
0 new messages