1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
//! # gstore //! //! gstore is a library that provides global and local state management for GTK Apps as well as a //! bunch of macros to build GTK widgets. //! //! gstore is inspired by react/redux. If you are familiar with these frameworks you might find a //! lot of their concepts used in gstore. //! //! ## Contents //! 1. Concepts //! 2. Local State //! 3. Actions //! //! ## Concepts //! //! gstore is an app framework above of GTK. GTK is a single-threaded UI library. gstore uses a //! background thread to handle global state mutations and connects it with the GTK main thread via //! user defined enums (called Actions) using `std::sync::mpsc::channel`. //! //! > Note: currently this leads to a major drawback: The GTK main thread has to check every `n` //! > milliseconds for sent Actions. Please feel free to contact me if you have a better idea how to //! > do this. //! //! The diagram below aims to visualize the basic concept of gstore: //! //! ```plain //! +-----------------------------------------------------------------+ //! | | //! | State | //! | | //! +-----------------------------------------------------------------+ //! | | //! | Mutexed global state | //! | | //! +-----+------------------------------------------------+----------+ //! ^ ^ //! | | //! | Read Access | Read/Write Access //! | | //! | | //! +-----+-----+ +--------+----------+ //! | | | | //! | UI thread | | Background thread | //! | | | | //! +-----------+ send Action on user input +-------------------+ //! | | | | //! | GTK main +-------------------------------->+ runs reducers | //! | thread | | | //! | | +-------------+ | | //! | | | | | | //! | | | enum Action | | | //! | | | | | | //! | | +-------------+ | | //! | | | | //! | | receive Action when handled | | //! | | | | //! | +<--------------------------------+ | //! | | | | //! +-----------+ +-------------------+ //! ``` //! //! The background thread mutates the global state while the UI thread only reads the global state. //! State mutation means a new version of the state is by a stateless function. In redux this is //! called reducers. In other languages we know this kind function a fold. //! //! In contrast to redux there are currently no additional restrictions of what to do in a reducers. //! //! 2. Usage //! ```rust,no_run //! #[macro_use] //! extern crate gstore; //! use gtk::prelude::*; //! use gstore::prelude::*; //! //! // slice //! // ----------------------------------------------------------------- //! // Define a slice //! slice! { //! CountState { count: i64 = 0 } //! CountAction { Increment, Decrement } //! } //! //! // define a reducer on that slice //! fn reduce_count(action: CountAction, state: CountState) -> CountState { //! match action { //! CountAction::Increment => CountState { count: state.count + 1, ..state }, //! CountAction::Decrement => CountState { count: state.count - 1, ..state } //! } //! } //! //! // define a selector for your slice (the state is the Root state) //! fn select_count(state: &crate::State) -> i64 { state.counting.count } //! //! // define functions for convenient action dispatching //! fn increment() -> Action { //! crate::Action::Counting(CountAction::Increment) //! } //! //! fn decrement() -> Action { //! crate::Action::Counting(CountAction::Decrement) //! } //! // ----------------------------------------------------------------- //! //! // store //! // ----------------------------------------------------------------- //! // combine slices in your store //! store! { //! counting: Counting = crate::{CountState, CountAction, reduce_count} //! } //! //! fn main() { //! //! let logging_middleware = middleware(|store, next, action: Action| { //! println!("Handling action {:?}", action); //! next(store, action.clone()); //! println!("Handled action {:?}", action); //! }); //! //! let store: Store = Store::new(root_reducer, vec![logging_middleware]); //! //! gstore::gtk::run(store, |store| { //! let window = window(store.clone()); //! store.dispatch(Action::Start); //! window //! }) //! } //! //! // ----------------------------------------------------------------- //! //! // window //! // ----------------------------------------------------------------- //! // define ui component //! //! use_state! { //! [message: String, set_message] = "The current count:".to_string() //! } //! //! fn window(store: Store) -> gtk::ApplicationWindow { //! application_window! { //! widget { //! properties { //! default_width: 400 //! default_height: 400 //! } //! children [ //! label! { //! properties { //! label: message() //! } //! } //! ] //! } //! } //! } //! //! ``` extern crate log; pub use once_cell; pub mod gtk; pub mod l10n; pub mod store; pub mod widgets; pub mod render_handling; pub mod prelude { pub use crate::gtk::*; pub use crate::l10n::*; pub use crate::store::*; pub use crate::widgets::*; }