Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

xilem: Add message_handler view #456

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions xilem/examples/data_thread.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright 2024 the Xilem Authors
// SPDX-License-Identifier: Apache-2.0

use std::{sync::mpsc, thread, time};

use xilem::{
core::{fork, MessageProxy},
view::{label, message_handler},
EventLoop, WidgetView, Xilem,
};

struct AppData {
number: i32,
// Used to send MessageProxy from message_handler view to data_thread
proxy_sender: mpsc::SyncSender<MessageProxy<i32>>,
}

fn app_logic(data: &mut AppData) -> impl WidgetView<AppData> {
fork(
label(format!("Number: {}", &data.number)),
message_handler(
|data: &mut AppData, proxy: MessageProxy<i32>| {
// Send message proxy to the data_thread
data.proxy_sender.send(proxy).unwrap();
},
|data: &mut AppData, msg: i32| {
// Receive data from the data_thread
data.number = msg;
},
),
)
}

fn data_thread(proxy_receiver: mpsc::Receiver<MessageProxy<i32>>) {
// Wait for the MessageProxy
if let Ok(proxy) = proxy_receiver.recv() {
// Generate data and send it to the message_handler view
let mut number = 0;
while let Ok(()) = proxy.message(number) {
number += 1;
thread::sleep(time::Duration::from_secs(1));
}
}
}

fn main() {
let (proxy_sender, proxy_receiver) = mpsc::sync_channel(1);
let data = AppData {
proxy_sender,
number: 0,
};
thread::spawn(move || data_thread(proxy_receiver));
Xilem::new(data, app_logic)
.run_windowed(EventLoop::with_user_event(), "Centered Flex".into())
.unwrap();
}
106 changes: 106 additions & 0 deletions xilem/src/view/message_handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Copyright 2024 the Xilem Authors
// SPDX-License-Identifier: Apache-2.0

use std::{marker::PhantomData, ops::Deref, sync::Arc};

use xilem_core::{
DynMessage, Message, MessageProxy, NoElement, RawProxy, View, ViewId, ViewPathTracker,
};

use crate::ViewCtx;

/// No-element view which allows to update app state in response to
/// asynchronous user messages, for example from another thread.
///
/// `store_proxy` serves as a way to obtain [`MessageProxy`], which can then
/// be used to send messages to this view.
/// It is given a mutable reference to the app state and a message proxy, so
/// the proxy can be e.g. saved to the app state here, or sent to another thread.
/// Note, `store_proxy` is called once, shortly after the view is built.
/// Changes to the app state won't it to rerun.
///
/// `handle_event` receives messages from the aforementioned `MessageProxy`,
/// along with a mutable reference to the app state.
pub fn message_handler<M, F, H, State, Action>(
store_proxy: F,
handle_event: H,
) -> MessageHandler<F, H, M>
where
F: Fn(&mut State, MessageProxy<M>) -> Action + 'static,
H: Fn(&mut State, M) -> Action + 'static,
M: Message + 'static,
{
MessageHandler {
store_proxy,
handle_event,
message: PhantomData,
}
}

#[derive(Debug)]
struct StoreProxyMessage;

pub struct MessageHandler<F, H, M> {
store_proxy: F,
handle_event: H,
message: PhantomData<fn() -> M>,
}

impl<State, Action, F, H, M> View<State, Action, ViewCtx> for MessageHandler<F, H, M>
where
F: Fn(&mut State, MessageProxy<M>) -> Action + 'static,
H: Fn(&mut State, M) -> Action + 'static,
M: Message + 'static,
{
type Element = NoElement;
type ViewState = (Arc<dyn RawProxy<DynMessage>>, Arc<[ViewId]>);

fn build(&self, ctx: &mut ViewCtx) -> (Self::Element, Self::ViewState) {
let path: Arc<[ViewId]> = ctx.view_path().into();
ctx.proxy
.send_message(path.clone(), Box::new(StoreProxyMessage))
.unwrap();
(NoElement, (ctx.proxy.clone(), path.clone()))
}

fn rebuild<'el>(
&self,
_: &Self,
_: &mut Self::ViewState,
_: &mut ViewCtx,
(): xilem_core::Mut<'el, Self::Element>,
) -> xilem_core::Mut<'el, Self::Element> {
// Nothing to do
}

fn teardown(
&self,
_: &mut Self::ViewState,
_: &mut ViewCtx,
_: xilem_core::Mut<'_, Self::Element>,
) {
// Nothing to do
}

fn message(
&self,
(raw_proxy, path): &mut Self::ViewState,
id_path: &[xilem_core::ViewId],
message: DynMessage,
app_state: &mut State,
) -> xilem_core::MessageResult<Action> {
debug_assert!(
id_path.is_empty(),
"id path should be empty in MessageHandler::message"
);
if message.deref().as_any().is::<StoreProxyMessage>() {
let proxy = MessageProxy::new(raw_proxy.clone(), path.clone());
let action = (self.store_proxy)(app_state, proxy);
xilem_core::MessageResult::Action(action)
} else {
let message = message.downcast::<M>().unwrap();
let action = (self.handle_event)(app_state, *message);
xilem_core::MessageResult::Action(action)
}
}
}
3 changes: 3 additions & 0 deletions xilem/src/view/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ pub use sized_box::*;
mod label;
pub use label::*;

mod message_handler;
pub use message_handler::*;

mod prose;
pub use prose::*;

Expand Down