overlapping widgets: who gets the events

58 views
Skip to first unread message

Oguz Akyuz

unread,
Apr 7, 2023, 3:17:48 AM4/7/23
to fltk.general
I've been using FLTK for a long time but this appears to be a very simple issue that I couldn't fully grasp. If there are fully overlapping two widgets (they are *not* in a parent/child relationship), how are the events sent to and processed by them?

Specifically, I have a box (derived) that can do resizing by responding to ENTER and DRAG events. Colocated with it is an Fl_Scroll (also derived). Both widgets need to respond to PUSH event. This is because the box needs the mouse coordinate when push happens so that it can be dynamically resized (by drag). But the scroll also needs the push event because it needs an item in its list to be highlighted.

I assume that such a case is a pretty typical scenario where one event needs to be handled by fully/partially overlapping widgets. Or is not and am I doing a big mistake by creating overlapping widgets?

Thanks,
Oguz

Matthias Melcher

unread,
Apr 7, 2023, 4:25:38 AM4/7/23
to fltk.general
FLTK assumes that widgets are not overlapping, except of course as children of a group. The group sends the event to the first child that contains the position of the mouse event. If that child widget handles the PUSH event (returns 1), Fl_Group will not do any more attempts to send the message. If that child does not handle FL_PUSH, Fl_Group will search for the next child that contains the mouse coordinates. If none of the children handles the event, Fl_Group returns 0 to the parent group.

Albrecht Schlosser

unread,
Apr 7, 2023, 8:25:56 AM4/7/23
to fltkg...@googlegroups.com
On 4/7/23 10:25 'Matthias Melcher' wrote:
> FLTK assumes that widgets are not overlapping, except of course as
> children of a group. The group sends the event to the first child that
> contains the position of the mouse event.

Note that the order here is from last child to first child of the group
which makes it appear that overlapped widgets get events only after the
widget on top of it has been tried and denied the reception (see below).
The general rule is: drawing is done in the order first to last and
event delivery in the opposite order. This supports overlapping widgets
partially, although it is not recommended (see Matthias' note above).
Relying on this can cause unexpected (undefined) behavior.

> If that child widget handles the PUSH event (returns 1), Fl_Group will
> not do any more attempts to send the message. If that child does not
> handle FL_PUSH, Fl_Group will search for the next child that contains
> the mouse coordinates. If none of the children handles the event,
> Fl_Group returns 0 to the parent group.

As written above, that's likely all you need to know for FL_PUSH events.
However, there are some exceptions to the rule when FLTK "knows" which
widget is the "current" widget for a particular event type, i.e.
keyboard events go to the focus widget, mouse scroll events to the
widget below the mouse, and some more. There's a full documentation
chapter about this: https://www.fltk.org/doc-1.4/events.html.

Ian MacArthur

unread,
Apr 7, 2023, 11:15:10 AM4/7/23
to fltk.general
As a parallel approach to the comments from Albrecht and Matt, I think it would be very useful if the OP (Oguz) could outline the effect that they hope to achieve via overlapping the widgets.
It is possible that some other technique (perhaps even the old Fl_Overlay_Window approach) might be a way to get to the desired effect without having to struggle with overlaying widgets.

I often find it is more helpful to try and think about what has to be achieved, rather than to get trapped in making one specific implementation work, since that implementation may be the "wrong solution", and the fact that it is proving difficult may be the clue.
 

Oguz Akyuz

unread,
Apr 7, 2023, 11:15:54 AM4/7/23
to fltkg...@googlegroups.com
Thank you for the clarification. Then in this case, is there any restriction that prevents the FL_MOVE event to be sent to a group? I want to be able to resize the group by catching the MOVE and DRAG events. However, because the group does not appear to get the MOVE event, I had to create a box that overlaps with the group.

Oguz

On Fri, Apr 7, 2023 at 11:25 AM 'Matthias Melcher' via fltk.general <fltkg...@googlegroups.com> wrote:
FLTK assumes that widgets are not overlapping, except of course as children of a group. The group sends the event to the first child that contains the position of the mouse event. If that child widget handles the PUSH event (returns 1), Fl_Group will not do any more attempts to send the message. If that child does not handle FL_PUSH, Fl_Group will search for the next child that contains the mouse coordinates. If none of the children handles the event, Fl_Group returns 0 to the parent group.

--
You received this message because you are subscribed to a topic in the Google Groups "fltk.general" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/fltkgeneral/TfDQoRJ6G-Q/unsubscribe.
To unsubscribe from this group and all its topics, send an email to fltkgeneral...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/fltkgeneral/69579784-a4b3-4d72-9b96-61c097aa75c4n%40googlegroups.com.

Matthias Melcher

unread,
Apr 7, 2023, 11:57:23 AM4/7/23
to fltk.general
"The mouse has moved without any mouse buttons held down.

This event is sent to the Fl::belowmouse() widget.


In order to receive FL_MOVE events, the widget must

return non-zero when handling FL_ENTER."


This means that if you override the handle() method of your group, you must look  out for FL_ENTER events, and either call Fl_Group::handle or do your own handling, but make sure that you return 1. You will then receive FL_MOVE events. Only one widget receives move events. The same with FL_DRAG: you need to return 1 on FL_PUSH to receive FL_DRAG events. See event section in the docs.



Ian MacArthur

unread,
Apr 7, 2023, 12:00:03 PM4/7/23
to fltk.general
On Friday, 7 April 2023 at 16:15:54 UTC+1 oguz wrote:
Thank you for the clarification. Then in this case, is there any restriction that prevents the FL_MOVE event to be sent to a group? I want to be able to resize the group by catching the MOVE and DRAG events. However, because the group does not appear to get the MOVE event, I had to create a box that overlaps with the group.

Off-hand, I do not think there is anything that means a Group can not respond to Move events, but generally fltk widgets will only be sent a MOVE event if they respond non-zero to an ENTER event (which Group might not do by default - haven't actually checked!)

You could make a subclass of Group, and in your derived handle make sure you return 1 for an ENTER event, you should then get MOVE events also, I think.

If you *do* make a subclass of Group, and in the derived handle you return 1 for a PUSH event, you should then get DRAG events also, I think, which might be a way to get the info that you need, if you can not get the MOVE events directly?


Oguz Akyuz

unread,
Apr 8, 2023, 8:01:48 AM4/8/23
to fltkg...@googlegroups.com
Thank you very much for the great answers. Per Ian's suggestion, let me explain what I am trying to do. See the ascii-art below. It represents my main window:

- - - - - - - - - - - - 
|   Y  |               |
|   -   |               |
|   A  |       L      |
|       |               |
|       |               |
- - - - - - - - - - - - 

A and Y are widgets derived from Fl_Scroll. L is another derived widget. You can think of Y as a list of drives on the system, A as a list of directories, and L as a list of
image icons on a 2D grid. We can call the region occupied by Y and A as the sidebar. Now I want the sidebar to be resizable by dragging it left and right. As a result,
the size of L will be adjusted.

To achieve this, I've tried the following possibilities. I created a group for Y and A (the group itself is derived from Fl_Group). The group entirely encapsulates them. 
Now I want the group to be resizable by  capturing the MOVE, PUSH, DRAG, and ENTER events. I need MOVE because when the cursor comes near to the border, I 
change the cursor to indicate resizability (if the user drags when the cursor is away, I do not allow resizing). But I failed to get the MOVE event to my group. Despite 
returning 0 from the handles of both Y and A, and returning 1 from the handle of the group, the MOVE event is not received by the group.

For the second attempt, I tried to create a widget (basically a derived Fl_Box), that entirely overlaps with Y and A (similar to the group). The box is not visible because
it is FL_NO_BOX. This creates an overlapping widget situation and hence was the reason for my initial question. Now this box gets the MOVE, PUSH, DRAG, and ENTER
events and actually everything about the resizing appears to work. However, it prevents the widgets Y and A from receiving PUSH events. So I manually called the handles 
of Y and A from the box when the PUSH event is received (probably not a good idea as I had to make the handles public), and this allows the items in Y and A to be selected. 
But the scrolling behaviour does not work in this case. The scrollbars of Y and A become unclickable and actually some erratic behaviour is observed when I scroll using the
mouse wheel.

So I hope my problem is clear and would greatly appreciate any further suggestions on solving this in a reliable manner.

Oguz



--
You received this message because you are subscribed to a topic in the Google Groups "fltk.general" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/fltkgeneral/TfDQoRJ6G-Q/unsubscribe.
To unsubscribe from this group and all its topics, send an email to fltkgeneral...@googlegroups.com.

Matthias Melcher

unread,
Apr 8, 2023, 11:31:45 AM4/8/23
to fltk.general
You are looking for Fl_Tile:

#include <FL/Fl_Window.H>

#include <FL/Fl_Tile.H>

#include <FL/Fl_Box.H>


int main(int argc, char **argv) {

  Fl_Window win(300, 300);

  Fl_Tile tile(0, 0, 300, 300);

  Fl_Group left(0, 0, 100, 300);

  Fl_Box y(0, 0, 100, 100, "Y");

  y.box(FL_DOWN_BOX);

  Fl_Box a(0, 100, 100, 200, "A");

  a.box(FL_DOWN_BOX);

  left.end();

  Fl_Box l(100, 0, 200, 300, "L");

  l.box(FL_DOWN_BOX);

  win.show(argc, argv);

  return Fl::run();

}


Oguz Akyuz

unread,
Apr 9, 2023, 7:55:27 AM4/9/23
to fltkg...@googlegroups.com
Thank you very much, this works excellently. 

Oguz

--
You received this message because you are subscribed to a topic in the Google Groups "fltk.general" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/fltkgeneral/TfDQoRJ6G-Q/unsubscribe.
To unsubscribe from this group and all its topics, send an email to fltkgeneral...@googlegroups.com.

Evan Laforge

unread,
Apr 9, 2023, 11:31:50 AM4/9/23
to fltkg...@googlegroups.com
My program uses overlapping widgets, I use them for a text entry which pops up temporarily in the middle of a vertical track representing time (which is incidentally an FL_Tile).  Sort of like an IME entry box.  When you hit enter or change focus, the input disappears and its content is entered in to the underlying track.  I also have another place for the titles of tracks, because they can be narrow but I wanted the input to be able to temporarily expand horizontally while editing, in which case it will cover neighbors.

I remember I needed custom code in draw() to draw the possible floating child last, and in handle() to check Fl::event_inside and possibly deliver to the floating child first.  Also because of the overlapping, though the widget belongs to the child, it winds up being managed directly by the parent.  I think there were a few other hacks I had to do to smooth over violated expectations about widgets not overlapping and ownership being hierarchical, but it was so long ago I don't remember exactly what.  What I do remember is feeling like it was hackery but not being able to think of a better way.

Matthias Melcher

unread,
Apr 9, 2023, 11:49:31 AM4/9/23
to fltk.general
Ah, that's a nice use for overlapping widgets. I have solved this some time ago with a non-modal borderless window that contained only the text widget. If the user did anything outside of that window, I would hide and destroy it immediately and give control back to the app (a mix of non-modal, popup, and tooltip window). IIRC, it worked well, except MacOS had this thing where you could drag the app window without the popup window knowing about it, which made the text input hover randomly over the desktop ;-)

Evan Laforge

unread,
Apr 9, 2023, 1:38:24 PM4/9/23
to fltkg...@googlegroups.com

Ah, that's a nice use for overlapping widgets. I have solved this some time ago with a non-modal borderless window that contained only the text widget. If the user did anything outside of that window, I would hide and destroy it immediately and give control back to the app (a mix of non-modal, popup, and tooltip window). IIRC, it worked well, except MacOS had this thing where you could drag the app window without the popup window knowing about it, which made the text input hover randomly over the desktop ;-)

I believe I tried the separate window thing first, because it seemed less hacky.  But there was something that caused me to abandon it, from looking through the commit log, this is the one where I changed tacks (pasted below).  The commit also has a small novel's worth of notes where I'm digging into mouse focus vs keyboard focus, but it appears the source of the problem was (ironically) that OS X worked, but X11 didn't.  I got different sequences of FL_FOCUS, FL_UNFOCUS, FL_SHOW, FL_ENTER, and FL_LEAVE on OS X and X11, and the X11 one got an extra FL_ENTER, FL_LEAVE, FL_ENTER, FL_UNFOCUS that made it impossible to distinguish between spurious events emitted when the window was created, vs the legit sequence indicating unfocus and leave and therefore time to delete the window.  I believe I did try to debug that (along with streams of 24 FL_NO_EVENTs that got mixed in) inside fltk itself, but gave up eventually.  I believe there was also interaction with (fvwm) ClickToFocus vs PointToFocus vs. SloppyFocus, but even OS X style ClickToFocus still had the problem.

In any case, since the change on Feb 2022, the overlapping widgets approach has been working, after a series of patches to fix bugs.

Anyway, here's the commit message in case it's interesting, please excuse any snark in there, it looks like I was a bit frustrated after struggling with it for a while:

    nonfloating: replace the floating input window with an overlapping widget

    Stuff that used to be done by a FloatingInput containing a WrappedInput is now
    done only by WrappedInput.  The reason is that the delicate event dance needed
    to get the window to show and disappear at the right time didn't work on X11.
    What's more, even if I could get fltk's events to behave sanely (i.e. not emit
    multiple unfocus and focus events on a new window), I don't see how it could
    ever work with a point-to-focus setup.

    That surprised me because I thought I tested this on linux before?  How did I
    not notice the inability to enter any text?

    This is also really showing the problems in fltk, from things like only having
    callbacks, and not allowing any usable argument for them, to more serious
    issues like mysterious undocumented and possibly buggy event delivery.  Or
    maybe it's just that everything in C++ winds up being this way.

    One nice side-effect is that since I'm not making a whole new window anymore,
    click to focus and set cursor position simultaneously now works. 

Matthias Melcher

unread,
Apr 9, 2023, 7:37:55 PM4/9/23
to fltk.general

Thanks. This is really interesting stuff to know. Since popup menus seem to behave nicely in general, and we had a parallel request not too long ago for a popup list that can take keystrokes to limit the list, I should put some time into making these kind of windows work as expected.

As for event order and number of events, we try to keep this consistent across platforms, but we depend very much on the underlying drivers (WIN32, X11, Wayland, MacOS, ...), and these are not always reliable between versions either. It is possible though that we overlooked something, of course.

Evan Laforge

unread,
Apr 9, 2023, 8:19:30 PM4/9/23
to fltkg...@googlegroups.com
Thanks. This is really interesting stuff to know. Since popup menus seem to behave nicely in general, and we had a parallel request not too long ago for a popup list that can take keystrokes to limit the list, I should put some time into making these kind of windows work as expected.

I'd be willing to test out and give feedback on any kind of "official" support for popups like this.  I sort of bashed my implementation into place, but I'm not really confident that it's stable, because overlapping widgets is surely breaking an (unspoken?) precondition in a lot of fltk internals.

Here are some interesting tidbits from my notes at the time, I recall I was trying to stop fltk from throwing focus to an input that I didn't ask for focus.  Perhaps when the window closed and focus went back to the main window?

    . Maybe this is because of fltk conflating two different kinds of focus:
      OS focus on the window, and window kbd focus on a widget.  Fltk must
      keep track of kbd focus separately, because it remembers what widget has
      focus when window focus comes back.  I want to ignore the OS mouse
      focus, and only trigger on a loss of kbd focus. 

    * Why does the block title get focus when the block does?
      . I wonder if this has to do with fltk's implicit kbd focus, maybe it's
        tab navigation?  Can I disable that?
      . I could with Fl::event_key()==0, but it's not that, because it gets it
        when the window gets focus.  Why?
      . Fl_Group::savedfocus_ gets take_focus() on FL_FOCUS, how is that set?
        global fl_oldfocus, set by Fl::focus()
      . Previously I worked around that by not calling the Fl_Window::handle
        but then it doesn't return focus to something already focused.
      . If the problem is that savedfocus_ gets set to random inputs (why?)
        then maybe I can keep my own savedfocus.
      . fl_oldfocus is not the widget, it's null or the window!
        Could it be savedfocus_?  Nothing else sets it!
        I don't think it's navkey, but the way it picks first child is
        suggestive.
      . Oh the case falls through, I guess because Block doesn't take focus?
      . I can prevent this by allowing Block to take focus.  But, savedfocus_
        is not in fact saving focus.  fl_oldfocus is the toplevel FL_UNFOCUS,
        so maybe it never did save focus.
 
As for event order and number of events, we try to keep this consistent across platforms, but we depend very much on the underlying drivers (WIN32, X11, Wayland, MacOS, ...), and these are not always reliable between versions either. It is possible though that we overlooked something, of course.

 I'd be absolutely in favor of at least some research and documentation about how events happen, specifically the gains/loses focus parts.  One thing is that I use fvwm, which is an older window manager and may have bugs or at least different behaviour from more modern ones.

This sort of thing is subtle and maybe hard to test.  I think we don't need to pin down an exact event sequence (though it would be nice!), but to have some invariants, such as "new window with focus will get exactly one FL_FOCUS" which would make a FL_FOCUS, FL_UNFOCUS, FL_FOCUS sequence into a bug.  If we can have a way to simulate or record and play back the sequence of methods that a window manager will call when various actions are taken, then we could put that in a test as both documentation and a regression check.

Also for popups there needs to be some cooperation with the window manager, for instance a point-to-focus mode will need to know this window is special and should keep focus except on click, also that it should move with the window, etc.  I don't even know if X11 window managers support this kind of thing.  It was this kind of consideration that led me to believe popups are better handled inside the widget library, not as a separate window.
Reply all
Reply to author
Forward
0 new messages