Input and Events
McRogueFace provides keyboard, mouse, and window event handling through Python callbacks. Events are dispatched through the scene system, allowing different scenes to have different input handlers.
Related Pages:
- UI-Widget-Patterns - Modal dialogs, buttons, hotbars
- Grid-Interaction-Patterns - Entity selection, movement, context menus
- Writing-Tests - Testing input with automation API
Key Files:
src/GameEngine.cpp::processEvent()- Event dispatchsrc/PyScene.cpp- Scene input handlingsrc/PyCallable.cpp- Callback wrappers (PyClickCallable, PyHoverCallable, PyCellCallable)
Keyboard Input
Register a keyboard handler on a Scene object. The callback receives enum values, not strings:
import mcrfpy
scene = mcrfpy.Scene("game")
def handle_key(key, action):
"""
key: mcrfpy.Key enum (e.g., Key.W, Key.ESCAPE)
action: mcrfpy.InputState enum (PRESSED or RELEASED)
"""
if key == mcrfpy.Key.ESCAPE and action == mcrfpy.InputState.PRESSED:
menu_scene.activate()
elif key == mcrfpy.Key.W and action == mcrfpy.InputState.PRESSED:
player_move(0, -1)
scene.on_key = handle_key
mcrfpy.current_scene = scene
Key Enum (mcrfpy.Key)
All keyboard keys are available as attributes of the mcrfpy.Key enum:
- Letters:
Key.AthroughKey.Z - Numbers:
Key.NUM_0throughKey.NUM_9 - Numpad:
Key.NUMPAD_0throughKey.NUMPAD_9 - Arrows:
Key.UP,Key.DOWN,Key.LEFT,Key.RIGHT - Special:
Key.SPACE,Key.ENTER,Key.ESCAPE,Key.TAB,Key.BACKSPACE,Key.DELETE - Modifiers:
Key.LEFT_SHIFT,Key.RIGHT_SHIFT,Key.LEFT_CONTROL,Key.RIGHT_CONTROL,Key.LEFT_ALT,Key.RIGHT_ALT - Function:
Key.F1throughKey.F15 - Navigation:
Key.HOME,Key.END,Key.PAGE_UP,Key.PAGE_DOWN,Key.INSERT - Punctuation:
Key.COMMA,Key.PERIOD,Key.SEMICOLON,Key.APOSTROPHE,Key.SLASH,Key.BACKSLASH,Key.GRAVE,Key.HYPHEN,Key.EQUAL - Brackets:
Key.LEFT_BRACKET,Key.RIGHT_BRACKET - Numpad ops:
Key.ADD,Key.SUBTRACT,Key.MULTIPLY,Key.DIVIDE
InputState Enum (mcrfpy.InputState)
| Value | Meaning |
|---|---|
InputState.PRESSED |
Key/button was just pressed down |
InputState.RELEASED |
Key/button was just released |
Legacy compatibility: Enum values support string comparison for backwards compatibility: InputState.PRESSED == "start" returns True. However, using the enum directly is preferred.
Mouse Input
Element Click Handlers
All UIDrawable elements (Frame, Caption, Sprite, Grid, Line, Circle, Arc) support mouse event callbacks:
| Property | Signature | When Called |
|---|---|---|
on_click |
(pos: Vector, button: MouseButton, action: InputState) |
Mouse button pressed/released on element |
on_enter |
(pos: Vector) |
Mouse enters element bounds |
on_exit |
(pos: Vector) |
Mouse leaves element bounds |
on_move |
(pos: Vector) |
Mouse moves within element |
frame = mcrfpy.Frame(pos=(100, 100), size=(200, 50))
def on_frame_click(pos, button, action):
if button == mcrfpy.MouseButton.LEFT and action == mcrfpy.InputState.PRESSED:
print(f"Left-clicked at ({pos.x}, {pos.y})")
elif button == mcrfpy.MouseButton.RIGHT and action == mcrfpy.InputState.PRESSED:
print("Right-clicked!")
frame.on_click = on_frame_click
frame.on_enter = lambda pos: print(f"Mouse entered at ({pos.x}, {pos.y})")
frame.on_exit = lambda pos: print("Mouse exited")
frame.on_move = lambda pos: print(f"Mouse at ({pos.x}, {pos.y})")
The hovered property (read-only) indicates whether the mouse is currently over an element.
MouseButton Enum (mcrfpy.MouseButton)
| Value | Meaning |
|---|---|
MouseButton.LEFT |
Left mouse button |
MouseButton.RIGHT |
Right mouse button |
MouseButton.MIDDLE |
Middle mouse button (scroll wheel click) |
MouseButton.SCROLL_UP |
Scroll wheel up |
MouseButton.SCROLL_DOWN |
Scroll wheel down |
Grid Cell Events
Grids provide cell-level mouse events with grid coordinates:
| Property | Signature | Description |
|---|---|---|
on_cell_click |
(cell_pos: Vector, button: MouseButton, action: InputState) |
Cell clicked |
on_cell_enter |
(cell_pos: Vector) |
Mouse enters cell |
on_cell_exit |
(cell_pos: Vector) |
Mouse leaves cell |
hovered_cell |
(x, y) tuple or None |
Currently hovered cell (read-only) |
grid = mcrfpy.Grid(grid_size=(20, 15), pos=(50, 50), size=(400, 300))
def on_cell_click(cell_pos, button, action):
if button == mcrfpy.MouseButton.LEFT and action == mcrfpy.InputState.PRESSED:
x, y = int(cell_pos.x), int(cell_pos.y)
point = grid.at(x, y)
print(f"Cell ({x}, {y}): walkable={point.walkable}")
grid.on_cell_click = on_cell_click
grid.on_cell_enter = lambda cell_pos: print(f"Entered cell ({cell_pos.x}, {cell_pos.y})")
grid.on_cell_exit = lambda cell_pos: print(f"Left cell ({cell_pos.x}, {cell_pos.y})")
See Grid-Interaction-Patterns for usage examples.
Event Priority
Click Dispatch Order
Clicks are dispatched in reverse render order (front to back):
- UI elements with highest z_index receive clicks first
- If handled (callback exists), propagation stops
- Entities on grids receive clicks next
- Grid cells receive clicks last
Keyboard Priority
Keyboard events go only to the current scene's on_key handler. There is no concept of "focused" UI elements for keyboard input.
Common Patterns
WASD Movement
scene = mcrfpy.Scene("game")
def handle_key(key, action):
if action != mcrfpy.InputState.PRESSED:
return
moves = {
mcrfpy.Key.W: (0, -1),
mcrfpy.Key.A: (-1, 0),
mcrfpy.Key.S: (0, 1),
mcrfpy.Key.D: (1, 0),
}
if key in moves:
dx, dy = moves[key]
player_move(dx, dy)
scene.on_key = handle_key
Button Widget
def make_button(text, x, y, callback):
btn = mcrfpy.Frame(pos=(x, y), size=(120, 40),
fill_color=mcrfpy.Color(60, 60, 80))
label = mcrfpy.Caption(text=text, x=10, y=8)
btn.children.append(label)
def on_click(pos, button, action):
if button == mcrfpy.MouseButton.LEFT and action == mcrfpy.InputState.PRESSED:
callback()
btn.on_click = on_click
btn.on_enter = lambda pos: setattr(btn, 'fill_color', mcrfpy.Color(80, 80, 110))
btn.on_exit = lambda pos: setattr(btn, 'fill_color', mcrfpy.Color(60, 60, 80))
return btn
Scene Switching with Keys
game_scene = mcrfpy.Scene("game")
menu_scene = mcrfpy.Scene("menu")
def game_keys(key, action):
if key == mcrfpy.Key.ESCAPE and action == mcrfpy.InputState.PRESSED:
menu_scene.activate()
def menu_keys(key, action):
if key == mcrfpy.Key.ESCAPE and action == mcrfpy.InputState.PRESSED:
game_scene.activate()
game_scene.on_key = game_keys
menu_scene.on_key = menu_keys
Testing Input
Use the automation API to simulate input in tests:
from mcrfpy import automation
# Keyboard
automation.keyDown("w") # Press W
automation.keyUp("w") # Release W
automation.press("space") # Press and release
# Mouse
automation.click(100, 200, button='left')
automation.click(100, 200, button='right')
automation.moveTo(300, 400) # Move mouse
# Screenshots for verification
automation.screenshot("test_result.png")
See Writing-Tests for complete testing patterns.
API Quick Reference
Scene:
scene.on_key = handler- Register keyboard handler(key: Key, action: InputState)
UIDrawable (Frame, Caption, Sprite, Grid, etc.):
on_click- Click handler:(pos: Vector, button: MouseButton, action: InputState)on_enter- Mouse enter:(pos: Vector)on_exit- Mouse leave:(pos: Vector)on_move- Mouse move:(pos: Vector)hovered- Read-only bool
Grid cell events:
on_cell_click- Cell click:(cell_pos: Vector, button: MouseButton, action: InputState)on_cell_enter- Cell enter:(cell_pos: Vector)on_cell_exit- Cell leave:(cell_pos: Vector)hovered_cell- Read-only(x, y)tuple orNone
Enums:
mcrfpy.Key- Keyboard key codes (A-Z, NUM_0-NUM_9, F1-F15, arrows, modifiers)mcrfpy.MouseButton- LEFT, RIGHT, MIDDLE, SCROLL_UP, SCROLL_DOWNmcrfpy.InputState- PRESSED, RELEASED
Deprecated (still functional but not recommended):
mcrfpy.keypressScene(callback)- Usescene.on_keyinstead- Old callback signature
(key: str, pressed: bool)- Use enum-based signature instead
Last updated: 2026-02-07