Discussion:
FLTK-1.3 group button menus in wrong placewherearranged
Greg Ercolano
2013-04-08 16:02:52 UTC
Permalink
//hhhhhhhhhhhhhhhhhhhhhhhhhH
int Sbut::handle ( int event )
{
if ( event == FL_PUSH )
{
cout << "Sbut handle 1: PUSH " << endl;
do_callback();
return 1;
}
return 0;
}
Also, unrelated, the above bit of code should be repaired.

As it is, the button's own handle() method is being completely eclipsed.
Fl_Button::handle(event); should really be called in there somewhere
to allow the button's mechanics to operate.

If the purpose of this handle() is just to print a message on PUSH, then:

int Sbut::handle ( int event )
{
if ( event == FL_PUSH )
cout << "Sbut handle 1: PUSH " << endl;
return(Fl_Button::handle(event);
}

..and the button's own code will handle the do_callback() on PUSH.
marty moore
2013-04-08 15:58:20 UTC
Permalink
Hi,
Thank you very much for explaining the problem fully. I really appreciate that you took the time to analyze it, make suggestions, and even play with my code.

You have really impressed me with the quality of help available on the fltk forum.

Thanks again,
Marty
Yep, this was suggested as a workaround only, either to see how it
can be done (just for learning), or if one really wants to use
Fl_Pack. Deriving from Fl_Pack (as I assume you meant above) for
another container widget and do the polling makes things even worse,
but I assume that you only wrote it for the same reasons as I did.
Yes, indeed.
I think the better thing is to make your own container based on Fl_Group, since that gives more control over how the "packed" widgets will finally be drawn anyway...
Also, more or less as an aside, I think your (Albrecht's) analysis of how Fl_Pack is "causing" this issue is correct - since I was already poking at marty's code, I tweaked it to use a timer as you had suggested (only for the purposes of experiment of course!) and the code now "works as expecetd"...
----------
// marty - button add example
#include <iostream>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <FL/Fl.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Menu_Button.H>
#include <FL/Fl_Menu_Item.H>
#include <FL/Fl_Pack.H>
#include <FL/Fl_Window.H>
using std::cout;
using std::endl;
using std::string;
static Fl_Pack* G1;
static Fl_Window* WIN;
static int idx = 0;
static Fl_Color LBG = FL_DARK_BLUE;
static Fl_Color LFG = FL_WHITE;
static Fl_Color LSC = FL_BLUE;;
static Fl_Color LTC = FL_WHITE;
static Fl_Font LFONT = (FL_HELVETICA | FL_BOLD);
static Fl_Fontsize FSIZE = 20;
static int LSPC = 5;
enum eop { DEL=-1, ADD=1 };
class Sbut : public Fl_Menu_Button
{
Sbut (int i);
static void onButton ( Fl_Widget* w, void* v);
int handle ( int event );
int i() { return _i; }
int _i;
};
//ffffffffffffffffffffffffffff
void clearTo ( int idx )
{
cout << "clearto " << idx << ": ";
Fl_Widget* w;
int last = G1->children()-1;
if ( last > idx )
cout << " removing ";
{
// remove last to idx
for ( int i=last; i>-1; i-- )
{
if ( i != idx )
{
cout << i << " ";
w = G1->child(i);
G1->remove(w);
delete w;
}
}
}
WIN->redraw();
cout << endl;
}
//////////////////////////////
static void pop_on_timeout(void *v)
{
Sbut* sb = (Sbut*)v;
WIN->redraw();
cout << "pop_on_timeout: popping menu" << endl;
sb->popup();
}
//hhhhhhhhhhhhhhhhhhhhhhhhhhhh
void onMenu (Fl_Widget* w, void* v)
{
eop op = (eop)fl_intptr_t(v);
Sbut* sb;
if ( op == ADD )
{
cout << "onMenu: add" << endl;
sb = new Sbut(idx);
G1->add(sb);
idx++;
Fl::add_timeout(0.2, pop_on_timeout, (void*)sb);
}
else if ( op == DEL )
{
cout << "onMenu 2: deleting" << endl;
int i = G1->find(w);
clearTo(i);
sb = (Sbut*)G1->child(0);
Fl::add_timeout(0.2, pop_on_timeout, (void*)sb);
}
}
//mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
Fl_Menu_Item xMenu[] = {
{"add button to end, and popup menu", 0, onMenu, (void*)ADD},
{"delete other buttons, and popup menu", 0, onMenu, (void*)DEL},
{0}
};
//cccccccccccccccccccccccccc
Sbut::Sbut (int id )
: Fl_Menu_Button( 0, 0, 100, 50)
{
// cout << "Sbut(s) 0: " << id << endl;
_i = idx;
callback(onButton);
char _cstr[16];
sprintf(_cstr, "%i", id);
copy_label(_cstr);
// cout << "label = " << label() << endl;
color( LBG, LSC);
labelcolor(LFG);
labelfont(LFONT);
labelsize(FSIZE);
menu(xMenu);
if ( menu() )
{
Fl_Menu_Item* mi; // non-const pointer
mi = (Fl_Menu_Item*)menu();
int sz = mi->size();
for ( int j=0; j<sz; j++ )
{
// cout << "set: " << j << endl;
color(LBG, LSC);
mi->labelsize(FSIZE);
mi->labelfont(LFONT);
mi->labelcolor(LFG);
mi = mi->next();
}
}
}
//hhhhhhhhhhhhhhhhhhhhhhhhhH
int Sbut::handle ( int event )
{
if ( event == FL_PUSH )
{
cout << "Sbut handle 1: PUSH " << endl;
do_callback();
return 1;
}
return 0;
}
//hhhhhhhhhhhhhhhhhhhhhhhhhH
void Sbut::onButton ( Fl_Widget* w, void* v)
{
int i = G1->find(w);
cout << "Sbut onButton 0: " << i << endl;
Sbut* sb = (Sbut*)w;
sb->popup();
}
//***********************
int main (int argc, char **argv)
{
Sbut* sb;
WIN = new Fl_Window(0, 0, 200, 300);
G1 = new Fl_Pack(0, 0, 150, 150);
G1->begin();
for ( int i=0; i<5; i++ )
{
sb = new Sbut(i);
idx++;
}
G1->end();
WIN->end();
// WIN->resizable(G1);
WIN->resizable(WIN);
WIN->show(argc, argv);
return Fl::run();
}
/* end of file */
Ian MacArthur
2013-04-08 11:05:43 UTC
Permalink
Yep, this was suggested as a workaround only, either to see how it
can be done (just for learning), or if one really wants to use
Fl_Pack. Deriving from Fl_Pack (as I assume you meant above) for
another container widget and do the polling makes things even worse,
but I assume that you only wrote it for the same reasons as I did.
Yes, indeed.
I think the better thing is to make your own container based on Fl_Group, since that gives more control over how the "packed" widgets will finally be drawn anyway...


Also, more or less as an aside, I think your (Albrecht's) analysis of how Fl_Pack is "causing" this issue is correct - since I was already poking at marty's code, I tweaked it to use a timer as you had suggested (only for the purposes of experiment of course!) and the code now "works as expecetd"...

----------

// marty - button add example

#include <iostream>
#include <string>
#include <cstdio>
#include <cstdlib>

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

using std::cout;
using std::endl;
using std::string;

static Fl_Pack* G1;
static Fl_Window* WIN;

static int idx = 0;

static Fl_Color LBG = FL_DARK_BLUE;
static Fl_Color LFG = FL_WHITE;
static Fl_Color LSC = FL_BLUE;;
static Fl_Color LTC = FL_WHITE;
static Fl_Font LFONT = (FL_HELVETICA | FL_BOLD);
static Fl_Fontsize FSIZE = 20;
static int LSPC = 5;

enum eop { DEL=-1, ADD=1 };

class Sbut : public Fl_Menu_Button
{
public:
Sbut (int i);

static void onButton ( Fl_Widget* w, void* v);
int handle ( int event );
int i() { return _i; }

int _i;
};

//ffffffffffffffffffffffffffff
void clearTo ( int idx )
{
cout << "clearto " << idx << ": ";
Fl_Widget* w;

int last = G1->children()-1;
if ( last > idx )
cout << " removing ";
{
// remove last to idx
for ( int i=last; i>-1; i-- )
{
if ( i != idx )
{
cout << i << " ";
w = G1->child(i);
G1->remove(w);
delete w;
}
}
}
WIN->redraw();
cout << endl;
}

//////////////////////////////
static void pop_on_timeout(void *v)
{
Sbut* sb = (Sbut*)v;
WIN->redraw();
cout << "pop_on_timeout: popping menu" << endl;
sb->popup();
}

//hhhhhhhhhhhhhhhhhhhhhhhhhhhh
void onMenu (Fl_Widget* w, void* v)
{
eop op = (eop)fl_intptr_t(v);
Sbut* sb;

if ( op == ADD )
{
cout << "onMenu: add" << endl;
sb = new Sbut(idx);
G1->add(sb);
idx++;
Fl::add_timeout(0.2, pop_on_timeout, (void*)sb);
}
else if ( op == DEL )
{
cout << "onMenu 2: deleting" << endl;
int i = G1->find(w);
clearTo(i);
sb = (Sbut*)G1->child(0);
Fl::add_timeout(0.2, pop_on_timeout, (void*)sb);
}
}

//mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
Fl_Menu_Item xMenu[] = {
{"add button to end, and popup menu", 0, onMenu, (void*)ADD},
{"delete other buttons, and popup menu", 0, onMenu, (void*)DEL},
{0}
};


//cccccccccccccccccccccccccc
Sbut::Sbut (int id )
: Fl_Menu_Button( 0, 0, 100, 50)
{
// cout << "Sbut(s) 0: " << id << endl;
_i = idx;
callback(onButton);
char _cstr[16];
sprintf(_cstr, "%i", id);
copy_label(_cstr);
// cout << "label = " << label() << endl;
color( LBG, LSC);
labelcolor(LFG);
labelfont(LFONT);
labelsize(FSIZE);
menu(xMenu);
if ( menu() )
{
Fl_Menu_Item* mi; // non-const pointer
mi = (Fl_Menu_Item*)menu();
int sz = mi->size();
for ( int j=0; j<sz; j++ )
{
// cout << "set: " << j << endl;
color(LBG, LSC);
mi->labelsize(FSIZE);
mi->labelfont(LFONT);
mi->labelcolor(LFG);
mi = mi->next();
}
}
}

//hhhhhhhhhhhhhhhhhhhhhhhhhH
int Sbut::handle ( int event )
{
if ( event == FL_PUSH )
{
cout << "Sbut handle 1: PUSH " << endl;
do_callback();
return 1;
}
return 0;
}

//hhhhhhhhhhhhhhhhhhhhhhhhhH
void Sbut::onButton ( Fl_Widget* w, void* v)
{
int i = G1->find(w);
cout << "Sbut onButton 0: " << i << endl;
Sbut* sb = (Sbut*)w;
sb->popup();
}

//***********************
int main (int argc, char **argv)
{
Sbut* sb;
WIN = new Fl_Window(0, 0, 200, 300);
G1 = new Fl_Pack(0, 0, 150, 150);
G1->begin();
for ( int i=0; i<5; i++ )
{
sb = new Sbut(i);
idx++;
}
G1->end();
WIN->end();
// WIN->resizable(G1);
WIN->resizable(WIN);
WIN->show(argc, argv);
return Fl::run();
}

/* end of file */
marty moore
2013-04-11 18:12:56 UTC
Permalink
Post by Greg Ercolano
//hhhhhhhhhhhhhhhhhhhhhhhhhH
int Sbut::handle ( int event )
{
if ( event == FL_PUSH )
{
cout << "Sbut handle 1: PUSH " << endl;
do_callback();
return 1;
}
return 0;
}
Also, unrelated, the above bit of code should be repaired.
As it is, the button's own handle() method is being completely eclipsed.
Fl_Button::handle(event); should really be called in there somewhere
to allow the button's mechanics to operate.
int Sbut::handle ( int event )
{
if ( event == FL_PUSH )
cout << "Sbut handle 1: PUSH " << endl;
return(Fl_Button::handle(event);
}
..and the button's own code will handle the do_callback() on PUSH.
Hi again,
I've been playing with your suggestions and,, comments on my example program.
Here's what I've found:

1. The resetting of all the buttons (Sbut::resetAll) works fine, but only if 'sb->popup()'
isn't called at the end of the 'onMenu' function. If I call 'sb->popup()', then the menus aren't quite in the right place, although you can see half the button that owns the menu. The 'fl_add_timeout' method is the only thing that seemed to work, so the reviesed example program (beloe) has both methods. The automatic popping up of the next menu is important for my application's design.

2. This program still uses Fl_Pack. I had originally tried Fl_Pack to see what it would do. When changed over to Fl_Group, buttons lower in the window don't respond properly. It seems like the clicked button and above are the only responding buttons. I quit playing with Fl_Group since I couldn't remedy the unpredictable buttons.

3. I've removed the 'Sbut::handle' method since all it did in this program was to tell me that the buttons weren't responding. Thanks for the edit. In my application, though, I need to do some stuff before 'popup()' is called, so I carried it over from my app.

I'm applying what I've learned to my application.
Thanks again for your efforts.
Marty

revised example program:
****************************

#include <iostream>
#include <string>
#include <cstdio>
#include <cstdlib>

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

using std::cout;
using std::endl;
using std::string;


class Grp;
static Grp* BG;
static Fl_Window* WIN;

static int idx = 0;

static Fl_Color LBG = FL_DARK_BLUE;
static Fl_Color LFG = FL_WHITE;
static Fl_Color LSC = FL_BLUE;;
static Fl_Color LTC = FL_WHITE;
static Fl_Font LFONT = (FL_HELVETICA | FL_BOLD);
static Fl_Fontsize FSIZE = 30;
static int LSPC = 2;

enum eop { DEL=-1, ADD=1 };

class Sbut : public Fl_Menu_Button
{
public:
Sbut (int i);

static void pop_on_timeout(void *v);

int _lw, _lh;
};

//**************************************
class Grp : public Fl_Pack
{
public:
Grp (int x, int y, int w, int h);
~Grp() {}

void clearTo ( int idx );
void print (string s);
void resetAll ();
};

//cccccccccccccccccccccccccccccccccccccc
Grp::Grp (int x, int y, int w, int h)
: Fl_Pack(x,y,w,h)
{
cout << "Grp 0:" << endl;
}

//ffffffffffffffffffffffffffffffffffffff
void Grp::print (string s)
{
int lw, lh;
cout << "Grp print: " << s << endl;
for ( int i=0; i<children(); i++ )
{
child(i)->measure_label(lw,lh);
cout << i << ": " << child(i)->x() << " "
<< child(i)->y() << " "
<< child(i)->w() << " "
<< child(i)->h()
<< "| "
<< lw << ": " << lh
<< " " << 6*lw << ": " << 2*lh
<< endl;
}
}

//ffffffffffffffffffffffffffffffffffffff
void Grp::resetAll ()
{
cout << "resetAll 0:" << endl;

int ih = child(0)->h();
int hh = ih;
int ww = 0;
int yy = -ih;
int xx = 0;


for ( int i=0; i<children(); i++ )
{
if ( child(i)->w() > ww )
ww = child(i)->w();
}
for ( int i=0; i<children(); i++ )
{
if ( BG->child(i)->w() > ww )
ww = BG->child(i)->w();
yy += ih + LSPC;
child(i)->resize( LSPC, yy, ww, hh);
}
resize( 0, 0, ww, yy);
WIN->resize( 0, 0, ww+2*LSPC, yy+ih+LSPC);
print("resetAll 99:");
}

//ffffffffffffffffffffffffffff
void Grp::clearTo ( int idx )
{
cout << "Grp clearto " << idx << ": ";
Fl_Widget* w;

int last = BG->children()-1;
if ( last > idx )
cout << " removing ";
{
// remove last to idx
for ( int i=last; i>-1; i-- )
{
if ( i != idx )
{
cout << i << " ";
w = BG->child(i);
BG->remove(w);
delete w;
}
}
}
WIN->redraw();
cout << endl;
}


//static void pop_on_timeout(void *v)
void Sbut::pop_on_timeout(void *v)
{
Sbut* sb = (Sbut*)v;
// WIN->redraw();
cout << "pop_on_timeout: popping menu" << endl;
sb->popup();
}

//hhhhhhhhhhhhhhhhhhhhhhhhhhhh
void onMenu (Fl_Widget* w, void* v)
{
eop op = (eop)fl_intptr_t(v);
Sbut* sb;

if ( op == ADD )
{
cout << "onMenu: add" << endl;
sb = new Sbut(idx);
BG->add(sb);
idx++;
BG->resetAll();
}
else if ( op == DEL )
{
cout << "onMenu 2: deleting" << endl;
int i = BG->find(w);
BG->clearTo(i);
sb = (Sbut*)BG->child(0);
BG->resetAll();
}
// sb->popup();
Fl::add_timeout(0.2, Sbut::pop_on_timeout,
(void*)sb);

}

//mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
Fl_Menu_Item xMenu[] = {
{"add button to end, and popup menu", 0, onMenu, (void*)ADD},
{"delete other buttons, and popup menu", 0, onMenu, (void*)DEL},
{0}
};


//cccccccccccccccccccccccccc
Sbut::Sbut (int id )
: Fl_Menu_Button(0, 0, 5*FSIZE, 2*FSIZE)
{
char _cstr[16];
sprintf(_cstr, "%i", id);
copy_label(_cstr);
// cout << "label = " << label() << endl;
color( LBG, LSC);
labelcolor(LFG);
labelfont(LFONT);
labelsize(FSIZE);
measure_label(_lw, _lh);
size(5*_lw, int(1.3*_lh));
menu(xMenu);
if ( menu() )
{
Fl_Menu_Item* mi; // non-const pointer
mi = (Fl_Menu_Item*)menu();
int sz = mi->size();
for ( int j=0; j<sz; j++ )
{
// cout << "set: " << j << endl;
color(LBG, LSC);
mi->labelsize(FSIZE);
mi->labelfont(LFONT);
mi->labelcolor(LFG);
mi = mi->next();
}
}
}

//***********************
int main (int argc, char **argv)
{
Sbut* sb;
WIN = new Fl_Window(0, 0, 200, 300);
BG = new Grp(0, 0, 150, 150);
for ( int i=0; i<5; i++ )
{
sb = new Sbut(i);
idx++;
}
BG->end();
WIN->end();
BG->resetAll();
WIN->show(argc, argv);
return Fl::run();
}
MacArthur, Ian (Selex ES, UK)
2013-04-12 08:46:19 UTC
Permalink
Post by marty moore
2. This program still uses Fl_Pack. I had originally tried Fl_Pack to
see what it would do. When changed over to Fl_Group, buttons lower in
the window don't respond properly. It seems like the clicked button and
above are the only responding buttons. I quit playing with Fl_Group
since I couldn't remedy the unpredictable buttons.
Sounds like you have put the buttons outside the bounding box of the group, so they don't get any key clicks.

You need to ensure the parent group is sized to properly contain its children, or Bad Things can happen.



Selex ES Ltd
Registered Office: Sigma House, Christopher Martin Road, Basildon, Essex SS14 3EL
A company registered in England & Wales. Company no. 02426132
********************************************************************
This email and any attachments are confidential to the intended
recipient and may also be privileged. If you are not the intended
recipient please delete it from your system and notify the sender.
You should not copy it or use it for any purpose nor disclose or
distribute its contents to any other person.
********************************************************************
Albrecht Schlosser
2013-04-12 10:40:14 UTC
Permalink
Post by MacArthur, Ian (Selex ES, UK)
Post by marty moore
2. This program still uses Fl_Pack. I had originally tried Fl_Pack to
see what it would do. When changed over to Fl_Group, buttons lower in
the window don't respond properly. It seems like the clicked button and
above are the only responding buttons. I quit playing with Fl_Group
since I couldn't remedy the unpredictable buttons.
Sounds like you have put the buttons outside the bounding box of the group, so they don't get any key clicks.
You need to ensure the parent group is sized to properly contain its children, or Bad Things can happen.
That's exactly what I had thought when reading this.

To Marty: please try again with Fl_Group, because Fl_Pack won't do
what you want, unless you use Fl::add_timeout() for the popup.

Besides looking at the correct bounding box issue, you must only
calculate the new button coordinates after changing children inside
the group. Only if you want predictable resizing behavior, then you
must also call init_sizes() after rearranging children. That should
really be easy to implement...

Albrecht

Duncan Gibson
2013-04-12 07:26:13 UTC
Permalink
Post by marty moore
2. This program still uses Fl_Pack. I had originally tried Fl_Pack
to see what it would do. When changed over to Fl_Group, buttons
lower in the window don't respond properly. It seems like the
clicked button and above are the only responding buttons.I quit
playing with Fl_Group since I couldn't remedy the unpredictable
buttons.
Were [some of] the dynamically added buttons outside the Fl_Group's
initial bounding box by any chance? Did you resize the bounding box?
Depending on how you add the buttons, you might need init_sizes() too.

D.
Loading...