use web_view::*;
pub mod utils;
pub mod widgets;
use utils::event::{Event, Key};
use utils::style::{inline_script, inline_style, scss_to_css};
use utils::theme::Theme;
use widgets::menubar::MenuBar;
use widgets::widget::Widget;
use json;
pub struct App;
impl App {
pub fn run(mut window: Window) {
let title = &window.title.to_owned();
let width = window.width;
let height = window.height;
let resizable = window.resizable;
let debug = window.debug;
let context = if debug {
""
} else {
r#"(function() { event.preventDefault(); } )()"#
};
let timer = match window.timer {
None => "".to_string(),
Some(period) => {
format!(r#"<script>{}</script>"#, Event::tick_js(period))
}
};
let html = format!(
r#"
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
{styles}
</head>
<body onkeydown="{key}" onmousedown="{click}" oncontextmenu="{context}">
<div id="app"></div>
{scripts}
{timer}
</body>
</html>
"#,
styles = format!(
"{}\n{}\n{}\n",
inline_style(include_str!(concat!(
env!("OUT_DIR"),
"/app.css"
))),
inline_style(&window.theme.css()),
inline_style(&window.style),
),
scripts = format!(
"{}\n{}\n",
inline_script(include_str!("www/app/morphdom.min.js")),
inline_script(include_str!("www/app/app.js"))
),
key = Event::key_js(),
click = Event::undefined_js(),
context = context,
timer = timer,
);
let webview = web_view::builder()
.title(title)
.content(Content::Html(html))
.size(width, height)
.resizable(resizable)
.user_data("")
.debug(debug)
.invoke_handler(|webview, arg| {
let event: Event = match json::parse(arg) {
Ok(value) => match value["type"].as_str().unwrap() {
"Update" => Event::Update,
"Tick" => Event::Tick,
"Key" => match Key::new(value["key"].as_str().unwrap())
{
Some(key) => Event::Key { key },
None => Event::Undefined,
},
"Change" => Event::Change {
source: value["source"]
.as_str()
.unwrap()
.to_string(),
value: value["value"].as_str().unwrap().to_string(),
},
_ => Event::Undefined,
},
Err(_) => Event::Undefined,
};
window.trigger(&event);
match event {
Event::Undefined => (),
_ => window.trigger(&Event::Update),
};
window.render(webview)
})
.build()
.unwrap();
webview.run().unwrap();
std::process::exit(0);
}
}
pub trait WindowListener {
fn on_key(&self, _key: Key);
fn on_tick(&self);
}
pub struct Window {
title: String,
width: i32,
height: i32,
resizable: bool,
debug: bool,
theme: Theme,
style: String,
child: Option<Box<dyn Widget>>,
menubar: Option<MenuBar>,
listener: Option<Box<dyn WindowListener>>,
timer: Option<u32>,
}
impl Window {
pub fn new() -> Self {
Self {
title: "Untitled".to_string(),
width: 640,
height: 480,
resizable: false,
debug: false,
theme: Theme::Default,
style: "".to_string(),
child: None,
menubar: None,
listener: None,
timer: None,
}
}
pub fn set_child(&mut self, widget: Box<dyn Widget>) {
self.child = Some(widget);
}
pub fn set_menubar(&mut self, menubar: MenuBar) {
self.menubar = Some(menubar);
}
pub fn set_title(&mut self, title: &str) {
self.title = title.to_string();
}
pub fn set_size(&mut self, width: i32, height: i32) {
self.width = width;
self.height = height;
}
pub fn set_resizable(&mut self) {
self.resizable = true;
}
pub fn set_debug(&mut self) {
self.debug = true;
}
pub fn set_theme(&mut self, theme: Theme) {
self.theme = theme;
}
pub fn set_style(&mut self, style: &str) {
self.style = scss_to_css(style);
}
pub fn set_listener(&mut self, listener: Box<dyn WindowListener>) {
self.listener = Some(listener);
}
pub fn set_timer(&mut self, period: u32) {
self.timer = Some(period);
}
fn render(&self, webview: &mut WebView<&str>) -> WVResult {
let rendered = format!(
r#"render("<div id=\"app\">{}</div>")"#,
self.eval().replace(r#"""#, r#"\""#)
);
webview.eval(&rendered)
}
fn eval(&self) -> String {
match (&self.menubar, &self.child) {
(Some(menubar), Some(child)) => {
format!("{}{}", menubar.eval(), child.eval())
}
(None, Some(child)) => child.eval().to_string(),
(Some(menubar), None) => menubar.eval().to_string(),
(None, None) => "".to_string(),
}
}
fn trigger(&mut self, event: &Event) {
match event {
Event::Change { .. } | Event::Update | Event::Undefined => {
match (&mut self.menubar, &mut self.child) {
(Some(menubar), Some(child)) => {
menubar.trigger(event);
child.trigger(event);
}
(None, Some(child)) => child.trigger(event),
(Some(menubar), None) => menubar.trigger(event),
(None, None) => (),
};
}
Event::Key { key } => {
match &self.listener {
None => (),
Some(listener) => {
listener.on_key(*key);
}
};
match (&mut self.menubar, &mut self.child) {
(Some(menubar), Some(child)) => {
menubar.trigger(event);
child.trigger(event);
}
(None, Some(child)) => child.trigger(event),
(Some(menubar), None) => menubar.trigger(event),
(None, None) => (),
};
}
Event::Tick => {
match &self.listener {
None => (),
Some(listener) => {
listener.on_tick();
}
};
}
}
}
}