scrollbar

19 views
Skip to first unread message

Dave Jordan

unread,
Aug 4, 2021, 8:02:20 AM8/4/21
to fltkg...@googlegroups.com
Hi everybody.
I'm using a hold browser and i think its scrollbar is stealing a shortcut to a menu item i have assigned to ctrl-end. How can I prevent this? subclass the scrollbar? Or is it legit to have a handle and a callback for the same widget i.e. the browser?
-dave

Albrecht Schlosser

unread,
Aug 5, 2021, 4:29:46 AM8/5/21
to fltkg...@googlegroups.com
On 8/4/21 2:02 PM Dave Jordan wrote:
I'm using a hold browser and i think its scrollbar is stealing a shortcut to a menu item i have assigned to ctrl-end. How can I prevent this? subclass the scrollbar? Or is it legit to have a handle and a callback for the same widget i.e. the browser?

Yes, you can subclass and have a callback attached to the widget. If you want to "filter" shortcuts (that can't be changed in the base class you would subclass and define your handle() where you'd test for the event in question and return 0 *before* you call the handle() method of the base class, for instance

myclass::handle(int e) {
  if (e == FL_SHORTCUT && Fl::key() == FL_END)
    return 0;
  return baseclass::handle();
}

In this case the FL_END keypress would be propagated to any other widget that "wants" it.

Note: I can't tell if your assumption is correct, but if it is something like the code above should help.

Bill Spitzak

unread,
Aug 5, 2021, 11:51:18 AM8/5/21
to fltkg...@googlegroups.com
You may also be able to do something so that the shortcut is delivered to your widget first, before it is tried on the scrollbar.

First thing is to make sure your contents widget checks for the SHORTCUT event and consumes it (returns true). I vaguely remember this is intended to work.

If that does not fix it then I think the best approach is to subclass the parent of your widget and the scrollbar and make it's event handler deliver the SHORTCUT event to the widget first, returning true if that returns true. You can then call the base class with the events after that (it is true it will call your widget again with any SHORTCUT events that were ignored but this is harmless as your widget will ignore them a second time).

--
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 on the web visit https://groups.google.com/d/msgid/fltkgeneral/d5a5b77f-3d72-a769-c490-17d7bf1491c7%40online.de.

Greg Ercolano

unread,
Aug 5, 2021, 3:24:55 PM8/5/21
to fltkg...@googlegroups.com

On 8/4/21 5:02 AM, Dave Jordan wrote:

Hi everybody.
I'm using a hold browser and i think its scrollbar is stealing a shortcut to a menu item i have assigned to ctrl-end. How can I prevent this? subclass the scrollbar? Or is it legit to have a handle and a callback for the same widget i.e. the browser?

    Yes - looking at the code for src/Fl_Scrollbar.cxx, it appears to react to FL_End
    as a shortcut regardless of the keyboard modifiers, so apparently Alt, Shift, Ctrl, etc.
    can all be used with the End key to handle moving the scrollbar.

    I think order of widgets in the parent group matter for shortcut event delivery;
    the group's children are likely processed in order of creation, so the first widget
    to respond to the shortcut 'wins'.

    So if possible, try creating your 'widget that needs ctrl-end' /before/ creating the
    hold browser. Or, you can rearrange the pointers in the parent group using I think
    remove() and add().

    Or I think the better approach might be to make your own subclass of the hold browser
    that /doesn't/ react to Ctrl+End, which should be easy to do, and will guarantee it doesn't
    steal the events you want.

    In your subclass handle() method, you can check for Ctrl+End and ignore the event
    by just doing a return(0); when it's received. This will eclipse the event from the base class
    so it can't react to it. Pretty sure this would work:



int MyHoldBrowser::handle(int e) {
    // Specifically ignore the Fl_End + FL_Ctrl key combo so that other widgets may use it
    switch (e) {
        case FL_KEYDOWN:     // keyboard press events (aka. FL_KEYBOARD)
        case FL_KEYUP:       // keyboard releae events
        case FL_SHORTCUT:    // keyboard shortcuts
            switch ( Fl::event_key() ) {
                case FL_End:                              // Fl_End pressed?
                    if ( Fl::event_state() & FL_CTRL )    // Ctrl key also pressed?
                        { return 0; }                     //
short circuit this event from base class so other widgets can use it
                    break;
            }
            break;
    }
    return Fl_Hold_Browser::handle(e);                    // all other events pass to base class
}



    You can easily tweak that to ignore other modifier key combos as well that you might need,
    like Alt-End, Shift-End, and ignore those combos with other keys as well, like Home, PgUp/Dn, etc.

Gmail Ercolano

unread,
Aug 5, 2021, 3:39:13 PM8/5/21
to fltkg...@googlegroups.com


On 8/5/21 12:24 PM, Greg Ercolano wrote:
[..]

    Or I think the better approach might be to make your own subclass of the hold browser
    that /doesn't/ react to Ctrl+End, which should be easy to do, and will guarantee it doesn't
    steal the events you want.

    In your subclass handle() method, you can check for Ctrl+End and ignore the event
    by just doing a return(0); when it's received. This will eclipse the event from the base class

    so it can't react to it. Pretty sure this would work: [..]


    Here's a small working example program that demonstrates that technique.

    Here I created two widgets:

  •     "MyHoldBrowser" which subclasses Fl_Hold_Browser and has the handle() method
        I mentioned that forces it to ignore the Ctrl+End key combo. ("End" will still work
        for moving the scrollbar, just not with the Ctrl key included in any way)

  •     "MyEndWidget" which simulates your widget that 'needs ctrl-end as a shortcut',
        and prints a message to the console when it's able to see that key combo as a shortcut,
        showing the event is not stolen by the hold browser.


    When you run the app, hitting Ctrl+End should print the message indicating MyEndWidget
    was able to process the event.

    As an experiment, you can try commenting out the entire MyHoldBrowser::handle() method
    (shown in blue below), then it will act like the default Fl_Hold_Browser, and you'll see Ctrl+End no longer
    prints the message, replicating the "event stealing" behavior you mentioned. This would show the
    subclassing technique with the handle() method solves the issue.


#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Hold_Browser.H>
#include <FL/Fl_Box.H>

class MyHoldBrowser : public Fl_Hold_Browser {
public:
  MyHoldBrowser(int X,int Y,int W,int H,const char *title=0) : Fl_Hold_Browser(X,Y,W,H,title) {
  }
  int handle(int e) {
      // Specifically ignore the Fl_End + FL_Ctrl key combo 
      // so other widgets may use it
      switch (e) {
          case FL_KEYDOWN:     // keyboard press events (aka. FL_KEYBOARD)
          case FL_KEYUP:       // keyboard releae events
          case FL_SHORTCUT:    // keyboard shortcuts
              switch ( Fl::event_key() ) {
                  case FL_End:                              // Fl_End pressed?
                      if ( Fl::event_state() & FL_CTRL )    // Ctrl key also pressed?
                          { return 0; }                     // eclipse event from base class so other widgets can use it
                      break;
              }
              break;
      }
      return Fl_Hold_Browser::handle(e);                    // all other events pass to base class
  }
};

class MyEndWidget : public Fl_Box {
public:
  MyEndWidget(int X,int Y,int W,int H,const char *title=0) : Fl_Box(X,Y,W,H,title) {
  }
  int handle(int e) {
      if ( e == FL_SHORTCUT &&
           Fl::event_key() == FL_End &&
           Fl::event_state() & FL_CTRL ) {
          printf("Handling Ctrl-End!\n");                  // debugging
          return 1;
      }
      return Fl_Box::handle(e);
  }
};

int main()
{
    // Make window with the border color
    Fl_Double_Window *win = new Fl_Double_Window(800,800,"Test");

    // Create our own Hold Browser subclass
    MyHoldBrowser *brow = new MyHoldBrowser(10,10,300,800-20,"Hold Browser");
    {
        // Make 100 items in the browser so scrollbar appears
        char s[80];
        for (int t=0; t<100; t++ ) { sprintf(s, "%04d", t); brow->add(s); }
    }

    // Create our own widget that needs Ctrl+End
    //     If it receives that key combo, it prints a message to the screen.
    //     Let's create this /last/, ensuring that widget creation order 
    //     doesn't matter to make this technique work.
    //
    MyEndWidget *myend = new MyEndWidget(400,10,300,800-20,"My Widget");
    myend->color(FL_RED);
    myend->box(FL_FLAT_BOX);

    win->end();
    win->show();
    return Fl::run();
}



    
Although I didn't try it, I think what would also 'work' is if you left the default Fl_Hold_Browser
behavior intact, and just created the MyEndWidget first, and the hold browser after that.
Changing the order should (I think) also allow your widget to get the shortcut event first,
without having to subclass Fl_Hold_Browser.

But depending on widget order might be hard to maintain, and be too obscure, so I think
the above behavior might be the better choice.

Dave Jordan

unread,
Aug 6, 2021, 12:35:43 AM8/6/21
to fltkg...@googlegroups.com
On Thu, Aug 5, 2021 at 3:39 PM Gmail Ercolano <ercola...@gmail.com> wrote:


On 8/5/21 12:24 PM, Greg Ercolano wrote:
[..]

    Or I think the better approach might be to make your own subclass of the hold browser
    that /doesn't/ react to Ctrl+End, which should be easy to do, and will guarantee it doesn't
    steal the events you want.

    In your subclass handle() method, you can check for Ctrl+End and ignore the event
    by just doing a return(0); when it's received. This will eclipse the event from the base class
    so it can't react to it. Pretty sure this would work: [..]

 i'm going with the below, it works so far

int Brwsr::handle(int e) {

if(e == FL_SHORTCUT) return EVNOTHANDLED; // (aka 0)

// process tab and shift-tab -- they give other widgets the focus
if(e == FL_KEYBOARD && (! (Fl::event_state() & (FL_CTRL | FL_ALT | FL_META)))) {
if(Fl::event_key() == FL_Tab) {
// ...
}
}
return Fl_Hold_Browser::handle(e);     // let other events/keystrokes do whatever they want
}
There is also a callback, not shown, and I was not clear on whether it would play nice with the handle, but now it seems obvious :)

Thanks again Greg and everybody for insight.
-dave
Reply all
Reply to author
Forward
0 new messages