Commit 9c123886 authored by Dan Sheppard's avatar Dan Sheppard
Browse files

Basic structured reporting of position. Bugs:

  1. doesn't do any filtering of events;
  2. still doesn't actually generate custom event;
  3. still no way to configure subscriptions.
parent b8862214
......@@ -56,9 +56,10 @@ impl Position {
}
pub fn get_edge(&self, which: &Direction) -> f64 {
let bp = self.get_screen_in_bp();
match *which {
LEFT => self.pos.0 - self.screen_size.0 as f64/2.,
RIGHT => self.pos.0 + self.screen_size.0 as f64/2.,
LEFT => self.pos.0 - bp/2. + self.px_to_bp(self.min_x_bumper),
RIGHT => self.pos.0 + bp/2. - self.px_to_bp(self.max_x_bumper),
UP => self.pos.1 - self.screen_size.1 as f64/2.,
DOWN => self.pos.1 + self.screen_size.1 as f64/2.
}
......@@ -68,6 +69,10 @@ impl Position {
Dot(self.pos.0,self.pos.1)
}
fn px_to_bp(&self, px: f64) -> f64 {
px / self.screen_size.0 as f64 * self.zoom.get_screen_in_bp()
}
fn set_limit_min_zoom(&mut self) {
let max_bp = /* maximum "displayed" bp is ... */
self.max_x-self.min_x+1. /* ... available bp on stick ... */
......
......@@ -18,13 +18,12 @@ pub struct Stage {
impl Stage {
pub fn new() -> Stage {
let size = cpixel(0,0);
let mut out = Stage {
Stage {
pos: Position::new(Dot(0.,0.),size),
mouse_pos: Dot(0,0),
base: 0.,
dims: size,
};
out
}
}
pub fn update_report(&self, report: &Report) {
......
use std::sync::{ Arc, Mutex };
use stdweb::web::{ Element, HtmlElement };
use stdweb::web::HtmlElement;
use stdweb::unstable::TryInto;
use composit::{ Compositor, StateManager, Stage };
......
use std::sync::{ Arc, Mutex, Weak };
use stdweb::web::{ Element, HtmlElement };
use stdweb::web::HtmlElement;
use composit::{ register_compositor_ticks };
use controller::global::{ App, GlobalWeak };
......@@ -104,7 +104,7 @@ impl AppRunner {
/* register canvas-bound events */
{
let mut el = self.0.lock().unwrap().el.clone();
let el = self.0.lock().unwrap().el.clone();
register_user_events(self,&el);
register_direct_events(self,&el);
register_dom_events(self,&el);
......
......@@ -3,14 +3,41 @@ use std::sync::{ Arc, Mutex };
use controller::global::{ App, AppRunner };
struct Status {
value: String,
last_value: Option<String>,
use serde_json::Map as JSONMap;
use serde_json::Value as JSONValue;
use serde_json::Number as JSONNumber;
#[derive(Clone)]
pub enum StatusJigsawType {
Number,
String,
Boolean
}
#[derive(Clone)]
pub enum StatusJigsaw {
Atom(String,StatusJigsawType),
Array(Vec<StatusJigsaw>),
Object(HashMap<String,StatusJigsaw>)
}
struct StatusOutput {
last_value: Option<JSONValue>,
jigsaw: StatusJigsaw,
last_sent: Option<f64>,
send_every: Option<f64>
send_every: Option<f64>
}
impl Status {
impl StatusOutput {
fn new(jigsaw: StatusJigsaw) -> StatusOutput {
StatusOutput {
jigsaw,
last_sent: None,
send_every: Some(0.),
last_value: None,
}
}
fn is_send_now(&self, key: &str, t: f64) -> bool {
if let Some(interval) = self.send_every {
if interval == 0. { return true; }
......@@ -22,78 +49,132 @@ impl Status {
}
return false;
}
fn send_now(&mut self, key: &str, t: f64) -> bool {
if let Some(ref last_value) = self.last_value {
if *last_value == self.value { return false; }
}
if self.is_send_now(key,t) {
self.last_sent = Some(t);
self.last_value = Some(self.value.clone());
return true;
}
return false;
}
fn set_interval(&mut self, interval: Option<f64>) {
self.send_every = interval;
}
}
const REPORT_CONFIG: &[(&str,Option<f64>)] = &[
("stick",Some(500.)),
("start",Some(500.)),
("end",Some(500.)),
];
lazy_static! {
static ref REPORT_CONFIG:
Vec<(&'static str,StatusJigsaw,Option<f64>)> = vec!{
("location",StatusJigsaw::Array(vec!{
StatusJigsaw::Atom("stick".to_string(),StatusJigsawType::String),
StatusJigsaw::Atom("start".to_string(),StatusJigsawType::Number),
StatusJigsaw::Atom("end".to_string(),StatusJigsawType::Number),
}),Some(500.))
};
}
pub struct ReportImpl {
statuses: HashMap<String,Status>
pieces: HashMap<String,String>,
outputs: HashMap<String,StatusOutput>
}
impl ReportImpl {
pub fn new() -> ReportImpl {
let out = ReportImpl {
statuses: HashMap::<String,Status>::new()
pieces: HashMap::<String,String>::new(),
outputs: HashMap::<String,StatusOutput>::new()
};
out
}
fn get_entry(&mut self, key: &str) -> &mut Status {
self.statuses.entry(key.to_string()).or_insert_with(||
Status {
value: "".to_string(),
last_value: None,
last_sent: None,
send_every: Some(0.)
}
)
fn get_piece(&mut self, key: &str) -> Option<String> {
self.pieces.get(key).map(|s| s.clone())
}
fn get_output(&mut self, key: &str) -> Option<&mut StatusOutput> {
self.outputs.get_mut(key)
}
fn add_output(&mut self, key: &str, jigsaw: StatusJigsaw) {
self.outputs.insert(key.to_string(),StatusOutput::new(jigsaw));
}
pub fn set_status(&mut self, key: &str, value: &str) {
let s = self.get_entry(key);
if s.value == value { return; }
s.value = value.to_string();
self.pieces.insert(key.to_string(),value.to_string());
}
pub fn set_interval(&mut self, key: &str, interval: Option<f64>) {
let s = self.get_entry(key);
s.set_interval(interval);
if let Some(ref mut p) = self.get_output(key) {
p.send_every = interval;
}
}
fn new_report(&mut self, t: f64) -> HashMap<String,String> {
let mut out = HashMap::<String,String>::new();
for (k,s) in &mut self.statuses {
if s.send_now(k,t) {
out.insert(k.to_string(),s.value.clone());
fn make_number(&self, data: &str) -> JSONValue {
data.parse::<f64>()
.map(|v| JSONValue::Number(JSONNumber::from_f64(v).unwrap()))
.unwrap_or(JSONValue::Null)
}
fn make_bool(&self, data: &str) -> JSONValue {
data.parse::<bool>()
.map(|v| JSONValue::Bool(v))
.unwrap_or(JSONValue::Null)
}
fn make_atom(&self, key: &str, type_: &StatusJigsawType) -> JSONValue {
let v = self.pieces.get(key);
if let Some(ref v) = v {
match type_ {
StatusJigsawType::Number => self.make_number(v),
StatusJigsawType::String => JSONValue::String(v.to_string()),
StatusJigsawType::Boolean => self.make_bool(v)
}
} else {
JSONValue::Null
}
}
fn make_array(&self, values: &Vec<StatusJigsaw>) -> JSONValue {
let mut out = Vec::<JSONValue>::new();
for v in values {
out.push(self.make_value(v));
}
JSONValue::Array(out)
}
fn make_object(&self, values: &HashMap<String,StatusJigsaw>) -> JSONValue {
let mut out = JSONMap::<String,JSONValue>::new();
for (k,v) in values {
out.insert(k.to_string(),self.make_value(v));
}
JSONValue::Object(out)
}
fn make_value(&self, j: &StatusJigsaw) -> JSONValue {
match j {
StatusJigsaw::Atom(key,type_) => self.make_atom(key,type_),
StatusJigsaw::Array(values) => self.make_array(values),
StatusJigsaw::Object(values) => self.make_object(values)
}
}
fn new_report(&mut self, t: f64) -> Option<JSONValue> {
let mut out = JSONMap::<String,JSONValue>::new();
for (k,s) in &self.outputs {
let value = self.make_value(&s.jigsaw);
if let Some(ref last_value) = s.last_value {
if last_value == &value { continue; }
}
if s.is_send_now(k,t) {
out.insert(k.to_string(),value.clone());
}
}
for (k,v) in &out {
if let Some(ref mut p) = self.outputs.get_mut(k) {
p.last_value = Some(v.clone());
p.last_sent = Some(t);
}
}
if out.len() > 0 {
Some(JSONValue::Object(out))
} else {
None
}
out
}
pub fn tick(&mut self, app: &App, t: f64) {
let out = self.new_report(t);
if out.len() > 0 {
debug!("status","{:?}",out);
if let Some(out) = self.new_report(t) {
debug!("status","{}",out.to_string());
}
}
}
......@@ -104,7 +185,11 @@ pub struct Report(Arc<Mutex<ReportImpl>>);
impl Report {
pub fn new(ar: &mut AppRunner) -> Report {
let mut out = Report(Arc::new(Mutex::new(ReportImpl::new())));
for (k,v) in REPORT_CONFIG {
for (k,j,v) in REPORT_CONFIG.iter() {
{
let mut imp = out.0.lock().unwrap();
imp.add_output(k,j.clone());
}
out.set_interval(k,*v);
}
ar.add_timer(enclose! { (out) move |app,t| {
......
......@@ -11,13 +11,97 @@ use controller::input::EggDetector;
use controller::global::App;
use debug::testcard_base;
use debug::DebugConsole;
use dom::{ DEBUGSTAGE, DEBUGSTAGE_CSS, Bling };
use dom::Bling;
use dom::domutil;
use dom::event::{
EventListener, EventControl, EventType, EventData, Target
};
pub const DEBUGSTAGE : &str = r##"
<div class="bpane-container">
<div class="bpane-canv">
<h1>Debug Mode</h1>
</div>
<div class="bpane-right">
<div class="console">
<select class="testcard">
<option value="">- testcards -</option>
<option value="polar">Polar Testcard</option>
<option value="draw">Jank Testcard</option>
<option value="onoff">On/Off Testcard</option>
<option value="button">Button Testcard</option>
<option value="text">Text Testcard</option>
<option value="ruler">Ruler Testcard</option>
<option value="leaf">Leaf Testcard</option>
</select>
<select class="folder"></select>
<button class="mark">mark!</button>
<button class="start">start</button>
<pre class="content console2"></pre>
</div>
<div class="buttons"></div>
<div class="managedcanvasholder"></div>
</div>
</div>
"##;
pub const DEBUGSTAGE_CSS : &str = r##"
html, body {
margin: 0px;
padding: 0px;
height: 100%;
width: 100%;
overflow: hidden;
}
.bpane-container {
display: flex;
height: 100%;
width: 100%;
}
.bpane-container .bpane-right {
width: 20%;
}
.bpane-container .console .content {
height: 85%;
overflow: scroll;
border: 1px solid #ccc;
}
.bpane-container .managedcanvasholder {
display: block;
border: 2px solid red;
display: inline-block;
overflow: scroll;
width: 100%;
}
.bpane-container, .bpane-container .bpane-canv canvas {
height: 100%;
width: 100%;
}
.bpane-container .bpane-canv {
width: 80%;
height: 100%;
}
.bpane-container .bpane-canv canvas {
width: 100%;
height: 100%;
}
#stage {
height: 100%;
}
.bpane-container .console {
height: 50%;
}
@import url('https://fonts.googleapis.com/css?family=Lato');
"##;
fn setup_debug_console(el: &HtmlElement) {
let cons_el = domutil::query_selector(&el.clone().into(),".console2");
DebugConsole::new(&cons_el,&el.clone().into());
......
use std::sync::{ Arc, Mutex };
use stdweb::unstable::TryInto;
use stdweb::web::{ Element, HtmlElement, INode };
use stdweb::web::HtmlElement;
use controller::global::App;
use dom::{ PLAINSTAGE, PLAINSTAGE_CSS };
use dom::domutil;
pub trait Bling {
fn apply_bling(&self, el: &HtmlElement) -> HtmlElement;
fn activate(&mut self, _ar: &Arc<Mutex<App>>, _el: &HtmlElement) {}
fn key(&mut self, _app: &Arc<Mutex<App>>, _key: &str) {}
}
pub struct NoBling {}
impl NoBling {
pub fn new() -> NoBling { NoBling{} }
}
impl Bling for NoBling {
fn apply_bling(&self, el: &HtmlElement) -> HtmlElement {
let el = el.clone().into();
domutil::inner_html(&el,PLAINSTAGE);
if let Some(old) = domutil::query_selector2(&el,"#bpane-css") {
domutil::remove(&old);
}
let css = domutil::append_element(&domutil::query_select("head"),"style");
domutil::add_attr(&css,"id","bpane-css");
domutil::inner_html(&css,PLAINSTAGE_CSS);
domutil::query_selector(&el.clone().into(),".bpane-canv").clone().try_into().unwrap()
}
}
pub const DEBUGSTAGE : &str = r##"
<div class="bpane-container">
<div class="bpane-canv">
<h1>Debug Mode</h1>
</div>
<div class="bpane-right">
<div class="console">
<select class="testcard">
<option value="">- testcards -</option>
<option value="polar">Polar Testcard</option>
<option value="draw">Jank Testcard</option>
<option value="onoff">On/Off Testcard</option>
<option value="button">Button Testcard</option>
<option value="text">Text Testcard</option>
<option value="ruler">Ruler Testcard</option>
<option value="leaf">Leaf Testcard</option>
</select>
<select class="folder"></select>
<button class="mark">mark!</button>
<button class="start">start</button>
<pre class="content console2"></pre>
</div>
<div class="buttons"></div>
<div class="managedcanvasholder"></div>
</div>
</div>
"##;
pub const PLAINSTAGE : &str = r##"
<div class="bpane-container">
<div class="bpane-canv">
</div>
<div class="managedcanvasholder"></div>
</div>
"##;
pub const DEBUGSTAGE_CSS : &str = r##"
html, body {
margin: 0px;
padding: 0px;
height: 100%;
width: 100%;
overflow: hidden;
}
.bpane-container {
display: flex;
height: 100%;
width: 100%;
}
.bpane-container .bpane-right {
width: 20%;
}
.bpane-container .console .content {
height: 85%;
overflow: scroll;
border: 1px solid #ccc;
}
.bpane-container .managedcanvasholder {
display: block;
border: 2px solid red;
display: inline-block;
overflow: scroll;
width: 100%;
}
.bpane-container, .bpane-container .bpane-canv canvas {
height: 100%;
width: 100%;
}
.bpane-container .bpane-canv {
width: 80%;
height: 100%;
}
.bpane-container .bpane-canv canvas {
width: 100%;
height: 100%;
}
#stage {
height: 100%;
}
.bpane-container .console {
height: 50%;
}
@import url('https://fonts.googleapis.com/css?family=Lato');
"##;
pub const PLAINSTAGE_CSS : &str = r##"
.bpane-container {
position: relative;
}
.bpane-canv {
position: absolute;
top: 0;
left: 0;
overflow: hidden;
}
.bpane-container .managedcanvasholder {
display: none;
}
.bpane-canv canvas {
margin: 0;
display:block;
}
.bpane-container, .bpane-container .bpane-canv {
height: 100%;
width: 100%;
}
@import url('https://fonts.googleapis.com/css?family=Lato');
"##;
pub mod event; // XXX not pub, only during dev
pub mod domutil;
mod debugstage;
mod appeventdata;
mod bling;
mod nobling;
pub use self::debugstage::{ DEBUGSTAGE, DEBUGSTAGE_CSS, PLAINSTAGE, PLAINSTAGE_CSS };
pub use self::appeventdata::AppEventData;
pub use self::bling::{ Bling, NoBling };
pub use self::bling::Bling;
pub use self::nobling::NoBling;
use stdweb::unstable::TryInto;
use stdweb::web::HtmlElement;
use dom::{ domutil, Bling };
pub const PLAINSTAGE : &str = r##"
<div class="bpane-container">
<div class="bpane-canv">
</div>
<div class="managedcanvasholder"></div>
</div>
"##;
pub const PLAINSTAGE_CSS : &str = r##"
.bpane-container {
position: relative;
}