diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..67df2ae --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,244 @@ +use orbtk::prelude::*; +use rhai::RegisterFn; +use rhai::{packages::*, Engine, EvalAltResult, INT}; +use std::sync::mpsc::{Receiver, Sender}; +use std::time::Duration; +pub mod parser; +use crate::parser::Value; +use crate::parser::QML; +use parser::Value::{QmlIdent, QmlNumber, QmlString}; + +#[macro_use] +extern crate pest_derive; + +#[derive(AsAny)] +pub struct MainViewState { + pub qml: Vec<(String, QML)>, + pub rx: Option>, + pub state1: Option, + pub state2: Option, +} + +impl Widget for MainViewState { + fn create() -> Self { + unimplemented!() + } + + fn build(self, ctx: &mut BuildContext) -> Entity { + let top_level = self.qml.clone(); + let widget = ctx.create_entity(); + let this = render_ctx(widget, ctx, &top_level, 0, 0).unwrap(); + ctx.register_state(this, Box::new(self.state2.unwrap())); + ctx.register_state(widget, Box::new(self.state1.unwrap())); + this + } + + fn insert_handler(self, handler: impl Into>) -> Self { + self + } + + fn child(self, child: Entity) -> Self { + self + } +} + +#[derive(AsAny)] +pub struct MVState { + pub engine: Engine, + pub tx: Option>, + pub rx: Option>, +} + +impl MVState { + fn action(&mut self, action: String) { + match self.tx.clone() { + Some(tx) => { + self.engine + .register_fn("update_property", move |x: &str, y: &str, z: &str| { + tx.send((String::from(x), String::from(y), String::from(z))); + }); + let result = self.engine.eval::<()>(action.as_str()); + } + _ => {} + } + } +} + +impl State for MVState { + fn init(&mut self, registry: &mut Registry, ctx: &mut Context) { + self.update(registry, ctx); + } + + fn update(&mut self, registry: &mut Registry, ctx: &mut Context) { + match &self.rx { + Some(rx) => match rx.recv_timeout(Duration::from_millis(1)) { + Ok((x, y, z)) => { + ctx.child(x.as_str()) + .set::(y.as_str(), String16::from("hijack")); + } + _ => {} + }, + _ => {} + } + } +} + +impl Template for MainViewState {} + +fn parse_number(val: Option<&Value>) -> f64 { + match val { + Some(QmlNumber(num)) => num.clone(), + _ => 0.0, + } +} + +fn render_ctx( + id: Entity, + ctx: &mut BuildContext, + qml: &Vec<(String, QML)>, + row: u32, + col: u32, +) -> Option { + for (ident, child) in qml.iter() { + match ident.as_str() { + "Grid" => { + let mut grid = Grid::create(); + grid = grid.attach(Grid::row(row as usize)); + grid = grid.attach(Grid::column(col as usize)); + let rows = parse_number(child.properties.get("rows")) as u32; + let cols = parse_number(child.properties.get("cols")) as u32; + + let mut grid_rows = Rows::create(); + for i in 0..rows { + grid_rows = grid_rows.row("stretch"); + } + grid = grid.rows(grid_rows.build()); + + let mut grid_cols = Columns::create(); + for i in 0..rows { + grid_cols = grid_cols.column("stretch"); + } + grid = grid.columns(grid_cols.build()); + + let mut grow = 0u32; + let mut gcol = 0u32; + //rect = rect.attach(Grid::column(gcol as usize)); + for (i, s) in child.children.iter() { + match render_ctx(id, ctx, &vec![(i.clone(), s.clone())], grow, gcol) { + Some(entity) => { + grid = grid.child(entity); + grow += 1; + if grow as u32 == rows { + grow = 0; + gcol += 1; + } + } + _ => {} + } + } + + return Some(grid.build(ctx)); + } + "Rectangle" => { + let width = parse_number(child.properties.get("width")); + let height = parse_number(child.properties.get("height")); + + let color = match child.properties.get("color") { + Some(QmlString(col)) => match col.as_str() { + "red" => Color::rgb(0xff, 00, 00), + "blue" => Color::rgb(0x00, 00, 0xff), + _ => Color::rgb(0xff, 0xff, 0xff), + }, + _ => Color::rgb(0xff, 0xff, 0xff), + }; + let mut rect = Container::create() + // .width(width) + // .height(height) + .background(color); + + rect = rect.attach(Grid::row(row as usize)); + rect = rect.attach(Grid::column(col as usize)); + + for (i, s) in child.children.iter() { + match render_ctx(id, ctx, &vec![(i.clone(), s.clone())], 0, 0) { + Some(entity) => { + rect = rect.child(entity); + } + _ => {} + } + } + + return Some(rect.build(ctx)); + } + "Button" => { + let mut button = Button::create(); + button = button.attach(Grid::row(row as usize)); + button = button.attach(Grid::column(col as usize)); + + let code = match child.properties.get("onclick").unwrap() { + QmlString(code) => code.clone(), + _ => String::new(), + }; + + button = button.on_click(move |states, _| -> bool { + state(id, states).action(code.clone()); + return true; + }); + + let text = match child.properties.get("text").unwrap() { + QmlString(text) => text.clone(), + _ => String::new(), + }; + + match child.properties.get("anchors.centerIn") { + Some(QmlIdent(str)) => { + if str.eq("parent") { + button = button.vertical_alignment(Alignment::Center); + button = button.horizontal_alignment(Alignment::Center); + } + } + _ => {} + } + button = button.text(text); + + return Some(button.build(ctx)); + } + "Text" => { + let text = match child.properties.get("text") { + Some(QmlString(text)) => text.clone(), + _ => String::new(), + }; + + let mut tt = TextBlock::create(); + tt = tt.text(text); + tt = tt.attach(Grid::row(row as usize)); + tt = tt.attach(Grid::column(col as usize)); + + match child.properties.get("id") { + Some(QmlIdent(text)) => { + tt = tt.id(text.clone()); + } + _ => {} + }; + + match child.properties.get("anchors.centerIn") { + Some(QmlIdent(str)) => { + if str.eq("parent") { + tt = tt.vertical_alignment(Alignment::Center); + tt = tt.horizontal_alignment(Alignment::Center); + } + } + _ => {} + } + let entity = tt.build(ctx); + return Some(entity); + } + _ => println!("unknown ident {}", ident), + } + } + None +} + +fn state<'a>(id: Entity, states: &'a mut StatesContext) -> &'a mut MVState { + states.get_mut(id) +} diff --git a/src/main.rs b/src/main.rs index 45685c9..15cf20c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,380 +1,51 @@ use orbtk::prelude::*; -use rhai::{packages::*, Engine, EvalAltResult, INT}; use rhai::RegisterFn; +use rhai::{packages::*, Engine, EvalAltResult, INT}; -extern crate pest; -#[macro_use] -extern crate pest_derive; - -use crate::Value::{QmlIdent, QmlNumber, QmlString}; -use pest::iterators::{Pair, Pairs}; -use pest::Parser; +use rqml::parser::parse_qml; +use rqml::{MVState, MainViewState}; use std::borrow::{Borrow, BorrowMut}; use std::fs::read_to_string; -use std::sync::mpsc::{Sender, Receiver}; use std::sync::mpsc; +use std::sync::mpsc::{Receiver, Sender}; use std::thread; -use crate::Action::Code; use std::time::Duration; -#[derive(Parser)] -#[grammar = "../pest/qml.pest"] -struct QmlParser; - -#[derive(Debug, Clone)] -enum Value { - QmlString(String), - QmlNumber(f64), - QmlIdent(String), -} - -#[derive(Debug, Clone)] -struct QML { - properties: HashMap, - children: Vec<(String, QML)>, -} - -fn parse_qml(qml: Pairs) -> QML { - let mut qmldoc = QML { - properties: Default::default(), - children: vec![], - }; - for pair in qml { - // println!("Rule {:?} Str {}", pair.as_rule(), pair.as_str()); - - match pair.as_rule() { - Rule::import => { - let mut tokens = pair.into_inner(); - println!("Found new Import {} ", tokens.next().unwrap().as_str()); - } - Rule::body => { - let mut tokens = pair.into_inner(); - let ident = tokens.next().unwrap().as_str(); - println!("Found new Body: {} ", ident); - qmldoc - .children - .push((String::from(ident), parse_qml(tokens.into_iter()))); - } - Rule::property => { - let mut tokens = pair.into_inner(); - let ident = tokens.next().unwrap().as_str(); - let value = tokens.next().unwrap(); - // let value = tokens.next().unwrap().as_str(); - match value.as_rule() { - Rule::number => { - let num: f64 = value.as_str().parse().unwrap(); - println!("Found new Property: {} {:?}", ident, num); - qmldoc - .properties - .insert(String::from(ident), QmlNumber(num)); - } - Rule::string => { - println!("Found new Property: {} {}", ident, value.as_str()); - qmldoc.properties.insert( - String::from(ident), - QmlString(String::from(value.into_inner().next().unwrap().as_str())), - ); - } - Rule::ident => { - println!("Found new Property: {} {}", ident, value.as_str()); - qmldoc - .properties - .insert(String::from(ident), QmlIdent(String::from(value.as_str()))); - } - _ => {} - } - } - Rule::function => { - let mut tokens = pair.into_inner(); - let ident = tokens.next().unwrap().as_str(); - let value = tokens.concat(); - qmldoc.properties.insert( - String::from(ident), - QmlString(String::from(value.clone().trim())), - ); - } - _ => return parse_qml(pair.into_inner()), - } - } - qmldoc -} - fn main() { - Application::new() .window(|ctx| { - let mut app_context:HashMap = HashMap::new(); - let qml = read_to_string("./res/example.qml").unwrap(); - let qml_tokens = QmlParser::parse(Rule::qml, qml.as_str()).unwrap_or_else(|e| panic!("{}", e)); - let qml_doc = parse_qml(qml_tokens); + let mut app_context: HashMap = HashMap::new(); + let qml_doc = parse_qml("./res/example.qml"); let top_level = qml_doc.children.clone(); - - let width = match top_level[0].1.properties.get("width") { - Some(QmlNumber(num)) => num.clone(), - _ => 600.0, - }; - let height = match top_level[0].1.properties.get("height") { - Some(QmlNumber(num)) => num.clone(), - _ => 600.0, - }; println!("{:?}", qml_doc); - let (tx, rx): (Sender<(String,String,String)>, Receiver<(String,String,String)>) = mpsc::channel(); + let (tx, rx): ( + Sender<(String, String, String)>, + Receiver<(String, String, String)>, + ) = mpsc::channel(); let w = Window::create() .title("QML") .position((100.0, 100.0)) .resizeable(true) - .size(width, height) - .child(MainViewState{ action: None, qml: top_level.clone(), rx: None, state1: Some(MVState{tx:Some(tx.clone()),rx:None}),state2: Some(MVState{rx:Some(rx),tx:None})}.build(ctx)) + .size(600.0, 600.0) + .child( + MainViewState { + qml: top_level.clone(), + rx: None, + state1: Some(MVState { + tx: Some(tx.clone()), + rx: None, + engine: Engine::new(), + }), + state2: Some(MVState { + rx: Some(rx), + tx: None, + engine: Engine::new(), + }), + } + .build(ctx), + ) .build(ctx); w }) .run(); } - -#[derive(Copy, Clone, PartialEq)] -enum Action { - Code, -} - - -#[derive(AsAny)] -pub struct MainViewState { - action: Option, - qml: Vec<(String, QML)>, - rx: Option>, - state1: Option, - state2: Option -} - -impl Widget for MainViewState { - fn create() -> Self { - unimplemented!() - } - - fn build(self, ctx: &mut BuildContext) -> Entity { - - let top_level = self.qml.clone(); - let widget = ctx.create_entity(); - let this = render_ctx(widget, ctx, &top_level, 0, 0).unwrap(); - ctx.register_state(this, Box::new(self.state2.unwrap())); - ctx.register_state(widget, Box::new(self.state1.unwrap())); - this - } - - fn insert_handler(self, handler: impl Into>) -> Self { - self - } - - fn child(self, child: Entity) -> Self { - self - } -} - -#[derive(AsAny)] -struct MVState { - tx: Option>, - rx: Option> -} - -impl MVState { - fn action(&mut self, action: String) { - match self.tx.clone() { - Some(tx) => { - let mut engine = Engine::new(); - engine.register_fn("update_property", move |x: &str,y: &str,z: &str| { - tx.send((String::from(x), String::from(y), String::from(z))); - }); - let result = engine.eval::<()>(action.as_str()); - } - _ => {} - } - } -} - - -impl State for MVState { - fn init(&mut self, registry: &mut Registry, ctx: &mut Context) { - self.update(registry, ctx); - } - - fn update(&mut self, registry: &mut Registry, ctx: &mut Context) { - - match &self.rx { - Some(rx)=> { - match rx.recv_timeout(Duration::from_millis(1)) { - Ok((x,y,z)) => { - - ctx.child(x.as_str()).set::(y.as_str(),String16::from("hijack")); - }, - _ => {} - } - }, - _ => {} - } - } -} - -impl Template for MainViewState { - -} - -fn parse_number(val: Option<&Value>) -> f64 { - match val { - Some(QmlNumber(num)) => num.clone(), - _ => 0.0, - } -} - -fn render_ctx( - id: Entity, - ctx: &mut BuildContext, - qml: &Vec<(String, QML)>, - row: u32, - col: u32 -) -> Option { - for (ident, child) in qml.iter() { - match ident.as_str() { - "Grid" => { - let mut grid = Grid::create(); - grid = grid.attach(Grid::row(row as usize)); - grid = grid.attach(Grid::column(col as usize)); - let rows = parse_number(child.properties.get("rows")) as u32; - let cols = parse_number(child.properties.get("cols")) as u32; - - let mut grid_rows = Rows::create(); - for i in 0..rows { - grid_rows = grid_rows.row("stretch"); - } - grid = grid.rows(grid_rows.build()); - - let mut grid_cols = Columns::create(); - for i in 0..rows { - grid_cols = grid_cols.column("stretch"); - } - grid = grid.columns(grid_cols.build()); - - let mut grow = 0u32; - let mut gcol = 0u32; - //rect = rect.attach(Grid::column(gcol as usize)); - for (i, s) in child.children.iter() { - match render_ctx(id, ctx, &vec![(i.clone(), s.clone())], grow, gcol) { - Some(entity) => { - grid = grid.child(entity); - grow += 1; - if grow as u32 == rows { - grow = 0; - gcol += 1; - } - } - _ => {} - } - } - - return Some(grid.build(ctx)); - } - "Rectangle" => { - let width = parse_number(child.properties.get("width")); - let height = parse_number(child.properties.get("height")); - - let color = match child.properties.get("color") { - Some(QmlString(col)) => match col.as_str() { - "red" => Color::rgb(0xff, 00, 00), - "blue" => Color::rgb(0x00, 00, 0xff), - _ => Color::rgb(0xff, 0xff, 0xff), - }, - _ => Color::rgb(0xff, 0xff, 0xff), - }; - let mut rect = Container::create() - // .width(width) - // .height(height) - .background(color); - - rect = rect.attach(Grid::row(row as usize)); - rect = rect.attach(Grid::column(col as usize)); - - - for (i, s) in child.children.iter() { - match render_ctx(id, ctx, &vec![(i.clone(), s.clone())], 0, 0) { - Some(entity) => { - rect = rect.child(entity); - } - _ => {} - } - } - - return Some(rect.build(ctx)); - } - "Button" => { - let mut button = Button::create(); - button = button.attach(Grid::row(row as usize)); - button = button.attach(Grid::column(col as usize)); - - let code = match child.properties.get("onclick").unwrap() { - QmlString(code) => code.clone(), - _ => String::new(), - }; - - button = button.on_click(move |states, _| -> bool { - state(id, states).action(code.clone()); - return true; - }); - - let text = match child.properties.get("text").unwrap() { - QmlString(text) => text.clone(), - _ => String::new(), - }; - - match child.properties.get("anchors.centerIn") { - Some(QmlIdent(str)) => { - if str.eq("parent") { - button = button.vertical_alignment(Alignment::Center); - button = button.horizontal_alignment(Alignment::Center); - } - } - _ => {} - } - button = button.text(text); - - return Some(button.build(ctx)); - } - "Text" => { - let text = match child.properties.get("text") { - Some(QmlString(text)) => text.clone(), - _ => String::new(), - }; - - - let mut tt = TextBlock::create(); - tt = tt.text(text); - tt = tt.attach(Grid::row(row as usize)); - tt = tt.attach(Grid::column(col as usize)); - - match child.properties.get("id") { - Some(QmlIdent(text)) => { - tt = tt.id(text.clone()); - }, - _ => { } - }; - - match child.properties.get("anchors.centerIn") { - Some(QmlIdent(str)) => { - if str.eq("parent") { - tt = tt.vertical_alignment(Alignment::Center); - tt = tt.horizontal_alignment(Alignment::Center); - } - } - _ => {} - } - let entity = tt.build(ctx); - return Some(entity); - } - _ => println!("unknown ident {}", ident), - } - } - None -} - - -fn state<'a>(id: Entity, states: &'a mut StatesContext) -> &'a mut MVState { - states.get_mut(id) -} \ No newline at end of file diff --git a/src/parser.rs b/src/parser.rs new file mode 100644 index 0000000..671377b --- /dev/null +++ b/src/parser.rs @@ -0,0 +1,96 @@ +extern crate pest; + +use crate::Value::{QmlIdent, QmlNumber, QmlString}; +use pest::iterators::{Pair, Pairs}; +use pest::Parser; +use std::collections::HashMap; +use std::fs::read_to_string; + +#[derive(Parser)] +#[grammar = "../pest/qml.pest"] +struct QmlParser; + +#[derive(Debug, Clone)] +pub enum Value { + QmlString(String), + QmlNumber(f64), + QmlIdent(String), +} + +#[derive(Debug, Clone)] +pub struct QML { + pub properties: HashMap, + pub children: Vec<(String, QML)>, +} + +pub fn parse_qml(path: &str) -> QML { + let qml_file = read_to_string(path).unwrap(); + let qml_tokens = + QmlParser::parse(Rule::qml, qml_file.as_str()).unwrap_or_else(|e| panic!("{}", e)); + parse(qml_tokens) +} + +fn parse(qml: Pairs) -> QML { + let mut qmldoc = QML { + properties: Default::default(), + children: vec![], + }; + for pair in qml { + // println!("Rule {:?} Str {}", pair.as_rule(), pair.as_str()); + + match pair.as_rule() { + Rule::import => { + let mut tokens = pair.into_inner(); + println!("Found new Import {} ", tokens.next().unwrap().as_str()); + } + Rule::body => { + let mut tokens = pair.into_inner(); + let ident = tokens.next().unwrap().as_str(); + println!("Found new Body: {} ", ident); + qmldoc + .children + .push((String::from(ident), parse(tokens.into_iter()))); + } + Rule::property => { + let mut tokens = pair.into_inner(); + let ident = tokens.next().unwrap().as_str(); + let value = tokens.next().unwrap(); + // let value = tokens.next().unwrap().as_str(); + match value.as_rule() { + Rule::number => { + let num: f64 = value.as_str().parse().unwrap(); + println!("Found new Property: {} {:?}", ident, num); + qmldoc + .properties + .insert(String::from(ident), QmlNumber(num)); + } + Rule::string => { + println!("Found new Property: {} {}", ident, value.as_str()); + qmldoc.properties.insert( + String::from(ident), + QmlString(String::from(value.into_inner().next().unwrap().as_str())), + ); + } + Rule::ident => { + println!("Found new Property: {} {}", ident, value.as_str()); + qmldoc + .properties + .insert(String::from(ident), QmlIdent(String::from(value.as_str()))); + } + _ => {} + } + } + Rule::function => { + let mut tokens = pair.into_inner(); + let ident = tokens.next().unwrap().as_str(); + let value = tokens.concat(); + qmldoc.properties.insert( + String::from(ident), + QmlString(String::from(value.clone().trim())), + ); + } + _ => return parse(pair.into_inner()), + } + } + qmldoc +}