#[macro_use]
extern crate sciter;
use sciter::dom::event::{MethodParams, DRAW_EVENTS, EVENT_GROUPS};
use sciter::dom::{Element, HELEMENT};
use sciter::graphics::{self, rgb, Graphics, HGFX};
use sciter::types::RECT;
use sciter::Value;
type Time = [u8; 3usize];
#[derive(Default)]
struct Clock {
element: Option<Element>,
now: Time,
gmt: i8,
is_frozen: bool,
}
impl sciter::EventHandler for Clock {
fn get_subscription(&mut self) -> Option<EVENT_GROUPS> {
Some(EVENT_GROUPS::HANDLE_TIMER
| EVENT_GROUPS::HANDLE_DRAW
| EVENT_GROUPS::HANDLE_METHOD_CALL
)
}
fn attached(&mut self, root: HELEMENT) {
self.element = Some(Element::from(root));
let me = self.element.as_ref().unwrap();
if let Some(attr) = me.get_attribute("utc") {
if let Ok(v) = attr.parse::<i8>() {
self.gmt = v;
}
}
if let Some(_attr) = me.get_attribute("frozen") {
self.is_frozen = true;
}
if !self.is_frozen {
me.start_timer(300, 1).expect("Can't set timer");
}
}
fn on_method_call(&mut self, _root: HELEMENT, params: MethodParams) -> bool {
match params {
MethodParams::GetValue(retval) => {
let v: Value = self.now.iter().map(|v| i32::from(*v)).collect();
println!("return current time as {:?}", v);
*retval = v;
}
MethodParams::SetValue(v) => {
println!("set current time from {:?}", v);
if v.is_string() {
let s = v.as_string().unwrap();
let t = s.split(':').take(3).map(|n| n.parse::<u8>());
let mut new_time = Time::default();
for (i, n) in t.enumerate() {
if let Err(_) = n {
eprintln!("clock::set_value({:?}) is invalid", v);
return true; }
new_time[i] = n.unwrap();
}
self.set_time(new_time);
} else if v.is_varray() {
let mut new_time = Time::default();
for (i, n) in v.values().take(3).map(|n| n.to_int()).enumerate() {
if n.is_none() {
eprintln!("clock::set_value({:?}) is invalid", v);
return true;
}
new_time[i] = n.unwrap() as u8
}
self.set_time(new_time);
} else {
eprintln!("clock::set_value({:?}) is unsupported", v);
return true;
}
}
_ => {
return false;
}
}
return true;
}
fn on_timer(&mut self, root: HELEMENT, _timer_id: u64) -> bool {
if self.update_time() {
Element::from(root).refresh().expect("Can't refresh element");
}
true
}
fn on_draw(&mut self, _root: HELEMENT, gfx: HGFX, area: &RECT, layer: DRAW_EVENTS) -> bool {
if layer == DRAW_EVENTS::DRAW_CONTENT {
let mut gfx = Graphics::from(gfx);
self
.draw_clock(&mut gfx, &area)
.map_err(|e| println!("error in draw_clock: {:?}", e) )
.ok();
}
return false;
}
}
const PI2: f32 = 2.0 * std::f32::consts::PI;
impl Clock {
fn update_time(&mut self) -> bool {
if self.is_frozen {
return false;
}
if let Some(now) = self.get_time() {
let update = self.now != now;
self.now = now;
update
} else {
false
}
}
fn set_time(&mut self, new_time: Time) {
self.now = new_time;
if let Some(el) = self.element.as_ref() {
el.refresh().ok();
}
}
fn get_time(&self) -> Option<Time> {
let el = self.element.as_ref().unwrap();
let script_func = if self.is_frozen { "getLocalTime" } else { "getUtcTime" };
if let Ok(time) = el.call_function(script_func, &make_args!(self.gmt as i32)) {
assert_eq!(time.len(), 3);
let mut now = Time::default();
for (i, n) in time.values().take(3).map(|n| n.to_int()).enumerate() {
now[i] = n.unwrap() as u8;
}
Some(now)
} else {
eprintln!("error: can't eval get time script");
None
}
}
fn draw_clock(&mut self, gfx: &mut Graphics, area: &RECT) -> graphics::Result<()> {
let mut gfx = gfx.save_state()?;
let left = area.left as f32;
let top = area.top as f32;
let width = area.width() as f32;
let height = area.height() as f32;
let scale = if width < height { width / 300.0 } else { height / 300.0 };
gfx
.translate((left + width / 2.0, top + height / 2.0))?
.scale((scale, scale))?
.rotate(-PI2 / 4.)?;
gfx.line_color(0)?.line_cap(graphics::LINE_CAP::ROUND)?;
self.draw_outline(&mut *gfx)?;
self.draw_time(&mut *gfx)?;
Ok(())
}
fn draw_outline(&mut self, gfx: &mut Graphics) -> graphics::Result<()> {
{
let mut gfx = gfx.save_state()?;
gfx.line_width(8.0)?.line_color(rgb(0x32, 0x5F, 0xA2))?;
for _ in 0..12 {
gfx.rotate(PI2 / 12.)?.line((137., 0.), (144., 0.))?;
}
}
{
let mut gfx = gfx.save_state()?;
gfx.line_width(3.0)?.line_color(rgb(0xA5, 0x2A, 0x2A))?;
for i in 0..60 {
if i % 5 != 0 {
gfx.line((143., 0.), (146., 0.))?;
}
gfx.rotate(PI2 / 60.)?;
}
}
Ok(())
}
fn draw_time(&mut self, gfx: &mut Graphics) -> graphics::Result<()> {
let time = &self.now;
let hours = f32::from(time[0]);
let minutes = f32::from(time[1]);
let seconds = f32::from(time[2]);
{
let mut gfx = gfx.save_state()?;
gfx.rotate(hours * (PI2 / 12 as f32) + minutes * (PI2 / (12 * 60) as f32) + seconds * (PI2 / (12 * 60 * 60) as f32))?;
gfx
.line_width(14.0)?
.line_color(rgb(0x32, 0x5F, 0xA2))?
.line((-20., 0.), (70., 0.))?;
}
{
let mut gfx = gfx.save_state()?;
gfx.rotate(minutes * (PI2 / 60 as f32) + seconds * (PI2 / (60 * 60) as f32))?;
gfx
.line_width(10.0)?
.line_color(rgb(0x32, 0x5F, 0xA2))?
.line((-28., 0.), (100., 0.))?;
}
{
let mut gfx = gfx.save_state()?;
gfx.rotate(seconds * (PI2 / 60 as f32))?;
gfx
.line_width(6.0)?
.line_color(rgb(0xD4, 0, 0))?
.fill_color(rgb(0xD4, 0, 0))?
.line((-30., 0.), (83., 0.))?
.circle((0., 0.), 10.)?;
}
Ok(())
}
}
#[derive(Default)]
struct Text;
impl sciter::EventHandler for Text {
fn get_subscription(&mut self) -> Option<EVENT_GROUPS> {
Some(EVENT_GROUPS::HANDLE_DRAW)
}
fn attached(&mut self, _root: HELEMENT) {
}
fn on_draw(&mut self, _root: HELEMENT, gfx: HGFX, area: &RECT, layer: DRAW_EVENTS) -> bool {
if layer == DRAW_EVENTS::DRAW_CONTENT {
let mut gfx = Graphics::from(gfx);
let e = Element::from(_root);
self
.draw_text(&e, &mut gfx, &area)
.map_err(|e| println!("error in draw_clock: {:?}", e) )
.ok();
return true;
}
return false;
}
}
impl Text {
fn draw_text(&mut self, e: &Element, gfx: &mut Graphics, area: &RECT) -> graphics::Result<()> {
let mut gfx = gfx.save_state()?;
use sciter::graphics::Text;
let t = Text::with_style(&e, "native text", "font-style: italic")?;
gfx.draw_text(&t, (area.left as f32, area.top as f32), 7)?;
Ok(())
}
}
fn main() {
let mut frame = sciter::WindowBuilder::main_window().with_size((800, 600)).create();
frame.register_behavior("native-clock", || Box::new(Clock::default()));
frame.register_behavior("native-text", || Box::new(Text::default()));
frame.load_html(include_bytes!("clock.htm"), Some("example://clock.htm"));
frame.run_app();
}