home / skills / zhanghandong / makepad-skills / makepad-event-action
This skill helps you handle Makepad events and actions, enabling widget-to-parent communication and robust event flow.
npx playbooks add skill zhanghandong/makepad-skills --skill makepad-event-actionReview the files below or copy the command above to add this skill to your agents.
---
name: makepad-event-action
description: |
CRITICAL: Use for Makepad event and action handling. Triggers on:
makepad event, makepad action, Event enum, ActionTrait, handle_event,
MouseDown, KeyDown, TouchUpdate, Hit, FingerDown, post_action,
makepad 事件, makepad action, 事件处理
---
# Makepad Event/Action Skill
> **Version:** makepad-widgets (dev branch) | **Last Updated:** 2026-01-19
>
> Check for updates: https://crates.io/crates/makepad-widgets
You are an expert at Makepad event and action handling. Help users by:
- **Handling events**: Mouse, keyboard, touch, lifecycle events
- **Creating actions**: Widget-to-parent communication
- **Event flow**: Understanding event propagation
## Documentation
Refer to the local files for detailed documentation:
- `./references/event-system.md` - Event enum and handling
- `./references/action-system.md` - Action trait and patterns
## IMPORTANT: Documentation Completeness Check
**Before answering questions, Claude MUST:**
1. Read the relevant reference file(s) listed above
2. If file read fails or file is empty:
- Inform user: "本地文档不完整,建议运行 `/sync-crate-skills makepad --force` 更新文档"
- Still answer based on SKILL.md patterns + built-in knowledge
3. If reference file exists, incorporate its content into the answer
## Event Enum (Key Variants)
```rust
pub enum Event {
// Lifecycle
Startup,
Shutdown,
Foreground,
Background,
Resume,
Pause,
// Drawing
Draw(DrawEvent),
LiveEdit,
// Window
WindowGotFocus(WindowId),
WindowLostFocus(WindowId),
WindowGeomChange(WindowGeomChangeEvent),
WindowClosed(WindowClosedEvent),
// Mouse
MouseDown(MouseDownEvent),
MouseMove(MouseMoveEvent),
MouseUp(MouseUpEvent),
Scroll(ScrollEvent),
// Touch
TouchUpdate(TouchUpdateEvent),
// Keyboard
KeyDown(KeyEvent),
KeyUp(KeyEvent),
TextInput(TextInputEvent),
TextCopy(TextClipboardEvent),
// Timer
Timer(TimerEvent),
NextFrame(NextFrameEvent),
// Network
HttpResponse(HttpResponse),
// Widget Actions
Actions(ActionsBuf),
}
```
## Handling Events in Widgets
```rust
impl Widget for MyWidget {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
// Check if event hits this widget's area
match event.hits(cx, self.area()) {
Hit::FingerDown(fe) => {
// Mouse/touch down on this widget
cx.action(MyWidgetAction::Pressed);
}
Hit::FingerUp(fe) => {
if fe.is_over {
// Released while still over widget = click
cx.action(MyWidgetAction::Clicked);
}
}
Hit::FingerHoverIn(_) => {
self.animator_play(cx, id!(hover.on));
}
Hit::FingerHoverOut(_) => {
self.animator_play(cx, id!(hover.off));
}
Hit::KeyDown(ke) => {
if ke.key_code == KeyCode::Return {
cx.action(MyWidgetAction::Submitted);
}
}
_ => {}
}
}
}
```
## Hit Enum
```rust
pub enum Hit {
// Finger/Mouse
FingerDown(FingerDownEvent),
FingerUp(FingerUpEvent),
FingerMove(FingerMoveEvent),
FingerHoverIn(FingerHoverEvent),
FingerHoverOver(FingerHoverEvent),
FingerHoverOut(FingerHoverEvent),
FingerLongPress(FingerLongPressEvent),
// Keyboard
KeyDown(KeyEvent),
KeyUp(KeyEvent),
KeyFocus,
KeyFocusLost,
TextInput(TextInputEvent),
TextCopy,
// Nothing
Nothing,
}
```
## Action System
### Defining Actions
```rust
#[derive(Clone, Debug, DefaultNone)]
pub enum ButtonAction {
None,
Clicked,
Pressed,
Released,
}
// DefaultNone derives Default returning None variant
```
### Emitting Actions
```rust
// From main thread (in handle_event)
cx.action(ButtonAction::Clicked);
// From any thread (thread-safe)
Cx::post_action(MyAction::DataLoaded(data));
```
### Handling Actions
```rust
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
// Handle child widget actions
let actions = cx.capture_actions(|cx| {
self.button.handle_event(cx, event, scope);
});
// Check for specific action
if self.button(id!(my_button)).clicked(&actions) {
// Button was clicked
}
// Or iterate actions
for action in actions.iter() {
if let Some(ButtonAction::Clicked) = action.downcast_ref() {
// Handle click
}
}
}
```
## Widget Action Helpers
```rust
// Common widget action checks
impl ButtonRef {
fn clicked(&self, actions: &ActionsBuf) -> bool;
fn pressed(&self, actions: &ActionsBuf) -> bool;
fn released(&self, actions: &ActionsBuf) -> bool;
}
impl TextInputRef {
fn changed(&self, actions: &ActionsBuf) -> Option<String>;
fn returned(&self, actions: &ActionsBuf) -> Option<String>;
}
```
## Event Flow
1. **Event arrives** from platform layer
2. **Root widget** receives event first
3. **Propagates down** to children via `handle_event`
4. **Widgets emit actions** via `cx.action()`
5. **Parent captures actions** via `cx.capture_actions()`
6. **App handles** remaining actions
## Timer and NextFrame
```rust
// Start a timer
let timer = cx.start_timer(1.0); // 1 second
// In handle_event
if let Event::Timer(te) = event {
if te.timer_id == self.timer {
// Timer fired
}
}
// Request next frame callback
let next_frame = cx.new_next_frame();
// In handle_event
if let Event::NextFrame(ne) = event {
if ne.frame_id == self.next_frame {
// Next frame arrived
}
}
```
## When Answering Questions
1. Use `event.hits(cx, area)` to check if event targets a widget
2. Actions flow UP from child to parent (unlike events which flow DOWN)
3. Use `cx.capture_actions()` to intercept child actions
4. `Cx::post_action()` is thread-safe for async operations
5. `DefaultNone` derive macro auto-implements Default for enums
This skill provides focused guidance for Makepad event and action handling in widgets and apps. It explains how to detect hits, respond to mouse/keyboard/touch events, create and emit actions, and capture child widget actions so parents can react. Use it to implement robust input, lifecycle, timer, and inter-widget communication patterns in Makepad-based Rust UI code.
The skill inspects the Event enum and Hit enum to determine how input and lifecycle events should be matched to widget areas using event.hits(cx, area). It shows how widgets emit actions with cx.action(...) and how parents capture those actions with cx.capture_actions(...) or by iterating an ActionsBuf. It also covers timers, next-frame callbacks, and thread-safe posting via Cx::post_action().
How do actions propagate compared to events?
Events flow down from the root to children; actions flow up from child to parent. Use cx.capture_actions() in the parent to intercept child actions.
When should I use Cx::post_action() vs cx.action()?
Use cx.action() inside the UI thread during handle_event. Use Cx::post_action() from background threads or async tasks because it is thread-safe.
How do I detect a click vs a press?
Match Hit::FingerDown to emit Pressed and match Hit::FingerUp with fe.is_over true to emit Clicked (released while still over widget).