From 3fbd4639a2812a3522bb4b0dbbd902f8f5db262d Mon Sep 17 00:00:00 2001 From: Andrii Zymohliad Date: Sun, 28 Jul 2024 20:45:19 +0300 Subject: [PATCH] Add message_handler view --- xilem/examples/data_thread.rs | 51 +++++++++++++++ xilem/src/view/message_handler.rs | 105 ++++++++++++++++++++++++++++++ xilem/src/view/mod.rs | 3 + 3 files changed, 159 insertions(+) create mode 100644 xilem/examples/data_thread.rs create mode 100644 xilem/src/view/message_handler.rs diff --git a/xilem/examples/data_thread.rs b/xilem/examples/data_thread.rs new file mode 100644 index 000000000..1089bf068 --- /dev/null +++ b/xilem/examples/data_thread.rs @@ -0,0 +1,51 @@ +// 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 { + proxy_sender: mpsc::SyncSender>, + number: i32, +} + +fn app_logic(data: &mut AppData) -> impl WidgetView { + fork( + label(format!("Number: {}", &data.number)), + message_handler( + |data: &mut AppData, proxy: MessageProxy| { + data.proxy_sender.send(proxy).unwrap(); + }, + |data: &mut AppData, msg: i32| { + data.number = msg; + }, + ), + ) +} + +fn data_thread(proxy_receiver: mpsc::Receiver>) { + if let Ok(proxy) = proxy_receiver.recv() { + 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() +} diff --git a/xilem/src/view/message_handler.rs b/xilem/src/view/message_handler.rs new file mode 100644 index 000000000..64f30ed23 --- /dev/null +++ b/xilem/src/view/message_handler.rs @@ -0,0 +1,105 @@ +// 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-data in response to +/// asynchronous user messages. +/// +/// `store_proxy` serves as a way to obtain [`MessageProxy`], which can then +/// be used to send messages to self. +/// It is given a mutable reference to the app data and a proxy, so the proxy +/// can be saved to the app data here, or sent to another thread for example. +/// Note, it is always called only once, changes to the app data won't trigger +/// `store_proxy` to rerun. +/// +/// `handle_event` receives messages from the aforementioned `MessageProxy`, +/// along with a mutable reference to the app data. +pub fn message_handler( + store_proxy: F, + handle_event: H, +) -> MessageHandler +where + F: Fn(&mut State, MessageProxy) + '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 { + store_proxy: F, + handle_event: H, + message: PhantomData M>, +} + +impl View for MessageHandler +where + F: Fn(&mut State, MessageProxy) + 'static, + H: Fn(&mut State, M) -> Action + 'static, + M: Message + 'static, +{ + type Element = NoElement; + type ViewState = (Arc>, 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 { + debug_assert!( + id_path.is_empty(), + "id path should be empty in MessageHandler::message" + ); + if message.deref().as_any().is::() { + let proxy = MessageProxy::new(raw_proxy.clone(), path.clone()); + (self.store_proxy)(app_state, proxy); + xilem_core::MessageResult::Nop + } else { + let message = message.downcast::().unwrap(); + xilem_core::MessageResult::Action((self.handle_event)(app_state, *message)) + } + } +} diff --git a/xilem/src/view/mod.rs b/xilem/src/view/mod.rs index b9e8170d7..bdb27d301 100644 --- a/xilem/src/view/mod.rs +++ b/xilem/src/view/mod.rs @@ -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::*;