Crate gtk_estate

Crate gtk_estate 

Source
Expand description

§GTK Estate

Crates.io License Downloads Docs Twitch Status

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

FeatureEnabled Feature
gtk4_v4_18gtk/v4_18
gtk4_v4_16gtk/v4_16
gtk4_v4_14gtk4/v4_14
gtk4_v4_12gtk4/v4_12
gtk4_v4_10gtk4/v4_10
gtk4_v4_8gtk4/v4_8
gtk4_v4_6gtk4/v4_6
gtk4_v4_4gtk4/v4_4
gtk4_v4_2gtk4/v4_2
gtk4_gnome_45gtk4/gnome_45
gtk4_gnome_44gtk4/gnome_44
gtk4_gnome_43gtk4/gnome_43
gtk4_gnome_42gtk4/gnome_42
gtk4_unsafe-assume-initializedgtk4/unsafe-assume-initialized
gtk4_xml_validationgtk4/xml_validation
gtk4_blueprintgtk4/blueprint

§libadwaita

FeatureEnabled Feature
adwdep:adw
adw_gtk_v4_2adw/gtk_v4_2
adw_gtk_v4_6adw/gtk_v4_6
adw_gio_v2_80adw/gio_v2_80
adw_v1_1adw/v1_1
adw_v1_2adw/v1_2
adw_v1_3adw/v1_3
adw_v1_4adw/v1_4
adw_v1_5adw/v1_5
adw_v1_6adw/v1_6
adw_v1_7adw/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§

pub extern crate corlib;
pub extern crate gtk;

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_traitsstrong_widget_state
Use to setup a state container struct with a strongly referenced widget object.
impl_weak_self_methodsstrong_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_addthread_local_state
This macro gets a StateContainers Rc instance and adds the “$this” widget state to its WidgetStateContainers instance.
scs_set_application_statethread_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_addthread_local_state
This macro gets a StateContainers Rc instance and adds the “$this” widget state to its StrongWidgetStateContainers instance.

Structs§

ScopedSignalHandlerId
Makes it easier to handle signals
ScopedSignalHandlerIdHashMap
For making it easier to handle multiple signals at once.
ScopedSourceId
Makes it easier to handle SourceIds.
StateContainers
The struct within which all widget states are centrally located.
StrongWidgetAdapterstrong_widget_state
Strongly-references implementers of WidgetExt and enables them to be used in scenarios requiring dyn compatibility.
StrongWidgetStateContainersstrong_widget_state
Keeps track of Rc’d DynStrongWidgetStateContainer implementers.
TimeOut
This object helps you setup time-outs with associated parent objects.
WidgetAdapter
Weakly-references implementers of WidgetExt and enables them to be used in scenarios requiring dyn compatibility.
WidgetStateContainers
Keeps track of Rc’d DynWidgetStateContainer implementers, dropping them when their weakly referenced widgets get destroyed.
WidgetUpgradeError
The error type used when a weakly-referenced widget object upgrade fails.

Enums§

TimeOutRunType
Used by TimeOut to determine which timer resolution to use.

Traits§

DynStrongWidgetStateContainerstrong_widget_state
Like the StrongWidgetStateContainer trait, but dynamic.
DynWidgetStateContainer
Like the WidgetStateContainer trait, but dynamic.
StrongWidgetObjectstrong_widget_state
Implement on an object which stores a Widget object for the purpose of dynmically comparing with other objects.
StrongWidgetStateContainerstrong_widget_state
Indicates that the implementing object contains a StrongWidgetAdapter object and makes it accessible.
WidgetContainer
For when your state-container directly contains the widget object.
WidgetObject
A trait for the container of a weakly-referenced widget object.
WidgetStateContainer
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§

RcTimeOutFn
The Rc’d version of TimeOutFn.
RcWidgetStateContainers
The Rc’d version of WidgetStateContainers.
TimeOutFn
The signature of the closure called by TimeOut.
WidgetUpgradeResult
The result of a weakly-referenced widget object being upgraded.