Overview
GPUI provides event system for component coordination:
Event Mechanisms:
-
Custom Events: Define and emit type-safe events
-
Observations: React to entity state changes
-
Subscriptions: Listen to events from other entities
-
Global Events: App-wide event handling
Quick Start
Define and Emit Events
#[derive(Clone)] enum MyEvent { DataUpdated(String), ActionTriggered, }
impl MyComponent { fn update_data(&mut self, data: String, cx: &mut Context<Self>) { self.data = data.clone();
// Emit event
cx.emit(MyEvent::DataUpdated(data));
cx.notify();
}
}
Subscribe to Events
impl Listener { fn new(source: Entity<MyComponent>, cx: &mut App) -> Entity<Self> { cx.new(|cx| { // Subscribe to events cx.subscribe(&source, |this, emitter, event: &MyEvent, cx| { match event { MyEvent::DataUpdated(data) => { this.handle_update(data.clone(), cx); } MyEvent::ActionTriggered => { this.handle_action(cx); } } }).detach();
Self { source }
})
}
}
Observe Entity Changes
impl Observer { fn new(target: Entity<Target>, cx: &mut App) -> Entity<Self> { cx.new(|cx| { // Observe entity for any changes cx.observe(&target, |this, observed, cx| { // Called when observed.update() calls cx.notify() println!("Target changed"); cx.notify(); }).detach();
Self { target }
})
}
}
Common Patterns
- Parent-Child Communication
// Parent emits events impl Parent { fn notify_children(&mut self, cx: &mut Context<Self>) { cx.emit(ParentEvent::Updated); cx.notify(); } }
// Children subscribe impl Child { fn new(parent: Entity<Parent>, cx: &mut App) -> Entity<Self> { cx.new(|cx| { cx.subscribe(&parent, |this, parent, event, cx| { this.handle_parent_event(event, cx); }).detach();
Self { parent }
})
}
}
- Global Event Broadcasting
struct EventBus { listeners: Vec<WeakEntity<dyn Listener>>, }
impl EventBus { fn broadcast(&mut self, event: GlobalEvent, cx: &mut Context<Self>) { self.listeners.retain(|weak| { weak.update(cx, |listener, cx| { listener.on_event(&event, cx); }).is_ok() }); } }
- Observer Pattern
cx.observe(&entity, |this, observed, cx| { // React to any state change let state = observed.read(cx); this.sync_with_state(state, cx); }).detach();
Best Practices
✅ Detach Subscriptions
// ✅ Detach to keep alive cx.subscribe(&entity, |this, source, event, cx| { // Handle event }).detach();
✅ Clean Event Types
#[derive(Clone)] enum AppEvent { DataChanged { id: usize, value: String }, ActionPerformed(ActionType), Error(String), }
❌ Avoid Event Loops
// ❌ Don't create mutual subscriptions entity1.subscribe(entity2) → emits event entity2.subscribe(entity1) → emits event → infinite loop!
Reference Documentation
API Reference: See api-reference.md
-
Event definition, emission, subscriptions
-
Observations, global events
-
Subscription lifecycle
Patterns: See patterns.md
-
Event-driven architectures
-
Communication patterns
-
Best practices and pitfalls