This is an engineering notebook post concerning Leo's key-handling code. Please feel free to ignore unless you might someday maintain this code.
This is a "thinking out loud post", inspired by the recent difficulties in the "keys2" branch. Leo's key-handling code is complex and difficult. Some of this
difficulty is inherent. Some is likely self-inflicted, so simplifications may be
possible.
StatusI have installed Greek, German and Chinese language packs on Windows, so I can easily type σ, or ß or 都.
At present (at least in the "keys2" branch), Leo does not handle most (all?) Greek characters. Traces show that proper key events are generated, but somehow these characters are "disqualified" later.
Similarly, Leo handles regular German characters properly, but not ß.
Otoh, Leo
does handle 都 properly. And I see why. When I type a key, a popup appears with a menu of items. When I select the item Qt (or Windows?) inserts the Chinese character directly in the widget
without generating a key event!
Initial thoughtsLast night I realized (Doh!) that I hadn't read the
QKeyEvent docs in a long time. This produced some surprises.
Even before reading the docs, I realized that modifier keys other than "Shift" are problematic. It's not entirely clear what they mean.
Let's define a
bare shift as a Shift modifier no accompanied by any other modifier.
Let's define a
bare key as a key that has (at most) the Shift modifier.
The separation between event.binding and event.char causes a great deal of confusion. I would like to clear that up.
At present, k.masterKeyHandler uses event.char and event.stroke, but this is strange. One would think that the stroke would contain a char ivar.
Reading the docs
Here are what appear to be the essential excerpts:
QQQ
event.text(): Returns the Unicode text that this key generated.
Return
values when modifier keys such as Shift, Control, Alt, and Meta are
pressed differ among platforms and could return an empty string.
event.key():
See Qt::Key for the list of keyboard codes. These codes are independent of the underlying window system.
This function does not distinguish between capital and non-capital letters, use the text() function.
From
Qt::Key: On Windows, when the KeyDown event for [Key_AltGr] is sent, the Ctrl+Alt modifiers are also set.
QQQ
There is a lot to chew on here.
Generating charactersfilter.toBinding returns (binding, char). At present, the distinction between binding and char is muddled.
It's
imperative that this confusion be cleaned up. Thinking out loud, let's see if the following plan might work:
At present: filter.qtKey
does use event.key() to generate the keynum field, but does not test for Key_AltGr. This likely causes a lot problems/bug later.
1. For bare keys, char should just be event.text(). Indeed, this contains the desired (single) unicode character,
with the proper case.
At present, various parts of code contain
case-adjusting code. Wherever they appear, these lines are extremely difficult to get right. But not all of the case-adjusting code can disappear. In particular, there must be case-adjusting code to handle user settings.
2. For keys containing modifiers other than Shift, char should probably be empty.
But there is a crucial complication. For keys such as Home, Right, etc. event.text() generates multi-character (unicode) text.
3. In any case, the eventFilter logic should "adjust" event modifiers as follows:
A: Remove the Shift modifier from bare events. This should eliminate the case-adjusting logic that presently exists in filter.tweak.
B. Remove the Alt and Ctrl modifiers if keynum == Key_AltGr.
Ensuring that user settings match bindingsAt present, this kinda happens automatically, because both key binding and user create g.KeyStroke events. In both cases, the ctor, via ks.finalize_binding, converts the incoming binding to stroke.s.
In essence, this is the canonical form of either the key binding or the user setting. And in particular, ks.finalize_char (a helper of ks.finalize_binding) has the dreaded case-adjustment code.
What modifiers are allowed?But now we come to a new question in Leo's history! What are we to do about Alt, Ctrl, Meta and Command modifiers? That is, to which characters are these modifiers
allowed to be applied?
This question has tricky ramifications. It seems to me, that
regardless of what modifiers the QKeyEvent
might deliver, it makes no sense to allow user settings like Alt-σ or Ctrl-ß or Meta+都.
Otoh, we definitely
do want to allow user settings like Shift-Tab, Shift-Home, Shift-Ctrl-End, etc. And the present code does allow such things. There are already lists of such characters in g.KeyStroke. Perhaps more should be added.
SummaryLeo's key handling must be perfect. Nothing else is acceptable.
Surprisingly, Greek and German letters appear to be a better test of the code than Chinese.
Modifiers probably should be adjusted more than they are now.
Case-adjusting code can never be completely eliminated, but is should be possible to eliminate it from the eventFilter code.
At present, the exact meaning of the (binding, char) tuple returned from filter.toBinding is unclear. This has ripple effects in the code.
When this confusion is gone, it should be possible clean the code further. This would be a separate project, but now is the time to clean the code as much as possible, when all the details are fresh in my mind.
More details may come to mind, but I have been working on this post most of the night. Time for a break.
Edward