From 148ffc6daec462ea507d035109d451c4ab6da00e Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Tue, 25 Jan 2022 20:52:54 +0100 Subject: [PATCH] refactor(ccs): Separate lib.rs into modules --- crates/component-system/src/component.rs | 98 ++++++++++++++ crates/component-system/src/handle.rs | 68 ++++++++++ crates/component-system/src/lib.rs | 162 +---------------------- 3 files changed, 170 insertions(+), 158 deletions(-) create mode 100644 crates/component-system/src/component.rs create mode 100644 crates/component-system/src/handle.rs diff --git a/crates/component-system/src/component.rs b/crates/component-system/src/component.rs new file mode 100644 index 00000000..e6d16660 --- /dev/null +++ b/crates/component-system/src/component.rs @@ -0,0 +1,98 @@ +// Copyright 2022 System76 +// SPDX-License-Identifier: MPL-2.0 + +use crate::*; + +/// The basis of a COSMIC widget. +/// +/// A component takes care of constructing the UI of a widget, managing an event-loop +/// which handles signals from within the widget, and supports forwarding messages to +/// the consumer of the component. +pub trait Component: Sized + 'static { + /// The arguments that are passed to the init_view method. + type InitialArgs; + + /// The message type that the component accepts as inputs. + type Input: 'static; + + /// The message type that the component provides as outputs. + type Output: 'static; + + /// The widget that was constructed by the component. + type RootWidget: Clone + AsRef; + + /// The type that's used for storing widgets created for this component. + type Widgets: 'static; + + /// Initializes the component and attaches it to the default local executor. + /// + /// Spawns an event loop on `glib::MainContext::default()`, which exists + /// for as long as the root widget remains alive. + fn register( + mut self, + args: Self::InitialArgs, + ) -> Registered { + let (mut sender, in_rx) = mpsc::unbounded_channel::(); + let (mut out_tx, output) = mpsc::unbounded_channel::(); + + let (mut widgets, widget) = self.init_view(args, &mut sender, &mut out_tx); + + let handle = Handle { + widget, + sender: sender.clone(), + }; + + let (inner_tx, mut inner_rx) = mpsc::unbounded_channel::>(); + + handle.widget.as_ref().connect_destroy({ + let sender = inner_tx.clone(); + move |_| { + let _ = sender.send(InnerMessage::Drop); + } + }); + + spawn_local(async move { + while let Some(event) = inner_rx.recv().await { + match event { + InnerMessage::Message(event) => { + self.update(&mut widgets, event, &mut sender, &mut out_tx); + } + + InnerMessage::Drop => break, + } + } + }); + + forward(in_rx, inner_tx, InnerMessage::Message); + + Registered { + handle, + receiver: output, + } + } + + /// Creates the initial view and root widget. + fn init_view( + &mut self, + args: Self::InitialArgs, + input: &mut Sender, + output: &mut Sender, + ) -> (Self::Widgets, Self::RootWidget); + + /// Handles input messages and enables the programmer to update the model and view. + #[allow(unused_variables)] + fn update( + &mut self, + widgets: &mut Self::Widgets, + message: Self::Input, + input: &mut Sender, + output: &mut Sender, + ) { + } +} + +/// Used to drop the component's event loop when the managed widget is destroyed. +enum InnerMessage { + Drop, + Message(T), +} diff --git a/crates/component-system/src/handle.rs b/crates/component-system/src/handle.rs new file mode 100644 index 00000000..832313a3 --- /dev/null +++ b/crates/component-system/src/handle.rs @@ -0,0 +1,68 @@ +// Copyright 2022 System76 +// SPDX-License-Identifier: MPL-2.0 + +use crate::*; + +/// Handle to an active widget component in the system. +pub struct Handle { + /// The widget that this component manages. + pub(crate) widget: W, + + /// Used for emitting events to the component. + pub(crate) sender: Sender, +} + +impl Widget for Handle { + fn widget(&self) -> &W { + &self.widget + } +} + +impl Handle { + pub fn emit(&self, event: I) { + let _ = self.sender.send(event); + } +} + +/// A newly-registered component which supports destructuring the handle +/// by forwarding or ignoring outputs from the component. +pub struct Registered, I, O> { + /// Handle to the component that was registered. + pub handle: Handle, + + /// The outputs being received by the component. + pub receiver: Receiver, +} + +impl, I: 'static, O: 'static> Registered { + /// Forwards output events to the designated sender. + pub fn forward X) + 'static>( + self, + sender: Sender, + transform: F, + ) -> Handle { + let Registered { handle, receiver } = self; + forward(receiver, sender, transform); + handle + } + + pub fn handle(self, mut func: F) -> Handle { + let Registered { + handle, + mut receiver, + } = self; + + spawn_local(async move { + while let Some(event) = receiver.recv().await { + func(event); + } + }); + + handle + } + + /// Ignore outputs from the component and take the handle. + pub fn ignore(self) -> Handle { + self.handle + } +} diff --git a/crates/component-system/src/lib.rs b/crates/component-system/src/lib.rs index 3cbc4696..ec46fb76 100644 --- a/crates/component-system/src/lib.rs +++ b/crates/component-system/src/lib.rs @@ -2,181 +2,27 @@ // SPDX-License-Identifier: MPL-2.0 mod app_runner; +mod component; +mod handle; mod macros; use gtk4::prelude::*; use tokio::sync::mpsc; pub use self::app_runner::AppRunner; +pub use self::component::Component; +pub use self::handle::{Handle, Registered}; pub use gtk4 as gtk; pub use relm4_macros::view; pub type Sender = mpsc::UnboundedSender; pub type Receiver = mpsc::UnboundedReceiver; -/// A newly-registered component which supports destructuring the handle -/// by forwarding or ignoring outputs from the component. -pub struct Registered, I, O> { - /// Handle to the component that was registered. - pub handle: Handle, - - /// The outputs being received by the component. - pub receiver: Receiver, -} - -impl, I: 'static, O: 'static> Registered { - /// Forwards output events to the designated sender. - pub fn forward X) + 'static>( - self, - sender: Sender, - transform: F, - ) -> Handle { - let Registered { handle, receiver } = self; - forward(receiver, sender, transform); - handle - } - - pub fn handle(self, mut func: F) -> Handle { - let Registered { - handle, - mut receiver, - } = self; - - spawn_local(async move { - while let Some(event) = receiver.recv().await { - func(event); - } - }); - - handle - } - - /// Ignore outputs from the component and take the handle. - pub fn ignore(self) -> Handle { - self.handle - } -} - -/// Handle to an active widget component in the system. -pub struct Handle { - /// The widget that this component manages. - widget: W, - - /// Used for emitting events to the component. - sender: Sender, -} - -impl Widget for Handle { - fn widget(&self) -> &W { - &self.widget - } -} - -impl Handle { - pub fn emit(&self, event: I) { - let _ = self.sender.send(event); - } -} - -/// Used to drop the component's event loop when the managed widget is destroyed. -enum InnerMessage { - Drop, - Message(T), -} - /// Provides a convenience function for getting a widget out of a type. pub trait Widget { fn widget(&self) -> &W; } -/// The basis of a COSMIC widget. -/// -/// A component takes care of constructing the UI of a widget, managing an event-loop -/// which handles signals from within the widget, and supports forwarding messages to -/// the consumer of the component. -pub trait Component: Sized + 'static { - /// The arguments that are passed to the init_view method. - type InitialArgs; - - /// The message type that the component accepts as inputs. - type Input: 'static; - - /// The message type that the component provides as outputs. - type Output: 'static; - - /// The widget that was constructed by the component. - type RootWidget: Clone + AsRef; - - /// The type that's used for storing widgets created for this component. - type Widgets: 'static; - - /// Initializes the component and attaches it to the default local executor. - /// - /// Spawns an event loop on `glib::MainContext::default()`, which exists - /// for as long as the root widget remains alive. - fn register( - mut self, - args: Self::InitialArgs, - ) -> Registered { - let (mut sender, in_rx) = mpsc::unbounded_channel::(); - let (mut out_tx, output) = mpsc::unbounded_channel::(); - - let (mut widgets, widget) = self.init_view(args, &mut sender, &mut out_tx); - - let handle = Handle { - widget, - sender: sender.clone(), - }; - - let (inner_tx, mut inner_rx) = mpsc::unbounded_channel::>(); - - handle.widget.as_ref().connect_destroy({ - let sender = inner_tx.clone(); - move |_| { - let _ = sender.send(InnerMessage::Drop); - } - }); - - spawn_local(async move { - while let Some(event) = inner_rx.recv().await { - match event { - InnerMessage::Message(event) => { - self.update(&mut widgets, event, &mut sender, &mut out_tx); - } - - InnerMessage::Drop => break, - } - } - }); - - forward(in_rx, inner_tx, |event| InnerMessage::Message(event)); - - Registered { - handle, - receiver: output, - } - } - - /// Creates the initial view and root widget. - fn init_view( - &mut self, - args: Self::InitialArgs, - input: &mut Sender, - output: &mut Sender, - ) -> (Self::Widgets, Self::RootWidget); - - /// Handles input messages and enables the programmer to update the model and view. - #[allow(unused_variables)] - fn update( - &mut self, - widgets: &mut Self::Widgets, - message: Self::Input, - input: &mut Sender, - output: &mut Sender, - ) { - } -} - /// Convenience function for `Component::register()`. pub fn register( model: C,