| name | robius-event-action |
| description | ALWAYS use this when the request matches Robius Event Action: CRITICAL: Use for Robius event and action patterns. |
Robius Event and Action Patterns Skill
Selective Reading Rule
Start with:
references/senior-master-standard.md
references/usage-routing.md
references/quality-checklist.md
Then load only the inherited docs, scripts, assets, or examples that match the user's actual task.
Best practices for event handling and action patterns in Makepad applications based on Robrix and Moly codebases.
Source codebases:
- Robrix: Matrix chat client - MessageAction, RoomsListAction, AppStateAction
- Moly: AI chat application - StoreAction, ChatAction, NavigationAction, Timer patterns
When to Use
Use this skill when:
- Implementing custom actions in Makepad
- Handling events in widgets
- Centralizing action handling in App
- Widget-to-widget communication
- Keywords: makepad action, makepad event, widget action, handle_actions, cx.widget_action
Custom Action Pattern
Defining Domain-Specific Actions
use makepad_widgets::*;
#[derive(Clone, DefaultNone, Debug)]
pub enum MessageAction {
React { details: MessageDetails, reaction: String },
Reply(MessageDetails),
Edit(MessageDetails),
Delete(MessageDetails),
OpenContextMenu { details: MessageDetails, abs_pos: DVec2 },
None,
}
#[derive(Clone, Debug)]
pub struct MessageDetails {
pub room_id: OwnedRoomId,
pub event_id: OwnedEventId,
pub content: String,
pub sender_id: OwnedUserId,
}
Emitting Actions from Widgets
impl Widget for Message {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
self.view.handle_event(cx, event, scope);
let area = self.view.area();
match event.hits(cx, area) {
Hit::FingerDown(_fe) => {
cx.set_key_focus(area);
}
Hit::FingerUp(fe) => {
if fe.is_over && fe.is_primary_hit() && fe.was_tap() {
cx.widget_action(
self.widget_uid(),
&scope.path,
MessageAction::Reply(self.get_details()),
);
}
}
Hit::FingerLongPress(lpe) => {
cx.widget_action(
self.widget_uid(),
&scope.path,
MessageAction::OpenContextMenu {
details: self.get_details(),
abs_pos: lpe.abs,
},
);
}
_ => {}
}
}
}
Centralized Action Handling in App
Using MatchEvent Trait
impl MatchEvent for App {
fn handle_startup(&mut self, cx: &mut Cx) {
self.initialize(cx);
}
fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions) {
for action in actions {
if let Some(action) = action.downcast_ref::<LoginAction>() {
match action {
LoginAction::LoginSuccess => {
self.app_state.logged_in = true;
self.update_ui_visibility(cx);
}
LoginAction::LoginFailure(error) => {
self.show_error(cx, error);
}
}
continue;
}
if let MessageAction::OpenContextMenu { details, abs_pos } =
action.as_widget_action().cast()
{
self.show_context_menu(cx, details, abs_pos);
;
}
action.() {
(AppStateAction::(room)) => {
.app_state.selected_room = (room.());
;
}
(AppStateAction::NavigateToRoom { destination }) => {
.(cx, destination);
;
}
_ => {}
}
action.() {
(ModalAction::Open { kind }) => {
.ui.(ids!(my_modal)).(cx);
;
}
(ModalAction::Close { was_internal }) => {
*was_internal {
.ui.(ids!(my_modal)).(cx);
}
;
}
_ => {}
}
}
}
}
{
(& , cx: & Cx, event: &Event) {
.(cx, event);
= & Scope::(& .app_state);
.ui.(cx, event, scope);
}
}
Action Types
Widget Actions (UI Thread)
Emitted by widgets, handled in the same frame:
cx.widget_action(
self.widget_uid(),
&scope.path,
MyAction::Something,
);
if let MyAction::Something = action.as_widget_action().cast() {
}
if let Some(uid) = action.as_widget_action().widget_uid() {
if uid == my_expected_uid {
if let MyAction::Something = action.as_widget_action().cast() {
}
}
}
Posted Actions (From Async)
Posted from async tasks, received in next event cycle:
Cx::post_action(DataFetchedAction { data });
SignalToUI::set_ui_signal();
if let Some(action) = action.downcast_ref::<DataFetchedAction>() {
self.process_data(&action.data);
}
Global Actions
For app-wide state changes:
cx.action(NavigationAction::GoBack);
if let Some(NavigationAction::GoBack) = action.downcast_ref() {
self.navigate_back(cx);
}
Event Handling Patterns
Hit Testing
impl Widget for MyWidget {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
let area = self.view.area();
match event.hits(cx, area) {
Hit::FingerDown(fe) => {
cx.set_key_focus(area);
}
Hit::FingerUp(fe) => {
if fe.is_over && fe.is_primary_hit() {
if fe.was_tap() {
}
if fe.was_long_press() {
}
}
}
Hit::FingerMove(fe) => {
}
Hit::FingerHoverIn(_) => {
self.animator_play(cx, id!(hover.on));
}
Hit::FingerHoverOut(_) => {
self.animator_play(cx, id!(hover.off));
}
Hit::FingerScroll(se) => {
}
_ => {}
}
}
}
Keyboard Events
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
if let Event::KeyDown(ke) = event {
match ke.key_code {
KeyCode::Return if !ke.modifiers.shift => {
self.submit(cx);
}
KeyCode::Escape => {
self.cancel(cx);
}
KeyCode::KeyC if ke.modifiers.control || ke.modifiers.logo => {
self.copy_to_clipboard(cx);
}
_ => {}
}
}
}
Signal Events
For handling async updates:
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
if let Event::Signal = event {
while let Some(update) = PENDING_UPDATES.pop() {
self.apply_update(cx, update);
}
}
}
Action Chaining Pattern
Widget emits action → Parent catches and re-emits with more context:
cx.widget_action(
self.widget_uid(),
&scope.path,
ItemAction::Selected(item_id),
);
if let ItemAction::Selected(item_id) = action.as_widget_action().cast() {
cx.widget_action(
self.widget_uid(),
&scope.path,
ListAction::ItemSelected {
list_id: self.list_id.clone(),
item_id,
},
);
}
Best Practices
- Use
DefaultNone derive: All action enums must have a None variant
- Use
continue after handling: Prevents unnecessary processing
- Downcast pattern for async actions: Posted actions are not widget actions
- Widget action cast for UI actions: Use
as_widget_action().cast()
- Always call
SignalToUI::set_ui_signal(): After posting actions from async
- Centralize in App::handle_actions: Keep action handling in one place
- Use descriptive action names:
MessageAction::Reply not MessageAction::Action1
Reference Files
references/action-patterns.md - Additional action patterns (Robrix)
references/event-handling.md - Event handling reference (Robrix)
references/moly-action-patterns.md - Moly-specific patterns
- Store-based action forwarding
- Timer-based retry pattern
- Radio button navigation
- External link handling
- Platform-conditional actions (#[cfg])
- UiRunner event handling
Limitations
- Use this skill only when the task clearly matches the scope described above.
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.