Settings
We have now learned multiple ways to handle states.
However, every time we close the application all of it is gone.
Let's learn how to use gio::Settings
by storing the state of a Switch
in it.
At the very beginning we have to create a GSchema
xml file in order to describe the kind of data our application plans to store in the settings.
Filename: listings/settings/1/org.gtk_rs.Settings1.gschema.xml
<?xml version="1.0" encoding="utf-8"?>
<schemalist>
<schema id="org.gtk_rs.Settings1" path="/org/gtk_rs/Settings1/">
<key name="is-switch-enabled" type="b">
<default>false</default>
<summary>Default switch state</summary>
</key>
</schema>
</schemalist>
Let's get through it step by step.
The id
is the same application id we used when we created our application.
Filename: listings/settings/1/main.rs
use gio::Settings;
use gtk::prelude::*;
use gtk::{gio, glib, Align, Application, ApplicationWindow, Switch};
const APP_ID: &str = "org.gtk_rs.Settings1";
fn main() -> glib::ExitCode {
// Create a new application
let app = Application::builder().application_id(APP_ID).build();
// Connect to "activate" signal of `app`
app.connect_activate(build_ui);
// Run the application
app.run()
}
fn build_ui(app: &Application) {
// Initialize settings
let settings = Settings::new(APP_ID);
// Get the last switch state from the settings
let is_switch_enabled = settings.boolean("is-switch-enabled");
// Create a switch
let switch = Switch::builder()
.margin_top(48)
.margin_bottom(48)
.margin_start(48)
.margin_end(48)
.valign(Align::Center)
.halign(Align::Center)
.state(is_switch_enabled)
.build();
switch.connect_state_set(move |_, is_enabled| {
// Save changed switch state in the settings
settings
.set_boolean("is-switch-enabled", is_enabled)
.expect("Could not set setting.");
// Allow to invoke other event handlers
glib::Propagation::Proceed
});
// Create a window
let window = ApplicationWindow::builder()
.application(app)
.title("My GTK App")
.child(&switch)
.build();
// Present window
window.present();
}
The path
must start and end with a forward slash character ('/') and must not contain two sequential slash characters.
When creating a path
, we advise to take the id
, replace the '.' with '/' and add '/' at the front and end of it.
We only want to store a single key with the name
"is-switch-enabled".
This is a boolean value so its type
is "b" (see GVariant Format Strings for the other options).
We also set its default value to false
(see GVariant Text Format for the full syntax).
Finally, we add a summary.
Now we need to copy and compile the schema.
You can install the schema by executing the following commands on a Linux or macOS machine:
mkdir -p $HOME/.local/share/glib-2.0/schemas cp org.gtk_rs.Settings1.gschema.xml $HOME/.local/share/glib-2.0/schemas/ glib-compile-schemas $HOME/.local/share/glib-2.0/schemas/
On Windows run:
mkdir C:/ProgramData/glib-2.0/schemas/ cp org.gtk_rs.Settings1.gschema.xml C:/ProgramData/glib-2.0/schemas/ glib-compile-schemas C:/ProgramData/glib-2.0/schemas/
We initialize the Settings
object by specifying the application id.
Filename: listings/settings/1/main.rs
use gio::Settings;
use gtk::prelude::*;
use gtk::{gio, glib, Align, Application, ApplicationWindow, Switch};
const APP_ID: &str = "org.gtk_rs.Settings1";
fn main() -> glib::ExitCode {
// Create a new application
let app = Application::builder().application_id(APP_ID).build();
// Connect to "activate" signal of `app`
app.connect_activate(build_ui);
// Run the application
app.run()
}
fn build_ui(app: &Application) {
// Initialize settings
let settings = Settings::new(APP_ID);
// Get the last switch state from the settings
let is_switch_enabled = settings.boolean("is-switch-enabled");
// Create a switch
let switch = Switch::builder()
.margin_top(48)
.margin_bottom(48)
.margin_start(48)
.margin_end(48)
.valign(Align::Center)
.halign(Align::Center)
.state(is_switch_enabled)
.build();
switch.connect_state_set(move |_, is_enabled| {
// Save changed switch state in the settings
settings
.set_boolean("is-switch-enabled", is_enabled)
.expect("Could not set setting.");
// Allow to invoke other event handlers
glib::Propagation::Proceed
});
// Create a window
let window = ApplicationWindow::builder()
.application(app)
.title("My GTK App")
.child(&switch)
.build();
// Present window
window.present();
}
Then we get the settings key and use it when we create our Switch
.
Filename: listings/settings/1/main.rs
use gio::Settings;
use gtk::prelude::*;
use gtk::{gio, glib, Align, Application, ApplicationWindow, Switch};
const APP_ID: &str = "org.gtk_rs.Settings1";
fn main() -> glib::ExitCode {
// Create a new application
let app = Application::builder().application_id(APP_ID).build();
// Connect to "activate" signal of `app`
app.connect_activate(build_ui);
// Run the application
app.run()
}
fn build_ui(app: &Application) {
// Initialize settings
let settings = Settings::new(APP_ID);
// Get the last switch state from the settings
let is_switch_enabled = settings.boolean("is-switch-enabled");
// Create a switch
let switch = Switch::builder()
.margin_top(48)
.margin_bottom(48)
.margin_start(48)
.margin_end(48)
.valign(Align::Center)
.halign(Align::Center)
.state(is_switch_enabled)
.build();
switch.connect_state_set(move |_, is_enabled| {
// Save changed switch state in the settings
settings
.set_boolean("is-switch-enabled", is_enabled)
.expect("Could not set setting.");
// Allow to invoke other event handlers
glib::Propagation::Proceed
});
// Create a window
let window = ApplicationWindow::builder()
.application(app)
.title("My GTK App")
.child(&switch)
.build();
// Present window
window.present();
}
Finally, we assure that the switch state is stored in the settings whenever we click on it.
Filename: listings/settings/1/main.rs
use gio::Settings;
use gtk::prelude::*;
use gtk::{gio, glib, Align, Application, ApplicationWindow, Switch};
const APP_ID: &str = "org.gtk_rs.Settings1";
fn main() -> glib::ExitCode {
// Create a new application
let app = Application::builder().application_id(APP_ID).build();
// Connect to "activate" signal of `app`
app.connect_activate(build_ui);
// Run the application
app.run()
}
fn build_ui(app: &Application) {
// Initialize settings
let settings = Settings::new(APP_ID);
// Get the last switch state from the settings
let is_switch_enabled = settings.boolean("is-switch-enabled");
// Create a switch
let switch = Switch::builder()
.margin_top(48)
.margin_bottom(48)
.margin_start(48)
.margin_end(48)
.valign(Align::Center)
.halign(Align::Center)
.state(is_switch_enabled)
.build();
switch.connect_state_set(move |_, is_enabled| {
// Save changed switch state in the settings
settings
.set_boolean("is-switch-enabled", is_enabled)
.expect("Could not set setting.");
// Allow to invoke other event handlers
glib::Propagation::Proceed
});
// Create a window
let window = ApplicationWindow::builder()
.application(app)
.title("My GTK App")
.child(&switch)
.build();
// Present window
window.present();
}
The Switch
now retains its state even after closing the application.
But we can make this even better.
The Switch
has a property "active" and Settings
allows us to bind properties to a specific setting.
So let's do exactly that.
We can remove the boolean
call before initializing the Switch
as well as the connect_state_set
call.
We then bind the setting to the property by specifying the key, object and name of the property.
Filename: listings/settings/2/main.rs
use gio::Settings;
use gtk::prelude::*;
use gtk::{gio, glib, Align, Application, ApplicationWindow, Switch};
const APP_ID: &str = "org.gtk_rs.Settings2";
fn main() -> glib::ExitCode {
// Create a new application
let app = Application::builder().application_id(APP_ID).build();
// Connect to "activate" signal of `app`
app.connect_activate(build_ui);
// Run the application
app.run()
}
fn build_ui(app: &Application) {
// Initialize settings
let settings = Settings::new(APP_ID);
// Create a switch
let switch = Switch::builder()
.margin_top(48)
.margin_bottom(48)
.margin_start(48)
.margin_end(48)
.valign(Align::Center)
.halign(Align::Center)
.build();
settings
.bind("is-switch-enabled", &switch, "active")
.build();
// Create a window
let window = ApplicationWindow::builder()
.application(app)
.title("My GTK App")
.child(&switch)
.build();
// Present window
window.present();
}
Whenever you have a property which nicely correspond to a setting, you probably want to bind it to it. In other cases, interacting with the settings via the getter and setter methods tends to be the right choice.