102 lines
No EOL
3.4 KiB
Markdown
102 lines
No EOL
3.4 KiB
Markdown
# COSMIC Component System
|
|
|
|
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.
|
|
|
|
## Using a Macro to Define a Component
|
|
|
|
The simplest way to define a component is to use the `component!()` macro.
|
|
|
|
```rs
|
|
pub enum MyCustomInputMessage {
|
|
Variant1,
|
|
Variant2,
|
|
}
|
|
|
|
component! {
|
|
// The model stores the state of this component.
|
|
pub struct MyCustomModel {
|
|
pub state: String,
|
|
}
|
|
|
|
// Widgets managed by the view are stored here.
|
|
pub struct MyCustomWidgets {
|
|
description: gtk::Label,
|
|
}
|
|
|
|
// The type of the input sender
|
|
type Input = MyCustomInputMessage;
|
|
|
|
// The type of the output sender
|
|
type Output = ();
|
|
|
|
// Declares the root widget and how it should be constructed.
|
|
type Root = gtk::Box {
|
|
ccs::view! {
|
|
root = gtk::Box {
|
|
set_orientation: gtk::Orientation::Vertical,
|
|
}
|
|
}
|
|
|
|
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 `component` is the `ComponentInner`,
|
|
// and `event` is a `MyCustomInputMessage` which was just received.
|
|
fn update(component, event) {
|
|
match event {
|
|
MyCustomInputMessage::Variant1 => {
|
|
|
|
}
|
|
|
|
MyCustomInputMessage::Variant2 => {
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
Components can be created and have their output events forwarded:
|
|
|
|
```rs
|
|
let counter = InfoButton::init("Clicked 0 times".into(), "Click".into())
|
|
.forward(input.clone(), |event| match event {
|
|
InfoButtonOutput::Clicked => AppEvent::Increment
|
|
});
|
|
```
|
|
|
|
The handle returned can be used to emit inputs to it, and to get the root widget.
|
|
|
|
```rs
|
|
counter.emit(InfoButtonInput::SetDescription(format!("Clicked {} times", count)));
|
|
|
|
box.append(counter.widget());
|
|
```
|
|
|
|
|
|
## See Also
|
|
|
|
[Relm4](https://github.com/AaronErhardt/relm4) uses a similar approach, but closely follows the Elm model. This library was created as an alternative approach that makes developing reusable components with forwardable events simpler.
|
|
|
|
## License
|
|
|
|
Licensed under the [Mozilla Public License 2.0](https://choosealicense.com/licenses/mpl-2.0/).
|
|
|
|
### Contribution
|
|
|
|
Any contribution intentionally submitted for inclusion in the work by you shall be licensed under the Mozilla Public License 2.0 (MPL-2.0). |