Update Input and Events wiki with current API: Key/InputState/MouseButton enums, scene.on_key, Vector-based callbacks
parent
ae0993ef59
commit
222ee83009
1 changed files with 264 additions and 162 deletions
|
|
@ -9,83 +9,123 @@ McRogueFace provides keyboard, mouse, and window event handling through Python c
|
|||
|
||||
**Key Files:**
|
||||
- `src/GameEngine.cpp::processEvent()` - Event dispatch
|
||||
- `src/Scene.cpp::sUserInput()` - Scene input handling
|
||||
- `src/McRFPy_API.cpp` - Python callback registration
|
||||
- `src/PyScene.cpp` - Scene input handling
|
||||
- `src/PyCallable.cpp` - Callback wrappers (PyClickCallable, PyHoverCallable, PyCellCallable)
|
||||
|
||||
---
|
||||
|
||||
## Keyboard Input
|
||||
|
||||
Register a scene-level callback that fires on every key press/release:
|
||||
Register a keyboard handler on a Scene object. The callback receives enum values, not strings:
|
||||
|
||||
```python
|
||||
import mcrfpy
|
||||
|
||||
def handle_key(key: str, pressed: bool):
|
||||
"""
|
||||
key: Key name (e.g., "W", "Space", "Escape", "Up")
|
||||
pressed: True on press, False on release
|
||||
"""
|
||||
if key == "Escape" and pressed:
|
||||
mcrfpy.setScene("menu")
|
||||
scene = mcrfpy.Scene("game")
|
||||
|
||||
mcrfpy.keypressScene(handle_key)
|
||||
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 Names
|
||||
### Key Enum (`mcrfpy.Key`)
|
||||
|
||||
Common key names from SFML:
|
||||
- Letters: `"A"` through `"Z"`
|
||||
- Numbers: `"Num0"` through `"Num9"`
|
||||
- Arrows: `"Up"`, `"Down"`, `"Left"`, `"Right"`
|
||||
- Special: `"Space"`, `"Enter"`, `"Escape"`, `"Tab"`, `"LShift"`, `"RShift"`, `"LControl"`, `"RControl"`
|
||||
- Function: `"F1"` through `"F12"`
|
||||
All keyboard keys are available as attributes of the `mcrfpy.Key` enum:
|
||||
|
||||
- **Letters:** `Key.A` through `Key.Z`
|
||||
- **Numbers:** `Key.Num0` through `Key.Num9`
|
||||
- **Arrows:** `Key.UP`, `Key.DOWN`, `Key.LEFT`, `Key.RIGHT`
|
||||
- **Special:** `Key.SPACE`, `Key.ENTER`, `Key.ESCAPE`, `Key.TAB`, `Key.BACKSPACE`, `Key.DELETE`
|
||||
- **Modifiers:** `Key.LSHIFT`, `Key.RSHIFT`, `Key.LCTRL`, `Key.RCTRL`, `Key.LALT`, `Key.RALT`
|
||||
- **Function:** `Key.F1` through `Key.F15`
|
||||
|
||||
### 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 Event Handlers
|
||||
### Element Click Handlers
|
||||
|
||||
All UIDrawables support mouse event callbacks:
|
||||
All UIDrawable elements (Frame, Caption, Sprite, Grid, Line, Circle, Arc) support mouse event callbacks:
|
||||
|
||||
| Property | Signature | When Called |
|
||||
|----------|-----------|-------------|
|
||||
| `on_click` | `(x, y, button) -> None` | Mouse button pressed on element |
|
||||
| `on_enter` | `() -> None` | Mouse enters element bounds |
|
||||
| `on_exit` | `() -> None` | Mouse leaves element bounds |
|
||||
| `on_move` | `(x, y) -> None` | Mouse moves within element |
|
||||
| `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 |
|
||||
|
||||
```python
|
||||
frame = mcrfpy.Frame(pos=(100, 100), size=(200, 50))
|
||||
|
||||
frame.on_click = lambda x, y, btn: print(f"Clicked at ({x}, {y}) with button {btn}")
|
||||
frame.on_enter = lambda: print("Mouse entered")
|
||||
frame.on_exit = lambda: print("Mouse exited")
|
||||
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 in addition to element events:
|
||||
Grids provide cell-level mouse events with grid coordinates:
|
||||
|
||||
| Property | Signature | Description |
|
||||
|----------|-----------|-------------|
|
||||
| `on_cell_click` | `(grid_x, grid_y, button) -> None` | Cell clicked |
|
||||
| `on_cell_enter` | `(grid_x, grid_y) -> None` | Mouse enters cell |
|
||||
| `on_cell_exit` | `(grid_x, grid_y) -> None` | Mouse leaves cell |
|
||||
| `hovered_cell` | `(x, y)` or `None` | Currently hovered cell (read-only) |
|
||||
|
||||
See [[Grid-Interaction-Patterns]] for usage examples.
|
||||
|
||||
### Mouse Position
|
||||
| `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) |
|
||||
|
||||
```python
|
||||
# Window coordinates (pixels)
|
||||
x, y = mcrfpy.getMousePos()
|
||||
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
|
||||
|
|
@ -95,33 +135,74 @@ x, y = mcrfpy.getMousePos()
|
|||
Clicks are dispatched in reverse render order (front to back):
|
||||
|
||||
1. **UI elements with highest z_index** receive clicks first
|
||||
2. If handled (callback returns truthy), propagation stops
|
||||
2. If handled (callback exists), propagation stops
|
||||
3. **Entities on grids** receive clicks next
|
||||
4. **Grid cells** receive clicks last
|
||||
|
||||
### Keyboard Priority
|
||||
|
||||
Keyboard events go only to the current scene's registered callback. There is no concept of "focused" UI elements for keyboard input.
|
||||
Keyboard events go only to the current scene's `on_key` handler. There is no concept of "focused" UI elements for keyboard input.
|
||||
|
||||
---
|
||||
|
||||
## Window Events
|
||||
## Common Patterns
|
||||
|
||||
### Resize Events
|
||||
|
||||
Window resize events are not currently exposed directly. Workaround using timer polling:
|
||||
### WASD Movement
|
||||
|
||||
```python
|
||||
last_size = mcrfpy.getWindowSize()
|
||||
scene = mcrfpy.Scene("game")
|
||||
|
||||
def check_resize(dt):
|
||||
global last_size
|
||||
current = mcrfpy.getWindowSize()
|
||||
if current != last_size:
|
||||
on_resize(current)
|
||||
last_size = current
|
||||
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)
|
||||
|
||||
mcrfpy.setTimer("resize_check", check_resize, 100)
|
||||
scene.on_key = handle_key
|
||||
```
|
||||
|
||||
### Button Widget
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
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
|
||||
```
|
||||
|
||||
---
|
||||
|
|
@ -133,30 +214,51 @@ Use the automation API to simulate input in tests:
|
|||
```python
|
||||
from mcrfpy import automation
|
||||
|
||||
automation.keypress("W", True) # Press W
|
||||
automation.keypress("W", False) # Release W
|
||||
automation.click(100, 200, button=0) # Left-click at position
|
||||
# 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 Reference
|
||||
## API Quick Reference
|
||||
|
||||
**Module Functions:**
|
||||
- `mcrfpy.keypressScene(callback)` - Register keyboard handler
|
||||
- `mcrfpy.getMousePos() -> (int, int)` - Get mouse position
|
||||
- `mcrfpy.getWindowSize() -> (int, int)` - Get window dimensions
|
||||
**Scene:**
|
||||
- `scene.on_key = handler` - Register keyboard handler `(key: Key, action: InputState)`
|
||||
|
||||
**UIDrawable Properties:**
|
||||
- `on_click`, `on_enter`, `on_exit`, `on_move` - Event handlers
|
||||
- `hovered` - Mouse hover state (read-only)
|
||||
**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 Properties:**
|
||||
- `on_cell_click`, `on_cell_enter`, `on_cell_exit` - Cell event handlers
|
||||
- `hovered_cell` - Currently hovered cell (read-only)
|
||||
**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 or `None`
|
||||
|
||||
**Enums:**
|
||||
- `mcrfpy.Key` - Keyboard key codes (A-Z, Num0-9, F1-F15, arrows, modifiers)
|
||||
- `mcrfpy.MouseButton` - LEFT, RIGHT, MIDDLE, SCROLL_UP, SCROLL_DOWN
|
||||
- `mcrfpy.InputState` - PRESSED, RELEASED
|
||||
|
||||
**Deprecated (still functional but not recommended):**
|
||||
- `mcrfpy.keypressScene(callback)` - Use `scene.on_key` instead
|
||||
- Old callback signature `(key: str, pressed: bool)` - Use enum-based signature instead
|
||||
|
||||
---
|
||||
|
||||
*Last updated: 2025-11-29*
|
||||
*Last updated: 2026-02-07*
|
||||
Loading…
Add table
Add a link
Reference in a new issue