Fl_Box: hide/show/change image on mouse-over event

44 views
Skip to first unread message

Karl Harbinger

unread,
Jan 22, 2018, 7:09:01 AM1/22/18
to fltk.general
I'm using a window with a png file as a background. That png file shows buttons and I put insivible fltk buttons (actually subclassed Fl_Boxes) on top of those buttons.
Now I want those buttons to become more interactive by putting an image of top of them that's only visible when I point with the mouse on it, but I can't seem to get it to work right.

An example: the window shows a hand-drawn red button (part of background.png) and I want to highlight it with a lighter red color (red_button_highlight.png) when I move the mouse button above it.

Any ideas?

Karl Harbinger

unread,
Jan 22, 2018, 8:07:58 AM1/22/18
to fltk.general
int mouse_over = 0;

class mouse_over_box : public Fl_Box
{
public:
  mouse_over_box
(int X, int Y, int W, int H, const char *L=0)
   
: Fl_Box(X, Y, W, H, L) { }

 
virtual ~mouse_over_box() { }

 
int handle(int event) {
   
int ret = Fl_Box::handle(event);
   
switch (event) {
     
case FL_ENTER:
        do_callback
();
        fl_cursor
(FL_CURSOR_HAND);
        mouse_over
= 1;
       
break;
     
case FL_LEAVE:
        do_callback
();
        fl_cursor
(FL_CURSOR_DEFAULT);
        mouse_over
= 0;
       
break;
   
}
   
return ret;
 
}
};

static void mouse_over_box_cb(Fl_Widget *o, void *v) {
  mouse_over_box
*b = (mouse_over_box *)o;
 
const char *img = (mouse_over == 0) ? (const char *)v : NULL;
  b
->image(new Fl_PNG_Image(img));
  b
->parent()->redraw();
}

/* ... */

  mouse_over_box
*box = new mouse_over_box(x, y, w, h);
  box
->callback(mouse_over_box_cb, (void *)"button.png");
  box
->clear_visible_focus();





Okay, this seems to do the trick.

Albrecht Schlosser

unread,
Jan 22, 2018, 1:18:44 PM1/22/18
to fltkg...@googlegroups.com
On 22.01.2018 14:07 Karl Harbinger wrote:

[actual code elided]

> Okay, this seems to do the trick.

Yes, AFAICT it does. But there are several things you can and *should*
improve. This is not real production code, is it?

Here are some thoughts (together with your code):

You really don't need to "waste" a callback for what you intend to do.
Admitted, there is no need for a callback if you derive from Fl_Box, but
anyway. More than that, your callback creates a memory leak and degrades
performance:

static void mouse_over_box_cb(Fl_Widget *o, void *v) {
mouse_over_box *b = (mouse_over_box *)o;
const char *img = (mouse_over == 0) ? (const char *)v : NULL;
b->image(new Fl_PNG_Image(img));

The statement above creates a new Fl_PNG_Image every time the mouse
enters the widget. The previous image is never deleted. The image is
always read from disk (unless you use an in-memory image in real code).

You can work around this by using Fl_Shared_Image to leave the caching
of the image(s) to the FLTK library:

if (img) // don't forget this if() !
b->image(Fl_Shared_Image::get(img)); // use Fl_Shared_Image
else
b->image(img); // NULL

But I wouldn't do this. Since you are subclassing anyway you can also
add another image variable and do everything you need in the handle()
method. Note: code is untested!

class mouse_over_box : public Fl_Box
{
public:
mouse_over_box(int X, int Y, int W, int H, const char *L=0)
: Fl_Box(X, Y, W, H, L),
: mouse_over_image(0)
{ }

virtual ~mouse_over_box() { }

Fl_Image *mouse_over_image;

int handle(int event) {
int ret = Fl_Box::handle(event);
switch (event) {
case FL_ENTER:
fl_cursor(FL_CURSOR_HAND);
image(mouse_over_image);
parent()->redraw();
break;
case FL_LEAVE:
fl_cursor(FL_CURSOR_DEFAULT);
image(0);
parent()->redraw();
break;
}
return ret;
}
};

/* ... */

mouse_over_box *box = new mouse_over_box(x, y, w, h);
box->mouse_over_image = new Fl_PNG_Image("button.png");
box->clear_visible_focus();

Note that you don't need a global variable and no callback. The image is
allocated only once and doesn't need to be deleted, but if you use
several images in several such boxes it may be good to delete the image
in the d'tor (take care if you share images - you may also want to use
Fl_Shared_Image and Fl_Shared_Image::release() in the d'tor).

Karl Harbinger

unread,
Jan 23, 2018, 12:44:50 PM1/23/18
to fltk.general
Thanks, that really helped me. No, this isn't production code, it's just a little hobby project.
Do I need to worry about freeing memory if I initialize the images globally without the "new" operator?

Albrecht Schlosser

unread,
Jan 23, 2018, 1:41:13 PM1/23/18
to fltkg...@googlegroups.com
On 23.01.2018 18:44 Karl Harbinger wrote:
> Thanks, that really helped me. No, this isn't production code, it's just
> a little hobby project.

Even hobby projects can have "production code" state and should not have
memory leaks or avoidable reading from disk when the user moves the
mouse. Particularly if the user is you (the developer of the hobby
project). ;-)

> Do I need to worry about freeing memory if I initialize the images
> globally without the "new" operator?

No, the system will free all memory used by your program.

If you add global objects the system will execute their destructors when
the program exits. This may be undesirable because the system spends
some time after you close the last window and before the application is
really finished. If you want to avoid this (for instance for your
images) you can allocate such static objects with new and never free
(delete) them. Take care if you do this only with static objects that
don't need their destructors to be executed, e.g. objects that have open
files or network connections etc..

Greg Ercolano

unread,
Jan 23, 2018, 2:07:27 PM1/23/18
to fltkg...@googlegroups.com
On 01/23/18 10:41, Albrecht Schlosser wrote:
>> Do I need to worry about freeing memory if I initialize the images
>> globally without the "new" operator?
>
> No, the system will free all memory used by your program.
>
> If you add global objects the system will execute their destructors when
> the program exits. This may be undesirable because the system spends
> some time after you close the last window and before the application is
> really finished. If you want to avoid this (for instance for your
> images) you can allocate such static objects with new and never free
> (delete) them. Take care if you do this only with static objects that
> don't need their destructors to be executed, e.g. objects that have open
> files or network connections etc..

Note that calling _exit() will bypass destructors and goes straight
to OS deallocation, which is very fast.

That can only be bad if you /need/ destructors to be called, such
as destructors that remove temp files and such.
Reply all
Reply to author
Forward
0 new messages