Factory
-Factories define how to generate widgets from data collections. +
Factories define how to generate widgets from data collections. GTK also has factories, yet Relm4 uses its own factory implementation which is much easier to use in regular Rust code.
This app will have a dynamic number of counters. @@ -215,7 +215,7 @@
- Move a counter up
- Move a counter down
@@ -268,19 +268,19 @@ Creat
#[name(add_button)]
gtk::Button {
- set_label: "+",
+ set_label: "+",
connect_clicked => CounterMsg::Increment,
},
#[name(remove_button)]
gtk::Button {
- set_label: "-",
+ set_label: "-",
connect_clicked => CounterMsg::Decrement,
},
#[name(move_up_button)]
gtk::Button {
- set_label: "Up",
+ set_label: "Up",
connect_clicked[sender, index] => move |_| {
sender.output(CounterOutput::MoveUp(index.clone())).unwrap();
}
@@ -288,7 +288,7 @@ Creat
#[name(move_down_button)]
gtk::Button {
- set_label: "Down",
+ set_label: "Down",
connect_clicked[sender, index] => move |_| {
sender.output(CounterOutput::MoveDown(index.clone())).unwrap();
}
@@ -296,7 +296,7 @@ Creat
#[name(to_front_button)]
gtk::Button {
- set_label: "To Start",
+ set_label: "To Start",
connect_clicked[sender, index] => move |_| {
sender.output(CounterOutput::SendFront(index.clone())).unwrap();
}
@@ -304,7 +304,7 @@ Creat
}
}
Initializing the model
-FactoryComponent
has separate functions for initializing the model and the widgets.
+
FactoryComponent
has separate functions for initializing the model and the widgets.
This means, that we are a bit less flexible, but don't need view_output!()
here.
Also, we just need to implement the init_model
function because init_widgets
is already implemented by the macro.
fn init_model(value: Self::Init, _index: &DynamicIndex, _sender: FactorySender<Self>) -> Self {
@@ -346,7 +346,7 @@ // Initialize the UI.
fn init(
counter: Self::Init,
- root: &Self::Root,
+ root: Self::Root,
sender: ComponentSender<Self>,
) -> ComponentParts<Self> {
let counters = FactoryVecDeque::builder()
@@ -373,7 +373,7 @@ view! {
gtk::Window {
- set_title: Some("Factory example"),
+ set_title: Some("Factory example"),
set_default_size: (300, 100),
gtk::Box {
@@ -382,12 +382,12 @@ The main
Awesome, we almost made it!
We only need to define the main function to run our application.
fn main() {
- let app = RelmApp::new("relm4.example.factory");
+ let app = RelmApp::new("relm4.example.factory");
app.run::<App>(0);
}
The complete code
@@ -492,19 +492,19 @@ The compl
#[name(add_button)]
gtk::Button {
- set_label: "+",
+ set_label: "+",
connect_clicked => CounterMsg::Increment,
},
#[name(remove_button)]
gtk::Button {
- set_label: "-",
+ set_label: "-",
connect_clicked => CounterMsg::Decrement,
},
#[name(move_up_button)]
gtk::Button {
- set_label: "Up",
+ set_label: "Up",
connect_clicked[sender, index] => move |_| {
sender.output(CounterOutput::MoveUp(index.clone())).unwrap();
}
@@ -512,7 +512,7 @@ The compl
#[name(move_down_button)]
gtk::Button {
- set_label: "Down",
+ set_label: "Down",
connect_clicked[sender, index] => move |_| {
sender.output(CounterOutput::MoveDown(index.clone())).unwrap();
}
@@ -520,7 +520,7 @@ The compl
#[name(to_front_button)]
gtk::Button {
- set_label: "To Start",
+ set_label: "To Start",
connect_clicked[sender, index] => move |_| {
sender.output(CounterOutput::SendFront(index.clone())).unwrap();
}
@@ -566,7 +566,7 @@ The compl
view! {
gtk::Window {
- set_title: Some("Factory example"),
+ set_title: Some("Factory example"),
set_default_size: (300, 100),
gtk::Box {
@@ -575,12 +575,12 @@ The compl
set_margin_all: 5,
gtk::Button {
- set_label: "Add counter",
+ set_label: "Add counter",
connect_clicked => AppMsg::AddCounter,
},
gtk::Button {
- set_label: "Remove counter",
+ set_label: "Remove counter",
connect_clicked => AppMsg::RemoveCounter,
},
@@ -596,7 +596,7 @@ The compl
// Initialize the UI.
fn init(
counter: Self::Init,
- root: &Self::Root,
+ root: Self::Root,
sender: ComponentSender<Self>,
) -> ComponentParts<Self> {
let counters = FactoryVecDeque::builder()
@@ -650,7 +650,7 @@ The compl
}
fn main() {
- let app = RelmApp::new("relm4.example.factory");
+ let app = RelmApp::new("relm4.example.factory");
app.run::<App>(0);
}
diff --git a/book/next/efficient_ui/tracker.html b/book/next/efficient_ui/tracker.html
index 6d4e5d42fb8b..4093b20ae048 100644
--- a/book/next/efficient_ui/tracker.html
+++ b/book/next/efficient_ui/tracker.html
@@ -210,7 +210,7 @@ The track
To reset all previous changes, you can call {struct_var_name}.reset()
.
Example
First we have to add the tracker library to Cargo.toml
:
-tracker = "0.1"
+tracker = "0.1"
Now let's have a look at a small example.
#[tracker::track]
@@ -224,7 +224,7 @@ Example
x: 0,
y: 0,
// the macro generates a new variable called
- // "tracker" which stores the changes
+ // "tracker" which stores the changes
tracker: 0,
};
@@ -248,22 +248,22 @@ The icons
Before we can select random icons, we need to quickly implement a function that will return us random image names available in the default GTK icon theme.
const ICON_LIST: &[&str] = &[
- "bookmark-new-symbolic",
- "edit-copy-symbolic",
- "edit-cut-symbolic",
- "edit-find-symbolic",
- "starred-symbolic",
- "system-run-symbolic",
- "emoji-objects-symbolic",
- "emoji-nature-symbolic",
- "display-brightness-symbolic",
+ "bookmark-new-symbolic",
+ "edit-copy-symbolic",
+ "edit-cut-symbolic",
+ "edit-find-symbolic",
+ "starred-symbolic",
+ "system-run-symbolic",
+ "emoji-objects-symbolic",
+ "emoji-nature-symbolic",
+ "display-brightness-symbolic",
];
fn random_icon_name() -> &'static str {
ICON_LIST
.iter()
.choose(&mut rand::thread_rng())
- .expect("Could not choose a random icon")
+ .expect("Could not choose a random icon")
}
The model
For our model we only need to store the two icon names and whether both of them are identical.
@@ -301,8 +301,8 @@ The view
view! {
#[root]
gtk::ApplicationWindow {
- #[track = "model.changed(AppModel::identical())"]
- set_class_active: ("identical", model.identical),
+ #[track = "model.changed(AppModel::identical())"]
+ set_class_active: ("identical", model.identical),
gtk::Box {
set_orientation: gtk::Orientation::Horizontal,
set_spacing: 10,
@@ -312,11 +312,11 @@ The view
set_spacing: 10,
gtk::Image {
set_pixel_size: 50,
- #[track = "model.changed(AppModel::first_icon())"]
+ #[track = "model.changed(AppModel::first_icon())"]
set_icon_name: Some(model.first_icon),
},
gtk::Button {
- set_label: "New random image",
+ set_label: "New random image",
connect_clicked[sender] => move |_| {
sender.input(AppInput::UpdateFirst)
}
@@ -327,11 +327,11 @@ The view
set_spacing: 10,
gtk::Image {
set_pixel_size: 50,
- #[track = "model.changed(AppModel::second_icon())"]
+ #[track = "model.changed(AppModel::second_icon())"]
set_icon_name: Some(model.second_icon),
},
gtk::Button {
- set_label: "New random image",
+ set_label: "New random image",
connect_clicked[sender] => move |_| {
sender.input(AppInput::UpdateSecond)
}
@@ -340,32 +340,34 @@ The view
}
}
}
-The overall UI is pretty simple: A window that contains a box. This box itself has two boxes that display the two icons and the two buttons to update them.
-We also added some additional code in init
that runs before the view is constructed. In our case, we want to add custom CSS that sets the background color for elements with class name "identical".
- relm4::set_global_css(".identical { background: #00ad5c; }");
-
- // Insert the macro code generation here
- let widgets = view_output!();
+The main function
+In this example, we need some additional code in fn main()
to add custom CSS that sets the background color for elements with class name "identical".
+Later, we just need to assign the "identical" class name to a widget to make it match the CSS selector.
+fn main() {
+ let app = RelmApp::new("relm4.test.simple");
+ app.set_global_css(".identical { background: #00ad5c; }");
+ app.run::<AppModel>(());
+}
The #[track]
attribute
The #[track]
attribute is applied to method invocations in our view code. It allows us to add a condition to the update: if the condition is true, the method will be called, otherwise, it will be skipped. The attribute syntax looks like this:
-#[track = "<boolean expression>"]
+#[track = "<boolean expression>"]
Let's have a look at its first appearance:
- #[track = "model.changed(AppModel::identical())"]
- set_class_active: ("identical", model.identical),
+ #[track = "model.changed(AppModel::identical())"]
+ set_class_active: ("identical", model.identical),
The set_class_active
method is used to either activate or disable a CSS class. It takes two parameters, the first is the class itself and the second is a boolean which specifies if the class should be added (true
) or removed (false
).
The value of the #[track]
attribute is parsed as a boolean expression. This expression will be used as a condition to check whether something has changed. If this condition is true
, the set_class_active
method will be called with the parameters it guards.
The macro expansion for method calls annotated with the #[track]
attribute look roughly like this:
if model.changed(AppModel::identical()) {
- self.main_window.set_class_active("identical", model.identical);
+ self.main_window.set_class_active("identical", model.identical);
}
That's all. It's pretty simple, actually. We just use a condition that allows us to update our widgets only when needed.
The second #[track]
attribute works similarly:
- #[track = "model.changed(AppModel::first_icon())"]
+ #[track = "model.changed(AppModel::first_icon())"]
set_icon_name: Some(model.first_icon),
-Debugging Helper
+Using a tracker as debugging helper
Since the #[track]
attribute parses expressions, you can use the following syntax to debug your trackers:
-#[track = "{ println!("Update widget"); argument }"]
+#[track = "{ println!("Update widget"); argument }"]
Initializing the model
There's one last thing to point out. When initializing our model, we need to initialize the tracker
field as well. The initial value doesn't really matter because we call reset()
in the update function anyway, but usually 0
is used.
@@ -382,22 +384,22 @@ The compl
use relm4::{gtk, ComponentParts, ComponentSender, RelmApp, RelmWidgetExt, SimpleComponent};
const ICON_LIST: &[&str] = &[
- "bookmark-new-symbolic",
- "edit-copy-symbolic",
- "edit-cut-symbolic",
- "edit-find-symbolic",
- "starred-symbolic",
- "system-run-symbolic",
- "emoji-objects-symbolic",
- "emoji-nature-symbolic",
- "display-brightness-symbolic",
+ "bookmark-new-symbolic",
+ "edit-copy-symbolic",
+ "edit-cut-symbolic",
+ "edit-find-symbolic",
+ "starred-symbolic",
+ "system-run-symbolic",
+ "emoji-objects-symbolic",
+ "emoji-nature-symbolic",
+ "display-brightness-symbolic",
];
fn random_icon_name() -> &'static str {
ICON_LIST
.iter()
.choose(&mut rand::thread_rng())
- .expect("Could not choose a random icon")
+ .expect("Could not choose a random icon")
}
// The track proc macro allows to easily track changes to different
@@ -424,8 +426,8 @@ The compl
view! {
#[root]
gtk::ApplicationWindow {
- #[track = "model.changed(AppModel::identical())"]
- set_class_active: ("identical", model.identical),
+ #[track = "model.changed(AppModel::identical())"]
+ set_class_active: ("identical", model.identical),
gtk::Box {
set_orientation: gtk::Orientation::Horizontal,
set_spacing: 10,
@@ -435,11 +437,11 @@ The compl
set_spacing: 10,
gtk::Image {
set_pixel_size: 50,
- #[track = "model.changed(AppModel::first_icon())"]
+ #[track = "model.changed(AppModel::first_icon())"]
set_icon_name: Some(model.first_icon),
},
gtk::Button {
- set_label: "New random image",
+ set_label: "New random image",
connect_clicked[sender] => move |_| {
sender.input(AppInput::UpdateFirst)
}
@@ -450,11 +452,11 @@ The compl
set_spacing: 10,
gtk::Image {
set_pixel_size: 50,
- #[track = "model.changed(AppModel::second_icon())"]
+ #[track = "model.changed(AppModel::second_icon())"]
set_icon_name: Some(model.second_icon),
},
gtk::Button {
- set_label: "New random image",
+ set_label: "New random image",
connect_clicked[sender] => move |_| {
sender.input(AppInput::UpdateSecond)
}
@@ -467,7 +469,7 @@ The compl
// Initialize the UI.
fn init(
_params: Self::Init,
- root: &Self::Root,
+ root: Self::Root,
sender: ComponentSender<Self>,
) -> ComponentParts<Self> {
let model = AppModel {
@@ -477,8 +479,6 @@ The compl
tracker: 0,
};
- relm4::set_global_css(".identical { background: #00ad5c; }");
-
// Insert the macro code generation here
let widgets = view_output!();
@@ -502,7 +502,8 @@ The compl
}
fn main() {
- let app = RelmApp::new("relm4.test.simple");
+ let app = RelmApp::new("relm4.test.simple");
+ app.set_global_css(".identical { background: #00ad5c; }");
app.run::<AppModel>(());
}
diff --git a/book/next/first_app.html b/book/next/first_app.html
index fa11c2a7f9fc..44cf9eaf8071 100644
--- a/book/next/first_app.html
+++ b/book/next/first_app.html
@@ -194,7 +194,7 @@ Your first app<
Application architecture
Often, programming concepts are easier to understand when explained with examples or metaphors from the real world. To understand how Relm4 apps work, you can think about a computer as a person.
-Our job as a programmer is to ensure that the users of our app will be able to communicate with the computer through the UI. Since the computer can't understand our human language, it needs some help from us to get the communication going.
+Our job as a programmer is to ensure that the users of our app will be able to communicate with the computer through the UI. Since the computer can't understand our human language, it needs some help from us to get the communication going.
Let's have a look at what we need to get this done!
Messages
For our app, we just want to tell the computer to either increment or decrement a counter.
@@ -236,7 +236,7 @@ fn init_root() -> Self::Root {
gtk::Window::builder()
- .title("Simple app")
+ .title("Simple app")
.default_width(300)
.default_height(100)
.build()
@@ -249,7 +249,7 @@ /// Initialize the UI and model.
fn init(
counter: Self::Init,
- window: &Self::Root,
+ window: Self::Root,
sender: ComponentSender<Self>,
) -> relm4::ComponentParts<Self> {
let model = AppModel { counter };
@@ -259,10 +259,10 @@
Of course, the computer needs to do more than just remembering things, it also needs to process information. Here, both the model and message types come into play.
The update
function of the SimpleComponent
trait tells the computer how to process messages and how to update its memory.
fn update(&mut self, message: Self::Input, _sender: ComponentSender<Self>) {
@@ -307,12 +307,12 @@
Running the App
The last step is to run the app we just wrote. To do so, we just need to initialize our model and pass it into RelmApp::new()
.
fn main() {
- let app = RelmApp::new("relm4.test.simple_manual");
+ let app = RelmApp::new("relm4.test.simple_manual");
app.run::<AppModel>(0);
}
🎉 Congratulations! You just wrote your first app with Relm4! 🎉
@@ -376,7 +376,7 @@ The compl
fn init_root() -> Self::Root {
gtk::Window::builder()
- .title("Simple app")
+ .title("Simple app")
.default_width(300)
.default_height(100)
.build()
@@ -385,7 +385,7 @@ The compl
/// Initialize the UI and model.
fn init(
counter: Self::Init,
- window: &Self::Root,
+ window: Self::Root,
sender: ComponentSender<Self>,
) -> relm4::ComponentParts<Self> {
let model = AppModel { counter };
@@ -395,10 +395,10 @@ The compl
.spacing(5)
.build();
- let inc_button = gtk::Button::with_label("Increment");
- let dec_button = gtk::Button::with_label("Decrement");
+ let inc_button = gtk::Button::with_label("Increment");
+ let dec_button = gtk::Button::with_label("Decrement");
- let label = gtk::Label::new(Some(&format!("Counter: {}", model.counter)));
+ let label = gtk::Label::new(Some(&format!("Counter: {}", model.counter)));
label.set_margin_all(5);
window.set_child(Some(&vbox));
@@ -435,12 +435,12 @@ The compl
fn update_view(&self, widgets: &mut Self::Widgets, _sender: ComponentSender<Self>) {
widgets
.label
- .set_label(&format!("Counter: {}", self.counter));
+ .set_label(&format!("Counter: {}", self.counter));
}
}
fn main() {
- let app = RelmApp::new("relm4.test.simple_manual");
+ let app = RelmApp::new("relm4.test.simple_manual");
app.run::<AppModel>(0);
}
diff --git a/book/next/gtk_rs.html b/book/next/gtk_rs.html
index 657572a2fe98..3ccd4cbb845e 100644
--- a/book/next/gtk_rs.html
+++ b/book/next/gtk_rs.html
@@ -187,19 +187,19 @@ gtk-rs overvi
GObjects
GTK is an object-oriented framework that uses the GObject library to implement objects. GObjects have some really useful features that we will discuss in the following sections.
Subclassing
-Like many other OOP frameworks or languages, GObjects can inherit from other GObjects. This is called subclassing. In the case of GTK, that’s really helpful because it allows us to create custom widgets.
+Like many other OOP frameworks or languages, GObjects can inherit from other GObjects. This is called subclassing. In the case of GTK, that’s really helpful because it allows us to create custom widgets.
For example, you could use subclassing to create your own button widget that acts as a counter. Or you can create a custom application window that better suits your application.
Read more about subclassing in the gtk-rs book.
Properties
Each GObject can have properties that work similar to the fields of a structure in Rust. You can set them and you can read (get) them. But one thing that's particularly cool is that properties can be bound to other properties.
-For example, you could bind the "visible" property of a widget to the "active" property of a gtk::ToggleButton
. This would allow you to show or hide the widget using the toggle button and the best part is, that it's done fully automatically!
+For example, you could bind the "visible" property of a widget to the "active" property of a gtk::ToggleButton
. This would allow you to show or hide the widget using the toggle button and the best part is, that it's done fully automatically!
Read more about properties in the gtk-rs book.
Signals
-GObjects can not only have properties but also signals. Actually, we've been using signals all the time, for example, by using the connect_clicked
method on a button. This method simply adds an event handler function for the "click" signal.
+GObjects can not only have properties but also signals. Actually, we've been using signals all the time, for example, by using the connect_clicked
method on a button. This method simply adds an event handler function for the "click" signal.
You can create your own signals in custom widgets. You can also use emit to emit signals on you widgets manually.
Read more about signals in the gtk-rs book.
diff --git a/book/next/index.html b/book/next/index.html
index 82473678ea0a..b8d0c56ccdee 100644
--- a/book/next/index.html
+++ b/book/next/index.html
@@ -187,7 +187,7 @@
-Relm4 is an idiomatic GUI library inspired by Elm and based on gtk4-rs.
+
Relm4 is an idiomatic GUI library inspired by Elm and based on gtk4-rs.
It is a new version of relm that's built from scratch and is compatible with GTK4 and libadwaita.
Creat
#[name(add_button)]
gtk::Button {
- set_label: "+",
+ set_label: "+",
connect_clicked => CounterMsg::Increment,
},
#[name(remove_button)]
gtk::Button {
- set_label: "-",
+ set_label: "-",
connect_clicked => CounterMsg::Decrement,
},
#[name(move_up_button)]
gtk::Button {
- set_label: "Up",
+ set_label: "Up",
connect_clicked[sender, index] => move |_| {
sender.output(CounterOutput::MoveUp(index.clone())).unwrap();
}
@@ -288,7 +288,7 @@ Creat
#[name(move_down_button)]
gtk::Button {
- set_label: "Down",
+ set_label: "Down",
connect_clicked[sender, index] => move |_| {
sender.output(CounterOutput::MoveDown(index.clone())).unwrap();
}
@@ -296,7 +296,7 @@ Creat
#[name(to_front_button)]
gtk::Button {
- set_label: "To Start",
+ set_label: "To Start",
connect_clicked[sender, index] => move |_| {
sender.output(CounterOutput::SendFront(index.clone())).unwrap();
}
@@ -304,7 +304,7 @@ Creat
}
}
Initializing the model
-FactoryComponent
has separate functions for initializing the model and the widgets.
+
FactoryComponent
has separate functions for initializing the model and the widgets.
This means, that we are a bit less flexible, but don't need view_output!()
here.
Also, we just need to implement the init_model
function because init_widgets
is already implemented by the macro.
fn init_model(value: Self::Init, _index: &DynamicIndex, _sender: FactorySender<Self>) -> Self {
@@ -346,7 +346,7 @@ // Initialize the UI.
fn init(
counter: Self::Init,
- root: &Self::Root,
+ root: Self::Root,
sender: ComponentSender<Self>,
) -> ComponentParts<Self> {
let counters = FactoryVecDeque::builder()
@@ -373,7 +373,7 @@ view! {
gtk::Window {
- set_title: Some("Factory example"),
+ set_title: Some("Factory example"),
set_default_size: (300, 100),
gtk::Box {
@@ -382,12 +382,12 @@ The main
Awesome, we almost made it!
We only need to define the main function to run our application.
fn main() {
- let app = RelmApp::new("relm4.example.factory");
+ let app = RelmApp::new("relm4.example.factory");
app.run::<App>(0);
}
The complete code
@@ -492,19 +492,19 @@ The compl
#[name(add_button)]
gtk::Button {
- set_label: "+",
+ set_label: "+",
connect_clicked => CounterMsg::Increment,
},
#[name(remove_button)]
gtk::Button {
- set_label: "-",
+ set_label: "-",
connect_clicked => CounterMsg::Decrement,
},
#[name(move_up_button)]
gtk::Button {
- set_label: "Up",
+ set_label: "Up",
connect_clicked[sender, index] => move |_| {
sender.output(CounterOutput::MoveUp(index.clone())).unwrap();
}
@@ -512,7 +512,7 @@ The compl
#[name(move_down_button)]
gtk::Button {
- set_label: "Down",
+ set_label: "Down",
connect_clicked[sender, index] => move |_| {
sender.output(CounterOutput::MoveDown(index.clone())).unwrap();
}
@@ -520,7 +520,7 @@ The compl
#[name(to_front_button)]
gtk::Button {
- set_label: "To Start",
+ set_label: "To Start",
connect_clicked[sender, index] => move |_| {
sender.output(CounterOutput::SendFront(index.clone())).unwrap();
}
@@ -566,7 +566,7 @@ The compl
view! {
gtk::Window {
- set_title: Some("Factory example"),
+ set_title: Some("Factory example"),
set_default_size: (300, 100),
gtk::Box {
@@ -575,12 +575,12 @@ The compl
set_margin_all: 5,
gtk::Button {
- set_label: "Add counter",
+ set_label: "Add counter",
connect_clicked => AppMsg::AddCounter,
},
gtk::Button {
- set_label: "Remove counter",
+ set_label: "Remove counter",
connect_clicked => AppMsg::RemoveCounter,
},
@@ -596,7 +596,7 @@ The compl
// Initialize the UI.
fn init(
counter: Self::Init,
- root: &Self::Root,
+ root: Self::Root,
sender: ComponentSender<Self>,
) -> ComponentParts<Self> {
let counters = FactoryVecDeque::builder()
@@ -650,7 +650,7 @@ The compl
}
fn main() {
- let app = RelmApp::new("relm4.example.factory");
+ let app = RelmApp::new("relm4.example.factory");
app.run::<App>(0);
}
diff --git a/book/next/efficient_ui/tracker.html b/book/next/efficient_ui/tracker.html
index 6d4e5d42fb8b..4093b20ae048 100644
--- a/book/next/efficient_ui/tracker.html
+++ b/book/next/efficient_ui/tracker.html
@@ -210,7 +210,7 @@ The track
To reset all previous changes, you can call {struct_var_name}.reset()
.
Example
First we have to add the tracker library to Cargo.toml
:
-tracker = "0.1"
+tracker = "0.1"
Now let's have a look at a small example.
#[tracker::track]
@@ -224,7 +224,7 @@ Example
x: 0,
y: 0,
// the macro generates a new variable called
- // "tracker" which stores the changes
+ // "tracker" which stores the changes
tracker: 0,
};
@@ -248,22 +248,22 @@ The icons
Before we can select random icons, we need to quickly implement a function that will return us random image names available in the default GTK icon theme.
const ICON_LIST: &[&str] = &[
- "bookmark-new-symbolic",
- "edit-copy-symbolic",
- "edit-cut-symbolic",
- "edit-find-symbolic",
- "starred-symbolic",
- "system-run-symbolic",
- "emoji-objects-symbolic",
- "emoji-nature-symbolic",
- "display-brightness-symbolic",
+ "bookmark-new-symbolic",
+ "edit-copy-symbolic",
+ "edit-cut-symbolic",
+ "edit-find-symbolic",
+ "starred-symbolic",
+ "system-run-symbolic",
+ "emoji-objects-symbolic",
+ "emoji-nature-symbolic",
+ "display-brightness-symbolic",
];
fn random_icon_name() -> &'static str {
ICON_LIST
.iter()
.choose(&mut rand::thread_rng())
- .expect("Could not choose a random icon")
+ .expect("Could not choose a random icon")
}
The model
For our model we only need to store the two icon names and whether both of them are identical.
@@ -301,8 +301,8 @@ The view
view! {
#[root]
gtk::ApplicationWindow {
- #[track = "model.changed(AppModel::identical())"]
- set_class_active: ("identical", model.identical),
+ #[track = "model.changed(AppModel::identical())"]
+ set_class_active: ("identical", model.identical),
gtk::Box {
set_orientation: gtk::Orientation::Horizontal,
set_spacing: 10,
@@ -312,11 +312,11 @@ The view
set_spacing: 10,
gtk::Image {
set_pixel_size: 50,
- #[track = "model.changed(AppModel::first_icon())"]
+ #[track = "model.changed(AppModel::first_icon())"]
set_icon_name: Some(model.first_icon),
},
gtk::Button {
- set_label: "New random image",
+ set_label: "New random image",
connect_clicked[sender] => move |_| {
sender.input(AppInput::UpdateFirst)
}
@@ -327,11 +327,11 @@ The view
set_spacing: 10,
gtk::Image {
set_pixel_size: 50,
- #[track = "model.changed(AppModel::second_icon())"]
+ #[track = "model.changed(AppModel::second_icon())"]
set_icon_name: Some(model.second_icon),
},
gtk::Button {
- set_label: "New random image",
+ set_label: "New random image",
connect_clicked[sender] => move |_| {
sender.input(AppInput::UpdateSecond)
}
@@ -340,32 +340,34 @@ The view
}
}
}
-The overall UI is pretty simple: A window that contains a box. This box itself has two boxes that display the two icons and the two buttons to update them.
-We also added some additional code in init
that runs before the view is constructed. In our case, we want to add custom CSS that sets the background color for elements with class name "identical".
- relm4::set_global_css(".identical { background: #00ad5c; }");
-
- // Insert the macro code generation here
- let widgets = view_output!();
+The main function
+In this example, we need some additional code in fn main()
to add custom CSS that sets the background color for elements with class name "identical".
+Later, we just need to assign the "identical" class name to a widget to make it match the CSS selector.
+fn main() {
+ let app = RelmApp::new("relm4.test.simple");
+ app.set_global_css(".identical { background: #00ad5c; }");
+ app.run::<AppModel>(());
+}
The #[track]
attribute
The #[track]
attribute is applied to method invocations in our view code. It allows us to add a condition to the update: if the condition is true, the method will be called, otherwise, it will be skipped. The attribute syntax looks like this:
-#[track = "<boolean expression>"]
+#[track = "<boolean expression>"]
Let's have a look at its first appearance:
- #[track = "model.changed(AppModel::identical())"]
- set_class_active: ("identical", model.identical),
+ #[track = "model.changed(AppModel::identical())"]
+ set_class_active: ("identical", model.identical),
The set_class_active
method is used to either activate or disable a CSS class. It takes two parameters, the first is the class itself and the second is a boolean which specifies if the class should be added (true
) or removed (false
).
The value of the #[track]
attribute is parsed as a boolean expression. This expression will be used as a condition to check whether something has changed. If this condition is true
, the set_class_active
method will be called with the parameters it guards.
The macro expansion for method calls annotated with the #[track]
attribute look roughly like this:
if model.changed(AppModel::identical()) {
- self.main_window.set_class_active("identical", model.identical);
+ self.main_window.set_class_active("identical", model.identical);
}
That's all. It's pretty simple, actually. We just use a condition that allows us to update our widgets only when needed.
The second #[track]
attribute works similarly:
- #[track = "model.changed(AppModel::first_icon())"]
+ #[track = "model.changed(AppModel::first_icon())"]
set_icon_name: Some(model.first_icon),
-Debugging Helper
+Using a tracker as debugging helper
Since the #[track]
attribute parses expressions, you can use the following syntax to debug your trackers:
-#[track = "{ println!("Update widget"); argument }"]
+#[track = "{ println!("Update widget"); argument }"]
Initializing the model
There's one last thing to point out. When initializing our model, we need to initialize the tracker
field as well. The initial value doesn't really matter because we call reset()
in the update function anyway, but usually 0
is used.
@@ -382,22 +384,22 @@ The compl
use relm4::{gtk, ComponentParts, ComponentSender, RelmApp, RelmWidgetExt, SimpleComponent};
const ICON_LIST: &[&str] = &[
- "bookmark-new-symbolic",
- "edit-copy-symbolic",
- "edit-cut-symbolic",
- "edit-find-symbolic",
- "starred-symbolic",
- "system-run-symbolic",
- "emoji-objects-symbolic",
- "emoji-nature-symbolic",
- "display-brightness-symbolic",
+ "bookmark-new-symbolic",
+ "edit-copy-symbolic",
+ "edit-cut-symbolic",
+ "edit-find-symbolic",
+ "starred-symbolic",
+ "system-run-symbolic",
+ "emoji-objects-symbolic",
+ "emoji-nature-symbolic",
+ "display-brightness-symbolic",
];
fn random_icon_name() -> &'static str {
ICON_LIST
.iter()
.choose(&mut rand::thread_rng())
- .expect("Could not choose a random icon")
+ .expect("Could not choose a random icon")
}
// The track proc macro allows to easily track changes to different
@@ -424,8 +426,8 @@ The compl
view! {
#[root]
gtk::ApplicationWindow {
- #[track = "model.changed(AppModel::identical())"]
- set_class_active: ("identical", model.identical),
+ #[track = "model.changed(AppModel::identical())"]
+ set_class_active: ("identical", model.identical),
gtk::Box {
set_orientation: gtk::Orientation::Horizontal,
set_spacing: 10,
@@ -435,11 +437,11 @@ The compl
set_spacing: 10,
gtk::Image {
set_pixel_size: 50,
- #[track = "model.changed(AppModel::first_icon())"]
+ #[track = "model.changed(AppModel::first_icon())"]
set_icon_name: Some(model.first_icon),
},
gtk::Button {
- set_label: "New random image",
+ set_label: "New random image",
connect_clicked[sender] => move |_| {
sender.input(AppInput::UpdateFirst)
}
@@ -450,11 +452,11 @@ The compl
set_spacing: 10,
gtk::Image {
set_pixel_size: 50,
- #[track = "model.changed(AppModel::second_icon())"]
+ #[track = "model.changed(AppModel::second_icon())"]
set_icon_name: Some(model.second_icon),
},
gtk::Button {
- set_label: "New random image",
+ set_label: "New random image",
connect_clicked[sender] => move |_| {
sender.input(AppInput::UpdateSecond)
}
@@ -467,7 +469,7 @@ The compl
// Initialize the UI.
fn init(
_params: Self::Init,
- root: &Self::Root,
+ root: Self::Root,
sender: ComponentSender<Self>,
) -> ComponentParts<Self> {
let model = AppModel {
@@ -477,8 +479,6 @@ The compl
tracker: 0,
};
- relm4::set_global_css(".identical { background: #00ad5c; }");
-
// Insert the macro code generation here
let widgets = view_output!();
@@ -502,7 +502,8 @@ The compl
}
fn main() {
- let app = RelmApp::new("relm4.test.simple");
+ let app = RelmApp::new("relm4.test.simple");
+ app.set_global_css(".identical { background: #00ad5c; }");
app.run::<AppModel>(());
}
diff --git a/book/next/first_app.html b/book/next/first_app.html
index fa11c2a7f9fc..44cf9eaf8071 100644
--- a/book/next/first_app.html
+++ b/book/next/first_app.html
@@ -194,7 +194,7 @@ Your first app<
Application architecture
Often, programming concepts are easier to understand when explained with examples or metaphors from the real world. To understand how Relm4 apps work, you can think about a computer as a person.
-Our job as a programmer is to ensure that the users of our app will be able to communicate with the computer through the UI. Since the computer can't understand our human language, it needs some help from us to get the communication going.
+Our job as a programmer is to ensure that the users of our app will be able to communicate with the computer through the UI. Since the computer can't understand our human language, it needs some help from us to get the communication going.
Let's have a look at what we need to get this done!
Messages
For our app, we just want to tell the computer to either increment or decrement a counter.
@@ -236,7 +236,7 @@ fn init_root() -> Self::Root {
gtk::Window::builder()
- .title("Simple app")
+ .title("Simple app")
.default_width(300)
.default_height(100)
.build()
@@ -249,7 +249,7 @@ /// Initialize the UI and model.
fn init(
counter: Self::Init,
- window: &Self::Root,
+ window: Self::Root,
sender: ComponentSender<Self>,
) -> relm4::ComponentParts<Self> {
let model = AppModel { counter };
@@ -259,10 +259,10 @@
Of course, the computer needs to do more than just remembering things, it also needs to process information. Here, both the model and message types come into play.
The update
function of the SimpleComponent
trait tells the computer how to process messages and how to update its memory.
fn update(&mut self, message: Self::Input, _sender: ComponentSender<Self>) {
@@ -307,12 +307,12 @@
Running the App
The last step is to run the app we just wrote. To do so, we just need to initialize our model and pass it into RelmApp::new()
.
fn main() {
- let app = RelmApp::new("relm4.test.simple_manual");
+ let app = RelmApp::new("relm4.test.simple_manual");
app.run::<AppModel>(0);
}
🎉 Congratulations! You just wrote your first app with Relm4! 🎉
@@ -376,7 +376,7 @@ The compl
fn init_root() -> Self::Root {
gtk::Window::builder()
- .title("Simple app")
+ .title("Simple app")
.default_width(300)
.default_height(100)
.build()
@@ -385,7 +385,7 @@ The compl
/// Initialize the UI and model.
fn init(
counter: Self::Init,
- window: &Self::Root,
+ window: Self::Root,
sender: ComponentSender<Self>,
) -> relm4::ComponentParts<Self> {
let model = AppModel { counter };
@@ -395,10 +395,10 @@ The compl
.spacing(5)
.build();
- let inc_button = gtk::Button::with_label("Increment");
- let dec_button = gtk::Button::with_label("Decrement");
+ let inc_button = gtk::Button::with_label("Increment");
+ let dec_button = gtk::Button::with_label("Decrement");
- let label = gtk::Label::new(Some(&format!("Counter: {}", model.counter)));
+ let label = gtk::Label::new(Some(&format!("Counter: {}", model.counter)));
label.set_margin_all(5);
window.set_child(Some(&vbox));
@@ -435,12 +435,12 @@ The compl
fn update_view(&self, widgets: &mut Self::Widgets, _sender: ComponentSender<Self>) {
widgets
.label
- .set_label(&format!("Counter: {}", self.counter));
+ .set_label(&format!("Counter: {}", self.counter));
}
}
fn main() {
- let app = RelmApp::new("relm4.test.simple_manual");
+ let app = RelmApp::new("relm4.test.simple_manual");
app.run::<AppModel>(0);
}
diff --git a/book/next/gtk_rs.html b/book/next/gtk_rs.html
index 657572a2fe98..3ccd4cbb845e 100644
--- a/book/next/gtk_rs.html
+++ b/book/next/gtk_rs.html
@@ -187,19 +187,19 @@ gtk-rs overvi
GObjects
GTK is an object-oriented framework that uses the GObject library to implement objects. GObjects have some really useful features that we will discuss in the following sections.
Subclassing
-Like many other OOP frameworks or languages, GObjects can inherit from other GObjects. This is called subclassing. In the case of GTK, that’s really helpful because it allows us to create custom widgets.
+Like many other OOP frameworks or languages, GObjects can inherit from other GObjects. This is called subclassing. In the case of GTK, that’s really helpful because it allows us to create custom widgets.
For example, you could use subclassing to create your own button widget that acts as a counter. Or you can create a custom application window that better suits your application.
Read more about subclassing in the gtk-rs book.
Properties
Each GObject can have properties that work similar to the fields of a structure in Rust. You can set them and you can read (get) them. But one thing that's particularly cool is that properties can be bound to other properties.
-For example, you could bind the "visible" property of a widget to the "active" property of a gtk::ToggleButton
. This would allow you to show or hide the widget using the toggle button and the best part is, that it's done fully automatically!
+For example, you could bind the "visible" property of a widget to the "active" property of a gtk::ToggleButton
. This would allow you to show or hide the widget using the toggle button and the best part is, that it's done fully automatically!
Read more about properties in the gtk-rs book.
Signals
-GObjects can not only have properties but also signals. Actually, we've been using signals all the time, for example, by using the connect_clicked
method on a button. This method simply adds an event handler function for the "click" signal.
+GObjects can not only have properties but also signals. Actually, we've been using signals all the time, for example, by using the connect_clicked
method on a button. This method simply adds an event handler function for the "click" signal.
You can create your own signals in custom widgets. You can also use emit to emit signals on you widgets manually.
Read more about signals in the gtk-rs book.
diff --git a/book/next/index.html b/book/next/index.html
index 82473678ea0a..b8d0c56ccdee 100644
--- a/book/next/index.html
+++ b/book/next/index.html
@@ -187,7 +187,7 @@
-Relm4 is an idiomatic GUI library inspired by Elm and based on gtk4-rs.
+
Relm4 is an idiomatic GUI library inspired by Elm and based on gtk4-rs.
It is a new version of relm that's built from scratch and is compatible with GTK4 and libadwaita.
Creat
#[name(to_front_button)]
gtk::Button {
- set_label: "To Start",
+ set_label: "To Start",
connect_clicked[sender, index] => move |_| {
sender.output(CounterOutput::SendFront(index.clone())).unwrap();
}
@@ -304,7 +304,7 @@ Creat
}
}
Initializing the model
-FactoryComponent
has separate functions for initializing the model and the widgets.
+
FactoryComponent
has separate functions for initializing the model and the widgets.
This means, that we are a bit less flexible, but don't need view_output!()
here.
Also, we just need to implement the init_model
function because init_widgets
is already implemented by the macro.
fn init_model(value: Self::Init, _index: &DynamicIndex, _sender: FactorySender<Self>) -> Self {
@@ -346,7 +346,7 @@ // Initialize the UI.
fn init(
counter: Self::Init,
- root: &Self::Root,
+ root: Self::Root,
sender: ComponentSender<Self>,
) -> ComponentParts<Self> {
let counters = FactoryVecDeque::builder()
@@ -373,7 +373,7 @@ view! {
gtk::Window {
- set_title: Some("Factory example"),
+ set_title: Some("Factory example"),
set_default_size: (300, 100),
gtk::Box {
@@ -382,12 +382,12 @@ The main
Awesome, we almost made it!
We only need to define the main function to run our application.
fn main() {
- let app = RelmApp::new("relm4.example.factory");
+ let app = RelmApp::new("relm4.example.factory");
app.run::<App>(0);
}
The complete code
@@ -492,19 +492,19 @@ The compl
#[name(add_button)]
gtk::Button {
- set_label: "+",
+ set_label: "+",
connect_clicked => CounterMsg::Increment,
},
#[name(remove_button)]
gtk::Button {
- set_label: "-",
+ set_label: "-",
connect_clicked => CounterMsg::Decrement,
},
#[name(move_up_button)]
gtk::Button {
- set_label: "Up",
+ set_label: "Up",
connect_clicked[sender, index] => move |_| {
sender.output(CounterOutput::MoveUp(index.clone())).unwrap();
}
@@ -512,7 +512,7 @@ The compl
#[name(move_down_button)]
gtk::Button {
- set_label: "Down",
+ set_label: "Down",
connect_clicked[sender, index] => move |_| {
sender.output(CounterOutput::MoveDown(index.clone())).unwrap();
}
@@ -520,7 +520,7 @@ The compl
#[name(to_front_button)]
gtk::Button {
- set_label: "To Start",
+ set_label: "To Start",
connect_clicked[sender, index] => move |_| {
sender.output(CounterOutput::SendFront(index.clone())).unwrap();
}
@@ -566,7 +566,7 @@ The compl
view! {
gtk::Window {
- set_title: Some("Factory example"),
+ set_title: Some("Factory example"),
set_default_size: (300, 100),
gtk::Box {
@@ -575,12 +575,12 @@ The compl
set_margin_all: 5,
gtk::Button {
- set_label: "Add counter",
+ set_label: "Add counter",
connect_clicked => AppMsg::AddCounter,
},
gtk::Button {
- set_label: "Remove counter",
+ set_label: "Remove counter",
connect_clicked => AppMsg::RemoveCounter,
},
@@ -596,7 +596,7 @@ The compl
// Initialize the UI.
fn init(
counter: Self::Init,
- root: &Self::Root,
+ root: Self::Root,
sender: ComponentSender<Self>,
) -> ComponentParts<Self> {
let counters = FactoryVecDeque::builder()
@@ -650,7 +650,7 @@ The compl
}
fn main() {
- let app = RelmApp::new("relm4.example.factory");
+ let app = RelmApp::new("relm4.example.factory");
app.run::<App>(0);
}
diff --git a/book/next/efficient_ui/tracker.html b/book/next/efficient_ui/tracker.html
index 6d4e5d42fb8b..4093b20ae048 100644
--- a/book/next/efficient_ui/tracker.html
+++ b/book/next/efficient_ui/tracker.html
@@ -210,7 +210,7 @@ The track
To reset all previous changes, you can call {struct_var_name}.reset()
.
Example
First we have to add the tracker library to Cargo.toml
:
-tracker = "0.1"
+tracker = "0.1"
Now let's have a look at a small example.
#[tracker::track]
@@ -224,7 +224,7 @@ Example
x: 0,
y: 0,
// the macro generates a new variable called
- // "tracker" which stores the changes
+ // "tracker" which stores the changes
tracker: 0,
};
@@ -248,22 +248,22 @@ The icons
Before we can select random icons, we need to quickly implement a function that will return us random image names available in the default GTK icon theme.
const ICON_LIST: &[&str] = &[
- "bookmark-new-symbolic",
- "edit-copy-symbolic",
- "edit-cut-symbolic",
- "edit-find-symbolic",
- "starred-symbolic",
- "system-run-symbolic",
- "emoji-objects-symbolic",
- "emoji-nature-symbolic",
- "display-brightness-symbolic",
+ "bookmark-new-symbolic",
+ "edit-copy-symbolic",
+ "edit-cut-symbolic",
+ "edit-find-symbolic",
+ "starred-symbolic",
+ "system-run-symbolic",
+ "emoji-objects-symbolic",
+ "emoji-nature-symbolic",
+ "display-brightness-symbolic",
];
fn random_icon_name() -> &'static str {
ICON_LIST
.iter()
.choose(&mut rand::thread_rng())
- .expect("Could not choose a random icon")
+ .expect("Could not choose a random icon")
}
The model
For our model we only need to store the two icon names and whether both of them are identical.
@@ -301,8 +301,8 @@ The view
view! {
#[root]
gtk::ApplicationWindow {
- #[track = "model.changed(AppModel::identical())"]
- set_class_active: ("identical", model.identical),
+ #[track = "model.changed(AppModel::identical())"]
+ set_class_active: ("identical", model.identical),
gtk::Box {
set_orientation: gtk::Orientation::Horizontal,
set_spacing: 10,
@@ -312,11 +312,11 @@ The view
set_spacing: 10,
gtk::Image {
set_pixel_size: 50,
- #[track = "model.changed(AppModel::first_icon())"]
+ #[track = "model.changed(AppModel::first_icon())"]
set_icon_name: Some(model.first_icon),
},
gtk::Button {
- set_label: "New random image",
+ set_label: "New random image",
connect_clicked[sender] => move |_| {
sender.input(AppInput::UpdateFirst)
}
@@ -327,11 +327,11 @@ The view
set_spacing: 10,
gtk::Image {
set_pixel_size: 50,
- #[track = "model.changed(AppModel::second_icon())"]
+ #[track = "model.changed(AppModel::second_icon())"]
set_icon_name: Some(model.second_icon),
},
gtk::Button {
- set_label: "New random image",
+ set_label: "New random image",
connect_clicked[sender] => move |_| {
sender.input(AppInput::UpdateSecond)
}
@@ -340,32 +340,34 @@ The view
}
}
}
-The overall UI is pretty simple: A window that contains a box. This box itself has two boxes that display the two icons and the two buttons to update them.
-We also added some additional code in init
that runs before the view is constructed. In our case, we want to add custom CSS that sets the background color for elements with class name "identical".
- relm4::set_global_css(".identical { background: #00ad5c; }");
-
- // Insert the macro code generation here
- let widgets = view_output!();
+The main function
+In this example, we need some additional code in fn main()
to add custom CSS that sets the background color for elements with class name "identical".
+Later, we just need to assign the "identical" class name to a widget to make it match the CSS selector.
+fn main() {
+ let app = RelmApp::new("relm4.test.simple");
+ app.set_global_css(".identical { background: #00ad5c; }");
+ app.run::<AppModel>(());
+}
The #[track]
attribute
The #[track]
attribute is applied to method invocations in our view code. It allows us to add a condition to the update: if the condition is true, the method will be called, otherwise, it will be skipped. The attribute syntax looks like this:
-#[track = "<boolean expression>"]
+#[track = "<boolean expression>"]
Let's have a look at its first appearance:
- #[track = "model.changed(AppModel::identical())"]
- set_class_active: ("identical", model.identical),
+ #[track = "model.changed(AppModel::identical())"]
+ set_class_active: ("identical", model.identical),
The set_class_active
method is used to either activate or disable a CSS class. It takes two parameters, the first is the class itself and the second is a boolean which specifies if the class should be added (true
) or removed (false
).
The value of the #[track]
attribute is parsed as a boolean expression. This expression will be used as a condition to check whether something has changed. If this condition is true
, the set_class_active
method will be called with the parameters it guards.
The macro expansion for method calls annotated with the #[track]
attribute look roughly like this:
if model.changed(AppModel::identical()) {
- self.main_window.set_class_active("identical", model.identical);
+ self.main_window.set_class_active("identical", model.identical);
}
That's all. It's pretty simple, actually. We just use a condition that allows us to update our widgets only when needed.
The second #[track]
attribute works similarly:
- #[track = "model.changed(AppModel::first_icon())"]
+ #[track = "model.changed(AppModel::first_icon())"]
set_icon_name: Some(model.first_icon),
-Debugging Helper
+Using a tracker as debugging helper
Since the #[track]
attribute parses expressions, you can use the following syntax to debug your trackers:
-#[track = "{ println!("Update widget"); argument }"]
+#[track = "{ println!("Update widget"); argument }"]
Initializing the model
There's one last thing to point out. When initializing our model, we need to initialize the tracker
field as well. The initial value doesn't really matter because we call reset()
in the update function anyway, but usually 0
is used.
@@ -382,22 +384,22 @@ The compl
use relm4::{gtk, ComponentParts, ComponentSender, RelmApp, RelmWidgetExt, SimpleComponent};
const ICON_LIST: &[&str] = &[
- "bookmark-new-symbolic",
- "edit-copy-symbolic",
- "edit-cut-symbolic",
- "edit-find-symbolic",
- "starred-symbolic",
- "system-run-symbolic",
- "emoji-objects-symbolic",
- "emoji-nature-symbolic",
- "display-brightness-symbolic",
+ "bookmark-new-symbolic",
+ "edit-copy-symbolic",
+ "edit-cut-symbolic",
+ "edit-find-symbolic",
+ "starred-symbolic",
+ "system-run-symbolic",
+ "emoji-objects-symbolic",
+ "emoji-nature-symbolic",
+ "display-brightness-symbolic",
];
fn random_icon_name() -> &'static str {
ICON_LIST
.iter()
.choose(&mut rand::thread_rng())
- .expect("Could not choose a random icon")
+ .expect("Could not choose a random icon")
}
// The track proc macro allows to easily track changes to different
@@ -424,8 +426,8 @@ The compl
view! {
#[root]
gtk::ApplicationWindow {
- #[track = "model.changed(AppModel::identical())"]
- set_class_active: ("identical", model.identical),
+ #[track = "model.changed(AppModel::identical())"]
+ set_class_active: ("identical", model.identical),
gtk::Box {
set_orientation: gtk::Orientation::Horizontal,
set_spacing: 10,
@@ -435,11 +437,11 @@ The compl
set_spacing: 10,
gtk::Image {
set_pixel_size: 50,
- #[track = "model.changed(AppModel::first_icon())"]
+ #[track = "model.changed(AppModel::first_icon())"]
set_icon_name: Some(model.first_icon),
},
gtk::Button {
- set_label: "New random image",
+ set_label: "New random image",
connect_clicked[sender] => move |_| {
sender.input(AppInput::UpdateFirst)
}
@@ -450,11 +452,11 @@ The compl
set_spacing: 10,
gtk::Image {
set_pixel_size: 50,
- #[track = "model.changed(AppModel::second_icon())"]
+ #[track = "model.changed(AppModel::second_icon())"]
set_icon_name: Some(model.second_icon),
},
gtk::Button {
- set_label: "New random image",
+ set_label: "New random image",
connect_clicked[sender] => move |_| {
sender.input(AppInput::UpdateSecond)
}
@@ -467,7 +469,7 @@ The compl
// Initialize the UI.
fn init(
_params: Self::Init,
- root: &Self::Root,
+ root: Self::Root,
sender: ComponentSender<Self>,
) -> ComponentParts<Self> {
let model = AppModel {
@@ -477,8 +479,6 @@ The compl
tracker: 0,
};
- relm4::set_global_css(".identical { background: #00ad5c; }");
-
// Insert the macro code generation here
let widgets = view_output!();
@@ -502,7 +502,8 @@ The compl
}
fn main() {
- let app = RelmApp::new("relm4.test.simple");
+ let app = RelmApp::new("relm4.test.simple");
+ app.set_global_css(".identical { background: #00ad5c; }");
app.run::<AppModel>(());
}
diff --git a/book/next/first_app.html b/book/next/first_app.html
index fa11c2a7f9fc..44cf9eaf8071 100644
--- a/book/next/first_app.html
+++ b/book/next/first_app.html
@@ -194,7 +194,7 @@ Your first app<
Application architecture
Often, programming concepts are easier to understand when explained with examples or metaphors from the real world. To understand how Relm4 apps work, you can think about a computer as a person.
-Our job as a programmer is to ensure that the users of our app will be able to communicate with the computer through the UI. Since the computer can't understand our human language, it needs some help from us to get the communication going.
+Our job as a programmer is to ensure that the users of our app will be able to communicate with the computer through the UI. Since the computer can't understand our human language, it needs some help from us to get the communication going.
Let's have a look at what we need to get this done!
Messages
For our app, we just want to tell the computer to either increment or decrement a counter.
@@ -236,7 +236,7 @@ fn init_root() -> Self::Root {
gtk::Window::builder()
- .title("Simple app")
+ .title("Simple app")
.default_width(300)
.default_height(100)
.build()
@@ -249,7 +249,7 @@ /// Initialize the UI and model.
fn init(
counter: Self::Init,
- window: &Self::Root,
+ window: Self::Root,
sender: ComponentSender<Self>,
) -> relm4::ComponentParts<Self> {
let model = AppModel { counter };
@@ -259,10 +259,10 @@
Of course, the computer needs to do more than just remembering things, it also needs to process information. Here, both the model and message types come into play.
The update
function of the SimpleComponent
trait tells the computer how to process messages and how to update its memory.
fn update(&mut self, message: Self::Input, _sender: ComponentSender<Self>) {
@@ -307,12 +307,12 @@
Running the App
The last step is to run the app we just wrote. To do so, we just need to initialize our model and pass it into RelmApp::new()
.
fn main() {
- let app = RelmApp::new("relm4.test.simple_manual");
+ let app = RelmApp::new("relm4.test.simple_manual");
app.run::<AppModel>(0);
}
🎉 Congratulations! You just wrote your first app with Relm4! 🎉
@@ -376,7 +376,7 @@ The compl
fn init_root() -> Self::Root {
gtk::Window::builder()
- .title("Simple app")
+ .title("Simple app")
.default_width(300)
.default_height(100)
.build()
@@ -385,7 +385,7 @@ The compl
/// Initialize the UI and model.
fn init(
counter: Self::Init,
- window: &Self::Root,
+ window: Self::Root,
sender: ComponentSender<Self>,
) -> relm4::ComponentParts<Self> {
let model = AppModel { counter };
@@ -395,10 +395,10 @@ The compl
.spacing(5)
.build();
- let inc_button = gtk::Button::with_label("Increment");
- let dec_button = gtk::Button::with_label("Decrement");
+ let inc_button = gtk::Button::with_label("Increment");
+ let dec_button = gtk::Button::with_label("Decrement");
- let label = gtk::Label::new(Some(&format!("Counter: {}", model.counter)));
+ let label = gtk::Label::new(Some(&format!("Counter: {}", model.counter)));
label.set_margin_all(5);
window.set_child(Some(&vbox));
@@ -435,12 +435,12 @@ The compl
fn update_view(&self, widgets: &mut Self::Widgets, _sender: ComponentSender<Self>) {
widgets
.label
- .set_label(&format!("Counter: {}", self.counter));
+ .set_label(&format!("Counter: {}", self.counter));
}
}
fn main() {
- let app = RelmApp::new("relm4.test.simple_manual");
+ let app = RelmApp::new("relm4.test.simple_manual");
app.run::<AppModel>(0);
}
diff --git a/book/next/gtk_rs.html b/book/next/gtk_rs.html
index 657572a2fe98..3ccd4cbb845e 100644
--- a/book/next/gtk_rs.html
+++ b/book/next/gtk_rs.html
@@ -187,19 +187,19 @@ gtk-rs overvi
GObjects
GTK is an object-oriented framework that uses the GObject library to implement objects. GObjects have some really useful features that we will discuss in the following sections.
Subclassing
-Like many other OOP frameworks or languages, GObjects can inherit from other GObjects. This is called subclassing. In the case of GTK, that’s really helpful because it allows us to create custom widgets.
+Like many other OOP frameworks or languages, GObjects can inherit from other GObjects. This is called subclassing. In the case of GTK, that’s really helpful because it allows us to create custom widgets.
For example, you could use subclassing to create your own button widget that acts as a counter. Or you can create a custom application window that better suits your application.
Read more about subclassing in the gtk-rs book.
Properties
Each GObject can have properties that work similar to the fields of a structure in Rust. You can set them and you can read (get) them. But one thing that's particularly cool is that properties can be bound to other properties.
-For example, you could bind the "visible" property of a widget to the "active" property of a gtk::ToggleButton
. This would allow you to show or hide the widget using the toggle button and the best part is, that it's done fully automatically!
+For example, you could bind the "visible" property of a widget to the "active" property of a gtk::ToggleButton
. This would allow you to show or hide the widget using the toggle button and the best part is, that it's done fully automatically!
Read more about properties in the gtk-rs book.
Signals
-GObjects can not only have properties but also signals. Actually, we've been using signals all the time, for example, by using the connect_clicked
method on a button. This method simply adds an event handler function for the "click" signal.
+GObjects can not only have properties but also signals. Actually, we've been using signals all the time, for example, by using the connect_clicked
method on a button. This method simply adds an event handler function for the "click" signal.
You can create your own signals in custom widgets. You can also use emit to emit signals on you widgets manually.
Read more about signals in the gtk-rs book.
diff --git a/book/next/index.html b/book/next/index.html
index 82473678ea0a..b8d0c56ccdee 100644
--- a/book/next/index.html
+++ b/book/next/index.html
@@ -187,7 +187,7 @@
-Relm4 is an idiomatic GUI library inspired by Elm and based on gtk4-rs.
+
Relm4 is an idiomatic GUI library inspired by Elm and based on gtk4-rs.
It is a new version of relm that's built from scratch and is compatible with GTK4 and libadwaita.
Initializing the model
-FactoryComponent
has separate functions for initializing the model and the widgets.
+
FactoryComponent
has separate functions for initializing the model and the widgets.
This means, that we are a bit less flexible, but don't need view_output!()
here.
Also, we just need to implement the init_model
function because init_widgets
is already implemented by the macro.
fn init_model(value: Self::Init, _index: &DynamicIndex, _sender: FactorySender<Self>) -> Self {
@@ -346,7 +346,7 @@ // Initialize the UI.
fn init(
counter: Self::Init,
- root: &Self::Root,
+ root: Self::Root,
sender: ComponentSender<Self>,
) -> ComponentParts<Self> {
let counters = FactoryVecDeque::builder()
@@ -373,7 +373,7 @@ view! {
gtk::Window {
- set_title: Some("Factory example"),
+ set_title: Some("Factory example"),
set_default_size: (300, 100),
gtk::Box {
@@ -382,12 +382,12 @@ The main
Awesome, we almost made it!
We only need to define the main function to run our application.
fn main() {
- let app = RelmApp::new("relm4.example.factory");
+ let app = RelmApp::new("relm4.example.factory");
app.run::<App>(0);
}
The complete code
@@ -492,19 +492,19 @@ The compl
#[name(add_button)]
gtk::Button {
- set_label: "+",
+ set_label: "+",
connect_clicked => CounterMsg::Increment,
},
#[name(remove_button)]
gtk::Button {
- set_label: "-",
+ set_label: "-",
connect_clicked => CounterMsg::Decrement,
},
#[name(move_up_button)]
gtk::Button {
- set_label: "Up",
+ set_label: "Up",
connect_clicked[sender, index] => move |_| {
sender.output(CounterOutput::MoveUp(index.clone())).unwrap();
}
@@ -512,7 +512,7 @@ The compl
#[name(move_down_button)]
gtk::Button {
- set_label: "Down",
+ set_label: "Down",
connect_clicked[sender, index] => move |_| {
sender.output(CounterOutput::MoveDown(index.clone())).unwrap();
}
@@ -520,7 +520,7 @@ The compl
#[name(to_front_button)]
gtk::Button {
- set_label: "To Start",
+ set_label: "To Start",
connect_clicked[sender, index] => move |_| {
sender.output(CounterOutput::SendFront(index.clone())).unwrap();
}
@@ -566,7 +566,7 @@ The compl
view! {
gtk::Window {
- set_title: Some("Factory example"),
+ set_title: Some("Factory example"),
set_default_size: (300, 100),
gtk::Box {
@@ -575,12 +575,12 @@ The compl
set_margin_all: 5,
gtk::Button {
- set_label: "Add counter",
+ set_label: "Add counter",
connect_clicked => AppMsg::AddCounter,
},
gtk::Button {
- set_label: "Remove counter",
+ set_label: "Remove counter",
connect_clicked => AppMsg::RemoveCounter,
},
@@ -596,7 +596,7 @@ The compl
// Initialize the UI.
fn init(
counter: Self::Init,
- root: &Self::Root,
+ root: Self::Root,
sender: ComponentSender<Self>,
) -> ComponentParts<Self> {
let counters = FactoryVecDeque::builder()
@@ -650,7 +650,7 @@ The compl
}
fn main() {
- let app = RelmApp::new("relm4.example.factory");
+ let app = RelmApp::new("relm4.example.factory");
app.run::<App>(0);
}
diff --git a/book/next/efficient_ui/tracker.html b/book/next/efficient_ui/tracker.html
index 6d4e5d42fb8b..4093b20ae048 100644
--- a/book/next/efficient_ui/tracker.html
+++ b/book/next/efficient_ui/tracker.html
@@ -210,7 +210,7 @@ The track
To reset all previous changes, you can call {struct_var_name}.reset()
.
Example
First we have to add the tracker library to Cargo.toml
:
-tracker = "0.1"
+tracker = "0.1"
Now let's have a look at a small example.
#[tracker::track]
@@ -224,7 +224,7 @@ Example
x: 0,
y: 0,
// the macro generates a new variable called
- // "tracker" which stores the changes
+ // "tracker" which stores the changes
tracker: 0,
};
@@ -248,22 +248,22 @@ The icons
{struct_var_name}.reset()
.Cargo.toml
:tracker = "0.1"
+tracker = "0.1"
Now let's have a look at a small example.
#[tracker::track]
@@ -224,7 +224,7 @@ Example
x: 0,
y: 0,
// the macro generates a new variable called
- // "tracker" which stores the changes
+ // "tracker" which stores the changes
tracker: 0,
};
@@ -248,22 +248,22 @@ The icons
Before we can select random icons, we need to quickly implement a function that will return us random image names available in the default GTK icon theme.
const ICON_LIST: &[&str] = &[
- "bookmark-new-symbolic",
- "edit-copy-symbolic",
- "edit-cut-symbolic",
- "edit-find-symbolic",
- "starred-symbolic",
- "system-run-symbolic",
- "emoji-objects-symbolic",
- "emoji-nature-symbolic",
- "display-brightness-symbolic",
+ "bookmark-new-symbolic",
+ "edit-copy-symbolic",
+ "edit-cut-symbolic",
+ "edit-find-symbolic",
+ "starred-symbolic",
+ "system-run-symbolic",
+ "emoji-objects-symbolic",
+ "emoji-nature-symbolic",
+ "display-brightness-symbolic",
];
fn random_icon_name() -> &'static str {
ICON_LIST
.iter()
.choose(&mut rand::thread_rng())
- .expect("Could not choose a random icon")
+ .expect("Could not choose a random icon")
}
The model
For our model we only need to store the two icon names and whether both of them are identical.
@@ -301,8 +301,8 @@The view
view! {
#[root]
gtk::ApplicationWindow {
- #[track = "model.changed(AppModel::identical())"]
- set_class_active: ("identical", model.identical),
+ #[track = "model.changed(AppModel::identical())"]
+ set_class_active: ("identical", model.identical),
gtk::Box {
set_orientation: gtk::Orientation::Horizontal,
set_spacing: 10,
@@ -312,11 +312,11 @@ The view
set_spacing: 10,
gtk::Image {
set_pixel_size: 50,
- #[track = "model.changed(AppModel::first_icon())"]
+ #[track = "model.changed(AppModel::first_icon())"]
set_icon_name: Some(model.first_icon),
},
gtk::Button {
- set_label: "New random image",
+ set_label: "New random image",
connect_clicked[sender] => move |_| {
sender.input(AppInput::UpdateFirst)
}
@@ -327,11 +327,11 @@ The view
set_spacing: 10,
gtk::Image {
set_pixel_size: 50,
- #[track = "model.changed(AppModel::second_icon())"]
+ #[track = "model.changed(AppModel::second_icon())"]
set_icon_name: Some(model.second_icon),
},
gtk::Button {
- set_label: "New random image",
+ set_label: "New random image",
connect_clicked[sender] => move |_| {
sender.input(AppInput::UpdateSecond)
}
@@ -340,32 +340,34 @@ The view
}
}
}
-The overall UI is pretty simple: A window that contains a box. This box itself has two boxes that display the two icons and the two buttons to update them.
-We also added some additional code in init
that runs before the view is constructed. In our case, we want to add custom CSS that sets the background color for elements with class name "identical".
relm4::set_global_css(".identical { background: #00ad5c; }");
-
- // Insert the macro code generation here
- let widgets = view_output!();
+The main function
+In this example, we need some additional code in fn main()
to add custom CSS that sets the background color for elements with class name "identical".
+Later, we just need to assign the "identical" class name to a widget to make it match the CSS selector.
fn main() {
+ let app = RelmApp::new("relm4.test.simple");
+ app.set_global_css(".identical { background: #00ad5c; }");
+ app.run::<AppModel>(());
+}
The #[track]
attribute
The #[track]
attribute is applied to method invocations in our view code. It allows us to add a condition to the update: if the condition is true, the method will be called, otherwise, it will be skipped. The attribute syntax looks like this:
#[track = "<boolean expression>"]
+#[track = "<boolean expression>"]
Let's have a look at its first appearance:
- #[track = "model.changed(AppModel::identical())"]
- set_class_active: ("identical", model.identical),
+ #[track = "model.changed(AppModel::identical())"]
+ set_class_active: ("identical", model.identical),
The set_class_active
method is used to either activate or disable a CSS class. It takes two parameters, the first is the class itself and the second is a boolean which specifies if the class should be added (true
) or removed (false
).
The value of the #[track]
attribute is parsed as a boolean expression. This expression will be used as a condition to check whether something has changed. If this condition is true
, the set_class_active
method will be called with the parameters it guards.
The macro expansion for method calls annotated with the #[track]
attribute look roughly like this:
if model.changed(AppModel::identical()) {
- self.main_window.set_class_active("identical", model.identical);
+ self.main_window.set_class_active("identical", model.identical);
}
That's all. It's pretty simple, actually. We just use a condition that allows us to update our widgets only when needed.
The second #[track]
attribute works similarly:
#[track = "model.changed(AppModel::first_icon())"]
+ #[track = "model.changed(AppModel::first_icon())"]
set_icon_name: Some(model.first_icon),
-Debugging Helper
+Using a tracker as debugging helper
Since the #[track]
attribute parses expressions, you can use the following syntax to debug your trackers:
-#[track = "{ println!("Update widget"); argument }"]
+#[track = "{ println!("Update widget"); argument }"]
Initializing the model
There's one last thing to point out. When initializing our model, we need to initialize the tracker
field as well. The initial value doesn't really matter because we call reset()
in the update function anyway, but usually 0
is used.
@@ -382,22 +384,22 @@ The compl
use relm4::{gtk, ComponentParts, ComponentSender, RelmApp, RelmWidgetExt, SimpleComponent};
const ICON_LIST: &[&str] = &[
- "bookmark-new-symbolic",
- "edit-copy-symbolic",
- "edit-cut-symbolic",
- "edit-find-symbolic",
- "starred-symbolic",
- "system-run-symbolic",
- "emoji-objects-symbolic",
- "emoji-nature-symbolic",
- "display-brightness-symbolic",
+ "bookmark-new-symbolic",
+ "edit-copy-symbolic",
+ "edit-cut-symbolic",
+ "edit-find-symbolic",
+ "starred-symbolic",
+ "system-run-symbolic",
+ "emoji-objects-symbolic",
+ "emoji-nature-symbolic",
+ "display-brightness-symbolic",
];
fn random_icon_name() -> &'static str {
ICON_LIST
.iter()
.choose(&mut rand::thread_rng())
- .expect("Could not choose a random icon")
+ .expect("Could not choose a random icon")
}
// The track proc macro allows to easily track changes to different
@@ -424,8 +426,8 @@ The compl
view! {
#[root]
gtk::ApplicationWindow {
- #[track = "model.changed(AppModel::identical())"]
- set_class_active: ("identical", model.identical),
+ #[track = "model.changed(AppModel::identical())"]
+ set_class_active: ("identical", model.identical),
gtk::Box {
set_orientation: gtk::Orientation::Horizontal,
set_spacing: 10,
@@ -435,11 +437,11 @@ The compl
set_spacing: 10,
gtk::Image {
set_pixel_size: 50,
- #[track = "model.changed(AppModel::first_icon())"]
+ #[track = "model.changed(AppModel::first_icon())"]
set_icon_name: Some(model.first_icon),
},
gtk::Button {
- set_label: "New random image",
+ set_label: "New random image",
connect_clicked[sender] => move |_| {
sender.input(AppInput::UpdateFirst)
}
@@ -450,11 +452,11 @@ The compl
set_spacing: 10,
gtk::Image {
set_pixel_size: 50,
- #[track = "model.changed(AppModel::second_icon())"]
+ #[track = "model.changed(AppModel::second_icon())"]
set_icon_name: Some(model.second_icon),
},
gtk::Button {
- set_label: "New random image",
+ set_label: "New random image",
connect_clicked[sender] => move |_| {
sender.input(AppInput::UpdateSecond)
}
@@ -467,7 +469,7 @@ The compl
// Initialize the UI.
fn init(
_params: Self::Init,
- root: &Self::Root,
+ root: Self::Root,
sender: ComponentSender<Self>,
) -> ComponentParts<Self> {
let model = AppModel {
@@ -477,8 +479,6 @@ The compl
tracker: 0,
};
- relm4::set_global_css(".identical { background: #00ad5c; }");
-
// Insert the macro code generation here
let widgets = view_output!();
@@ -502,7 +502,8 @@ The compl
}
fn main() {
- let app = RelmApp::new("relm4.test.simple");
+ let app = RelmApp::new("relm4.test.simple");
+ app.set_global_css(".identical { background: #00ad5c; }");
app.run::<AppModel>(());
}
diff --git a/book/next/first_app.html b/book/next/first_app.html
index fa11c2a7f9fc..44cf9eaf8071 100644
--- a/book/next/first_app.html
+++ b/book/next/first_app.html
@@ -194,7 +194,7 @@ Your first app<
Application architecture
Often, programming concepts are easier to understand when explained with examples or metaphors from the real world. To understand how Relm4 apps work, you can think about a computer as a person.
-Our job as a programmer is to ensure that the users of our app will be able to communicate with the computer through the UI. Since the computer can't understand our human language, it needs some help from us to get the communication going.
+Our job as a programmer is to ensure that the users of our app will be able to communicate with the computer through the UI. Since the computer can't understand our human language, it needs some help from us to get the communication going.
Let's have a look at what we need to get this done!
Messages
For our app, we just want to tell the computer to either increment or decrement a counter.
@@ -236,7 +236,7 @@ fn init_root() -> Self::Root {
gtk::Window::builder()
- .title("Simple app")
+ .title("Simple app")
.default_width(300)
.default_height(100)
.build()
@@ -249,7 +249,7 @@ /// Initialize the UI and model.
fn init(
counter: Self::Init,
- window: &Self::Root,
+ window: Self::Root,
sender: ComponentSender<Self>,
) -> relm4::ComponentParts<Self> {
let model = AppModel { counter };
@@ -259,10 +259,10 @@
Of course, the computer needs to do more than just remembering things, it also needs to process information. Here, both the model and message types come into play.
The update
function of the SimpleComponent
trait tells the computer how to process messages and how to update its memory.
fn update(&mut self, message: Self::Input, _sender: ComponentSender<Self>) {
@@ -307,12 +307,12 @@
Running the App
The last step is to run the app we just wrote. To do so, we just need to initialize our model and pass it into RelmApp::new()
.
fn main() {
- let app = RelmApp::new("relm4.test.simple_manual");
+ let app = RelmApp::new("relm4.test.simple_manual");
app.run::<AppModel>(0);
}
🎉 Congratulations! You just wrote your first app with Relm4! 🎉
@@ -376,7 +376,7 @@ The compl
fn init_root() -> Self::Root {
gtk::Window::builder()
- .title("Simple app")
+ .title("Simple app")
.default_width(300)
.default_height(100)
.build()
@@ -385,7 +385,7 @@ The compl
/// Initialize the UI and model.
fn init(
counter: Self::Init,
- window: &Self::Root,
+ window: Self::Root,
sender: ComponentSender<Self>,
) -> relm4::ComponentParts<Self> {
let model = AppModel { counter };
@@ -395,10 +395,10 @@ The compl
.spacing(5)
.build();
- let inc_button = gtk::Button::with_label("Increment");
- let dec_button = gtk::Button::with_label("Decrement");
+ let inc_button = gtk::Button::with_label("Increment");
+ let dec_button = gtk::Button::with_label("Decrement");
- let label = gtk::Label::new(Some(&format!("Counter: {}", model.counter)));
+ let label = gtk::Label::new(Some(&format!("Counter: {}", model.counter)));
label.set_margin_all(5);
window.set_child(Some(&vbox));
@@ -435,12 +435,12 @@ The compl
fn update_view(&self, widgets: &mut Self::Widgets, _sender: ComponentSender<Self>) {
widgets
.label
- .set_label(&format!("Counter: {}", self.counter));
+ .set_label(&format!("Counter: {}", self.counter));
}
}
fn main() {
- let app = RelmApp::new("relm4.test.simple_manual");
+ let app = RelmApp::new("relm4.test.simple_manual");
app.run::<AppModel>(0);
}
diff --git a/book/next/gtk_rs.html b/book/next/gtk_rs.html
index 657572a2fe98..3ccd4cbb845e 100644
--- a/book/next/gtk_rs.html
+++ b/book/next/gtk_rs.html
@@ -187,19 +187,19 @@ gtk-rs overvi
GObjects
GTK is an object-oriented framework that uses the GObject library to implement objects. GObjects have some really useful features that we will discuss in the following sections.
Subclassing
-Like many other OOP frameworks or languages, GObjects can inherit from other GObjects. This is called subclassing. In the case of GTK, that’s really helpful because it allows us to create custom widgets.
+Like many other OOP frameworks or languages, GObjects can inherit from other GObjects. This is called subclassing. In the case of GTK, that’s really helpful because it allows us to create custom widgets.
For example, you could use subclassing to create your own button widget that acts as a counter. Or you can create a custom application window that better suits your application.
Read more about subclassing in the gtk-rs book.
Properties
Each GObject can have properties that work similar to the fields of a structure in Rust. You can set them and you can read (get) them. But one thing that's particularly cool is that properties can be bound to other properties.
-For example, you could bind the "visible" property of a widget to the "active" property of a gtk::ToggleButton
. This would allow you to show or hide the widget using the toggle button and the best part is, that it's done fully automatically!
+For example, you could bind the "visible" property of a widget to the "active" property of a gtk::ToggleButton
. This would allow you to show or hide the widget using the toggle button and the best part is, that it's done fully automatically!
Read more about properties in the gtk-rs book.
Signals
-GObjects can not only have properties but also signals. Actually, we've been using signals all the time, for example, by using the connect_clicked
method on a button. This method simply adds an event handler function for the "click" signal.
+GObjects can not only have properties but also signals. Actually, we've been using signals all the time, for example, by using the connect_clicked
method on a button. This method simply adds an event handler function for the "click" signal.
You can create your own signals in custom widgets. You can also use emit to emit signals on you widgets manually.
Read more about signals in the gtk-rs book.
diff --git a/book/next/index.html b/book/next/index.html
index 82473678ea0a..b8d0c56ccdee 100644
--- a/book/next/index.html
+++ b/book/next/index.html
@@ -187,7 +187,7 @@
-Relm4 is an idiomatic GUI library inspired by Elm and based on gtk4-rs.
+
Relm4 is an idiomatic GUI library inspired by Elm and based on gtk4-rs.
It is a new version of relm that's built from scratch and is compatible with GTK4 and libadwaita.
/// Initialize the UI and model.
fn init(
counter: Self::Init,
- window: &Self::Root,
+ window: Self::Root,
sender: ComponentSender<Self>,
) -> relm4::ComponentParts<Self> {
let model = AppModel { counter };
@@ -259,10 +259,10 @@
Of course, the computer needs to do more than just remembering things, it also needs to process information. Here, both the model and message types come into play.
The update
function of the SimpleComponent
trait tells the computer how to process messages and how to update its memory.
fn update(&mut self, message: Self::Input, _sender: ComponentSender<Self>) {
@@ -307,12 +307,12 @@
Running the App
The last step is to run the app we just wrote. To do so, we just need to initialize our model and pass it into RelmApp::new()
.
fn main() {
- let app = RelmApp::new("relm4.test.simple_manual");
+ let app = RelmApp::new("relm4.test.simple_manual");
app.run::<AppModel>(0);
}
🎉 Congratulations! You just wrote your first app with Relm4! 🎉
@@ -376,7 +376,7 @@ The compl
fn init_root() -> Self::Root {
gtk::Window::builder()
- .title("Simple app")
+ .title("Simple app")
.default_width(300)
.default_height(100)
.build()
@@ -385,7 +385,7 @@ The compl
/// Initialize the UI and model.
fn init(
counter: Self::Init,
- window: &Self::Root,
+ window: Self::Root,
sender: ComponentSender<Self>,
) -> relm4::ComponentParts<Self> {
let model = AppModel { counter };
@@ -395,10 +395,10 @@ The compl
.spacing(5)
.build();
- let inc_button = gtk::Button::with_label("Increment");
- let dec_button = gtk::Button::with_label("Decrement");
+ let inc_button = gtk::Button::with_label("Increment");
+ let dec_button = gtk::Button::with_label("Decrement");
- let label = gtk::Label::new(Some(&format!("Counter: {}", model.counter)));
+ let label = gtk::Label::new(Some(&format!("Counter: {}", model.counter)));
label.set_margin_all(5);
window.set_child(Some(&vbox));
@@ -435,12 +435,12 @@ The compl
fn update_view(&self, widgets: &mut Self::Widgets, _sender: ComponentSender<Self>) {
widgets
.label
- .set_label(&format!("Counter: {}", self.counter));
+ .set_label(&format!("Counter: {}", self.counter));
}
}
fn main() {
- let app = RelmApp::new("relm4.test.simple_manual");
+ let app = RelmApp::new("relm4.test.simple_manual");
app.run::<AppModel>(0);
}
diff --git a/book/next/gtk_rs.html b/book/next/gtk_rs.html
index 657572a2fe98..3ccd4cbb845e 100644
--- a/book/next/gtk_rs.html
+++ b/book/next/gtk_rs.html
@@ -187,19 +187,19 @@ gtk-rs overvi
GObjects
GTK is an object-oriented framework that uses the GObject library to implement objects. GObjects have some really useful features that we will discuss in the following sections.
Subclassing
-Like many other OOP frameworks or languages, GObjects can inherit from other GObjects. This is called subclassing. In the case of GTK, that’s really helpful because it allows us to create custom widgets.
+Like many other OOP frameworks or languages, GObjects can inherit from other GObjects. This is called subclassing. In the case of GTK, that’s really helpful because it allows us to create custom widgets.
For example, you could use subclassing to create your own button widget that acts as a counter. Or you can create a custom application window that better suits your application.
Read more about subclassing in the gtk-rs book.
Properties
Each GObject can have properties that work similar to the fields of a structure in Rust. You can set them and you can read (get) them. But one thing that's particularly cool is that properties can be bound to other properties.
-For example, you could bind the "visible" property of a widget to the "active" property of a gtk::ToggleButton
. This would allow you to show or hide the widget using the toggle button and the best part is, that it's done fully automatically!
+For example, you could bind the "visible" property of a widget to the "active" property of a gtk::ToggleButton
. This would allow you to show or hide the widget using the toggle button and the best part is, that it's done fully automatically!
Read more about properties in the gtk-rs book.
Signals
-GObjects can not only have properties but also signals. Actually, we've been using signals all the time, for example, by using the connect_clicked
method on a button. This method simply adds an event handler function for the "click" signal.
+GObjects can not only have properties but also signals. Actually, we've been using signals all the time, for example, by using the connect_clicked
method on a button. This method simply adds an event handler function for the "click" signal.
You can create your own signals in custom widgets. You can also use emit to emit signals on you widgets manually.
Read more about signals in the gtk-rs book.
diff --git a/book/next/index.html b/book/next/index.html
index 82473678ea0a..b8d0c56ccdee 100644
--- a/book/next/index.html
+++ b/book/next/index.html
@@ -187,7 +187,7 @@
-Relm4 is an idiomatic GUI library inspired by Elm and based on gtk4-rs.
+
Relm4 is an idiomatic GUI library inspired by Elm and based on gtk4-rs.
It is a new version of relm that's built from scratch and is compatible with GTK4 and libadwaita.
Of course, the computer needs to do more than just remembering things, it also needs to process information. Here, both the model and message types come into play.
The update
function of the SimpleComponent
trait tells the computer how to process messages and how to update its memory.
fn update(&mut self, message: Self::Input, _sender: ComponentSender<Self>) {
@@ -307,12 +307,12 @@
Running the App
The last step is to run the app we just wrote. To do so, we just need to initialize our model and pass it into RelmApp::new()
.
fn main() {
- let app = RelmApp::new("relm4.test.simple_manual");
+ let app = RelmApp::new("relm4.test.simple_manual");
app.run::<AppModel>(0);
}
🎉 Congratulations! You just wrote your first app with Relm4! 🎉
@@ -376,7 +376,7 @@The compl
fn init_root() -> Self::Root {
gtk::Window::builder()
- .title("Simple app")
+ .title("Simple app")
.default_width(300)
.default_height(100)
.build()
@@ -385,7 +385,7 @@ The compl
/// Initialize the UI and model.
fn init(
counter: Self::Init,
- window: &Self::Root,
+ window: Self::Root,
sender: ComponentSender<Self>,
) -> relm4::ComponentParts<Self> {
let model = AppModel { counter };
@@ -395,10 +395,10 @@ The compl
.spacing(5)
.build();
- let inc_button = gtk::Button::with_label("Increment");
- let dec_button = gtk::Button::with_label("Decrement");
+ let inc_button = gtk::Button::with_label("Increment");
+ let dec_button = gtk::Button::with_label("Decrement");
- let label = gtk::Label::new(Some(&format!("Counter: {}", model.counter)));
+ let label = gtk::Label::new(Some(&format!("Counter: {}", model.counter)));
label.set_margin_all(5);
window.set_child(Some(&vbox));
@@ -435,12 +435,12 @@ The compl
fn update_view(&self, widgets: &mut Self::Widgets, _sender: ComponentSender<Self>) {
widgets
.label
- .set_label(&format!("Counter: {}", self.counter));
+ .set_label(&format!("Counter: {}", self.counter));
}
}
fn main() {
- let app = RelmApp::new("relm4.test.simple_manual");
+ let app = RelmApp::new("relm4.test.simple_manual");
app.run::<AppModel>(0);
}
diff --git a/book/next/gtk_rs.html b/book/next/gtk_rs.html
index 657572a2fe98..3ccd4cbb845e 100644
--- a/book/next/gtk_rs.html
+++ b/book/next/gtk_rs.html
@@ -187,19 +187,19 @@ gtk-rs overvi
GObjects
GTK is an object-oriented framework that uses the GObject library to implement objects. GObjects have some really useful features that we will discuss in the following sections.
Subclassing
-Like many other OOP frameworks or languages, GObjects can inherit from other GObjects. This is called subclassing. In the case of GTK, that’s really helpful because it allows us to create custom widgets.
+Like many other OOP frameworks or languages, GObjects can inherit from other GObjects. This is called subclassing. In the case of GTK, that’s really helpful because it allows us to create custom widgets.
For example, you could use subclassing to create your own button widget that acts as a counter. Or you can create a custom application window that better suits your application.
Read more about subclassing in the gtk-rs book.
Properties
Each GObject can have properties that work similar to the fields of a structure in Rust. You can set them and you can read (get) them. But one thing that's particularly cool is that properties can be bound to other properties.
-For example, you could bind the "visible" property of a widget to the "active" property of a gtk::ToggleButton
. This would allow you to show or hide the widget using the toggle button and the best part is, that it's done fully automatically!
+For example, you could bind the "visible" property of a widget to the "active" property of a gtk::ToggleButton
. This would allow you to show or hide the widget using the toggle button and the best part is, that it's done fully automatically!
Read more about properties in the gtk-rs book.
Signals
-GObjects can not only have properties but also signals. Actually, we've been using signals all the time, for example, by using the connect_clicked
method on a button. This method simply adds an event handler function for the "click" signal.
+GObjects can not only have properties but also signals. Actually, we've been using signals all the time, for example, by using the connect_clicked
method on a button. This method simply adds an event handler function for the "click" signal.
You can create your own signals in custom widgets. You can also use emit to emit signals on you widgets manually.
Read more about signals in the gtk-rs book.
diff --git a/book/next/index.html b/book/next/index.html
index 82473678ea0a..b8d0c56ccdee 100644
--- a/book/next/index.html
+++ b/book/next/index.html
@@ -187,7 +187,7 @@
-Relm4 is an idiomatic GUI library inspired by Elm and based on gtk4-rs.
+
Relm4 is an idiomatic GUI library inspired by Elm and based on gtk4-rs.
It is a new version of relm that's built from scratch and is compatible with GTK4 and libadwaita.
The compl
.spacing(5)
.build();
- let inc_button = gtk::Button::with_label("Increment");
- let dec_button = gtk::Button::with_label("Decrement");
+ let inc_button = gtk::Button::with_label("Increment");
+ let dec_button = gtk::Button::with_label("Decrement");
- let label = gtk::Label::new(Some(&format!("Counter: {}", model.counter)));
+ let label = gtk::Label::new(Some(&format!("Counter: {}", model.counter)));
label.set_margin_all(5);
window.set_child(Some(&vbox));
@@ -435,12 +435,12 @@ The compl
fn update_view(&self, widgets: &mut Self::Widgets, _sender: ComponentSender<Self>) {
widgets
.label
- .set_label(&format!("Counter: {}", self.counter));
+ .set_label(&format!("Counter: {}", self.counter));
}
}
fn main() {
- let app = RelmApp::new("relm4.test.simple_manual");
+ let app = RelmApp::new("relm4.test.simple_manual");
app.run::<AppModel>(0);
}
diff --git a/book/next/gtk_rs.html b/book/next/gtk_rs.html
index 657572a2fe98..3ccd4cbb845e 100644
--- a/book/next/gtk_rs.html
+++ b/book/next/gtk_rs.html
@@ -187,19 +187,19 @@ gtk-rs overvi
GObjects
GTK is an object-oriented framework that uses the GObject library to implement objects. GObjects have some really useful features that we will discuss in the following sections.
Subclassing
-Like many other OOP frameworks or languages, GObjects can inherit from other GObjects. This is called subclassing. In the case of GTK, that’s really helpful because it allows us to create custom widgets.
+Like many other OOP frameworks or languages, GObjects can inherit from other GObjects. This is called subclassing. In the case of GTK, that’s really helpful because it allows us to create custom widgets.
For example, you could use subclassing to create your own button widget that acts as a counter. Or you can create a custom application window that better suits your application.
Read more about subclassing in the gtk-rs book.
Properties
Each GObject can have properties that work similar to the fields of a structure in Rust. You can set them and you can read (get) them. But one thing that's particularly cool is that properties can be bound to other properties.
-For example, you could bind the "visible" property of a widget to the "active" property of a gtk::ToggleButton
. This would allow you to show or hide the widget using the toggle button and the best part is, that it's done fully automatically!
+For example, you could bind the "visible" property of a widget to the "active" property of a gtk::ToggleButton
. This would allow you to show or hide the widget using the toggle button and the best part is, that it's done fully automatically!
Read more about properties in the gtk-rs book.
Signals
-GObjects can not only have properties but also signals. Actually, we've been using signals all the time, for example, by using the connect_clicked
method on a button. This method simply adds an event handler function for the "click" signal.
+GObjects can not only have properties but also signals. Actually, we've been using signals all the time, for example, by using the connect_clicked
method on a button. This method simply adds an event handler function for the "click" signal.
You can create your own signals in custom widgets. You can also use emit to emit signals on you widgets manually.
Read more about signals in the gtk-rs book.
diff --git a/book/next/index.html b/book/next/index.html
index 82473678ea0a..b8d0c56ccdee 100644
--- a/book/next/index.html
+++ b/book/next/index.html
@@ -187,7 +187,7 @@
-Relm4 is an idiomatic GUI library inspired by Elm and based on gtk4-rs.
+
Relm4 is an idiomatic GUI library inspired by Elm and based on gtk4-rs.
It is a new version of relm that's built from scratch and is compatible with GTK4 and libadwaita.
gtk-rs overvi
GObjects
GTK is an object-oriented framework that uses the GObject library to implement objects. GObjects have some really useful features that we will discuss in the following sections.
Subclassing
-Like many other OOP frameworks or languages, GObjects can inherit from other GObjects. This is called subclassing. In the case of GTK, that’s really helpful because it allows us to create custom widgets.
+Like many other OOP frameworks or languages, GObjects can inherit from other GObjects. This is called subclassing. In the case of GTK, that’s really helpful because it allows us to create custom widgets.
For example, you could use subclassing to create your own button widget that acts as a counter. Or you can create a custom application window that better suits your application.
Read more about subclassing in the gtk-rs book.
Properties
Each GObject can have properties that work similar to the fields of a structure in Rust. You can set them and you can read (get) them. But one thing that's particularly cool is that properties can be bound to other properties.
-For example, you could bind the "visible" property of a widget to the "active" property of a gtk::ToggleButton
. This would allow you to show or hide the widget using the toggle button and the best part is, that it's done fully automatically!
+For example, you could bind the "visible" property of a widget to the "active" property of a gtk::ToggleButton
. This would allow you to show or hide the widget using the toggle button and the best part is, that it's done fully automatically!
Read more about properties in the gtk-rs book.
Signals
-GObjects can not only have properties but also signals. Actually, we've been using signals all the time, for example, by using the connect_clicked
method on a button. This method simply adds an event handler function for the "click" signal.
+GObjects can not only have properties but also signals. Actually, we've been using signals all the time, for example, by using the connect_clicked
method on a button. This method simply adds an event handler function for the "click" signal.
You can create your own signals in custom widgets. You can also use emit to emit signals on you widgets manually.
Read more about signals in the gtk-rs book.
diff --git a/book/next/index.html b/book/next/index.html
index 82473678ea0a..b8d0c56ccdee 100644
--- a/book/next/index.html
+++ b/book/next/index.html
@@ -187,7 +187,7 @@
-Relm4 is an idiomatic GUI library inspired by Elm and based on gtk4-rs.
+
Relm4 is an idiomatic GUI library inspired by Elm and based on gtk4-rs.
It is a new version of relm that's built from scratch and is compatible with GTK4 and libadwaita.
Read more about subclassing in the gtk-rs book.
gtk::ToggleButton
. This would allow you to show or hide the widget using the toggle button and the best part is, that it's done fully automatically!gtk::ToggleButton
. This would allow you to show or hide the widget using the toggle button and the best part is, that it's done fully automatically!Read more about properties in the gtk-rs book.
connect_clicked
method on a button. This method simply adds an event handler function for the "click" signal.connect_clicked
method on a button. This method simply adds an event handler function for the "click" signal.Read more about signals in the gtk-rs book.
diff --git a/book/next/index.html b/book/next/index.html index 82473678ea0a..b8d0c56ccdee 100644 --- a/book/next/index.html +++ b/book/next/index.html @@ -187,7 +187,7 @@-
Relm4 is an idiomatic GUI library inspired by Elm and based on gtk4-rs. +
Relm4 is an idiomatic GUI library inspired by Elm and based on gtk4-rs. It is a new version of relm that's built from scratch and is compatible with GTK4 and libadwaita.