Skip to content

Commit

Permalink
Always pass refresh context to type inbox
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasmerlin committed May 4, 2024
1 parent 46403f1 commit 887ea03
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 54 deletions.
34 changes: 20 additions & 14 deletions crates/egui_inbox/examples/router_login.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ mod auth {

impl AuthDialog {
pub fn dialog_ui(&mut self, ctx: &Context) {
self.app_state.inbox.read(ctx).for_each(|msg| match msg {
self.app_state.inbox.read().for_each(|msg| match msg {
AuthMessage::ShowLoginDialog {
message,
navigate_to_when_finished,
Expand Down Expand Up @@ -219,7 +219,7 @@ impl Router {
// Read the router's inbox to see if we should open any new pages
self.app_state
.inbox
.read(ui)
.read()
.for_each(|msg: RouterMessage| self.page = msg);

// If we read a component's inbox only when it's rendered, it could cause a memory leak
Expand All @@ -245,23 +245,29 @@ impl Router {
}

fn main() -> eframe::Result<()> {
let state = AppState {
inbox: TypeInbox::new(),
auth: Arc::new(Mutex::new(None)),
broadcast: TypeBroadcast::new(),
};
let mut auth = auth::AuthDialog::new(state.clone());
let home = HomeUi::new(state.clone());
let dashboard = DashboardUi::new(state.clone());

let mut router = Router::new(state.clone(), home, dashboard);

let auth_rx = state.broadcast.subscribe::<AuthEvent>();
let mut state = None;

eframe::run_simple_native(
"DnD Simple Example",
Default::default(),
move |ctx, _frame| {
let (state, auth, router, auth_rx) = state.get_or_insert_with(|| {
let state = AppState {
inbox: TypeInbox::new(ctx.clone()),
auth: Arc::new(Mutex::new(None)),
broadcast: TypeBroadcast::new(),
};
let auth = auth::AuthDialog::new(state.clone());
let home = HomeUi::new(state.clone());
let dashboard = DashboardUi::new(state.clone());

let router = Router::new(state.clone(), home, dashboard);

let auth_rx = state.broadcast.subscribe::<AuthEvent>();

(state, auth, router, auth_rx)
});

// Update our global auth state, based on the auth events
auth_rx.read(ctx).for_each(|event| match event {
AuthEvent::LoggedIn { user } => {
Expand Down
50 changes: 10 additions & 40 deletions crates/egui_inbox/src/type_inbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,72 +18,42 @@ impl<T> TypeInboxEntry<T> {
}
}

#[derive(Debug, Default)]
#[derive(Debug)]
struct TypeInboxInner {
map: TypeMap,
ctx: Option<RequestRepaintContext>,
ctx: RequestRepaintContext,
}

/// A type-map based version of [UiInbox] which can be used to send messages
/// to a component from different parts of the application.
///
/// Use [crate::TypeBroadcast] instead, if you want to send messages to multiple components (mpmc like channel).
#[derive(Clone, Debug, Default)]
#[derive(Clone, Debug)]
pub struct TypeInbox(Arc<Mutex<TypeInboxInner>>);

impl TypeInbox {
/// Create a new [TypeInbox] with the given [RequestRepaintContext].
/// Usually, this would be a [egui::Context].
pub fn new_with_ctx(ctx: impl AsRequestRepaint + 'static) -> Self {
pub fn new(ctx: impl AsRequestRepaint + 'static) -> Self {
Self(Arc::new(Mutex::new(TypeInboxInner {
map: TypeMap::new(),
ctx: Some(ctx.as_request_repaint()),
ctx: ctx.as_request_repaint(),
})))
}

/// Create a new [TypeInbox].
pub fn new() -> Self {
Self(Arc::new(Mutex::new(TypeInboxInner {
map: TypeMap::new(),
ctx: None,
})))
}

/// Send a message of type [T] to the component.
/// If the component has a [RequestRepaintContext] attached, a repaint will be requested.
pub fn send<T: Send + 'static>(&mut self, message: T) {
/// Send a message of type [T].
/// A repaint will be requested.
pub fn send<T: Send + 'static>(&self, message: T) {
let mut guard = self.0.lock();
let entry = guard.map.entry().or_insert_with(TypeInboxEntry::<T>::new);
entry.sender.send(message).ok();
if let Some(ctx) = &guard.ctx {
ctx.request_repaint();
}
guard.ctx.request_repaint();
}

/// Read the inbox, returning an iterator over all pending messages.
pub fn read<T: Send + 'static>(
&mut self,
ui: &impl AsRequestRepaint,
) -> impl Iterator<Item = T> + '_ {
pub fn read<T: Send + 'static>(&self) -> impl Iterator<Item = T> {
let mut guard = self.0.lock();

if guard.ctx.is_none() {
guard.ctx = Some(ui.as_request_repaint());
}

let iter = guard
.map
.entry()
.or_insert_with(TypeInboxEntry::<T>::new)
.inbox
.read_without_ctx();
iter
}

/// Read the inbox without setting the repaint context, returning an iterator over all pending messages.
/// This can be used with [TypeInbox::new_with_ctx].
pub fn read_without_ctx<T: Send + 'static>(&mut self) -> impl Iterator<Item = T> + '_ {
let mut guard = self.0.lock();
let iter = guard
.map
.entry()
Expand Down

0 comments on commit 887ea03

Please sign in to comment.