Scrolling (and not scrolling) in an Fl_Scroll

291 views
Skip to first unread message

John

unread,
Aug 15, 2014, 9:28:27 AM8/15/14
to fltkg...@googlegroups.com
I’m a dabbler in FLTK, but haven’t used it an a number of years.

I’m drawing some text in a scrolled window.  I want some of the text on the screen to always be the same and not scroll (imagine headers in a table).  That's not what I'm doing, but it's the same principle.   Everything else should scroll.

I draw everything else relative to the y() so it scrolls properly, but I draw the "headers" at the same location.  This results in erratic behavior.  I’ve tried it on a couple of machines, one running 1.1.7 and one running 1.3; I get the same results.

So I decided to start simple.  I started with Erco's “How to make a Scrollable ‘Canvas’ code.  Then at the bottom of the draw() method I added one line:
     fl_line(0,60,200,60);

I would have expected this line to always appear at the same place in the window as I scroll up and down (i.e. it wouldn’t scroll), but it does not.  Interestingly enough, if I resize the window the line gets redrawn in the correct location, but not if I scroll.

I’ve included the entire program below.

I've also had have another issue with scrolling text.  If the text changes while scrolling, things look flaky.   To demonstrate, I've also added two lines of text to the program.  For the first line I draw the string "60" at a location of y()+60.  This scrolls properly.  For the second line I draw a string containing the value of y() at a location of y()+80.  This scrolls, but the value does not update properly, and if you scroll it off the top and then back down, the text becomes gibberish; it looks like partial draws of multiple y values.  Again, if I resize the window everything gets redrawn correctly, but not if I scroll.

I obviously don't understand how scrolling works.  I can see the draw() method being called, and I can print out the value of y() to the terminal and it is correct, but it doesn't appear correctly when I draw it on the screen.

Thanks for any help you can give me.

-- John

// DEMONSTRATE HOW TO MAKE A SCROLLABLE "CANVAS" DRAWING OF AN 'X'
#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Scroll.H>
#include <FL/fl_draw.H>
#include <stdio.h>

// SCROLLABLE CANVAS EXAMPLE -- JUST DRAWS AN 'X'
class MyCanvas : public Fl_Widget {
public:
     MyCanvas(int X,int Y,int W,int H,const char*L=0) : Fl_Widget(X,Y,W,H,L) {
     }
     void draw() {
         // DRAW BG
         fl_color(color());
         fl_rectf(x(),y(),w(),h());
         // DRAW 'X' OVER BG
         //   Do your graphics here..
         //
         int x1=x(), y1=y();               // Fl_Scroll works by changing our widget's x() and y(),
         int x2=x()+w()-1, y2=y()+h()-1;   // so take these into account for our drawing coordinates 
         fl_color(FL_BLACK);
         fl_line(x1,y1,x2,y2);
         fl_line(x1,y2,x2,y1);

         ///////////////
         // NEW CODE HERE

         // draw a horizontal line that's always in the same position in the window (it shouldn't scroll
         // since we don't use y() when we calculate the y coordinate.  This does not work.
         fl_line(0,60,200,60);

         // draw a string that scrolls.  This works.
         fl_font(FL_HELVETICA,14);
         char astring[100];
         sprintf(astring, "%d", 60);
         fl_draw(astring, 0, y1+60);

         // draw a string that scrolls, and that changes as we scroll.  This does not work.
         sprintf(astring, "%d", y1);
         fl_draw(astring, 0, y1+80);

         // END NEW CODE
         ///////////////

     }
};
int main() {
     Fl_Double_Window win(301,251);
       Fl_Scroll scroll(0,0,200,200);
         MyCanvas canvas(0,0,350,350);     // purposely make drawing area larger than scroll
       scroll.end();
     win.end();
     win.resizable(canvas);
     win.show();
     return(Fl::run());
}
   

Greg Ercolano

unread,
Aug 15, 2014, 7:11:26 PM8/15/14
to fltkg...@googlegroups.com
On 08/15/14 06:28, 'John' via fltk.general wrote:
> I’m a dabbler in FLTK, but haven’t used it an a number of years.
>
> I’m drawing some text in a scrolled window. I want some of the text on
> the screen to always be the same and not scroll (imagine headers in a table).

Mmm, I think what's biting you here is that Fl_Scroll
can internally use screen-to-screen copying to handle
efficient scrolling, and thus may cause artifacts if
children do anything "illegal", such as drawing outside
of their xywh bounds.

I think in general the rule with Fl_Scroll is that all
children must not draw outside their xywh, or you'll
undefined behavior will result. (artifacts)

I recall running into this with children of Fl_Scroll
that drew labels outside their xywh area.

I'd say the right thing to do here is to make a separate
widget that draws your fixed-position header, and parent
it to the window, and put the scroll below it as a sibling.

This way the header will be treated as a separate widget.

If you need interaction between your header widget and
the scroller (i.e. if the horizontal scroll is moved,
your header knows about it), then you can trap the
Fl_Scroll's horizontal scroller callback and do what you
need to find its position and just the header's drawing
as needed.

Greg Ercolano

unread,
Aug 15, 2014, 9:02:32 PM8/15/14
to fltkg...@googlegroups.com
On 08/15/14 16:11, Greg Ercolano wrote:
> On 08/15/14 06:28, 'John' via fltk.general wrote:
>> I'm a dabbler in FLTK, but haven't used it an a number of years.
>>
>> I'm drawing some text in a scrolled window. I want some of the text on
>> the screen to always be the same and not scroll (imagine headers in a table).
> [..]
> I'd say the right thing to do here is to make a separate
> widget that draws your fixed-position header, and parent
> it to the window, and put the scroll below it as a sibling.
>
> This way the header will be treated as a separate widget.
>
> If you need interaction between your header widget and
> the scroller (i.e. if the horizontal scroll is moved,
> your header knows about it), then you can trap the
> Fl_Scroll's horizontal scroller callback and do what you
> need to find its position and just the header's drawing
> as needed.

Here's an example of the above, complete with trapping
Fl_Scroll's horiz scrollbar callback to scroll the "header"
along with the scroller's content.

In this case I added a MyHeader class which has code to
handle drawing the header, and there's some optional code
to attach Fl_Scroll's hscrollbar to the widget to handle
offsetting the header's drawing.
_____________________________________________________________________________

#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Scroll.H>
#include <FL/Fl_Box.H>
#include <FL/fl_draw.H>
#include <stdio.h>
class MyHeader : public Fl_Widget {
Fl_Callback *old_callback; // horiz scroll callback
void *old_userdata; // horiz scroll userdata
Fl_Scrollbar *hs_scrollbar; // horiz scrollbar
public:
MyHeader(int X,int Y,int W,int H,const char*L=0) : Fl_Widget(X,Y,W,H,L) {
old_callback = 0;
old_userdata = 0;
hs_scrollbar = 0;
}
void draw() {
// DRAW BG
fl_color(color());
fl_rectf(x(),y(),w(),h());

// Draw text -- clip
fl_push_clip(x(), y(), w(), h());
int offset = hs_scrollbar ? hs_scrollbar->value() : 0;
fl_color(FL_BLACK);
fl_line(x()-offset,y()+h()-2,x()+w()-offset,y()+h()-2);
fl_draw("foo | bar | bla", x()+10-offset, y()+h()-5);
fl_pop_clip();
}
static void HscrollCallback(Fl_Widget *w, void *userdata) {
MyHeader *mh = (MyHeader*)userdata; // establish 'this'
mh->redraw(); // trigger redraw; offset changed
if (mh->old_callback)
mh->old_callback(w, mh->old_userdata); // invoke Fl_Scroll's callback()
}
void InterceptHscroll(Fl_Scroll &scroll) {
// Keep old callback + userdata
old_callback = scroll.hscrollbar.callback();
old_userdata = scroll.hscrollbar.user_data();
hs_scrollbar = &(scroll.hscrollbar);
// Hook in our callback
scroll.hscrollbar.callback(HscrollCallback, (void*)this);
}
};

// SCROLLABLE CANVAS EXAMPLE -- JUST DRAWS AN 'X'
class MyCanvas : public Fl_Widget {
public:
MyCanvas(int X,int Y,int W,int H,const char*L=0) : Fl_Widget(X,Y,W,H,L) {
}
void draw() {
// DRAW BG
fl_color(color());
fl_rectf(x(),y(),w(),h());
// DRAW 'X' OVER BG
// Do your graphics here..
//
int x1=x(), y1=y(); // Fl_Scroll works by changing our widget's x() and y(),
int x2=x()+w()-1, y2=y()+h()-1; // so take these into account for our drawing coordinates
fl_color(FL_BLACK);
fl_line(x1,y1,x2,y2);
fl_line(x1,y2,x2,y1);

// Draw a string that scrolls
fl_font(FL_HELVETICA,14);
char astring[100];
sprintf(astring, "%d", 60);
fl_draw(astring, x1, y1+60);
}
};
int main() {
Fl_Double_Window win(301,251);
MyHeader header(0,0, 200,30);
Fl_Scroll scroll(0,30,200,200);
MyCanvas canvas(0,0,350,350); // purposely make drawing area larger than scroll
scroll.end();
header.InterceptHscroll(scroll);

John

unread,
Aug 20, 2014, 9:19:51 AM8/20/14
to fltkg...@googlegroups.com, erco_...@seriss.com

Thanks for the quick responses.

After looking at your code, I understand what you've done and why it works for the headers.

I guess the part I still don't understand is why the line that I'm trying to draw at a y of 60 ends up scrolling, until I resize the window in which case it jumps to the correct location.  And the text I draw based on y() that doesn't update properly, even if it scrolls properly.  So I understand why what you does works, but I don't understand why what I tried did not.

I think it must have to do with what you said about the Fl_Scroll use internal screen-to-screen copying to perform efficient scrolling.  The thing that confuses me is that I can see my draw method being called, but it doesn't draw what I'm asking (until I resize).

For my application, my data may change as the user scrolls and I want the new data to be drawn.  I suppose it would be ok if it didn't update until after the scrolling stopped.  Maybe I should detect when we start and stop scrolling and only draw new data when scrolling is not taking place.

I think I'll have to study it some more.  For the type of scrolling I want to do, I think I may need to just use and Fl_Scrollbar but not an Fl_Scroll.  I would draw just what fits into my canvas based on the position of the scrollbar.

Thanks.
Reply all
Reply to author
Forward
0 new messages