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::*;
}