/* #[macro_use] extern crate serde_derive; #[macro_use] extern crate lazy_static; */ use chrono::{Datelike, Duration, Local, NaiveTime}; use geo_types::{Latitude, Longitude}; use glib::{Object, Sender}; use gtk::{prelude::*, subclass::prelude::*, Orientation}; use ifc::IFC; use std::{ cell::RefCell, env, f64::consts::PI, ops::Deref, rc::Rc, sync::{Arc, RwLock}, }; mod drawing; use drawing::{Color, PieChart, Wedge}; /* use geo_types::{Latitude, Longitude}; */ mod soluna_client; use soluna_client::{SolunaClient, SunMoon}; /* mod solstices; use solstices::EVENTS; */ /* const EO_TEXT: &'static str = " day = {$day -> *[Sunday] Dimanĉo [Monday] Lundo [Tuesday] Mardo [Wednesday] Merkredo [Thursday] Ĵaŭdo [Friday] Vendredo [Saturday] Sabato [LeapDay] Leap Day [YearDay] Year Day } month = {$month -> *[January] Januaro [February] Februaro [March] Marto [April] Aprilo [May] Mayo [June] Junio [Sol] Solo [July] Julio [August] Aŭgusto [September] Septembro [October] Oktobro [November] Novembro [December] Decembro } spring_equinox = Printempa Ekvinokso summer_solstice = Somera Solstico autumn_equinox = Aŭtuna Ekvinokso winter_solstice = Vintra Solstico "; fn date(fluent: Arc, today: ifc::IFC) -> impl Render { let mut day_args = FluentArgs::new(); day_args.set( "day", FluentValue::String(Cow::Owned(String::from(today.weekday_ifc()))), ); let tago = fluent.tr("tago", Some(&day_args)).unwrap(); let mut month_args = FluentArgs::new(); month_args.set( "month", FluentValue::String(Cow::Owned(String::from(today.month_ifc()))), ); let monato = fluent.tr("month", Some(&month_args)).unwrap(); owned_html! {p : format!("{}, {} {}, {}", tago, monato, today.day(), today.year());} } */ /* fn soluna_desegno(sun_moon: SunMoon) -> impl Render { owned_html! { table { tr { th : "Sunleviĝo"; th : "Sunfalo"; th : "Lunleviĝo"; th : "Lunfalo"; } tr { td : format!("{}", sun_moon.sunrise.format("%H:%M")); td : format!("{}", sun_moon.sunset.format("%H:%M")); td : sun_moon.moonrise.map(|v| format!("{}", v.format("%H:%M"))).unwrap_or("".to_string()); td : sun_moon.moonset.map(|v| format!("{}", v.format("%H:%M"))).unwrap_or("".to_string()); td : format!("{:?}", sun_moon.moon_phase); } } } } */ /* fn astronomia_eventa_desegno( fluent: Arc, event: Option, ) -> impl Render { let s = match event { None => "".to_owned(), Some(event) => { let eventa_str = match event { Event::SpringEquinox(_) => fluent.tr("spring_equinox", None), Event::SummerSolstice(_) => fluent.tr("summer_solstice", None), Event::AutumnEquinox(_) => fluent.tr("autumn_equinox", None), Event::WinterSolstice(_) => fluent.tr("winter_solstice", None), } .unwrap(); format!("{} {}", eventa_str, event.date().format("%Y-%m-%d")) } }; owned_html! { div : s.clone(); } } */ /* fn page_template( fluent: Arc, today: ifc::IFC, sun_moon: SunMoon, event: Option, ) -> String { format!( "{}", html! { : doctype::HTML; html { head { title: "Superrigardo"; } body { h1(id="heading", class="title") : date(fluent.clone(), today); div : soluna_desegno(sun_moon.clone()); div : astronomia_eventa_desegno(fluent.clone(), event); // div : cirklo(); } } } ) } async fn page( fluent: Arc, solar_client: Arc, latitude: Latitude, longitude: Longitude, ) -> Result, Rejection> { let now = Utc::now(); let d = ifc::IFC::from(now.with_timezone(&New_York).date()); let sun_moon = solar_client .request(latitude, longitude, now.with_timezone(&New_York).date()) .await; let next_event = EVENTS.next_event(now); Ok(warp::reply::html(page_template( fluent, d, sun_moon, next_event, ))) } */ #[derive(Clone, Debug)] pub enum Message { Refresh(State), } #[derive(Clone, Debug)] pub struct State { date: IFC, transit: Option, } #[derive(Clone)] pub struct Core { tx: Arc>>>, } pub struct DatePrivate { date: Rc>, label: Rc>, } impl Default for DatePrivate { fn default() -> Self { Self { date: Rc::new(RefCell::new(IFC::from( chrono::Local::now().date_naive().with_year(12023).unwrap(), ))), label: Rc::new(RefCell::new(gtk::Label::new(None))), } } } #[glib::object_subclass] impl ObjectSubclass for DatePrivate { const NAME: &'static str = "Date"; type Type = Date; type ParentType = gtk::Box; } impl ObjectImpl for DatePrivate {} impl WidgetImpl for DatePrivate {} impl BoxImpl for DatePrivate {} glib::wrapper! { pub struct Date(ObjectSubclass) @extends gtk::Box, gtk::Widget; } impl Date { fn new() -> Self { let s: Self = Object::builder().build(); s.add_css_class("card"); s.add_css_class("activatable"); s.set_margin_bottom(8); s.set_margin_top(8); s.set_margin_start(8); s.set_margin_end(8); s.append(&*s.imp().label.borrow()); s.redraw(); s } fn update_date(&self, date: IFC) { *self.imp().date.borrow_mut() = date; self.redraw(); } fn redraw(&self) { let date = self.imp().date.borrow().clone(); self.imp().label.borrow_mut().set_text(&format!( "{:?}, {:?} {}, {}", date.weekday(), date.month(), date.day(), date.year() )); } } pub struct TransitClockPrivate { info: Rc>>, } impl Default for TransitClockPrivate { fn default() -> Self { Self { info: Rc::new(RefCell::new(None)), } } } #[glib::object_subclass] impl ObjectSubclass for TransitClockPrivate { const NAME: &'static str = "TransitClock"; type Type = TransitClock; type ParentType = gtk::DrawingArea; } impl ObjectImpl for TransitClockPrivate {} impl WidgetImpl for TransitClockPrivate {} impl DrawingAreaImpl for TransitClockPrivate {} glib::wrapper! { pub struct TransitClock(ObjectSubclass) @extends gtk::DrawingArea, gtk::Widget; } impl TransitClock { pub fn new() -> Self { let s: Self = Object::builder().build(); s.set_width_request(500); s.set_height_request(500); s.set_draw_func({ let s = s.clone(); move |_, context, width, height| { let center_x = width as f64 / 2.; let center_y = height as f64 / 2.; let radius = width.min(height) as f64 / 2. * 0.9; if let Some(ref info) = *s.imp().info.borrow() { let full_day = Duration::days(1).num_seconds() as f64; let sunrise = info.sunrise - NaiveTime::from_hms_opt(0, 0, 0).unwrap(); let sunset = info.sunset - NaiveTime::from_hms_opt(0, 0, 0).unwrap(); PieChart::new() .center(center_x, center_y) .radius(radius) .rotation(-PI / 2.) .wedges( vec![Wedge { start_angle: (PI * 2.) * sunset.num_seconds() as f64 / full_day, end_angle: (PI * 2.) * sunrise.num_seconds() as f64 / full_day, color: Color { r: 0.1, g: 0.1, b: 0.8, }, }] .into_iter(), ) .draw(context); (0..24).for_each(|tick| { context.set_source_rgb(0., 0., 0.); context.translate(center_x, center_y); context.rotate(tick as f64 * (PI / 12.)); context.move_to(radius - 5., 0.); context.line_to(radius - 10., 0.); let _ = context.stroke(); context.identity_matrix(); }); } } }); s } pub fn update_transit(&self, transit_info: SunMoon) { *self.imp().info.borrow_mut() = Some(transit_info); self.queue_draw(); } } pub fn main() { let app = gtk::Application::builder() .application_id("com.luminescent-dreams.dashboard") .resource_base_path("/com/luminescent-dreams/dashboard") .build(); let latitude = Latitude::from(41.78); let longitude = Longitude::from(-71.41); let runtime = Arc::new( tokio::runtime::Builder::new_multi_thread() .enable_all() .build() .unwrap(), ); let core = Core { tx: Arc::new(RwLock::new(None)), }; let app_handle = runtime.spawn({ let core = core.clone(); async move { let soluna_client = SolunaClient::new(); let transit = soluna_client .request(latitude, longitude, Local::now()) .await; let state = State { date: IFC::from(Local::now().date_naive().with_year(12023).unwrap()), transit: Some(transit), }; loop { if let Some(ref gtk_tx) = *core.tx.read().unwrap() { let _ = gtk_tx.send(Message::Refresh(state.clone())); } std::thread::sleep(std::time::Duration::from_secs(300)); } } }); app.connect_activate(move |app| { let (gtk_tx, gtk_rx) = gtk::glib::MainContext::channel::(gtk::glib::PRIORITY_DEFAULT); *core.tx.write().unwrap() = Some(gtk_tx); let window = gtk::ApplicationWindow::new(app); window.present(); let layout = gtk::Box::builder() .orientation(Orientation::Vertical) .hexpand(true) .vexpand(true) .build(); let date_label = Date::new(); layout.append(&date_label); let transit_clock = TransitClock::new(); layout.append(&transit_clock); window.set_child(Some(&layout)); gtk_rx.attach(None, move |msg| { let Message::Refresh(state) = msg; println!("new state: {:?}", state); date_label.update_date(state.date); if let Some(transit) = state.transit { transit_clock.update_transit(transit); } Continue(true) }); std::thread::spawn(move || {}); }); let args: Vec = env::args().collect(); ApplicationExtManual::run_with_args(&app, &args); let _ = runtime.block_on(async { app_handle.await }); /* let now = Local::now(); let ifc = ifc::IFC::from(now.date_naive().with_year(12023).unwrap()); let next_event = EVENTS.next_event(now.with_timezone(&Utc)).unwrap(); println!( "{:?}, {:?} {}, {}", ifc.weekday(), ifc.month(), ifc.day(), ifc.year() ); println!("{:?}", next_event); let latitude = Latitude::from(41.78); let longitude = Longitude::from(-71.41); let soluna_client = SolunaClient::new(); let sun_moon = soluna_client .request(latitude, longitude, Local::now()) .await; println!( "Solar Transit: {} -> {}", sun_moon.sunrise.format("%H:%M").to_string(), sun_moon.sunset.format("%H:%M").to_string() ); println!( "Lunar Transit: {} -> {} [{:?}]", sun_moon .moonrise .map(|time| time.format("%H:%M").to_string()) .unwrap_or(" -- ".to_owned()), sun_moon .moonset .map(|time| time.format("%H:%M").to_string()) .unwrap_or(" -- ".to_owned()), sun_moon.moon_phase ); */ }