Widgetwrap on resize?

33 views
Skip to first unread message

Hannu Vuolasaho

unread,
Jun 22, 2015, 8:49:16 PM6/22/15
to fltkg...@googlegroups.com
Hello!

I have Fl_Tile, and I have smaller widgets in Fl_Scroll. Those smaller widgets are same size.

What I want to do, is to get then nicely line up and have vertical scroll.

This is quite same behavior as word wrap in text but with widgets this time. Has anyone done this?

I got this far, and it starves for refresh()

Best regards,
Hannu Vuolasaho

#include <FL/Fl_Scroll.H>
#include <FL/Fl_Double_Window.H>
#include <vector>

#include <FL/Fl_Box.H>
#include <FL/Fl.H>
class SummaryScroll: public Fl_Scroll {
public:
  SummaryScroll(int X, int Y, int W, int H, const char * L = 0):
    Fl_Scroll(X,Y,W,H,L)
  {
    type(Fl_Scroll::VERTICAL);
  }
  void resize(int X, int Y, int W, int H){
    int childCount = children(), i = 0, inRow;
    int d = 3, dh, dw;
    std::vector<Fl_Widget * >kids;
    //resize me, not kids.
    Fl_Scroll::resize(X,Y,W,H);
    while(i < childCount){
      kids.push_back(child(i));
      i++;
    }
    //Now we have copy of kids.
    dh = kids.front()->h() + d;
    dw = kids.front()->w() + d;
    inRow =  W / dw ;
    if(inRow){
      for(i=0;i< childCount; i++){
        kids[i]->resize( X + (i % inRow) * dw + d, Y + (i / inRow) * dh + d, dw,dh);
      }
     } else {
      for(i=0;i< childCount; i++){
        kids[i]->resize( X, Y + i*dh, W, dh);
      }
     }
    Fl_Scroll::init_sizes();
  }
private:
 
};

int main(int argc, char** argv) {
  Fl_Window window(5*75,400);
  window.box(FL_NO_BOX);
  SummaryScroll scroll(0,0,5*75,300);

  int n = 0;
  for (int y=0; y<16; y++)
    for (int x=0; x<5; x++) {
      char buf[20]; sprintf(buf,"%d",n++);
      Fl_Box* b = new Fl_Box(x*75,y*25+(y>=8?5*75:0),75,25);
      b->copy_label(buf);
      b->color(n);
      b->labelcolor(FL_WHITE);
    }
  scroll.end();
  window.resizable(scroll);
  window.end();
  window.show(argc,argv);
  return Fl::run();
}
/* resize smaller...
|----------------|
| |--| |--| |--| |
| |__| |__| |__| |
|                |
| |--|           |
| |__|           |
|________________|

|-----------|
| |--| |--| |
| |__| |__| |
|           |
| |--| |--| |
| |__| |__| |
|___________|


 */


MacArthur, Ian (Selex ES, UK)

unread,
Jun 23, 2015, 6:57:30 AM6/23/15
to fltkg...@googlegroups.com
> I got this far, and it starves for refresh()

You don't seem to have set any boxtypes for any of your widgets, so it looks like nothing is actually getting drawn.

I don't think refresh is starving, I think it just has nothing to draw.

I couldn't see what your code was doing, so I set some boxtypes and it looks like it is mostly working, though the boxes do grow larger over time; you will need to manage the box sizes better I think.

Here’s my tweaked code, FWIW:

#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Scroll.H>

#include <vector>


class SummaryScroll: public Fl_Scroll {
public:
SummaryScroll(int X, int Y, int W, int H, const char * L = 0):
Fl_Scroll(X,Y,W,H,L)
{
type(Fl_Scroll::VERTICAL);
box(FL_BORDER_BOX);
}

void resize(int X, int Y, int W, int H) {
int childCount = children(),
i = 0,
inRow;
int d = 3,
dh,
dw;
std::vector<Fl_Widget *>kids;
// resize me, not kids.
Fl_Scroll::resize(X,Y,W,H);
while(i < childCount){
kids.push_back(child(i));
i++;
}
// Now we have copy of kids.
dh = kids.front()->h() + d;
dw = kids.front()->w() + d;
inRow = W / dw ;
if(inRow){
for(i=0;i< childCount; i++){
kids[i]->resize( X + (i % inRow) * dw + d, Y + (i / inRow) * dh + d, dw,dh);
}
} else {
for(i=0;i< childCount; i++){
kids[i]->resize( X, Y + i*dh, W, dh);
}
}
Fl_Scroll::init_sizes();
}
private:
};

int main(int argc, char** argv) {
Fl_Window window(5*75,400);
window.box(FL_FLAT_BOX);
SummaryScroll scroll(0,0,5*75,300);

int n = 0;
for (int y=0; y<16; y++)
for (int x=0; x<5; x++) {
char buf[20]; sprintf(buf,"%d",n++);
Fl_Box* b = new Fl_Box(x*75,y*25+(y>=8?5*75:0),75,25);
b->box(FL_BORDER_BOX);
b->copy_label(buf);
b->color(n);
b->labelcolor(FL_WHITE);
}
scroll.end();
window.resizable(scroll);
window.end();
window.show(argc,argv);
return Fl::run();
}

/* end of file */


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.
********************************************************************

MacArthur, Ian (Selex ES, UK)

unread,
Jun 23, 2015, 8:03:52 AM6/23/15
to fltkg...@googlegroups.com
> What I want to do, is to get then nicely line up
> and have vertical scroll.
> This is quite same behavior as word wrap in text
> but with widgets this time. Has anyone done this?

I had another play with your code over lunch; I like this version a bit better:-

#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Scroll.H>

#include <vector>

#define B_WIDTH 75
#define B_HEIGHT 25

class SummaryScroll: public Fl_Scroll
{
public:
SummaryScroll(int X, int Y, int W, int H, const char * L = 0):
Fl_Scroll(X,Y,W,H,L)
{
type(Fl_Scroll::VERTICAL);
box(FL_FLAT_BOX);
}

void resize(int X, int Y, int W, int H)
{
int childCount = children(),
i = 0,
inRow;
int dh,
dw;
std::vector<Fl_Widget*>kids;

// resize me, not kids.
Fl_Scroll::resize(X,Y,W,H);

while(i < childCount)
{
kids.push_back(child(i));
++i;
}
// Now we have copy of kids.
dh = kids.front()->h();
dw = kids.front()->w();
inRow = (W - 15) / dw ; // allow for scrollbar
if (inRow)
{
for(i = 0; i < childCount; ++i)
{
kids[i]->position(1 + X + (i % inRow) * dw, 1 + Y + (i / inRow) * dh);
}
}
else
{
for(i = 0; i < childCount; ++i)
{
kids[i]->position(1 + X + (i * dw), 1 + Y);
}
}
Fl_Scroll::init_sizes();
parent()->redraw();
}
private:
};

int main(int argc, char** argv)
{
Fl_Window window(5 * B_WIDTH, 400);
window.box(FL_FLAT_BOX);
window.begin();

SummaryScroll scroll(0, 0, (5 * B_WIDTH), 350);
scroll.begin();

int n = 0;
for (int y = 0; y < 16; ++y)
{
for (int x = 0; x < 5; ++x)
{
char buf[20];
sprintf(buf, "%d", ++n);
Fl_Box* b = new Fl_Box(x*B_WIDTH, y*B_HEIGHT, B_WIDTH, B_HEIGHT);
b->box(FL_BORDER_BOX);
b->copy_label(buf);
b->color(n % 15);

Albrecht Schlosser

unread,
Jun 23, 2015, 12:32:55 PM6/23/15
to fltkg...@googlegroups.com
I wonder how this takes into account that the scrollbars themselves are
children of the Fl_Scroll widget. I don't see any code doing this.

The scrollbars are exposed by the member vars scrollbar and hscrollbar,
and you should not rely on them being the first or the last widgets in
the group. But you could compare child(i) with scrollbar and hscrollbar,
resp. to "filter" these children, maybe in the loop above that pushes
all children into the vector.

Note that I didn't test the code, but I wanted to mention this fact.

HTH

Hannu Vuolasaho

unread,
Jun 23, 2015, 2:33:08 PM6/23/15
to fltkg...@googlegroups.com
Hello!

First I want to confess sin of copypasta. Last night I copied from
scroll.cxx example and got that bad code. I was thinking if this can
be even done with resize()

Ian's code didn't care about scrollbars and they were position()ed all
the time and still worked.. Strange.

It still doesn't come to resizing child. Maybe Fl_Scroll::resize
should be called so that children would be resized (inRow wold be
zero).

Next I'll try put that class to my program and see how it behaves there.

This is my current solution. I got rid of the vector and got some nice
spacing...

Best regards,
Hannu Vuolasaho

class SummaryScroll: public Fl_Scroll {
public:
SummaryScroll(int X, int Y, int W, int H, const char * L = 0):
Fl_Scroll(X,Y,W,H,L)
{
type(Fl_Scroll::VERTICAL);
}
void resize(int X, int Y, int W, int H){
int childCount = children(), i = 0, inRow;
int d = _spacing, dh, dw;
Fl_Widget::resize(X,Y,W,H);
if(childCount>0){
dw = child(0)->w();
dh = child(0)->h();
inRow = (W - Fl::scrollbar_size() - d) / (dw + d);
d += (W - Fl::scrollbar_size() - 2 * d) / inRow - dw - d;
if(inRow){
for(i = 0; i < childCount; ++i){
if(&hscrollbar == child(i))continue;
if(&scrollbar == child(i))continue;
child(i)->position(1 + d + X + (i % inRow) * (dw + d),
1 + _spacing + Y + (i / inRow) * (dh + _spacing));
}
} else {
// Child is too large for window.
// and actually never run...
for(i = 0; i < childCount; ++i){
child(i)->resize(1 + X + (i % inRow) * dw,
1 + Y + (i / inRow) * dh,
W - Fl::scrollbar_size(),
dh);
}
}
Fl_Scroll::init_sizes();
}
}
private:
const static int _spacing = 3;
};
int main(int argc, char** argv) {
Fl_Double_Window window(5*75,400);
window.box(FL_FLAT_BOX);
SummaryScroll scroll(0,0,5*75,300);

int n = 0;
for (int y=0; y<16; y++)
for (int x=0; x<5; x++) {
char buf[20]; sprintf(buf,"%d",n++);
Fl_Box* b = new Fl_Box(x*75,y*25+(y>=8?5*75:0),75,25);
b->copy_label(buf);
b->color(n);
b->labelcolor(FL_WHITE);
b->box(FL_FRAME_BOX);
}
scroll.end();
window.resizable(scroll);
window.end();
window.show(argc,argv);
return Fl::run();
}
/* end of file */

> --
> 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.
> For more options, visit https://groups.google.com/d/optout.

Ian MacArthur

unread,
Jun 23, 2015, 5:09:11 PM6/23/15
to fltkg...@googlegroups.com
On Tue Jun 23 2015 17:32:52, Albrecht Schlosser wrote:
>
>
> I wonder how this takes into account that the scrollbars themselves are children of the Fl_Scroll widget. I don't see any code doing this.

There isn’t any code doing this - I just took Hannu’s code and poked it to make it “work”...


> The scrollbars are exposed by the member vars scrollbar and hscrollbar, and you should not rely on them being the first or the last widgets in the group. But you could compare child(i) with scrollbar and hscrollbar, resp. to "filter" these children, maybe in the loop above that pushes all children into the vector.

The Fl_Scroll class contrives to ensure that the scrollbars are “always” ordered as the last two children in the list, so what the code *should* do in this test code is ignore the last two children...

Not sure why it appears to work anyway...!



Albrecht Schlosser

unread,
Jun 23, 2015, 6:00:43 PM6/23/15
to fltkg...@googlegroups.com
Well, you quoted "always" and you wrote *should*. Indeed this is not
always the case. One example is when you add() a child to the group.
This method is not overridden by Fl_Scroll, hence new children are
appended to the list. That is why I wrote "you should not rely on them
being the first or the last widgets in the group".

But, taking a look at the code - ... - you are almost right: Fl_Scroll
/tries/ to ensure that the scrollbars are always at the end of the list.
IIRC in the past this was only done in Fl_Scroll::draw(), but now it
appears to be also done in Fl_Scroll::resize() and even in
Fl_Scroll::handle(). See the private method
Fl_Scroll::fix_scrollbar_order(). There have been some fixes in the
past, IIRC done by Greg and maybe myself.

I'm surprised that fix_scrollbar_order() is called in handle(). This
means that every mouse move calls fix_scrollbar_order(). Personally I
believe that this is too much for FLTK's "fast and light" attitude, but
that's how it is currently implemented.

> Not sure why it appears to work anyway...!

Well, Fl_Scroll::resize() calls fix_scrollbar_order() and eventually
redraw(). Fl_Scroll::draw() [re]calculates the scrollbar positions as
needed, and this finally fixes everything. You might see an effect if
the calculation found out that e.g. the scroll was 3 widgets wide and 3
widgets high because there were 5 real widgets + the 2 scrollbars (i.e.
7 = 3 + 3 + 1 = 3 rows). However, after fixing the scrollbars, there
would be an empty space in row #3 because there are only 3 + 2 = 5 real
widgets. Or something like that.

In the current example code Fl_Scroll::resize() was not called, but
draw() would always position the scrollbars in the right place, and
handle() would fix the order of children in the group.


How to proceed: first of all: fix_scrollbar_order() is private (should
probably be protected so derived classes can use it). Everything else is
/current/ implementation details and may be changed in the future.

That said, as it is today, I suggest to call Fl_Scroll::resize() instead
of Fl_Widget::resize() to make sure fix_scrollbar_order() was called
/before/ repositioning the (n-2) widgets and finally calling
init_sizes() as was already done in the code I saw. Using all children
(instead of n-2) and comparing if one is scrollbar or hscrollbar would
make it bullet-proof - at least for the foreseeable future.

Hannu Vuolasaho

unread,
Jun 23, 2015, 6:35:24 PM6/23/15
to fltkg...@googlegroups.com
2015-06-24 1:00 GMT+03:00 Albrecht Schlosser <Albrech...@online.de>:

> That said, as it is today, I suggest to call Fl_Scroll::resize() instead of
> Fl_Widget::resize() to make sure fix_scrollbar_order() was called /before/
> repositioning the (n-2) widgets and finally calling init_sizes() as was
> already done in the code I saw. Using all children (instead of n-2) and
> comparing if one is scrollbar or hscrollbar would make it bullet-proof - at
> least for the foreseeable future.

I have the compare in my for loop :)

I'm still curious. Fl_Scroll::resize() documentation says widgets are moved
first and then resized and resizing is thing I want to avoid until
they don't fit
horizontaly. Also vertical should just go to scroll. Or is this just another bad
documentation which should be rewritten?

Anyway, I'll switch to right resize() function.

Maybe there should be Fl_Tetris class, which would keep children sizes and
put them tightly together and scroll until resizing would be required...

Best regards,
Hannu Vuolasaho

Albrecht Schlosser

unread,
Jun 23, 2015, 7:44:38 PM6/23/15
to fltkg...@googlegroups.com
On 24.06.2015 00:35 Hannu Vuolasaho wrote:
> 2015-06-24 1:00 GMT+03:00 Albrecht Schlosser <...>:

Please remove email addresses when posting (don't feed the spam bots).
Thanks.

>> That said, as it is today, I suggest to call Fl_Scroll::resize() instead of
>> Fl_Widget::resize() to make sure fix_scrollbar_order() was called /before/
>> repositioning the (n-2) widgets and finally calling init_sizes() as was
>> already done in the code I saw. Using all children (instead of n-2) and
>> comparing if one is scrollbar or hscrollbar would make it bullet-proof - at
>> least for the foreseeable future.
>
> I have the compare in my for loop :)
>
> I'm still curious. Fl_Scroll::resize() documentation says widgets are moved
> first and then resized and resizing is thing I want to avoid until
> they don't fit
> horizontaly. Also vertical should just go to scroll. Or is this just another bad
> documentation which should be rewritten?

Unfortunately yes - you are right. Technically you are reading the docs
of Fl_Group::resize(), since an explicit documentation for
Fl_Scroll::resize() is missing. That should be fixed. I'll take a look
into it. Thanks for the hint.

Note: the docs say: "Resizes the Fl_Group widget and all of its
children. The Fl_Group widget first resizes itself, and then it moves
and resizes all its children ...". Note that the docs explicitly mention
"Fl_Group". That's not an "excuse" - just to mention it.

> Anyway, I'll switch to right resize() function.

Yes, that's probably a good idea. Fl_Scroll::resize() does NOT resize
its children. It does not move them as Fl_Group::resize() would do
(proportionally) either.

It calls Fl_Widget::resize() to resize itself, moves all widgets to
their new position (only if the Fl_Scroll widget was moved), and fixes
the scrollbars.

BTW: I changed it in your test program, and it seemed to work as
expected. I changed some other statements as well, but it's too late now
(here) to proceed.

One or two notes though: there is one or more zero divide faults when
inRow becomes 0. Note that this is only the case when you resize the
window to be less than 80 or so pixels wide, which is impossible in your
test program (minimum window width is 100). Therefore I changed the
widget width to 150, and ... crash with zero divide.

Here:

d += (W - Fl::scrollbar_size() - 2 * d) / inRow - dw - d;

and in the else case where you use the modulo operator %:

child(i)->resize(1 + X + (i % inRow) * dw,

You'll find the cause and fix yourself...


> Maybe there should be Fl_Tetris class, which would keep children sizes and
> put them tightly together and scroll until resizing would be required...

Cute name ;-) We have Fl_Pack which can pack widgets either horizontally
or vertically, but not both. But it is very specialized and may not do
what you expect in some cases. If you have special resize and layout
requirements it is best to do it yourself.

Hannu Vuolasaho

unread,
Jun 23, 2015, 9:46:09 PM6/23/15
to fltkg...@googlegroups.com
2015-06-24 2:44 GMT+03:00 Albrecht Schlosser <...>:

>
> BTW: I changed it in your test program, and it seemed to work as expected. I
> changed some other statements as well, but it's too late now (here) to
> proceed.

It's too late here too. I found rather irritating bug still present
there. When just
adding the children, they are placed where I told them to be placed, not where
I wanted. I have to call scroll.resize(scroll.x(),
scroll.y(),scroll.w(), scroll.h()) or
something similar to have them spaced evenly and beautifully.

I have feeling about that when I write the model of the data which will be shown
on that scroll and it is sorted, boring x(n) y(1) resize() will be my
lazy way out.
>
> One or two notes though: there is one or more zero divide faults when inRow
> becomes 0. Note that this is only the case when you resize the window to be
> less than 80 or so pixels wide, which is impossible in your test program
> (minimum window width is 100). Therefore I changed the widget width to 150,
> and ... crash with zero divide.

Found that too on first run when I got my project compiling.

And that class is first iteration of it. It takes a couple of rewrites
to make it right.
My first color theme (earlier mail thread) was awful and clumsy. Second was
central control and now third iteration just trusts on having right
colors in map. :)
I wonder how much my SummaryScroll class will change.

> Cute name ;-) We have Fl_Pack which can pack widgets either horizontally or
> vertically, but not both. But it is very specialized and may not do what you
> expect in some cases. If you have special resize and layout requirements it
> is best to do it yourself.

I tried that Fl_Pack also. Problem is to throw the last one out of the
end to the
next row. and sorting the widgets is impossible.

And I have quite hard requirements. 1024x576 is my target screen. I have much
information to show and it needs to be understood quickly. So one pixel space
becomes important and color scheme tells much. I find this UI still
easy to use.

And now some sleep.

Best regards,
Hannu Vuolasaho

Albrecht Schlosser

unread,
Jun 28, 2015, 9:15:38 AM6/28/15
to fltkg...@googlegroups.com
On 24.06.2015 01:44 Albrecht Schlosser wrote:
> On 24.06.2015 00:35 Hannu Vuolasaho wrote:

>> I'm still curious. Fl_Scroll::resize() documentation says widgets are
>> moved
>> first and then resized and resizing is thing I want to avoid until
>> they don't fit
>> horizontaly. Also vertical should just go to scroll. Or is this just
>> another bad
>> documentation which should be rewritten?
>
> Unfortunately yes - you are right. Technically you are reading the docs
> of Fl_Group::resize(), since an explicit documentation for
> Fl_Scroll::resize() is missing. That should be fixed. I'll take a look
> into it. Thanks for the hint.

FYI: This has been fixed in current svn (r 10776).

New docs:


void Fl Scroll::resize ( int X, int Y, int W, int H ) [virtual]

Resizes the Fl Scroll widget and moves its children if necessary.

The Fl Scroll widget first resizes itself, and then it moves all its
children if (and only if) the Fl Scroll widget has been moved. The
children are moved by the same amount as the Fl Scroll widget has been
moved, hence all children keep their relative positions.

Note
Fl Scroll::resize() does not call Fl Group::resize(), and child widgets
are not resized.

Since children of an Fl Scroll are not resized, the resizable() widget
is ignored (if it is set).

The scrollbars are moved to their proper positions, as given by
Fl_Scroll::scrollbar.align(), and switched on or off as necessary.
Reply all
Reply to author
Forward
0 new messages