fltk/test/sudoku - more keyboard support

16 views
Skip to first unread message

Mark

unread,
May 31, 2021, 4:50:13 AM5/31/21
to fltk.general
Tucked away in fltk's test directory is a rather nice sudoku game.
I've been trying to do some modifications. The main one being:
```
  // Show N cells...
  count = (11 * (5 - difficulty_)) - 1;
```
By adding `- 1` the Hard level defaults to 32 filled in cells which appears to be the standard (cf https://en.wikipedia.org/wiki/Sudoku ). Before it was 33.

I also changed the font from Helvetica Bold to Helvetica Roman.

But the changes I'd really like to make are to add keyboard support for: Home, End, PgUp, PgDown to move the focus to the left-most cell in the current row, right-most cell in the current row, top-most cell in the current column, and bottom-most cell in the current column.

Could anyone give me some help with doing this please?

Albrecht Schlosser

unread,
May 31, 2021, 11:43:06 AM5/31/21
to fltkg...@googlegroups.com
On 5/31/21 10:15 AM 'Mark' via fltk.general wrote:
Tucked away in fltk's test directory is a rather nice sudoku game.
I've been trying to do some modifications. The main one being:
```
  // Show N cells...
  count = (11 * (5 - difficulty_)) - 1;
```
By adding `- 1` the Hard level defaults to 32 filled in cells which appears to be the standard (cf https://en.wikipedia.org/wiki/Sudoku ). Before it was 33.

Hmm, nice to know, but not a hard restriction, is it? The article says "no more than 32" which is still not true for the "Medium" and "Easy" levels.


I also changed the font from Helvetica Bold to Helvetica Roman.

Just a matter of taste, I think?


But the changes I'd really like to make are to add keyboard support for: Home, End, PgUp, PgDown to move the focus to the left-most cell in the current row, right-most cell in the current row, top-most cell in the current column, and bottom-most cell in the current column.

Could anyone give me some help with doing this please?

Interesting, this looks a little more difficult.

I think the main task is to set the Fl::focus() widget to an arbitrary (calculated) cell, depending on the keystroke, i.e. in class Sudoku this would be something like

  grid_cells_[i][j].take_focus();

The question is how to find the index i and j and how (in what context) to execute the take_focus() statement. Unfortunately the cells don't "know" their own indices, but that's probably not necessary.

One possible approach would be to find the index by testing which cell has the focus (Fl::focus()) and then calculate the new cell index. This could be done in a handle() method added to the Sudoku class. Pseudo code:

  int Sudoku::handle(int e) {
    switch(e) {
      case FL_KEYDOWN:
      case FL_SHORTCUT:
        int i, j;
        // 1) find the cell with focus -> i, j
        // 2) calculate new cell index depending on key value
       
grid_cells_[i][j].take_focus();
        return 1;
      default:
        break;
    }
    return Fl_Window::handle(e);
  }


This is only one potential way which might work (untested) with only a few code changes. I'm sure there are different ways...

I hope this can give you a start at least. Have fun!

Mark

unread,
Jun 1, 2021, 4:39:12 AM6/1/21
to fltk.general
I trie adding these methods:
```
int Sudoku::handle(int event) {
    if (event == FL_KEYBOARD) {
    int j, k;
    if (Fl::event_key(FL_Home)) {
        find_focus_cell(&j, &k);
        printf("home %d %d\n", j, k);
        return 1;
    }
    else if (Fl::event_key(FL_End)) {
        find_focus_cell(&j, &k);
        printf("end %d %d\n", j, k);
        return 1;
    }
    else if (Fl::event_key(FL_Page_Down)) {
        find_focus_cell(&j, &k);
        printf("pg down %d %d\n", j, k);
        return 1;
    }
    else if (Fl::event_key(FL_Page_Up)) {
        find_focus_cell(&j, &k);
        printf("pg up %d %d\n", j, k);
        return 1;
    }
    }
    return Fl_Widget::handle(event);
}

void Sudoku::find_focus_cell(int *x, int *y) {
    *x = *y = -1;
    auto focus_widget = Fl::focus();
    if (focus_widget != nullptr) {
    int j, k;
    for (j = 0; j < 9; ++j) {
        for (k = 0; k < 9; ++k) {
        if (grid_cells_[j][k] == focus_widget) {
            *x = j;
            *y = k;
            return;
        }
        }
    }
    }
}
```

The result is that the _only_ keyboard events that are recognized are Home, End, PageUp, PageDown: none of the normal arrow key and numbers etc., nor the menus work anymore!

Also my `find_focus_cell()` function never works; it always leaves the values at `-1`. This turns out not to be a surprise because FL_SHORTCUT only happens if Fl::focus() returns nullptr. However, to avoid this I use FL_KEYBOARD. But it still doesn't work.

So clearly I have a long way to go understanding how FLTK event-handling works!

Albrecht Schlosser

unread,
Jun 1, 2021, 6:28:45 AM6/1/21
to fltkg...@googlegroups.com
Hi Mark, please don't top-post here, see
https://en.wikipedia.org/wiki/Posting_style#Top-posting


On 6/1/21 10:36 AM 'Mark' via fltk.general wrote:
I trie adding these methods:
```
int Sudoku::handle(int event) {
    if (event == FL_KEYBOARD) {
    // ...
    }
    return Fl_Widget::handle(event);
}
```

The result is that the _only_ keyboard events that are recognized are Home, End, PageUp, PageDown: none of the normal arrow key and numbers etc., nor the menus work anymore!

You're pretty close, but ...

Point 1: you must call Fl_[Double_]Window::handle(event) rather than Fl_Widget::handle(event). Your handle() method short-circuited all the "magic" of Fl_Group's event delivery. You should [almost] always use the handle() method of the direct parent (base) class. In this case I'd *guess* that using Fl_Group::handle(event) would work as well. I assume that most of your problems are caused by this fault.


Also my `find_focus_cell()` function never works; it always leaves the values at `-1`. This turns out not to be a surprise because FL_SHORTCUT only happens if Fl::focus() returns nullptr. However, to avoid this I use FL_KEYBOARD. But it still doesn't work.

Point 2: what you really need is the FL_SHORTCUT event. FL_KEYBOARD events are sent directly to the Fl::focus() widget which is in the sudoko program always a cell. If the focus widget doesn't use the event (returns 0) it is converted to FL_SHORTCUT and sent to (a lot of!) other widgets. At some point it will arrive at the Sudoku class which is why I suggested to add a handle() method to the Sudoku class.

That all said, I used your code with minimal changes (and I also added take_focus() statements) and it works as I expect:

int Sudoku::handle(int event) {
if (event == FL_SHORTCUT) { // needs FL_SHORTCUT
int j, k;
if (Fl::event_key(FL_Home)) {
find_focus_cell(&j, &k);
printf("home %d %d\n", j, k);
grid_cells_[j][0]->take_focus(); // ADDED
return 1;
} else if (Fl::event_key(FL_End)) {
find_focus_cell(&j, &k);
printf("end %d %d\n", j, k);
grid_cells_[j][8]->take_focus(); // ADDED
return 1;
} else if (Fl::event_key(FL_Page_Down)) {
find_focus_cell(&j, &k);
printf("pg down %d %d\n", j, k);
grid_cells_[8][k]->take_focus(); // ADDED
return 1;
} else if (Fl::event_key(FL_Page_Up)) {
find_focus_cell(&j, &k);
printf("pg up %d %d\n", j, k);
grid_cells_[0][k]->take_focus(); // ADDED
return 1;
}
}
return Fl_Double_Window::handle(event); // needs Fl_[Double_]Window::
}

So clearly I have a long way to go understanding how FLTK event-handling works!

It's not a simple model but if you read the docs (maybe not only once!) you'll get a feeling how it works step by step. Although in some cases it may be surprising and looking at the FLTK code and the comments may be helpful.

Besides reading the docs I suggest to take a look at test/handle_events.cxx which shows a way to print events in a better readable form. You can also modify it to find out what you need to know in practice.

Mark

unread,
Jun 1, 2021, 8:53:44 AM6/1/21
to fltk.general
Sorry for the top-posting: I'm using the not very good google groups. I'll try to remember to delete the ... in future.

Mark

unread,
Jun 1, 2021, 8:53:44 AM6/1/21
to fltk.general
Thank you very much. My final version of the function is:
```c++
int Sudoku::handle(int event) {
    if (event == FL_SHORTCUT) {
    int j, k;
    if (Fl::event_key(FL_Home)) {
        find_focus_cell(&j, &k);
        grid_cells_[j][0]->take_focus();
        return 1;
    }
    else if (Fl::event_key(FL_End)) {
        find_focus_cell(&j, &k);
        grid_cells_[j][8]->take_focus();
        return 1;
    }
    else if (Fl::event_key(FL_Page_Down)) {
        find_focus_cell(&j, &k);
        grid_cells_[8][k]->take_focus();
        return 1;
    }
    else if (Fl::event_key(FL_Page_Up)) {
        find_focus_cell(&j, &k);
        grid_cells_[0][k]->take_focus();
        return 1;
    }
    else if (*Fl::event_text() == 'm') { // 'm' for middle
        grid_cells_[4][4]->take_focus();

        return 1;
    }
    }
    return Fl_Double_Window::handle(event);
}
```

albrech...@gmail.com

unread,
Jun 1, 2021, 8:59:32 AM6/1/21
to fltk.general
Note: This message was sent from Google Groups.
I hope this works as intended...

On Tuesday, June 1, 2021 at 2:53:44 PM UTC+2 Mark wrote:
Sorry for the top-posting: I'm using the not very good google groups. I'll try to remember to delete the ... in future.

You can expand the '...', trim the citations, and edit your text below or inbetween or whatever is applicable. A certain amount of citation is appreciated, just as much as another person needs for context.
 
Reply all
Reply to author
Forward
0 new messages