msgProc: Accelerator Tables

Accelerator tables map keyboard shortcuts to command identifiers using readable strings like "Ctrl+S" and "Ctrl+Shift+Z". Register a table, handle MSG_ACCEL in msgProc, and destroy when done.

explanation

Messages

MSG_ACCEL

MSG_ACCEL fires when a registered key combination is pressed. event.wParam is the command ID assigned to that entry. The matching MSG_KEYDOWN is automatically suppressed — the component never sees it. Tables are checked innermost-first by walking up the container hierarchy, with the global table as final fallback. A child container's table takes priority over its parent's, which takes priority over the global table.

Message Parameters

Parameter Type Description
wParam number The command ID assigned in the table entry
lParam number Reserved (always 0)

Example: Editor Shortcuts

const CMD_SAVE  = 101;
const CMD_UNDO  = 102;
const CMD_REDO  = 103;
const CMD_FIND  = 104;

init() {
    wakaPAC.loadAcceleratorTable(this.pacId, [
        { key: 'Ctrl+S',       cmdId: CMD_SAVE },
        { key: 'Ctrl+Z',       cmdId: CMD_UNDO },
        { key: 'Ctrl+Shift+Z', cmdId: CMD_REDO },
        { key: 'Ctrl+F',       cmdId: CMD_FIND },
    ]);
}

msgProc(event) {
    if (event.message === wakaPAC.MSG_ACCEL) {
        switch (event.wParam) {
            case CMD_SAVE: this.save();           break;
            case CMD_UNDO: this.undo();           break;
            case CMD_REDO: this.redo();           break;
            case CMD_FIND: this.openFindDialog(); break;
        }
    }
}

destroy() {
    wakaPAC.destroyAcceleratorTable(this.pacId);
}

API

loadAcceleratorTable()

wakaPAC.loadAcceleratorTable(this.pacId, [
    { key: 'Ctrl+S', cmdId: 101 },
]);

Registers an accelerator table for a container or globally. Calling this a second time for the same pacId completely replaces the existing table — entries are not merged. Component-scoped tables are automatically removed when the container is destroyed. The global table (null) is not tied to any container lifecycle and must be destroyed explicitly.

Parameter Type Description
pacId string | null Component ID to scope the table to, or null for a global application-wide table
entries Array Array of { key, cmdId } objects. key is a shortcut string; cmdId is a positive integer delivered as event.wParam when the shortcut fires

destroyAcceleratorTable()

wakaPAC.destroyAcceleratorTable(this.pacId);

Removes the accelerator table for a container or the global scope. The shortcut keys stop firing immediately.

Parameter Type Description
pacId string | null Component ID whose table should be removed, or null to remove the global table

getAcceleratorTable()

const entries = wakaPAC.getAcceleratorTable(this.pacId);

Returns a copy of the registered entries for a container or the global scope. Each entry includes the original key string, making this suitable for rendering a keyboard shortcuts help dialog without maintaining a separate data structure. Returns null if no table is registered for the given scope.

Parameter Type Description
pacId string | null Component ID to retrieve, or null for the global table
Returns Type Description
entries Array | null Copy of the registered entries as { key, cmdId } objects, or null if no table exists for this scope

Shortcut String Format

A shortcut string consists of optional modifier tokens followed by a key name, joined by +. Parsing is case-insensitive. The key name is a VK_* constant with the VK_ prefix omitted.

Token Description
Ctrl Ctrl key modifier
Shift Shift key modifier
Alt Alt key modifier
key name Examples: S, F5, Delete, Escape, Return, Space, OEM_PLUS
String Meaning
"Ctrl+S" Ctrl + S
"Ctrl+Shift+Z" Ctrl + Shift + Z
"F5" F5 with no modifiers
"Alt+F4" Alt + F4
"Ctrl+OEM_PLUS" Ctrl + the = / + key
"Delete" Delete with no modifiers

Nested Containers

A parent container's accelerator table automatically covers all its descendants. If focus is inside a child that has no table of its own, the parent's shortcuts still fire. This lets you define application-wide shortcuts at a high level without registering them on every child component.

// Parent registers shortcuts that apply to all children
wakaPAC('#app', {
    init() {
        wakaPAC.loadAcceleratorTable('app', [
            { key: 'Ctrl+S', cmdId: CMD_SAVE },
        ]);
    },
    msgProc(event) {
        if (event.message === wakaPAC.MSG_ACCEL && event.wParam === CMD_SAVE) {
            this.save();
        }
    }
});

// Child has no accelerator table — Ctrl+S still fires in the parent's msgProc
wakaPAC('#editor', { /* ... */ });

Best Practices

  • Define command IDs as named constants: Magic numbers in switch statements become impossible to maintain. Define them at the top of your component
  • Destroy the global table explicitly: The global table (null) is not tied to any container lifecycle — call destroyAcceleratorTable(null) when it is no longer needed
  • Use global tables sparingly: Reserve the global table for true application-level commands. Per-component tables give more control and avoid accidental shadowing
  • Don't shadow browser shortcuts: Combinations like Ctrl+W and Ctrl+T are handled by the browser before wakaPAC sees them