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...
}
public:BrdCell(...) : Fl_Box(...) {
...
}int handle(int e) { return brd_handle(this, e); }static int 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 1int 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 workcase 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 shortcutscase 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 ignoredcase 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;
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
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 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.
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.
--
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...
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.
> > 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.
In most cases it's more appropriate to test Fl::event_text()
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:
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.
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.