LCL Key Handling/pt

From Lazarus wiki
Jump to navigationJump to search

English (en) 日本語 (ja) português (pt)

Pressionando uma tecla (KeyDown)

Quando um widgetset recebe um evento Key Press ele precisa fazer o seguinte: antes de deixar o widget "nativo" manipular a tecla, enviar CN_KEYDOWN/CN_SYSKEYDOWN à LCL (CN_SYSKEYDOWN quando a tecla Alt está pressionada), que chama DoKeyDownBeforeInterface consistindo de:

  1. chamar Application.NotifyKeyDownBeforeHandler (invokes before handlers)
  2. get parent form, if it has keypreview, call it's DoKeyDownBeforeInterface
  3. let the associated dragobject handle the key
  4. call KeyDownBeforeInterface (if control does not have csNoStdEvents in ControlStyle):
    1. call KeyDown:
      1. call OnKeyDown handler if any

When unhandled, that is, Message.Result = 0, it lets the widget handle the key, and if the widget has not done anything useful, then send a LM_KEYDOWN/LM_SYSKEYDOWN message to the LCL, which calls DoRemainingKeyDown consisting of:

  1. if a popupmenu is assigned, check it for shortcuts (whether it is popped up or not)
  2. get parent form, let it handle possible shortcut (TCustomForm.IsShortcut):
    1. if form has OnShortcut event assigned, call it
    2. if form has Menu assigned, check it for shortcuts:
      • iterate through all menu items for this menu, check shortcuts, ".Click" the item
      • set the menu's ShortcutHandled property to false in the menu item's OnClick handler, if you did not handle the shortcut and want to let the key processing continue
    3. check the action lists for shortcuts, .Execute matching actions
  3. let the application handle a shortcut (Application.IsShortcut):
    1. if application has OnShortcut assigned, call it
    2. if there is a modal form active, let it handle a shortcut (see above, TCustomForm.IsShortcut)
    3. else if there is a focused form (Screen.ActiveCustomForm), let it handle a shortcut
    4. else if there is a main form (Application.MainForm), let it handle a shortcut
  4. if there is a Parent, iteratively call Parent.ChildKey to handle key message
    • use it to handle key shortcuts in a certain "area", for example a panel
    • TWinControl will call it's Parent's ChildKey; that's the "iteratively"
  5. call ControlKeyDown, which in TWinControl, calls Application.ControlKeyDown:
    1. handle tab navigation for controls
    • if your custom control does something special with tab, override and inhibit, for example TCustomMemo
  6. call KeyDownAfterInterface; TWinControl does nothing here
  7. let Application call KeyDownAfter handlers

Note that result (returning 1 or 0 in Message.Result) matters sometimes: windows for example will only send a WM_CHAR message for a key when a WM_KEYDOWN message that key has returned 0.

Key pressed, characters sent (KeyPress)

The widgetset can either send CN_CHAR message, or call the IntfUtf8KeyPress method. CN_CHAR does not support UTF-8 characters, so that is why a IntfUtf8KeyPress exists. CN_CHAR handling consists of:

  1. if widgetset (interface) does not send utf8 key presses, then call IntfUtf8KeyPress
  2. call DoKeyPress:
    1. get parent form, and if it has KeyPreview call it's DoKeyPress
    2. call KeyPress (if control does not have csNoStdEvents in ControlStyle):
      1. call OnKeyPress handler if any

IntfUtf8KeyPress (DoUtf8KeyPress) handling consists of:

  1. get parent form, and if it has KeyPreview call it's DoUtf8KeyPress
  2. call Utf8KeyPress (if control does not have csNoStdEvents in ControlStyle):
    1. call OnUtf8KeyPress handler if any

When unhandled, that is, Message.Result = 0, it lets the widget handle the key, and if the widget has not done anything useful, then send a LM_CHAR/LM_SYSCHAR message to the LCL, which calls SendDialogChar consisting of:

  1. get parent form and if found call it's .DialogChar:
    1. TWinControl broadcasts DialogChar to all it's children
      • use it to implement accelerator shortcuts for controls "near" (on the same form) as the focused control
      • example: TCustomLabel uses it to Focus the assigned .FocusControl when alt+accelerator is pressed


Releasing the key (KeyUp)

Releasing a key is very much, in the order of events happening, alike pressing a key, but some functions are missing. Again the widgetset will send a CN_KEYUP/CN_SYSKEYUP to the LCL before letting the widget handle the key, which will call DoKeyUpBeforeInterface consisting of:

  1. get parent form, if it has keypreview, call it's DoKeyUpBeforeInterface
  2. let the associated dragobject handle the key
  3. call KeyUpBeforeInterface (if control does not have csNoStdEvents in ControlStyle):
    1. call KeyUp:
      1. call OnKeyUp handler if any

When unhandled, that is, Message.Result = 0, it lets the widget handle the key, and if the widget has not done anything useful, then send a LM_KEYUP/LM_SYSKEYUP message to the LCL, which calls DoRemainingKeyUp consisting of:

  1. call ControlKeyUp, which in TWinControl, calls Application.ControlKeyDown:
    1. handle Enter and Escape keys for forms
    • if your custom control does something special with enter and escape, override and inhibit
  2. call KeyUpAfterInterface; TWinControl does nothing here

What should be sent via KeyPress

While KeyDown and KeyUp messages should always be sent, determining when something should be sent via CN_(SYS)CHAR/LM_(SYS)CHAR and IntfUTF8KeyPress is not so obvious.

The general rule is: if a key represents a character (something visual), that character must be sent.

However, what are the keys that represent a character? Since LCL should be Delphi compatible, and since Delphi is based on Microsoft Windows, KeyDown/Char/KeyUp messages should be sent according to the way that WM_KEYDOWN, WM_CHAR, WM_KEYUP messages are sent by Windows.

This behaviour is sometimes a bit strange (e.g: TAB only generates WM_KEYDOWN/WM_KEYUP, while BACKSPACE generates WM_KEYDOWN/WM_CHAR/WM_KEYUP) but that's it.

Note: In KeyDown/KeyUp messages, the virtual key code must be passed (that is, a VK_ constant), while In xx_(SYS)CHAR messages the ascii character (or the UTF8 character for IntfUTF8KeyPress) must be sent. That is, for ESC key you put VK_ESCAPE key code in KeyDown/KeyUp messages, and $1B in xx_(SYS)CHAR messages and in IntfUTF8KeyPress.

Keys that generate KeyDown/Char/KeyUp

  • 'Simple' letters (a..z)
  • Numbers
  • Esc
  • Backspace
  • Space
  • Return
  • Return on numeric keypad
  • Numeric keypad operators (Add, Subtract, Multiply, Divide)
  • Numbers on numeric keypad
  • Decimal separator on numeric keypad

(See later Notes on numeric keypad)

Keys that generate KeyDown/KeyUp only

  • Function keys (F1-F12)
  • Print Screen
  • Scroll Lock
  • Pause
  • Shift
  • Caps Lock
  • Tab
  • Control
  • Windows Logo Key
  • Alt and AltGr
  • Windows 'Application Key'
  • Insert
  • Delete
  • Home
  • End
  • Page Up
  • Page Down
  • Arrow Keys (Up, Down, Left, Right)
  • Num Lock

Notes on numeric keypad

Usually, numbers and decimal separator on numeric keypad can only be detected when Num Lock is on. Otherwise, they send keycodes as if user pressed arrow keys (or keys like Ins, Del, Home and so on). Return and operators aren't affected by Num Lock.

Usually when Num Lock is off, pressing numbers on numeric keypad it's the same as pressing arrow keys (so 8 generates VK_UP, 7 generates VK_HOME and so on) and you can't determine if the user pressed the "real" arrow key or a key on the keypad. In general, you shouldn't care about it.