LCL Key Handling/es

From Lazarus wiki
Jump to: navigation, search

English (en) español (es) français (fr) 日本語 (ja) português (pt)

Presionando una tecla (KeyDown)

Cuando el widgetset recibe la pulsación de una tecla, debe actuar como sigue. Antes de permitir al widget "nativo" manejar la tecla, envía CN_KEYDOWN / CN_SYSKEYDOWN al LCL (CN_SYSKEYDOWN cuando se pulsa la tecla alt), el cual llama a DoKeyDownBeforeInterface consistente en:


  1. call 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 (retornando 1 ó 0 en Message.Result) matters sometimes: windows por ejemplo solamente enviará un mensaje WM_CHAR por la pulsación de una tecla cuando el mensaje WM_KEYDOWN devuelve el valor 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

Notas acerca del keypad numérico

Habitualmente, solamente se detectan los números y el separador decimal en el teclado numérico si Num Lock está encendido. Cuando no lo está se envían códigos de tecla del estilo flecha arriba, flecha abajo (o teclas como Ins, Del, Home, en definitiva los otros símbolos que suelen aparecer junto a sus alternativos en el tecládo numérico).

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.