Totally rewrite dashboard as a GTK application #54
|
@ -0,0 +1,69 @@
|
|||
use chrono::Datelike;
|
||||
use glib::Object;
|
||||
use gtk::{prelude::*, subclass::prelude::*};
|
||||
use ifc::IFC;
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
pub struct DatePrivate {
|
||||
date: Rc<RefCell<IFC>>,
|
||||
label: Rc<RefCell<gtk::Label>>,
|
||||
}
|
||||
|
||||
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<DatePrivate>) @extends gtk::Box, gtk::Widget;
|
||||
}
|
||||
|
||||
impl Date {
|
||||
pub 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
|
||||
}
|
||||
|
||||
pub 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()
|
||||
));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
mod date;
|
||||
pub use date::Date;
|
||||
|
||||
mod transit_clock;
|
||||
pub use transit_clock::TransitClock;
|
|
@ -0,0 +1,92 @@
|
|||
use crate::{
|
||||
drawing::{Color, PieChart, Wedge},
|
||||
soluna_client::SunMoon,
|
||||
};
|
||||
use chrono::{Duration, NaiveTime};
|
||||
use glib::Object;
|
||||
use gtk::{prelude::*, subclass::prelude::*};
|
||||
use std::{cell::RefCell, f64::consts::PI, rc::Rc};
|
||||
|
||||
pub struct TransitClockPrivate {
|
||||
info: Rc<RefCell<Option<SunMoon>>>,
|
||||
}
|
||||
|
||||
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<TransitClockPrivate>) @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();
|
||||
}
|
||||
}
|
|
@ -1,30 +1,17 @@
|
|||
/*
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
*/
|
||||
|
||||
use chrono::{Datelike, Duration, Local, NaiveTime};
|
||||
use chrono::{Datelike, Local};
|
||||
use geo_types::{Latitude, Longitude};
|
||||
use glib::{Object, Sender};
|
||||
use glib::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};
|
||||
mod components;
|
||||
use components::{Date, TransitClock};
|
||||
|
||||
/*
|
||||
use geo_types::{Latitude, Longitude};
|
||||
*/
|
||||
mod drawing;
|
||||
|
||||
mod soluna_client;
|
||||
use soluna_client::{SolunaClient, SunMoon};
|
||||
|
@ -34,7 +21,6 @@ mod solstices;
|
|||
use solstices::EVENTS;
|
||||
*/
|
||||
|
||||
/*
|
||||
const EO_TEXT: &'static str = "
|
||||
day = {$day ->
|
||||
*[Sunday] Dimanĉo
|
||||
|
@ -68,115 +54,6 @@ autumn_equinox = Aŭtuna Ekvinokso
|
|||
winter_solstice = Vintra Solstico
|
||||
";
|
||||
|
||||
fn date(fluent: Arc<fluent_ergonomics::FluentErgo>, 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<Tz: TimeZone>(sun_moon: SunMoon<Tz>) -> 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<fluent_ergonomics::FluentErgo>,
|
||||
event: Option<Event>,
|
||||
) -> 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<fluent_ergonomics::FluentErgo>,
|
||||
today: ifc::IFC,
|
||||
sun_moon: SunMoon,
|
||||
event: Option<solstices::Event>,
|
||||
) -> 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<fluent_ergonomics::FluentErgo>,
|
||||
solar_client: Arc<SolunaClient>,
|
||||
latitude: Latitude,
|
||||
longitude: Longitude,
|
||||
) -> Result<Html<String>, 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),
|
||||
|
@ -193,154 +70,6 @@ pub struct Core {
|
|||
tx: Arc<RwLock<Option<Sender<Message>>>>,
|
||||
}
|
||||
|
||||
pub struct DatePrivate {
|
||||
date: Rc<RefCell<IFC>>,
|
||||
label: Rc<RefCell<gtk::Label>>,
|
||||
}
|
||||
|
||||
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<DatePrivate>) @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<RefCell<Option<SunMoon>>>,
|
||||
}
|
||||
|
||||
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<TransitClockPrivate>) @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")
|
||||
|
|
Loading…
Reference in New Issue