Expand description
§GTK Estate
X | Twitch | Youtube | Mastodon | GitHub | GitHub Sponsors
GTK Estate is a state association library for GTK widgets using the excellent GTK 4 and libadwaita libraries.
The main purpose of GTK Estate is to provide a convenient way to associate user-defined state objects with GTK and libadwaita container widgets. Using user-defined objects you control the user-centric state of their associated widgets and react to their signals. GTK Estate also contains objects such as TimeOut and helper functions that can assist you in building dynamic GTK applications programmatically.
The StateContainers struct is a thread-local singleton and contains widget state association hashmaps, each type of widget gets its own map instance (bucket).
You should read the gtk4-rs tutorial, if you haven’t already, before proceeding.
§An Example:
To give you a clear idea of how a GTK Estate application is put together, here is a version of the Simple Unix Time Outputer example application code (with some amendments to its imports, so that it will run as a test) (see below for more example applications).
You can run the following as a test using:
cargo test –features adw
//main.rs
//mod applicaion_state;
use gtk_estate::adw::{prelude::*, Application};
//use crate::applicaion_state::ApplicationState;
//mod window_state;
fn main()
{
//Create an Application object like usual (for GTK/Adwaita programmes oriented around Applications).
let app = Application::builder().application_id("org.unit_time_gui").build();
//Setup the application state.
ApplicationState::new(&app);
//Run the application
let run_res = app.run();
println!("Application ran exiting with code: {}", run_res.value());
}
//applicaion_state.rs
use gtk_estate::corlib::impl_weak_self_trait;
use gtk_estate::gtk::prelude::ApplicationExt;
//use gtk_estate::adw::Application;
use gtk_estate::scs_set_application_state;
use std::rc::{Rc, Weak};
use gtk_estate::StateContainers;
//use crate::window_state::WindowState;
use gtk_estate::corlib::WeakSelf;
pub struct ApplicationState
{
app: Application,
weak_self: Weak<ApplicationState>
}
impl ApplicationState
{
pub fn new(app: &Application) -> Rc<ApplicationState>
{
let this = Rc::new_cyclic(|weak_self|
{
Self
{
app: app.clone(),
weak_self: weak_self.clone()
}
});
app.connect_activate(move |app|
{
//new window
WindowState::new(app);
});
//Set the application state
scs_set_application_state!(this);
this
}
pub fn app_ref(&self) -> &Application
{
&self.app
}
}
impl_weak_self_trait!(ApplicationState);
//window_state.rs
//use std::rc::{Weak, Rc};
use gtk_estate::{impl_widget_state_container_traits, scs_add, /*StateContainers,*/ WidgetAdapter, WidgetStateContainer};
use gtk_estate::gtk::prelude::{BoxExt, WidgetExt};
use gtk_estate::gtk::{Box, Orientation, Label, Align};
use gtk_estate::adw::{/*Application,*/ ApplicationWindow, HeaderBar, WindowTitle};
use gtk_estate::corlib::convert::AsAnyRef;
use gtk_estate::{TimeOut, TimeOutRunType};
use time::OffsetDateTime;
use std::any::Any;
use gtk_estate::{DynWidgetStateContainer, WidgetObject};
//use gtk_estate::corlib::WeakSelf;
#[derive(Debug)]
pub struct WindowState
{
unix_time_label: Label,
time_out: TimeOut<WindowState>,
widget_adapter: Rc<WidgetAdapter<ApplicationWindow, Self>>,
}
impl WindowState
{
pub fn new(application: &Application) -> Rc<Self>
{
//Initialise The window content box.
let cbox = Box::new(Orientation::Vertical, 0);
cbox.set_vexpand(true);
//HeaderBar
let window_title = WindowTitle::new("Simple Unix Time Outputer", "");
let hb = HeaderBar::builder().title_widget(&window_title).build();
cbox.append(&hb);
//Internal Content
let internal_content = Box::new(Orientation::Vertical, 0);
//The Unix time display Label.
let unix_time_label = Label::new(Some(""));
internal_content.append(&unix_time_label);
internal_content.set_vexpand(true);
internal_content.set_valign(Align::Center);
cbox.append(&internal_content);
//Initialise ApplicationWindow
let builder = ApplicationWindow::builder();
let window = builder.application(application)
.default_width(1000)
.default_height(1000)
//Set the content of the ApplicationWindow.
.content(&cbox)
.visible(true)
.build();
//Initialise WindowState
let this = Rc::new_cyclic( move |weak_self|
{
Self
{
unix_time_label,
time_out: TimeOut::new(TimeOutRunType::Seconds(1), weak_self),
widget_adapter: WidgetAdapter::new(&window, weak_self)
}
});
//Add WindowState to the StateContainers object.
scs_add!(this);
//Setup the on_timeout closure.
let on_timeout = Rc::new(move |this: Rc<Self>|
{
let utc_now = OffsetDateTime::now_utc();
let uts = utc_now.unix_timestamp();
this.unix_time_label.set_label(&uts.to_string());
true
});
//Set the closure and start the TimeOut.
this.time_out.set_time_out_fn(&on_timeout);
this.time_out.start();
this
}
}
impl_widget_state_container_traits!(ApplicationWindow, WindowState);
Important details to note about the above example are:
The thread-local application state is set using the scs_set_application_state macro (Calls StateContainers::set_application_state basically) and the thread-local window state is set using the scs_add macro (This calls StateContainers::widget_state_ref).
Setting widget state using these macros (and methods) make these objects thread-locally accessible and allows you to keep Rc references around when they would otherwise be dropped.
The impl_weak_self_trait and impl_widget_state_container_traits macros implement the various traits (AsAnyRef, DynWidgetStateContainer, WidgetStateContainer and WeakSelf) on the constituent objects for convience and so that they can work with the StateContainers object (Accessed via scs_set_application_state and scs_add in this case).
By default StateContainers is a thread-local singleton which should only contain state which deals with user-interface and inter-thread-communication related tasks possibly using a crate like LibSync for the latter.
§Building Requirements
Requires the GTK4 library binaries on your system (See The GTK Book for GTK installation instructions).
Search your software repositories to find the relevant libadwaita libraries if you want to use any adw features.
§Building The Documentation
To build the documentation use:
cargo doc –features strong_widget_state
or (If applicable)
cargo +nightly doc –features strong_widget_state
§Features
§GTK4
| Feature | Enabled Feature |
|---|---|
| gtk4_v4_18 | gtk/v4_18 |
| gtk4_v4_16 | gtk/v4_16 |
| gtk4_v4_14 | gtk4/v4_14 |
| gtk4_v4_12 | gtk4/v4_12 |
| gtk4_v4_10 | gtk4/v4_10 |
| gtk4_v4_8 | gtk4/v4_8 |
| gtk4_v4_6 | gtk4/v4_6 |
| gtk4_v4_4 | gtk4/v4_4 |
| gtk4_v4_2 | gtk4/v4_2 |
| gtk4_gnome_45 | gtk4/gnome_45 |
| gtk4_gnome_44 | gtk4/gnome_44 |
| gtk4_gnome_43 | gtk4/gnome_43 |
| gtk4_gnome_42 | gtk4/gnome_42 |
| gtk4_unsafe-assume-initialized | gtk4/unsafe-assume-initialized |
| gtk4_xml_validation | gtk4/xml_validation |
| gtk4_blueprint | gtk4/blueprint |
§libadwaita
| Feature | Enabled Feature |
|---|---|
| adw | dep:adw |
| adw_gtk_v4_2 | adw/gtk_v4_2 |
| adw_gtk_v4_6 | adw/gtk_v4_6 |
| adw_gio_v2_80 | adw/gio_v2_80 |
| adw_v1_1 | adw/v1_1 |
| adw_v1_2 | adw/v1_2 |
| adw_v1_3 | adw/v1_3 |
| adw_v1_4 | adw/v1_4 |
| adw_v1_5 | adw/v1_5 |
| adw_v1_6 | adw/v1_6 |
| adw_v1_7 | adw/v1_7 |
§Additionally
GTK Estate Re-exposes gtk4 (gtk), libadwaita (adw (if selected)) and Corlib (corlib).
§Example Applications
Note: Some of these examples may be out of date.
§Compiler:
Use the latest stable compiler.
§Todo
- Add more GTK/adw helper functions and helper objects.
- Add more documentation
- Add procedural macros
§Coding Style
This project has a coding style which emphasises the use of white space over keeping the line and column counts as low as possible.
So this:
fn bar() {}
fn foo()
{
bar();
}
Not this:
fn bar() {}
fn foo()
{
bar();
}
§License
Licensed under either of:
- Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0 (see also: https://www.tldrlegal.com/license/apache-license-2-0-apache-2-0))
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT (see also: https://www.tldrlegal.com/license/mit-license))
at your discretion
§Contributing
Please clone the repository and create an issue explaining what feature or features you’d like to add or bug or bugs you’d like to fix and perhaps how you intend to implement these additions or fixes. Try to include details though it doesn’t need to be exhaustive and we’ll take it from there (dependant on availability).
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
Re-exports§
Modules§
- helpers
- Helper functions for different widget types.
- rc_
conversions - This module contains functions which upcast objects stored in std::rc::Rc objects and return the rc’d dynamic object.
Macros§
- impl_
contents_ box_ ref - Using a Box to frame your content? Us this macro to access it.
- impl_
strong_ widget_ state_ container_ traits strong_widget_state - Use to setup a state container struct with a strongly referenced widget object.
- impl_
weak_ self_ methods strong_widget_state - Implements weak_self methods on your state container object.
- impl_
widget_ container - Implements WidgetContainer on an object.
- impl_
widget_ state_ container_ traits - Use to setup a state container struct with a weakly referenced widget object.
- scs_add
thread_local_state - This macro gets a StateContainers Rc instance and adds the “$this” widget state to its WidgetStateContainers instance.
- scs_
set_ application_ state thread_local_state - This macro gets a StateContainers Rc instance and calls “set_application_state” on it, passing “$this”, to set the application state.
- scs_
strong_ add thread_local_state - This macro gets a StateContainers Rc instance and adds the “$this” widget state to its StrongWidgetStateContainers instance.
Structs§
- Scoped
Signal Handler Id - Makes it easier to handle signals
- Scoped
Signal Handler IdHash Map - For making it easier to handle multiple signals at once.
- Scoped
Source Id - Makes it easier to handle SourceIds.
- State
Containers - The struct within which all widget states are centrally located.
- Strong
Widget Adapter strong_widget_state - Strongly-references implementers of WidgetExt and enables them to be used in scenarios requiring dyn compatibility.
- Strong
Widget State Containers strong_widget_state - Keeps track of Rc’d DynStrongWidgetStateContainer implementers.
- TimeOut
- This object helps you setup time-outs with associated parent objects.
- Widget
Adapter - Weakly-references implementers of WidgetExt and enables them to be used in scenarios requiring dyn compatibility.
- Widget
State Containers - Keeps track of Rc’d DynWidgetStateContainer implementers, dropping them when their weakly referenced widgets get destroyed.
- Widget
Upgrade Error - The error type used when a weakly-referenced widget object upgrade fails.
Enums§
- Time
OutRun Type - Used by TimeOut to determine which timer resolution to use.
Traits§
- DynStrong
Widget State Container strong_widget_state - Like the StrongWidgetStateContainer trait, but dynamic.
- DynWidget
State Container - Like the WidgetStateContainer trait, but dynamic.
- Strong
Widget Object strong_widget_state - Implement on an object which stores a Widget object for the purpose of dynmically comparing with other objects.
- Strong
Widget State Container strong_widget_state - Indicates that the implementing object contains a StrongWidgetAdapter object and makes it accessible.
- Widget
Container - For when your state-container directly contains the widget object.
- Widget
Object - A trait for the container of a weakly-referenced widget object.
- Widget
State Container - Indicates that the implementing object contains a WidgetAdapter object and makes it accessible.
Functions§
- idle_
add_ local_ diy - Makes using gtk::glib::idle_add_local a bit easier.
- on_
widget_ upgrade_ error - Calls error_fn if the provided result is an Err.
- on_
widget_ upgrade_ error_ with_ param - Calls error_fn if the provided result is an Err.
- should_
continue - Converts a bool instance into a ControlFlow.
- should_
flow - Converts a ControlFlow instance into a bool.
- timeout_
add_ local_ diy - Calls glib::source::timeout_add_local and returns a ScopedSourceId.
- timeout_
add_ seconds_ local_ diy - Calls glib::source::timeout_add_seconds_local and returns a ScopedSourceId.
- widget_
upgrade_ error_ debug_ panic - If the provided result is an Err, the function panics with a debug formatted message.
- widget_
upgrade_ error_ debug_ println - If the provided result is an Err it is debug formatted and displayed in the standard-out.
- widget_
upgrade_ error_ display_ panic - If the provided result is an Err, the function panics.
- widget_
upgrade_ error_ display_ println - If the provided result is an Err it is displayed in the standard-out.
Type Aliases§
- RcTime
OutFn - The Rc’d version of TimeOutFn.
- RcWidget
State Containers - The Rc’d version of WidgetStateContainers.
- Time
OutFn - The signature of the closure called by TimeOut.
- Widget
Upgrade Result - The result of a weakly-referenced widget object being upgraded.