Customizing the look of Fl_Scroll

40 views
Skip to first unread message

Eric Sokolowsky

unread,
Aug 25, 2025, 3:40:08 PMAug 25
to fltk.general
Hello,

I'm constructing a new application that runs primarily under Linux/X11, but this question is not platform-specific. I'm designing it to have a custom look and feel with a black background and round buttons to make it more touch-screen friendly. I've been able to override some of the classes to implement my look and feel using the draw() function, such as Fl_Toggle_Button, Fl_Button, Fl_Slider, FL_Table, and others derived from Fl_Group with good success. However, I'm now trying to figure out how to derive from Fl_Scroll so I can have a scrolling list that is too large for a single screen. The problem is the scrollbars that are instanced within Fl_Scroll. If they were pointers, I could remove the default instances and replace them with my own, but since they are not pointers, I cannot override their default drawing behavior (at least how to do so is not immediately obvious to me). Does anyone have suggestions on how I might make a scrolling box with a custom look without having to reimplement everything already in Fl_Scroll? If it helps, I plan on only using a vertical scroll bar and not a horizontal scroll bar.

Thanks in advance for any advice.

Eric

Albrecht Schlosser

unread,
Sep 3, 2025, 8:13:15 AMSep 3
to fltkg...@googlegroups.com
Sorry for the long delay ...

On 8/25/25 21:40 Eric Sokolowsky wrote:
> I'm constructing a new application that runs primarily under
> Linux/X11, but this question is not platform-specific. I'm designing
> it to have a custom look and feel [...] I'm now trying to figure out
> how to derive from Fl_Scroll so I can have a scrolling list that is
> too large for a single screen. The problem is the scrollbars that are
> instanced within Fl_Scroll. If they were pointers, I could remove the
> default instances and replace them with my own, but since they are not
> pointers, I cannot override their default drawing behavior (at least
> how to do so is not immediately obvious to me).

You're right, this is a known issue (at least I'm aware of this). I
thought about it and came to the conclusion that it would be better to
have pointers and - eventually - methods to replace the default
scrollbar widgets with ones derived by the user. Fl_Scroll has some more
issues, particularly the fact that the scrollbars are (implemented as)
real children of the widget which needs special treatment by the widget
code and by users if they want to work on children. For instance,
children() returns two more than the number of "real children", i.e.
when the widget is instantiated, there are already two children not
added by the user (the scrollbars).

OTOH, the drawback of using pointers for the scrollbars is that we need
two more separate memory allocations, but IMHO this is negligible.

> Does anyone have suggestions on how I might make a scrolling box with
> a custom look without having to reimplement everything already in
> Fl_Scroll? If it helps, I plan on only using a vertical scroll bar and
> not a horizontal scroll bar.
>
> Thanks in advance for any advice.

Well, I don't know if this counts as "advice":

I've been working on another widget with project name Fl_Scroll_New. It
works well as a proof of concept but it is not sufficiently tested yet,
and there are no methods yet to replace the scrollbars by user-derived
subclasses. Therefore I'm hesitating to publish it ...

New features and modifications:

- scrollbars are allocated separately
- scrollbars are NOT visible children of the Fl_Scrollbar_New class,
i.e. they are not children of its Fl_Group
- children() returns the number of real children, i.e. 0 (zero) when the
widget is instantiated
- all code to reorder children to make the scrollbars the last children
(in Fl_Scroll) has been removed
- scrollbars are no longer directly accessible as members as in
Fl_Scroll (members `scrollbar` and `hscrollbar` IIRC)
- maybe more...

All these changes mean that we can't just replace the old Fl_Scroll
widget with the new implementation because of backwards compatibility. I
don't know of a better way than to offer another widget, may it be
called Fl_Scroll_New or whatever.

Other suggestions would be appreciated, but I'd like to avoid just
setting a `type` and intermixing old and new code within the same code
base (for maintenance reasons). Maintaining two different widgets is not
ideal as well, but anyway...

The "advice" would be to wait until we decided how to proceed and
published the new Fl_Scroll[_New] widget which you can use to derive
your own subclass of Fl_Scrollbar (and its draw() method). This will not
be done before FLTK 1.5, but the widget itself would probably be
compileable with FLTK 1.4.x.

Comments and suggestions (by devs and users) would be appreciated!

Eric Sokolowsky

unread,
Sep 9, 2025, 6:51:38 AMSep 9
to fltkg...@googlegroups.com
I proceeded to develop a solution on my own. The way I handle it in my scroll subclass is to just ignore the original scroll bars by setting the type to 0 and then drawing my own scroll bars the way I want them to by adding my scroll bar as a child of my scroll class. The scroll logic is done in a callback for the scroll bar.

Your new class sounds interesting but yes there are backward compatibility issues that are complicated to address.

Eric 

--
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.
To view this discussion visit https://groups.google.com/d/msgid/fltkgeneral/7ca020ef-6a72-46ab-b24e-aa87c73331f6%40aljus.de.

Albrecht Schlosser

unread,
Sep 9, 2025, 9:01:18 AMSep 9
to fltkg...@googlegroups.com
On 9/9/25 12:51 Eric Sokolowsky wrote:
I proceeded to develop a solution on my own. The way I handle it in my scroll subclass is to just ignore the original scroll bars by setting the type to 0 and then drawing my own scroll bars the way I want them to by adding my scroll bar as a child of my scroll class. The scroll logic is done in a callback for the scroll bar.

That's a very good idea, thanks for sharing it.


Your new class sounds interesting but yes there are backward compatibility issues that are complicated to address.

Meanwhile I found a way to do what you want to achieve with the "old" Fl_Scroll widget. I'm still working on it, but it appears to be doable.

For backwards compatibility it's necessary to leave the embedded scrollbars which means wasting some memory if someone (like you) wants to replace the scrollbars with their own class. The changes are straight-forward though:

- add new public accessors that return pointers to the scrollbars
- old code can still access the embedded scrollbars but this is (will be) deprecated
- new code should always access the scrollbars by using the pointers
- the implementation of Fl_Scroll also uses the pointers explicitly (code changed internally)
- there will be new methods to replace one or both scrollbars with a user's subclass of Fl_Scrollbar (I'm working on this)

Whenever a user replaces the internal scrollbars with their own, the embedded scrollbars are no longer used by Fl_Scroll's code, just as in your implementation. You don't need to implement the scrolling logic yourself, it's all done by your own scrollbars automagically ;-)
At least as long as you don't want this and replace the callback of the scrollbars as well for your special scrolling needs.

How does this sound?

Is your own code publicly available, and can you share it? I would like to see what you had to do to make it work so I don't forget an essential part. Thanks in advance.


Eric Sokolowsky

unread,
Sep 10, 2025, 10:00:39 AMSep 10
to fltkg...@googlegroups.com
Albrecht,

This sounds like a good approach. I can share parts of my code. Note that I don't have the horizontal scrollbar fully implemented because I don't need it for my application. The vertical scrollbar works well enough. I draw my own scrollbar using a class derived from Fl_Slider. My solution is not ideal because the draw() function calculates whether there's a new scroll bar, and if there is, the entire window is redrawn after the contents appear, making it look like the scrollbar appears after the rest of the content. Ideally the scrollbar would appear with the content when it's first drawn (and I think that there's a lot of logic in Fl_Scroll to make this happen; I didn't want to do all of this).

// The scroll class uses different scroll bars than Fl_Scroll so they match the new look and feel.
class hwi_scroll : public Fl_Scroll
{
public:
    hwi_scroll(int x, int y, int w, int h);
    void scroll_type(int t) { scroll_type_ = t; }
    static void cb_slider(hwi_slider* slider, hwi_scroll* scroll);
protected:
    virtual void draw(void);
    int scroll_type_ = 0;   // uses Fl_Scroll types. Default is never draw bars
    hwi_slider* scrollbar = nullptr;
    hwi_slider* hscrollbar = nullptr;
};


hwi_scroll::hwi_scroll(int x, int y, int w, int h) : Fl_Scroll(x, y, w, h)
{  
    Fl_Scroll::type(0);  // don't ever draw base class scrollbars
}

void hwi_scroll::cb_slider(hwi_slider* slider, hwi_scroll* scroll)
{  
    if (slider == scroll->scrollbar)
    {  
        scroll->scroll_to(scroll->xposition(), slider->value());
    }
    else if (slider == scroll->hscrollbar)
    {  
        scroll->scroll_to(slider->value(), scroll->yposition());
    }
}

void hwi_scroll::draw(void)
{  
    bool horizontal = false;   // set to true if the horizontal scrollbar should be shown (not completely implemented)
    bool vertical = false;     // set to true if the vertical scrollbar should be shown
   
    // Find last child that is not a slider to get scroll area extents. This assumes that the children are laid out in order.
    int scroll_height = 0, scroll_width = 0;
    for (int i = children() - 1; i >= 0; --i)
    {  
        if (dynamic_cast<Fl_Slider*>(child(i)))
            continue;
        if (scroll_type_ & Fl_Scroll::HORIZONTAL && child(i)->x() + child(i)->w() > this->x() + this->w())
        {  
            horizontal = true;
            scroll_width = child(i)->x() + child(i)->w() - this->x() - this->w();
        }
        if (scroll_type_ & Fl_Scroll::VERTICAL && child(i)->y() + child(i)->h() > this->y() + this->h())
        {  
            vertical = true;
            scroll_height = child(i)->y() + child(i)->h() - this->y() - this->h();
        }
        break;
    }

   
    if (scroll_type_ & Fl_Scroll::HORIZONTAL)
    {  
        if (scroll_type_ & Fl_Scroll::ALWAYS_ON)
            horizontal = true;
    }
   
    if (scroll_type_ & Fl_Scroll::VERTICAL)
    {  
        if (scroll_type_ & Fl_Scroll::ALWAYS_ON)
            vertical = true;
    }

    if (horizontal)
    {
        scroll_width += 0; // FIXME: temporary get rid of unused variable warning
    }
    if (vertical)
    {
        if (!scrollbar)
        {
            // make the children smaller to make room for the scroll bar
            int slider_width = unit_height;
            for (int c = 0; c < children(); ++c)
            {
                if (child(c)->w() > this->w() - slider_width)
                    child(c)->size(this->w() - slider_width, child(c)->h());
            }

            this->parent()->begin(); // scroll bar is not actually a child of the scroll class
            scrollbar = new hwi_slider(this->x() + this->w() - slider_width, this->y(), slider_width, this->h(), "");
            scrollbar->bounds(0, scroll_height);
            scrollbar->callback((Fl_Callback*)cb_slider, this);
            this->parent()->end();

            // FIXME: this works, but it's not ideal, because there's a delay between when the window appears and when the
            // scrollbar appears.
            redraw_window = true;
        }
    }

    Fl_Scroll::draw();
}


Reply all
Reply to author
Forward
0 new messages