├── .gitignore
├── src
├── gui
│ ├── mod.rs
│ ├── properties.rs
│ ├── window.rs
│ ├── font.rs
│ └── script
│ │ └── mod.rs
├── elements
│ ├── mod.rs
│ ├── image.rs
│ ├── element.rs
│ ├── scrollbox.rs
│ ├── button.rs
│ ├── vbox.rs
│ ├── hbox.rs
│ └── textbox.rs
├── lib.rs
├── util.rs
└── data
│ └── mod.rs
├── .idea
├── vcs.xml
├── modules.xml
├── misc.xml
└── skryn.iml
├── Skryn.iml
├── Cargo.toml
├── LICENSE
├── README.md
└── examples
├── mainexample.rs
└── calculator.rs
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | **/*.rs.bk
3 | .idea/workspace.xml
4 |
--------------------------------------------------------------------------------
/src/gui/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod font;
2 | pub mod properties;
3 | mod script;
4 | pub mod window;
5 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/elements/mod.rs:
--------------------------------------------------------------------------------
1 | mod button;
2 | mod element;
3 | mod hbox;
4 | mod image;
5 | mod scrollbox;
6 | mod textbox;
7 | mod vbox;
8 |
9 | pub use self::button::Button;
10 | pub use self::element::*;
11 | pub use self::hbox::HBox;
12 | pub use self::image::*;
13 | pub use self::scrollbox::ScrollBox;
14 | pub use self::textbox::TextBox;
15 | pub use self::vbox::VBox;
16 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/lib.rs:
--------------------------------------------------------------------------------
1 | #![windows_subsystem = "windows"]
2 | extern crate app_units;
3 | extern crate clipboard;
4 | extern crate euclid;
5 | extern crate font_kit;
6 | extern crate gleam;
7 | extern crate glutin;
8 | pub extern crate webrender;
9 | extern crate winit;
10 | #[macro_use]
11 | extern crate lazy_static;
12 | extern crate harfbuzz_sys;
13 | extern crate itertools;
14 | extern crate unicode_bidi;
15 |
16 | pub mod data;
17 | pub mod elements;
18 | pub mod gui;
19 | mod util;
20 |
--------------------------------------------------------------------------------
/Skryn.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.idea/skryn.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "skryn"
3 | version = "0.0.4"
4 | authors = ["Fasih Rana "]
5 | edition = "2018"
6 | description = "A servo/webrender based Desktop GUI library"
7 | license = "MIT"
8 |
9 | exclude = [
10 | ".idea",
11 | "Skryn.iml"
12 | ]
13 |
14 | [build-dependencies]
15 | cmake = "0.1.38"
16 |
17 | [dependencies]
18 | winit = "^0.19"
19 | glutin = "^0.21.0-rc2"
20 | webrender = "^0.58"
21 | gleam = "^0.6.13"
22 | app_units = "0.7"
23 | euclid = "0.19"
24 | scan-rules = "^0.2.0"
25 | font-kit = "^0.1.0"
26 | lazy_static = "^1.3.0"
27 | clipboard = "^0.5.0"
28 | itertools = "^0.8.0"
29 | harfbuzz = "=0.4.0"
30 | harfbuzz-sys = "=0.5.0"
31 | unicode-bidi = "^0.3.4"
32 |
33 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Fasih Rana
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/elements/image.rs:
--------------------------------------------------------------------------------
1 | use std::fs::File;
2 | use std::io::prelude::*;
3 |
4 | #[derive(Clone, Debug, PartialEq)]
5 | pub enum ImagePath {
6 | Local(String),
7 | URL(String),
8 | }
9 |
10 | #[derive(Clone, Debug, PartialEq)]
11 | pub struct Image {
12 | path: ImagePath,
13 | bytes: Vec,
14 | ext_id: u64,
15 | }
16 |
17 | impl Image {
18 | pub fn load(path: ImagePath) -> Option {
19 | if let ImagePath::Local(_s) = path.clone() {
20 | let f = File::open(&_s[0..]);
21 | if let Ok(mut c) = f {
22 | let l = c.metadata().unwrap().len();
23 | let mut bytes: Vec = Vec::new();
24 | bytes.resize(l as usize, 0);
25 | let r = c.read(&mut bytes);
26 | println!("read file ? {:?}", r);
27 | if r.is_ok() {
28 | //let bytes = contents.to_vec();
29 | return Some(Image {
30 | path,
31 | bytes,
32 | ext_id: 0,
33 | });
34 | }
35 | }
36 | }
37 |
38 | None
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/util.rs:
--------------------------------------------------------------------------------
1 | use webrender::api::*;
2 |
3 | pub trait HandyDandyRectBuilder {
4 | fn to(&self, x2: T, y2: T) -> LayoutRect;
5 | fn by(&self, w: T, h: T) -> LayoutRect;
6 | }
7 | // Allows doing `(x, y).to(x2, y2)` or `(x, y).by(width, height)` with i32
8 | // values to build a f32 LayoutRect
9 | impl HandyDandyRectBuilder for (i32, i32) {
10 | fn to(&self, x2: i32, y2: i32) -> LayoutRect {
11 | LayoutRect::new(
12 | LayoutPoint::new(self.0 as f32, self.1 as f32),
13 | LayoutSize::new((x2 - self.0) as f32, (y2 - self.1) as f32),
14 | )
15 | }
16 |
17 | fn by(&self, w: i32, h: i32) -> LayoutRect {
18 | LayoutRect::new(
19 | LayoutPoint::new(self.0 as f32, self.1 as f32),
20 | LayoutSize::new(w as f32, h as f32),
21 | )
22 | }
23 | }
24 |
25 | impl HandyDandyRectBuilder for (f32, f32) {
26 | fn to(&self, x2: f32, y2: f32) -> LayoutRect {
27 | LayoutRect::new(
28 | LayoutPoint::new(self.0, self.1),
29 | LayoutSize::new(x2 - self.0, y2 - self.1),
30 | )
31 | }
32 |
33 | fn by(&self, w: f32, h: f32) -> LayoutRect {
34 | LayoutRect::new(LayoutPoint::new(self.0, self.1), LayoutSize::new(w, h))
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # skryn
2 |
3 | ## What is skryn?
4 |
5 | skryn is a (work-in-progress) Desktop GUI framework based on [servo/webrender](https://github.com/servo/webrender). It's aim is to have a pure rust implementation of a framework that is easy to use and extend. The motivation behind starting skryn was to get a simple hardware based rendered GUI without having any other web technologies be in the way.
6 |
7 | ## Goals/Features
8 |
9 | 1. Start the window manager at desired FPS.
10 | 2. The minimum requirement to create your own elements is to implement the `Element` trait.
11 | 3. Use implemented elements to create complex elements.
12 | 4. Library provided minimalistic `Observable`s.
13 | 5. Multithreading safe.
14 | 6. Simplified length Units (Natural, Extent, Stretch, Pixel).
15 | 7. Show a cursor in `TextBox` element.
16 | 8. Paste into and Copy from `TextBox`.
17 | 9. Supports RTL languages.
18 |
19 | ## Project Status (Limitations/Features planned)
20 |
21 | There are many limitations in the project. Following is the known list of these (not limited to)
22 |
23 | 1. Cross Element communication
24 | 2. Observables need a better implementation.
25 | 3. There are no animations at the moment. (Possible through implementation of own Element).
26 | 4. Needs z-index like concept.
27 |
28 | ## Build on Ubuntu
29 | ### Requirements
30 | 1. Install cmake
31 | 2. Install libfreetype6-dev
32 | 3. Install libexpat1-dev
33 |
--------------------------------------------------------------------------------
/src/data/mod.rs:
--------------------------------------------------------------------------------
1 | use std::collections::HashMap;
2 |
3 | pub struct Observable {
4 | next_id: u64,
5 | value: T,
6 | observers: HashMap>,
7 | }
8 |
9 | impl Observable {
10 | pub fn new(value: T) -> Self {
11 | Self {
12 | next_id: 0,
13 | value,
14 | observers: HashMap::new(),
15 | }
16 | }
17 |
18 | pub fn get_value(&self) -> T {
19 | self.value.clone()
20 | }
21 |
22 | pub fn observe(&mut self, observer: Box) -> u64 {
23 | self.observers.insert(self.next_id, observer);
24 | let tmp = self.next_id;
25 | self.next_id += 1;
26 |
27 | tmp
28 | }
29 |
30 | pub fn stop(&mut self, id: u64) {
31 | self.observers.remove(&id);
32 | }
33 |
34 | fn notify_observers(&mut self) {
35 | for o in self.observers.values_mut() {
36 | o(&self.value);
37 | }
38 | }
39 | }
40 |
41 | pub enum Action {
42 | Add(T),
43 | Remove(T),
44 | Update(T),
45 | }
46 |
47 | pub trait Update {
48 | fn update(&mut self, value: Action);
49 | }
50 |
51 | impl Update for Observable {
52 | fn update(&mut self, value: Action) {
53 | if let Action::Update(v) = value {
54 | self.value = v;
55 | self.notify_observers();
56 | }
57 | }
58 | }
59 |
60 | pub type ObservableU32 = Observable;
61 | pub type ObservableString = Observable;
62 |
63 | /*pub type ObservableI8 = Observable;
64 | pub type ObservableU8 = Observable;
65 | pub type ObservableI16 = Observable;
66 | pub type ObservableU16 = Observable;
67 | pub type ObservableI32 = Observable;
68 | pub type ObservableU32 = Observable;
69 | pub type ObservableI64 = Observable;
70 | pub type ObservableU64 = Observable;
71 | pub type ObservableBool = Observable;
72 | pub type ObservableChar = Observable;
73 | pub type ObservableString = Observable;
74 | pub type ObservableISize = Observable;
75 | pub type ObservableUSize = Observable;
76 | pub type ObservableF32 = Observable;
77 | pub type ObservableF64 = Observable;
78 |
79 | pub type ObservableVec = Observable>;*/
80 |
--------------------------------------------------------------------------------
/src/elements/element.rs:
--------------------------------------------------------------------------------
1 | use std::any::Any;
2 | use std::collections::HashMap;
3 | use std::hash::{Hash, Hasher};
4 | use std::mem;
5 | use std::sync::{Arc, Mutex};
6 |
7 | use glutin;
8 | use glutin::{ScanCode, VirtualKeyCode};
9 | use webrender::api::*;
10 | use winit;
11 |
12 | use crate::gui::font;
13 | use crate::gui::properties;
14 |
15 | #[derive(Debug, Clone)]
16 | pub enum PrimitiveEvent {
17 | Exit,
18 | CursorEntered,
19 | CursorLeft,
20 | CursorMoved(properties::Position),
21 | Button(
22 | properties::Position,
23 | properties::Button,
24 | properties::ButtonState,
25 | properties::Modifiers,
26 | ),
27 | Char(char),
28 | KeyInput(
29 | Option,
30 | ScanCode,
31 | properties::ButtonState,
32 | properties::Modifiers,
33 | ),
34 | SetFocus(bool),
35 | Resized(glutin::dpi::LogicalSize),
36 | DPI(f64),
37 | HoverBegin(Vec),
38 | HoverEnd(Vec),
39 | }
40 |
41 | #[derive(Debug, Clone, Eq)]
42 | pub enum ElementEvent {
43 | Clicked,
44 | FocusChange,
45 | HoverBegin,
46 | HoverEnd,
47 | }
48 |
49 | impl Hash for ElementEvent {
50 | fn hash(&self, state: &mut H) {
51 | mem::discriminant(self).hash(state)
52 | }
53 | }
54 |
55 | impl PartialEq for ElementEvent {
56 | fn eq(&self, other: &ElementEvent) -> bool {
57 | mem::discriminant(self) == mem::discriminant(other)
58 | }
59 | }
60 |
61 | /*impl Copy for FnMut(&mut Element, &Any) -> bool {
62 |
63 | }*/
64 |
65 | //pub type EventFn = fn(&mut Element, &Any) -> bool;
66 | pub type EventClosure = dyn FnMut(&mut dyn Element, &dyn Any) -> bool;
67 |
68 | #[derive(Clone)]
69 | pub struct EventFn(Arc>);
70 | //it should be safe since Element will always be within a lock.
71 | //sending it as arc.mutex.element might end up in a deadlock
72 | unsafe impl Send for EventFn {}
73 | unsafe impl Sync for EventFn {}
74 |
75 | use std::ops::DerefMut;
76 | impl EventFn {
77 | pub fn new(f: Arc>) -> EventFn {
78 | EventFn(f)
79 | }
80 |
81 | pub fn call(&mut self, _e: &mut dyn Element, _d: &dyn Any) -> bool {
82 | //let h = ;//.unwrap()(_d)
83 | if let Ok(mut f) = self.0.lock() {
84 | let x = f.deref_mut();
85 | x(_e, _d)
86 | } else {
87 | false
88 | }
89 | }
90 | }
91 |
92 | //impl Copy for EventFn{}
93 | //impl Copy for FnMut<&mut Element, &Any> {}
94 |
95 | pub type EventHandlers = HashMap;
96 |
97 | pub trait Element: Send + Sync {
98 | fn get_ext_id(&self) -> u64;
99 | fn set(&mut self, prop: properties::Property);
100 | //fn get(&self, prop: &properties::Property) -> Option<&properties::Property>;
101 | fn get_properties(&self) -> properties::Properties;
102 | fn render(
103 | &mut self,
104 | api: &RenderApi,
105 | builder: &mut DisplayListBuilder,
106 | extent: properties::Extent,
107 | font_store: &mut font::FontStore,
108 | props: Option>,
109 | id: &mut properties::IdGenerator
110 | );
111 | fn get_bounds(&self) -> properties::Extent;
112 | #[allow(unused)]
113 | fn on_primitive_event(&mut self, item_tag: &[ItemTag], e: PrimitiveEvent) -> bool;
114 | #[allow(unused)]
115 | fn set_handler(&mut self, e: ElementEvent, f: EventFn) {}
116 | #[allow(unused)]
117 | fn exec_handler(&mut self, e: ElementEvent, d: &dyn Any) -> bool {
118 | false
119 | }
120 | fn as_any(&self) -> &dyn Any;
121 | fn as_any_mut(&mut self) -> &mut dyn Any;
122 | #[allow(unused)]
123 | fn on_event(
124 | &mut self,
125 | event: winit::WindowEvent,
126 | api: &RenderApi,
127 | document_id: DocumentId,
128 | ) -> bool {
129 | false
130 | }
131 | }
132 |
133 | pub type ElementObj = Arc>;
134 |
135 | pub trait HasChildren: Element {
136 | #[allow(unused)]
137 | fn get_child(&self, i: u32) -> Option>> {
138 | None
139 | }
140 | /*#[allow(unused)]
141 | fn get_child_mut(&mut self, i:u32) -> Option {None}*/
142 | #[allow(unused)]
143 | fn append(&mut self, e: Arc>) -> Option>> {
144 | None
145 | }
146 | }
147 |
148 | pub trait CanDisable: Element {
149 | fn set_enabled(&mut self, _: bool);
150 | fn get_enabled(&self) -> bool;
151 | }
152 |
--------------------------------------------------------------------------------
/src/elements/scrollbox.rs:
--------------------------------------------------------------------------------
1 | use std::any::Any;
2 | use std::mem;
3 | use std::sync::{Arc, Mutex};
4 |
5 | use webrender::api::*;
6 |
7 | use crate::elements::element::*;
8 | use crate::gui::font;
9 | use crate::gui::properties;
10 | use crate::util::*;
11 |
12 | pub struct ScrollBox {
13 | ext_id: u64,
14 | child: Option>>,
15 | props: properties::Properties,
16 | bounds: properties::Extent,
17 | content: properties::Extent,
18 | handlers: EventHandlers,
19 | }
20 |
21 | impl ScrollBox {
22 | pub fn new() -> Self {
23 | let mut props = properties::Properties::new();
24 | props.default();
25 | ScrollBox {
26 | ext_id: 0,
27 | child: None,
28 | props,
29 | bounds: properties::Extent {
30 | x: 0.0,
31 | y: 0.0,
32 | w: 0.0,
33 | h: 0.0,
34 | dpi: 0.0,
35 | },
36 | content: properties::Extent {
37 | x: 0.0,
38 | y: 0.0,
39 | w: 0.0,
40 | h: 0.0,
41 | dpi: 0.0,
42 | },
43 | handlers: EventHandlers::new(),
44 | }
45 | }
46 | }
47 |
48 | impl Element for ScrollBox {
49 | fn get_ext_id(&self) -> u64 {
50 | self.ext_id
51 | }
52 |
53 | fn render(
54 | &mut self,
55 | api: &RenderApi,
56 | builder: &mut DisplayListBuilder,
57 | extent: properties::Extent,
58 | font_store: &mut font::FontStore,
59 | _props: Option>,
60 | gen: &mut properties::IdGenerator,
61 | ) {
62 | let bgcolor = self.props.get_bg_color();
63 |
64 | let _id = gen.get();
65 | self.ext_id = _id;
66 |
67 | let mut bounds = properties::Extent {
68 | x: 0.0,
69 | y: 0.0,
70 | w: 0.0,
71 | h: 0.0,
72 | dpi: 0.0,
73 | };
74 |
75 | self.bounds = extent.clone();
76 |
77 | builder.push_stacking_context(
78 | &LayoutPrimitiveInfo::new((extent.x, extent.y).by(0.0, 0.0)),
79 | None,
80 | TransformStyle::Flat,
81 | MixBlendMode::Normal,
82 | &[],
83 | RasterSpace::Screen,
84 | );
85 |
86 | let mut info = LayoutPrimitiveInfo::new((0.0, 0.0).by(extent.w, extent.h));
87 | info.tag = Some((_id, 0));
88 | builder.push_rect(&info, bgcolor);
89 |
90 | let pipeline_id = builder.pipeline_id;
91 | let scroll_frame = builder.define_scroll_frame(
92 | Some(ExternalScrollId(_id, pipeline_id)),
93 | (0.0, 0.0).by(self.content.w, self.content.h),
94 | (0.0, 0.0).by(extent.w, extent.h),
95 | vec![],
96 | None,
97 | ScrollSensitivity::ScriptAndInputEvents,
98 | );
99 | builder.push_clip_id(scroll_frame);
100 |
101 | if let Some(ref mut elm) = self.child {
102 | match elm.lock() {
103 | Ok(ref mut elm) => {
104 | elm.render(
105 | api,
106 | builder,
107 | properties::Extent {
108 | x: 0.0,
109 | y: 0.0,
110 | w: extent.w,
111 | h: extent.h,
112 | dpi: extent.dpi,
113 | },
114 | font_store,
115 | None,
116 | gen,
117 | );
118 | bounds = elm.get_bounds();
119 | }
120 | Err(_err_str) => panic!("unable to lock element : {}", _err_str),
121 | }
122 | }
123 |
124 | builder.pop_clip_id(); //scroll frame
125 | builder.pop_stacking_context();
126 |
127 | self.content = bounds;
128 | }
129 |
130 | fn set(&mut self, prop: properties::Property) {
131 | self.props.set(prop);
132 | }
133 |
134 | /*fn get(&self, prop: &properties::Property) -> Option<&properties::Property> {
135 | self.props.get(&prop)
136 | }*/
137 |
138 | fn get_properties(&self) -> properties::Properties {
139 | self.props.clone()
140 | }
141 |
142 | fn get_bounds(&self) -> properties::Extent {
143 | self.bounds.clone()
144 | }
145 |
146 | fn on_primitive_event(&mut self, ext_ids: &[ItemTag], e: PrimitiveEvent) -> bool {
147 | let mut handled = false;
148 | if let Some(ref mut _child_elm) = self.child {
149 | match (&e, _child_elm.lock()) {
150 | (PrimitiveEvent::SetFocus(_), Ok(ref mut _child_elm)) => {
151 | if ext_ids.len() > 1
152 | && ext_ids[0].0 == self.ext_id
153 | && ext_ids[1].0 == _child_elm.get_ext_id()
154 | {
155 | _child_elm
156 | .on_primitive_event(&ext_ids[1..], PrimitiveEvent::SetFocus(true));
157 | } else {
158 | _child_elm.on_primitive_event(&[], PrimitiveEvent::SetFocus(false));
159 | }
160 | }
161 | (PrimitiveEvent::Char(_c), Ok(ref mut _child_elm)) => {
162 | handled = _child_elm.on_primitive_event(&[], e.clone());
163 | }
164 | // XXX: These used to be unreachable; they trigger a panic in the WRRenderBackend thread
165 | // (PrimitiveEvent::HoverBegin(_n_tags), Ok(ref mut _child_elm)) => {
166 | // _child_elm.on_primitive_event(&[],e.clone());
167 | // },
168 | // (PrimitiveEvent::HoverEnd(_o_tags), Ok(ref mut _child_elm)) => {
169 | // _child_elm.on_primitive_event(&[],e.clone());
170 | // },
171 | (_, Ok(ref mut _child_elm)) => {
172 | if !handled {
173 | if ext_ids.len() == 1 {
174 | handled = _child_elm.on_primitive_event(&[], e.clone());
175 | } else if ext_ids.len() > 1 {
176 | handled = _child_elm.on_primitive_event(&ext_ids[1..], e.clone());
177 | }
178 | }
179 | }
180 | (_, Err(_err_str)) => {
181 | //this should be unreachable
182 | panic!("unable to lock element : {}", _err_str)
183 | }
184 | }
185 | }
186 | // if none of the children handled the event
187 | // see if you can handle it here
188 | if !handled {
189 | if let PrimitiveEvent::Button(_p, _b, _s, m) = e {
190 | handled = self.exec_handler(ElementEvent::Clicked, &m);
191 | }
192 | }
193 | handled
194 | }
195 |
196 | fn set_handler(&mut self, _e: ElementEvent, _f: EventFn) {
197 | self.handlers.insert(_e, _f);
198 | }
199 |
200 | fn exec_handler(&mut self, _e: ElementEvent, _d: &dyn Any) -> bool {
201 | let h = self.handlers.get_mut(&_e).cloned();
202 | if let Some(mut h) = h {
203 | h.call(self, _d)
204 | } else {
205 | false
206 | }
207 | }
208 |
209 | fn as_any(&self) -> &dyn Any {
210 | self
211 | }
212 | fn as_any_mut(&mut self) -> &mut dyn Any {
213 | self
214 | }
215 | }
216 |
217 | impl Default for ScrollBox {
218 | fn default() -> Self {
219 | Self::new()
220 | }
221 | }
222 |
223 | impl HasChildren for ScrollBox {
224 | #[allow(unused)]
225 | fn get_child(&self, i: u32) -> Option>> {
226 | None
227 | }
228 | #[allow(unused)]
229 | //fn get_child_mut(&mut self, i:u32) -> Option<&mut Element> {None}
230 | fn append(&mut self, e: Arc>) -> Option>> {
231 | let mut ret = Some(e);
232 | mem::swap(&mut self.child, &mut ret);
233 | ret
234 | }
235 | }
236 |
--------------------------------------------------------------------------------
/src/elements/button.rs:
--------------------------------------------------------------------------------
1 | use std::any::Any;
2 | use std::sync::Arc;
3 |
4 | use webrender::api::*;
5 |
6 | use crate::elements::element::*;
7 | use crate::gui::font;
8 | use crate::gui::properties;
9 |
10 | pub struct Button {
11 | ext_id: u64,
12 | value: Vec,
13 | props: properties::Properties,
14 | bounds: properties::Extent,
15 | text_bounds: properties::Extent,
16 | event_handlers: EventHandlers,
17 | drawn: u8,
18 | hovering: bool,
19 | enabled: bool,
20 | }
21 |
22 | impl Button {
23 | pub fn new(s: String) -> Self {
24 | let mut props = properties::Properties::new();
25 | props.default();
26 | props
27 | .set(properties::Property::TextAlign(properties::Align::Middle))
28 | .set(properties::Property::BgColor(ColorF::new(
29 | 0.8, 0.9, 0.9, 1.0,
30 | )))
31 | .set(properties::Property::Color(ColorF::new(0.2, 0.2, 0.2, 1.0)))
32 | .set(properties::Property::HoverBgColor(ColorF::new(
33 | 0.6, 0.7, 0.7, 1.0,
34 | )));
35 | Button {
36 | ext_id: 0,
37 | value: s.chars().collect(),
38 | props,
39 | bounds: properties::Extent::new(),
40 | text_bounds: properties::Extent::new(),
41 | event_handlers: EventHandlers::new(),
42 | drawn: 0,
43 | hovering: false,
44 | enabled: true,
45 | }
46 | }
47 |
48 | pub fn set_value(&mut self, s: String) {
49 | self.value = s.chars().collect();
50 | //self.cache.clear();
51 | self.drawn = 0;
52 | }
53 |
54 | pub fn get_value(&self) -> String {
55 | self.value.clone().iter().collect()
56 | }
57 |
58 | fn get_width_sums(&mut self) -> (f32, f32) {
59 | let left = self.props.get_left();
60 | let right = self.props.get_right();
61 | let width = self.props.get_width();
62 |
63 | let mut stretchy: f32 = 0.0;
64 | let mut pixel: f32 = 0.0;
65 |
66 | match left {
67 | properties::Unit::Stretch(_s) => stretchy += _s,
68 | properties::Unit::Pixel(_p) => pixel += _p,
69 | _ => (),
70 | }
71 |
72 | match right {
73 | properties::Unit::Stretch(_s) => stretchy += _s,
74 | properties::Unit::Pixel(_p) => pixel += _p,
75 | _ => (),
76 | }
77 |
78 | match width {
79 | properties::Unit::Stretch(_s) => stretchy += _s,
80 | properties::Unit::Pixel(_p) => pixel += _p,
81 | _ => (),
82 | }
83 |
84 | (pixel, stretchy)
85 | }
86 |
87 | fn get_height_sums(&mut self) -> (f32, f32) {
88 | let top = self.props.get_top();
89 | let bottom = self.props.get_bottom();
90 |
91 | let mut stretchy: f32 = 0.0;
92 | let num_lines = {
93 | let tmp: String = self.value.iter().collect();
94 | tmp.lines().count() as i32
95 | };
96 | let mut pixel: f32 = (self.props.get_size() * num_lines) as f32;
97 |
98 | match top {
99 | properties::Unit::Stretch(_s) => stretchy += _s,
100 | properties::Unit::Pixel(_p) => pixel += _p,
101 | _ => (),
102 | }
103 |
104 | match bottom {
105 | properties::Unit::Stretch(_s) => stretchy += _s,
106 | properties::Unit::Pixel(_p) => pixel += _p,
107 | _ => (),
108 | }
109 |
110 | (pixel, stretchy)
111 | }
112 | }
113 |
114 | impl Element for Button {
115 | fn get_ext_id(&self) -> u64 {
116 | self.ext_id
117 | }
118 |
119 | fn set(&mut self, prop: properties::Property) {
120 | self.props.set(prop);
121 | }
122 |
123 | /*fn get(&self, prop: &properties::Property) -> Option<&properties::Property> {
124 | self.props.get(&prop)
125 | }*/
126 |
127 | fn get_properties(&self) -> properties::Properties {
128 | self.props.clone()
129 | }
130 |
131 | fn render(
132 | &mut self,
133 | _api: &RenderApi,
134 | builder: &mut DisplayListBuilder,
135 | extent: properties::Extent,
136 | font_store: &mut font::FontStore,
137 | _props: Option>,
138 | gen: &mut properties::IdGenerator,
139 | ) {
140 | let _id = gen.get();
141 | self.ext_id = _id;
142 |
143 | let mut color = self.props.get_color();
144 | let mut bgcolor = self.props.get_bg_color();
145 | let width = self.props.get_width();
146 | let height = self.props.get_height();
147 | let size = self.props.get_size() as f32;
148 | let family = self.props.get_family();
149 | let text_align = self.props.get_text_align();
150 | let top = self.props.get_top();
151 | let right = self.props.get_right();
152 | let bottom = self.props.get_bottom();
153 | let left = self.props.get_left();
154 |
155 | if self.hovering && self.enabled {
156 | color = self.props.get_hover_color();
157 | bgcolor = self.props.get_hover_bg_color();
158 | }
159 |
160 | let (wp_sum, ws_sum) = self.get_width_sums();
161 | let mut remaining_width = extent.w - wp_sum;
162 | if remaining_width < 0.0 {
163 | remaining_width = 0.0;
164 | }
165 | let mut w_stretchy_factor = remaining_width / ws_sum;
166 | if w_stretchy_factor.is_nan() || w_stretchy_factor.is_infinite() {
167 | w_stretchy_factor = 0.0;
168 | }
169 |
170 | let (hp_sum, hs_sum) = self.get_height_sums();
171 | let mut remaining_height = extent.h - hp_sum;
172 | if remaining_height < 0.0 {
173 | remaining_height = 0.0;
174 | }
175 | let mut h_stretchy_factor = remaining_height / hs_sum;
176 | if h_stretchy_factor.is_nan() || h_stretchy_factor.is_infinite() {
177 | h_stretchy_factor = 0.0;
178 | }
179 |
180 | let mut calc_x = extent.x;
181 | let mut calc_y = extent.y;
182 | let mut calc_w = extent.w;
183 | let mut calc_h = extent.h;
184 |
185 | match top {
186 | properties::Unit::Pixel(_p) => {
187 | calc_y += _p;
188 | calc_h -= _p;
189 | }
190 | properties::Unit::Stretch(_s) => {
191 | calc_y += _s * h_stretchy_factor;
192 | calc_h -= _s * h_stretchy_factor;
193 | }
194 | _ => (),
195 | }
196 | match bottom {
197 | properties::Unit::Pixel(_p) => calc_h -= _p,
198 | properties::Unit::Stretch(_s) => calc_h -= _s * h_stretchy_factor,
199 | _ => (),
200 | }
201 | match left {
202 | properties::Unit::Pixel(_p) => {
203 | calc_x += _p;
204 | calc_w -= _p;
205 | }
206 | properties::Unit::Stretch(_s) => {
207 | calc_x += _s * w_stretchy_factor;
208 | calc_w -= _s * w_stretchy_factor;
209 | }
210 | _ => (),
211 | }
212 | match right {
213 | properties::Unit::Pixel(_p) => calc_w -= _p,
214 | properties::Unit::Stretch(_s) => calc_w -= _s * w_stretchy_factor,
215 | _ => (),
216 | }
217 |
218 | let text_y = calc_y + (calc_h - self.text_bounds.h) / 2.0;
219 | let metrics = font_store.get_font_metrics(&family);
220 | let baseline = match metrics {
221 | Some(metrics) => {
222 | let tmp = metrics.ascent - metrics.descent;
223 | let tmp = size / tmp;
224 | tmp * (metrics.ascent)
225 | }
226 | None => size,
227 | };
228 |
229 | let mut paras = font::Paragraphs::from_chars(&self.value);
230 | paras.shape(
231 | calc_x,
232 | text_y,
233 | calc_w,
234 | calc_h,
235 | size,
236 | baseline,
237 | &family,
238 | &text_align,
239 | );
240 |
241 | self.text_bounds = paras.get_extent();
242 |
243 | let mut calc_w = self.text_bounds.w;
244 | let mut calc_h = self.text_bounds.h;
245 |
246 | calc_w = match width {
247 | properties::Unit::Extent => extent.w,
248 | properties::Unit::Pixel(px) => px,
249 | properties::Unit::Stretch(s) => s * extent.w,
250 | properties::Unit::Natural => calc_w,
251 | };
252 |
253 | calc_h = match height {
254 | properties::Unit::Extent => extent.h,
255 | properties::Unit::Pixel(px) => px,
256 | properties::Unit::Stretch(s) => s * extent.h,
257 | properties::Unit::Natural => calc_h,
258 | };
259 |
260 | self.bounds = properties::Extent {
261 | x: extent.x,
262 | y: extent.y,
263 | w: calc_w,
264 | h: calc_h,
265 | dpi: extent.dpi,
266 | };
267 |
268 | let mut info = LayoutPrimitiveInfo::new(LayoutRect::new(
269 | LayoutPoint::new(extent.x, extent.y),
270 | LayoutSize::new(self.bounds.w, self.bounds.h),
271 | ));
272 | info.tag = Some((_id, 0));
273 | builder.push_rect(&info, bgcolor);
274 |
275 | let info = LayoutPrimitiveInfo::new(LayoutRect::new(
276 | LayoutPoint::new(self.text_bounds.x, self.text_bounds.y),
277 | LayoutSize::new(self.text_bounds.w, self.text_bounds.h),
278 | ));
279 |
280 | let glyphs = paras.glyphs();
281 |
282 | let (_, fi_key) = font_store.get_font_instance(&family, size as i32);
283 | builder.push_text(&info, &glyphs, fi_key, color, Some(GlyphOptions::default()));
284 | }
285 |
286 | fn get_bounds(&self) -> properties::Extent {
287 | self.bounds.clone()
288 | }
289 |
290 | fn on_primitive_event(&mut self, ext_ids: &[ItemTag], e: PrimitiveEvent) -> bool {
291 | let mut handled = false;
292 |
293 | match e {
294 | PrimitiveEvent::Button(_p, b, s, m) => {
295 | self.drawn = 0;
296 | if ext_ids.len() == 1
297 | && ext_ids[0].0 == self.ext_id
298 | && b == properties::Button::Left
299 | && s == properties::ButtonState::Released
300 | && self.enabled
301 | {
302 | handled = self.exec_handler(ElementEvent::Clicked, &m);
303 | }
304 | }
305 | PrimitiveEvent::HoverBegin(n_tags) => {
306 | let matched = n_tags.iter().find(|x| x.0 == self.ext_id);
307 | if matched.is_some() {
308 | self.hovering = true;
309 | }
310 | }
311 | PrimitiveEvent::HoverEnd(o_tags) => {
312 | let matched = o_tags.iter().find(|x| x.0 == self.ext_id);
313 | if matched.is_some() {
314 | self.hovering = false;
315 | }
316 | }
317 | _ => (),
318 | }
319 |
320 | handled
321 | }
322 |
323 | fn set_handler(&mut self, _e: ElementEvent, _f: EventFn) {
324 | self.event_handlers.insert(_e, _f);
325 | }
326 |
327 | fn exec_handler(&mut self, _e: ElementEvent, _d: &dyn Any) -> bool {
328 | let h = self.event_handlers.get_mut(&_e).cloned();
329 | if let Some(mut h) = h {
330 | h.call(self, _d)
331 | } else {
332 | false
333 | }
334 | }
335 |
336 | fn as_any(&self) -> &dyn Any {
337 | self
338 | }
339 | fn as_any_mut(&mut self) -> &mut dyn Any {
340 | self
341 | }
342 | }
343 |
344 | impl CanDisable for Button {
345 | fn set_enabled(&mut self, value: bool) {
346 | self.enabled = value;
347 | }
348 |
349 | fn get_enabled(&self) -> bool {
350 | self.enabled
351 | }
352 | }
353 |
--------------------------------------------------------------------------------
/src/elements/vbox.rs:
--------------------------------------------------------------------------------
1 | use std::any::Any;
2 | use std::sync::{Arc, Mutex};
3 |
4 | use webrender::api::*;
5 |
6 | use crate::elements::element::*;
7 | use crate::gui::font;
8 | use crate::gui::properties;
9 | use crate::util::*;
10 |
11 | pub struct VBox {
12 | ext_id: u64,
13 | children: Vec>>,
14 | props: properties::Properties,
15 | bounds: properties::Extent,
16 | handlers: EventHandlers,
17 | }
18 |
19 | impl VBox {
20 | pub fn new() -> Self {
21 | let mut props = properties::Properties::new();
22 | props.default();
23 | VBox {
24 | ext_id: 0,
25 | children: Vec::new(),
26 | props,
27 | bounds: properties::Extent {
28 | x: 0.0,
29 | y: 0.0,
30 | w: 0.0,
31 | h: 0.0,
32 | dpi: 0.0,
33 | },
34 | handlers: EventHandlers::new(),
35 | }
36 | }
37 |
38 | fn get_height_sums(&mut self) -> (f32, f32) {
39 | let top = self.props.get_top();
40 | let bottom = self.props.get_bottom();
41 |
42 | let mut stretchy: f32 = 0.0;
43 | let mut pixel: f32 = 0.0;
44 |
45 | match top {
46 | properties::Unit::Stretch(_s) => stretchy += _s,
47 | properties::Unit::Pixel(_p) => pixel += _p,
48 | _ => (),
49 | }
50 |
51 | match bottom {
52 | properties::Unit::Stretch(_s) => stretchy += _s,
53 | properties::Unit::Pixel(_p) => pixel += _p,
54 | _ => (),
55 | }
56 |
57 | for elm in self.children.iter() {
58 | if let Ok(ref _e) = elm.lock() {
59 | let _p = _e.get_bounds().h;
60 |
61 | match _e.get_properties().get_height() {
62 | properties::Unit::Stretch(_s) => stretchy += _s,
63 | _ => {
64 | if !_p.is_nan() && !_p.is_infinite() {
65 | pixel += _p;
66 | }
67 | }
68 | }
69 | }
70 | }
71 |
72 | (pixel, stretchy)
73 | }
74 |
75 | fn get_width_sums(&mut self) -> (f32, f32) {
76 | let left = self.props.get_left();
77 | let right = self.props.get_right();
78 | let width = self.props.get_width();
79 |
80 | let mut stretchy: f32 = 0.0;
81 | let mut pixel: f32 = 0.0;
82 |
83 | match left {
84 | properties::Unit::Stretch(_s) => stretchy += _s,
85 | properties::Unit::Pixel(_p) => pixel += _p,
86 | _ => (),
87 | }
88 |
89 | match right {
90 | properties::Unit::Stretch(_s) => stretchy += _s,
91 | properties::Unit::Pixel(_p) => pixel += _p,
92 | _ => (),
93 | }
94 |
95 | match width {
96 | properties::Unit::Stretch(_s) => stretchy += _s,
97 | properties::Unit::Pixel(_p) => pixel += _p,
98 | _ => (),
99 | }
100 |
101 | (pixel, stretchy)
102 | }
103 | }
104 |
105 | impl Default for VBox {
106 | fn default() -> Self {
107 | Self::new()
108 | }
109 | }
110 |
111 | impl Element for VBox {
112 | fn get_ext_id(&self) -> u64 {
113 | self.ext_id
114 | }
115 |
116 | fn set(&mut self, prop: properties::Property) {
117 | self.props.set(prop);
118 | }
119 |
120 | /*fn get(&self, prop: &properties::Property) -> Option<&properties::Property> {
121 | self.props.get(&prop)
122 | }*/
123 |
124 | fn get_properties(&self) -> properties::Properties {
125 | self.props.clone()
126 | }
127 |
128 | fn render(
129 | &mut self,
130 | api: &RenderApi,
131 | builder: &mut DisplayListBuilder,
132 | extent: properties::Extent,
133 | font_store: &mut font::FontStore,
134 | _props: Option>,
135 | gen: &mut properties::IdGenerator,
136 | ) {
137 | let bgcolor = self.props.get_bg_color();
138 | let top = self.props.get_top();
139 | let bottom = self.props.get_bottom();
140 | let left = self.props.get_left();
141 | let right = self.props.get_right();
142 | let width = self.props.get_width();
143 | let height = self.props.get_height();
144 |
145 | let (wp_sum, ws_sum) = self.get_width_sums();
146 | let mut remaining_width = extent.w - wp_sum;
147 | if remaining_width < 0.0 {
148 | remaining_width = 0.0;
149 | }
150 | let mut w_stretchy_factor = remaining_width / ws_sum;
151 | if w_stretchy_factor.is_nan() {
152 | w_stretchy_factor = 0.0;
153 | }
154 |
155 | let (hp_sum, hs_sum) = self.get_height_sums();
156 | let mut remaining_height = extent.h - hp_sum;
157 | if remaining_height < 0.0 {
158 | remaining_height = 0.0;
159 | }
160 | let mut h_stretchy_factor = remaining_height / hs_sum;
161 | if h_stretchy_factor.is_nan() {
162 | h_stretchy_factor = 0.0;
163 | }
164 |
165 | let _id = gen.get();
166 | self.ext_id = _id;
167 |
168 | let mut info = LayoutPrimitiveInfo::new(
169 | (self.bounds.x, self.bounds.y).by(self.bounds.w, self.bounds.h),
170 | );
171 | info.tag = Some((_id, 0));
172 | builder.push_rect(&info, bgcolor);
173 |
174 | let mut next_x = 0.0;
175 | let mut next_y = 0.0;
176 |
177 | match top {
178 | properties::Unit::Stretch(_s) => next_y = h_stretchy_factor * _s,
179 | properties::Unit::Pixel(_p) => next_y = _p,
180 | _ => (),
181 | }
182 |
183 | match left {
184 | properties::Unit::Stretch(_s) => next_x = w_stretchy_factor * _s,
185 | properties::Unit::Pixel(_p) => next_x = _p,
186 | _ => (),
187 | }
188 |
189 | match height {
190 | properties::Unit::Stretch(_s) => remaining_height = _s * h_stretchy_factor,
191 | properties::Unit::Pixel(_p) => remaining_height = _p,
192 | _ => (),
193 | }
194 |
195 | match width {
196 | properties::Unit::Stretch(_s) => remaining_width = _s * w_stretchy_factor,
197 | properties::Unit::Pixel(_p) => remaining_width = _p,
198 | _ => (),
199 | }
200 |
201 | for elm in self.children.iter_mut() {
202 | let mut child_extent = properties::Extent {
203 | x: next_x + extent.x,
204 | y: next_y + extent.y,
205 | w: remaining_width,
206 | h: remaining_height,
207 | dpi: extent.dpi,
208 | };
209 |
210 | match elm.lock() {
211 | Ok(ref mut elm) => {
212 | let e_height = elm.get_properties().get_height();
213 |
214 | match e_height {
215 | properties::Unit::Pixel(_p) => {
216 | child_extent.h = _p;
217 | }
218 | properties::Unit::Stretch(_s) => {
219 | child_extent.h = h_stretchy_factor;
220 | }
221 | _ => (),
222 | }
223 |
224 | elm.render(api, builder, child_extent, font_store, None, gen);
225 | let _ex = elm.get_bounds();
226 | next_y += _ex.h;
227 | }
228 | Err(_err_str) => panic!("unable to lock element : {}", _err_str),
229 | }
230 | }
231 |
232 | match bottom {
233 | properties::Unit::Stretch(_s) => next_y += h_stretchy_factor * _s,
234 | properties::Unit::Pixel(_p) => next_y += _p,
235 | _ => (),
236 | }
237 |
238 | next_x += remaining_width;
239 | match right {
240 | properties::Unit::Stretch(_s) => next_x += w_stretchy_factor * _s,
241 | properties::Unit::Pixel(_p) => next_x += _p,
242 | _ => (),
243 | }
244 |
245 | // TODO: Remove
246 | // only here for debugging.
247 | /*if next_y == 0.0 {
248 | next_y = extent.h;
249 | }
250 | if next_x == 0.0 {
251 | next_x = extent.w;
252 | }*/
253 |
254 | self.bounds = properties::Extent {
255 | x: extent.x,
256 | y: extent.y,
257 | w: next_x,
258 | h: next_y,
259 | dpi: extent.dpi,
260 | };
261 | }
262 |
263 | fn get_bounds(&self) -> properties::Extent {
264 | self.bounds.clone()
265 | }
266 |
267 | fn on_primitive_event(&mut self, ext_ids: &[ItemTag], e: PrimitiveEvent) -> bool {
268 | let mut handled = false;
269 | for _child_elm in self.children.iter_mut() {
270 | match (&e, _child_elm.lock()) {
271 | (PrimitiveEvent::SetFocus(_), Ok(ref mut _child_elm)) => {
272 | if ext_ids.len() > 1
273 | && ext_ids[0].0 == self.ext_id
274 | && ext_ids[1].0 == _child_elm.get_ext_id()
275 | {
276 | _child_elm
277 | .on_primitive_event(&ext_ids[1..], PrimitiveEvent::SetFocus(true));
278 | } else {
279 | _child_elm.on_primitive_event(&[], PrimitiveEvent::SetFocus(false));
280 | }
281 | }
282 | (PrimitiveEvent::Char(_c), Ok(ref mut _child_elm)) => {
283 | handled = _child_elm.on_primitive_event(&[], e.clone());
284 | if handled {
285 | break;
286 | }
287 | }
288 | (PrimitiveEvent::HoverBegin(_), Ok(ref mut _child_elm)) => {
289 | _child_elm.on_primitive_event(&[], e.clone());
290 | }
291 | (PrimitiveEvent::HoverEnd(_), Ok(ref mut _child_elm)) => {
292 | _child_elm.on_primitive_event(&[], e.clone());
293 | }
294 | (_, Ok(ref mut _child_elm)) => {
295 | if !handled {
296 | if ext_ids.len() == 1 {
297 | handled = _child_elm.on_primitive_event(&[], e.clone());
298 | } else if ext_ids.len() > 1 {
299 | handled = _child_elm.on_primitive_event(&ext_ids[1..], e.clone());
300 | }
301 | }
302 | }
303 | (_, Err(_err_str)) => {
304 | //this should be unreachable
305 | panic!("unable to lock element : {}", _err_str)
306 | }
307 | }
308 | }
309 | // if none of the children handled the event
310 | // see if you can handle it here
311 | if !handled {
312 | if let PrimitiveEvent::Button(_p, _b, _s, m) = e {
313 | handled = self.exec_handler(ElementEvent::Clicked, &m);
314 | }
315 | }
316 | handled
317 | }
318 |
319 | fn set_handler(&mut self, _e: ElementEvent, _f: EventFn) {
320 | self.handlers.insert(_e, _f);
321 | }
322 |
323 | fn exec_handler(&mut self, _e: ElementEvent, _d: &dyn Any) -> bool {
324 | let h = self.handlers.get_mut(&_e).cloned();
325 | if let Some(mut h) = h {
326 | h.call(self, _d)
327 | } else {
328 | false
329 | }
330 | }
331 |
332 | fn as_any(&self) -> &dyn Any {
333 | self
334 | }
335 | fn as_any_mut(&mut self) -> &mut dyn Any {
336 | self
337 | }
338 | }
339 |
340 | impl HasChildren for VBox {
341 | #[allow(unused)]
342 | fn get_child(&self, i: u32) -> Option>> {
343 | None
344 | }
345 | #[allow(unused)]
346 | //fn get_child_mut(&mut self, i:u32) -> Option<&mut Element> {None}
347 | fn append(&mut self, e: Arc>) -> Option>> {
348 | self.children.push(e);
349 | None
350 | }
351 | }
352 |
--------------------------------------------------------------------------------
/src/elements/hbox.rs:
--------------------------------------------------------------------------------
1 | use std::any::Any;
2 | use std::sync::{Arc, Mutex};
3 |
4 | use webrender::api::*;
5 |
6 | use crate::elements::element::*;
7 | use crate::gui::font;
8 | use crate::gui::properties;
9 | use crate::util::*;
10 |
11 | pub struct HBox {
12 | ext_id: u64,
13 | children: Vec>>,
14 | props: properties::Properties,
15 | bounds: properties::Extent,
16 | handlers: EventHandlers,
17 | }
18 |
19 | impl HBox {
20 | pub fn new() -> Self {
21 | let mut props = properties::Properties::new();
22 | props.default();
23 | HBox {
24 | ext_id: 0,
25 | children: Vec::new(),
26 | props,
27 | bounds: properties::Extent {
28 | x: 0.0,
29 | y: 0.0,
30 | w: 0.0,
31 | h: 0.0,
32 | dpi: 0.0,
33 | },
34 | handlers: EventHandlers::new(),
35 | }
36 | }
37 |
38 | fn get_width_sums(&mut self) -> (f32, f32) {
39 | let left = self.props.get_left();
40 | let right = self.props.get_right();
41 |
42 | let mut stretchy: f32 = 0.0;
43 | let mut pixel: f32 = 0.0;
44 |
45 | match left {
46 | properties::Unit::Stretch(_s) => stretchy += _s,
47 | properties::Unit::Pixel(_p) => pixel += _p,
48 | _ => (),
49 | }
50 |
51 | match right {
52 | properties::Unit::Stretch(_s) => stretchy += _s,
53 | properties::Unit::Pixel(_p) => pixel += _p,
54 | _ => (),
55 | }
56 |
57 | for elm in self.children.iter() {
58 | if let Ok(ref _e) = elm.lock() {
59 | let _p = _e.get_bounds().w;
60 |
61 | match _e.get_properties().get_width() {
62 | properties::Unit::Stretch(_s) => stretchy += _s,
63 | _ => {
64 | if !_p.is_nan() && !_p.is_infinite() {
65 | pixel += _p;
66 | }
67 | }
68 | }
69 | }
70 | }
71 |
72 | (pixel, stretchy)
73 | }
74 |
75 | fn get_height_sums(&mut self) -> (f32, f32) {
76 | let top = self.props.get_top();
77 | let bottom = self.props.get_bottom();
78 | let height = self.props.get_height();
79 |
80 | let mut stretchy: f32 = 0.0;
81 | let mut pixel: f32 = 0.0;
82 |
83 | match top {
84 | properties::Unit::Stretch(_s) => stretchy += _s,
85 | properties::Unit::Pixel(_p) => pixel += _p,
86 | _ => (),
87 | }
88 |
89 | match bottom {
90 | properties::Unit::Stretch(_s) => stretchy += _s,
91 | properties::Unit::Pixel(_p) => pixel += _p,
92 | _ => (),
93 | }
94 |
95 | match height {
96 | properties::Unit::Stretch(_s) => stretchy += _s,
97 | properties::Unit::Pixel(_p) => pixel += _p,
98 | _ => (),
99 | }
100 |
101 | (pixel, stretchy)
102 | }
103 | }
104 |
105 | impl Default for HBox {
106 | fn default() -> Self {
107 | Self::new()
108 | }
109 | }
110 |
111 | impl Element for HBox {
112 | fn get_ext_id(&self) -> u64 {
113 | self.ext_id
114 | }
115 |
116 | fn set(&mut self, prop: properties::Property) {
117 | self.props.set(prop);
118 | }
119 |
120 | /*fn get(&self, prop: &properties::Property) -> Option<&properties::Property> {
121 | self.props.get(&prop)
122 | }*/
123 |
124 | fn get_properties(&self) -> properties::Properties {
125 | self.props.clone()
126 | }
127 |
128 | fn render(
129 | &mut self,
130 | api: &RenderApi,
131 | builder: &mut DisplayListBuilder,
132 | extent: properties::Extent,
133 | font_store: &mut font::FontStore,
134 | _props: Option>,
135 | gen: &mut properties::IdGenerator,
136 | ) {
137 | let bgcolor = self.props.get_bg_color();
138 | let top = self.props.get_top();
139 | let bottom = self.props.get_bottom();
140 | let left = self.props.get_left();
141 | let right = self.props.get_right();
142 | let width = self.props.get_width();
143 | let height = self.props.get_height();
144 |
145 | let (hp_sum, hs_sum) = self.get_height_sums();
146 | let mut remaining_height = extent.h - hp_sum;
147 | if remaining_height < 0.0 {
148 | remaining_height = 0.0;
149 | }
150 | let mut h_stretchy_factor = remaining_height / hs_sum;
151 | if h_stretchy_factor.is_nan() {
152 | h_stretchy_factor = 0.0;
153 | }
154 |
155 | let (wp_sum, ws_sum) = self.get_width_sums();
156 | let mut remaining_width = extent.w - wp_sum;
157 | if remaining_width < 0.0 {
158 | remaining_width = 0.0;
159 | }
160 | let mut w_stretchy_factor = remaining_width / ws_sum;
161 | if w_stretchy_factor.is_nan() {
162 | w_stretchy_factor = 0.0;
163 | }
164 |
165 | // let mut remaining_width = extent.w;
166 | // let mut w_stretchy_factor = extent.w;
167 |
168 | let _id = gen.get();
169 | self.ext_id = _id;
170 |
171 | let mut info = LayoutPrimitiveInfo::new(
172 | (self.bounds.x, self.bounds.y).by(self.bounds.w, self.bounds.h),
173 | );
174 | info.tag = Some((_id, 0));
175 | builder.push_rect(&info, bgcolor);
176 |
177 | let mut next_x = 0.0;
178 | let mut next_y = 0.0;
179 |
180 | match left {
181 | properties::Unit::Stretch(_s) => next_x = w_stretchy_factor * _s,
182 | properties::Unit::Pixel(_p) => next_x = _p,
183 | _ => (),
184 | }
185 |
186 | match top {
187 | properties::Unit::Stretch(_s) => next_y = h_stretchy_factor * _s,
188 | properties::Unit::Pixel(_p) => next_y = _p,
189 | _ => (),
190 | }
191 |
192 | match width {
193 | properties::Unit::Stretch(_s) => remaining_width = _s * w_stretchy_factor,
194 | properties::Unit::Pixel(_p) => remaining_width = _p,
195 | _ => (),
196 | }
197 |
198 | match height {
199 | properties::Unit::Stretch(_s) => remaining_height = _s * h_stretchy_factor,
200 | properties::Unit::Pixel(_p) => remaining_height = _p,
201 | _ => (),
202 | }
203 |
204 | for elm in self.children.iter_mut() {
205 | let mut child_extent = properties::Extent {
206 | x: next_x + extent.x,
207 | y: next_y + extent.y,
208 | w: remaining_width,
209 | h: remaining_height,
210 | dpi: extent.dpi,
211 | };
212 |
213 | match elm.lock() {
214 | Ok(ref mut elm) => {
215 | let e_width = elm.get_properties().get_width();
216 |
217 | match e_width {
218 | properties::Unit::Pixel(_p) => {
219 | child_extent.w = _p;
220 | }
221 | properties::Unit::Stretch(_s) => {
222 | child_extent.w = w_stretchy_factor;
223 | }
224 | _ => (),
225 | }
226 |
227 | elm.render(api, builder, child_extent, font_store, None, gen);
228 | let _ex = elm.get_bounds();
229 | next_x += _ex.w;
230 | }
231 | Err(_err_str) => panic!("unable to lock element : {}", _err_str),
232 | }
233 | }
234 |
235 | match right {
236 | properties::Unit::Stretch(_s) => next_x += _s * w_stretchy_factor,
237 | properties::Unit::Pixel(_p) => next_x += _p,
238 | _ => (),
239 | }
240 |
241 | next_y += remaining_height;
242 | match bottom {
243 | properties::Unit::Stretch(_s) => next_y += _s * h_stretchy_factor,
244 | properties::Unit::Pixel(_p) => next_y += _p,
245 | _ => (),
246 | }
247 |
248 | // TODO: Remove
249 | // only here for debugging.
250 | if next_x == 0.0 {
251 | next_x = extent.w;
252 | }
253 |
254 | self.bounds = properties::Extent {
255 | x: extent.x,
256 | y: extent.y,
257 | w: next_x,
258 | h: next_y,
259 | dpi: extent.dpi,
260 | };
261 | }
262 |
263 | fn get_bounds(&self) -> properties::Extent {
264 | self.bounds.clone()
265 | }
266 |
267 | fn on_primitive_event(&mut self, ext_ids: &[ItemTag], e: PrimitiveEvent) -> bool {
268 | let mut handled = false;
269 | for _child_elm in self.children.iter_mut() {
270 | match (&e, _child_elm.lock()) {
271 | (PrimitiveEvent::SetFocus(_), Ok(ref mut _child_elm)) => {
272 | if ext_ids.len() > 1
273 | && ext_ids[0].0 == self.ext_id
274 | && ext_ids[1].0 == _child_elm.get_ext_id()
275 | {
276 | _child_elm
277 | .on_primitive_event(&ext_ids[1..], PrimitiveEvent::SetFocus(true));
278 | } else {
279 | _child_elm.on_primitive_event(&[], PrimitiveEvent::SetFocus(false));
280 | }
281 | }
282 | (PrimitiveEvent::Char(_c), Ok(ref mut _child_elm)) => {
283 | handled = _child_elm.on_primitive_event(&[], e.clone());
284 | if handled {
285 | break;
286 | }
287 | }
288 | (PrimitiveEvent::HoverBegin(_), Ok(ref mut _child_elm)) => {
289 | _child_elm.on_primitive_event(&[], e.clone());
290 | }
291 | (PrimitiveEvent::HoverEnd(_), Ok(ref mut _child_elm)) => {
292 | _child_elm.on_primitive_event(&[], e.clone());
293 | }
294 | (_, Ok(ref mut _child_elm)) => {
295 | if !handled {
296 | if ext_ids.len() == 1 {
297 | handled = _child_elm.on_primitive_event(&[], e.clone());
298 | } else if ext_ids.len() > 1 {
299 | handled = _child_elm.on_primitive_event(&ext_ids[1..], e.clone());
300 | }
301 | }
302 | }
303 | (_, Err(_err_str)) => {
304 | //this should be unreachable
305 | panic!("unable to lock element : {}", _err_str)
306 | }
307 | }
308 | }
309 | // if none of the children handled the event
310 | // see if you can handle it here
311 | if !handled {
312 | if let PrimitiveEvent::Button(_p, _b, _s, m) = e {
313 | handled = self.exec_handler(ElementEvent::Clicked, &m);
314 | }
315 | }
316 | handled
317 | }
318 |
319 | fn set_handler(&mut self, _e: ElementEvent, _f: EventFn) {
320 | self.handlers.insert(_e, _f);
321 | }
322 |
323 | fn exec_handler(&mut self, _e: ElementEvent, _d: &dyn Any) -> bool {
324 | let h = self.handlers.get_mut(&_e).cloned();
325 | if let Some(mut h) = h {
326 | h.call(self, _d)
327 | } else {
328 | false
329 | }
330 | }
331 |
332 | fn as_any(&self) -> &dyn Any {
333 | self
334 | }
335 | fn as_any_mut(&mut self) -> &mut dyn Any {
336 | self
337 | }
338 | }
339 |
340 | impl HasChildren for HBox {
341 | #[allow(unused)]
342 | fn get_child(&self, i: u32) -> Option>> {
343 | None
344 | }
345 | #[allow(unused)]
346 | //fn get_child_mut(&mut self, i:u32) -> Option<&mut Element> {None}
347 | fn append(&mut self, e: Arc>) -> Option>> {
348 | self.children.push(e);
349 | None
350 | }
351 | }
352 |
--------------------------------------------------------------------------------
/src/gui/properties.rs:
--------------------------------------------------------------------------------
1 | use std::collections::HashSet;
2 | use std::hash::{Hash, Hasher};
3 | use std::mem;
4 | use std::sync::{Arc, Mutex};
5 |
6 | use webrender::api::ColorF;
7 | //use webrender::api::DeviceSize
8 |
9 | #[derive(Clone, Debug, PartialEq)]
10 | pub struct Point {
11 | pub x: f32,
12 | pub y: f32,
13 | }
14 |
15 | impl Point {
16 | pub fn new() -> Point {
17 | Point { x: 0.0, y: 0.0 }
18 | }
19 | }
20 |
21 | #[derive(Clone, Debug, PartialEq)]
22 | pub struct Extent {
23 | pub x: f32,
24 | pub y: f32,
25 | pub w: f32,
26 | pub h: f32,
27 | pub dpi: f32,
28 | }
29 |
30 | impl Extent {
31 | pub fn new() -> Extent {
32 | Extent {
33 | x: 0.0,
34 | y: 0.0,
35 | w: 0.0,
36 | h: 0.0,
37 | dpi: 0.0,
38 | }
39 | }
40 | }
41 |
42 | #[derive(Clone, Debug)]
43 | pub enum Unit {
44 | Natural,
45 | Extent,
46 | Pixel(f32),
47 | Stretch(f32),
48 | }
49 | impl PartialEq for Unit {
50 | fn eq(&self, other: &Unit) -> bool {
51 | mem::discriminant(self) == mem::discriminant(other)
52 | }
53 | }
54 | impl Eq for Unit {}
55 | impl Hash for Unit {
56 | fn hash(&self, state: &mut H) {
57 | mem::discriminant(self).hash(state)
58 | }
59 | }
60 |
61 | #[derive(Clone, Debug, Eq, PartialEq)]
62 | pub enum Align {
63 | Left,
64 | Middle,
65 | Right,
66 | }
67 |
68 | #[derive(Clone, Debug)]
69 | pub enum Property {
70 | Size(i32), //in pixels
71 | Family(String),
72 | Left(Unit), //in pixels or stretches
73 | Width(Unit), //in pixels or stretches
74 | Right(Unit), //in pixels or stretches
75 | Top(Unit), //in pixels or stretches
76 | Height(Unit), //in pixels or stretches
77 | Bottom(Unit), //in pixels or stretches
78 | MinWidth(Unit),
79 | MinHeight(Unit),
80 | Color(ColorF),
81 | BgColor(ColorF),
82 | HoverColor(ColorF),
83 | HoverBgColor(ColorF),
84 | FocusColor(ColorF),
85 | FocusBgColor(ColorF),
86 | ActiveColor(ColorF),
87 | ActiveBgColor(ColorF),
88 | DisabledColor(ColorF),
89 | DisabledBgColor(ColorF),
90 | TextAlign(Align),
91 | }
92 |
93 | lazy_static! {
94 | pub static ref SIZE: Property = Property::Size(0);
95 | pub static ref FAMILY: Property = Property::Family(String::from(""));
96 | pub static ref LEFT: Property = Property::Left(Unit::Stretch(0.0));
97 | pub static ref WIDTH: Property = Property::Width(Unit::Stretch(1.0));
98 | pub static ref RIGHT: Property = Property::Right(Unit::Stretch(0.0));
99 | pub static ref TOP: Property = Property::Top(Unit::Stretch(0.0));
100 | pub static ref HEIGHT: Property = Property::Height(Unit::Stretch(1.0));
101 | pub static ref BOTTOM: Property = Property::Bottom(Unit::Stretch(0.0));
102 | pub static ref MIN_WIDTH: Property = Property::MinWidth(Unit::Pixel(0.0));
103 | pub static ref MIN_HEIGHT: Property = Property::MinHeight(Unit::Pixel(0.0));
104 | pub static ref COLOR: Property = Property::Color(ColorF {
105 | r: 0.2,
106 | g: 0.2,
107 | b: 0.2,
108 | a: 1.0,
109 | });
110 | pub static ref BG_COLOR: Property = Property::BgColor(ColorF {
111 | r: 0.9,
112 | g: 0.9,
113 | b: 0.9,
114 | a: 1.0,
115 | });
116 | pub static ref HOVER_COLOR: Property = Property::HoverColor(ColorF {
117 | r: 0.2,
118 | g: 0.2,
119 | b: 0.2,
120 | a: 1.0,
121 | });
122 | pub static ref HOVER_BG_COLOR: Property = Property::HoverBgColor(ColorF {
123 | r: 0.8,
124 | g: 0.8,
125 | b: 0.8,
126 | a: 1.0,
127 | });
128 | pub static ref FOCUS_COLOR: Property = Property::FocusColor(ColorF {
129 | r: 0.0,
130 | g: 0.0,
131 | b: 0.0,
132 | a: 1.0,
133 | });
134 | pub static ref FOCUS_BG_COLOR: Property = Property::FocusBgColor(ColorF {
135 | r: 0.9,
136 | g: 0.9,
137 | b: 0.9,
138 | a: 1.0,
139 | });
140 | pub static ref ACTIVE_COLOR: Property = Property::ActiveColor(ColorF {
141 | r: 0.2,
142 | g: 0.2,
143 | b: 0.2,
144 | a: 1.0,
145 | });
146 | pub static ref ACTIVE_BG_COLOR: Property = Property::ActiveBgColor(ColorF {
147 | r: 1.0,
148 | g: 1.0,
149 | b: 1.0,
150 | a: 1.0,
151 | });
152 | pub static ref DISABLED_COLOR: Property = Property::DisabledColor(ColorF {
153 | r: 0.2,
154 | g: 0.2,
155 | b: 0.2,
156 | a: 1.0,
157 | });
158 | pub static ref DISABLED_BG_COLOR: Property = Property::DisabledBgColor(ColorF {
159 | r: 0.8,
160 | g: 0.8,
161 | b: 0.8,
162 | a: 1.0,
163 | });
164 | pub static ref TEXT_ALIGN: Property = Property::TextAlign(Align::Left);
165 | }
166 |
167 | impl PartialEq for Property {
168 | fn eq(&self, other: &Property) -> bool {
169 | mem::discriminant(self) == mem::discriminant(other)
170 | }
171 | }
172 | impl Eq for Property {}
173 |
174 | impl Hash for Property {
175 | fn hash(&self, state: &mut H) {
176 | mem::discriminant(self).hash(state)
177 | }
178 | }
179 |
180 | #[derive(Clone, Debug, Default)]
181 | pub struct Properties(HashSet);
182 |
183 | impl Properties {
184 | pub fn new() -> Properties {
185 | Properties(HashSet::new())
186 | }
187 |
188 | pub fn default(&mut self) -> &mut Properties {
189 | if cfg!(target_os = "linux")
190 | {
191 | self.set(Property::Family(String::from("FreeMono")));
192 | }
193 | if cfg!(target_os = "windows") || cfg!(target_os = "macos")
194 | {
195 | self.set(Property::Family(String::from("Arial")));
196 | }
197 | self.set(Property::Size(16))
198 | .set(Property::Left(Unit::Stretch(0.0)))
199 | .set(Property::Width(Unit::Stretch(1.0)))
200 | .set(Property::Right(Unit::Stretch(0.0)))
201 | .set(Property::Top(Unit::Stretch(0.0)))
202 | .set(Property::Height(Unit::Stretch(1.0)))
203 | .set(Property::Bottom(Unit::Stretch(0.0)))
204 | .set(Property::MinWidth(Unit::Pixel(0.0)))
205 | .set(Property::MinHeight(Unit::Pixel(0.0)))
206 | .set(Property::Color(ColorF::new(0.8, 0.8, 0.8, 1.0)))
207 | .set(Property::BgColor(ColorF::new(1.0, 1.0, 1.0, 0.0)))
208 | .set(Property::FocusColor(ColorF::new(1.0, 1.0, 1.0, 1.0)))
209 | .set(Property::FocusBgColor(ColorF::new(0.0, 0.0, 0.0, 0.0)))
210 | .set(Property::HoverColor(ColorF::new(0.9, 0.9, 0.9, 1.0)))
211 | .set(Property::HoverBgColor(ColorF::new(0.0, 0.0, 0.0, 0.0)))
212 | .set(Property::DisabledColor(ColorF::new(0.5, 0.5, 0.5, 1.0)))
213 | .set(Property::DisabledBgColor(ColorF::new(0.0, 0.0, 0.0, 0.0)))
214 | .set(Property::TextAlign(Align::Left))
215 | }
216 |
217 | pub fn set(&mut self, property: Property) -> &mut Properties {
218 | {
219 | let x = &mut self.0;
220 | x.replace(property);
221 | }
222 | self
223 | }
224 |
225 | pub fn get(&self, property: &Property) -> Option<&Property> {
226 | self.0.get(property)
227 | }
228 |
229 | pub fn get_size(&self) -> i32 {
230 | if let Some(Property::Size(x)) = self.get(&SIZE) {
231 | *x
232 | } else {
233 | panic!("Size not found")
234 | }
235 | }
236 |
237 | pub fn get_family(&self) -> String {
238 | if let Some(Property::Family(x)) = self.get(&FAMILY) {
239 | x.clone()
240 | } else {
241 | panic!("Family not found")
242 | }
243 | }
244 |
245 | pub fn get_left(&self) -> Unit {
246 | if let Some(Property::Left(x)) = self.get(&LEFT) {
247 | x.clone()
248 | } else {
249 | panic!("Left not found")
250 | }
251 | }
252 |
253 | pub fn get_width(&self) -> Unit {
254 | if let Some(Property::Width(x)) = self.get(&WIDTH) {
255 | x.clone()
256 | } else {
257 | panic!("Width not found")
258 | }
259 | }
260 |
261 | pub fn get_right(&self) -> Unit {
262 | if let Some(Property::Right(x)) = self.get(&RIGHT) {
263 | x.clone()
264 | } else {
265 | panic!("Right not found")
266 | }
267 | }
268 |
269 | pub fn get_top(&self) -> Unit {
270 | if let Some(Property::Top(x)) = self.get(&TOP) {
271 | x.clone()
272 | } else {
273 | panic!("Top not found")
274 | }
275 | }
276 |
277 | pub fn get_height(&self) -> Unit {
278 | if let Some(Property::Height(x)) = self.get(&HEIGHT) {
279 | x.clone()
280 | } else {
281 | panic!("Height not found")
282 | }
283 | }
284 |
285 | pub fn get_bottom(&self) -> Unit {
286 | if let Some(Property::Bottom(x)) = self.get(&BOTTOM) {
287 | x.clone()
288 | } else {
289 | panic!("Bottom not found")
290 | }
291 | }
292 |
293 | pub fn get_min_width(&self) -> Unit {
294 | if let Some(Property::MinWidth(x)) = self.get(&MIN_WIDTH) {
295 | x.clone()
296 | } else {
297 | panic!("Min Width not found")
298 | }
299 | }
300 |
301 | pub fn get_min_height(&self) -> Unit {
302 | if let Some(Property::MinHeight(x)) = self.get(&MIN_HEIGHT) {
303 | x.clone()
304 | } else {
305 | panic!("Min Height not found")
306 | }
307 | }
308 |
309 | pub fn get_color(&self) -> ColorF {
310 | if let Some(Property::Color(x)) = self.get(&COLOR) {
311 | *x
312 | } else {
313 | panic!("Color not found")
314 | }
315 | }
316 |
317 | pub fn get_bg_color(&self) -> ColorF {
318 | if let Some(Property::BgColor(x)) = self.get(&BG_COLOR) {
319 | *x
320 | } else {
321 | panic!("Background Color not found")
322 | }
323 | }
324 |
325 | pub fn get_focus_color(&self) -> ColorF {
326 | if let Some(Property::FocusColor(x)) = self.get(&FOCUS_COLOR) {
327 | *x
328 | } else {
329 | panic!("Focus Color not found")
330 | }
331 | }
332 |
333 | pub fn get_focus_bg_color(&self) -> ColorF {
334 | if let Some(Property::FocusBgColor(x)) = self.get(&FOCUS_BG_COLOR) {
335 | *x
336 | } else {
337 | panic!("Focus Background Color not found")
338 | }
339 | }
340 |
341 | pub fn get_hover_color(&self) -> ColorF {
342 | if let Some(Property::HoverColor(x)) = self.get(&HOVER_COLOR) {
343 | *x
344 | } else {
345 | panic!("Hover Color not found")
346 | }
347 | }
348 |
349 | pub fn get_hover_bg_color(&self) -> ColorF {
350 | if let Some(Property::HoverBgColor(x)) = self.get(&HOVER_BG_COLOR) {
351 | *x
352 | } else {
353 | panic!("Hover Background Color not found")
354 | }
355 | }
356 |
357 | pub fn get_disabled_color(&self) -> ColorF {
358 | if let Some(Property::DisabledColor(x)) = self.get(&DISABLED_COLOR) {
359 | *x
360 | } else {
361 | panic!("Disabled Color not found")
362 | }
363 | }
364 |
365 | pub fn get_disabled_bg_color(&self) -> ColorF {
366 | if let Some(Property::DisabledBgColor(x)) = self.get(&DISABLED_BG_COLOR) {
367 | *x
368 | } else {
369 | panic!("Disabled Background Color not found")
370 | }
371 | }
372 |
373 | pub fn get_text_align(&self) -> Align {
374 | if let Some(Property::TextAlign(x)) = self.get(&TEXT_ALIGN) {
375 | x.clone()
376 | } else {
377 | panic!("Text Align not found")
378 | }
379 | }
380 | }
381 |
382 | #[derive(Clone, Debug, PartialEq)]
383 | pub struct Position {
384 | pub x: f32,
385 | pub y: f32,
386 | }
387 |
388 | #[derive(Clone, Debug, PartialEq)]
389 | pub struct Modifiers {
390 | pub shift: bool,
391 | pub ctrl: bool,
392 | pub alt: bool,
393 | pub logo: bool,
394 | }
395 |
396 | #[derive(Clone, Debug, PartialEq)]
397 | pub enum ButtonState {
398 | Pressed,
399 | Released,
400 | }
401 |
402 | #[derive(Clone, Debug, PartialEq)]
403 | pub enum Button {
404 | Left,
405 | Middle,
406 | Right,
407 | Other,
408 | }
409 |
410 | #[derive(Clone, Debug)]
411 | pub struct IdGenerator {
412 | pub next_id: Arc>,
413 | }
414 |
415 | impl IdGenerator {
416 | pub fn new(start: u64) -> Self {
417 | IdGenerator {
418 | next_id: Arc::new(Mutex::new(start)),
419 | }
420 | }
421 | pub fn get(&mut self) -> u64 {
422 | let mut counter = self.next_id.lock().unwrap();
423 | *counter += 1;
424 | *counter
425 | }
426 | pub fn zero(&mut self) {
427 | let mut counter = self.next_id.lock().unwrap();
428 | *counter = 0;
429 | }
430 | }
--------------------------------------------------------------------------------
/examples/mainexample.rs:
--------------------------------------------------------------------------------
1 | extern crate skryn;
2 | extern crate webrender;
3 |
4 | use std::any::Any;
5 | use std::sync::{Arc, Mutex};
6 | use std::thread;
7 | use std::time::Duration;
8 |
9 | use skryn::data::*;
10 | use skryn::elements::*;
11 | use skryn::gui::font::FontStore;
12 | use skryn::gui::properties::{Extent, IdGenerator, Properties, Property};
13 |
14 | use webrender::api::{ColorF, DisplayListBuilder, RenderApi};
15 |
16 | /*
17 | Lets start with a Person struct. A
18 | Person has a name and age. Both can change
19 | so we will use the Observable struct.
20 | For convenience ObservableString and
21 | ObservableU32 already exist as types.
22 | */
23 |
24 | struct Person {
25 | name: ObservableString,
26 | age: ObservableU32,
27 | }
28 |
29 | /*
30 | The Observables fire events when their value changes.
31 | So we must have a way for adding event functions for
32 | both the name and age. Also, we must have functions
33 | so that we remove the listeners when they are
34 | not needed.
35 | */
36 |
37 | impl Person {
38 | fn new(name: String, age: u32) -> Person {
39 | Person {
40 | name: ObservableString::new(name),
41 | age: ObservableU32::new(age),
42 | }
43 | }
44 |
45 | #[allow(unused)]
46 | fn on_name_change(&mut self, listener: Box) -> u64 {
47 | self.name.observe(listener)
48 | }
49 |
50 | fn on_age_change(&mut self, listener: Box) -> u64 {
51 | self.age.observe(listener)
52 | }
53 |
54 | fn remove_name_listener(&mut self, id: u64) {
55 | self.name.stop(id);
56 | }
57 |
58 | fn remove_age_listener(&mut self, id: u64) {
59 | self.age.stop(id);
60 | }
61 | }
62 |
63 | /*
64 | Now that we have our Person with two
65 | Obsevable fields, we need to create an
66 | Element to encapsulate the view.
67 | */
68 |
69 | struct PersonElm {
70 | //Every element is given an id. This
71 | //is used in referencing the main bounding
72 | //box of Elements
73 | id: u64,
74 | //Have a reference to Person. As you'll see
75 | //below, the age is incremented every second
76 | //by another thread
77 | person: Arc>,
78 | //For ease, we will use a built in Element
79 | //VBox (Vertical growing box). There is also
80 | //HBox.
81 | vbox: Arc>,
82 | //Every element must know what area it takes.
83 | //This is used in the rendering and must be
84 | //updated if the underlying views change in
85 | //value or bound
86 | bounds: Extent,
87 | //The following ids will be used to remove
88 | //Observable listeners when the PersonElm
89 | //is removed
90 | age_observer_id: Option,
91 | name_observer_id: Option,
92 | }
93 |
94 | impl PersonElm {
95 | fn new(p: Arc>) -> PersonElm {
96 | //Create two TextBoxes and display their initial value
97 | let mut _p = p.lock().unwrap();
98 | let mut _tbox = TextBox::new(format!("fasih is the author of skryn and his middle and last name respectively are:\nahmed\nrana"));
99 | //let mut _tbox = TextBox::new(format!("{}\n\n{}",
100 | // "Fasih's full name is فصیح احمد رانا, and his first name means eloquent.",
101 | //w "فصیح کا پورا نام Fasih Ahmed Rana ہے، اور انگریزی میں اس کے نام کا مطلب eloquent ہے."));
102 | _tbox.set(skryn::gui::properties::Property::TextAlign(skryn::gui::properties::Align::Middle));
103 | _tbox.set_placeholder("".to_owned());
104 | let name = Arc::new(Mutex::new(_tbox));
105 | let age = Arc::new(Mutex::new(TextBox::new(String::from(format!(
106 | "{}",
107 | _p.age.get_value()
108 | )))));
109 | //This is an alert button just to show how easy it is to spawn new windows.
110 | let alert_button = Arc::new(Mutex::new(Button::new(format!("Press here"))));
111 |
112 | //the text in the following button should be showing up in three lines based on the width
113 | let cancel_button = Arc::new(Mutex::new(Button::new(format!(
114 | "فصیح احمد رانا"
115 | ))));
116 |
117 | let h = Arc::new(Mutex::new(HBox::new()));
118 | match h.lock() {
119 | Ok(ref mut h) => {
120 | h.set(skryn::gui::properties::Property::Left(
121 | skryn::gui::properties::Unit::Stretch(1.0),
122 | ));
123 | h.set(skryn::gui::properties::Property::Right(
124 | skryn::gui::properties::Unit::Stretch(1.0),
125 | ));
126 | h.append(alert_button.clone());
127 | h.append(cancel_button.clone());
128 | h.set(skryn::gui::properties::Property::Height(
129 | skryn::gui::properties::Unit::Pixel(25.0),
130 | ));
131 | h.set(skryn::gui::properties::Property::BgColor(ColorF::new(
132 | 0.2, 1.0, 0.2, 1.0,
133 | )));
134 | }
135 | Err(_err_str) => panic!("unable to lock element : {}", _err_str),
136 | }
137 |
138 | //skryn has 4 Length Units at the moment.
139 | //This is to simplify the rendering of boxes
140 | // -> Natural means what ever the natural space an element takes based on the components inside it.
141 | // -> Extent means the extent (bounding box) of parent element
142 | // -> Stretch means use a percentage of the parent's extent (bounding box)
143 | // -> Pixel is the static length
144 | name.lock()
145 | .unwrap()
146 | .set(skryn::gui::properties::Property::Height(
147 | skryn::gui::properties::Unit::Stretch(1.0),
148 | ));
149 | age.lock()
150 | .unwrap()
151 | .set(skryn::gui::properties::Property::Height(
152 | skryn::gui::properties::Unit::Stretch(1.0),
153 | ));
154 | age.lock().unwrap().set_editable(false);
155 | alert_button
156 | .lock()
157 | .unwrap()
158 | .set(skryn::gui::properties::Property::Width(
159 | skryn::gui::properties::Unit::Pixel(100.0),
160 | ));
161 | cancel_button
162 | .lock()
163 | .unwrap()
164 | .set(skryn::gui::properties::Property::Width(
165 | skryn::gui::properties::Unit::Pixel(100.0),
166 | ));
167 | //Here we have used the Stretch unit for elements above to make sure our VBox below is utilized to the full.
168 | let v = Arc::new(Mutex::new(VBox::new()));
169 | v.lock().unwrap().set(skryn::gui::properties::Property::Top(
170 | skryn::gui::properties::Unit::Stretch(1.0),
171 | ));
172 | v.lock()
173 | .unwrap()
174 | .set(skryn::gui::properties::Property::Bottom(
175 | skryn::gui::properties::Unit::Stretch(1.0),
176 | ));
177 | v.lock()
178 | .unwrap()
179 | .set(skryn::gui::properties::Property::Left(
180 | skryn::gui::properties::Unit::Stretch(0.2),
181 | ));
182 | v.lock()
183 | .unwrap()
184 | .set(skryn::gui::properties::Property::Right(
185 | skryn::gui::properties::Unit::Stretch(0.2),
186 | ));
187 | match v.lock() {
188 | Ok(ref mut v) => {
189 | v.append(name.clone());
190 | v.append(age.clone());
191 | v.append(h.clone());
192 | v.set(skryn::gui::properties::Property::BgColor(ColorF::new(
193 | 1.0, 0.2, 0.2, 1.0,
194 | )));
195 | }
196 | Err(_err_str) => panic!("unable to lock element : {}", _err_str),
197 | }
198 |
199 | //The following is a simple action taken when our button is clicked.
200 | //An alert window is created.
201 | alert_button.lock().unwrap().set_handler(
202 | ElementEvent::Clicked,
203 | EventFn::new(Arc::new(Mutex::new(move |_e: &mut dyn Element, _d: &dyn Any| {
204 | Alert::show("This is an Alert Box".to_owned(), "Alert".to_owned());
205 | true
206 | }))),
207 | );
208 |
209 | // make sure you sae the observer id for age
210 | // so that we can remove the listener when
211 | // this Element is no longer required.
212 | let age_o_id = _p.on_age_change(Box::new(move |v| {
213 | let _ageelm = age.lock().unwrap().set_value(format!("{}", v));
214 | }));
215 |
216 | //finally return the constructed element
217 | PersonElm {
218 | id: 0,
219 | person: p.clone(),
220 | vbox: v,
221 | bounds: Extent {
222 | x: 0.0,
223 | y: 0.0,
224 | w: 0.0,
225 | h: 0.0,
226 | dpi: 0.0,
227 | },
228 | age_observer_id: Some(age_o_id),
229 | name_observer_id: None,
230 | }
231 | }
232 | }
233 |
234 | /*
235 | Implementing Element trait is the minimum requirement
236 | for creating a custom element
237 | */
238 | impl Element for PersonElm {
239 | fn get_ext_id(&self) -> u64 {
240 | self.id
241 | }
242 |
243 | fn set(&mut self, _prop: Property) {
244 | self.vbox.lock().unwrap().set(_prop);
245 | }
246 |
247 | fn get_properties(&self) -> skryn::gui::properties::Properties {
248 | self.vbox.lock().unwrap().get_properties()
249 | }
250 |
251 | fn render(
252 | &mut self,
253 | api: &RenderApi,
254 | builder: &mut DisplayListBuilder,
255 | extent: Extent,
256 | font_store: &mut FontStore,
257 | _props: Option>,
258 | gen: &mut IdGenerator,
259 | ) {
260 | match self.vbox.lock() {
261 | Ok(ref mut elm) => {
262 | elm.render(api, builder, extent, font_store, None, gen);
263 | self.bounds = elm.get_bounds();
264 | }
265 | Err(_err_str) => panic!("unable to lock element : {}", _err_str),
266 | }
267 | }
268 |
269 | fn get_bounds(&self) -> Extent {
270 | self.bounds.clone()
271 | }
272 |
273 | /*
274 | Simply pass the events to VBox, which is our container in PersonElm.
275 |
276 | The ext_ids, is a trace of the id part of Elements where the event
277 | is relevant. For example, if you click the button, the ids passed
278 | will be that of vbox and alert_button.
279 |
280 | There are certain events where ext_ids are empty, but passing the
281 | event to the children is still required for e.g., SetFocus
282 | */
283 | fn on_primitive_event(&mut self, ext_ids: &[(u64, u16)], e: PrimitiveEvent) -> bool {
284 | match self.vbox.lock() {
285 | Ok(ref mut elm) => {
286 | return elm.on_primitive_event(ext_ids, e);
287 | }
288 | Err(_err_str) => panic!("unable to lock element : {}", _err_str),
289 | }
290 | }
291 |
292 | fn set_handler(&mut self, _e: ElementEvent, _f: EventFn) {}
293 |
294 | fn exec_handler(&mut self, _e: ElementEvent, _d: &dyn Any) -> bool {
295 | false
296 | }
297 |
298 | fn as_any(&self) -> &dyn Any {
299 | self
300 | }
301 |
302 | fn as_any_mut(&mut self) -> &mut dyn Any {
303 | self
304 | }
305 | }
306 |
307 | impl Drop for PersonElm {
308 | fn drop(&mut self) {
309 | if let Some(id) = self.name_observer_id {
310 | self.person.lock().unwrap().remove_name_listener(id);
311 | }
312 | if let Some(id) = self.age_observer_id {
313 | self.person.lock().unwrap().remove_age_listener(id);
314 | }
315 | }
316 | }
317 |
318 | /*
319 | Simpler implementation.
320 | Here we just have an Alert builder to show
321 | an alert message.
322 | */
323 | struct Alert;
324 | impl Alert {
325 | fn show(message: String, heading: String) {
326 | let msg_box = TextBox::new(message);
327 | skryn::gui::window::Manager::add(Arc::new(Mutex::new(msg_box)), heading, 400.0, 100.0);
328 | }
329 | }
330 |
331 | fn main() {
332 | //create the person.
333 | let person = Person::new(String::from(""), 0);
334 |
335 | let person = Arc::new(Mutex::new(person));
336 |
337 | //will move this into its separate thread to update the age every second.
338 | let tmp_person = person.clone();
339 |
340 | //create an Instance of PersonElm and add it to the window manager.
341 | let form = PersonElm::new(person);
342 | skryn::gui::window::Manager::add(
343 | Arc::new(Mutex::new(form)),
344 | String::from("Main window"),
345 | 300.0,
346 | 200.0,
347 | );
348 |
349 | //spawn a worker thread to update the age
350 | thread::spawn(move || {
351 | let mut t = 0;
352 | loop {
353 | {
354 | let mut x = tmp_person.lock().unwrap();
355 | t = t + 1;
356 | x.age.update(Action::Update(t / 100));
357 | }
358 | thread::sleep(Duration::from_millis(10));
359 | }
360 | });
361 |
362 | //start the window manager at 60 fps
363 | skryn::gui::window::Manager::start(60);
364 | }
365 |
--------------------------------------------------------------------------------
/examples/calculator.rs:
--------------------------------------------------------------------------------
1 | extern crate skryn;
2 | extern crate webrender;
3 |
4 | use std::any::Any;
5 | use std::sync::{Arc, Mutex};
6 |
7 | //use skryn::data::*;
8 | use skryn::elements::*;
9 |
10 | use webrender::api::ColorF;
11 |
12 | #[derive(Debug, Clone)]
13 | enum Operation {
14 | Input(f64),
15 | Add,
16 | Subtract,
17 | Multiply,
18 | Divide,
19 | Answer,
20 | }
21 |
22 | #[derive(Debug, Clone)]
23 | struct Calculator {
24 | ops: Vec,
25 | }
26 |
27 | impl Calculator {
28 | fn new() -> Calculator {
29 | Calculator { ops: vec![] }
30 | }
31 |
32 | fn push_num(&mut self, n: f64) {
33 | let ops = self.ops.clone();
34 | if self.ops.is_empty() {
35 | self.ops.push(Operation::Input(n));
36 | } else if let Some(x) = ops.last() {
37 | if let Operation::Input(_i) = x {
38 | //no op
39 | } else {
40 | self.ops.push(Operation::Input(n))
41 | }
42 | }
43 | }
44 |
45 | fn push_op(&mut self, op: Operation) -> Result