refactor!(ccs): Improvements to design
This commit is contained in:
parent
148ffc6dae
commit
dff9265e35
7 changed files with 157 additions and 128 deletions
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
This library is a GTK4 GUI framework inspired by [Relm](https://github.com/antoyo/relm), which is inspired by [Elm](https://guide.elm-lang.org/architecture/). The philosophy for this framework is to isolate custom widgets into reusable components. You start with a custom `Model` type that implements `Component`, which is used to register a component with an optional argument. On registration, the model is used to construct the view and its widgets in the `init_view()` function. An event-handler is also spawned to handle events from both the component and any component emitting events to it. Those events are received and handled in the `update()` function. Both the `init_view()` and `update()` methods also have access to an outbound sender, which the caller can forward and consume however desired. See the examples directory for a demonstration of how to create a component.
|
This library is a GTK4 GUI framework inspired by [Relm](https://github.com/antoyo/relm), which is inspired by [Elm](https://guide.elm-lang.org/architecture/). The philosophy for this framework is to isolate custom widgets into reusable components. You start with a custom `Model` type that implements `Component`, which is used to register a component with an optional argument. On registration, the model is used to construct the view and its widgets in the `init_view()` function. An event-handler is also spawned to handle events from both the component and any component emitting events to it. Those events are received and handled in the `update()` function. Both the `init_view()` and `update()` methods also have access to an outbound sender, which the caller can forward and consume however desired. See the examples directory for a demonstration of how to create a component.
|
||||||
|
|
||||||
## Defining a Component
|
## Using a Macro to Define a Component
|
||||||
|
|
||||||
The simplest way to define a component is to use the `component!()` macro.
|
The simplest way to define a component is to use the `component!()` macro.
|
||||||
|
|
||||||
|
|
@ -13,14 +13,13 @@ pub enum MyCustomInputMessage {
|
||||||
}
|
}
|
||||||
|
|
||||||
component! {
|
component! {
|
||||||
// The `()` is the args parameter accepted by `init_view()`
|
// The model stores the state of this component.
|
||||||
// and `Component::register()`.
|
pub struct MyCustomModel {
|
||||||
pub struct MyCustomModel(()) {
|
|
||||||
pub state: String,
|
pub state: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
// The `gtk::Box` is the root widget returned in `init_view()`.
|
// Widgets managed by the view are stored here.
|
||||||
pub struct MyCustomWidgets(gtk::Box) {
|
pub struct MyCustomWidgets {
|
||||||
description: gtk::Label,
|
description: gtk::Label,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -30,24 +29,35 @@ component! {
|
||||||
// The type of the output sender
|
// The type of the output sender
|
||||||
type Output = ();
|
type Output = ();
|
||||||
|
|
||||||
// `self` is `MyCustomModel`, and must return `(MyCustomWidget, RootWidget)`
|
// Declares the root widget and how it should be constructed.
|
||||||
fn init_view(self, args, input, output) {
|
type Root = gtk::Box {
|
||||||
ccs::view! {
|
ccs::view! {
|
||||||
root = gtk::Box {
|
root = gtk::Box {
|
||||||
set_orientation: gtk::Orientation::Vertical,
|
set_orientation: gtk::Orientation::Vertical,
|
||||||
|
|
||||||
append: description = >k::Label {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(MyCustomWidgets { description }, root)
|
root
|
||||||
|
};
|
||||||
|
|
||||||
|
// Constructs the inner component's model and widgets, using the
|
||||||
|
// initial parameter given by `args`.
|
||||||
|
fn init(args: (), root, input, output) {
|
||||||
|
let description = gtk::Label::new();
|
||||||
|
|
||||||
|
root.append(&description);
|
||||||
|
|
||||||
|
ComponentInner {
|
||||||
|
model: MyCustomModel { state: String::new() },
|
||||||
|
widgets: MyCustomWidgets { description },
|
||||||
|
input,
|
||||||
|
output
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Where events are received, with `self` also being `MyCustomModel`, and
|
// Where events are received, with `component` is the `ComponentInner`,
|
||||||
// `widgets` is `MyCustomInputMessage`. `event` is `MyCustonInputMessage`.
|
// and `event` is a `MyCustomInputMessage` which was just received.
|
||||||
fn update(self, widgets, event, input, output) {
|
fn update(component, event) {
|
||||||
match event {
|
match event {
|
||||||
MyCustomInputMessage::Variant1 => {
|
MyCustomInputMessage::Variant1 => {
|
||||||
|
|
||||||
|
|
@ -64,8 +74,7 @@ component! {
|
||||||
Components can be created and have their output events forwarded:
|
Components can be created and have their output events forwarded:
|
||||||
|
|
||||||
```rs
|
```rs
|
||||||
let counter = InfoButton::default()
|
let counter = InfoButton::init("Clicked 0 times".into(), "Click".into())
|
||||||
.register("Clicked 0 times".into(), "Click".into())
|
|
||||||
.forward(input.clone(), |event| match event {
|
.forward(input.clone(), |event| match event {
|
||||||
InfoButtonOutput::Clicked => AppEvent::Increment
|
InfoButtonOutput::Clicked => AppEvent::Increment
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,12 @@ pub enum AppEvent {
|
||||||
component! {
|
component! {
|
||||||
/// The model where component state is stored.
|
/// The model where component state is stored.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct App(gtk::Application) {
|
pub struct App {
|
||||||
pub counter: usize,
|
pub counter: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Widgets that are initialized in the view.
|
/// Widgets that are initialized in the view.
|
||||||
pub struct AppWidgets(gtk::ApplicationWindow) {
|
pub struct AppWidgets {
|
||||||
list: gtk::ListBox,
|
list: gtk::ListBox,
|
||||||
destroyable: Option<Handle<gtk::Box, InfoButtonInput>>,
|
destroyable: Option<Handle<gtk::Box, InfoButtonInput>>,
|
||||||
counter: Handle<gtk::Box, InfoButtonInput>,
|
counter: Handle<gtk::Box, InfoButtonInput>,
|
||||||
|
|
@ -27,13 +27,14 @@ component! {
|
||||||
|
|
||||||
type Input = AppEvent;
|
type Input = AppEvent;
|
||||||
type Output = ();
|
type Output = ();
|
||||||
|
type Root = gtk::ApplicationWindow {
|
||||||
|
Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
fn init_view(self, app, input, _output) {
|
fn init(app: gtk::Application, root, input, output) {
|
||||||
let button_group = gtk::SizeGroup::new(gtk::SizeGroupMode::Both);
|
let button_group = gtk::SizeGroup::new(gtk::SizeGroupMode::Both);
|
||||||
|
|
||||||
// Create an `InfoButton` component.
|
let destroyable = InfoButton::init((String::new(), "Destroy".into(), button_group.clone()))
|
||||||
let destroyable = InfoButton::default()
|
|
||||||
.register((String::new(), "Destroy".into(), button_group.clone()))
|
|
||||||
.forward(input.clone(), |event| match event {
|
.forward(input.clone(), |event| match event {
|
||||||
InfoButtonOutput::Clicked => AppEvent::Destroy,
|
InfoButtonOutput::Clicked => AppEvent::Destroy,
|
||||||
});
|
});
|
||||||
|
|
@ -44,55 +45,53 @@ component! {
|
||||||
));
|
));
|
||||||
|
|
||||||
// Create a counter component, too.
|
// Create a counter component, too.
|
||||||
let counter = InfoButton::default()
|
let counter = InfoButton::init(("Click me too".into(), "Click".into(), button_group))
|
||||||
.register(("Click me too".into(), "Click".into(), button_group))
|
|
||||||
.forward(input.clone(), |event| match event {
|
.forward(input.clone(), |event| match event {
|
||||||
InfoButtonOutput::Clicked => AppEvent::Increment,
|
InfoButtonOutput::Clicked => AppEvent::Increment,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Construct the view for this component, attaching the component's widget.
|
|
||||||
ccs::view! {
|
ccs::view! {
|
||||||
window = gtk::ApplicationWindow {
|
container = gtk::Box {
|
||||||
set_application: Some(&app),
|
set_halign: gtk::Align::Center,
|
||||||
set_child = Some(>k::Box) {
|
set_size_request: args!(400, -1),
|
||||||
set_halign: gtk::Align::Center,
|
set_orientation: gtk::Orientation::Vertical,
|
||||||
set_size_request: args!(400, -1),
|
|
||||||
set_orientation: gtk::Orientation::Vertical,
|
|
||||||
|
|
||||||
append: list = >k::ListBox {
|
append: list = >k::ListBox {
|
||||||
set_selection_mode: gtk::SelectionMode::None,
|
set_selection_mode: gtk::SelectionMode::None,
|
||||||
set_hexpand: true,
|
set_hexpand: true,
|
||||||
|
|
||||||
append: destroyable.widget(),
|
append: destroyable.widget(),
|
||||||
append: counter.widget(),
|
append: counter.widget(),
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.show();
|
root.set_application(Some(&app));
|
||||||
|
root.set_child(Some(&container));
|
||||||
|
|
||||||
(
|
root.show();
|
||||||
AppWidgets {
|
|
||||||
list,
|
ComponentInner {
|
||||||
counter,
|
model: Self::default(),
|
||||||
destroyable: Some(destroyable),
|
widgets: AppWidgets { list, destroyable: Some(destroyable), counter },
|
||||||
},
|
input,
|
||||||
window,
|
output
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates the view
|
/// Updates the view
|
||||||
fn update(self, widgets, event, _input, _output) {
|
fn update(component, event) {
|
||||||
|
let &mut ComponentInner { ref mut model, ref mut widgets, .. } = component;
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
AppEvent::Increment => {
|
AppEvent::Increment => {
|
||||||
self.counter += 1;
|
model.counter += 1;
|
||||||
|
|
||||||
widgets
|
widgets
|
||||||
.counter
|
.counter
|
||||||
.emit(InfoButtonInput::SetDescription(format!(
|
.emit(InfoButtonInput::SetDescription(format!(
|
||||||
"Clicked {} times",
|
"Clicked {} times",
|
||||||
self.counter
|
model.counter
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,19 +14,17 @@ pub enum InfoButtonOutput {
|
||||||
|
|
||||||
component! {
|
component! {
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct InfoButton((String, String, gtk::SizeGroup)) {
|
pub struct InfoButton {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct InfoButtonWidgets(gtk::Box) {
|
pub struct InfoButtonWidgets {
|
||||||
description: gtk::Label,
|
description: gtk::Label,
|
||||||
}
|
}
|
||||||
|
|
||||||
type Input = InfoButtonInput;
|
type Input = InfoButtonInput;
|
||||||
type Output = InfoButtonOutput;
|
type Output = InfoButtonOutput;
|
||||||
|
|
||||||
fn init_view(self, args, _input, output) {
|
type Root = gtk::Box {
|
||||||
let (desc, button_label, sg) = args;
|
|
||||||
ccs::view! {
|
ccs::view! {
|
||||||
root = gtk::Box {
|
root = gtk::Box {
|
||||||
set_orientation: gtk::Orientation::Horizontal,
|
set_orientation: gtk::Orientation::Horizontal,
|
||||||
|
|
@ -35,34 +33,52 @@ component! {
|
||||||
set_margin_top: 8,
|
set_margin_top: 8,
|
||||||
set_margin_bottom: 8,
|
set_margin_bottom: 8,
|
||||||
set_spacing: 24,
|
set_spacing: 24,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
append: description = >k::Label {
|
root
|
||||||
set_label: &desc,
|
};
|
||||||
set_halign: gtk::Align::Start,
|
|
||||||
set_hexpand: true,
|
|
||||||
set_valign: gtk::Align::Center,
|
|
||||||
set_ellipsize: gtk::pango::EllipsizeMode::End,
|
|
||||||
},
|
|
||||||
|
|
||||||
append: button = >k::Button {
|
fn init(args: (String, String, gtk::SizeGroup), root, input, output) {
|
||||||
set_label: &button_label,
|
let (desc, button_label, sg) = args;
|
||||||
|
|
||||||
connect_clicked(output) => move |_| {
|
ccs::view! {
|
||||||
let _ = output.send(InfoButtonOutput::Clicked);
|
description = gtk::Label {
|
||||||
}
|
set_label: &desc,
|
||||||
|
set_halign: gtk::Align::Start,
|
||||||
|
set_hexpand: true,
|
||||||
|
set_valign: gtk::Align::Center,
|
||||||
|
set_ellipsize: gtk::pango::EllipsizeMode::End,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ccs::view! {
|
||||||
|
button = gtk::Button {
|
||||||
|
set_label: &button_label,
|
||||||
|
|
||||||
|
connect_clicked(output) => move |_| {
|
||||||
|
let _ = output.send(InfoButtonOutput::Clicked);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
root.append(&description);
|
||||||
|
root.append(&button);
|
||||||
|
|
||||||
sg.add_widget(&button);
|
sg.add_widget(&button);
|
||||||
|
|
||||||
(InfoButtonWidgets { description }, root)
|
ComponentInner {
|
||||||
|
model: InfoButton {},
|
||||||
|
widgets: InfoButtonWidgets { description },
|
||||||
|
input,
|
||||||
|
output,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(self, widgets, message, _input, _output) {
|
fn update(component, message) {
|
||||||
match message {
|
match message {
|
||||||
InfoButtonInput::SetDescription(value) => {
|
InfoButtonInput::SetDescription(value) => {
|
||||||
widgets.description.set_text(&value);
|
component.widgets.description.set_text(&value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,6 @@ fn main() {
|
||||||
gtk4::builders::ApplicationBuilder::new()
|
gtk4::builders::ApplicationBuilder::new()
|
||||||
.application_id("org.pop.CosmicComponentExample")
|
.application_id("org.pop.CosmicComponentExample")
|
||||||
.cosmic_run(|app| {
|
.cosmic_run(|app| {
|
||||||
App::default().register(app);
|
App::init(app);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,14 @@
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
|
/// The pieces that make up the state of the component.
|
||||||
|
pub struct ComponentInner<Model, Widgets, Input, Output> {
|
||||||
|
pub model: Model,
|
||||||
|
pub widgets: Widgets,
|
||||||
|
pub input: Sender<Input>,
|
||||||
|
pub output: Sender<Output>,
|
||||||
|
}
|
||||||
|
|
||||||
/// The basis of a COSMIC widget.
|
/// The basis of a COSMIC widget.
|
||||||
///
|
///
|
||||||
/// A component takes care of constructing the UI of a widget, managing an event-loop
|
/// A component takes care of constructing the UI of a widget, managing an event-loop
|
||||||
|
|
@ -10,7 +18,7 @@ use crate::*;
|
||||||
/// the consumer of the component.
|
/// the consumer of the component.
|
||||||
pub trait Component: Sized + 'static {
|
pub trait Component: Sized + 'static {
|
||||||
/// The arguments that are passed to the init_view method.
|
/// The arguments that are passed to the init_view method.
|
||||||
type InitialArgs;
|
type InitParams;
|
||||||
|
|
||||||
/// The message type that the component accepts as inputs.
|
/// The message type that the component accepts as inputs.
|
||||||
type Input: 'static;
|
type Input: 'static;
|
||||||
|
|
@ -19,27 +27,36 @@ pub trait Component: Sized + 'static {
|
||||||
type Output: 'static;
|
type Output: 'static;
|
||||||
|
|
||||||
/// The widget that was constructed by the component.
|
/// The widget that was constructed by the component.
|
||||||
type RootWidget: Clone + AsRef<gtk4::Widget>;
|
type Root: Clone + AsRef<gtk4::Widget>;
|
||||||
|
|
||||||
/// The type that's used for storing widgets created for this component.
|
/// The type that's used for storing widgets created for this component.
|
||||||
type Widgets: 'static;
|
type Widgets: 'static;
|
||||||
|
|
||||||
|
/// Initializes the root widget
|
||||||
|
fn init_root() -> Self::Root;
|
||||||
|
|
||||||
|
fn init_inner(
|
||||||
|
params: Self::InitParams,
|
||||||
|
root_widget: &Self::Root,
|
||||||
|
input: Sender<Self::Input>,
|
||||||
|
output: Sender<Self::Output>,
|
||||||
|
) -> ComponentInner<Self, Self::Widgets, Self::Input, Self::Output>;
|
||||||
|
|
||||||
/// Initializes the component and attaches it to the default local executor.
|
/// Initializes the component and attaches it to the default local executor.
|
||||||
///
|
///
|
||||||
/// Spawns an event loop on `glib::MainContext::default()`, which exists
|
/// Spawns an event loop on `glib::MainContext::default()`, which exists
|
||||||
/// for as long as the root widget remains alive.
|
/// for as long as the root widget remains alive.
|
||||||
fn register(
|
fn init(params: Self::InitParams) -> Registered<Self::Root, Self::Input, Self::Output> {
|
||||||
mut self,
|
let (sender, in_rx) = mpsc::unbounded_channel::<Self::Input>();
|
||||||
args: Self::InitialArgs,
|
let (out_tx, output) = mpsc::unbounded_channel::<Self::Output>();
|
||||||
) -> Registered<Self::RootWidget, Self::Input, Self::Output> {
|
|
||||||
let (mut sender, in_rx) = mpsc::unbounded_channel::<Self::Input>();
|
|
||||||
let (mut out_tx, output) = mpsc::unbounded_channel::<Self::Output>();
|
|
||||||
|
|
||||||
let (mut widgets, widget) = self.init_view(args, &mut sender, &mut out_tx);
|
let root = Self::init_root();
|
||||||
|
|
||||||
|
let mut component = Self::init_inner(params, &root, sender, out_tx);
|
||||||
|
|
||||||
let handle = Handle {
|
let handle = Handle {
|
||||||
widget,
|
widget: root,
|
||||||
sender: sender.clone(),
|
sender: component.input.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let (inner_tx, mut inner_rx) = mpsc::unbounded_channel::<InnerMessage<Self::Input>>();
|
let (inner_tx, mut inner_rx) = mpsc::unbounded_channel::<InnerMessage<Self::Input>>();
|
||||||
|
|
@ -55,7 +72,7 @@ pub trait Component: Sized + 'static {
|
||||||
while let Some(event) = inner_rx.recv().await {
|
while let Some(event) = inner_rx.recv().await {
|
||||||
match event {
|
match event {
|
||||||
InnerMessage::Message(event) => {
|
InnerMessage::Message(event) => {
|
||||||
self.update(&mut widgets, event, &mut sender, &mut out_tx);
|
Self::update(&mut component, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
InnerMessage::Drop => break,
|
InnerMessage::Drop => break,
|
||||||
|
|
@ -71,22 +88,11 @@ pub trait Component: Sized + 'static {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates the initial view and root widget.
|
|
||||||
fn init_view(
|
|
||||||
&mut self,
|
|
||||||
args: Self::InitialArgs,
|
|
||||||
input: &mut Sender<Self::Input>,
|
|
||||||
output: &mut Sender<Self::Output>,
|
|
||||||
) -> (Self::Widgets, Self::RootWidget);
|
|
||||||
|
|
||||||
/// Handles input messages and enables the programmer to update the model and view.
|
/// Handles input messages and enables the programmer to update the model and view.
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn update(
|
fn update(
|
||||||
&mut self,
|
component: &mut ComponentInner<Self, Self::Widgets, Self::Input, Self::Output>,
|
||||||
widgets: &mut Self::Widgets,
|
|
||||||
message: Self::Input,
|
message: Self::Input,
|
||||||
input: &mut Sender<Self::Input>,
|
|
||||||
output: &mut Sender<Self::Output>,
|
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use gtk4::prelude::*;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
pub use self::app_runner::AppRunner;
|
pub use self::app_runner::AppRunner;
|
||||||
pub use self::component::Component;
|
pub use self::component::{Component, ComponentInner};
|
||||||
pub use self::handle::{Handle, Registered};
|
pub use self::handle::{Handle, Registered};
|
||||||
pub use gtk4 as gtk;
|
pub use gtk4 as gtk;
|
||||||
pub use relm4_macros::view;
|
pub use relm4_macros::view;
|
||||||
|
|
@ -23,12 +23,14 @@ pub trait Widget<W> {
|
||||||
fn widget(&self) -> &W;
|
fn widget(&self) -> &W;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convenience function for `Component::register()`.
|
pub trait CosmicWidgetExt<W>: Widget<W>
|
||||||
pub fn register<C: Component>(
|
where
|
||||||
model: C,
|
W: AsRef<gtk::Widget>,
|
||||||
args: C::InitialArgs,
|
{
|
||||||
) -> Registered<C::RootWidget, C::Input, C::Output> {
|
fn attach_size_group(&self, sg: >k::SizeGroup) -> &Self {
|
||||||
model.register(args)
|
sg.add_widget(self.widget().as_ref());
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convenience function for forwarding events from a receiver to different sender.
|
/// Convenience function for forwarding events from a receiver to different sender.
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,14 @@
|
||||||
macro_rules! component {
|
macro_rules! component {
|
||||||
(
|
(
|
||||||
$(#[$attr:meta])*
|
$(#[$attr:meta])*
|
||||||
$mvis:vis struct $model:ident ($args:ty) {
|
$mvis:vis struct $model:ident {
|
||||||
$(
|
$(
|
||||||
$mpvis:vis $property:ident : $type:ty,
|
$mpvis:vis $property:ident : $type:ty,
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
|
|
||||||
$(#[$attr2:meta])*
|
$(#[$attr2:meta])*
|
||||||
$wvis:vis struct $widgets_:ident($root:ty) {
|
$wvis:vis struct $widgets_:ident {
|
||||||
$(
|
$(
|
||||||
$wpvis:vis $widgets_property:ident : $widgets_type:ty,
|
$wpvis:vis $widgets_property:ident : $widgets_type:ty,
|
||||||
)*
|
)*
|
||||||
|
|
@ -20,22 +20,20 @@ macro_rules! component {
|
||||||
|
|
||||||
type Input = $input:ty;
|
type Input = $input:ty;
|
||||||
type Output = $output:ty;
|
type Output = $output:ty;
|
||||||
|
type Root = $root:ty $init_root:block;
|
||||||
|
|
||||||
$(#[$attr3:meta])*
|
$(#[$attr4:meta])*
|
||||||
fn init_view(
|
fn init(
|
||||||
$selfv:ident,
|
$argsv:ident: $args:ty,
|
||||||
$argsv:ident,
|
$rootv:ident,
|
||||||
$inputv:ident,
|
$inputv:ident,
|
||||||
$outputv:ident
|
$outputv:ident
|
||||||
) $init_view:block
|
) $init_view:block
|
||||||
|
|
||||||
$(#[$attr4:meta])*
|
$(#[$attr5:meta])*
|
||||||
fn update(
|
fn update(
|
||||||
$selfv2:ident,
|
$componentv:ident,
|
||||||
$widgetsv:ident,
|
$messagev:ident
|
||||||
$messagev:ident,
|
|
||||||
$inputv2:ident,
|
|
||||||
$outputv2:ident
|
|
||||||
) $update:block
|
) $update:block
|
||||||
) => {
|
) => {
|
||||||
$(#[$attr])*
|
$(#[$attr])*
|
||||||
|
|
@ -49,27 +47,26 @@ macro_rules! component {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for $model {
|
impl Component for $model {
|
||||||
type InitialArgs = $args;
|
type InitParams = $args;
|
||||||
type Input = $input;
|
type Input = $input;
|
||||||
type Output = $output;
|
type Output = $output;
|
||||||
type RootWidget = $root;
|
type Root = $root;
|
||||||
type Widgets = $widgets_;
|
type Widgets = $widgets_;
|
||||||
|
|
||||||
$(#[$attr3])*
|
fn init_root() -> Self::Root $init_root
|
||||||
fn init_view(
|
|
||||||
&mut $selfv2,
|
|
||||||
$argsv: Self::InitialArgs,
|
|
||||||
$inputv: &mut Sender<Self::Input>,
|
|
||||||
$outputv: &mut Sender<Self::Output>
|
|
||||||
) -> (Self::Widgets, Self::RootWidget) $init_view
|
|
||||||
|
|
||||||
$(#[$attr4])*
|
$(#[$attr4])*
|
||||||
|
fn init_inner(
|
||||||
|
$argsv: Self::InitParams,
|
||||||
|
$rootv: &Self::Root,
|
||||||
|
$inputv: Sender<Self::Input>,
|
||||||
|
$outputv: Sender<Self::Output>
|
||||||
|
) -> ComponentInner<Self, Self::Widgets, Self::Input, Self::Output> $init_view
|
||||||
|
|
||||||
|
$(#[$attr5])*
|
||||||
fn update(
|
fn update(
|
||||||
&mut $selfv2,
|
$componentv: &mut ComponentInner<Self, Self::Widgets, Self::Input, Self::Output>,
|
||||||
$widgetsv: &mut Self::Widgets,
|
|
||||||
$messagev: Self::Input,
|
$messagev: Self::Input,
|
||||||
$inputv2: &mut Sender<Self::Input>,
|
|
||||||
$outputv2: &mut Sender<Self::Output>
|
|
||||||
) $update
|
) $update
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue