#![warn(clippy::all)]
#![warn(missing_docs)]
#![warn(rustdoc::missing_doc_code_examples)]
#![warn(clippy::missing_docs_in_private_items)]
#![doc = include_str!("../README.md")]
use std::fmt::Formatter;
use std::{cell::RefCell, collections::HashMap, fmt};
use wry::{
application::{
event::{Event, StartCause, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::{Window, WindowBuilder},
},
webview::{WebView, WebViewAttributes, WebViewBuilder},
};
pub mod cookbook;
mod html_chunks;
use html_chunks::{add_dropdown, add_number, add_slider, beginning, end, middle};
#[derive(Clone)]
pub enum Input {
Number {
label: Option<String>,
initial_value: f64,
},
Slider {
label: Option<String>,
min: f64,
max: f64,
step: f64,
initial_value: f64,
},
Dropdown {
label: Option<String>,
options: Vec<f64>,
initial_value: usize,
},
}
impl Input {
fn get_html(&self, idx: usize) -> String {
match self {
Input::Number {
initial_value,
label,
} => add_number(idx, initial_value, label),
Input::Slider {
min,
max,
step,
initial_value,
label,
} => add_slider(idx, initial_value, max, min, step, label),
Input::Dropdown {
initial_value,
options,
label,
} => add_dropdown(idx, initial_value, options, label),
_ => "".to_string(),
}
}
}
impl Default for Input {
fn default() -> Self {
Input::Number {
label: None,
initial_value: 0.0,
}
}
}
pub enum Output {
Number {
label: Option<String>,
},
}
impl Default for Output {
fn default() -> Self {
Self::Number {
label: Some("Result".to_string()),
}
}
}
impl Output {
fn get_html(&self) -> String {
let label = match self {
Output::Number { label } => match label {
None => {
format!("Result")
}
Some(string) => string.to_string(),
},
};
format!("<label for=\"output\" class=\"col-form-label mt-3\"><i>{label}</i></label>
<input type=\"text\" class=\"form-control\" id=\"output\" name=\"output\" aria-describedby=\"output\" readonly>")
}
}
pub struct Teaser {
title: String,
description: String,
inputs: Vec<Input>,
output: Output,
function: Box<dyn 'static + Fn(Vec<f64>) -> f64>,
}
impl Default for Teaser {
fn default() -> Self {
Self {
title: "Demo".to_string(),
description: "".to_string(),
inputs: vec![Input::default()],
output: Output::default(),
function: Box::new(|x| x[0]),
}
}
}
impl Teaser {
pub fn with_title(mut self, title: String) -> Self {
self.title = title;
self
}
pub fn with_description(mut self, description: String) -> Self {
self.description = description;
self
}
pub fn with_inputs(mut self, inputs: Vec<Input>) -> Self {
self.inputs = inputs;
self
}
pub fn with_output(mut self, output: Output) -> Self {
self.output = output;
self
}
pub fn with_function<F>(mut self, predictor: F) -> Self
where
F: 'static + Fn(Vec<f64>) -> f64,
{
self.function = Box::new(predictor);
self
}
pub fn run(self) {
thread_local! {
static WEBVIEW: RefCell<HashMap<usize, WebView>> = RefCell::new(HashMap::new());
}
let mut html = beginning(self.description);
for (idx, input) in self.inputs.iter().enumerate() {
html = format!("{} {}", html, input.get_html(idx));
}
html = format!("{} {} {} {}", html, middle(), self.output.get_html(), end());
let event_loop = EventLoop::new();
let window = WindowBuilder::new()
.with_title(self.title)
.build(&event_loop)
.unwrap();
let mut webview_settings = WebViewAttributes::default();
webview_settings.devtools = true;
let mut webview_builder = WebViewBuilder::new(window).unwrap();
webview_builder.webview = webview_settings;
let _webview = webview_builder
.with_html(html)
.unwrap()
.with_ipc_handler(move |_window: &Window, req: String| {
let number_strings = req.split(",");
let mut inputs = vec![0.0; 0];
for number in number_strings {
inputs.push(number.parse().unwrap());
}
let y = (*self.function)(inputs);
WEBVIEW
.with(|webview| {
let webview = webview.borrow();
let my_webview = webview.get(&0).unwrap();
my_webview.evaluate_script(&*format!(
"document.getElementById('output').value = {}",
y
))
})
.expect("TODO: panic message");
})
.build()
.unwrap();
WEBVIEW.with(|wv| {
let mut hash = HashMap::new();
hash.insert(0_usize, _webview);
wv.replace(hash);
});
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
match event {
Event::NewEvents(StartCause::Init) => println!("Wry application started!"),
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => *control_flow = ControlFlow::ExitWithCode(0),
_ => {}
}
});
}
}