FLTK logo

Re: [fltk.general] What is the right way to do these shortcuts?

FLTK matrix user chat room
(using Element browser app)   FLTK gitter user chat room   GitHub FLTK Project   FLTK News RSS Feed  
  FLTK Apps      FLTK Library      Forums      Links     Login 
 All Forums  |  Back to fltk.general  ]
 
Previous Message ]New Message | Reply ]Next Message ]

Re: What is the right way to do these shortcuts? Albrecht Schlosser May 14, 2021  
 
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.

--
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+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/fltkgeneral/53e89b08-fc7a-e94f-df4a-bd32ae89bc91%40online.de.
Direct Link to Message ]
 
     
Previous Message ]New Message | Reply ]Next Message ]
 
 

Comments are owned by the poster. All other content is copyright 1998-2024 by Bill Spitzak and others. This project is hosted by The FLTK Team. Please report site problems to 'erco@seriss.com'.