Install any skill in seconds. Free to start, no credit card required.
Get Started Free →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, 事件处理
.claude/skills/sickn33-makepad-event-action/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-08 | ✗→✓ | ▲ Improved | 67% | 0% |
| case-15 | ✗→✓ | ▲ Improved | 20% | 0% |
| case-17 | ✗→✓ | ▲ Improved | 45% | 0% |
| case-19 | ✗→✓ | ▲ Improved | 242% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 122% | 0% |
> 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:
handle_event, Event variants, Hit processing, or widget action propagation.Refer to the local files for detailed documentation:
./references/event-system.md - Event enum and handling./references/action-system.md - Action trait and patternsBefore answering questions, Claude MUST:
/sync-crate-skills makepad --force 更新文档"rustpub 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), }
rustimpl 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); } } _ => {} } } }
rustpub 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, }
rust#[derive(Clone, Debug, DefaultNone)] pub enum ButtonAction { None, Clicked, Pressed, Released, } // DefaultNone derives Default returning None variant
rust// From main thread (in handle_event) cx.action(ButtonAction::Clicked); // From any thread (thread-safe) Cx::post_action(MyAction::DataLoaded(data));
rustfn 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 } } }
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>; }
handle_eventcx.action()cx.capture_actions()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 } }
event.hits(cx, area) to check if event targets a widgetcx.capture_actions() to intercept child actionsCx::post_action() is thread-safe for async operationsDefaultNone derive macro auto-implements Default for enums| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 17,861 | 11,051 | -38% | 1 | 1 | 0% | 3,439 | 4,330 | +26% | 0 | 0 | — |
case-02 | pass→pass | 5,259 | 2,670 | -49% | 1 | 1 | 0% | 1,000 | 2,216 | +122% | 0 | 0 | — |
case-03 | pass→pass | 7,902 | 4,872 | -38% | 1 | 1 | 0% | 1,520 | 2,526 | +66% | 0 | 0 | — |
case-04 | pass→pass | 6,992 | 3,613 | -48% | 1 | 1 | 0% | 1,115 | 2,375 | +113% | 0 | 0 | — |
case-05 | pass→pass | 5,383 | 3,625 | -33% | 1 | 1 | 0% | 953 | 2,492 | +161% | 0 | 0 | — |
case-06 | pass→pass | 13,977 | 10,616 | -24% | 1 | 1 | 0% | 2,368 | 3,656 | +54% | 0 | 0 | — |
case-07 | pass→pass | 8,768 | 7,104 | -19% | 1 | 1 | 0% | 1,599 | 3,072 | +92% | 0 | 0 | — |
case-08 | fail→pass | 9,798 | 6,059 | -38% | 1 | 1 | 0% | 1,797 | 3,005 | +67% | 0 | 0 | — |
case-09 | fail→fail | 11,422 | 6,432 | -44% | 1 | 1 | 0% | 2,115 | 2,746 | +30% | 0 | 0 | — |
case-10 | pass→pass | 5,296 | 3,007 | -43% | 1 | 1 | 0% | 1,001 | 2,280 | +128% | 0 | 0 | — |
case-11 | pass→pass | 11,428 | 2,914 | -75% | 1 | 1 | 0% | 2,017 | 2,254 | +12% | 0 | 0 | — |
case-12 | pass→pass | 4,599 | 3,968 | -14% | 1 | 1 | 0% | 974 | 2,452 | +152% | 0 | 0 | — |
case-13 | pass→pass | 5,647 | 4,594 | -19% | 1 | 1 | 0% | 1,026 | 2,437 | +138% | 0 | 0 | — |
case-14 | pass→pass | 5,128 | 3,970 | -23% | 1 | 1 | 0% | 887 | 2,462 | +178% | 0 | 0 | — |
case-15 | fail→pass | 10,618 | 3,919 | -63% | 1 | 1 | 0% | 1,942 | 2,340 | +20% | 0 | 0 | — |
case-16 | pass→pass | 7,952 | 3,461 | -56% | 1 | 1 | 0% | 1,510 | 2,353 | +56% | 0 | 0 | — |
case-17 | fail→pass | 8,748 | 3,018 | -66% | 1 | 1 | 0% | 1,577 | 2,288 | +45% | 0 | 0 | — |
case-18 | pass→pass | 4,981 | 2,865 | -42% | 1 | 1 | 0% | 831 | 2,137 | +157% | 0 | 0 | — |
case-19 | fail→pass | 4,085 | 4,044 | -1% | 1 | 1 | 0% | 693 | 2,368 | +242% | 0 | 0 | — |
case-20 | pass→pass | 4,807 | 3,293 | -31% | 1 | 1 | 0% | 941 | 2,314 | +146% | 0 | 0 | — |
case-21 | pass→pass | 11,208 | 9,712 | -13% | 1 | 1 | 0% | 2,227 | 3,615 | +62% | 0 | 0 | — |
case-22 | pass→pass | 13,960 | 8,966 | -36% | 1 | 1 | 0% | 2,614 | 3,386 | +30% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +18 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.