├── .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 | 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, &str> { 46 | let ops = self.ops.clone(); 47 | let len = self.ops.len(); 48 | let mut return_value = Ok(None); 49 | match op { 50 | Operation::Answer => { 51 | if len > 2 { 52 | match (&ops[len - 3], &ops[len - 1]) { 53 | (Operation::Input(l), Operation::Input(r)) => { 54 | match &ops[len - 2] { 55 | Operation::Add => { 56 | self.ops.push(Operation::Answer); 57 | return_value = Ok(Some(l + r)); 58 | } 59 | Operation::Subtract => { 60 | self.ops.push(Operation::Answer); 61 | return_value = Ok(Some(l - r)); 62 | //self.ops.push(Operation::Input(l-r)); 63 | } 64 | Operation::Multiply => { 65 | self.ops.push(Operation::Answer); 66 | return_value = Ok(Some(l * r)); 67 | //self.ops.push(Operation::Input(l*r)); 68 | } 69 | Operation::Divide => { 70 | self.ops.push(Operation::Answer); 71 | return_value = Ok(Some(l / r)); 72 | //self.ops.push(Operation::Input(l/r)); 73 | } 74 | _ => (), 75 | } 76 | } 77 | _ => (), 78 | } 79 | } 80 | } 81 | Operation::Input(n) => { 82 | self.push_num(n); 83 | return_value = Ok(Some(n)) 84 | } 85 | _ => { 86 | if let Some(xop) = ops.last() { 87 | match xop { 88 | Operation::Input(_) => { 89 | self.ops.push(op); 90 | return_value = Ok(None) 91 | } 92 | _ => (), 93 | } 94 | } 95 | } 96 | } 97 | 98 | return_value 99 | } 100 | } 101 | 102 | #[derive(Clone)] 103 | struct CalculatorView { 104 | calc: Arc>, 105 | view: Arc>, 106 | textbox: Arc>, 107 | } 108 | 109 | impl CalculatorView { 110 | fn new() -> CalculatorView { 111 | let calc = Arc::new(Mutex::new(Calculator::new())); 112 | let mut view = VBox::new(); 113 | 114 | let mut history_scroll = ScrollBox::new(); 115 | history_scroll.set(skryn::gui::properties::Property::BgColor(ColorF::new( 116 | 0.95, 0.95, 0.95, 1.0, 117 | ))); 118 | 119 | let mut history = TextBox::new("Calculation History\n".to_owned()); 120 | history.set(skryn::gui::properties::Property::Color(ColorF::new( 121 | 0.2, 0.2, 0.2, 1.0, 122 | ))); 123 | history.set(skryn::gui::properties::Property::Height( 124 | skryn::gui::properties::Unit::Natural, 125 | )); 126 | history.set(skryn::gui::properties::Property::Size(16)); 127 | history.set(skryn::gui::properties::Property::TextAlign( 128 | skryn::gui::properties::Align::Right, 129 | )); 130 | history.set_editable(false); 131 | let history = Arc::new(Mutex::new(history)); 132 | 133 | history_scroll.append(history.clone()); 134 | view.append(Arc::new(Mutex::new(history_scroll))); 135 | 136 | let mut tbox = TextBox::new("".to_owned()); 137 | tbox.set_singleline(true); 138 | tbox.set(skryn::gui::properties::Property::Height( 139 | skryn::gui::properties::Unit::Pixel(40.0), 140 | )); 141 | tbox.set(skryn::gui::properties::Property::Size(32)); 142 | tbox.set(skryn::gui::properties::Property::HoverBgColor(ColorF::new( 143 | 0.75, 0.75, 0.75, 1.0, 144 | ))); 145 | tbox.set(skryn::gui::properties::Property::TextAlign( 146 | skryn::gui::properties::Align::Right, 147 | )); 148 | let tbox = Arc::new(Mutex::new(tbox)); 149 | view.append(tbox.clone()); 150 | 151 | let mut hbox = HBox::new(); 152 | hbox.set(skryn::gui::properties::Property::Height( 153 | skryn::gui::properties::Unit::Pixel(44.0), 154 | )); 155 | let mut addbutt = Button::new("+".to_owned()); 156 | addbutt.set(skryn::gui::properties::Property::Size(32)); 157 | addbutt.set(skryn::gui::properties::Property::TextAlign( 158 | skryn::gui::properties::Align::Middle, 159 | )); 160 | addbutt.set(skryn::gui::properties::Property::Top( 161 | skryn::gui::properties::Unit::Stretch(1.0), 162 | )); 163 | addbutt.set(skryn::gui::properties::Property::Bottom( 164 | skryn::gui::properties::Unit::Stretch(1.0), 165 | )); 166 | let tmpbox = tbox.clone(); 167 | let tmpcalc = calc.clone(); 168 | let tmphist = history.clone(); 169 | addbutt.set_handler( 170 | skryn::elements::ElementEvent::Clicked, 171 | EventFn::new(Arc::new(Mutex::new(move |_e: &mut Element, _d: &Any| { 172 | let mut tb = tmpbox.lock().unwrap(); 173 | let val = tb.get_value().parse::(); 174 | if let Ok(n) = val { 175 | if tmpcalc.lock().unwrap().push_op(Operation::Input(n)).is_ok() { 176 | tmphist.lock().unwrap().append_value(&format!("{}\n+\n", n)); 177 | tmpcalc.lock().unwrap().push_op(Operation::Add).unwrap(); 178 | } 179 | tb.set_value("".to_owned()); 180 | } else { 181 | Alert::show( 182 | format!("Value {} could not be parsed to a number.", tb.get_value()), 183 | "Number Error".to_owned(), 184 | ); 185 | } 186 | 187 | true 188 | }))), 189 | ); 190 | hbox.append(Arc::new(Mutex::new(addbutt))); 191 | 192 | let mut subbutt = Button::new("-".to_owned()); 193 | subbutt.set(skryn::gui::properties::Property::Size(32)); 194 | subbutt.set(skryn::gui::properties::Property::TextAlign( 195 | skryn::gui::properties::Align::Middle, 196 | )); 197 | subbutt.set(skryn::gui::properties::Property::Top( 198 | skryn::gui::properties::Unit::Stretch(1.0), 199 | )); 200 | subbutt.set(skryn::gui::properties::Property::Bottom( 201 | skryn::gui::properties::Unit::Stretch(1.0), 202 | )); 203 | let tmpbox = tbox.clone(); 204 | let tmpcalc = calc.clone(); 205 | let tmphist = history.clone(); 206 | subbutt.set_handler( 207 | skryn::elements::ElementEvent::Clicked, 208 | EventFn::new(Arc::new(Mutex::new(move |_e: &mut Element, _d: &Any| { 209 | let mut tb = tmpbox.lock().unwrap(); 210 | let val = tb.get_value().parse::(); 211 | if let Ok(n) = val { 212 | if tmpcalc.lock().unwrap().push_op(Operation::Input(n)).is_ok() { 213 | tmphist.lock().unwrap().append_value(&format!("{}\n-\n", n)); 214 | tmpcalc 215 | .lock() 216 | .unwrap() 217 | .push_op(Operation::Subtract) 218 | .unwrap(); 219 | } 220 | tb.set_value("".to_owned()); 221 | } else { 222 | Alert::show( 223 | format!("Value {} could not be parsed to a number.", tb.get_value()), 224 | "Number Error".to_owned(), 225 | ); 226 | } 227 | 228 | true 229 | }))), 230 | ); 231 | hbox.append(Arc::new(Mutex::new(subbutt))); 232 | 233 | let mut mulbutt = Button::new("*".to_owned()); 234 | mulbutt.set(skryn::gui::properties::Property::Size(32)); 235 | mulbutt.set(skryn::gui::properties::Property::TextAlign( 236 | skryn::gui::properties::Align::Middle, 237 | )); 238 | mulbutt.set(skryn::gui::properties::Property::Top( 239 | skryn::gui::properties::Unit::Stretch(1.0), 240 | )); 241 | mulbutt.set(skryn::gui::properties::Property::Bottom( 242 | skryn::gui::properties::Unit::Stretch(1.0), 243 | )); 244 | let tmpbox = tbox.clone(); 245 | let tmpcalc = calc.clone(); 246 | let tmphist = history.clone(); 247 | mulbutt.set_handler( 248 | skryn::elements::ElementEvent::Clicked, 249 | EventFn::new(Arc::new(Mutex::new(move |_e: &mut Element, _d: &Any| { 250 | let mut tb = tmpbox.lock().unwrap(); 251 | let val = tb.get_value().parse::(); 252 | if let Ok(n) = val { 253 | if tmpcalc.lock().unwrap().push_op(Operation::Input(n)).is_ok() { 254 | tmphist.lock().unwrap().append_value(&format!("{}\n*\n", n)); 255 | tmpcalc 256 | .lock() 257 | .unwrap() 258 | .push_op(Operation::Multiply) 259 | .unwrap(); 260 | } 261 | tb.set_value("".to_owned()); 262 | } else { 263 | Alert::show( 264 | format!("Value {} could not be parsed to a number.", tb.get_value()), 265 | "Number Error".to_owned(), 266 | ); 267 | } 268 | 269 | true 270 | }))), 271 | ); 272 | hbox.append(Arc::new(Mutex::new(mulbutt))); 273 | 274 | let mut divbutt = Button::new("/".to_owned()); 275 | divbutt.set(skryn::gui::properties::Property::Size(32)); 276 | divbutt.set(skryn::gui::properties::Property::TextAlign( 277 | skryn::gui::properties::Align::Middle, 278 | )); 279 | divbutt.set(skryn::gui::properties::Property::Top( 280 | skryn::gui::properties::Unit::Stretch(1.0), 281 | )); 282 | divbutt.set(skryn::gui::properties::Property::Bottom( 283 | skryn::gui::properties::Unit::Stretch(1.0), 284 | )); 285 | let tmpbox = tbox.clone(); 286 | let tmpcalc = calc.clone(); 287 | let tmphist = history.clone(); 288 | divbutt.set_handler( 289 | skryn::elements::ElementEvent::Clicked, 290 | EventFn::new(Arc::new(Mutex::new(move |_e: &mut Element, _d: &Any| { 291 | let mut tb = tmpbox.lock().unwrap(); 292 | let val = tb.get_value().parse::(); 293 | if let Ok(n) = val { 294 | if tmpcalc.lock().unwrap().push_op(Operation::Input(n)).is_ok() { 295 | tmphist.lock().unwrap().append_value(&format!("{}\n/\n", n)); 296 | tmpcalc.lock().unwrap().push_op(Operation::Divide).unwrap(); 297 | } 298 | tb.set_value("".to_owned()); 299 | } else { 300 | Alert::show( 301 | format!("Value {} could not be parsed to a number.", tb.get_value()), 302 | "Number Error".to_owned(), 303 | ); 304 | } 305 | 306 | true 307 | }))), 308 | ); 309 | hbox.append(Arc::new(Mutex::new(divbutt))); 310 | 311 | let mut eqlbutt = Button::new("=".to_owned()); 312 | eqlbutt.set(skryn::gui::properties::Property::Size(32)); 313 | eqlbutt.set(skryn::gui::properties::Property::TextAlign( 314 | skryn::gui::properties::Align::Middle, 315 | )); 316 | eqlbutt.set(skryn::gui::properties::Property::Top( 317 | skryn::gui::properties::Unit::Stretch(1.0), 318 | )); 319 | eqlbutt.set(skryn::gui::properties::Property::Bottom( 320 | skryn::gui::properties::Unit::Stretch(1.0), 321 | )); 322 | let tmpbox = tbox.clone(); 323 | let tmpcalc = calc.clone(); 324 | let tmphist = history.clone(); 325 | eqlbutt.set_handler( 326 | skryn::elements::ElementEvent::Clicked, 327 | EventFn::new(Arc::new(Mutex::new(move |_e: &mut Element, _d: &Any| { 328 | let mut tb = tmpbox.lock().unwrap(); 329 | let val = tb.get_value().parse::(); 330 | if let Ok(n) = val { 331 | if tmpcalc.lock().unwrap().push_op(Operation::Input(n)).is_ok() { 332 | let v = tmpcalc.lock().unwrap().push_op(Operation::Answer).unwrap(); 333 | if let Some(_v) = v { 334 | tmphist.lock().unwrap().append_value(&format!("{}\n=\n", n)); 335 | tb.set_value(format!("{}", _v)); 336 | } 337 | } 338 | } else { 339 | Alert::show( 340 | format!("Value {} could not be parsed to a number.", tb.get_value()), 341 | "Number Error".to_owned(), 342 | ); 343 | } 344 | 345 | true 346 | }))), 347 | ); 348 | hbox.append(Arc::new(Mutex::new(eqlbutt))); 349 | 350 | view.append(Arc::new(Mutex::new(hbox))); 351 | 352 | CalculatorView { 353 | calc: calc, 354 | view: Arc::new(Mutex::new(view)), 355 | textbox: tbox, 356 | } 357 | } 358 | } 359 | 360 | struct Alert; 361 | impl Alert { 362 | fn show(message: String, heading: String) { 363 | let msg_box = TextBox::new(message); 364 | skryn::gui::window::Manager::add(Arc::new(Mutex::new(msg_box)), heading, 400.0, 100.0); 365 | } 366 | } 367 | 368 | fn main() { 369 | let calc = CalculatorView::new(); 370 | 371 | //Calc.push_num(1.0); 372 | /*calc.push_op(Operation::Input(1.0)); 373 | calc.push_op(Operation::Add); 374 | calc.push_op(Operation::Input(1.0)); 375 | calc.push_op(Operation::Answer); 376 | calc.push_op(Operation::Add); 377 | calc.push_op(Operation::Input(1.0)); 378 | calc.push_op(Operation::Answer); 379 | calc.push_op(Operation::Input(1.0)); // should be ignored?*/ 380 | 381 | //println!("{:?}", calc.ops); 382 | 383 | skryn::gui::window::Manager::add(calc.view.clone(), String::from("Calculator"), 300.0, 200.0); 384 | skryn::gui::window::Manager::start(60); 385 | } 386 | -------------------------------------------------------------------------------- /src/elements/textbox.rs: -------------------------------------------------------------------------------- 1 | use std::any::Any; 2 | use std::sync::Arc; 3 | 4 | use clipboard::{ClipboardProvider}; 5 | 6 | use webrender::api::*; 7 | 8 | use crate::elements::element::*; 9 | use crate::gui::font; 10 | use crate::gui::properties; 11 | use crate::gui::properties::Position; 12 | 13 | pub struct TextBox { 14 | ext_id: u64, 15 | value: Vec, 16 | placeholder: Vec, 17 | props: properties::Properties, 18 | bounds: properties::Extent, 19 | focus: bool, 20 | event_handlers: EventHandlers, 21 | drawn: u8, 22 | editable: bool, 23 | enabled: bool, 24 | singleline: bool, 25 | cursor: Option<(font::Char, properties::Position)>, 26 | cursor_index: usize, 27 | cursor_after: bool, 28 | hovering: bool, 29 | is_password: bool, 30 | cache: font::Paragraphs, 31 | selecting: bool, 32 | } 33 | 34 | impl TextBox { 35 | pub fn new(s: String) -> Self { 36 | let mut props = properties::Properties::new(); 37 | props.default(); 38 | props.set(properties::Property::Height(properties::Unit::Natural)); 39 | TextBox { 40 | ext_id: 0, 41 | value: s.chars().collect(), 42 | placeholder: "".chars().collect(), 43 | props, 44 | bounds: properties::Extent { 45 | x: 0.0, 46 | y: 0.0, 47 | w: 0.0, 48 | h: 0.0, 49 | dpi: 0.0, 50 | }, 51 | focus: false, 52 | event_handlers: EventHandlers::new(), 53 | drawn: 0, 54 | editable: true, 55 | enabled: true, 56 | singleline: false, 57 | cursor: None, 58 | cursor_index: 0, 59 | cursor_after: false, 60 | hovering: false, 61 | is_password: false, 62 | cache: font::Paragraphs::new(), 63 | selecting: false, 64 | } 65 | } 66 | 67 | pub fn set_value(&mut self, s: String) { 68 | self.value = s.chars().collect(); 69 | self.drawn = 0; 70 | } 71 | 72 | pub fn append_value(&mut self, s: &str) { 73 | let mut s: Vec = s.chars().collect(); 74 | self.value.append(&mut s); 75 | self.drawn = 0; 76 | } 77 | 78 | pub fn get_value(&self) -> String { 79 | self.value.clone().iter().collect() 80 | } 81 | 82 | pub fn set_editable(&mut self, editable: bool) { 83 | self.editable = editable; 84 | self.drawn = 0; 85 | if !editable { 86 | self.focus = false; 87 | } 88 | } 89 | 90 | pub fn get_editable(&self) -> bool { 91 | self.editable 92 | } 93 | 94 | pub fn set_is_password(&mut self, val: bool) { 95 | self.is_password = val; 96 | } 97 | 98 | pub fn set_singleline(&mut self, singleline: bool) { 99 | self.singleline = singleline; 100 | } 101 | 102 | pub fn get_cursor_index(&self) -> (usize,bool) { 103 | match self.cursor { 104 | None => (0,false), 105 | Some((ref ch, ref p)) => { 106 | let ind = ch.get_index(); 107 | let pos = ch.get_position(); 108 | let adv = ch.get_metric().advance; 109 | let mut after = false; 110 | if p.x > pos.x + (adv.x/2.) { 111 | //ind += 1; 112 | after = true; 113 | } 114 | (ind,after) 115 | } 116 | } 117 | } 118 | 119 | pub fn set_placeholder(&mut self, p: String) { 120 | self.placeholder = p.chars().collect(); 121 | } 122 | 123 | pub fn get_placeholder(&self) -> String { 124 | self.placeholder.clone().iter().collect() 125 | } 126 | 127 | fn set_cursor(&mut self, p: &Position){ 128 | let tmp = self.cache.get_char_at_pos(&p, &self.value); 129 | if tmp.is_some() { 130 | self.cursor = Some((tmp.unwrap(), p.clone())); 131 | let tmp = self.get_cursor_index(); 132 | self.cursor_index = tmp.0; 133 | self.cursor_after = tmp.1; 134 | //println!("Clicked at ind[{}] {:?} ... appears after? {}", self.cursor_index, self.cursor, self.cursor_after); 135 | } 136 | } 137 | } 138 | 139 | impl Element for TextBox { 140 | fn get_ext_id(&self) -> u64 { 141 | self.ext_id 142 | } 143 | 144 | fn set(&mut self, prop: properties::Property) { 145 | self.props.set(prop); 146 | } 147 | 148 | /*fn get(&self, prop: &properties::Property) -> Option<&properties::Property> { 149 | self.props.get(&prop) 150 | }*/ 151 | 152 | fn get_properties(&self) -> properties::Properties { 153 | self.props.clone() 154 | } 155 | 156 | fn render( 157 | &mut self, 158 | _api: &RenderApi, 159 | builder: &mut DisplayListBuilder, 160 | extent: properties::Extent, 161 | font_store: &mut font::FontStore, 162 | _props: Option>, 163 | gen: &mut properties::IdGenerator, 164 | ) { 165 | let _id = gen.get(); 166 | self.ext_id = _id; 167 | 168 | let size = self.props.get_size() as f32; 169 | let family = self.props.get_family(); 170 | let mut color = self.props.get_color(); 171 | let mut bgcolor = self.props.get_bg_color(); 172 | let width = self.props.get_width(); 173 | let height = self.props.get_height(); 174 | let text_align = self.props.get_text_align(); 175 | 176 | if self.hovering { 177 | color = self.props.get_hover_color(); 178 | bgcolor = self.props.get_hover_bg_color(); 179 | } 180 | 181 | if self.focus && self.editable { 182 | color = self.props.get_focus_color(); 183 | bgcolor = self.props.get_focus_bg_color(); 184 | } 185 | 186 | if !self.enabled { 187 | color = self.props.get_disabled_color(); 188 | bgcolor = self.props.get_disabled_bg_color(); 189 | } 190 | 191 | if self.value.is_empty() && !self.placeholder.is_empty() && !self.focus && !self.hovering { 192 | color = self.props.get_disabled_color(); 193 | } 194 | 195 | let (_f_key, fi_key) = font_store.get_font_instance(&family, size as i32); 196 | 197 | let val_str = "●".repeat(self.value.len()).chars().collect(); 198 | 199 | let value = if !self.is_password { 200 | &self.value 201 | } else { 202 | &val_str 203 | }; 204 | 205 | let value = if value.is_empty() { 206 | &self.placeholder 207 | } else { 208 | &value 209 | }; 210 | 211 | let metrics = font_store.get_font_metrics(&family); 212 | let baseline = match metrics { 213 | Some(metrics) => { 214 | let tmp = metrics.ascent - metrics.descent; 215 | let tmp = size / tmp; 216 | tmp * (metrics.ascent) 217 | } 218 | None => size, 219 | }; 220 | 221 | let mut paras = font::Paragraphs::from_chars(value); 222 | paras.shape( 223 | extent.x, 224 | extent.y, 225 | extent.w, 226 | extent.h, 227 | size, 228 | baseline, 229 | &family, 230 | &text_align, 231 | ); 232 | let _bounds = paras.get_extent(); 233 | let glyphs = paras.glyphs(); 234 | 235 | if !self.value.is_empty() { 236 | self.cache = paras; 237 | } 238 | 239 | /*let (mut cursor_x, mut cursor_y, cursor_i) = (0.0, 0.0, self.cursor); 240 | 241 | if cursor_i == 0 && self.cache.is_empty() { 242 | cursor_x = _bounds.x; 243 | cursor_y = _bounds.y + size; 244 | } else if cursor_i == 0 && !self.cache.is_empty() { 245 | cursor_x = (self.cache[0].0).0; 246 | cursor_y = (self.cache[0].1).1; 247 | } else if !self.cache.is_empty() { 248 | cursor_x = (self.cache[cursor_i - 1].1).0; 249 | cursor_y = (self.cache[cursor_i - 1].1).1; 250 | }*/ 251 | 252 | let mut calc_w = _bounds.w; 253 | let mut calc_h = _bounds.h; 254 | 255 | calc_w = match width { 256 | properties::Unit::Extent => extent.w, 257 | properties::Unit::Pixel(px) => px, 258 | properties::Unit::Stretch(s) => s * extent.w, 259 | properties::Unit::Natural => calc_w, 260 | }; 261 | 262 | calc_h = match height { 263 | properties::Unit::Extent => extent.h, 264 | properties::Unit::Pixel(px) => px, 265 | properties::Unit::Stretch(s) => s * extent.h, 266 | properties::Unit::Natural => calc_h, 267 | }; 268 | 269 | self.bounds = properties::Extent { 270 | x: extent.x, 271 | y: extent.y, 272 | w: calc_w, 273 | h: calc_h, 274 | dpi: extent.dpi, 275 | }; 276 | 277 | let mut info = LayoutPrimitiveInfo::new(LayoutRect::new( 278 | LayoutPoint::new(extent.x, extent.y), 279 | LayoutSize::new(self.bounds.w, self.bounds.h), 280 | )); 281 | info.tag = Some((_id, 0)); 282 | builder.push_rect(&info, bgcolor); 283 | 284 | let info = LayoutPrimitiveInfo::new(LayoutRect::new( 285 | LayoutPoint::new(extent.x, extent.y), 286 | LayoutSize::new(self.bounds.w, self.bounds.h), 287 | )); 288 | builder.push_text(&info, &glyphs, fi_key, color, Some(GlyphOptions::default())); 289 | 290 | //add the cursor 291 | if self.focus && self.enabled && self.editable { 292 | let ch = self.cache.get_char_at_index(self.cursor_index); 293 | match ch { 294 | Some(ref ch) => { 295 | let pos = ch.get_position(); 296 | let mut info = LayoutPrimitiveInfo::new(LayoutRect::new( 297 | LayoutPoint::new(pos.x, pos.y - size), 298 | LayoutSize::new(1.0, size), 299 | )); 300 | if self.cursor_after { 301 | info = LayoutPrimitiveInfo::new(LayoutRect::new( 302 | LayoutPoint::new(pos.x + ch.get_metric().advance.x, pos.y - size), 303 | LayoutSize::new(1.0, size), 304 | )); 305 | } 306 | builder.push_rect(&info, color); 307 | }, 308 | None => (), 309 | } 310 | } 311 | } 312 | 313 | fn get_bounds(&self) -> properties::Extent { 314 | self.bounds.clone() 315 | } 316 | 317 | fn on_primitive_event(&mut self, ext_ids: &[ItemTag], e: PrimitiveEvent) -> bool { 318 | let mut handled = false; 319 | match e { 320 | PrimitiveEvent::KeyInput(vkc, _sc, _s, _m) => match vkc { 321 | _ => (), 322 | }, 323 | PrimitiveEvent::Char(mut c) => { 324 | if self.focus && self.enabled && self.editable { 325 | if c == '\x08' { //backspace 326 | let len = self.value.len(); 327 | //if len > 0 and after is true then should delete from index 328 | if len > 0 && self.cursor_after { 329 | self.value.remove(self.cursor_index); 330 | if self.cursor_index == 0 { 331 | self.cursor_after = false; 332 | } else { 333 | self.cursor_index -= 1; 334 | } 335 | } 336 | // if len > 0 and after is false 337 | else if len > 0 && !self.cursor_after { 338 | if self.cursor_index > 0 { 339 | self.value.remove(self.cursor_index - 1); 340 | self.cursor_index -= 1; 341 | } 342 | } 343 | } else if c == '\u{7f}' { //delete key 344 | let len = self.value.len(); 345 | if len > self.cursor_index+1 && self.cursor_after { 346 | self.value.remove(self.cursor_index+1); 347 | } 348 | else if len > self.cursor_index && !self.cursor_after{ 349 | self.value.remove(self.cursor_index); 350 | let len = self.value.len(); 351 | if self.cursor_index > 0 && self.cursor_index == len && len > 0 { 352 | self.cursor_index -=1; 353 | self.cursor_after = true; 354 | } 355 | } 356 | 357 | } else if c == '\u{3}' { 358 | 359 | } else if c == '\u{16}' { 360 | 361 | } else { 362 | if c == '\r' { 363 | c = '\n'; 364 | } 365 | if self.cursor.is_some() { 366 | if self.cursor_after { 367 | self.value.insert(self.cursor_index + 1,c); 368 | } else { 369 | self.value.insert(self.cursor_index,c); 370 | } 371 | if self.value.len() == 1 { 372 | self.cursor_index = 0; 373 | self.cursor_after = true; 374 | } 375 | else { 376 | self.cursor_index += 1; 377 | } 378 | } 379 | } 380 | handled = true; 381 | } 382 | }, 383 | PrimitiveEvent::SetFocus(f) => { 384 | if self.enabled && self.focus != f { 385 | self.focus = f; 386 | handled = self.exec_handler(ElementEvent::FocusChange, &f); 387 | } 388 | }, 389 | PrimitiveEvent::Button(p, b, s, m) => { 390 | if !ext_ids.is_empty() 391 | && ext_ids[0].0 == self.ext_id 392 | && b == properties::Button::Left 393 | { 394 | if s == properties::ButtonState::Pressed{ 395 | self.selecting = true; 396 | self.set_cursor(&p); 397 | } 398 | else if s == properties::ButtonState::Released 399 | { 400 | self.selecting = false; 401 | self.set_cursor(&p); 402 | handled = self.exec_handler(ElementEvent::Clicked, &m); 403 | } 404 | } 405 | }, 406 | PrimitiveEvent::CursorMoved(p) => { 407 | 408 | if self.selecting && !ext_ids.is_empty() && ext_ids[0].0 == self.ext_id{ 409 | 410 | println!("cursor moved"); 411 | self.set_cursor(&p); 412 | 413 | /*let tmp = self.cache.get_char_at_pos(&p, &self.value); 414 | if tmp.is_some() { 415 | self.cursor = Some((tmp.unwrap(), p.clone())); 416 | let tmp = self.get_cursor_index(); 417 | self.cursor_index = tmp.0; 418 | self.cursor_after = tmp.1; 419 | println!("Clicked at ind[{}] {:?} ... appears after? {}", self.cursor_index, self.cursor, self.cursor_after); 420 | }*/ 421 | } 422 | }, 423 | PrimitiveEvent::HoverBegin(n_tags) => { 424 | let matched = n_tags.iter().find(|x| x.0 == self.ext_id); 425 | if matched.is_some() { 426 | self.hovering = true; 427 | } 428 | }, 429 | PrimitiveEvent::HoverEnd(o_tags) => { 430 | let matched = o_tags.iter().find(|x| x.0 == self.ext_id); 431 | if matched.is_some() { 432 | self.hovering = false; 433 | } 434 | } 435 | _ => (), 436 | } 437 | handled 438 | } 439 | 440 | fn set_handler(&mut self, e: ElementEvent, f: EventFn) { 441 | self.event_handlers.insert(e, f); 442 | } 443 | 444 | fn exec_handler(&mut self, e: ElementEvent, d: &dyn Any) -> bool { 445 | let h = self.event_handlers.get_mut(&e).cloned(); 446 | if let Some(mut h) = h { 447 | h.call(self, d) 448 | } else { 449 | false 450 | } 451 | } 452 | 453 | fn as_any(&self) -> &dyn Any { 454 | self 455 | } 456 | fn as_any_mut(&mut self) -> &mut dyn Any { 457 | self 458 | } 459 | } 460 | 461 | impl CanDisable for TextBox { 462 | fn set_enabled(&mut self, value: bool) { 463 | self.enabled = value; 464 | } 465 | 466 | fn get_enabled(&self) -> bool { 467 | self.enabled 468 | } 469 | } 470 | -------------------------------------------------------------------------------- /src/gui/window.rs: -------------------------------------------------------------------------------- 1 | use euclid; 2 | use gleam::gl; 3 | use glutin; 4 | 5 | use webrender; 6 | use webrender::api::*; 7 | 8 | use crate::elements::{Element, PrimitiveEvent}; 9 | use crate::gui::font; 10 | use crate::gui::properties; 11 | use crate::util::*; 12 | 13 | use std::mem; 14 | use std::ops::DerefMut; 15 | use std::sync::{Arc, Mutex}; 16 | use std::thread; 17 | use std::time::{Duration, SystemTime}; 18 | use std::fmt; 19 | 20 | impl Into for glutin::dpi::LogicalPosition { 21 | fn into(self) -> properties::Position { 22 | properties::Position { 23 | x: self.x as f32, 24 | y: self.y as f32, 25 | } 26 | } 27 | } 28 | 29 | impl Into for glutin::dpi::PhysicalPosition { 30 | fn into(self) -> properties::Position { 31 | properties::Position { 32 | x: self.x as f32, 33 | y: self.y as f32, 34 | } 35 | } 36 | } 37 | 38 | impl Into for WorldPoint { 39 | fn into(self) -> properties::Position { 40 | match self { 41 | WorldPoint { x, y, _unit } => properties::Position { x, y }, 42 | } 43 | } 44 | } 45 | 46 | impl Into for glutin::ModifiersState { 47 | fn into(self) -> properties::Modifiers { 48 | properties::Modifiers { 49 | shift: self.shift, 50 | ctrl: self.ctrl, 51 | alt: self.alt, 52 | logo: self.logo, 53 | } 54 | } 55 | } 56 | 57 | impl Into for glutin::MouseButton { 58 | fn into(self) -> properties::Button { 59 | match self { 60 | glutin::MouseButton::Left => properties::Button::Left, 61 | glutin::MouseButton::Right => properties::Button::Right, 62 | glutin::MouseButton::Middle => properties::Button::Middle, 63 | glutin::MouseButton::Other(_) => properties::Button::Other, 64 | } 65 | } 66 | } 67 | 68 | impl Into for glutin::ElementState { 69 | fn into(self) -> properties::ButtonState { 70 | match self { 71 | glutin::ElementState::Pressed => properties::ButtonState::Pressed, 72 | glutin::ElementState::Released => properties::ButtonState::Released, 73 | } 74 | } 75 | } 76 | 77 | struct WindowNotifier { 78 | events_proxy: glutin::EventsLoopProxy, 79 | } 80 | 81 | impl WindowNotifier { 82 | fn new(events_proxy: glutin::EventsLoopProxy) -> WindowNotifier { 83 | WindowNotifier { events_proxy } 84 | } 85 | } 86 | 87 | impl RenderNotifier for WindowNotifier { 88 | fn clone(&self) -> Box { 89 | Box::new(WindowNotifier { 90 | events_proxy: self.events_proxy.clone(), 91 | }) 92 | } 93 | 94 | fn wake_up(&self) { 95 | #[cfg(not(target_os = "android"))] 96 | let _ = self.events_proxy.wakeup(); 97 | } 98 | 99 | fn new_frame_ready( 100 | &self, 101 | _doc_id: DocumentId, 102 | _scrolled: bool, 103 | _composite_needed: bool, 104 | _render_time: Option, 105 | ) { 106 | self.wake_up(); 107 | } 108 | } 109 | 110 | 111 | struct Internals { 112 | gl_window: Option>, 113 | events_loop: glutin::EventsLoop, 114 | font_store: Arc>, 115 | api: RenderApi, 116 | document_id: DocumentId, 117 | pipeline_id: PipelineId, 118 | epoch: Epoch, 119 | renderer: webrender::Renderer, 120 | cursor_position: WorldPoint, 121 | dpi: f64, 122 | cursor_in_window: bool, 123 | } 124 | 125 | impl fmt::Debug for Internals { 126 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 127 | write!(f, "Internals {{ window_id: {:?}, document_id: {:?}, pipeline_id: {:?}, cursor_position: {}, dpi: {} }}", 128 | self.get_window_id()/*gl_window.window().id()*/, self.document_id, self.pipeline_id, self.cursor_position, self.dpi ) 129 | } 130 | } 131 | 132 | impl Internals { 133 | fn new(name: &str, width: f64, height: f64) -> Internals { 134 | let events_loop = glutin::EventsLoop::new(); 135 | let window_builder = glutin::WindowBuilder::new() 136 | .with_title(name) 137 | .with_multitouch() 138 | .with_dimensions(glutin::dpi::LogicalSize::new(width, height)); 139 | let window = 140 | glutin::ContextBuilder::new() 141 | .with_gl(glutin::GlRequest::GlThenGles { 142 | opengl_version: (3, 2), 143 | opengles_version: (3, 0), 144 | }) 145 | .build_windowed(window_builder, &events_loop) 146 | .unwrap(); 147 | 148 | let window = unsafe { 149 | window.make_current().unwrap() 150 | }; 151 | 152 | let gl = match window.get_api() { 153 | glutin::Api::OpenGl => unsafe { 154 | gl::GlFns::load_with(|symbol| window.get_proc_address(symbol) as *const _) 155 | }, 156 | glutin::Api::OpenGlEs => unsafe { 157 | gl::GlesFns::load_with(|symbol| window.get_proc_address(symbol) as *const _) 158 | }, 159 | glutin::Api::WebGl => unimplemented!(), 160 | }; 161 | 162 | let dpi = window.window().get_hidpi_factor(); 163 | 164 | let opts = webrender::RendererOptions { 165 | device_pixel_ratio: dpi as f32, 166 | clear_color: Some(ColorF::new(0.2, 0.2, 0.2, 1.0)), 167 | //enable_scrollbars: true, 168 | //enable_aa:true, 169 | ..webrender::RendererOptions::default() 170 | }; 171 | 172 | let framebuffer_size = { 173 | let size = window.window().get_inner_size().unwrap().to_physical(dpi); 174 | DeviceIntSize::new(size.width as i32, size.height as i32) 175 | //DeviceUintSize::new(size.width as u32, size.height as u32) 176 | }; 177 | 178 | let notifier = Box::new(WindowNotifier::new(events_loop.create_proxy())); 179 | let (renderer, sender) = 180 | webrender::Renderer::new(gl.clone(), notifier, opts, None).unwrap(); 181 | let api = sender.create_api(); 182 | let document_id = api.add_document(framebuffer_size, 0); 183 | 184 | let epoch = Epoch(0); 185 | let pipeline_id = PipelineId(0, 0); 186 | 187 | let font_store = Arc::new(Mutex::new(font::FontStore::new( 188 | api.clone_sender().create_api(), 189 | document_id, 190 | ))); 191 | 192 | let mut txn = Transaction::new(); 193 | txn.set_root_pipeline(pipeline_id); 194 | api.send_transaction(document_id, txn); 195 | 196 | let window = unsafe{window.make_not_current().unwrap()}; 197 | 198 | Internals { 199 | gl_window: Some(window), 200 | events_loop, 201 | font_store, 202 | api, 203 | document_id, 204 | pipeline_id, 205 | epoch, 206 | renderer, 207 | cursor_position: WorldPoint::new(0.0, 0.0), 208 | dpi, 209 | cursor_in_window: false, 210 | } 211 | } 212 | 213 | fn events(&mut self, tags: &[ItemTag]) -> Vec { 214 | let mut events = Vec::new(); 215 | 216 | let mut cursor_in_window = self.cursor_in_window; 217 | let mut cursor_position = self.cursor_position; 218 | let mut dpi = self.dpi; 219 | let mut txn = None; 220 | 221 | self.events_loop.poll_events(|event| { 222 | match event { 223 | glutin::Event::Awakened => return, 224 | _ => () 225 | } 226 | //println!("event -> {:?}", &event); 227 | match event { 228 | glutin::Event::WindowEvent { 229 | event: glutin::WindowEvent::CloseRequested, 230 | window_id, 231 | } => { 232 | { 233 | TODEL.lock().unwrap().push(window_id); 234 | } 235 | events.push(PrimitiveEvent::Exit); 236 | } 237 | glutin::Event::WindowEvent { 238 | event: glutin::WindowEvent::CursorEntered { .. }, 239 | window_id, 240 | } => { 241 | //events.push(PrimitiveEvent::CursorEntered); 242 | println!("window ID cursor entered ... {:?}", window_id); 243 | cursor_in_window = true; 244 | } 245 | glutin::Event::WindowEvent { 246 | event: glutin::WindowEvent::CursorMoved { position, .. }, 247 | .. 248 | } => { 249 | //until tomaka/winit#807 is fixed, we'll just not check whether the cursor is in the window 250 | if cfg!(target_os = "macos"){ 251 | cursor_position = WorldPoint::new(position.x as f32, position.y as f32); 252 | events.push(PrimitiveEvent::CursorMoved(position.into())); 253 | } else { 254 | if cursor_in_window { 255 | cursor_position = WorldPoint::new(position.x as f32, position.y as f32); 256 | events.push(PrimitiveEvent::CursorMoved(position.into())); 257 | } 258 | } 259 | } 260 | glutin::Event::WindowEvent { 261 | event: glutin::WindowEvent::CursorLeft { .. }, 262 | window_id, 263 | .. 264 | } => { 265 | //events.push(PrimitiveEvent::CursorLeft); 266 | 267 | println!("window ID cursor left ... {:?}", window_id); 268 | cursor_in_window = false; 269 | cursor_position.x = -1.0; 270 | cursor_position.y = -1.0; 271 | } 272 | glutin::Event::WindowEvent { 273 | event: glutin::WindowEvent::Resized(size), 274 | .. 275 | } => { 276 | events.push(PrimitiveEvent::Resized(size)); 277 | } 278 | glutin::Event::WindowEvent { 279 | event: glutin::WindowEvent::HiDpiFactorChanged(factor), 280 | .. 281 | } => { 282 | dpi = factor; 283 | events.push(PrimitiveEvent::DPI(factor)); 284 | } 285 | glutin::Event::WindowEvent { 286 | event: 287 | glutin::WindowEvent::MouseInput { 288 | state, 289 | button, 290 | modifiers, 291 | .. 292 | }, 293 | .. 294 | } => { 295 | let _pos: properties::Position = cursor_position.into(); 296 | let _button = button.into(); 297 | let _state = state.into(); 298 | let _modifiers = modifiers.into(); 299 | 300 | if !tags.is_empty() 301 | && button == glutin::MouseButton::Left 302 | && state == glutin::ElementState::Released 303 | { 304 | events.push(PrimitiveEvent::SetFocus(true)); 305 | } 306 | events.push(PrimitiveEvent::Button(_pos, _button, _state, _modifiers)); 307 | } 308 | glutin::Event::WindowEvent { 309 | event: 310 | glutin::WindowEvent::MouseWheel { 311 | delta, modifiers, .. 312 | }, 313 | .. 314 | } => { 315 | if txn.is_none() { 316 | txn = Some(Transaction::new()); 317 | } 318 | const LINE_HEIGHT: f32 = 38.0; 319 | let (dx, dy) = if modifiers.alt { 320 | match delta { 321 | glutin::MouseScrollDelta::LineDelta(_, dy) => (dy * LINE_HEIGHT, 0.0), 322 | glutin::MouseScrollDelta::PixelDelta(pos) => (pos.y as f32, 0.0), 323 | } 324 | } else { 325 | match delta { 326 | glutin::MouseScrollDelta::LineDelta(_, dy) => (0.0, dy * LINE_HEIGHT), 327 | glutin::MouseScrollDelta::PixelDelta(pos) => (0.0, pos.y as f32), 328 | } 329 | }; 330 | 331 | if let Some(ref mut _txn) = txn { 332 | _txn.scroll( 333 | ScrollLocation::Delta(LayoutVector2D::new(dx, dy)), 334 | cursor_position, 335 | ); 336 | } 337 | 338 | //println!("scrolling {} {}",dx,dy); 339 | } 340 | glutin::Event::WindowEvent { 341 | event: 342 | glutin::WindowEvent::KeyboardInput { 343 | input: 344 | glutin::KeyboardInput { 345 | scancode, 346 | state, 347 | virtual_keycode, 348 | modifiers, 349 | }, 350 | .. 351 | }, 352 | .. 353 | } => { 354 | events.push(PrimitiveEvent::KeyInput( 355 | virtual_keycode, 356 | scancode, 357 | state.into(), 358 | modifiers.into(), 359 | )); 360 | } 361 | glutin::Event::WindowEvent { 362 | event: glutin::WindowEvent::ReceivedCharacter(c), 363 | .. 364 | } => { 365 | if c == '\x1b' { 366 | events.push(PrimitiveEvent::SetFocus(false)); 367 | } else { 368 | events.push(PrimitiveEvent::Char(c)); 369 | } 370 | } 371 | _ => (), 372 | } 373 | }); 374 | 375 | self.dpi = dpi; 376 | 377 | if let Some(mut _txn) = txn { 378 | _txn.generate_frame(); 379 | self.api.send_transaction(self.document_id, _txn); 380 | } 381 | 382 | self.cursor_in_window = cursor_in_window; 383 | self.cursor_position = cursor_position; 384 | 385 | events 386 | } 387 | 388 | fn get_window_id(&self) -> Option { 389 | match self.gl_window { 390 | None => None, 391 | Some(ref window) => Some(window.window().id()), 392 | } 393 | 394 | } 395 | 396 | fn deinit(self) { 397 | self.font_store.lock().unwrap().deinit(); 398 | self.renderer.deinit(); 399 | } 400 | } 401 | 402 | pub struct Window { 403 | width: f64, 404 | height: f64, 405 | root: Arc>, 406 | name: String, 407 | id_generator: properties::IdGenerator, 408 | internals: Option, 409 | tags: Vec, 410 | } 411 | 412 | impl fmt::Debug for Window { 413 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 414 | write!(f, "Window {{ name: {}, width: {}, height: {}, internals: {:?} }}", self.name, self.width, self.height, self.internals) 415 | } 416 | } 417 | 418 | impl Window { 419 | pub fn new(root: Arc>, name: String, width: f64, height: f64) -> Window { 420 | let id_generator = properties::IdGenerator::new(0); 421 | 422 | let mut _w = Window { 423 | width, 424 | height, 425 | root, 426 | name, 427 | id_generator, 428 | internals: None, 429 | tags: vec![], 430 | }; 431 | 432 | _w.start_window(); 433 | 434 | _w 435 | } 436 | 437 | fn start_window(&mut self) { 438 | self.internals = Some(Internals::new(&self.name, self.width, self.height)); 439 | } 440 | 441 | fn get_tags(&mut self) -> (Vec, Vec) { 442 | let mut tags: Vec = vec![]; 443 | 444 | let mut new_tags: Vec = vec![]; 445 | let mut old_tags: Vec = vec![]; 446 | 447 | if let Some(ref mut i) = self.internals { 448 | if i.cursor_position.x > 0.0 && i.cursor_position.y > 0.0 { 449 | let results = i.api.hit_test( 450 | i.document_id, 451 | None, 452 | i.cursor_position, 453 | HitTestFlags::FIND_ALL, 454 | ); 455 | let mut ind = results.items.len(); 456 | while ind > 0 { 457 | ind -= 1; 458 | tags.push(results.items[ind].tag); 459 | } 460 | } 461 | } 462 | 463 | if self.tags.is_empty() { 464 | self.tags = tags.clone(); 465 | new_tags = tags.clone(); 466 | } else { 467 | for t in tags.iter() { 468 | let exists = self.tags.iter().find(|x| *x == t); 469 | if exists.is_none() { 470 | new_tags.push(t.clone()); 471 | } 472 | } 473 | 474 | for t in self.tags.iter() { 475 | let exists = tags.iter().find(|x| *x == t); 476 | if exists.is_none() { 477 | old_tags.push(t.clone()); 478 | } 479 | } 480 | 481 | self.tags = tags.clone(); 482 | } 483 | 484 | if !new_tags.is_empty() || !old_tags.is_empty() { 485 | println! {"HoverBegin {:?}\nHoverEnd {:?}", new_tags, old_tags}; 486 | } 487 | 488 | (new_tags, old_tags) 489 | } 490 | 491 | fn action_events(&mut self, events: Vec, tags: &Vec) { 492 | for e in events.iter() { 493 | /*if exit { 494 | return true; 495 | }*/ 496 | match e { 497 | /*PrimitiveEvent::Exit => { 498 | //exit = true; 499 | },*/ 500 | PrimitiveEvent::CursorLeft => { 501 | self.tags.clear(); 502 | } 503 | PrimitiveEvent::Resized(size) => { 504 | self.width = size.width; 505 | self.height = size.height; 506 | } 507 | PrimitiveEvent::SetFocus(b) => { 508 | if !*b { 509 | self.root.lock().unwrap().on_primitive_event(&[], e.clone()); 510 | } else { 511 | self.root 512 | .lock() 513 | .unwrap() 514 | .on_primitive_event(&tags, e.clone()); 515 | } 516 | } 517 | PrimitiveEvent::Button(_, _, _, _) => { 518 | self.root 519 | .lock() 520 | .unwrap() 521 | .on_primitive_event(&tags, e.clone()); 522 | } 523 | PrimitiveEvent::Char(_) => { 524 | self.root 525 | .lock() 526 | .unwrap() 527 | .on_primitive_event(&tags, e.clone()); 528 | } 529 | PrimitiveEvent::CursorMoved(_) => { 530 | self.root 531 | .lock() 532 | .unwrap() 533 | .on_primitive_event(&tags, e.clone()); 534 | } 535 | PrimitiveEvent::KeyInput(_, _, _, _) => { 536 | self.root 537 | .lock() 538 | .unwrap() 539 | .on_primitive_event(&tags, e.clone()); 540 | } 541 | _ => (), 542 | } 543 | } 544 | } 545 | 546 | pub fn tick(&mut self) -> bool { 547 | let exit = false; 548 | 549 | let events; 550 | let mut dpi; 551 | let api; 552 | 553 | let (new_tags, old_tags) = self.get_tags(); 554 | let tags = self.tags.clone(); 555 | 556 | //collect events 557 | match self.internals { 558 | Some(ref mut i) => { 559 | events = i.events(&tags); 560 | dpi = i.dpi; 561 | api = i.api.clone_sender().create_api(); 562 | }, 563 | _ => panic!("in tick but no window internals initialized"), 564 | } 565 | 566 | if !new_tags.is_empty() { 567 | //events.insert(0,PrimitiveEvent::HoverBegin(new_tags)); 568 | self.root 569 | .lock() 570 | .unwrap() 571 | .on_primitive_event(&[], PrimitiveEvent::HoverBegin(new_tags)); 572 | } 573 | 574 | if !old_tags.is_empty() { 575 | //events.insert( 0,PrimitiveEvent::HoverEnd(old_tags)); 576 | self.root 577 | .lock() 578 | .unwrap() 579 | .on_primitive_event(&[], PrimitiveEvent::HoverEnd(old_tags)); 580 | } 581 | 582 | //if events.len() > 0 { 583 | // println!("{:?}", events); 584 | //} 585 | 586 | self.action_events(events, &tags); 587 | 588 | let mut window: Option> = None; 589 | 590 | match self.internals { 591 | Some(ref mut i) => { 592 | //begin 593 | std::mem::swap(&mut i.gl_window, &mut window); 594 | }, 595 | _ => () 596 | } 597 | 598 | let window = unsafe{window.unwrap().make_current().unwrap()}; 599 | 600 | //let win_id = window.window().id(); 601 | let mut txn = Transaction::new(); 602 | let mut builder = None; 603 | let mut font_store = None; 604 | 605 | let (layout_size, framebuffer_size) = match self.internals { 606 | Some(ref mut i) => { 607 | dpi = window.window().get_hidpi_factor(); 608 | let framebuffer_size = { 609 | let size = window.window().get_inner_size().unwrap().to_physical(dpi); 610 | DeviceIntSize::new(size.width as i32, size.height as i32) 611 | //DeviceUintSize::new(size.width as u32, size.height as u32) 612 | }; 613 | let layout_size = framebuffer_size.to_f32() / euclid::TypedScale::new(dpi as f32); 614 | 615 | builder = Some(DisplayListBuilder::new(i.pipeline_id, layout_size)); 616 | 617 | font_store = Some(i.font_store.clone()); 618 | 619 | (Some(layout_size), Some(framebuffer_size)) 620 | }, 621 | _ => (None, None) 622 | }; 623 | 624 | let mut builder = builder.unwrap(); 625 | let font_store = font_store.unwrap(); 626 | let mut font_store = font_store.lock().unwrap(); 627 | let font_store = font_store.deref_mut(); 628 | let framebuffer_size = framebuffer_size.unwrap(); 629 | let layout_size = layout_size.unwrap(); 630 | 631 | self.render_root(&api, &mut builder, font_store, dpi as f32); 632 | 633 | if let Some(ref mut i) = self.internals { 634 | txn.set_window_parameters( 635 | framebuffer_size, 636 | DeviceIntRect::new(DeviceIntPoint::zero(), framebuffer_size), 637 | //DeviceUintRect::new(DeviceUintPoint::zero(), framebuffer_size), 638 | dpi as f32, 639 | ); 640 | 641 | txn.set_display_list(i.epoch, None, layout_size, builder.finalize(), true); 642 | //txn.set_root_pipeline(i.pipeline_id); 643 | txn.generate_frame(); 644 | i.api.send_transaction(i.document_id, txn); 645 | 646 | i.renderer.update(); 647 | i.renderer.render(framebuffer_size).unwrap(); 648 | let _ = i.renderer.flush_pipeline_info(); 649 | window.swap_buffers().ok(); 650 | } 651 | 652 | let mut window = unsafe{Some(window.make_not_current().unwrap())}; 653 | match self.internals { 654 | Some(ref mut i) => { 655 | //Finally 656 | std::mem::swap(&mut i.gl_window, &mut window); 657 | }, 658 | _ => () 659 | } 660 | 661 | exit 662 | } 663 | 664 | fn render_root( 665 | &mut self, 666 | api: &RenderApi, 667 | builder: &mut DisplayListBuilder, 668 | font_store: &mut font::FontStore, 669 | dpi: f32, 670 | ) { 671 | let mut gen = self.id_generator.clone(); 672 | gen.zero(); 673 | 674 | let info = LayoutPrimitiveInfo::new((0.0, 0.0).by(self.width as f32, self.height as f32)); 675 | builder.push_stacking_context( 676 | &info, 677 | None, 678 | TransformStyle::Flat, 679 | MixBlendMode::Normal, 680 | &[], 681 | RasterSpace::Screen, 682 | ); 683 | 684 | self.root.lock().unwrap().render( 685 | api, 686 | builder, 687 | properties::Extent { 688 | x: 0.0, 689 | y: 0.0, 690 | w: self.width as f32, 691 | h: self.height as f32, 692 | dpi, 693 | }, 694 | font_store, 695 | None, 696 | &mut gen, 697 | ); 698 | 699 | builder.pop_stacking_context(); 700 | } 701 | } 702 | 703 | impl Drop for Window { 704 | fn drop(&mut self) { 705 | let mut x = None; 706 | mem::swap(&mut x, &mut self.internals); 707 | let x = x.unwrap(); 708 | x.deinit(); 709 | } 710 | } 711 | 712 | lazy_static! { 713 | static ref TOADD: Mutex>, String, f64, f64)>> = Mutex::new(vec![]); 714 | static ref TODEL: Mutex> = Mutex::new(vec![]); 715 | } 716 | 717 | pub struct Manager { 718 | windows: Vec, 719 | } 720 | 721 | impl Manager { 722 | fn get() -> Option>> { 723 | static mut MANAGER: Option>> = None; 724 | 725 | unsafe { 726 | if MANAGER.is_none() { 727 | MANAGER = Some(Arc::new(Mutex::new(Manager { windows: vec![] }))); 728 | } 729 | 730 | MANAGER.clone() 731 | } 732 | } 733 | 734 | pub fn start(fps: u64) { 735 | let fps = 1000 / fps; 736 | let mut lasttime; 737 | loop { 738 | lasttime = SystemTime::now(); 739 | let mut i = 0; 740 | let mut wmo = Manager::get(); 741 | if let Some(ref mut _wmo) = wmo { 742 | if let Ok(ref mut wm) = _wmo.lock() { 743 | //add the windows to be added 744 | if let Ok(ref mut to_add) = TOADD.lock() { 745 | loop { 746 | if to_add.len() > 0 { 747 | let _t = to_add.remove(0); 748 | wm.windows.push(Window::new(_t.0, _t.1, _t.2, _t.3)); 749 | } else { 750 | break; 751 | } 752 | } 753 | } 754 | //render the windows 755 | while i < wm.windows.len() { 756 | wm.windows[i].tick(); 757 | i += 1; 758 | } 759 | //Remove Windows not required 760 | if let Ok(ref mut to_del) = TODEL.lock() { 761 | for wid in to_del.iter() { 762 | let wid = wid.clone(); 763 | println!("Drop window ID {:?}, thread ID: {:?}", wid, thread::current().id()); 764 | 765 | for i in 0..wm.windows.len(){ 766 | if let Some(ref mut internal) = wm.windows[i].internals { 767 | if wid == internal.get_window_id().unwrap() { 768 | internal.api.shut_down(); 769 | let x = wm.windows.remove(i); 770 | drop(x); 771 | println!("Window ID dropped {:?}, thread ID: {:?}", wid, thread::current().id()); 772 | break; 773 | } 774 | } 775 | } 776 | } 777 | to_del.clear(); 778 | } 779 | //if all windows done, then exit the app 780 | if wm.windows.is_empty() { 781 | return; 782 | } 783 | } 784 | } 785 | if let Ok(t) = lasttime.elapsed() { 786 | let mut t = u64::from(t.subsec_millis()); 787 | if t > fps { 788 | t = fps; 789 | } 790 | thread::sleep(Duration::from_millis(fps - t)); 791 | } else { 792 | thread::sleep(Duration::from_millis(fps)); 793 | } 794 | } 795 | } 796 | 797 | pub fn add(elem: Arc>, name: String, width: f64, height: f64) { 798 | if let Ok(ref mut to_add) = TOADD.lock() { 799 | to_add.push((elem, name, width, height)); 800 | } 801 | } 802 | } 803 | -------------------------------------------------------------------------------- /src/gui/font.rs: -------------------------------------------------------------------------------- 1 | use app_units; 2 | use font_kit; 3 | use font_kit::{family_name::FamilyName, font, source::SystemSource}; 4 | use super::properties::*; 5 | use std::collections::HashMap; 6 | use webrender::api::*; 7 | 8 | use super::properties::{Align, Position}; 9 | use self::shaper::GlyphMetric; 10 | use unicode_bidi::BidiClass; 11 | use unicode_bidi::BidiInfo; 12 | 13 | mod shaper { 14 | use std::collections::HashMap; 15 | use std::os::raw::{c_char, c_int, c_uint, c_void}; 16 | use std::ptr; 17 | use std::sync::{Arc, Mutex}; 18 | use webrender::api::GlyphIndex; 19 | //harfbuzz functions 20 | use harfbuzz_sys::{ 21 | hb_blob_create, hb_buffer_add_utf8, hb_buffer_create, hb_buffer_destroy, 22 | hb_buffer_get_glyph_infos, hb_buffer_get_glyph_positions, hb_buffer_set_direction, 23 | hb_buffer_set_script, hb_face_create, hb_font_create, 24 | hb_font_get_glyph_extents, hb_font_set_ppem, hb_font_set_scale, hb_shape, 25 | //hb_blob_destroy, hb_face_destroy, hb_font_destroy, 26 | }; 27 | //harfbuzz structs 28 | use harfbuzz_sys::{ 29 | hb_blob_t, hb_face_t, hb_font_t, hb_glyph_extents_t, 30 | }; 31 | //harfbuzz consts 32 | use harfbuzz_sys::{ 33 | HB_DIRECTION_LTR, HB_DIRECTION_RTL, HB_MEMORY_MODE_READONLY, 34 | }; 35 | 36 | use super::super::properties::Position; 37 | 38 | //pub type Dimensions = ((f32, f32), (f32, f32)); 39 | pub type Glyph = (GlyphIndex, GlyphMetric); 40 | 41 | /*#[derive(Debug, Clone)] 42 | pub struct Point{ 43 | pub x: f32, 44 | pub y: f32, 45 | }*/ 46 | 47 | #[derive(Debug, Clone)] 48 | pub struct GlyphMetric { 49 | pub advance: Position, 50 | pub offset: Position, 51 | pub bearing: Position, 52 | pub width: f32, 53 | pub height: f32, 54 | pub size: f32, 55 | pub baseline: f32, 56 | } 57 | 58 | #[derive(Debug, Clone)] 59 | struct HBFont { 60 | blob: usize, 61 | face: usize, 62 | font: usize, 63 | bytes: Vec, 64 | } 65 | 66 | lazy_static! { 67 | static ref FONT: Arc>> = Arc::new(Mutex::new(HashMap::new())); 68 | } 69 | 70 | pub fn shape_text( 71 | val: &str, 72 | size: u32, 73 | baseline: f32, 74 | family: &str, 75 | rtl: bool, 76 | script: super::super::script::Script, 77 | ) -> Vec { 78 | //println!("\"{}\"script is {:?}", val, script); 79 | let script = script.to_hb_script(); 80 | unsafe { 81 | let hb_font = { 82 | let mut font_map = FONT.lock().unwrap(); 83 | if !font_map.contains_key(family) { 84 | let font = super::load_font_by_name(family); 85 | let font_vec: Vec = (*(font.copy_font_data().unwrap())).clone(); 86 | let tmp_len = font_vec.len(); 87 | let tmp = (&font_vec).as_ptr(); 88 | 89 | //let tmp = (tmp).buffer(); 90 | let blob = hb_blob_create( 91 | tmp as *const c_char, 92 | tmp_len as c_uint, 93 | HB_MEMORY_MODE_READONLY, 94 | ptr::null_mut() as *mut c_void, 95 | None, 96 | ); 97 | 98 | let face = hb_face_create(blob, 1 as c_uint); 99 | 100 | let font = hb_font_create(face); 101 | 102 | let hb_font = HBFont { 103 | blob: blob as *const hb_blob_t as usize, 104 | face: face as *const hb_face_t as usize, 105 | font: font as *const hb_font_t as usize, 106 | bytes: font_vec, 107 | }; 108 | 109 | font_map.insert(family.to_owned(), hb_font); 110 | } 111 | 112 | font_map.get(family).unwrap().clone().font as *const hb_font_t as *mut hb_font_t 113 | }; 114 | 115 | hb_font_set_ppem(hb_font, size, size); 116 | hb_font_set_scale(hb_font, size as i32, size as i32); 117 | 118 | let buf = hb_buffer_create(); 119 | hb_buffer_add_utf8( 120 | buf, 121 | val.as_ptr() as *const c_char, 122 | val.len() as c_int, 123 | 0, 124 | val.len() as c_int, 125 | ); 126 | hb_buffer_set_script(buf, script); 127 | if rtl { 128 | hb_buffer_set_direction(buf, HB_DIRECTION_RTL); 129 | } else { 130 | hb_buffer_set_direction(buf, HB_DIRECTION_LTR); 131 | } 132 | 133 | hb_shape(hb_font, buf, ptr::null_mut(), 0); 134 | 135 | let mut g_count = 0; 136 | let mut p_count = 0; 137 | let g_info = hb_buffer_get_glyph_infos(buf, &mut g_count); 138 | let g_pos = hb_buffer_get_glyph_positions(buf, &mut p_count); 139 | 140 | let mut g_vec = Vec::new(); 141 | 142 | //let mut cursor_x = 0; 143 | for i in 0..g_count { 144 | let info = g_info.offset(i as isize); 145 | let pos = g_pos.offset(i as isize); 146 | 147 | let mut extent = hb_glyph_extents_t { 148 | x_bearing: 0, 149 | y_bearing: 0, 150 | width: 0, 151 | height: 0, 152 | }; 153 | hb_font_get_glyph_extents( 154 | hb_font, 155 | (*info).codepoint, 156 | &mut extent as *mut hb_glyph_extents_t, 157 | ); 158 | 159 | let metric = GlyphMetric { 160 | advance: Position { 161 | x: (*pos).x_advance as f32, 162 | y: (*pos).y_advance as f32, 163 | }, 164 | offset: Position { 165 | x: (*pos).x_offset as f32, 166 | y: (*pos).y_offset as f32, 167 | }, 168 | bearing: Position { 169 | x: extent.x_bearing as f32, 170 | y: extent.y_bearing as f32, 171 | }, 172 | width: extent.width as f32, 173 | height: extent.height as f32, 174 | size: size as f32, 175 | baseline, 176 | }; 177 | 178 | let glyphid = (*info).codepoint; 179 | 180 | g_vec.push((glyphid, metric)); 181 | } 182 | 183 | //destroy all 184 | hb_buffer_destroy(buf); 185 | 186 | g_vec 187 | } 188 | } 189 | } 190 | 191 | fn load_font_by_name(name: &str) -> font::Font { 192 | let mut props = font_kit::properties::Properties::new(); 193 | 194 | props.weight = font_kit::properties::Weight::NORMAL; 195 | props.stretch = font_kit::properties::Stretch::NORMAL; 196 | props.style = font_kit::properties::Style::Normal; 197 | 198 | let source = SystemSource::new(); 199 | 200 | source 201 | .select_best_match(&[FamilyName::Title(name.into())], &props) 202 | .unwrap() 203 | .load() 204 | .unwrap() 205 | } 206 | 207 | fn add_font(font: &font_kit::font::Font, api: &RenderApi, document_id: DocumentId) -> FontKey { 208 | let f = font.copy_font_data().unwrap(); 209 | let key = api.generate_font_key(); 210 | 211 | let mut txn = Transaction::new(); 212 | txn.add_raw_font(key, (*f).to_owned(), 0); 213 | api.send_transaction(document_id, txn); 214 | 215 | key 216 | } 217 | 218 | #[derive(Debug, Clone)] 219 | pub struct Char { 220 | char: char, 221 | metric: GlyphMetric, 222 | index: usize, 223 | position: Position, 224 | rtl: bool, 225 | glyph: GlyphIndex, 226 | } 227 | 228 | impl Char { 229 | fn new(char: char, index: usize, rtl: bool) -> Char { 230 | Char { 231 | char, 232 | metric: GlyphMetric { 233 | advance: Position { x: 0.0, y: 0.0 }, 234 | offset: Position { x: 0.0, y: 0.0 }, 235 | bearing: Position { x: 0.0, y: 0.0 }, 236 | width: 0.0, 237 | height: 0.0, 238 | size: 0.0, 239 | baseline: 0.0, 240 | }, 241 | index, 242 | position: Position { x: 0.0, y: 0.0 }, 243 | rtl, 244 | glyph: 0, 245 | } 246 | } 247 | 248 | pub fn get_char(&self) -> char { 249 | self.char 250 | } 251 | pub fn get_metric(&self) -> GlyphMetric { 252 | self.metric.clone() 253 | } 254 | pub fn get_index(&self) -> usize { 255 | self.index 256 | } 257 | pub fn get_position(&self) -> Position { 258 | self.position.clone() 259 | } 260 | pub fn get_cursor_position(&self, p: &Position) -> Position { 261 | let mut ret = self.position.clone(); 262 | if p.x > ret.x + (self.metric.advance.x/2.) { 263 | ret.x += self.metric.advance.x; 264 | } 265 | ret 266 | } 267 | pub fn get_rtl(&self) -> bool { 268 | self.rtl 269 | } 270 | pub fn get_glyph(&self) -> GlyphIndex { 271 | self.glyph 272 | } 273 | } 274 | 275 | #[derive(Debug, Clone)] 276 | pub struct Segment { 277 | rtl: bool, 278 | extent: Extent, 279 | class: BidiClass, 280 | script: super::script::Script, 281 | chars: Vec, 282 | glyphs: Vec, 283 | } 284 | 285 | impl Segment { 286 | pub fn resolve_class(level: &super::super::unicode_bidi::Level, class: BidiClass) -> BidiClass { 287 | match class { 288 | BidiClass::B => BidiClass::B, 289 | BidiClass::WS => BidiClass::WS, 290 | BidiClass::S => BidiClass::S, 291 | _ => level.bidi_class(), 292 | } 293 | } 294 | 295 | pub fn breaking_class(&self) -> bool { 296 | match self.class { 297 | BidiClass::B => true, 298 | BidiClass::WS => true, 299 | BidiClass::S => true, 300 | _ => false, 301 | } 302 | } 303 | 304 | fn shape(&mut self, size: f32, baseline: f32, family: &str) { 305 | let value: String = self.chars.iter().map(|c| c.char).collect(); 306 | 307 | let glyphs = shaper::shape_text( 308 | value.as_str(), 309 | size as u32, 310 | baseline, 311 | family, 312 | self.rtl, 313 | self.script, 314 | ); 315 | 316 | self.glyphs.clear(); 317 | 318 | let mut _x = 0.; 319 | 320 | let mut i = 0; 321 | while i < self.chars.len() { 322 | let (glyph, ref metric) = glyphs[i]; 323 | 324 | self.chars[i].glyph = glyph; 325 | self.chars[i].metric = metric.clone(); 326 | self.chars[i].position.x = _x; 327 | self.chars[i].position.y = size; 328 | 329 | i += 1; 330 | _x += metric.advance.x; 331 | } 332 | self.extent.h = size; 333 | self.extent.w = _x; 334 | } 335 | 336 | fn position(&mut self, x: f32, y: f32) { 337 | self.glyphs.clear(); 338 | 339 | self.extent.x = x; 340 | self.extent.y = y; 341 | 342 | let mut _x = x; 343 | for ch in self.chars.iter_mut() { 344 | ch.position.x = _x; 345 | ch.position.y = y + ch.metric.baseline; 346 | 347 | _x += ch.metric.advance.x; 348 | 349 | self.glyphs.push(GlyphInstance { 350 | index: ch.glyph, 351 | point: LayoutPoint::new(ch.position.x, ch.position.y), 352 | }); 353 | } 354 | } 355 | } 356 | 357 | #[derive(Debug, Clone)] 358 | pub struct SegmentRef<'a> { 359 | _ref: &'a Segment, 360 | } 361 | 362 | #[derive(Debug, Clone)] 363 | pub struct ParaLine { 364 | extent: Extent, 365 | segments: Vec>, 366 | } 367 | 368 | impl ParaLine { 369 | #[allow(mutable_transmutes)] 370 | fn position(&mut self, x: f32, y: f32) { 371 | self.extent.x = x; 372 | self.extent.y = y; 373 | let mut _x = x; 374 | for segment in self.segments.iter_mut() { 375 | let tmp = unsafe { 376 | std::mem::transmute::<&'static Segment, &'static mut Segment>(segment._ref) 377 | }; 378 | tmp.position(_x, y); 379 | _x += tmp.extent.w; 380 | } 381 | } 382 | 383 | pub fn get_char_at_pos( 384 | &self, 385 | _p: &super::properties::Position, 386 | ) -> Option { 387 | let mut ch = Some(self.segments[0]._ref.chars[0].clone()); 388 | for r in self.segments.iter(){ 389 | if r._ref.class == BidiClass::R { 390 | for _c in r._ref.chars.iter().rev() { 391 | if _c.position.x < _p.x { 392 | ch = Some(_c.clone()); 393 | } 394 | } 395 | } else { 396 | for _c in r._ref.chars.iter() { 397 | if _c.position.x < _p.x { 398 | ch = Some(_c.clone()); 399 | } 400 | } 401 | } 402 | } 403 | 404 | ch 405 | } 406 | } 407 | 408 | #[derive(Debug, Clone)] 409 | pub struct ParaText { 410 | extent: Extent, 411 | lines: Vec, 412 | rtl: bool, 413 | } 414 | 415 | impl ParaText { 416 | fn position(&mut self, x: f32, y: f32, w: f32, _h: f32, size: f32, text_align: &Align) { 417 | let mut _y = y; 418 | let mut max_w = 0.; 419 | let mut min_x = x + w; 420 | for line in self.lines.iter_mut() { 421 | let mut tmp = 0.; 422 | match text_align { 423 | Align::Middle => { 424 | tmp = (w - line.extent.w) / 2.; 425 | } 426 | Align::Right => { 427 | tmp = w - line.extent.w; 428 | } 429 | _ => (), 430 | } 431 | 432 | line.position(x + tmp, _y); 433 | line.extent.h = size; 434 | 435 | if max_w < line.extent.w { 436 | max_w = line.extent.w; 437 | } 438 | if min_x > line.extent.x { 439 | min_x = line.extent.x; 440 | } 441 | 442 | _y += size; 443 | } 444 | 445 | self.extent.x = min_x; 446 | self.extent.y = y; 447 | self.extent.w = max_w; 448 | self.extent.h = size * self.lines.len() as f32; 449 | } 450 | 451 | fn shape_ltr(&mut self, line_directions: Vec<(usize, bool)>, w: f32) { 452 | let para = self; 453 | let line = para.lines.pop().unwrap(); 454 | 455 | let mut tmp_line = ParaLine { 456 | segments: Vec::new(), 457 | extent: Extent::new(), 458 | }; 459 | 460 | let mut prev_rtl = false; 461 | let mut prev_rtl_pos = 0; 462 | let mut i = 0; 463 | for dir in line_directions.iter() { 464 | let _tmp = dir.1; 465 | let mut prev_breaking_class = false; 466 | for j in i..dir.0 { 467 | if line.segments[j]._ref.extent.w + tmp_line.extent.w > w && prev_breaking_class { 468 | if para.extent.w < tmp_line.extent.w { 469 | para.extent.w = tmp_line.extent.w; 470 | } 471 | para.lines.push(tmp_line); 472 | tmp_line = ParaLine { 473 | segments: Vec::new(), 474 | extent: Extent::new(), 475 | }; 476 | prev_rtl = false; 477 | prev_rtl_pos = 0; 478 | } 479 | 480 | prev_breaking_class = line.segments[j]._ref.breaking_class(); 481 | tmp_line.extent.w += line.segments[j]._ref.extent.w; 482 | 483 | //where to insert the word? 484 | if prev_rtl != line.segments[j]._ref.rtl { 485 | prev_rtl = line.segments[j]._ref.rtl; 486 | if prev_rtl { 487 | prev_rtl_pos = tmp_line.segments.len(); 488 | } 489 | } 490 | 491 | if prev_rtl { 492 | tmp_line 493 | .segments 494 | .insert(prev_rtl_pos, line.segments[j].clone()); 495 | } else { 496 | tmp_line.segments.push(line.segments[j].clone()); 497 | } 498 | } 499 | i = dir.0; 500 | } 501 | if para.extent.w < tmp_line.extent.w { 502 | para.extent.w = tmp_line.extent.w; 503 | } 504 | para.lines.push(tmp_line); 505 | } 506 | 507 | fn shape_rtl(&mut self, line_directions: Vec<(usize, bool)>, w: f32) { 508 | let para = self; 509 | let line = para.lines.pop().unwrap(); 510 | 511 | let mut tmp_line = ParaLine { 512 | segments: Vec::new(), 513 | extent: Extent::new(), 514 | }; 515 | 516 | let mut i = 0; 517 | let mut ltr_pos: Option = None; 518 | for dir in line_directions.iter() { 519 | let mut prev_breaking_class = false; 520 | 521 | for j in i..dir.0 { 522 | if line.segments[j]._ref.extent.w + tmp_line.extent.w > w && prev_breaking_class { 523 | if para.extent.w < tmp_line.extent.w { 524 | para.extent.w = tmp_line.extent.w; 525 | } 526 | para.lines.push(tmp_line); 527 | tmp_line = ParaLine { 528 | segments: Vec::new(), 529 | extent: Extent::new(), 530 | }; 531 | ltr_pos = None; 532 | } 533 | 534 | let tmp_pos = if line.segments[j]._ref.rtl { 535 | ltr_pos = None; 536 | 0 537 | } else { 538 | if ltr_pos.is_none() { 539 | ltr_pos = Some(0); 540 | 0 541 | } else { 542 | match ltr_pos { 543 | Some(ref mut x) => { 544 | (*x) += 1; 545 | (*x).clone() 546 | } 547 | _ => 0, 548 | } 549 | } 550 | }; 551 | 552 | prev_breaking_class = line.segments[j]._ref.breaking_class(); 553 | tmp_line.extent.w += line.segments[j]._ref.extent.w; 554 | 555 | tmp_line.segments.insert(tmp_pos, line.segments[j].clone()); 556 | } 557 | i = dir.0; 558 | } 559 | if para.extent.w < tmp_line.extent.w { 560 | para.extent.w = tmp_line.extent.w; 561 | } 562 | para.lines.push(tmp_line); 563 | } 564 | 565 | pub fn get_char_at_pos( 566 | &self, 567 | _p: &super::properties::Position, 568 | _force: bool, 569 | ) -> Option { 570 | let mut ret = None; 571 | 572 | let mut l = self.lines.len(); 573 | if l > 0 { 574 | l-=1; 575 | if _force { 576 | if self.lines[0].extent.y > _p.y { 577 | //first line of para 578 | //println!("\t ... forced in first line of para"); 579 | ret = self.lines[0].get_char_at_pos(_p); 580 | 581 | } else { 582 | //last line of para 583 | //println!("\t ... forced in last line of para"); 584 | ret = self.lines[l].get_char_at_pos(_p); 585 | } 586 | } else { 587 | for i in 0..l+1 { 588 | let y1 = self.lines[i].extent.y; 589 | let y2 = self.lines[i].extent.y+self.lines[i].extent.h; 590 | //println!("\t ... checking line {} [p.y {} between {} and {}?]", i, _p.y, y1, y2); 591 | if y1 <= _p.y && y2 >= _p.y { 592 | //println!("\t ... in line {} of para", i); 593 | ret = self.lines[i].get_char_at_pos(_p); 594 | } else { 595 | //println!("\t ... skipped line {} of para", i); 596 | } 597 | } 598 | } 599 | } 600 | 601 | ret 602 | } 603 | } 604 | 605 | #[derive(Debug, Clone)] 606 | pub struct Paragraphs { 607 | extent: Extent, 608 | segments: Vec, 609 | paras: Vec, 610 | } 611 | 612 | impl Paragraphs { 613 | pub fn new() -> Paragraphs { 614 | Paragraphs { 615 | segments: Vec::new(), 616 | paras: Vec::new(), 617 | extent: Extent::new(), 618 | } 619 | } 620 | 621 | pub fn get_extent(&mut self) -> Extent { 622 | self.extent.clone() 623 | } 624 | 625 | pub fn from_chars(text: &Vec) -> Paragraphs { 626 | let mut segments = vec![]; 627 | 628 | let c_tmp = text.iter().next(); 629 | if c_tmp.is_some() { 630 | let value: String = text.iter().collect(); 631 | let info = BidiInfo::new(&value, None); 632 | 633 | let mut class = Segment::resolve_class(&info.levels[0], info.original_classes[0]); 634 | let mut script = super::script::get_script(text[0].clone()); 635 | let mut segment = Segment { 636 | chars: vec![], 637 | rtl: info.levels[0].is_rtl(), 638 | extent: Extent::new(), 639 | class, 640 | script, 641 | glyphs: vec![], 642 | }; 643 | let mut i = 0; 644 | let mut j = 0; 645 | 646 | for c in text.iter() { 647 | script = super::script::get_script(c.clone()); 648 | class = Segment::resolve_class(&info.levels[i], info.original_classes[i]); 649 | if class != BidiClass::B && class == segment.class && script == segment.script { 650 | segment 651 | .chars 652 | .push(Char::new(c.clone(), j, info.levels[i].is_rtl())); 653 | } else { 654 | segments.push(segment); 655 | segment = Segment { 656 | chars: vec![Char::new(c.clone(), j, info.levels[i].is_rtl())], 657 | rtl: info.levels[i].is_rtl(), 658 | extent: Extent::new(), 659 | class, 660 | script, 661 | glyphs: vec![], 662 | }; 663 | } 664 | 665 | let c_len = c.len_utf8(); 666 | i += c_len; 667 | j += 1; 668 | } 669 | 670 | segments.push(segment); 671 | } 672 | 673 | Paragraphs { 674 | segments, 675 | paras: vec![], 676 | extent: Extent::new(), 677 | } 678 | } 679 | 680 | fn init_paras<'a>( 681 | &'a mut self, 682 | size: f32, 683 | baseline: f32, 684 | family: &str, 685 | ) -> Vec> { 686 | self.paras.clear(); 687 | 688 | let mut ret_direction = vec![]; 689 | 690 | let mut para = ParaText { 691 | lines: Vec::new(), 692 | extent: Extent::new(), 693 | rtl: false, 694 | }; 695 | let mut line = ParaLine { 696 | segments: Vec::new(), 697 | extent: Extent::new(), 698 | }; 699 | let mut i = 0; 700 | let mut direction = false; 701 | let mut para_direction = vec![]; 702 | let mut rtl: Option = None; 703 | for segment in self.segments.iter_mut() { 704 | //print!("{:?}", segment.class); 705 | 706 | if rtl.is_none() { 707 | rtl = Some(true); 708 | para.rtl = segment.rtl; 709 | } 710 | segment.shape(size, baseline, family); 711 | 712 | let tmp = unsafe { std::mem::transmute::<&'a Segment, &'static Segment>(segment) }; 713 | let tmp = SegmentRef { _ref: tmp }; 714 | if direction != segment.rtl { 715 | if line.segments.len() > 0 { 716 | para_direction.push((i, direction)); 717 | } 718 | direction = segment.rtl; 719 | } 720 | 721 | line.segments.push(tmp); 722 | 723 | if segment.class == BidiClass::B { 724 | para_direction.push((i, direction)); 725 | para.lines.push(line); 726 | self.paras.push(para); 727 | ret_direction.push(para_direction); 728 | para = ParaText { 729 | lines: Vec::new(), 730 | extent: Extent::new(), 731 | rtl: false, 732 | }; 733 | line = ParaLine { 734 | segments: Vec::new(), 735 | extent: Extent::new(), 736 | }; 737 | para_direction = vec![]; 738 | rtl = None; 739 | i = 0; 740 | direction = false; 741 | } else { 742 | i += 1; 743 | } 744 | } 745 | if line.segments.len() > 0 { 746 | para_direction.push((i, direction)); 747 | } 748 | ret_direction.push(para_direction); 749 | para.lines.push(line); 750 | self.paras.push(para); 751 | 752 | ret_direction 753 | } 754 | 755 | pub fn shape<'a>( 756 | &'a mut self, 757 | x: f32, 758 | y: f32, 759 | w: f32, 760 | h: f32, 761 | size: f32, 762 | baseline: f32, 763 | family: &str, 764 | text_align: &Align, 765 | ) { 766 | let mut para_directions = self.init_paras(size, baseline, family); 767 | 768 | for para in self.paras.iter_mut() { 769 | let line_directions = para_directions.remove(0); 770 | if para.lines.len() == 0 || para.lines[0].segments.len() == 0 { 771 | continue; 772 | } 773 | 774 | if para.rtl { 775 | para.shape_rtl(line_directions, w); 776 | } else { 777 | para.shape_ltr(line_directions, w); 778 | } 779 | } 780 | 781 | self.position(x, y, w, h, size, text_align); 782 | } 783 | 784 | fn position(&mut self, x: f32, y: f32, w: f32, h: f32, size: f32, text_align: &Align) { 785 | let mut _y = y; 786 | let mut min_x = x + w; 787 | let mut min_y = y + h; 788 | 789 | let mut max_w = 0.; 790 | for para in self.paras.iter_mut() { 791 | para.position(x, _y, w, h, size, text_align); 792 | 793 | if para.extent.w > max_w { 794 | max_w = para.extent.w; 795 | } 796 | if min_x > para.extent.x { 797 | min_x = para.extent.x; 798 | } 799 | if min_y > para.extent.y { 800 | min_y = para.extent.y; 801 | } 802 | _y += para.extent.h; 803 | } 804 | 805 | self.extent.x = min_x; 806 | self.extent.y = min_y; 807 | self.extent.w = max_w; 808 | self.extent.h = _y - y; 809 | } 810 | 811 | pub fn get_char_at_pos( 812 | &self, 813 | _p: &super::properties::Position, 814 | _val: &Vec, 815 | ) -> Option { 816 | let mut ret = None; 817 | 818 | let mut l = self.paras.len(); 819 | if l > 0 { 820 | l-=1; 821 | for i in 0..l+1 { 822 | let force = (i == 0 && self.paras[i].extent.y > _p.y) || (ret.is_none() && i==l); 823 | if force || (self.paras[i].extent.y <= _p.y && self.paras[i].extent.y+self.paras[i].extent.h >= _p.y){ 824 | //println!("checking para {} [{}, {:?}, {:?}]", i, force, _p, self.paras[i].extent); 825 | let tmp = self.paras[i].get_char_at_pos(_p, force); 826 | //println!("\t ... Char found {:?}", tmp); 827 | if tmp.is_some() { 828 | ret = tmp; 829 | } 830 | } 831 | } 832 | } 833 | 834 | ret 835 | } 836 | 837 | pub fn get_char_at_index(&self, index: usize) -> Option{ 838 | let ret = None; 839 | for para in self.paras.iter() { 840 | for line in para.lines.iter() { 841 | for segment in line.segments.iter() { 842 | for ch in segment._ref.chars.iter() { 843 | if ch.index == index { 844 | return Some(ch.clone()); 845 | } 846 | } 847 | } 848 | } 849 | } 850 | ret 851 | } 852 | 853 | pub fn glyphs(&self) -> Vec { 854 | let mut arr = vec![]; 855 | for para in self.paras.iter() { 856 | for line in para.lines.iter() { 857 | for segment in line.segments.iter() { 858 | arr.append(&mut segment._ref.glyphs.clone()); 859 | } 860 | } 861 | } 862 | arr 863 | } 864 | } 865 | 866 | struct InstanceKeys { 867 | key: FontKey, 868 | font: font_kit::font::Font, 869 | instances: HashMap, 870 | } 871 | 872 | impl InstanceKeys { 873 | fn new(key: FontKey, font: font_kit::font::Font) -> InstanceKeys { 874 | InstanceKeys { 875 | key, 876 | font, 877 | instances: HashMap::new(), 878 | } 879 | } 880 | 881 | fn get_instance_key( 882 | &mut self, 883 | size: i32, 884 | api: &RenderApi, 885 | document_id: DocumentId, 886 | ) -> FontInstanceKey { 887 | let x = self.instances.get(&size); 888 | if x.is_some() { 889 | return *x.unwrap(); 890 | } 891 | 892 | let ikey = api.generate_font_instance_key(); 893 | 894 | let mut txn = Transaction::new(); 895 | txn.add_font_instance( 896 | ikey, 897 | self.key, 898 | app_units::Au::from_px(size), 899 | None, 900 | None, 901 | Vec::new(), 902 | ); 903 | api.send_transaction(document_id, txn); 904 | 905 | ikey 906 | } 907 | } 908 | 909 | pub struct FontStore { 910 | store: HashMap, 911 | api: RenderApi, 912 | document_id: DocumentId, 913 | } 914 | 915 | impl FontStore { 916 | pub fn new(api: RenderApi, document_id: DocumentId) -> FontStore { 917 | FontStore { 918 | api, 919 | document_id, 920 | store: HashMap::new(), 921 | } 922 | } 923 | 924 | pub fn get_font_instance(&mut self, family: &str, size: i32) -> (FontKey, FontInstanceKey) { 925 | { 926 | let ikeys = self.store.get_mut(family); 927 | if let Some(keys) = ikeys { 928 | let ik = keys.get_instance_key(size, &(self.api), self.document_id); 929 | return (keys.key, ik); 930 | } 931 | } 932 | 933 | let font = load_font_by_name(family); 934 | let fkey = add_font(&font, &self.api, self.document_id); 935 | 936 | let mut keys = InstanceKeys::new(fkey, font); 937 | let ikey = keys.get_instance_key(size, &self.api, self.document_id); 938 | 939 | self.store.insert(family.into(), keys); 940 | 941 | (fkey, ikey) 942 | } 943 | 944 | pub fn get_font_metrics(&self, family: &str) -> Option { 945 | let ikeys = self.store.get(family); 946 | if let Some(keys) = ikeys { 947 | Some(keys.font.metrics()) 948 | } else { 949 | None 950 | } 951 | } 952 | 953 | pub fn deinit(&mut self) { 954 | let mut txn = Transaction::new(); 955 | for ik in self.store.values() { 956 | for k in ik.instances.values() { 957 | txn.delete_font_instance(*k); 958 | } 959 | txn.delete_font(ik.key); 960 | } 961 | self.api.send_transaction(self.document_id, txn); 962 | } 963 | } 964 | 965 | -------------------------------------------------------------------------------- /src/gui/script/mod.rs: -------------------------------------------------------------------------------- 1 | use self::Script::*; 2 | use harfbuzz_sys::*; 3 | 4 | #[allow(non_camel_case_types)] 5 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 6 | /// Represents the Unicode character property **Script**. 7 | /// 8 | /// http://www.unicode.org/reports/tr24/ 9 | pub enum Script { 10 | Unknown, 11 | Ahom, 12 | Anatolian_Hieroglyphs, 13 | Arabic, 14 | Armenian, 15 | Avestan, 16 | Balinese, 17 | Bamum, 18 | Bassa_Vah, 19 | Batak, 20 | Bengali, 21 | Bopomofo, 22 | Brahmi, 23 | Braille, 24 | Buginese, 25 | Buhid, 26 | Canadian_Aboriginal, 27 | Carian, 28 | Caucasian_Albanian, 29 | Chakma, 30 | Cham, 31 | Cherokee, 32 | Common, 33 | Coptic, 34 | Cuneiform, 35 | Cypriot, 36 | Cyrillic, 37 | Deseret, 38 | Devanagari, 39 | Duployan, 40 | Egyptian_Hieroglyphs, 41 | Elbasan, 42 | Ethiopic, 43 | Georgian, 44 | Glagolitic, 45 | Gothic, 46 | Grantha, 47 | Greek, 48 | Gujarati, 49 | Gurmukhi, 50 | Han, 51 | Hangul, 52 | Hanunoo, 53 | Hatran, 54 | Hebrew, 55 | Hiragana, 56 | Imperial_Aramaic, 57 | Inherited, 58 | Inscriptional_Pahlavi, 59 | Inscriptional_Parthian, 60 | Javanese, 61 | Kaithi, 62 | Kannada, 63 | Katakana, 64 | Kayah_Li, 65 | Kharoshthi, 66 | Khmer, 67 | Khojki, 68 | Khudawadi, 69 | Lao, 70 | Latin, 71 | Lepcha, 72 | Limbu, 73 | Linear_A, 74 | Linear_B, 75 | Lisu, 76 | Lycian, 77 | Lydian, 78 | Mahajani, 79 | Malayalam, 80 | Mandaic, 81 | Manichaean, 82 | Meetei_Mayek, 83 | Mende_Kikakui, 84 | Meroitic_Cursive, 85 | Meroitic_Hieroglyphs, 86 | Miao, 87 | Modi, 88 | Mongolian, 89 | Mro, 90 | Multani, 91 | Myanmar, 92 | Nabataean, 93 | New_Tai_Lue, 94 | Nko, 95 | Ogham, 96 | Ol_Chiki, 97 | Old_Hungarian, 98 | Old_Italic, 99 | Old_North_Arabian, 100 | Old_Permic, 101 | Old_Persian, 102 | Old_South_Arabian, 103 | Old_Turkic, 104 | Oriya, 105 | Osmanya, 106 | Pahawh_Hmong, 107 | Palmyrene, 108 | Pau_Cin_Hau, 109 | Phags_Pa, 110 | Phoenician, 111 | Psalter_Pahlavi, 112 | Rejang, 113 | Runic, 114 | Samaritan, 115 | Saurashtra, 116 | Sharada, 117 | Shavian, 118 | Siddham, 119 | SignWriting, 120 | Sinhala, 121 | Sora_Sompeng, 122 | Sundanese, 123 | Syloti_Nagri, 124 | Syriac, 125 | Tagalog, 126 | Tagbanwa, 127 | Tai_Le, 128 | Tai_Tham, 129 | Tai_Viet, 130 | Takri, 131 | Tamil, 132 | Telugu, 133 | Thaana, 134 | Thai, 135 | Tibetan, 136 | Tifinagh, 137 | Tirhuta, 138 | Ugaritic, 139 | Vai, 140 | Warang_Citi, 141 | Yi, 142 | } 143 | 144 | fn bsearch_range_value_table(c: char, r: &'static [(char, char, Script)]) -> Script { 145 | use std::cmp::Ordering::{Equal, Greater, Less}; 146 | match r.binary_search_by(|&(lo, hi, _)| { 147 | if lo <= c && c <= hi { 148 | Equal 149 | } else if hi < c { 150 | Less 151 | } else { 152 | Greater 153 | } 154 | }) { 155 | Ok(idx) => { 156 | let (_, _, cat) = r[idx]; 157 | cat 158 | } 159 | Err(_) => Unknown, 160 | } 161 | } 162 | 163 | /// Find the script of a single char. 164 | pub fn get_script(c: char) -> Script { 165 | bsearch_range_value_table(c, SCRIPT_TABLE) 166 | } 167 | 168 | const SCRIPT_TABLE: &'static [(char, char, Script)] = &[ 169 | ('\u{0}', '\u{40}', Common), 170 | ('\u{41}', '\u{5a}', Latin), 171 | ('\u{5b}', '\u{60}', Common), 172 | ('\u{61}', '\u{7a}', Latin), 173 | ('\u{7b}', '\u{a9}', Common), 174 | ('\u{aa}', '\u{aa}', Latin), 175 | ('\u{ab}', '\u{b9}', Common), 176 | ('\u{ba}', '\u{ba}', Latin), 177 | ('\u{bb}', '\u{bf}', Common), 178 | ('\u{c0}', '\u{d6}', Latin), 179 | ('\u{d7}', '\u{d7}', Common), 180 | ('\u{d8}', '\u{f6}', Latin), 181 | ('\u{f7}', '\u{f7}', Common), 182 | ('\u{f8}', '\u{2b8}', Latin), 183 | ('\u{2b9}', '\u{2df}', Common), 184 | ('\u{2e0}', '\u{2e4}', Latin), 185 | ('\u{2e5}', '\u{2e9}', Common), 186 | ('\u{2ea}', '\u{2eb}', Bopomofo), 187 | ('\u{2ec}', '\u{2ff}', Common), 188 | ('\u{300}', '\u{36f}', Inherited), 189 | ('\u{370}', '\u{373}', Greek), 190 | ('\u{374}', '\u{374}', Common), 191 | ('\u{375}', '\u{377}', Greek), 192 | ('\u{37a}', '\u{37d}', Greek), 193 | ('\u{37e}', '\u{37e}', Common), 194 | ('\u{37f}', '\u{37f}', Greek), 195 | ('\u{384}', '\u{384}', Greek), 196 | ('\u{385}', '\u{385}', Common), 197 | ('\u{386}', '\u{386}', Greek), 198 | ('\u{387}', '\u{387}', Common), 199 | ('\u{388}', '\u{38a}', Greek), 200 | ('\u{38c}', '\u{38c}', Greek), 201 | ('\u{38e}', '\u{3a1}', Greek), 202 | ('\u{3a3}', '\u{3e1}', Greek), 203 | ('\u{3e2}', '\u{3ef}', Coptic), 204 | ('\u{3f0}', '\u{3ff}', Greek), 205 | ('\u{400}', '\u{484}', Cyrillic), 206 | ('\u{485}', '\u{486}', Inherited), 207 | ('\u{487}', '\u{52f}', Cyrillic), 208 | ('\u{531}', '\u{556}', Armenian), 209 | ('\u{559}', '\u{55f}', Armenian), 210 | ('\u{561}', '\u{587}', Armenian), 211 | ('\u{589}', '\u{589}', Common), 212 | ('\u{58a}', '\u{58a}', Armenian), 213 | ('\u{58d}', '\u{58f}', Armenian), 214 | ('\u{591}', '\u{5c7}', Hebrew), 215 | ('\u{5d0}', '\u{5ea}', Hebrew), 216 | ('\u{5f0}', '\u{5f4}', Hebrew), 217 | ('\u{600}', '\u{604}', Arabic), 218 | ('\u{605}', '\u{605}', Common), 219 | ('\u{606}', '\u{60b}', Arabic), 220 | ('\u{60c}', '\u{60c}', Common), 221 | ('\u{60d}', '\u{61a}', Arabic), 222 | ('\u{61b}', '\u{61c}', Common), 223 | ('\u{61e}', '\u{61e}', Arabic), 224 | ('\u{61f}', '\u{61f}', Common), 225 | ('\u{620}', '\u{63f}', Arabic), 226 | ('\u{640}', '\u{640}', Common), 227 | ('\u{641}', '\u{64a}', Arabic), 228 | ('\u{64b}', '\u{655}', Inherited), 229 | ('\u{656}', '\u{66f}', Arabic), 230 | ('\u{670}', '\u{670}', Inherited), 231 | ('\u{671}', '\u{6dc}', Arabic), 232 | ('\u{6dd}', '\u{6dd}', Common), 233 | ('\u{6de}', '\u{6ff}', Arabic), 234 | ('\u{700}', '\u{70d}', Syriac), 235 | ('\u{70f}', '\u{74a}', Syriac), 236 | ('\u{74d}', '\u{74f}', Syriac), 237 | ('\u{750}', '\u{77f}', Arabic), 238 | ('\u{780}', '\u{7b1}', Thaana), 239 | ('\u{7c0}', '\u{7fa}', Nko), 240 | ('\u{800}', '\u{82d}', Samaritan), 241 | ('\u{830}', '\u{83e}', Samaritan), 242 | ('\u{840}', '\u{85b}', Mandaic), 243 | ('\u{85e}', '\u{85e}', Mandaic), 244 | ('\u{8a0}', '\u{8b4}', Arabic), 245 | ('\u{8e3}', '\u{8ff}', Arabic), 246 | ('\u{900}', '\u{950}', Devanagari), 247 | ('\u{951}', '\u{952}', Inherited), 248 | ('\u{953}', '\u{963}', Devanagari), 249 | ('\u{964}', '\u{965}', Common), 250 | ('\u{966}', '\u{97f}', Devanagari), 251 | ('\u{980}', '\u{983}', Bengali), 252 | ('\u{985}', '\u{98c}', Bengali), 253 | ('\u{98f}', '\u{990}', Bengali), 254 | ('\u{993}', '\u{9a8}', Bengali), 255 | ('\u{9aa}', '\u{9b0}', Bengali), 256 | ('\u{9b2}', '\u{9b2}', Bengali), 257 | ('\u{9b6}', '\u{9b9}', Bengali), 258 | ('\u{9bc}', '\u{9c4}', Bengali), 259 | ('\u{9c7}', '\u{9c8}', Bengali), 260 | ('\u{9cb}', '\u{9ce}', Bengali), 261 | ('\u{9d7}', '\u{9d7}', Bengali), 262 | ('\u{9dc}', '\u{9dd}', Bengali), 263 | ('\u{9df}', '\u{9e3}', Bengali), 264 | ('\u{9e6}', '\u{9fb}', Bengali), 265 | ('\u{a01}', '\u{a03}', Gurmukhi), 266 | ('\u{a05}', '\u{a0a}', Gurmukhi), 267 | ('\u{a0f}', '\u{a10}', Gurmukhi), 268 | ('\u{a13}', '\u{a28}', Gurmukhi), 269 | ('\u{a2a}', '\u{a30}', Gurmukhi), 270 | ('\u{a32}', '\u{a33}', Gurmukhi), 271 | ('\u{a35}', '\u{a36}', Gurmukhi), 272 | ('\u{a38}', '\u{a39}', Gurmukhi), 273 | ('\u{a3c}', '\u{a3c}', Gurmukhi), 274 | ('\u{a3e}', '\u{a42}', Gurmukhi), 275 | ('\u{a47}', '\u{a48}', Gurmukhi), 276 | ('\u{a4b}', '\u{a4d}', Gurmukhi), 277 | ('\u{a51}', '\u{a51}', Gurmukhi), 278 | ('\u{a59}', '\u{a5c}', Gurmukhi), 279 | ('\u{a5e}', '\u{a5e}', Gurmukhi), 280 | ('\u{a66}', '\u{a75}', Gurmukhi), 281 | ('\u{a81}', '\u{a83}', Gujarati), 282 | ('\u{a85}', '\u{a8d}', Gujarati), 283 | ('\u{a8f}', '\u{a91}', Gujarati), 284 | ('\u{a93}', '\u{aa8}', Gujarati), 285 | ('\u{aaa}', '\u{ab0}', Gujarati), 286 | ('\u{ab2}', '\u{ab3}', Gujarati), 287 | ('\u{ab5}', '\u{ab9}', Gujarati), 288 | ('\u{abc}', '\u{ac5}', Gujarati), 289 | ('\u{ac7}', '\u{ac9}', Gujarati), 290 | ('\u{acb}', '\u{acd}', Gujarati), 291 | ('\u{ad0}', '\u{ad0}', Gujarati), 292 | ('\u{ae0}', '\u{ae3}', Gujarati), 293 | ('\u{ae6}', '\u{af1}', Gujarati), 294 | ('\u{af9}', '\u{af9}', Gujarati), 295 | ('\u{b01}', '\u{b03}', Oriya), 296 | ('\u{b05}', '\u{b0c}', Oriya), 297 | ('\u{b0f}', '\u{b10}', Oriya), 298 | ('\u{b13}', '\u{b28}', Oriya), 299 | ('\u{b2a}', '\u{b30}', Oriya), 300 | ('\u{b32}', '\u{b33}', Oriya), 301 | ('\u{b35}', '\u{b39}', Oriya), 302 | ('\u{b3c}', '\u{b44}', Oriya), 303 | ('\u{b47}', '\u{b48}', Oriya), 304 | ('\u{b4b}', '\u{b4d}', Oriya), 305 | ('\u{b56}', '\u{b57}', Oriya), 306 | ('\u{b5c}', '\u{b5d}', Oriya), 307 | ('\u{b5f}', '\u{b63}', Oriya), 308 | ('\u{b66}', '\u{b77}', Oriya), 309 | ('\u{b82}', '\u{b83}', Tamil), 310 | ('\u{b85}', '\u{b8a}', Tamil), 311 | ('\u{b8e}', '\u{b90}', Tamil), 312 | ('\u{b92}', '\u{b95}', Tamil), 313 | ('\u{b99}', '\u{b9a}', Tamil), 314 | ('\u{b9c}', '\u{b9c}', Tamil), 315 | ('\u{b9e}', '\u{b9f}', Tamil), 316 | ('\u{ba3}', '\u{ba4}', Tamil), 317 | ('\u{ba8}', '\u{baa}', Tamil), 318 | ('\u{bae}', '\u{bb9}', Tamil), 319 | ('\u{bbe}', '\u{bc2}', Tamil), 320 | ('\u{bc6}', '\u{bc8}', Tamil), 321 | ('\u{bca}', '\u{bcd}', Tamil), 322 | ('\u{bd0}', '\u{bd0}', Tamil), 323 | ('\u{bd7}', '\u{bd7}', Tamil), 324 | ('\u{be6}', '\u{bfa}', Tamil), 325 | ('\u{c00}', '\u{c03}', Telugu), 326 | ('\u{c05}', '\u{c0c}', Telugu), 327 | ('\u{c0e}', '\u{c10}', Telugu), 328 | ('\u{c12}', '\u{c28}', Telugu), 329 | ('\u{c2a}', '\u{c39}', Telugu), 330 | ('\u{c3d}', '\u{c44}', Telugu), 331 | ('\u{c46}', '\u{c48}', Telugu), 332 | ('\u{c4a}', '\u{c4d}', Telugu), 333 | ('\u{c55}', '\u{c56}', Telugu), 334 | ('\u{c58}', '\u{c5a}', Telugu), 335 | ('\u{c60}', '\u{c63}', Telugu), 336 | ('\u{c66}', '\u{c6f}', Telugu), 337 | ('\u{c78}', '\u{c7f}', Telugu), 338 | ('\u{c81}', '\u{c83}', Kannada), 339 | ('\u{c85}', '\u{c8c}', Kannada), 340 | ('\u{c8e}', '\u{c90}', Kannada), 341 | ('\u{c92}', '\u{ca8}', Kannada), 342 | ('\u{caa}', '\u{cb3}', Kannada), 343 | ('\u{cb5}', '\u{cb9}', Kannada), 344 | ('\u{cbc}', '\u{cc4}', Kannada), 345 | ('\u{cc6}', '\u{cc8}', Kannada), 346 | ('\u{cca}', '\u{ccd}', Kannada), 347 | ('\u{cd5}', '\u{cd6}', Kannada), 348 | ('\u{cde}', '\u{cde}', Kannada), 349 | ('\u{ce0}', '\u{ce3}', Kannada), 350 | ('\u{ce6}', '\u{cef}', Kannada), 351 | ('\u{cf1}', '\u{cf2}', Kannada), 352 | ('\u{d01}', '\u{d03}', Malayalam), 353 | ('\u{d05}', '\u{d0c}', Malayalam), 354 | ('\u{d0e}', '\u{d10}', Malayalam), 355 | ('\u{d12}', '\u{d3a}', Malayalam), 356 | ('\u{d3d}', '\u{d44}', Malayalam), 357 | ('\u{d46}', '\u{d48}', Malayalam), 358 | ('\u{d4a}', '\u{d4e}', Malayalam), 359 | ('\u{d57}', '\u{d57}', Malayalam), 360 | ('\u{d5f}', '\u{d63}', Malayalam), 361 | ('\u{d66}', '\u{d75}', Malayalam), 362 | ('\u{d79}', '\u{d7f}', Malayalam), 363 | ('\u{d82}', '\u{d83}', Sinhala), 364 | ('\u{d85}', '\u{d96}', Sinhala), 365 | ('\u{d9a}', '\u{db1}', Sinhala), 366 | ('\u{db3}', '\u{dbb}', Sinhala), 367 | ('\u{dbd}', '\u{dbd}', Sinhala), 368 | ('\u{dc0}', '\u{dc6}', Sinhala), 369 | ('\u{dca}', '\u{dca}', Sinhala), 370 | ('\u{dcf}', '\u{dd4}', Sinhala), 371 | ('\u{dd6}', '\u{dd6}', Sinhala), 372 | ('\u{dd8}', '\u{ddf}', Sinhala), 373 | ('\u{de6}', '\u{def}', Sinhala), 374 | ('\u{df2}', '\u{df4}', Sinhala), 375 | ('\u{e01}', '\u{e3a}', Thai), 376 | ('\u{e3f}', '\u{e3f}', Common), 377 | ('\u{e40}', '\u{e5b}', Thai), 378 | ('\u{e81}', '\u{e82}', Lao), 379 | ('\u{e84}', '\u{e84}', Lao), 380 | ('\u{e87}', '\u{e88}', Lao), 381 | ('\u{e8a}', '\u{e8a}', Lao), 382 | ('\u{e8d}', '\u{e8d}', Lao), 383 | ('\u{e94}', '\u{e97}', Lao), 384 | ('\u{e99}', '\u{e9f}', Lao), 385 | ('\u{ea1}', '\u{ea3}', Lao), 386 | ('\u{ea5}', '\u{ea5}', Lao), 387 | ('\u{ea7}', '\u{ea7}', Lao), 388 | ('\u{eaa}', '\u{eab}', Lao), 389 | ('\u{ead}', '\u{eb9}', Lao), 390 | ('\u{ebb}', '\u{ebd}', Lao), 391 | ('\u{ec0}', '\u{ec4}', Lao), 392 | ('\u{ec6}', '\u{ec6}', Lao), 393 | ('\u{ec8}', '\u{ecd}', Lao), 394 | ('\u{ed0}', '\u{ed9}', Lao), 395 | ('\u{edc}', '\u{edf}', Lao), 396 | ('\u{f00}', '\u{f47}', Tibetan), 397 | ('\u{f49}', '\u{f6c}', Tibetan), 398 | ('\u{f71}', '\u{f97}', Tibetan), 399 | ('\u{f99}', '\u{fbc}', Tibetan), 400 | ('\u{fbe}', '\u{fcc}', Tibetan), 401 | ('\u{fce}', '\u{fd4}', Tibetan), 402 | ('\u{fd5}', '\u{fd8}', Common), 403 | ('\u{fd9}', '\u{fda}', Tibetan), 404 | ('\u{1000}', '\u{109f}', Myanmar), 405 | ('\u{10a0}', '\u{10c5}', Georgian), 406 | ('\u{10c7}', '\u{10c7}', Georgian), 407 | ('\u{10cd}', '\u{10cd}', Georgian), 408 | ('\u{10d0}', '\u{10fa}', Georgian), 409 | ('\u{10fb}', '\u{10fb}', Common), 410 | ('\u{10fc}', '\u{10ff}', Georgian), 411 | ('\u{1100}', '\u{11ff}', Hangul), 412 | ('\u{1200}', '\u{1248}', Ethiopic), 413 | ('\u{124a}', '\u{124d}', Ethiopic), 414 | ('\u{1250}', '\u{1256}', Ethiopic), 415 | ('\u{1258}', '\u{1258}', Ethiopic), 416 | ('\u{125a}', '\u{125d}', Ethiopic), 417 | ('\u{1260}', '\u{1288}', Ethiopic), 418 | ('\u{128a}', '\u{128d}', Ethiopic), 419 | ('\u{1290}', '\u{12b0}', Ethiopic), 420 | ('\u{12b2}', '\u{12b5}', Ethiopic), 421 | ('\u{12b8}', '\u{12be}', Ethiopic), 422 | ('\u{12c0}', '\u{12c0}', Ethiopic), 423 | ('\u{12c2}', '\u{12c5}', Ethiopic), 424 | ('\u{12c8}', '\u{12d6}', Ethiopic), 425 | ('\u{12d8}', '\u{1310}', Ethiopic), 426 | ('\u{1312}', '\u{1315}', Ethiopic), 427 | ('\u{1318}', '\u{135a}', Ethiopic), 428 | ('\u{135d}', '\u{137c}', Ethiopic), 429 | ('\u{1380}', '\u{1399}', Ethiopic), 430 | ('\u{13a0}', '\u{13f5}', Cherokee), 431 | ('\u{13f8}', '\u{13fd}', Cherokee), 432 | ('\u{1400}', '\u{167f}', Canadian_Aboriginal), 433 | ('\u{1680}', '\u{169c}', Ogham), 434 | ('\u{16a0}', '\u{16ea}', Runic), 435 | ('\u{16eb}', '\u{16ed}', Common), 436 | ('\u{16ee}', '\u{16f8}', Runic), 437 | ('\u{1700}', '\u{170c}', Tagalog), 438 | ('\u{170e}', '\u{1714}', Tagalog), 439 | ('\u{1720}', '\u{1734}', Hanunoo), 440 | ('\u{1735}', '\u{1736}', Common), 441 | ('\u{1740}', '\u{1753}', Buhid), 442 | ('\u{1760}', '\u{176c}', Tagbanwa), 443 | ('\u{176e}', '\u{1770}', Tagbanwa), 444 | ('\u{1772}', '\u{1773}', Tagbanwa), 445 | ('\u{1780}', '\u{17dd}', Khmer), 446 | ('\u{17e0}', '\u{17e9}', Khmer), 447 | ('\u{17f0}', '\u{17f9}', Khmer), 448 | ('\u{1800}', '\u{1801}', Mongolian), 449 | ('\u{1802}', '\u{1803}', Common), 450 | ('\u{1804}', '\u{1804}', Mongolian), 451 | ('\u{1805}', '\u{1805}', Common), 452 | ('\u{1806}', '\u{180e}', Mongolian), 453 | ('\u{1810}', '\u{1819}', Mongolian), 454 | ('\u{1820}', '\u{1877}', Mongolian), 455 | ('\u{1880}', '\u{18aa}', Mongolian), 456 | ('\u{18b0}', '\u{18f5}', Canadian_Aboriginal), 457 | ('\u{1900}', '\u{191e}', Limbu), 458 | ('\u{1920}', '\u{192b}', Limbu), 459 | ('\u{1930}', '\u{193b}', Limbu), 460 | ('\u{1940}', '\u{1940}', Limbu), 461 | ('\u{1944}', '\u{194f}', Limbu), 462 | ('\u{1950}', '\u{196d}', Tai_Le), 463 | ('\u{1970}', '\u{1974}', Tai_Le), 464 | ('\u{1980}', '\u{19ab}', New_Tai_Lue), 465 | ('\u{19b0}', '\u{19c9}', New_Tai_Lue), 466 | ('\u{19d0}', '\u{19da}', New_Tai_Lue), 467 | ('\u{19de}', '\u{19df}', New_Tai_Lue), 468 | ('\u{19e0}', '\u{19ff}', Khmer), 469 | ('\u{1a00}', '\u{1a1b}', Buginese), 470 | ('\u{1a1e}', '\u{1a1f}', Buginese), 471 | ('\u{1a20}', '\u{1a5e}', Tai_Tham), 472 | ('\u{1a60}', '\u{1a7c}', Tai_Tham), 473 | ('\u{1a7f}', '\u{1a89}', Tai_Tham), 474 | ('\u{1a90}', '\u{1a99}', Tai_Tham), 475 | ('\u{1aa0}', '\u{1aad}', Tai_Tham), 476 | ('\u{1ab0}', '\u{1abe}', Inherited), 477 | ('\u{1b00}', '\u{1b4b}', Balinese), 478 | ('\u{1b50}', '\u{1b7c}', Balinese), 479 | ('\u{1b80}', '\u{1bbf}', Sundanese), 480 | ('\u{1bc0}', '\u{1bf3}', Batak), 481 | ('\u{1bfc}', '\u{1bff}', Batak), 482 | ('\u{1c00}', '\u{1c37}', Lepcha), 483 | ('\u{1c3b}', '\u{1c49}', Lepcha), 484 | ('\u{1c4d}', '\u{1c4f}', Lepcha), 485 | ('\u{1c50}', '\u{1c7f}', Ol_Chiki), 486 | ('\u{1cc0}', '\u{1cc7}', Sundanese), 487 | ('\u{1cd0}', '\u{1cd2}', Inherited), 488 | ('\u{1cd3}', '\u{1cd3}', Common), 489 | ('\u{1cd4}', '\u{1ce0}', Inherited), 490 | ('\u{1ce1}', '\u{1ce1}', Common), 491 | ('\u{1ce2}', '\u{1ce8}', Inherited), 492 | ('\u{1ce9}', '\u{1cec}', Common), 493 | ('\u{1ced}', '\u{1ced}', Inherited), 494 | ('\u{1cee}', '\u{1cf3}', Common), 495 | ('\u{1cf4}', '\u{1cf4}', Inherited), 496 | ('\u{1cf5}', '\u{1cf6}', Common), 497 | ('\u{1cf8}', '\u{1cf9}', Inherited), 498 | ('\u{1d00}', '\u{1d25}', Latin), 499 | ('\u{1d26}', '\u{1d2a}', Greek), 500 | ('\u{1d2b}', '\u{1d2b}', Cyrillic), 501 | ('\u{1d2c}', '\u{1d5c}', Latin), 502 | ('\u{1d5d}', '\u{1d61}', Greek), 503 | ('\u{1d62}', '\u{1d65}', Latin), 504 | ('\u{1d66}', '\u{1d6a}', Greek), 505 | ('\u{1d6b}', '\u{1d77}', Latin), 506 | ('\u{1d78}', '\u{1d78}', Cyrillic), 507 | ('\u{1d79}', '\u{1dbe}', Latin), 508 | ('\u{1dbf}', '\u{1dbf}', Greek), 509 | ('\u{1dc0}', '\u{1df5}', Inherited), 510 | ('\u{1dfc}', '\u{1dff}', Inherited), 511 | ('\u{1e00}', '\u{1eff}', Latin), 512 | ('\u{1f00}', '\u{1f15}', Greek), 513 | ('\u{1f18}', '\u{1f1d}', Greek), 514 | ('\u{1f20}', '\u{1f45}', Greek), 515 | ('\u{1f48}', '\u{1f4d}', Greek), 516 | ('\u{1f50}', '\u{1f57}', Greek), 517 | ('\u{1f59}', '\u{1f59}', Greek), 518 | ('\u{1f5b}', '\u{1f5b}', Greek), 519 | ('\u{1f5d}', '\u{1f5d}', Greek), 520 | ('\u{1f5f}', '\u{1f7d}', Greek), 521 | ('\u{1f80}', '\u{1fb4}', Greek), 522 | ('\u{1fb6}', '\u{1fc4}', Greek), 523 | ('\u{1fc6}', '\u{1fd3}', Greek), 524 | ('\u{1fd6}', '\u{1fdb}', Greek), 525 | ('\u{1fdd}', '\u{1fef}', Greek), 526 | ('\u{1ff2}', '\u{1ff4}', Greek), 527 | ('\u{1ff6}', '\u{1ffe}', Greek), 528 | ('\u{2000}', '\u{200b}', Common), 529 | ('\u{200c}', '\u{200d}', Inherited), 530 | ('\u{200e}', '\u{2064}', Common), 531 | ('\u{2066}', '\u{2070}', Common), 532 | ('\u{2071}', '\u{2071}', Latin), 533 | ('\u{2074}', '\u{207e}', Common), 534 | ('\u{207f}', '\u{207f}', Latin), 535 | ('\u{2080}', '\u{208e}', Common), 536 | ('\u{2090}', '\u{209c}', Latin), 537 | ('\u{20a0}', '\u{20be}', Common), 538 | ('\u{20d0}', '\u{20f0}', Inherited), 539 | ('\u{2100}', '\u{2125}', Common), 540 | ('\u{2126}', '\u{2126}', Greek), 541 | ('\u{2127}', '\u{2129}', Common), 542 | ('\u{212a}', '\u{212b}', Latin), 543 | ('\u{212c}', '\u{2131}', Common), 544 | ('\u{2132}', '\u{2132}', Latin), 545 | ('\u{2133}', '\u{214d}', Common), 546 | ('\u{214e}', '\u{214e}', Latin), 547 | ('\u{214f}', '\u{215f}', Common), 548 | ('\u{2160}', '\u{2188}', Latin), 549 | ('\u{2189}', '\u{218b}', Common), 550 | ('\u{2190}', '\u{23fa}', Common), 551 | ('\u{2400}', '\u{2426}', Common), 552 | ('\u{2440}', '\u{244a}', Common), 553 | ('\u{2460}', '\u{27ff}', Common), 554 | ('\u{2800}', '\u{28ff}', Braille), 555 | ('\u{2900}', '\u{2b73}', Common), 556 | ('\u{2b76}', '\u{2b95}', Common), 557 | ('\u{2b98}', '\u{2bb9}', Common), 558 | ('\u{2bbd}', '\u{2bc8}', Common), 559 | ('\u{2bca}', '\u{2bd1}', Common), 560 | ('\u{2bec}', '\u{2bef}', Common), 561 | ('\u{2c00}', '\u{2c2e}', Glagolitic), 562 | ('\u{2c30}', '\u{2c5e}', Glagolitic), 563 | ('\u{2c60}', '\u{2c7f}', Latin), 564 | ('\u{2c80}', '\u{2cf3}', Coptic), 565 | ('\u{2cf9}', '\u{2cff}', Coptic), 566 | ('\u{2d00}', '\u{2d25}', Georgian), 567 | ('\u{2d27}', '\u{2d27}', Georgian), 568 | ('\u{2d2d}', '\u{2d2d}', Georgian), 569 | ('\u{2d30}', '\u{2d67}', Tifinagh), 570 | ('\u{2d6f}', '\u{2d70}', Tifinagh), 571 | ('\u{2d7f}', '\u{2d7f}', Tifinagh), 572 | ('\u{2d80}', '\u{2d96}', Ethiopic), 573 | ('\u{2da0}', '\u{2da6}', Ethiopic), 574 | ('\u{2da8}', '\u{2dae}', Ethiopic), 575 | ('\u{2db0}', '\u{2db6}', Ethiopic), 576 | ('\u{2db8}', '\u{2dbe}', Ethiopic), 577 | ('\u{2dc0}', '\u{2dc6}', Ethiopic), 578 | ('\u{2dc8}', '\u{2dce}', Ethiopic), 579 | ('\u{2dd0}', '\u{2dd6}', Ethiopic), 580 | ('\u{2dd8}', '\u{2dde}', Ethiopic), 581 | ('\u{2de0}', '\u{2dff}', Cyrillic), 582 | ('\u{2e00}', '\u{2e42}', Common), 583 | ('\u{2e80}', '\u{2e99}', Han), 584 | ('\u{2e9b}', '\u{2ef3}', Han), 585 | ('\u{2f00}', '\u{2fd5}', Han), 586 | ('\u{2ff0}', '\u{2ffb}', Common), 587 | ('\u{3000}', '\u{3004}', Common), 588 | ('\u{3005}', '\u{3005}', Han), 589 | ('\u{3006}', '\u{3006}', Common), 590 | ('\u{3007}', '\u{3007}', Han), 591 | ('\u{3008}', '\u{3020}', Common), 592 | ('\u{3021}', '\u{3029}', Han), 593 | ('\u{302a}', '\u{302d}', Inherited), 594 | ('\u{302e}', '\u{302f}', Hangul), 595 | ('\u{3030}', '\u{3037}', Common), 596 | ('\u{3038}', '\u{303b}', Han), 597 | ('\u{303c}', '\u{303f}', Common), 598 | ('\u{3041}', '\u{3096}', Hiragana), 599 | ('\u{3099}', '\u{309a}', Inherited), 600 | ('\u{309b}', '\u{309c}', Common), 601 | ('\u{309d}', '\u{309f}', Hiragana), 602 | ('\u{30a0}', '\u{30a0}', Common), 603 | ('\u{30a1}', '\u{30fa}', Katakana), 604 | ('\u{30fb}', '\u{30fc}', Common), 605 | ('\u{30fd}', '\u{30ff}', Katakana), 606 | ('\u{3105}', '\u{312d}', Bopomofo), 607 | ('\u{3131}', '\u{318e}', Hangul), 608 | ('\u{3190}', '\u{319f}', Common), 609 | ('\u{31a0}', '\u{31ba}', Bopomofo), 610 | ('\u{31c0}', '\u{31e3}', Common), 611 | ('\u{31f0}', '\u{31ff}', Katakana), 612 | ('\u{3200}', '\u{321e}', Hangul), 613 | ('\u{3220}', '\u{325f}', Common), 614 | ('\u{3260}', '\u{327e}', Hangul), 615 | ('\u{327f}', '\u{32cf}', Common), 616 | ('\u{32d0}', '\u{32fe}', Katakana), 617 | ('\u{3300}', '\u{3357}', Katakana), 618 | ('\u{3358}', '\u{33ff}', Common), 619 | ('\u{3400}', '\u{4db5}', Han), 620 | ('\u{4dc0}', '\u{4dff}', Common), 621 | ('\u{4e00}', '\u{9fd5}', Han), 622 | ('\u{a000}', '\u{a48c}', Yi), 623 | ('\u{a490}', '\u{a4c6}', Yi), 624 | ('\u{a4d0}', '\u{a4ff}', Lisu), 625 | ('\u{a500}', '\u{a62b}', Vai), 626 | ('\u{a640}', '\u{a69f}', Cyrillic), 627 | ('\u{a6a0}', '\u{a6f7}', Bamum), 628 | ('\u{a700}', '\u{a721}', Common), 629 | ('\u{a722}', '\u{a787}', Latin), 630 | ('\u{a788}', '\u{a78a}', Common), 631 | ('\u{a78b}', '\u{a7ad}', Latin), 632 | ('\u{a7b0}', '\u{a7b7}', Latin), 633 | ('\u{a7f7}', '\u{a7ff}', Latin), 634 | ('\u{a800}', '\u{a82b}', Syloti_Nagri), 635 | ('\u{a830}', '\u{a839}', Common), 636 | ('\u{a840}', '\u{a877}', Phags_Pa), 637 | ('\u{a880}', '\u{a8c4}', Saurashtra), 638 | ('\u{a8ce}', '\u{a8d9}', Saurashtra), 639 | ('\u{a8e0}', '\u{a8fd}', Devanagari), 640 | ('\u{a900}', '\u{a92d}', Kayah_Li), 641 | ('\u{a92e}', '\u{a92e}', Common), 642 | ('\u{a92f}', '\u{a92f}', Kayah_Li), 643 | ('\u{a930}', '\u{a953}', Rejang), 644 | ('\u{a95f}', '\u{a95f}', Rejang), 645 | ('\u{a960}', '\u{a97c}', Hangul), 646 | ('\u{a980}', '\u{a9cd}', Javanese), 647 | ('\u{a9cf}', '\u{a9cf}', Common), 648 | ('\u{a9d0}', '\u{a9d9}', Javanese), 649 | ('\u{a9de}', '\u{a9df}', Javanese), 650 | ('\u{a9e0}', '\u{a9fe}', Myanmar), 651 | ('\u{aa00}', '\u{aa36}', Cham), 652 | ('\u{aa40}', '\u{aa4d}', Cham), 653 | ('\u{aa50}', '\u{aa59}', Cham), 654 | ('\u{aa5c}', '\u{aa5f}', Cham), 655 | ('\u{aa60}', '\u{aa7f}', Myanmar), 656 | ('\u{aa80}', '\u{aac2}', Tai_Viet), 657 | ('\u{aadb}', '\u{aadf}', Tai_Viet), 658 | ('\u{aae0}', '\u{aaf6}', Meetei_Mayek), 659 | ('\u{ab01}', '\u{ab06}', Ethiopic), 660 | ('\u{ab09}', '\u{ab0e}', Ethiopic), 661 | ('\u{ab11}', '\u{ab16}', Ethiopic), 662 | ('\u{ab20}', '\u{ab26}', Ethiopic), 663 | ('\u{ab28}', '\u{ab2e}', Ethiopic), 664 | ('\u{ab30}', '\u{ab5a}', Latin), 665 | ('\u{ab5b}', '\u{ab5b}', Common), 666 | ('\u{ab5c}', '\u{ab64}', Latin), 667 | ('\u{ab65}', '\u{ab65}', Greek), 668 | ('\u{ab70}', '\u{abbf}', Cherokee), 669 | ('\u{abc0}', '\u{abed}', Meetei_Mayek), 670 | ('\u{abf0}', '\u{abf9}', Meetei_Mayek), 671 | ('\u{ac00}', '\u{d7a3}', Hangul), 672 | ('\u{d7b0}', '\u{d7c6}', Hangul), 673 | ('\u{d7cb}', '\u{d7fb}', Hangul), 674 | ('\u{f900}', '\u{fa6d}', Han), 675 | ('\u{fa70}', '\u{fad9}', Han), 676 | ('\u{fb00}', '\u{fb06}', Latin), 677 | ('\u{fb13}', '\u{fb17}', Armenian), 678 | ('\u{fb1d}', '\u{fb36}', Hebrew), 679 | ('\u{fb38}', '\u{fb3c}', Hebrew), 680 | ('\u{fb3e}', '\u{fb3e}', Hebrew), 681 | ('\u{fb40}', '\u{fb41}', Hebrew), 682 | ('\u{fb43}', '\u{fb44}', Hebrew), 683 | ('\u{fb46}', '\u{fb4f}', Hebrew), 684 | ('\u{fb50}', '\u{fbc1}', Arabic), 685 | ('\u{fbd3}', '\u{fd3d}', Arabic), 686 | ('\u{fd3e}', '\u{fd3f}', Common), 687 | ('\u{fd50}', '\u{fd8f}', Arabic), 688 | ('\u{fd92}', '\u{fdc7}', Arabic), 689 | ('\u{fdf0}', '\u{fdfd}', Arabic), 690 | ('\u{fe00}', '\u{fe0f}', Inherited), 691 | ('\u{fe10}', '\u{fe19}', Common), 692 | ('\u{fe20}', '\u{fe2d}', Inherited), 693 | ('\u{fe2e}', '\u{fe2f}', Cyrillic), 694 | ('\u{fe30}', '\u{fe52}', Common), 695 | ('\u{fe54}', '\u{fe66}', Common), 696 | ('\u{fe68}', '\u{fe6b}', Common), 697 | ('\u{fe70}', '\u{fe74}', Arabic), 698 | ('\u{fe76}', '\u{fefc}', Arabic), 699 | ('\u{feff}', '\u{feff}', Common), 700 | ('\u{ff01}', '\u{ff20}', Common), 701 | ('\u{ff21}', '\u{ff3a}', Latin), 702 | ('\u{ff3b}', '\u{ff40}', Common), 703 | ('\u{ff41}', '\u{ff5a}', Latin), 704 | ('\u{ff5b}', '\u{ff65}', Common), 705 | ('\u{ff66}', '\u{ff6f}', Katakana), 706 | ('\u{ff70}', '\u{ff70}', Common), 707 | ('\u{ff71}', '\u{ff9d}', Katakana), 708 | ('\u{ff9e}', '\u{ff9f}', Common), 709 | ('\u{ffa0}', '\u{ffbe}', Hangul), 710 | ('\u{ffc2}', '\u{ffc7}', Hangul), 711 | ('\u{ffca}', '\u{ffcf}', Hangul), 712 | ('\u{ffd2}', '\u{ffd7}', Hangul), 713 | ('\u{ffda}', '\u{ffdc}', Hangul), 714 | ('\u{ffe0}', '\u{ffe6}', Common), 715 | ('\u{ffe8}', '\u{ffee}', Common), 716 | ('\u{fff9}', '\u{fffd}', Common), 717 | ('\u{10000}', '\u{1000b}', Linear_B), 718 | ('\u{1000d}', '\u{10026}', Linear_B), 719 | ('\u{10028}', '\u{1003a}', Linear_B), 720 | ('\u{1003c}', '\u{1003d}', Linear_B), 721 | ('\u{1003f}', '\u{1004d}', Linear_B), 722 | ('\u{10050}', '\u{1005d}', Linear_B), 723 | ('\u{10080}', '\u{100fa}', Linear_B), 724 | ('\u{10100}', '\u{10102}', Common), 725 | ('\u{10107}', '\u{10133}', Common), 726 | ('\u{10137}', '\u{1013f}', Common), 727 | ('\u{10140}', '\u{1018c}', Greek), 728 | ('\u{10190}', '\u{1019b}', Common), 729 | ('\u{101a0}', '\u{101a0}', Greek), 730 | ('\u{101d0}', '\u{101fc}', Common), 731 | ('\u{101fd}', '\u{101fd}', Inherited), 732 | ('\u{10280}', '\u{1029c}', Lycian), 733 | ('\u{102a0}', '\u{102d0}', Carian), 734 | ('\u{102e0}', '\u{102e0}', Inherited), 735 | ('\u{102e1}', '\u{102fb}', Common), 736 | ('\u{10300}', '\u{10323}', Old_Italic), 737 | ('\u{10330}', '\u{1034a}', Gothic), 738 | ('\u{10350}', '\u{1037a}', Old_Permic), 739 | ('\u{10380}', '\u{1039d}', Ugaritic), 740 | ('\u{1039f}', '\u{1039f}', Ugaritic), 741 | ('\u{103a0}', '\u{103c3}', Old_Persian), 742 | ('\u{103c8}', '\u{103d5}', Old_Persian), 743 | ('\u{10400}', '\u{1044f}', Deseret), 744 | ('\u{10450}', '\u{1047f}', Shavian), 745 | ('\u{10480}', '\u{1049d}', Osmanya), 746 | ('\u{104a0}', '\u{104a9}', Osmanya), 747 | ('\u{10500}', '\u{10527}', Elbasan), 748 | ('\u{10530}', '\u{10563}', Caucasian_Albanian), 749 | ('\u{1056f}', '\u{1056f}', Caucasian_Albanian), 750 | ('\u{10600}', '\u{10736}', Linear_A), 751 | ('\u{10740}', '\u{10755}', Linear_A), 752 | ('\u{10760}', '\u{10767}', Linear_A), 753 | ('\u{10800}', '\u{10805}', Cypriot), 754 | ('\u{10808}', '\u{10808}', Cypriot), 755 | ('\u{1080a}', '\u{10835}', Cypriot), 756 | ('\u{10837}', '\u{10838}', Cypriot), 757 | ('\u{1083c}', '\u{1083c}', Cypriot), 758 | ('\u{1083f}', '\u{1083f}', Cypriot), 759 | ('\u{10840}', '\u{10855}', Imperial_Aramaic), 760 | ('\u{10857}', '\u{1085f}', Imperial_Aramaic), 761 | ('\u{10860}', '\u{1087f}', Palmyrene), 762 | ('\u{10880}', '\u{1089e}', Nabataean), 763 | ('\u{108a7}', '\u{108af}', Nabataean), 764 | ('\u{108e0}', '\u{108f2}', Hatran), 765 | ('\u{108f4}', '\u{108f5}', Hatran), 766 | ('\u{108fb}', '\u{108ff}', Hatran), 767 | ('\u{10900}', '\u{1091b}', Phoenician), 768 | ('\u{1091f}', '\u{1091f}', Phoenician), 769 | ('\u{10920}', '\u{10939}', Lydian), 770 | ('\u{1093f}', '\u{1093f}', Lydian), 771 | ('\u{10980}', '\u{1099f}', Meroitic_Hieroglyphs), 772 | ('\u{109a0}', '\u{109b7}', Meroitic_Cursive), 773 | ('\u{109bc}', '\u{109cf}', Meroitic_Cursive), 774 | ('\u{109d2}', '\u{109ff}', Meroitic_Cursive), 775 | ('\u{10a00}', '\u{10a03}', Kharoshthi), 776 | ('\u{10a05}', '\u{10a06}', Kharoshthi), 777 | ('\u{10a0c}', '\u{10a13}', Kharoshthi), 778 | ('\u{10a15}', '\u{10a17}', Kharoshthi), 779 | ('\u{10a19}', '\u{10a33}', Kharoshthi), 780 | ('\u{10a38}', '\u{10a3a}', Kharoshthi), 781 | ('\u{10a3f}', '\u{10a47}', Kharoshthi), 782 | ('\u{10a50}', '\u{10a58}', Kharoshthi), 783 | ('\u{10a60}', '\u{10a7f}', Old_South_Arabian), 784 | ('\u{10a80}', '\u{10a9f}', Old_North_Arabian), 785 | ('\u{10ac0}', '\u{10ae6}', Manichaean), 786 | ('\u{10aeb}', '\u{10af6}', Manichaean), 787 | ('\u{10b00}', '\u{10b35}', Avestan), 788 | ('\u{10b39}', '\u{10b3f}', Avestan), 789 | ('\u{10b40}', '\u{10b55}', Inscriptional_Parthian), 790 | ('\u{10b58}', '\u{10b5f}', Inscriptional_Parthian), 791 | ('\u{10b60}', '\u{10b72}', Inscriptional_Pahlavi), 792 | ('\u{10b78}', '\u{10b7f}', Inscriptional_Pahlavi), 793 | ('\u{10b80}', '\u{10b91}', Psalter_Pahlavi), 794 | ('\u{10b99}', '\u{10b9c}', Psalter_Pahlavi), 795 | ('\u{10ba9}', '\u{10baf}', Psalter_Pahlavi), 796 | ('\u{10c00}', '\u{10c48}', Old_Turkic), 797 | ('\u{10c80}', '\u{10cb2}', Old_Hungarian), 798 | ('\u{10cc0}', '\u{10cf2}', Old_Hungarian), 799 | ('\u{10cfa}', '\u{10cff}', Old_Hungarian), 800 | ('\u{10e60}', '\u{10e7e}', Arabic), 801 | ('\u{11000}', '\u{1104d}', Brahmi), 802 | ('\u{11052}', '\u{1106f}', Brahmi), 803 | ('\u{1107f}', '\u{1107f}', Brahmi), 804 | ('\u{11080}', '\u{110c1}', Kaithi), 805 | ('\u{110d0}', '\u{110e8}', Sora_Sompeng), 806 | ('\u{110f0}', '\u{110f9}', Sora_Sompeng), 807 | ('\u{11100}', '\u{11134}', Chakma), 808 | ('\u{11136}', '\u{11143}', Chakma), 809 | ('\u{11150}', '\u{11176}', Mahajani), 810 | ('\u{11180}', '\u{111cd}', Sharada), 811 | ('\u{111d0}', '\u{111df}', Sharada), 812 | ('\u{111e1}', '\u{111f4}', Sinhala), 813 | ('\u{11200}', '\u{11211}', Khojki), 814 | ('\u{11213}', '\u{1123d}', Khojki), 815 | ('\u{11280}', '\u{11286}', Multani), 816 | ('\u{11288}', '\u{11288}', Multani), 817 | ('\u{1128a}', '\u{1128d}', Multani), 818 | ('\u{1128f}', '\u{1129d}', Multani), 819 | ('\u{1129f}', '\u{112a9}', Multani), 820 | ('\u{112b0}', '\u{112ea}', Khudawadi), 821 | ('\u{112f0}', '\u{112f9}', Khudawadi), 822 | ('\u{11300}', '\u{11303}', Grantha), 823 | ('\u{11305}', '\u{1130c}', Grantha), 824 | ('\u{1130f}', '\u{11310}', Grantha), 825 | ('\u{11313}', '\u{11328}', Grantha), 826 | ('\u{1132a}', '\u{11330}', Grantha), 827 | ('\u{11332}', '\u{11333}', Grantha), 828 | ('\u{11335}', '\u{11339}', Grantha), 829 | ('\u{1133c}', '\u{11344}', Grantha), 830 | ('\u{11347}', '\u{11348}', Grantha), 831 | ('\u{1134b}', '\u{1134d}', Grantha), 832 | ('\u{11350}', '\u{11350}', Grantha), 833 | ('\u{11357}', '\u{11357}', Grantha), 834 | ('\u{1135d}', '\u{11363}', Grantha), 835 | ('\u{11366}', '\u{1136c}', Grantha), 836 | ('\u{11370}', '\u{11374}', Grantha), 837 | ('\u{11480}', '\u{114c7}', Tirhuta), 838 | ('\u{114d0}', '\u{114d9}', Tirhuta), 839 | ('\u{11580}', '\u{115b5}', Siddham), 840 | ('\u{115b8}', '\u{115dd}', Siddham), 841 | ('\u{11600}', '\u{11644}', Modi), 842 | ('\u{11650}', '\u{11659}', Modi), 843 | ('\u{11680}', '\u{116b7}', Takri), 844 | ('\u{116c0}', '\u{116c9}', Takri), 845 | ('\u{11700}', '\u{11719}', Ahom), 846 | ('\u{1171d}', '\u{1172b}', Ahom), 847 | ('\u{11730}', '\u{1173f}', Ahom), 848 | ('\u{118a0}', '\u{118f2}', Warang_Citi), 849 | ('\u{118ff}', '\u{118ff}', Warang_Citi), 850 | ('\u{11ac0}', '\u{11af8}', Pau_Cin_Hau), 851 | ('\u{12000}', '\u{12399}', Cuneiform), 852 | ('\u{12400}', '\u{1246e}', Cuneiform), 853 | ('\u{12470}', '\u{12474}', Cuneiform), 854 | ('\u{12480}', '\u{12543}', Cuneiform), 855 | ('\u{13000}', '\u{1342e}', Egyptian_Hieroglyphs), 856 | ('\u{14400}', '\u{14646}', Anatolian_Hieroglyphs), 857 | ('\u{16800}', '\u{16a38}', Bamum), 858 | ('\u{16a40}', '\u{16a5e}', Mro), 859 | ('\u{16a60}', '\u{16a69}', Mro), 860 | ('\u{16a6e}', '\u{16a6f}', Mro), 861 | ('\u{16ad0}', '\u{16aed}', Bassa_Vah), 862 | ('\u{16af0}', '\u{16af5}', Bassa_Vah), 863 | ('\u{16b00}', '\u{16b45}', Pahawh_Hmong), 864 | ('\u{16b50}', '\u{16b59}', Pahawh_Hmong), 865 | ('\u{16b5b}', '\u{16b61}', Pahawh_Hmong), 866 | ('\u{16b63}', '\u{16b77}', Pahawh_Hmong), 867 | ('\u{16b7d}', '\u{16b8f}', Pahawh_Hmong), 868 | ('\u{16f00}', '\u{16f44}', Miao), 869 | ('\u{16f50}', '\u{16f7e}', Miao), 870 | ('\u{16f8f}', '\u{16f9f}', Miao), 871 | ('\u{1b000}', '\u{1b000}', Katakana), 872 | ('\u{1b001}', '\u{1b001}', Hiragana), 873 | ('\u{1bc00}', '\u{1bc6a}', Duployan), 874 | ('\u{1bc70}', '\u{1bc7c}', Duployan), 875 | ('\u{1bc80}', '\u{1bc88}', Duployan), 876 | ('\u{1bc90}', '\u{1bc99}', Duployan), 877 | ('\u{1bc9c}', '\u{1bc9f}', Duployan), 878 | ('\u{1bca0}', '\u{1bca3}', Common), 879 | ('\u{1d000}', '\u{1d0f5}', Common), 880 | ('\u{1d100}', '\u{1d126}', Common), 881 | ('\u{1d129}', '\u{1d166}', Common), 882 | ('\u{1d167}', '\u{1d169}', Inherited), 883 | ('\u{1d16a}', '\u{1d17a}', Common), 884 | ('\u{1d17b}', '\u{1d182}', Inherited), 885 | ('\u{1d183}', '\u{1d184}', Common), 886 | ('\u{1d185}', '\u{1d18b}', Inherited), 887 | ('\u{1d18c}', '\u{1d1a9}', Common), 888 | ('\u{1d1aa}', '\u{1d1ad}', Inherited), 889 | ('\u{1d1ae}', '\u{1d1e8}', Common), 890 | ('\u{1d200}', '\u{1d245}', Greek), 891 | ('\u{1d300}', '\u{1d356}', Common), 892 | ('\u{1d360}', '\u{1d371}', Common), 893 | ('\u{1d400}', '\u{1d454}', Common), 894 | ('\u{1d456}', '\u{1d49c}', Common), 895 | ('\u{1d49e}', '\u{1d49f}', Common), 896 | ('\u{1d4a2}', '\u{1d4a2}', Common), 897 | ('\u{1d4a5}', '\u{1d4a6}', Common), 898 | ('\u{1d4a9}', '\u{1d4ac}', Common), 899 | ('\u{1d4ae}', '\u{1d4b9}', Common), 900 | ('\u{1d4bb}', '\u{1d4bb}', Common), 901 | ('\u{1d4bd}', '\u{1d4c3}', Common), 902 | ('\u{1d4c5}', '\u{1d505}', Common), 903 | ('\u{1d507}', '\u{1d50a}', Common), 904 | ('\u{1d50d}', '\u{1d514}', Common), 905 | ('\u{1d516}', '\u{1d51c}', Common), 906 | ('\u{1d51e}', '\u{1d539}', Common), 907 | ('\u{1d53b}', '\u{1d53e}', Common), 908 | ('\u{1d540}', '\u{1d544}', Common), 909 | ('\u{1d546}', '\u{1d546}', Common), 910 | ('\u{1d54a}', '\u{1d550}', Common), 911 | ('\u{1d552}', '\u{1d6a5}', Common), 912 | ('\u{1d6a8}', '\u{1d7cb}', Common), 913 | ('\u{1d7ce}', '\u{1d7ff}', Common), 914 | ('\u{1d800}', '\u{1da8b}', SignWriting), 915 | ('\u{1da9b}', '\u{1da9f}', SignWriting), 916 | ('\u{1daa1}', '\u{1daaf}', SignWriting), 917 | ('\u{1e800}', '\u{1e8c4}', Mende_Kikakui), 918 | ('\u{1e8c7}', '\u{1e8d6}', Mende_Kikakui), 919 | ('\u{1ee00}', '\u{1ee03}', Arabic), 920 | ('\u{1ee05}', '\u{1ee1f}', Arabic), 921 | ('\u{1ee21}', '\u{1ee22}', Arabic), 922 | ('\u{1ee24}', '\u{1ee24}', Arabic), 923 | ('\u{1ee27}', '\u{1ee27}', Arabic), 924 | ('\u{1ee29}', '\u{1ee32}', Arabic), 925 | ('\u{1ee34}', '\u{1ee37}', Arabic), 926 | ('\u{1ee39}', '\u{1ee39}', Arabic), 927 | ('\u{1ee3b}', '\u{1ee3b}', Arabic), 928 | ('\u{1ee42}', '\u{1ee42}', Arabic), 929 | ('\u{1ee47}', '\u{1ee47}', Arabic), 930 | ('\u{1ee49}', '\u{1ee49}', Arabic), 931 | ('\u{1ee4b}', '\u{1ee4b}', Arabic), 932 | ('\u{1ee4d}', '\u{1ee4f}', Arabic), 933 | ('\u{1ee51}', '\u{1ee52}', Arabic), 934 | ('\u{1ee54}', '\u{1ee54}', Arabic), 935 | ('\u{1ee57}', '\u{1ee57}', Arabic), 936 | ('\u{1ee59}', '\u{1ee59}', Arabic), 937 | ('\u{1ee5b}', '\u{1ee5b}', Arabic), 938 | ('\u{1ee5d}', '\u{1ee5d}', Arabic), 939 | ('\u{1ee5f}', '\u{1ee5f}', Arabic), 940 | ('\u{1ee61}', '\u{1ee62}', Arabic), 941 | ('\u{1ee64}', '\u{1ee64}', Arabic), 942 | ('\u{1ee67}', '\u{1ee6a}', Arabic), 943 | ('\u{1ee6c}', '\u{1ee72}', Arabic), 944 | ('\u{1ee74}', '\u{1ee77}', Arabic), 945 | ('\u{1ee79}', '\u{1ee7c}', Arabic), 946 | ('\u{1ee7e}', '\u{1ee7e}', Arabic), 947 | ('\u{1ee80}', '\u{1ee89}', Arabic), 948 | ('\u{1ee8b}', '\u{1ee9b}', Arabic), 949 | ('\u{1eea1}', '\u{1eea3}', Arabic), 950 | ('\u{1eea5}', '\u{1eea9}', Arabic), 951 | ('\u{1eeab}', '\u{1eebb}', Arabic), 952 | ('\u{1eef0}', '\u{1eef1}', Arabic), 953 | ('\u{1f000}', '\u{1f02b}', Common), 954 | ('\u{1f030}', '\u{1f093}', Common), 955 | ('\u{1f0a0}', '\u{1f0ae}', Common), 956 | ('\u{1f0b1}', '\u{1f0bf}', Common), 957 | ('\u{1f0c1}', '\u{1f0cf}', Common), 958 | ('\u{1f0d1}', '\u{1f0f5}', Common), 959 | ('\u{1f100}', '\u{1f10c}', Common), 960 | ('\u{1f110}', '\u{1f12e}', Common), 961 | ('\u{1f130}', '\u{1f16b}', Common), 962 | ('\u{1f170}', '\u{1f19a}', Common), 963 | ('\u{1f1e6}', '\u{1f1ff}', Common), 964 | ('\u{1f200}', '\u{1f200}', Hiragana), 965 | ('\u{1f201}', '\u{1f202}', Common), 966 | ('\u{1f210}', '\u{1f23a}', Common), 967 | ('\u{1f240}', '\u{1f248}', Common), 968 | ('\u{1f250}', '\u{1f251}', Common), 969 | ('\u{1f300}', '\u{1f579}', Common), 970 | ('\u{1f57b}', '\u{1f5a3}', Common), 971 | ('\u{1f5a5}', '\u{1f6d0}', Common), 972 | ('\u{1f6e0}', '\u{1f6ec}', Common), 973 | ('\u{1f6f0}', '\u{1f6f3}', Common), 974 | ('\u{1f700}', '\u{1f773}', Common), 975 | ('\u{1f780}', '\u{1f7d4}', Common), 976 | ('\u{1f800}', '\u{1f80b}', Common), 977 | ('\u{1f810}', '\u{1f847}', Common), 978 | ('\u{1f850}', '\u{1f859}', Common), 979 | ('\u{1f860}', '\u{1f887}', Common), 980 | ('\u{1f890}', '\u{1f8ad}', Common), 981 | ('\u{1f910}', '\u{1f918}', Common), 982 | ('\u{1f980}', '\u{1f984}', Common), 983 | ('\u{1f9c0}', '\u{1f9c0}', Common), 984 | ('\u{20000}', '\u{2a6d6}', Han), 985 | ('\u{2a700}', '\u{2b734}', Han), 986 | ('\u{2b740}', '\u{2b81d}', Han), 987 | ('\u{2b820}', '\u{2cea1}', Han), 988 | ('\u{2f800}', '\u{2fa1d}', Han), 989 | ('\u{e0001}', '\u{e0001}', Common), 990 | ('\u{e0020}', '\u{e007f}', Common), 991 | ('\u{e0100}', '\u{e01ef}', Inherited), 992 | ]; 993 | 994 | impl Script { 995 | pub fn to_hb_script(self) -> harfbuzz_sys::hb_script_t { 996 | match self { 997 | Ahom => HB_SCRIPT_AHOM, 998 | Anatolian_Hieroglyphs => HB_SCRIPT_ANATOLIAN_HIEROGLYPHS, 999 | Arabic => HB_SCRIPT_ARABIC, 1000 | Armenian => HB_SCRIPT_ARMENIAN, 1001 | Avestan => HB_SCRIPT_AVESTAN, 1002 | Balinese => HB_SCRIPT_BALINESE, 1003 | Bamum => HB_SCRIPT_BAMUM, 1004 | Bassa_Vah => HB_SCRIPT_BASSA_VAH, 1005 | Batak => HB_SCRIPT_BATAK, 1006 | Bengali => HB_SCRIPT_BENGALI, 1007 | Bopomofo => HB_SCRIPT_BOPOMOFO, 1008 | Brahmi => HB_SCRIPT_BRAHMI, 1009 | Braille => HB_SCRIPT_BRAILLE, 1010 | Buginese => HB_SCRIPT_BUGINESE, 1011 | Buhid => HB_SCRIPT_BUHID, 1012 | Canadian_Aboriginal => HB_SCRIPT_CANADIAN_SYLLABICS, 1013 | Carian => HB_SCRIPT_CARIAN, 1014 | Caucasian_Albanian => HB_SCRIPT_CAUCASIAN_ALBANIAN, 1015 | Chakma => HB_SCRIPT_CHAKMA, 1016 | Cham => HB_SCRIPT_CHAM, 1017 | Cherokee => HB_SCRIPT_CHEROKEE, 1018 | Common => HB_SCRIPT_COMMON, 1019 | Coptic => HB_SCRIPT_COPTIC, 1020 | Cuneiform => HB_SCRIPT_CUNEIFORM, 1021 | Cypriot => HB_SCRIPT_CYPRIOT, 1022 | Cyrillic => HB_SCRIPT_CYRILLIC, 1023 | Deseret => HB_SCRIPT_DESERET, 1024 | Devanagari => HB_SCRIPT_DEVANAGARI, 1025 | Duployan => HB_SCRIPT_DUPLOYAN, 1026 | Egyptian_Hieroglyphs => HB_SCRIPT_EGYPTIAN_HIEROGLYPHS, 1027 | Elbasan => HB_SCRIPT_ELBASAN, 1028 | Ethiopic => HB_SCRIPT_ETHIOPIC, 1029 | Georgian => HB_SCRIPT_GEORGIAN, 1030 | Glagolitic => HB_SCRIPT_GLAGOLITIC, 1031 | Gothic => HB_SCRIPT_GOTHIC, 1032 | Grantha => HB_SCRIPT_GRANTHA, 1033 | Greek => HB_SCRIPT_GREEK, 1034 | Gujarati => HB_SCRIPT_GUJARATI, 1035 | Gurmukhi => HB_SCRIPT_GURMUKHI, 1036 | Han => HB_SCRIPT_HAN, 1037 | Hangul => HB_SCRIPT_HANGUL, 1038 | Hanunoo => HB_SCRIPT_HANUNOO, 1039 | Hatran => HB_SCRIPT_HATRAN, 1040 | Hebrew => HB_SCRIPT_HEBREW, 1041 | Hiragana => HB_SCRIPT_HIRAGANA, 1042 | Imperial_Aramaic => HB_SCRIPT_IMPERIAL_ARAMAIC, 1043 | Inherited => HB_SCRIPT_INHERITED, 1044 | Inscriptional_Pahlavi => HB_SCRIPT_INSCRIPTIONAL_PAHLAVI, 1045 | Inscriptional_Parthian => HB_SCRIPT_INSCRIPTIONAL_PARTHIAN, 1046 | Javanese => HB_SCRIPT_JAVANESE, 1047 | Kaithi => HB_SCRIPT_KAITHI, 1048 | Kannada => HB_SCRIPT_KANNADA, 1049 | Katakana => HB_SCRIPT_KATAKANA, 1050 | Kayah_Li => HB_SCRIPT_KAYAH_LI, 1051 | Kharoshthi => HB_SCRIPT_KHAROSHTHI, 1052 | Khmer => HB_SCRIPT_KHMER, 1053 | Khojki => HB_SCRIPT_KHOJKI, 1054 | Khudawadi => HB_SCRIPT_KHUDAWADI, 1055 | Lao => HB_SCRIPT_LAO, 1056 | Latin => HB_SCRIPT_LATIN, 1057 | Lepcha => HB_SCRIPT_LEPCHA, 1058 | Limbu => HB_SCRIPT_LIMBU, 1059 | Linear_A => HB_SCRIPT_LINEAR_A, 1060 | Linear_B => HB_SCRIPT_LINEAR_B, 1061 | Lisu => HB_SCRIPT_LISU, 1062 | Lycian => HB_SCRIPT_LYCIAN, 1063 | Lydian => HB_SCRIPT_LYDIAN, 1064 | Mahajani => HB_SCRIPT_MAHAJANI, 1065 | Malayalam => HB_SCRIPT_MALAYALAM, 1066 | Mandaic => HB_SCRIPT_MANDAIC, 1067 | Manichaean => HB_SCRIPT_MANICHAEAN, 1068 | Meetei_Mayek => HB_SCRIPT_MEETEI_MAYEK, 1069 | Mende_Kikakui => HB_SCRIPT_MENDE_KIKAKUI, 1070 | Meroitic_Cursive => HB_SCRIPT_MEROITIC_CURSIVE, 1071 | Meroitic_Hieroglyphs => HB_SCRIPT_MEROITIC_HIEROGLYPHS, 1072 | Miao => HB_SCRIPT_MIAO, 1073 | Modi => HB_SCRIPT_MODI, 1074 | Mongolian => HB_SCRIPT_MONGOLIAN, 1075 | Mro => HB_SCRIPT_MRO, 1076 | Multani => HB_SCRIPT_MULTANI, 1077 | Myanmar => HB_SCRIPT_MYANMAR, 1078 | Nabataean => HB_SCRIPT_NABATAEAN, 1079 | New_Tai_Lue => HB_SCRIPT_NEW_TAI_LUE, 1080 | Nko => HB_SCRIPT_NKO, 1081 | Ogham => HB_SCRIPT_OGHAM, 1082 | Ol_Chiki => HB_SCRIPT_OL_CHIKI, 1083 | Old_Hungarian => HB_SCRIPT_OLD_HUNGARIAN, 1084 | Old_Italic => HB_SCRIPT_OLD_ITALIC, 1085 | Old_North_Arabian => HB_SCRIPT_OLD_NORTH_ARABIAN, 1086 | Old_Permic => HB_SCRIPT_OLD_PERMIC, 1087 | Old_Persian => HB_SCRIPT_OLD_PERSIAN, 1088 | Old_South_Arabian => HB_SCRIPT_OLD_SOUTH_ARABIAN, 1089 | Old_Turkic => HB_SCRIPT_OLD_TURKIC, 1090 | Oriya => HB_SCRIPT_ORIYA, 1091 | Osmanya => HB_SCRIPT_OSMANYA, 1092 | Pahawh_Hmong => HB_SCRIPT_PAHAWH_HMONG, 1093 | Palmyrene => HB_SCRIPT_PALMYRENE, 1094 | Pau_Cin_Hau => HB_SCRIPT_PAU_CIN_HAU, 1095 | Phags_Pa => HB_SCRIPT_PHAGS_PA, 1096 | Phoenician => HB_SCRIPT_PHOENICIAN, 1097 | Psalter_Pahlavi => HB_SCRIPT_PSALTER_PAHLAVI, 1098 | Rejang => HB_SCRIPT_REJANG, 1099 | Runic => HB_SCRIPT_RUNIC, 1100 | Samaritan => HB_SCRIPT_SAMARITAN, 1101 | Saurashtra => HB_SCRIPT_SAURASHTRA, 1102 | Sharada => HB_SCRIPT_SHARADA, 1103 | Shavian => HB_SCRIPT_SHAVIAN, 1104 | Siddham => HB_SCRIPT_SIDDHAM, 1105 | SignWriting => HB_SCRIPT_SIGNWRITING, 1106 | Sinhala => HB_SCRIPT_SINHALA, 1107 | Sora_Sompeng => HB_SCRIPT_SORA_SOMPENG, 1108 | Sundanese => HB_SCRIPT_SUNDANESE, 1109 | Syloti_Nagri => HB_SCRIPT_SYLOTI_NAGRI, 1110 | Syriac => HB_SCRIPT_SYRIAC, 1111 | Tagalog => HB_SCRIPT_TAGALOG, 1112 | Tagbanwa => HB_SCRIPT_TAGBANWA, 1113 | Tai_Le => HB_SCRIPT_TAI_LE, 1114 | Tai_Tham => HB_SCRIPT_TAI_THAM, 1115 | Tai_Viet => HB_SCRIPT_TAI_VIET, 1116 | Takri => HB_SCRIPT_TAKRI, 1117 | Tamil => HB_SCRIPT_TAMIL, 1118 | Telugu => HB_SCRIPT_TELUGU, 1119 | Thaana => HB_SCRIPT_THAANA, 1120 | Thai => HB_SCRIPT_THAI, 1121 | Tibetan => HB_SCRIPT_TIBETAN, 1122 | Tifinagh => HB_SCRIPT_TIFINAGH, 1123 | Tirhuta => HB_SCRIPT_TIRHUTA, 1124 | Ugaritic => HB_SCRIPT_UGARITIC, 1125 | Unknown => HB_SCRIPT_UNKNOWN, 1126 | Vai => HB_SCRIPT_VAI, 1127 | Warang_Citi => HB_SCRIPT_WARANG_CITI, 1128 | Yi => HB_SCRIPT_YI, 1129 | } 1130 | } 1131 | } 1132 | --------------------------------------------------------------------------------