1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
use gtk::glib::Sender;
mod impls;
/// Trait that defines the types associated with the model.
///
/// A model can be anything that stores application state.
///
/// # Example
///
/// ```
/// # use relm4::Model;
/// # struct AppWidgets {}
/// #
/// struct AppModel {
/// counter: u8,
/// }
///
/// enum AppMsg {
/// Increment,
/// Decrement,
/// }
///
/// impl Model for AppModel {
/// type Msg = AppMsg;
/// type Widgets = AppWidgets;
/// type Components = ();
/// }
/// ```
pub trait Model: std::marker::Sized {
/// The message type that defines the messages that can be sent to modify the model.
type Msg: 'static;
/// The widgets type that can initialize and update the GUI with the data the model provides.
///
/// If you don't want any widgets (for example for defining a worker), just use `()` here.
type Widgets;
/// The components type that initializes the child components of this model.
///
/// If you don't want any component associated with this model just use `()`.
type Components: Components<Self>;
}
/// Define the behavior to update the model of the main app.
///
/// # Example
///
/// ```
/// # use relm4::{AppUpdate, Sender, Model};
/// # struct AppWidgets {}
/// #
/// # struct AppModel {
/// # counter: u8,
/// # }
/// #
/// # enum AppMsg {
/// # Increment,
/// # Decrement,
/// # }
/// #
/// # impl Model for AppModel {
/// # type Msg = AppMsg;
/// # type Widgets = AppWidgets;
/// # type Components = ();
/// # }
/// #
/// impl AppUpdate for AppModel {
/// fn update(&mut self, msg: AppMsg, _components: &(), _sender: Sender<AppMsg>) -> bool {
/// match msg {
/// AppMsg::Increment => {
/// self.counter += 1;
/// }
/// AppMsg::Decrement => {
/// self.counter -= 1;
/// }
/// }
/// true
/// }
/// }
/// ```
pub trait AppUpdate: Model {
/// Updates the model.
/// Typically a `match` statement is used to process the message.
///
/// Return [`true`] to continue running the application and return [`false`] to quit.
///
/// Components and sender don't need to be used but help you sending messages to
/// your components or queuing messages for self.
fn update(
&mut self,
msg: Self::Msg,
components: &Self::Components,
sender: Sender<Self::Msg>,
) -> bool;
}
/// Define the behavior to initialize and update a component or worker.
pub trait ComponentUpdate<ParentModel: Model>: Model {
/// Initialize the model of the component or worker.
///
/// In case you want to share information or settings with the parent component you
/// get the parent's model passed as parameter.
fn init_model(parent_model: &ParentModel) -> Self;
/// Updates the model.
/// Typically a `match` statement is used to process the message.
///
/// Components and sender don't need to be used but help you sending messages to
/// your components or queuing messages for self.
///
/// The parent sender allows to send messages to the parent component which for also can be the main app.
fn update(
&mut self,
msg: Self::Msg,
components: &Self::Components,
sender: Sender<Self::Msg>,
parent_sender: Sender<ParentModel::Msg>,
);
}
/// A message handler that can be used in situations where a [`RelmWorker`](crate::RelmWorker)
/// isn't flexible enough.
pub trait MessageHandler<ParentModel: Model> {
/// The message type of this message handler.
type Msg;
/// The sender type that can be used to send a message to a [`RelmMsgHandler`](crate::RelmMsgHandler).
type Sender;
/// Initialize this message handler.
fn init(parent_model: &ParentModel, parent_sender: Sender<ParentModel::Msg>) -> Self;
/// Sends a message to the message handler.
fn send(&self, msg: Self::Msg);
/// Get a sender for sending messages to this [`RelmMsgHandler`](crate::RelmMsgHandler).
fn sender(&self) -> Self::Sender;
}
/// Define behavior to turn the data of you model into widgets.
///
/// This trait and the associated struct can also be implemented by the `relm4-macros::widget` macro.
///
/// This trait has two generic types, its own model and the model of the parent (which can be `()`).
/// This allows you to define widgets that can work with different models and parent models.
/// Most commonly this is used to create reusable components.
pub trait Widgets<ModelType, ParentModel>
where
ModelType: Model<Widgets = Self>,
ParentModel: Model,
{
/// The root represents the first widget that all other widgets of this app or component are attached to.
/// The root of the main app must be a [`gtk::ApplicationWindow`].
type Root: std::fmt::Debug;
/// Initialize the UI.
///
/// Use the parent widgets to connect them to the widgets of this model.
///
/// Use the sender to connect UI events and send messages back to modify the model.
fn init_view(
model: &ModelType,
_components: &ModelType::Components,
sender: Sender<ModelType::Msg>,
) -> Self;
/// Optional method to initialize components.
/// This is only useful if you want to attach the widgets of a component to the widgets of this model.
fn connect_parent(&mut self, _parent_widgets: &ParentModel::Widgets) {}
/// Return a clone of the root widget. This is typically a GTK4 widget.
fn root_widget(&self) -> Self::Root;
/// Update the view to represent the updated model.
fn view(&mut self, model: &ModelType, sender: Sender<ModelType::Msg>);
}
/// Define how to initialize one or more components.
///
/// Typically a struct is used to store multiple components that are child
/// components of the app or another component.
///
/// # Example
///
/// ```
/// # use relm4::{RelmComponent, Components, AppUpdate, Sender, Model, ComponentUpdate};
/// # struct AppWidgets {}
/// #
/// # struct AppModel {
/// # counter: u8,
/// # }
/// #
/// # enum AppMsg {
/// # Increment,
/// # Decrement,
/// # }
/// #
/// # impl Model for AppModel {
/// # type Msg = AppMsg;
/// # type Widgets = AppWidgets;
/// # type Components = ();
/// # }
/// #
/// # struct CompMsg {};
/// # struct Comp1Model {};
/// # struct Comp2Model {};
/// #
/// # impl Model for Comp1Model {
/// # type Msg = CompMsg;
/// # type Widgets = ();
/// # type Components = ();
/// # }
/// #
/// # impl Model for Comp2Model {
/// # type Msg = CompMsg;
/// # type Widgets = ();
/// # type Components = ();
/// # }
/// #
/// # impl ComponentUpdate<AppModel> for Comp1Model {
/// # fn init_model(_parent_model: &AppModel) -> Self {
/// # Comp1Model {}
/// # }
/// #
/// # fn update(
/// # &mut self,
/// # message: CompMsg,
/// # _components: &(),
/// # _sender: Sender<CompMsg>,
/// # _parent_sender: Sender<AppMsg>,
/// # ) {}
/// # }
/// #
/// # impl ComponentUpdate<AppModel> for Comp2Model {
/// # fn init_model(_parent_model: &AppModel) -> Self {
/// # Comp2Model {}
/// # }
/// #
/// # fn update(
/// # &mut self,
/// # message: CompMsg,
/// # _components: &(),
/// # _sender: Sender<CompMsg>,
/// # _parent_sender: Sender<AppMsg>,
/// # ) {}
/// # }
/// #
/// struct AppComponents {
/// comp1: RelmComponent<Comp1Model, AppModel>,
/// comp2: RelmComponent<Comp2Model, AppModel>,
/// }
///
/// impl Components<AppModel> for AppComponents {
/// fn init_components(parent_model: &AppModel, parent_sender: Sender<AppMsg>) -> Self {
/// AppComponents {
/// comp1: RelmComponent::new(parent_model, parent_sender.clone()),
/// comp2: RelmComponent::new(parent_model, parent_sender),
/// }
/// }
///
/// fn connect_parent(&mut self, parent_widgets: &AppWidgets) {
/// self.comp1.connect_parent(parent_widgets);
/// self.comp2.connect_parent(parent_widgets);
/// }
/// }
/// ```
pub trait Components<ParentModel: ?Sized + Model> {
/// Initialize your components and workers inside this function.
fn init_components(parent_model: &ParentModel, parent_sender: Sender<ParentModel::Msg>)
-> Self;
/// Connect the components to their parent components widgets (to set the parent window for example).
fn connect_parent(&mut self, _parent_widgets: &ParentModel::Widgets);
}
#[cfg(feature = "tokio-rt")]
#[cfg_attr(doc, doc(cfg(feature = "tokio-rt")))]
#[async_trait::async_trait]
/// [`ComponentUpdate`] for asynchronous workers and components.
pub trait AsyncComponentUpdate<ParentModel: Model>: Model {
/// Initialize the model of the component or worker.
///
/// In case you want to share information or settings with the parent component you
/// get the parent's model passed as parameter.
fn init_model(parent_model: &ParentModel) -> Self;
/// Update the model. The parent_sender allows to send messages to the parent.
async fn update(
&mut self,
msg: Self::Msg,
components: &Self::Components,
sender: Sender<Self::Msg>,
parent_sender: Sender<ParentModel::Msg>,
);
}