What is the right way to do these shortcuts?

26 views
Skip to first unread message

Dave Jordan

unread,
May 12, 2021, 6:13:59 AM5/12/21
to fltkg...@googlegroups.com
This method works except for 1 UI flaw and 1 obvious inefficiency, both of which are described under switch(e)
I know can't be doing it quite right. I have tried test_shortcut; no idea whether it's appropriate or what is the correct usage.

All the shortcuts keys are created here

class Appmenu : public Fl_Menu_Bar {
public:
Appmenu(App *app) : Fl_Menu_Bar(... dims ...) {
add("menu/save", FL_CTRL + 's', save_cb, app); // app is the main and only application window
.
.
.
}
};

App *app also contains an Fl_Group which contains > 200 subclassed Fl_Boxes.
I have written no explicit event handling in the Fl_Group.
If I do, the Group is going to get all the events before the Boxes do, and that will open a whole 'nother can of worms.

class Cell : public Fl_Box {
public:
BrdCell(...) : Fl_Box(...) {
...
}
int handle(int e) { return brd_handle(this, e); }
static int brd_handle(Cell *cell, int e);
};

int Cell::brd_handle(Cell *cell, int e) {

#define S_NORM 0x100000 // raw bits of event states. to do: also detect capslock
#define S_SHFT 0x110000
#define S_CTRL 0x140000
#define S_ALT 0x180000
#define EVNOTHANDLED 0
#define EVHANDLED 1

int s = Fl::event_state();
int k = Fl::event_key();

switch(e) {

// ***************************************************************************************************************************************
// The inefficiency is here in FL_SHORTCUT: every sibling cell is checked to see if it will handle the shortcut.
// Eventually the menu widget is checked and the shortcut happens.
// And i suppose this is also the source of the UI problem, wherein after all the searching about, if a shortcut function involves another
// cell taking focus, the old cell will not receive FL_UNFOCUS (leaving it highlighted).
// ***************************************************************************************************************************************

case FL_SHORTCUT: return EVNOTHANDLED; // also need ctrl+key to return nothandled for shortcuts to work
case FL_KEYBOARD:
switch(s) {
case S_NORM:
{ block-specific vars defined here
if k is alpha {
do stuff;
cell->redraw();
}
else {
if k == delete, tab, arrows {
box label gets deleted or one of various sibling cells ->take_focus()
or if the cell that is graphically last has focus, then text editor ->take_focus() (an uncle to the box widgets)
}
}
}
break;
case S_SHFT: cout << "brd state=shift" << endl; break;
case S_CTRL: cout << "brd state=ctrl" << endl; return EVNOTHANDLED; // seems to be necessary for shortcuts
case S_ALT: cout << "brd state=alt" << endl; break;
// default: not currently coded
} // end switch (state)
break;
case FL_FOCUS:
cell->color(hilite); cell->redraw(); break; // hilite color applies to all cells and is assigned per user option, elsewhere.
case FL_UNFOCUS:
cell->color(cell->lolite); cell->redraw(); break; // lolite color applies per cell
case FL_PUSH:
cell->take_focus(); break;

case FL_RELEASE: case FL_MOVE: case FL_ENTER: // remainder of events are ignored
case FL_LEAVE: case FL_SHOW: case FL_HIDE:
case FL_KEYUP: case FL_DRAG: case FL_ACTIVATE:
case FL_DEACTIVATE: case FL_NO_EVENT:
break;
default: cout << "wha???" << endl;
} // end switch(event)
return EVHANDLED;
}

Greg Ercolano

unread,
May 12, 2021, 10:29:27 AM5/12/21
to fltkg...@googlegroups.com


On 5/12/21 3:13 AM, Dave Jordan wrote:
This method works except for 1 UI flaw and 1 obvious inefficiency, both of which are described under switch(e)
I know can't be doing it quite right. I have tried test_shortcut; no idea whether it's appropriate or
what is the correct usage.

All the shortcuts keys are created here

class Appmenu : public Fl_Menu_Bar {
public:
Appmenu(App *app) : Fl_Menu_Bar(... dims ...) {
add("menu/save", FL_CTRL + 's', save_cb, app); // app is the main and only application window

    Hmm, I must be missing something.

    IN that one add("menu/save"..) line, FLTK has been told to invoke the save_cb() method
    if either the menu -> save is picked, or if someone hits Ctrl-S.

   
So you're done right there, nothing else to do.

    Unless you're trying to define other global shortcuts not defined in the menu..?
    In which case you'd create a handle() method that looks for FL_SHORTCUT
    events, and then look at the key combo being pressed to see if it's what you want, e.g.

        case FL_SHORTCUT:
            if ( Fl::event_key() == 'a' && Fl::event_state() & FL_Alt )   // SHIFT-a

    I'd say look at the docs on event handling first:
    https://www.fltk.org/doc-1.4/events.html

    Especially read about FL_FOCUS/UNFOCUS and FL_ENTER/LEAVE to be sure
    you're using the right ones.

    Some comments on the handle() code you posted..

    1) You shouldn't need to do this:

           int handle(int e) { return brd_handle(this, e); }
           static int brd_handle(Cell *cell, int e);

       You don't have to work with 'this' -- handle() is not a static callback,
       it's a virtual method you're overriding, and it's invoked by FLTK for you
       whenever an event is delivered in the context of the class.

    3) Only define a handle() method for your class if you need one.

       The default event handler in the base class (Fl_Box in this case) will handle the default behavior.

    4) If you define a handle() method, be sure to call the base class's handle() method
       so it can still see the events. If you don't, you'll be eclipsing all events from the base class,
       which is usually bad. In the below rewrite of your handle() method, I call Fl_Box::handle(e)
       at the top and keep its return value in ret, so any events that IT handles but you don't
       return the proper 0 or 1 value out of the handle() method so FLTK can process the event properly.
      
    5) Only check for events you need to use -- don't do anything with other events.
       For instance, don't try to add a case statement for every event you aren't
       dealing with, you simply return the base class's return value.

    Here's what I think your handle() method should probably look like, if you need one at all:

int Cell::handle(int e) {
    int ret = Fl_Box::handle(e);
    // IMPORTANT: let box's own handle() method get access to the event and keep track of its return value
                                    // If you don't do this, you'll be eclipsing events from the base class Fl_Box, which is generally bad
                                    // unless you know what
you're doing..

    switch (e) {
                    // now check for events you want to handle, /ignore/ the rest
        case FL_FOCUS:
           
// do your stuff here when the widget takes focus
            ret = 1;               
// tells FLTK you're interested in FOCUS events -- see docs on FL_FOCUS for specifics
            break;
        case FL_UNFOCUS:
           
// do your stuff here when the widget goes out of focus
            ret = 1;
            break;
        case FL_PUSH:
           
//
            // do your stuff. You may not need to call take_focus() unless you're doing something special
            //

            if ( you_did_something_with_the_event ) ret = 1;   // do this only if you actually handled the push event, and no one else should get it
            break;
    }
    return ret;
}

    That should be it.
    Thing is, I'm not sure you need a handle() method.

    You probably don't need to do specific stuff with take_focus() and such unless you're
    doing something very special.

    Also, I'm not sure you should be doing anything with FL_FOCUS. If you want the cell
    to light up when you hover over it, use FL_ENTER and FL_LEAVE instead, that might be
    what you want -- I'm not sure. See the docs at the above link and read what it says
    FL_ENTER/FL_LEAVE do vs FL_FOCUS/FL_UNFOCUS.

    Focus is mainly for keyboard oriented widgets, like if you were implementing a text input
    widget or something, and want to be able to take focus with a click or keyboard navigation
    of focus using TAB, etc.

Greg Ercolano

unread,
May 12, 2021, 11:05:21 AM5/12/21
to fltkg...@googlegroups.com

On 5/12/21 7:29 AM, Greg Ercolano wrote:

On 5/12/21 3:13 AM, Dave Jordan wrote:

This method works except for 1 UI flaw and 1 obvious inefficiency, both of which are described under switch(e)
I know can't be doing it quite right. I have tried test_shortcut; no idea whether it's appropriate or
what is the correct usage.

All the shortcuts keys are created here

class Appmenu : public Fl_Menu_Bar {
public:
Appmenu(App *app) : Fl_Menu_Bar(... dims ...) {
add("menu/save", FL_CTRL + 's', save_cb, app); // app is the main and only application window

    Hmm, I must be missing something.

    IN that one add("menu/save"..) line, FLTK has been told to invoke the save_cb() method
    if either the menu -> save is picked, or if someone hits Ctrl-S.

   
So you're done right there, nothing else to do.

    Oh, and by nothing else to do, I meant you don't need a handle() method at all
    to make that work.

    All the handling of the shortcuts has been defined already in that one add() line,
    and FLTK will invoke your callback functions (like save_cb()) for you.

lifeatt...@gmail.com

unread,
May 12, 2021, 8:59:42 PM5/12/21
to fltk.general
>Also, I'm not sure you should be doing anything with FL_FOCUS. If you want the cell to light up when you 
>hover over it, use FL_ENTER and FL_LEAVE instead, that might be what you want -- I'm not sure.

A valid point about cell highlighting. But in the OP's pseudo-code there is this section:

case FL_KEYBOARD:
switch(s) {
case S_NORM:{ block-specific vars defined here; if k is alpha {do stuff;cell->redraw();} ...

So FL_FOCUS/FL_UNFOCUS handling is necessary to receive keyboard events.

By not calling FL_BOX::handle() in Cell::handle(), the app/main function is likely not receiving the CTRL+S shortcut 
for the save menu. [Unless my understanding of events is wrong]

Kevin

Dave Jordan

unread,
May 12, 2021, 9:15:32 PM5/12/21
to fltkg...@googlegroups.com
--

I'm pretty sure i need to grab the focus/unfocus events to correctly color the cells.
both the highlight and the unhighlight colors are supposed to be user options.
also, re FL_KEYBOARD, there's a lot of processing going on in the "do stuff," like keystroke table lookup,
counting vowels entered vs. consonants, some other counting functions...

i'm in the process now of adding the base class handle call to the custom handle, and using ret value.

pretty soon i'm going to post this beast somewhere -- github seems a little over the top for my needs -- if there are any other suggestions I'm all ears.

-dave

Dave Jordan

unread,
May 13, 2021, 4:07:48 AM5/13/21
to fltkg...@googlegroups.com
ok, well, i can't get shortcuts working the canonical way, but with some trial and error i have their equivalents working and i believe i now have a pretty good mouse/keyboard handler.
thanks y'all for your input.
-dave

Albrecht Schlosser

unread,
May 13, 2021, 4:49:07 AM5/13/21
to fltkg...@googlegroups.com
On 5/12/21 12:13 PM Dave Jordan wrote:
> This method works except for 1 UI flaw and 1 obvious inefficiency, both
> of which are described under switch(e)

Others did already reply with some good stuff, hence I'm trying to be
short and precise with other technical info.

> I know can't be doing it quite right. I have tried test_shortcut; no
> idea whether it's appropriate or what is the correct usage.

Please describe what you want to achieve, this would make it easier to
understand what doesn't work.

> All the shortcuts keys are created here
>
> class Appmenu : public Fl_Menu_Bar {
...

Okay, that should work.

> App *app also contains an Fl_Group which contains > 200 subclassed Fl_Boxes.
> I have written no explicit event handling in the Fl_Group.
> If I do, the Group is going to get /all/ the events before the Boxes do,
> and that will open a whole 'nother can of worms.

Why? It could really be helpful if the group would get the events before
the children so you could filter yourself which events to propagate to
the children and which not to propagate.

> class Cell : public Fl_Box {
>
> int handle(int e) { return brd_handle(this, e); }
> static int brd_handle(Cell *cell, int e);

As Greg wrote, that's an unnecessary extra step, you don't need the
static method brd_handle(). Just use Cell::handle() for your stuff.

> int Cell::brd_handle(Cell *cell, int e) {
>
> #define S_NORM 0x100000 // raw bits of event states. to do: also
> detect capslock
> #define S_SHFT 0x110000
> #define S_CTRL 0x140000
> #define S_ALT 0x180000

*NEVER* do this, i.e. define your own macros with explicit bit values,
use the symbolic names instead. That's why they are defined. Additional
information: we recently discussed /changing/ some of these bit values
which would break your code because you used bit numbers!

So "translating your code back" to defined names:

#define S_NORM FL_NUM_LOCK // raw bits ...
#define S_SHFT (FL_NUM_LOCK | FL_SHIFT)
#define S_CTRL (FL_NUM_LOCK | FL_CTRL)
#define S_ALT (FL_NUM_LOCK | FL_ALT)

Note that these bits are intended to be used to test if a particular bit
is set (i.e. a key is pressed or ON (NumLock)). You are testing all
these states for exact values which would fail, for instance, if a user
holds a mouse button pressed while typing a key. That's not unusual,
BTW, I have an application where I can press keys while dragging the mouse.

I also don't understand why you defined

#define S_NORM 0x100000

This bit is defined in Enumeration.H:

#define FL_NUM_LOCK 0x00100000

According to your logic below you require NumLock to be active for every
FL_KEYBOARD event you handle (?). That seems wrong, but see above: you
didn't describe /what/ exactly you want to achieve...

> int s = Fl::event_state();

So, given the bit structure of the defined constants you might at least
want to change this to

int s = Fl::event_state() & (FL_NUM_LOCK|FL_SHIFT|FL_CTRL|FL_ALT);

which would ignore all other state bits than those you're interested in
(leaving the rest of your tests as-is).

> int k = Fl::event_key();
>
> switch(e) {
>
> // The inefficiency is here in FL_SHORTCUT: every sibling cell
> is checked to see if it will handle the shortcut.

This is intended. It may not be obvious but that's how shortcuts work.

Here it /might/ be useful to implement the handle() method of the parent
group so you can filter events and don't propagate FL_SHORTCUT events to
the children if you really care about that "inefficiency".

> // And i suppose this is also the source of the UI problem,
> wherein after all the searching about, if a shortcut
> function involves another
>
> // cell taking focus, the old cell will not receive FL_UNFOCUS
> (leaving it highlighted).

Hmm, I believe this /should/ work. If you call take_focus() and the
focus is assigned to the other cell the current Fl::focus() widget
should IMHO get the FL_UNFOCUS event.

> case FL_SHORTCUT: return EVNOTHANDLED; // also need ctrl+key to
> return nothandledfor shortcuts to work

As Greg already wrote, you should only add cases for events you want to
handle. But see below for another bug...

> case FL_KEYBOARD:

This will only be triggered if the cell has the input focus, but you
know that.

> switch(s) {
>
> case S_NORM:

As I wrote above, case S_NORM seems to be wrong because there are too
many bit combinations and the user needs to activate NumLock. But it may
be that that's what you want?

> case S_SHFT: cout << "brd state=shift" << endl; break;
>
> case S_CTRL: cout << "brd state=ctrl" << endl; return
> EVNOTHANDLED; // seems to be necessary for shortcuts

See "bug" below.

> case S_ALT: cout << "brd state=alt" << endl; break;
>
> // default: not currently coded
>
> } // end switch (state)
>
> break;
> ...
> case FL_RELEASE: case FL_MOVE: case FL_ENTER:// remainder of
> events are ignored

That comment is not true. These events are not handled, but they are not
"ignored": see below.

> case FL_LEAVE: case FL_SHOW: case FL_HIDE:
>
> case FL_KEYUP: case FL_DRAG: case FL_ACTIVATE:
>
> case FL_DEACTIVATE: case FL_NO_EVENT:
>
> break;
>
> default: cout << "wha???" << endl;
>
> } // end switch(event)

Here's the bug I mentioned before:

The events you want to be ignored by using 'break;' transfer control
after the switch statement and thus

> return EVHANDLED;

which tells FLTK that you /handled/ the event. That means that the event
is actually _dropped without handling_ it at all and it can't be handled
by any other widget. If that's what you mean with "ignored", well, then
yes, but that's in FLTK event handling two different things and you
should be aware of this.

Albrecht Schlosser

unread,
May 13, 2021, 7:30:06 AM5/13/21
to fltkg...@googlegroups.com
Update: with demo program ...

On 5/13/21 10:49 AM Albrecht Schlosser wrote:
> On 5/12/21 12:13 PM Dave Jordan wrote:

>> App *app also contains an Fl_Group which contains > 200 subclassed
>> Fl_Boxes.
>> I have written no explicit event handling in the Fl_Group.
>> If I do, the Group is going to get /all/ the events before the Boxes
>> do, and that will open a whole 'nother can of worms.
>
> Why? It could really be helpful if the group would get the events before
> the children so you could filter yourself which events to propagate to
> the children and which not to propagate.
...
> Here it /might/ be useful to implement the handle() method of the parent
> group so you can filter events and don't propagate FL_SHORTCUT events to
> the children if you really care about that "inefficiency".

Note: I didn't show this in the demo program.

>>         // And i suppose this is also the source of the UI problem,
>>         wherein after all the searching about, if a shortcut
>>         function involves another
>>
>>         // cell taking focus, the old cell will not receive FL_UNFOCUS
>>         (leaving it highlighted).
>
> Hmm, I believe this /should/ work. If you call take_focus() and the
> focus is assigned to the other cell the current Fl::focus() widget
> should IMHO get the FL_UNFOCUS event.

This focus handling stuff is demonstrated in the attached
handle_focus.cxx demo program. The focus widget is highlighted
(FL_YELLOW) and the box type of the widget below the mouse is changed to
FL_UP_BOX (which would normally also use a different highlight color).

You can type a, b, c, or d (lowercase) to change the focus to the named
box which is handled by the focus widget as FL_KEYBOARD event.

You can also type the uppercase characters A, B, C, and D to move the
focus to the respective cell. This is handled by an FL_SHORTCUT event
since uppercase letters are ignored by the focus widget. Note that FLTK
switches the case from uppercase to lowercase after an attempt to
deliver the uppercase FL_SHORTCUT event to /all/ widgets.

Losing the focus and getting it back can also be seen if you click on
another window and back on the test program.

@Dave: your UI issue (not clearing the highlight color when losing the
focus) might be caused by a missing redraw. If your window is resizable
you can see if this is the case when you resize the window because this
would likely redraw every widget.
handle_focus.cxx

Dave Jordan

unread,
May 13, 2021, 2:57:40 PM5/13/21
to fltkg...@googlegroups.com

Thanks, Albrecht, for putting in as much effort as is apparent here.
Below are comments, clarifications, and a few much simpler questions.

On Thu, May 13, 2021 at 4:49 AM Albrecht Schlosser <Albrech...@online.de> wrote:
On 5/12/21 12:13 PM Dave Jordan wrote:
> This method works except for 1 UI flaw and 1 obvious inefficiency, both
> of which are described under switch(e)

Others did already reply with some good stuff, hence I'm trying to be
short and precise with other technical info.

> I know can't be doing it quite right. I have tried test_shortcut; no
> idea whether it's appropriate or what is the correct usage.

Please describe what you want to achieve, this would make it easier to
understand what doesn't work.

> All the shortcuts keys are created here
>
> class Appmenu : public Fl_Menu_Bar {
...

Okay, that should work.

> App *app also contains an Fl_Group which contains > 200 subclassed Fl_Boxes.
> I have written no explicit event handling in the Fl_Group.
> If I do, the Group is going to get /all/ the events before the Boxes do,
> and that will open a whole 'nother can of worms.

Why? It could really be helpful if the group would get the events before
the children so you could filter yourself which events to propagate to
the children and which not to propagate.

The can of worms is not the potential usefulness or lack thereof but merely the extra work i would have of going back thru all the modules
and getting the new object references correct. I could see it maybe as a necessity if i think of some new feature that won't work otherwise...
> class Cell : public Fl_Box {
>
>     int handle(int e) { return brd_handle(this, e); }
>     static int brd_handle(Cell *cell, int e);

As Greg wrote, that's an unnecessary extra step, you don't need the
static method brd_handle(). Just use Cell::handle() for your stuff.

I have the vague idea that i'm telling the compiler not to include all the handling code in every instantiation. i have seen statically declared methods in fltk examples before .
> int Cell::brd_handle(Cell *cell, int e) {
>
>     #define S_NORM 0x100000 // raw bits of event states. to do: also
>     detect capslock
>     #define S_SHFT 0x110000
>     #define S_CTRL 0x140000
>     #define S_ALT 0x180000

*NEVER* do this, i.e. define your own macros with explicit bit values,

saw that coming!
use the symbolic names instead. That's why they are defined. Additional
information: we recently discussed /changing/ some of these bit values
which would break your code because you used bit numbers!

So "translating your code back" to defined names:

#define S_NORM FL_NUM_LOCK // raw bits ...
#define S_SHFT (FL_NUM_LOCK | FL_SHIFT)
#define S_CTRL (FL_NUM_LOCK | FL_CTRL)
#define S_ALT  (FL_NUM_LOCK | FL_ALT)

Note that these bits are intended to be used to test if a particular bit
is set (i.e. a key is pressed or ON (NumLock)). You are testing all
these states for exact values which would fail, for instance, if a user
holds a mouse button pressed while typing a key. That's not unusual,
BTW, I have an application where I can press keys while dragging the mouse.

I also don't understand why you defined

   #define S_NORM 0x100000

This bit is defined in Enumeration.H:

   #define FL_NUM_LOCK     0x00100000

i must have had numlock on while i was generating those bit values. Definitely not what i meant to do.
but they're all gone for not being so useful after all, and for the reasons you stated. I do have enough training to know
it wasn't kosher in the first place :)
 
According to your logic below you require NumLock to be active for every
FL_KEYBOARD event you handle (?). That seems wrong, but see above: you
didn't describe /what/ exactly you want to achieve...

>     int s = Fl::event_state();

So, given the bit structure of the defined constants you might at least
want to change this to

       int s = Fl::event_state() & (FL_NUM_LOCK|FL_SHIFT|FL_CTRL|FL_ALT);
 
which would ignore all other state bits than those you're interested in
(leaving the rest of your tests as-is).

>     int k = Fl::event_key();

what if i want ctrl-x but not alt-x and not ctrl-alt-x?

>     switch(e) {
>
>         // The inefficiency is here in FL_SHORTCUT: every sibling cell
>         is checked to see if it will handle the shortcut.

This is intended. It may not be obvious but that's how shortcuts work.
yes i saw in the docs how FLTK "tries really hard" and its good.

Here it /might/ be useful to implement the handle() method of the parent
group so you can filter events and don't propagate FL_SHORTCUT events to
the children if you really care about that "inefficiency".

>         // And i suppose this is also the source of the UI problem,
>         wherein after all the searching about, if a shortcut
>         function involves another
>
>         // cell taking focus, the old cell will not receive FL_UNFOCUS
>         (leaving it highlighted).

Hmm, I believe this /should/ work. If you call take_focus() and the
focus is assigned to the other cell the current Fl::focus() widget
should IMHO get the FL_UNFOCUS event.

there is something in one of the functions that causes a cell to "forget it has the focus"
i have a workaround for now, and i may become moot soon b/c i think i know what it is.
Ok i just checked and the save() function calls the deactivate method of the container for the cells. that seems like it could do it.
will test and get back.

>         case FL_SHORTCUT: return EVNOTHANDLED; // also need ctrl+key to
>         return nothandledfor shortcuts to work

As Greg already wrote, you should only add cases for events you want to
handle. But see below for another bug...

 
Here's the bug I mentioned before:


The events you want to be ignored by using 'break;' transfer control
after the switch statement and thus

>     return EVHANDLED;

which tells FLTK that you /handled/ the event. That means that the event
is actually _dropped without handling_ it at all and it can't be handled
by any other widget. If that's what you mean with "ignored", well, then
yes, but that's in FLTK event handling two different things and you
should be aware of this.

right, i had those "ignored" cases there so i could see "interesting" event names without scrolling thru hundreds of FL_ENTER and FL_LEAVE
which are irrelevant to everything so far in my UI design.

Albrecht Schlosser

unread,
May 14, 2021, 10:15:02 AM5/14/21
to fltkg...@googlegroups.com
On 5/13/21 8:57 PM Dave Jordan wrote:
>
> Thanks, Albrecht, for putting in as much effort as is apparent here.

Welcome!

> Below are comments, clarifications, and a few much simpler questions.
>
> On Thu, May 13, 2021 at 4:49 AM Albrecht Schlosser wrote:
> On 5/12/21 12:13 PM Dave Jordan wrote:
>
> > App *app also contains an Fl_Group which contains > 200
> subclassed Fl_Boxes.
> > I have written no explicit event handling in the Fl_Group.
> > If I do, the Group is going to get /all/ the events before the
> Boxes do,
> > and that will open a whole 'nother can of worms.
>
> Why? It could really be helpful if the group would get the events
> before
> the children so you could filter yourself which events to propagate to
> the children and which not to propagate.
>
> The can of worms is not the potential usefulness or lack thereof but
> merely the extra work i would have of going back thru all the modules
> and getting the new object references correct. I could see it maybe as a
> necessity if i think of some new feature that won't work otherwise...

Hmm, I can't see that implementing a handle() method in your own group
widget (derived from Fl_Group) would change anything of the internal
logic. An example handle method might look like this:

MyGroup::handle(int ev) {

if (ev == FL_SHORTCUT) {
// preprocess event, decide which widgets
// should get it, maybe ignore it ?

if (some_condition ...)
return 0; // do NOT process, ignore
else if (uppercase...)
return 0;
}
// Process all other events:
return Fl_Group::handle(ev);
}

Note that this handle method is designed to *filter* events so they
don't reach the children, hence Fl_Group::handle(ev) is called /after/
the filter mechanism decided to ignore some events.

This handle() method could be the only method you need to implement,
except maybe constructor and destructor. The only necessary change would
be to change Fl_Group to MyGroup in the parent group widget of your cells.

This should be transparent to the rest of the implementation -- but I
don't know your code...

> > class Cell : public Fl_Box {
> >
> >     int handle(int e) { return brd_handle(this, e); }
> >     static int brd_handle(Cell *cell, int e);
>
> As Greg wrote, that's an unnecessary extra step, you don't need the
> static method brd_handle(). Just use Cell::handle() for your stuff.
>
> I have the vague idea that i'm telling the compiler not to include all
> the handling code in every instantiation.

That vague idea is wrong. Code is never included in object instantiations.

> i have seen statically declared methods in fltk examples before .

Yes, those static methods exist. Imagine such methods as being "class
methods" rather than "object methods". There are two main reasons to use
static methods:

(1) Implementation of special constructors or for instance counters per
class, one example being an object id. The static counter is initialized
to 0 and every object calls the method

static int MyClass::get_id();

which increments the global/class counter and returns the id.

(2) FLTK callback functions must be static. Here it's the static method
that is used as the callback which then calls the (non-static) object
method for easier implementation. This is the opposite (calling
sequence) of what you were doing.

> > int Cell::brd_handle(Cell *cell, int e) {
> >
> >     #define S_NORM 0x100000 // raw bits of event states. to do: also
> >     detect capslock
> >     #define S_SHFT 0x110000
> >     #define S_CTRL 0x140000
> >     #define S_ALT 0x180000
>
> *NEVER* do this, i.e. define your own macros with explicit bit values,
>
> saw that coming!

;-)

> use the symbolic names instead. That's why they are defined. Additional
> information: we recently discussed /changing/ some of these bit values
> which would break your code because you used bit numbers!
>
> So "translating your code back" to defined names:
>
> #define S_NORM FL_NUM_LOCK // raw bits ...
> #define S_SHFT (FL_NUM_LOCK | FL_SHIFT)
> #define S_CTRL (FL_NUM_LOCK | FL_CTRL)
> #define S_ALT  (FL_NUM_LOCK | FL_ALT)
>
> Note that these bits are intended to be used to test if a particular
> bit is set (i.e. a key is pressed or ON (NumLock)).
>[...]
> >     int s = Fl::event_state();
>
> So, given the bit structure of the defined constants you might at least
> want to change this to
>
>        int s = Fl::event_state() &
> (FL_NUM_LOCK|FL_SHIFT|FL_CTRL|FL_ALT);
>
> which would ignore all other state bits than those you're interested in
> (leaving the rest of your tests as-is).
>
> >     int k = Fl::event_key();
>
> what if i want ctrl-x but not alt-x and not ctrl-alt-x?

First of all: testing Fl::event_key() is often the wrong test,
particularly if it comes to special keys or keys with NumLock on or off
or keys on international or OS specific keyboards. For instance, the
'key' you press on the numeric keypad is the same whether NumLock is on
or off, but the 'text' it generates is different. If I want to use the
'~' character I need to press AltGr/+ on my German PC (Notebook)
keyboard but Option/n on my MacBook.

In most cases it's more appropriate to test Fl::event_text() which is
the text created by the keypress -- in the example above that would be
"~". Think also of UTF-8 characters ...

Back to your question, short answer: that's what bit tests are used for.
Test specific bits...

Long answer: let's assume you want to ignore NumLock and you are only
interested in modifier keys SHIFT, CTRL, and ALT and all combinations.
The first step is to "mask out" all other event state bits as shown
above, but modified:

int s = Fl::event_state() & (FL_SHIFT|FL_CTRL|FL_ALT);

Now s can only be one of 8 different values and you can proceed with a
switch/case:

switch (s) {
case FL_CTRL:
process_ctrl_x();
break;
case FL_ALT:
process_alt_x();
break
default:
break;
}

or you can test for special combinations like this:

if (s == FL_CTRL|FL_ALT)
process_ctrl_alt_x();

etc. ...

Note that it's important to mask the irrelevant bits (mouse buttons
etc.) out if you want to test with "==" or a switch, but you can always
test for individual bits alone:

int ctrl = Fl::event_state() & FL_CTRL;
int alt = Fl::event_state() & FL_ALT;
int shift = Fl::event_state() & FL_SHIFT;

if (ctrl && shift && !alt)
do_something();

There are endless possibilities, the best way depends on what you want
to achieve.

That said, this is in parts basic programming and not FLTK specific but
I wanted to mention different ways to test for FLTK event states.

> >         // And i suppose this is also the source of the UI problem,
> >         wherein after all the searching about, if a shortcut
> >         function involves another
> >
> >         // cell taking focus, the old cell will not receive
> FL_UNFOCUS
> >         (leaving it highlighted).
>
> Hmm, I believe this /should/ work. If you call take_focus() and the
> focus is assigned to the other cell the current Fl::focus() widget
> should IMHO get the FL_UNFOCUS event.

See my demo program which shows that it *does* work fine.

> there is something in one of the functions that causes a cell to "forget
> it has the focus"
> i have a workaround for now, and i may become moot soon b/c i think i
> know what it is.
> Ok i just checked and the save() function calls the deactivate method of
> the container for the cells. that seems like it could do it.
> will test and get back.

Okay, that's specific to your code then. Glad you found it.

> Here's the bug I mentioned before:
>
> The events you want to be ignored by using 'break;' transfer control
> after the switch statement and thus
>
> >     return EVHANDLED;
>
> which tells FLTK that you /handled/ the event. That means that the
> event
> is actually _dropped without handling_ it at all and it can't be
> handled
> by any other widget. If that's what you mean with "ignored", well, then
> yes, but that's in FLTK event handling two different things and you
> should be aware of this.
>
>
> right, i had those "ignored" cases there so i could see "interesting"
> event names without scrolling thru hundreds of FL_ENTER and FL_LEAVE
> which are irrelevant to everything so far in my UI design.

The important point is not that you had these cases in your handle
method but that you returned 1 (EVHANDLED) which lets FLTK drop this
event after your handle method got it. Just logging the event is
supposedly not "handling" it, so you may eclipse these events from other
widgets that would otherwise handle them.

Dave Jordan

unread,
May 15, 2021, 8:49:07 PM5/15/21
to fltkg...@googlegroups.com
im starting to like the idea of filtering this way

>      > class Cell : public Fl_Box {
>      >
>      >     int handle(int e) { return brd_handle(this, e); }
>      >     static int brd_handle(Cell *cell, int e);
>
>     As Greg wrote, that's an unnecessary extra step, you don't need the
>     static method brd_handle(). Just use Cell::handle() for your stuff.
>
> I have the vague idea that i'm telling the compiler not to include all
> the handling code in every instantiation.

That vague idea is wrong. Code is never included in object instantiations.

good to know -- changes implemented

In most cases it's more appropriate to test Fl::event_text() 

perish the thought! The part of my event processing that needs to be as responsive as possible deals with individual keystrokes.
If i have to start strcmping this input its going to ruin my day.
OTOH, if non-EN-US keyboards prevent FLTK from using event_key() then I guess i'll be forced to look at event_text().
What about output, you may ask.
If you know Scrabble and its ilk (which is what my app is all about), you know that there are typically wildcard tiles that are blank.
But blank doesn't cut it for the UI, so I've already built the capability of mapping from any keystroke to any glyph that the user can compose.

Back to your question, short answer: that's what bit tests are used for.
Test specific bits...

   int s = Fl::event_state() & (FL_SHIFT|FL_CTRL|FL_ALT);

Now s can only be one of 8 different values and you can proceed with a
switch/case:

examples of masking very helpful, thanks

The important point is not that you had these cases in your handle
method but that you returned 1 (EVHANDLED) which lets FLTK drop this
event after your handle method got it. Just logging the event is
supposedly not "handling" it, so you may eclipse these events from other
widgets that would otherwise handle them.

point well taken.

-dave

Ian MacArthur

unread,
May 16, 2021, 4:42:12 PM5/16/21
to fltkg...@googlegroups.com
On 16 May 2021, at 01:48, Dave Jordan wrote:
>
> perish the thought! The part of my event processing that needs to be as responsive as possible deals with individual keystrokes.
> If i have to start strcmping this input its going to ruin my day.

Hmm - do you actually *know* this is slow? When I read this, if I’m honest, my first thought was “premature optimization”...

My experience suggest strongly that the simple way might well be more than fast enough, on any recent computer.
I’d strongly suggest trying the easy thing first, and only doing the complicated “manual” handling if it actually proves necessary (which I suspect it will not, TBH.)


> OTOH, if non-EN-US keyboards prevent FLTK from using event_key() then I guess i'll be forced to look at event_text().

Indeed... amongst other things.

> What about output, you may ask.
> If you know Scrabble and its ilk (which is what my app is all about), you know that there are typically wildcard tiles that are blank.
> But blank doesn't cut it for the UI, so I've already built the capability of mapping from any keystroke to any glyph that the user can compose.
>

My experience of Scrabble suggests it is not exactly a fast-paced game where serious optimisations are required to support effective gameplay...




Albrecht Schlosser

unread,
May 16, 2021, 5:12:26 PM5/16/21
to fltkg...@googlegroups.com
On 5/16/21 10:42 PM Ian MacArthur wrote:
> On 16 May 2021, at 01:48, Dave Jordan wrote:
>> perish the thought! The part of my event processing that needs to be as responsive as possible deals with individual keystrokes.
>> If i have to start strcmping this input its going to ruin my day.
> Hmm - do you actually *know* this is slow? When I read this, if I’m honest, my first thought was “premature optimization”...
>
> My experience suggest strongly that the simple way might well be more than fast enough, on any recent computer.
> I’d strongly suggest trying the easy thing first, and only doing the complicated “manual” handling if it actually proves necessary (which I suspect it will not, TBH.)
>
>
>> OTOH, if non-EN-US keyboards prevent FLTK from using event_key() then I guess i'll be forced to look at event_text().
> Indeed... amongst other things.

See attached example: US-English, German, French keyboards with one
example keypress in FLTK's test/keyboard, all on Linux (I didn't bother
to take macOS screenshots).

The actual keyboard I used was a German (notebook) keyboard, set up as
English (US) keyboard, i.e. the key I pressed would have been '/' on the
US keyboard, hence Fl::event_text() is "/" whereas Fl::event_key() is
minus ('-') which is the key on my German keyboard. This is a special
test case but there are many more reasons why one should prefer
Fl::event_text().

Note: in most (US-ASCII) cases Fl::event_text()[0] is the text
representation of the ASCII character, but with UTF-8 and such things
like German Umlauts (needed n scrabble games) things are different.

Albrecht Schlosser

unread,
May 16, 2021, 5:13:26 PM5/16/21
to fltkg...@googlegroups.com
Added missing attachment (PDF).
keyboards-fltk.pdf

pvr...@btinternet.com

unread,
May 17, 2021, 4:05:43 AM5/17/21
to fltk.general
British and US keyboards are different, as I know  to my cost. I had " (Shift-2 in UK) in a password and my work laptop wanted the password while in BIOS mode when the keyboard was still in US mode. I had to phone IT to find out where the " character was on a US keyboard.

Phil.

pvr...@btinternet.com

unread,
May 17, 2021, 4:36:04 AM5/17/21
to fltk.general
On Monday, May 17, 2021 at 9:05:43 AM UTC+1 pvr...@btinternet.com wrote:
British and US keyboards are different, as I know  to my cost. I had " (Shift-2 in UK) in a password and my work laptop wanted the password while in BIOS mode when the keyboard was still in US mode. I had to phone IT to find out where the " character was on a US keyboard.

Phil.


Apologies for top-posting, I'm not used to using the web interface.

Phil.

Dave Jordan

unread,
May 17, 2021, 11:56:00 PM5/17/21
to fltkg...@googlegroups.com
thanks everybody for the input!
 
i have FL_SHORTCUTs working the way they were intended now.

I have figured out how to unmap & remap key functions in Fl_Text_Editor

the apparent multiple/missing un/focus vents were caused by a shortcut
that wended its way thru a good bit of code and hit a window->hide() followed by ->show
which was just there for aesthetics and which turned out to be unnecessary.

re event_key vs event_text: i am aware different countries/language use many symbols not found on US keyboards.
What i need to see is whether event_key will report them as unique integers, maybe not across the whole world (altho it could)
but within a user's installation, which i hope is not too much to ask.

regardless, the app is fleshing out nicely. Still looking for somewhere to be hosted. NOT the git.

-dave


Reply all
Reply to author
Forward
0 new messages