From 12ac34117df664ca2b2d58d544c19b515b5348bb Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Mon, 7 Aug 2023 19:06:00 -0400 Subject: [PATCH 01/14] Render the date. Poorly --- Cargo.lock | 239 ++---------------------------------------- dashboard/Cargo.toml | 36 +++---- dashboard/src/main.rs | 92 +++++++++++++--- 3 files changed, 100 insertions(+), 267 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 350c091..0679578 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,12 +68,6 @@ dependencies = [ "rustc-demangle", ] -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - [[package]] name = "base64" version = "0.21.2" @@ -113,15 +107,6 @@ version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array 0.14.7", -] - [[package]] name = "bumpalo" version = "3.13.0" @@ -273,15 +258,6 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" -[[package]] -name = "cpufeatures" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" -dependencies = [ - "libc", -] - [[package]] name = "crc32fast" version = "1.3.2" @@ -340,16 +316,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array 0.14.7", - "typenum", -] - [[package]] name = "cyberpunk-splash" version = "0.1.0" @@ -364,13 +330,15 @@ dependencies = [ name = "dashboard" version = "0.1.0" dependencies = [ + "cairo-rs", "chrono", "fluent", "fluent-ergonomics", "futures", "geo-types", - "horrorshow", - "http", + "gio", + "glib", + "gtk4", "ifc", "lazy_static", "memorycache", @@ -380,17 +348,6 @@ dependencies = [ "serde_json", "tokio", "unic-langid", - "warp", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", ] [[package]] @@ -399,7 +356,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2517b0555262aeeda0d107a40ecfbbcf185921180ffb4acf316ebe0887467e26" dependencies = [ - "generic-array 0.11.2", + "generic-array", "num-traits", "serde", "typenum", @@ -786,16 +743,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - [[package]] name = "geo-types" version = "0.1.0" @@ -1078,31 +1025,6 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" -[[package]] -name = "headers" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" -dependencies = [ - "base64 0.13.1", - "bitflags 1.3.2", - "bytes", - "headers-core", - "http", - "httpdate", - "mime", - "sha1", -] - -[[package]] -name = "headers-core" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" -dependencies = [ - "http", -] - [[package]] name = "heck" version = "0.4.1" @@ -1128,12 +1050,6 @@ dependencies = [ "image", ] -[[package]] -name = "horrorshow" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8371fb981840150b1a54f7cb117bf6699f7466a1d4861daac33bc6fe2b5abea0" - [[package]] name = "http" version = "0.2.9" @@ -1413,16 +1329,6 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" -[[package]] -name = "mime_guess" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" -dependencies = [ - "mime", - "unicase", -] - [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1450,24 +1356,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "multer" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01acbdc23469fd8fe07ab135923371d5f5a422fbf9c522158677c8eb15bc51c2" -dependencies = [ - "bytes", - "encoding_rs", - "futures-util", - "http", - "httparse", - "log", - "memchr", - "mime", - "spin", - "version_check", -] - [[package]] name = "nanorand" version = "0.7.0" @@ -1962,7 +1850,7 @@ version = "0.11.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" dependencies = [ - "base64 0.21.2", + "base64", "bytes", "encoding_rs", "futures-core", @@ -2027,15 +1915,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "rustls-pemfile" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" -dependencies = [ - "base64 0.21.2", -] - [[package]] name = "rusty-fork" version = "0.3.0" @@ -2063,12 +1942,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - [[package]] name = "scopeguard" version = "1.2.0" @@ -2180,17 +2053,6 @@ dependencies = [ "typeshare", ] -[[package]] -name = "sha1" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -2407,29 +2269,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-stream" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-tungstenite" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54319c93411147bced34cb5609a80e0a8e44c5999c93903a81cd866630ec0bfd" -dependencies = [ - "futures-util", - "log", - "tokio", - "tungstenite", -] - [[package]] name = "tokio-util" version = "0.7.8" @@ -2491,7 +2330,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", - "log", "pin-project-lite", "tracing-core", ] @@ -2511,25 +2349,6 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" -[[package]] -name = "tungstenite" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ee6ab729cd4cf0fd55218530c4522ed30b7b6081752839b68fcec8d0960788" -dependencies = [ - "base64 0.13.1", - "byteorder", - "bytes", - "http", - "httparse", - "log", - "rand", - "sha1", - "thiserror", - "url", - "utf-8", -] - [[package]] name = "type-map" version = "0.4.0" @@ -2591,15 +2410,6 @@ dependencies = [ "tinystr", ] -[[package]] -name = "unicase" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" -dependencies = [ - "version_check", -] - [[package]] name = "unicode-bidi" version = "0.3.13" @@ -2632,12 +2442,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - [[package]] name = "uuid" version = "0.8.2" @@ -2684,37 +2488,6 @@ dependencies = [ "try-lock", ] -[[package]] -name = "warp" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba431ef570df1287f7f8b07e376491ad54f84d26ac473489427231e1718e1f69" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "headers", - "http", - "hyper", - "log", - "mime", - "mime_guess", - "multer", - "percent-encoding", - "pin-project", - "rustls-pemfile", - "scoped-tls", - "serde", - "serde_json", - "serde_urlencoded", - "tokio", - "tokio-stream", - "tokio-tungstenite", - "tokio-util", - "tower-service", - "tracing", -] - [[package]] name = "wasi" version = "0.10.0+wasi-snapshot-preview1" diff --git a/dashboard/Cargo.toml b/dashboard/Cargo.toml index 668e3a8..a50b084 100644 --- a/dashboard/Cargo.toml +++ b/dashboard/Cargo.toml @@ -6,21 +6,21 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -chrono = { version = "0.4", features = ["serde"] } -fluent = { version = "0.16" } -fluent-ergonomics = { path = "../fluent-ergonomics/" } -futures = { version = "0.3" } -geo-types = { path = "../geo-types/" } -lazy_static = { version = "1.4" } -unic-langid = { version = "0.9" } -horrorshow = { version = "0.8" } -http = { version = "*" } -ifc = { path = "../ifc/" } -memorycache = { path = "../memorycache" } -reqwest = { version = "0.11", features = ["json"] } -serde_derive = { version = "1" } -serde_json = { version = "1" } -serde = { version = "1" } -tokio = { version = "1", features = ["full"] } -warp = { version = "0.3" } - +cairo-rs = { version = "0.17" } +chrono = { version = "0.4", features = ["serde"] } +fluent-ergonomics = { path = "../fluent-ergonomics/" } +fluent = { version = "0.16" } +futures = { version = "0.3" } +geo-types = { path = "../geo-types/" } +gio = { version = "0.17" } +glib = { version = "0.17" } +gtk = { version = "0.6", package = "gtk4" } +ifc = { path = "../ifc/" } +lazy_static = { version = "1.4" } +memorycache = { path = "../memorycache" } +reqwest = { version = "0.11", features = ["json"] } +serde_derive = { version = "1" } +serde_json = { version = "1" } +serde = { version = "1" } +tokio = { version = "1", features = ["full"] } +unic-langid = { version = "0.9" } diff --git a/dashboard/src/main.rs b/dashboard/src/main.rs index 1d0b604..e1a9a3e 100644 --- a/dashboard/src/main.rs +++ b/dashboard/src/main.rs @@ -1,28 +1,25 @@ +/* #[macro_use] extern crate serde_derive; #[macro_use] -extern crate horrorshow; -#[macro_use] extern crate lazy_static; +*/ -use chrono::{Datelike, Local, TimeZone, Utc}; -use fluent::{FluentArgs, FluentValue}; +use chrono::Datelike; +use glib::Object; +use gtk::{prelude::*, subclass::prelude::*}; + +/* use geo_types::{Latitude, Longitude}; -use horrorshow::helper::doctype; -use horrorshow::prelude::*; use ifc; -use std::{ - borrow::Cow, - net::{IpAddr, Ipv4Addr, SocketAddr}, - sync::Arc, -}; -use warp::{reply::Html, Filter, Rejection}; +use std::sync::Arc; mod soluna_client; -use soluna_client::{SolunaClient, SunMoon}; +use soluna_client::SolunaClient; mod solstices; -use solstices::{Event, EVENTS}; +use solstices::EVENTS; +*/ /* const EO_TEXT: &'static str = " @@ -167,8 +164,70 @@ async fn page( } */ -#[tokio::main] -pub async fn main() { +#[derive(Default)] +pub struct DatePrivate {} + +#[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(); + + let dt = ifc::IFC::from(chrono::Local::now().date_naive().with_year(12023).unwrap()); + s.append(>k::Label::new(Some( + format!( + "{:?}, {:?} {}, {}", + dt.weekday(), + dt.month(), + dt.day(), + dt.year() + ) + .as_ref(), + ))); + s + } +} + +pub fn main() { + /* + let runtime = Arc::new( + tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap(), + ); + */ + let app = gtk::Application::builder() + .application_id("com.luminescent-dreams.dashboard") + .build(); + + app.connect_activate( + // let runtime = runtime.clone(); + |app| { + let window = gtk::ApplicationWindow::new(app); + window.present(); + + let date = Date::new(); + window.set_child(Some(&date)); + }, + ); + + app.run(); + + /* 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(); @@ -207,4 +266,5 @@ pub async fn main() { .unwrap_or(" -- ".to_owned()), sun_moon.moon_phase ); + */ } -- 2.44.1 From 96fc16d0f036e3da8faaf1b65053f86d25e6e70e Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Mon, 7 Aug 2023 22:35:43 -0400 Subject: [PATCH 02/14] Start drawing the clock --- dashboard/src/main.rs | 89 +++++++++++++++++++++++++++++++--- dashboard/src/soluna_client.rs | 5 +- 2 files changed, 84 insertions(+), 10 deletions(-) diff --git a/dashboard/src/main.rs b/dashboard/src/main.rs index e1a9a3e..7447564 100644 --- a/dashboard/src/main.rs +++ b/dashboard/src/main.rs @@ -5,18 +5,20 @@ extern crate serde_derive; extern crate lazy_static; */ -use chrono::Datelike; +use chrono::{Datelike, Duration, NaiveTime}; use glib::Object; -use gtk::{prelude::*, subclass::prelude::*}; +use gtk::{prelude::*, subclass::prelude::*, Orientation}; +use ifc::IFC; +use std::{cell::RefCell, f64::consts::PI, ops::Deref, rc::Rc}; /* use geo_types::{Latitude, Longitude}; -use ifc; -use std::sync::Arc; +*/ mod soluna_client; -use soluna_client::SolunaClient; +use soluna_client::{LunarPhase, SunMoon}; +/* mod solstices; use solstices::EVENTS; */ @@ -186,7 +188,7 @@ impl Date { fn new() -> Self { let s: Self = Object::builder().build(); - let dt = ifc::IFC::from(chrono::Local::now().date_naive().with_year(12023).unwrap()); + let dt = IFC::from(chrono::Local::now().date_naive().with_year(12023).unwrap()); s.append(>k::Label::new(Some( format!( "{:?}, {:?} {}, {}", @@ -201,6 +203,67 @@ impl Date { } } +#[derive(Default)] +pub struct DayPrivate { + info: Rc>>, +} + +#[glib::object_subclass] +impl ObjectSubclass for DayPrivate { + const NAME: &'static str = "Day"; + type Type = Day; + type ParentType = gtk::DrawingArea; +} + +impl ObjectImpl for DayPrivate {} +impl WidgetImpl for DayPrivate {} +impl DrawingAreaImpl for DayPrivate {} + +glib::wrapper! { + pub struct Day(ObjectSubclass) @extends gtk::DrawingArea, gtk::Widget; +} + +impl Day { + pub fn new(sun_moon_info: SunMoon) -> Self { + let s: Self = Object::builder().build(); + s.set_width_request(500); + s.set_height_request(500); + + *s.imp().info.borrow_mut() = Some(sun_moon_info); + s.set_draw_func({ + let s = s.clone(); + move |_, context, width, height| { + let info = s.imp().info.borrow(); + + // context.set_source_rgb(240., 240., 240.); + context.set_source_rgb(0., 0., 0.); + let _ = context.paint(); + + context.set_line_width(5.); + context.set_source_rgb(0.7, 0., 0.9); + context.arc(width as f64 / 2., height as f64 / 2., 200., 0., PI * 2.); + let _ = context.stroke(); + + if let Some(info) = info.deref() { + context.move_to(width as f64 / 2., height as f64 / 2.); + 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(); + + let start = (PI * 2.) * sunset.num_seconds() as f64 / full_day - (PI / 2.); + let end = (PI * 2.) * sunrise.num_seconds() as f64 / full_day - (PI / 2.); + + context.set_source_rgb(0., 0.7, 0.9); + context.arc(width as f64 / 2., height as f64 / 2., 200., start, end); + let _ = context.fill(); + } + } + }); + + s + } +} + pub fn main() { /* let runtime = Arc::new( @@ -220,8 +283,20 @@ pub fn main() { let window = gtk::ApplicationWindow::new(app); window.present(); + let layout = gtk::Box::builder() + .orientation(Orientation::Vertical) + .build(); let date = Date::new(); - window.set_child(Some(&date)); + let day = Day::new(SunMoon { + sunrise: NaiveTime::from_hms_opt(6, 0, 0).unwrap(), + sunset: NaiveTime::from_hms_opt(0, 0, 0).unwrap(), + moonrise: NaiveTime::from_hms_opt(15, 0, 0), + moonset: NaiveTime::from_hms_opt(5, 0, 0), + moon_phase: LunarPhase::WaningCrescent, + }); + layout.append(&date); + layout.append(&day); + window.set_child(Some(&layout)); }, ); diff --git a/dashboard/src/soluna_client.rs b/dashboard/src/soluna_client.rs index 60e3e69..f705043 100644 --- a/dashboard/src/soluna_client.rs +++ b/dashboard/src/soluna_client.rs @@ -1,12 +1,11 @@ // 41.78, -71.41 // https://api.solunar.org/solunar/41.78,-71.41,20211029,-4 -use chrono::{ - DateTime, Duration, NaiveDate, NaiveDateTime, NaiveTime, Offset, TimeZone, Timelike, Utc, -}; +use chrono::{DateTime, Duration, NaiveTime, Offset, TimeZone, Utc}; use geo_types::{Latitude, Longitude}; use memorycache::MemoryCache; use reqwest; +use serde::Deserialize; const ENDPOINT: &str = "https://api.solunar.org/solunar"; -- 2.44.1 From 6f31ac1e8d8c10838b84d62ef5fafa654152bd60 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Mon, 7 Aug 2023 23:53:42 -0400 Subject: [PATCH 03/14] Add clock ticks --- dashboard/src/main.rs | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/dashboard/src/main.rs b/dashboard/src/main.rs index 7447564..e945f3c 100644 --- a/dashboard/src/main.rs +++ b/dashboard/src/main.rs @@ -204,26 +204,26 @@ impl Date { } #[derive(Default)] -pub struct DayPrivate { +pub struct ClockPrivate { info: Rc>>, } #[glib::object_subclass] -impl ObjectSubclass for DayPrivate { - const NAME: &'static str = "Day"; - type Type = Day; +impl ObjectSubclass for ClockPrivate { + const NAME: &'static str = "Clock"; + type Type = Clock; type ParentType = gtk::DrawingArea; } -impl ObjectImpl for DayPrivate {} -impl WidgetImpl for DayPrivate {} -impl DrawingAreaImpl for DayPrivate {} +impl ObjectImpl for ClockPrivate {} +impl WidgetImpl for ClockPrivate {} +impl DrawingAreaImpl for ClockPrivate {} glib::wrapper! { - pub struct Day(ObjectSubclass) @extends gtk::DrawingArea, gtk::Widget; + pub struct Clock(ObjectSubclass) @extends gtk::DrawingArea, gtk::Widget; } -impl Day { +impl Clock { pub fn new(sun_moon_info: SunMoon) -> Self { let s: Self = Object::builder().build(); s.set_width_request(500); @@ -244,6 +244,15 @@ impl Day { context.arc(width as f64 / 2., height as f64 / 2., 200., 0., PI * 2.); let _ = context.stroke(); + (0..24).for_each(|hour| { + context.translate(width as f64 / 2., height as f64 / 2.); + context.rotate(PI / 12. * hour as f64); + context.move_to(210., 0.); + context.line_to(230., 0.); + context.identity_matrix(); + let _ = context.stroke(); + }); + if let Some(info) = info.deref() { context.move_to(width as f64 / 2., height as f64 / 2.); let full_day = Duration::days(1).num_seconds() as f64; @@ -287,7 +296,7 @@ pub fn main() { .orientation(Orientation::Vertical) .build(); let date = Date::new(); - let day = Day::new(SunMoon { + let day = Clock::new(SunMoon { sunrise: NaiveTime::from_hms_opt(6, 0, 0).unwrap(), sunset: NaiveTime::from_hms_opt(0, 0, 0).unwrap(), moonrise: NaiveTime::from_hms_opt(15, 0, 0), -- 2.44.1 From 36b0238662a851179ffd30600e9d65c46f2cc5f7 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Tue, 8 Aug 2023 17:25:21 -0400 Subject: [PATCH 04/14] Write up a Pie Chart widget --- Cargo.lock | 34 ++++++++ dashboard/Cargo.toml | 1 + dashboard/src/main.rs | 126 +++++++++++++++++++++++------- dashboard/src/ui/mod.rs | 2 + dashboard/src/ui/pie_chart.rs | 141 ++++++++++++++++++++++++++++++++++ flake.lock | 8 +- flake.nix | 2 +- 7 files changed, 280 insertions(+), 34 deletions(-) create mode 100644 dashboard/src/ui/mod.rs create mode 100644 dashboard/src/ui/pie_chart.rs diff --git a/Cargo.lock b/Cargo.lock index 0679578..7c88714 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -341,6 +341,7 @@ dependencies = [ "gtk4", "ifc", "lazy_static", + "libadwaita", "memorycache", "reqwest", "serde", @@ -1263,6 +1264,39 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" +[[package]] +name = "libadwaita" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ab9c0843f9f23ff25634df2743690c3a1faffe0a190e60c490878517eb81abf" +dependencies = [ + "bitflags 1.3.2", + "gdk-pixbuf", + "gdk4", + "gio", + "glib", + "gtk4", + "libadwaita-sys", + "libc", + "pango", +] + +[[package]] +name = "libadwaita-sys" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4231cb2499a9f0c4cdfa4885414b33e39901ddcac61150bc0bb4ff8a57ede404" +dependencies = [ + "gdk4-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "gtk4-sys", + "libc", + "pango-sys", + "system-deps", +] + [[package]] name = "libc" version = "0.2.147" diff --git a/dashboard/Cargo.toml b/dashboard/Cargo.toml index a50b084..f4b1434 100644 --- a/dashboard/Cargo.toml +++ b/dashboard/Cargo.toml @@ -6,6 +6,7 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +adw = { package = "libadwaita", version = "0.4", features = ["v1_3"] } cairo-rs = { version = "0.17" } chrono = { version = "0.4", features = ["serde"] } fluent-ergonomics = { path = "../fluent-ergonomics/" } diff --git a/dashboard/src/main.rs b/dashboard/src/main.rs index e945f3c..2afaeea 100644 --- a/dashboard/src/main.rs +++ b/dashboard/src/main.rs @@ -9,7 +9,10 @@ use chrono::{Datelike, Duration, NaiveTime}; use glib::Object; use gtk::{prelude::*, subclass::prelude::*, Orientation}; use ifc::IFC; -use std::{cell::RefCell, f64::consts::PI, ops::Deref, rc::Rc}; +use std::{cell::RefCell, env, f64::consts::PI, ops::Deref, rc::Rc}; + +mod ui; +use ui::{Color, PieChart, Wedge}; /* use geo_types::{Latitude, Longitude}; @@ -187,6 +190,12 @@ glib::wrapper! { 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); let dt = IFC::from(chrono::Local::now().date_naive().with_year(12023).unwrap()); s.append(>k::Label::new(Some( @@ -203,56 +212,79 @@ impl Date { } } -#[derive(Default)] -pub struct ClockPrivate { +pub struct TransitClockPrivate { info: Rc>>, + chart: Rc>, +} + +impl Default for TransitClockPrivate { + fn default() -> Self { + Self { + info: Rc::new(RefCell::new(None)), + chart: Rc::new(RefCell::new(PieChart::builder().rotation(-PI / 2.).build())), + } + } } #[glib::object_subclass] -impl ObjectSubclass for ClockPrivate { - const NAME: &'static str = "Clock"; - type Type = Clock; - type ParentType = gtk::DrawingArea; +impl ObjectSubclass for TransitClockPrivate { + const NAME: &'static str = "TransitClock"; + type Type = TransitClock; + type ParentType = gtk::Box; } -impl ObjectImpl for ClockPrivate {} -impl WidgetImpl for ClockPrivate {} -impl DrawingAreaImpl for ClockPrivate {} +impl ObjectImpl for TransitClockPrivate {} +impl WidgetImpl for TransitClockPrivate {} +impl BoxImpl for TransitClockPrivate {} glib::wrapper! { - pub struct Clock(ObjectSubclass) @extends gtk::DrawingArea, gtk::Widget; + pub struct TransitClock(ObjectSubclass) @extends gtk::Box, gtk::Widget; } -impl Clock { +impl TransitClock { pub fn new(sun_moon_info: SunMoon) -> Self { let s: Self = Object::builder().build(); s.set_width_request(500); s.set_height_request(500); + { + let mut chart = s.imp().chart.borrow_mut(); + s.append(&*chart); + { + let full_day = Duration::days(1).num_seconds() as f64; + let sunrise = sun_moon_info.sunrise - NaiveTime::from_hms_opt(0, 0, 0).unwrap(); + let sunset = sun_moon_info.sunset - NaiveTime::from_hms_opt(0, 0, 0).unwrap(); + + println!("{:?} -> {:?}", sunrise, sunset); + + chart.add_wedge(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, + }, + }); + } + } + *s.imp().info.borrow_mut() = Some(sun_moon_info); + + /* s.set_draw_func({ let s = s.clone(); move |_, context, width, height| { let info = s.imp().info.borrow(); - // context.set_source_rgb(240., 240., 240.); - context.set_source_rgb(0., 0., 0.); + context.set_source_rgba(0., 0., 0., 0.); let _ = context.paint(); - context.set_line_width(5.); + context.set_line_width(2.); context.set_source_rgb(0.7, 0., 0.9); context.arc(width as f64 / 2., height as f64 / 2., 200., 0., PI * 2.); let _ = context.stroke(); - (0..24).for_each(|hour| { - context.translate(width as f64 / 2., height as f64 / 2.); - context.rotate(PI / 12. * hour as f64); - context.move_to(210., 0.); - context.line_to(230., 0.); - context.identity_matrix(); - let _ = context.stroke(); - }); - if let Some(info) = info.deref() { context.move_to(width as f64 / 2., height as f64 / 2.); let full_day = Duration::days(1).num_seconds() as f64; @@ -266,13 +298,34 @@ impl Clock { context.arc(width as f64 / 2., height as f64 / 2., 200., start, end); let _ = context.fill(); } + + (0..24).for_each(|hour| { + context.translate(width as f64 / 2., height as f64 / 2.); + context.rotate(PI / 12. * hour as f64); + context.move_to(185., 0.); + context.line_to(195., 0.); + context.identity_matrix(); + let _ = context.stroke(); + }); } }); + */ s } } +/* +pub struct AppPrivate { +} + +impl AppPrivate { + const NAME: &'static str = "App"; + type Type = App; + type ParentType = adw::Application; +} +*/ + pub fn main() { /* let runtime = Arc::new( @@ -284,6 +337,7 @@ pub fn main() { */ let app = gtk::Application::builder() .application_id("com.luminescent-dreams.dashboard") + .resource_base_path("/com/luminescent-dreams/dashboard") .build(); app.connect_activate( @@ -294,22 +348,36 @@ pub fn main() { let layout = gtk::Box::builder() .orientation(Orientation::Vertical) + .hexpand(true) + .vexpand(true) .build(); let date = Date::new(); - let day = Clock::new(SunMoon { - sunrise: NaiveTime::from_hms_opt(6, 0, 0).unwrap(), - sunset: NaiveTime::from_hms_opt(0, 0, 0).unwrap(), + layout.append(&date); + + let button = gtk::Button::builder() + .label("Text") + .margin_bottom(8) + .margin_top(8) + .margin_start(8) + .margin_end(8) + .build(); + layout.append(&button); + + let day = TransitClock::new(SunMoon { + sunrise: NaiveTime::from_hms_opt(7, 0, 0).unwrap(), + sunset: NaiveTime::from_hms_opt(22, 0, 0).unwrap(), moonrise: NaiveTime::from_hms_opt(15, 0, 0), moonset: NaiveTime::from_hms_opt(5, 0, 0), moon_phase: LunarPhase::WaningCrescent, }); - layout.append(&date); layout.append(&day); + window.set_child(Some(&layout)); }, ); - app.run(); + let args: Vec = env::args().collect(); + ApplicationExtManual::run_with_args(&app, &args); /* let now = Local::now(); diff --git a/dashboard/src/ui/mod.rs b/dashboard/src/ui/mod.rs new file mode 100644 index 0000000..1fa5546 --- /dev/null +++ b/dashboard/src/ui/mod.rs @@ -0,0 +1,2 @@ +mod pie_chart; +pub use pie_chart::{Color, PieChart, Wedge}; diff --git a/dashboard/src/ui/pie_chart.rs b/dashboard/src/ui/pie_chart.rs new file mode 100644 index 0000000..f51732d --- /dev/null +++ b/dashboard/src/ui/pie_chart.rs @@ -0,0 +1,141 @@ +use chrono::{Duration, NaiveTime}; +use glib::Object; +use gtk::{prelude::*, subclass::prelude::*}; +use std::{cell::RefCell, f64::consts::PI, rc::Rc}; + +#[derive(Clone, Debug)] +pub struct Color { + pub r: f64, + pub g: f64, + pub b: f64, +} + +#[derive(Clone, Debug)] +pub struct Wedge { + pub start_angle: f64, + pub end_angle: f64, + pub color: Color, +} + +#[derive(Default)] +pub struct PieChartPrivate { + rotation: Rc>, + wedges: Rc>>, +} + +#[glib::object_subclass] +impl ObjectSubclass for PieChartPrivate { + const NAME: &'static str = "PieChart"; + type Type = PieChart; + type ParentType = gtk::DrawingArea; +} + +impl ObjectImpl for PieChartPrivate {} +impl WidgetImpl for PieChartPrivate {} +impl DrawingAreaImpl for PieChartPrivate {} + +glib::wrapper! { + pub struct PieChart(ObjectSubclass) @extends gtk::DrawingArea, gtk::Widget; +} + +impl PieChart { + pub fn new(builder: PieChartBuilder) -> Self { + let s: Self = Object::builder().build(); + s.set_content_width(500); + s.set_content_height(500); + + *s.imp().rotation.borrow_mut() = builder.rotation; + *s.imp().wedges.borrow_mut() = builder.wedges; + + s.set_draw_func({ + let s = s.clone(); + move |_, context, width, height| { + println!("redrawing: {} {}", width, height); + let radius = width.min(height) as f64 / 2. * 0.9; + let center_x = (width / 2) as f64; + let center_y = (height / 2) as f64; + + let rotation = s.imp().rotation.borrow().clone(); + let wedges = s.imp().wedges.borrow().clone(); + + context.set_source_rgba(0., 0., 0., 0.); + let _ = context.paint(); + + context.set_line_width(2.); + + wedges.iter().for_each( + |Wedge { + start_angle, + end_angle, + color, + }| { + println!( + "wedge: {:.02?} {:.02?} {:.02?}", + start_angle, end_angle, color + ); + println!( + "wedge: {:.02?} {:.02?} [{:.02?}]", + start_angle + rotation, + end_angle + rotation, + rotation + ); + context.move_to(center_x, center_y); + context.set_source_rgb(color.r, color.g, color.b); + context.arc( + center_x, + center_y, + radius, + start_angle + rotation, + end_angle + rotation, + ); + let _ = context.fill(); + }, + ); + + context.set_source_rgb(1., 1., 1.); + context.arc(center_x, center_y, radius, 0., 2. * PI); + let _ = context.stroke(); + } + }); + + s + } + + pub fn set_rotation(&mut self, rotation: f64) { + *self.imp().rotation.borrow_mut() = rotation; + self.queue_draw(); + } + + pub fn add_wedge(&mut self, wedge: Wedge) { + (*self.imp().wedges.borrow_mut()).push(wedge); + self.queue_draw(); + } + + pub fn builder() -> PieChartBuilder { + PieChartBuilder { + rotation: 0., + wedges: vec![], + } + } +} + +pub struct PieChartBuilder { + rotation: f64, + wedges: Vec, +} + +impl PieChartBuilder { + pub fn rotation(mut self, rotation: f64) -> Self { + self.rotation = rotation; + self + } + + pub fn wedges(mut self, wedges: Vec) -> Self { + self.wedges = wedges; + self + } + + pub fn build(self) -> PieChart { + PieChart::new(self) + } +} diff --git a/flake.lock b/flake.lock index 0df1f2e..ca25ad3 100644 --- a/flake.lock +++ b/flake.lock @@ -51,16 +51,16 @@ }, "nixpkgs": { "locked": { - "lastModified": 1688392541, - "narHash": "sha256-lHrKvEkCPTUO+7tPfjIcb7Trk6k31rz18vkyqmkeJfY=", + "lastModified": 1691421349, + "narHash": "sha256-RRJyX0CUrs4uW4gMhd/X4rcDG8PTgaaCQM5rXEJOx6g=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "ea4c80b39be4c09702b0cb3b42eab59e2ba4f24b", + "rev": "011567f35433879aae5024fc6ec53f2a0568a6c4", "type": "github" }, "original": { "id": "nixpkgs", - "ref": "nixos-22.11", + "ref": "nixos-23.05", "type": "indirect" } }, diff --git a/flake.nix b/flake.nix index 39da673..9fdfd6e 100644 --- a/flake.nix +++ b/flake.nix @@ -2,7 +2,7 @@ description = "Lumenescent Dreams Tools"; inputs = { - nixpkgs.url = "nixpkgs/nixos-22.11"; + nixpkgs.url = "nixpkgs/nixos-23.05"; unstable.url = "nixpkgs/nixos-unstable"; pkgs-cargo2nix.url = "github:cargo2nix/cargo2nix"; typeshare.url = "github:1Password/typeshare"; -- 2.44.1 From f403b8632f292f6c975082518e2e8699e2310e34 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Tue, 8 Aug 2023 17:25:53 -0400 Subject: [PATCH 05/14] Clean up dead code --- dashboard/src/main.rs | 59 ------------------------------------------- 1 file changed, 59 deletions(-) diff --git a/dashboard/src/main.rs b/dashboard/src/main.rs index 2afaeea..ef4316b 100644 --- a/dashboard/src/main.rs +++ b/dashboard/src/main.rs @@ -271,70 +271,11 @@ impl TransitClock { *s.imp().info.borrow_mut() = Some(sun_moon_info); - /* - s.set_draw_func({ - let s = s.clone(); - move |_, context, width, height| { - let info = s.imp().info.borrow(); - - context.set_source_rgba(0., 0., 0., 0.); - let _ = context.paint(); - - context.set_line_width(2.); - context.set_source_rgb(0.7, 0., 0.9); - context.arc(width as f64 / 2., height as f64 / 2., 200., 0., PI * 2.); - let _ = context.stroke(); - - if let Some(info) = info.deref() { - context.move_to(width as f64 / 2., height as f64 / 2.); - 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(); - - let start = (PI * 2.) * sunset.num_seconds() as f64 / full_day - (PI / 2.); - let end = (PI * 2.) * sunrise.num_seconds() as f64 / full_day - (PI / 2.); - - context.set_source_rgb(0., 0.7, 0.9); - context.arc(width as f64 / 2., height as f64 / 2., 200., start, end); - let _ = context.fill(); - } - - (0..24).for_each(|hour| { - context.translate(width as f64 / 2., height as f64 / 2.); - context.rotate(PI / 12. * hour as f64); - context.move_to(185., 0.); - context.line_to(195., 0.); - context.identity_matrix(); - let _ = context.stroke(); - }); - } - }); - */ - s } } -/* -pub struct AppPrivate { -} - -impl AppPrivate { - const NAME: &'static str = "App"; - type Type = App; - type ParentType = adw::Application; -} -*/ - pub fn main() { - /* - let runtime = Arc::new( - tokio::runtime::Builder::new_multi_thread() - .enable_all() - .build() - .unwrap(), - ); - */ let app = gtk::Application::builder() .application_id("com.luminescent-dreams.dashboard") .resource_base_path("/com/luminescent-dreams/dashboard") -- 2.44.1 From 28192ff8d7052fd0eac4f687e31cf1010b4cd69f Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Wed, 9 Aug 2023 10:54:27 -0400 Subject: [PATCH 06/14] Convert the pie chart to a drawing tool instead of a full widget --- dashboard/src/{ui => drawing}/mod.rs | 0 dashboard/src/drawing/pie_chart.rs | 100 +++++++++++++++++++ dashboard/src/main.rs | 76 +++++++++------ dashboard/src/ui/pie_chart.rs | 141 --------------------------- 4 files changed, 147 insertions(+), 170 deletions(-) rename dashboard/src/{ui => drawing}/mod.rs (100%) create mode 100644 dashboard/src/drawing/pie_chart.rs delete mode 100644 dashboard/src/ui/pie_chart.rs diff --git a/dashboard/src/ui/mod.rs b/dashboard/src/drawing/mod.rs similarity index 100% rename from dashboard/src/ui/mod.rs rename to dashboard/src/drawing/mod.rs diff --git a/dashboard/src/drawing/pie_chart.rs b/dashboard/src/drawing/pie_chart.rs new file mode 100644 index 0000000..8b9e97a --- /dev/null +++ b/dashboard/src/drawing/pie_chart.rs @@ -0,0 +1,100 @@ +use cairo::{Context, Format, ImageSurface}; +use std::f64::consts::PI; + +#[derive(Clone, Debug)] +pub struct Color { + pub r: f64, + pub g: f64, + pub b: f64, +} + +#[derive(Clone, Debug)] +pub struct Wedge { + pub start_angle: f64, + pub end_angle: f64, + pub color: Color, +} + +pub struct PieChart { + rotation: f64, + wedges: Vec, + width: i32, + height: i32, +} + +impl PieChart { + pub fn new() -> Self { + Self { + rotation: 0., + wedges: vec![], + width: 0, + height: 0, + } + } + + pub fn rotation(mut self, rotation: f64) -> Self { + self.rotation = rotation; + self + } + + pub fn wedges(mut self, wedge: impl Iterator) -> Self { + let mut wedges: Vec = wedge.collect(); + self.wedges.append(&mut wedges); + self + } + + pub fn width(mut self, width: i32) -> Self { + self.width = width; + self + } + + pub fn height(mut self, height: i32) -> Self { + self.height = height; + self + } + + pub fn draw(self, context: &Context) { + println!("drawing: {} {}", self.width, self.height); + let radius = self.width.min(self.height) as f64 / 2. * 0.9; + let center_x = (self.width / 2) as f64; + let center_y = (self.height / 2) as f64; + + context.set_source_rgba(0., 0., 0., 0.); + let _ = context.paint(); + + context.set_line_width(2.); + + self.wedges.iter().for_each( + |Wedge { + start_angle, + end_angle, + color, + }| { + println!( + "wedge: {:.02?} {:.02?} {:.02?}", + start_angle, end_angle, color + ); + println!( + "wedge: {:.02?} {:.02?} [{:.02?}]", + start_angle + self.rotation, + end_angle + self.rotation, + self.rotation + ); + context.move_to(center_x, center_y); + context.set_source_rgb(color.r, color.g, color.b); + context.arc( + center_x, + center_y, + radius, + start_angle + self.rotation, + end_angle + self.rotation, + ); + let _ = context.fill(); + }, + ); + + context.set_source_rgb(1., 1., 1.); + context.arc(center_x, center_y, radius, 0., 2. * PI); + let _ = context.stroke(); + } +} diff --git a/dashboard/src/main.rs b/dashboard/src/main.rs index ef4316b..111feb8 100644 --- a/dashboard/src/main.rs +++ b/dashboard/src/main.rs @@ -11,8 +11,8 @@ use gtk::{prelude::*, subclass::prelude::*, Orientation}; use ifc::IFC; use std::{cell::RefCell, env, f64::consts::PI, ops::Deref, rc::Rc}; -mod ui; -use ui::{Color, PieChart, Wedge}; +mod drawing; +use drawing::{Color, PieChart, Wedge}; /* use geo_types::{Latitude, Longitude}; @@ -214,14 +214,12 @@ impl Date { pub struct TransitClockPrivate { info: Rc>>, - chart: Rc>, } impl Default for TransitClockPrivate { fn default() -> Self { Self { info: Rc::new(RefCell::new(None)), - chart: Rc::new(RefCell::new(PieChart::builder().rotation(-PI / 2.).build())), } } } @@ -230,15 +228,15 @@ impl Default for TransitClockPrivate { impl ObjectSubclass for TransitClockPrivate { const NAME: &'static str = "TransitClock"; type Type = TransitClock; - type ParentType = gtk::Box; + type ParentType = gtk::DrawingArea; } impl ObjectImpl for TransitClockPrivate {} impl WidgetImpl for TransitClockPrivate {} -impl BoxImpl for TransitClockPrivate {} +impl DrawingAreaImpl for TransitClockPrivate {} glib::wrapper! { - pub struct TransitClock(ObjectSubclass) @extends gtk::Box, gtk::Widget; + pub struct TransitClock(ObjectSubclass) @extends gtk::DrawingArea, gtk::Widget; } impl TransitClock { @@ -247,30 +245,50 @@ impl TransitClock { s.set_width_request(500); s.set_height_request(500); - { - let mut chart = s.imp().chart.borrow_mut(); - s.append(&*chart); - { - let full_day = Duration::days(1).num_seconds() as f64; - let sunrise = sun_moon_info.sunrise - NaiveTime::from_hms_opt(0, 0, 0).unwrap(); - let sunset = sun_moon_info.sunset - NaiveTime::from_hms_opt(0, 0, 0).unwrap(); - - println!("{:?} -> {:?}", sunrise, sunset); - - chart.add_wedge(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, - }, - }); - } - } - *s.imp().info.borrow_mut() = Some(sun_moon_info); + 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.; + 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() + .width(width) + .height(height) + .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(center_x, center_y); + context.line_to(center_x + 10., center_y); + let _ = context.stroke(); + context.identity_matrix(); + }); + } + } + }); + s } } diff --git a/dashboard/src/ui/pie_chart.rs b/dashboard/src/ui/pie_chart.rs deleted file mode 100644 index f51732d..0000000 --- a/dashboard/src/ui/pie_chart.rs +++ /dev/null @@ -1,141 +0,0 @@ -use chrono::{Duration, NaiveTime}; -use glib::Object; -use gtk::{prelude::*, subclass::prelude::*}; -use std::{cell::RefCell, f64::consts::PI, rc::Rc}; - -#[derive(Clone, Debug)] -pub struct Color { - pub r: f64, - pub g: f64, - pub b: f64, -} - -#[derive(Clone, Debug)] -pub struct Wedge { - pub start_angle: f64, - pub end_angle: f64, - pub color: Color, -} - -#[derive(Default)] -pub struct PieChartPrivate { - rotation: Rc>, - wedges: Rc>>, -} - -#[glib::object_subclass] -impl ObjectSubclass for PieChartPrivate { - const NAME: &'static str = "PieChart"; - type Type = PieChart; - type ParentType = gtk::DrawingArea; -} - -impl ObjectImpl for PieChartPrivate {} -impl WidgetImpl for PieChartPrivate {} -impl DrawingAreaImpl for PieChartPrivate {} - -glib::wrapper! { - pub struct PieChart(ObjectSubclass) @extends gtk::DrawingArea, gtk::Widget; -} - -impl PieChart { - pub fn new(builder: PieChartBuilder) -> Self { - let s: Self = Object::builder().build(); - s.set_content_width(500); - s.set_content_height(500); - - *s.imp().rotation.borrow_mut() = builder.rotation; - *s.imp().wedges.borrow_mut() = builder.wedges; - - s.set_draw_func({ - let s = s.clone(); - move |_, context, width, height| { - println!("redrawing: {} {}", width, height); - let radius = width.min(height) as f64 / 2. * 0.9; - let center_x = (width / 2) as f64; - let center_y = (height / 2) as f64; - - let rotation = s.imp().rotation.borrow().clone(); - let wedges = s.imp().wedges.borrow().clone(); - - context.set_source_rgba(0., 0., 0., 0.); - let _ = context.paint(); - - context.set_line_width(2.); - - wedges.iter().for_each( - |Wedge { - start_angle, - end_angle, - color, - }| { - println!( - "wedge: {:.02?} {:.02?} {:.02?}", - start_angle, end_angle, color - ); - println!( - "wedge: {:.02?} {:.02?} [{:.02?}]", - start_angle + rotation, - end_angle + rotation, - rotation - ); - context.move_to(center_x, center_y); - context.set_source_rgb(color.r, color.g, color.b); - context.arc( - center_x, - center_y, - radius, - start_angle + rotation, - end_angle + rotation, - ); - let _ = context.fill(); - }, - ); - - context.set_source_rgb(1., 1., 1.); - context.arc(center_x, center_y, radius, 0., 2. * PI); - let _ = context.stroke(); - } - }); - - s - } - - pub fn set_rotation(&mut self, rotation: f64) { - *self.imp().rotation.borrow_mut() = rotation; - self.queue_draw(); - } - - pub fn add_wedge(&mut self, wedge: Wedge) { - (*self.imp().wedges.borrow_mut()).push(wedge); - self.queue_draw(); - } - - pub fn builder() -> PieChartBuilder { - PieChartBuilder { - rotation: 0., - wedges: vec![], - } - } -} - -pub struct PieChartBuilder { - rotation: f64, - wedges: Vec, -} - -impl PieChartBuilder { - pub fn rotation(mut self, rotation: f64) -> Self { - self.rotation = rotation; - self - } - - pub fn wedges(mut self, wedges: Vec) -> Self { - self.wedges = wedges; - self - } - - pub fn build(self) -> PieChart { - PieChart::new(self) - } -} -- 2.44.1 From 6fb774964265ef137211563eaa20bc174de863c6 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Wed, 9 Aug 2023 10:58:23 -0400 Subject: [PATCH 07/14] re-add the tick marks --- dashboard/src/drawing/pie_chart.rs | 2 +- dashboard/src/main.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dashboard/src/drawing/pie_chart.rs b/dashboard/src/drawing/pie_chart.rs index 8b9e97a..4d394d2 100644 --- a/dashboard/src/drawing/pie_chart.rs +++ b/dashboard/src/drawing/pie_chart.rs @@ -55,7 +55,7 @@ impl PieChart { pub fn draw(self, context: &Context) { println!("drawing: {} {}", self.width, self.height); - let radius = self.width.min(self.height) as f64 / 2. * 0.9; + let radius = self.width.min(self.height) as f64 / 2.; let center_x = (self.width / 2) as f64; let center_y = (self.height / 2) as f64; diff --git a/dashboard/src/main.rs b/dashboard/src/main.rs index 111feb8..ecf1ec7 100644 --- a/dashboard/src/main.rs +++ b/dashboard/src/main.rs @@ -252,7 +252,7 @@ impl TransitClock { 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.; + 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(); @@ -280,8 +280,8 @@ impl TransitClock { context.set_source_rgb(0., 0., 0.); context.translate(center_x, center_y); context.rotate(tick as f64 * (PI / 12.)); - context.move_to(center_x, center_y); - context.line_to(center_x + 10., center_y); + context.move_to(radius, 0.); + context.line_to(radius - 10., 0.); let _ = context.stroke(); context.identity_matrix(); }); -- 2.44.1 From 0078f1db273b0c78ad04c9c61a36e702d1cda986 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Wed, 9 Aug 2023 11:03:08 -0400 Subject: [PATCH 08/14] Improve the API of the pie chart --- dashboard/src/drawing/pie_chart.rs | 36 ++++++++++++++---------------- dashboard/src/main.rs | 6 ++--- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/dashboard/src/drawing/pie_chart.rs b/dashboard/src/drawing/pie_chart.rs index 4d394d2..4b0e7bb 100644 --- a/dashboard/src/drawing/pie_chart.rs +++ b/dashboard/src/drawing/pie_chart.rs @@ -1,4 +1,4 @@ -use cairo::{Context, Format, ImageSurface}; +use cairo::Context; use std::f64::consts::PI; #[derive(Clone, Debug)] @@ -18,8 +18,9 @@ pub struct Wedge { pub struct PieChart { rotation: f64, wedges: Vec, - width: i32, - height: i32, + center_x: f64, + center_y: f64, + radius: f64, } impl PieChart { @@ -27,8 +28,9 @@ impl PieChart { Self { rotation: 0., wedges: vec![], - width: 0, - height: 0, + center_x: 0., + center_y: 0., + radius: 0., } } @@ -43,22 +45,18 @@ impl PieChart { self } - pub fn width(mut self, width: i32) -> Self { - self.width = width; + pub fn center(mut self, center_x: f64, center_y: f64) -> Self { + self.center_x = center_x; + self.center_y = center_y; self } - pub fn height(mut self, height: i32) -> Self { - self.height = height; + pub fn radius(mut self, radius: f64) -> Self { + self.radius = radius; self } pub fn draw(self, context: &Context) { - println!("drawing: {} {}", self.width, self.height); - let radius = self.width.min(self.height) as f64 / 2.; - let center_x = (self.width / 2) as f64; - let center_y = (self.height / 2) as f64; - context.set_source_rgba(0., 0., 0., 0.); let _ = context.paint(); @@ -80,12 +78,12 @@ impl PieChart { end_angle + self.rotation, self.rotation ); - context.move_to(center_x, center_y); + context.move_to(self.center_x, self.center_y); context.set_source_rgb(color.r, color.g, color.b); context.arc( - center_x, - center_y, - radius, + self.center_x, + self.center_y, + self.radius, start_angle + self.rotation, end_angle + self.rotation, ); @@ -94,7 +92,7 @@ impl PieChart { ); context.set_source_rgb(1., 1., 1.); - context.arc(center_x, center_y, radius, 0., 2. * PI); + context.arc(self.center_x, self.center_y, self.radius, 0., 2. * PI); let _ = context.stroke(); } } diff --git a/dashboard/src/main.rs b/dashboard/src/main.rs index ecf1ec7..88fc830 100644 --- a/dashboard/src/main.rs +++ b/dashboard/src/main.rs @@ -259,8 +259,8 @@ impl TransitClock { let sunset = info.sunset - NaiveTime::from_hms_opt(0, 0, 0).unwrap(); PieChart::new() - .width(width) - .height(height) + .center(center_x, center_y) + .radius(radius) .rotation(-PI / 2.) .wedges( vec![Wedge { @@ -280,7 +280,7 @@ impl TransitClock { context.set_source_rgb(0., 0., 0.); context.translate(center_x, center_y); context.rotate(tick as f64 * (PI / 12.)); - context.move_to(radius, 0.); + context.move_to(radius - 5., 0.); context.line_to(radius - 10., 0.); let _ = context.stroke(); context.identity_matrix(); -- 2.44.1 From 4714a06daad1d397c6c8ee420d8d1f9ef22e58bf Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Wed, 9 Aug 2023 12:53:14 -0400 Subject: [PATCH 09/14] Make the application responsive to time passing --- dashboard/src/main.rs | 192 ++++++++++++++++++++++++++++++------------ 1 file changed, 139 insertions(+), 53 deletions(-) diff --git a/dashboard/src/main.rs b/dashboard/src/main.rs index 88fc830..4207ecf 100644 --- a/dashboard/src/main.rs +++ b/dashboard/src/main.rs @@ -5,11 +5,19 @@ extern crate serde_derive; extern crate lazy_static; */ -use chrono::{Datelike, Duration, NaiveTime}; -use glib::Object; +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}; +use std::{ + cell::RefCell, + env, + f64::consts::PI, + ops::Deref, + rc::Rc, + sync::{Arc, RwLock}, +}; mod drawing; use drawing::{Color, PieChart, Wedge}; @@ -19,7 +27,7 @@ use geo_types::{Latitude, Longitude}; */ mod soluna_client; -use soluna_client::{LunarPhase, SunMoon}; +use soluna_client::{SolunaClient, SunMoon}; /* mod solstices; @@ -169,8 +177,37 @@ async fn page( } */ -#[derive(Default)] -pub struct DatePrivate {} +#[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 { @@ -197,19 +234,27 @@ impl Date { s.set_margin_start(8); s.set_margin_end(8); - let dt = IFC::from(chrono::Local::now().date_naive().with_year(12023).unwrap()); - s.append(>k::Label::new(Some( - format!( - "{:?}, {:?} {}, {}", - dt.weekday(), - dt.month(), - dt.day(), - dt.year() - ) - .as_ref(), - ))); + 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 { @@ -240,13 +285,11 @@ glib::wrapper! { } impl TransitClock { - pub fn new(sun_moon_info: SunMoon) -> Self { + pub fn new() -> Self { let s: Self = Object::builder().build(); s.set_width_request(500); s.set_height_request(500); - *s.imp().info.borrow_mut() = Some(sun_moon_info); - s.set_draw_func({ let s = s.clone(); move |_, context, width, height| { @@ -291,6 +334,11 @@ impl TransitClock { s } + + pub fn update_transit(&self, transit_info: SunMoon) { + *self.imp().info.borrow_mut() = Some(transit_info); + self.queue_draw(); + } } pub fn main() { @@ -299,45 +347,83 @@ pub fn main() { .resource_base_path("/com/luminescent-dreams/dashboard") .build(); - app.connect_activate( - // let runtime = runtime.clone(); - |app| { - let window = gtk::ApplicationWindow::new(app); - window.present(); + let latitude = Latitude::from(41.78); + let longitude = Longitude::from(-71.41); - let layout = gtk::Box::builder() - .orientation(Orientation::Vertical) - .hexpand(true) - .vexpand(true) - .build(); - let date = Date::new(); - layout.append(&date); - - let button = gtk::Button::builder() - .label("Text") - .margin_bottom(8) - .margin_top(8) - .margin_start(8) - .margin_end(8) - .build(); - layout.append(&button); - - let day = TransitClock::new(SunMoon { - sunrise: NaiveTime::from_hms_opt(7, 0, 0).unwrap(), - sunset: NaiveTime::from_hms_opt(22, 0, 0).unwrap(), - moonrise: NaiveTime::from_hms_opt(15, 0, 0), - moonset: NaiveTime::from_hms_opt(5, 0, 0), - moon_phase: LunarPhase::WaningCrescent, - }); - layout.append(&day); - - window.set_child(Some(&layout)); - }, + 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()); -- 2.44.1 From e2d3875587ed984a065a946c25caf3a7cf7a4b2a Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Wed, 9 Aug 2023 13:15:32 -0400 Subject: [PATCH 10/14] Extract components into a subcrate --- dashboard/src/components/date.rs | 69 ++++++ dashboard/src/components/mod.rs | 5 + dashboard/src/components/transit_clock.rs | 92 +++++++ dashboard/src/main.rs | 281 +--------------------- 4 files changed, 171 insertions(+), 276 deletions(-) create mode 100644 dashboard/src/components/date.rs create mode 100644 dashboard/src/components/mod.rs create mode 100644 dashboard/src/components/transit_clock.rs diff --git a/dashboard/src/components/date.rs b/dashboard/src/components/date.rs new file mode 100644 index 0000000..3df0e39 --- /dev/null +++ b/dashboard/src/components/date.rs @@ -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>, + 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 { + 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() + )); + } +} diff --git a/dashboard/src/components/mod.rs b/dashboard/src/components/mod.rs new file mode 100644 index 0000000..a011020 --- /dev/null +++ b/dashboard/src/components/mod.rs @@ -0,0 +1,5 @@ +mod date; +pub use date::Date; + +mod transit_clock; +pub use transit_clock::TransitClock; diff --git a/dashboard/src/components/transit_clock.rs b/dashboard/src/components/transit_clock.rs new file mode 100644 index 0000000..24a0d5b --- /dev/null +++ b/dashboard/src/components/transit_clock.rs @@ -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>>, +} + +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(); + } +} diff --git a/dashboard/src/main.rs b/dashboard/src/main.rs index 4207ecf..3ed36a5 100644 --- a/dashboard/src/main.rs +++ b/dashboard/src/main.rs @@ -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, 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), @@ -193,154 +70,6 @@ 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") -- 2.44.1 From 5e2b7fbc9989c09f6bee7ba759022d4e6d01c54b Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Wed, 9 Aug 2023 13:54:56 -0400 Subject: [PATCH 11/14] Extract an application window and encapsulate the entire UI --- dashboard/src/app_window.rs | 44 +++++++++++++++++++++++++++++++++++ dashboard/src/main.rs | 46 +++++++++++++------------------------ dashboard/src/solstices.rs | 4 ++-- dashboard/src/types.rs | 8 +++++++ 4 files changed, 70 insertions(+), 32 deletions(-) create mode 100644 dashboard/src/app_window.rs create mode 100644 dashboard/src/types.rs diff --git a/dashboard/src/app_window.rs b/dashboard/src/app_window.rs new file mode 100644 index 0000000..dbe9979 --- /dev/null +++ b/dashboard/src/app_window.rs @@ -0,0 +1,44 @@ +use crate::{ + components::{Date, TransitClock}, + types::State, +}; +use gtk::prelude::*; + +#[derive(Clone)] +pub struct ApplicationWindow { + pub window: gtk::ApplicationWindow, + pub date_label: Date, + pub transit_clock: TransitClock, +} + +impl ApplicationWindow { + pub fn new(app: >k::Application) -> Self { + let window = gtk::ApplicationWindow::new(app); + let date_label = Date::new(); + let transit_clock = TransitClock::new(); + + let layout = gtk::Box::builder() + .orientation(gtk::Orientation::Vertical) + .hexpand(true) + .vexpand(true) + .build(); + + layout.append(&date_label); + layout.append(&transit_clock); + + window.set_child(Some(&layout)); + + Self { + window, + date_label, + transit_clock, + } + } + + pub fn update_state(&self, state: State) { + self.date_label.update_date(state.date); + if let Some(transit) = state.transit { + self.transit_clock.update_transit(transit); + } + } +} diff --git a/dashboard/src/main.rs b/dashboard/src/main.rs index 3ed36a5..7b5ebd6 100644 --- a/dashboard/src/main.rs +++ b/dashboard/src/main.rs @@ -8,6 +8,9 @@ use std::{ sync::{Arc, RwLock}, }; +mod app_window; +use app_window::ApplicationWindow; + mod components; use components::{Date, TransitClock}; @@ -16,11 +19,13 @@ mod drawing; mod soluna_client; use soluna_client::{SolunaClient, SunMoon}; -/* mod solstices; use solstices::EVENTS; -*/ +mod types; +use types::State; + +/* const EO_TEXT: &'static str = " day = {$day -> *[Sunday] Dimanĉo @@ -53,18 +58,13 @@ summer_solstice = Somera Solstico autumn_equinox = Aŭtuna Ekvinokso winter_solstice = Vintra Solstico "; +*/ #[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>>>, @@ -118,31 +118,17 @@ pub fn main() { *core.tx.write().unwrap() = Some(gtk_tx); - let window = gtk::ApplicationWindow::new(app); - window.present(); + let window = ApplicationWindow::new(app); + window.window.present(); - let layout = gtk::Box::builder() - .orientation(Orientation::Vertical) - .hexpand(true) - .vexpand(true) - .build(); - let date_label = Date::new(); - layout.append(&date_label); + gtk_rx.attach(None, { + let window = window.clone(); + move |msg| { + let Message::Refresh(state) = msg; + ApplicationWindow::update_state(&window, state); - 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) } - - Continue(true) }); std::thread::spawn(move || {}); diff --git a/dashboard/src/solstices.rs b/dashboard/src/solstices.rs index 048ece7..210be72 100644 --- a/dashboard/src/solstices.rs +++ b/dashboard/src/solstices.rs @@ -1,7 +1,7 @@ use chrono; use chrono::prelude::*; +use lazy_static::lazy_static; use serde_derive::{Deserialize, Serialize}; -use serde_json; use std::collections::HashMap; // http://astropixels.com/ephemeris/soleq2001.html @@ -133,7 +133,7 @@ fn parse_events() -> Vec> { pub struct Solstices(HashMap); impl Solstices { - pub fn akiru(&self, year: i32) -> Option { + pub fn acquire(&self, year: i32) -> Option { self.0.get(&year).map(|c| c.clone()) } diff --git a/dashboard/src/types.rs b/dashboard/src/types.rs new file mode 100644 index 0000000..d82f9e6 --- /dev/null +++ b/dashboard/src/types.rs @@ -0,0 +1,8 @@ +use crate::soluna_client::SunMoon; +use ifc::IFC; + +#[derive(Clone, Debug)] +pub struct State { + pub date: IFC, + pub transit: Option, +} -- 2.44.1 From 1eaa7746fff026c849815626e3b4d389e3f22988 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Wed, 9 Aug 2023 14:02:20 -0400 Subject: [PATCH 12/14] Add equinox/solstice information --- dashboard/src/app_window.rs | 18 ++++++++++++++++-- dashboard/src/main.rs | 6 ++++-- dashboard/src/types.rs | 3 ++- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/dashboard/src/app_window.rs b/dashboard/src/app_window.rs index dbe9979..66c5dc1 100644 --- a/dashboard/src/app_window.rs +++ b/dashboard/src/app_window.rs @@ -8,14 +8,13 @@ use gtk::prelude::*; pub struct ApplicationWindow { pub window: gtk::ApplicationWindow, pub date_label: Date, + pub next_event: gtk::Label, pub transit_clock: TransitClock, } impl ApplicationWindow { pub fn new(app: >k::Application) -> Self { let window = gtk::ApplicationWindow::new(app); - let date_label = Date::new(); - let transit_clock = TransitClock::new(); let layout = gtk::Box::builder() .orientation(gtk::Orientation::Vertical) @@ -23,7 +22,20 @@ impl ApplicationWindow { .vexpand(true) .build(); + let date_label = Date::new(); layout.append(&date_label); + + let next_event = gtk::Label::builder() + .margin_bottom(8) + .margin_top(8) + .margin_start(8) + .margin_end(8) + .build(); + next_event.add_css_class("card"); + next_event.add_css_class("activatable"); + layout.append(&next_event); + + let transit_clock = TransitClock::new(); layout.append(&transit_clock); window.set_child(Some(&layout)); @@ -31,12 +43,14 @@ impl ApplicationWindow { Self { window, date_label, + next_event, transit_clock, } } pub fn update_state(&self, state: State) { self.date_label.update_date(state.date); + self.next_event.set_text(&format!("{:?}", state.next_event)); if let Some(transit) = state.transit { self.transit_clock.update_transit(transit); } diff --git a/dashboard/src/main.rs b/dashboard/src/main.rs index 7b5ebd6..c2724e8 100644 --- a/dashboard/src/main.rs +++ b/dashboard/src/main.rs @@ -1,4 +1,4 @@ -use chrono::{Datelike, Local}; +use chrono::{Datelike, Local, Utc}; use geo_types::{Latitude, Longitude}; use glib::Sender; use gtk::{prelude::*, subclass::prelude::*, Orientation}; @@ -99,8 +99,10 @@ pub fn main() { .request(latitude, longitude, Local::now()) .await; + let now = Local::now(); let state = State { - date: IFC::from(Local::now().date_naive().with_year(12023).unwrap()), + date: IFC::from(now.date_naive().with_year(12023).unwrap()), + next_event: EVENTS.next_event(now.with_timezone(&Utc)).unwrap(), transit: Some(transit), }; loop { diff --git a/dashboard/src/types.rs b/dashboard/src/types.rs index d82f9e6..045d448 100644 --- a/dashboard/src/types.rs +++ b/dashboard/src/types.rs @@ -1,8 +1,9 @@ -use crate::soluna_client::SunMoon; +use crate::{solstices::Event, soluna_client::SunMoon}; use ifc::IFC; #[derive(Clone, Debug)] pub struct State { pub date: IFC, + pub next_event: Event, pub transit: Option, } -- 2.44.1 From ab97e14ad76fe5ffe89ed526c6be411ddabc71a3 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Wed, 9 Aug 2023 14:04:48 -0400 Subject: [PATCH 13/14] Fix warnings --- dashboard/src/drawing/pie_chart.rs | 10 ---------- dashboard/src/main.rs | 5 ++--- dashboard/src/solstices.rs | 4 +++- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/dashboard/src/drawing/pie_chart.rs b/dashboard/src/drawing/pie_chart.rs index 4b0e7bb..918deb5 100644 --- a/dashboard/src/drawing/pie_chart.rs +++ b/dashboard/src/drawing/pie_chart.rs @@ -68,16 +68,6 @@ impl PieChart { end_angle, color, }| { - println!( - "wedge: {:.02?} {:.02?} {:.02?}", - start_angle, end_angle, color - ); - println!( - "wedge: {:.02?} {:.02?} [{:.02?}]", - start_angle + self.rotation, - end_angle + self.rotation, - self.rotation - ); context.move_to(self.center_x, self.center_y); context.set_source_rgb(color.r, color.g, color.b); context.arc( diff --git a/dashboard/src/main.rs b/dashboard/src/main.rs index c2724e8..fee8214 100644 --- a/dashboard/src/main.rs +++ b/dashboard/src/main.rs @@ -1,7 +1,7 @@ use chrono::{Datelike, Local, Utc}; use geo_types::{Latitude, Longitude}; use glib::Sender; -use gtk::{prelude::*, subclass::prelude::*, Orientation}; +use gtk::prelude::*; use ifc::IFC; use std::{ env, @@ -12,12 +12,11 @@ mod app_window; use app_window::ApplicationWindow; mod components; -use components::{Date, TransitClock}; mod drawing; mod soluna_client; -use soluna_client::{SolunaClient, SunMoon}; +use soluna_client::SolunaClient; mod solstices; use solstices::EVENTS; diff --git a/dashboard/src/solstices.rs b/dashboard/src/solstices.rs index 210be72..6a0943b 100644 --- a/dashboard/src/solstices.rs +++ b/dashboard/src/solstices.rs @@ -133,9 +133,11 @@ fn parse_events() -> Vec> { pub struct Solstices(HashMap); impl Solstices { + /* pub fn acquire(&self, year: i32) -> Option { self.0.get(&year).map(|c| c.clone()) } + */ pub fn next_event(&self, date: chrono::DateTime) -> Option { let year_events = self.0.get(&date.year()); @@ -152,7 +154,7 @@ impl Solstices { } else { self.0 .get(&(date.year() + 1)) - .map(|events| Event::SpringEquinox(year_events.spring_equinox.clone())) + .map(|_| Event::SpringEquinox(year_events.spring_equinox.clone())) } } None => None, -- 2.44.1 From bfd23bff5bcd61a7eb9805569399dcbdf665fb92 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Wed, 9 Aug 2023 14:11:10 -0400 Subject: [PATCH 14/14] Cleanups --- Cargo.lock | 34 ---------------------------------- dashboard/Cargo.toml | 3 +-- dashboard/src/main.rs | 41 ----------------------------------------- 3 files changed, 1 insertion(+), 77 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7c88714..0679578 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -341,7 +341,6 @@ dependencies = [ "gtk4", "ifc", "lazy_static", - "libadwaita", "memorycache", "reqwest", "serde", @@ -1264,39 +1263,6 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" -[[package]] -name = "libadwaita" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ab9c0843f9f23ff25634df2743690c3a1faffe0a190e60c490878517eb81abf" -dependencies = [ - "bitflags 1.3.2", - "gdk-pixbuf", - "gdk4", - "gio", - "glib", - "gtk4", - "libadwaita-sys", - "libc", - "pango", -] - -[[package]] -name = "libadwaita-sys" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4231cb2499a9f0c4cdfa4885414b33e39901ddcac61150bc0bb4ff8a57ede404" -dependencies = [ - "gdk4-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "gtk4-sys", - "libc", - "pango-sys", - "system-deps", -] - [[package]] name = "libc" version = "0.2.147" diff --git a/dashboard/Cargo.toml b/dashboard/Cargo.toml index f4b1434..56d2f49 100644 --- a/dashboard/Cargo.toml +++ b/dashboard/Cargo.toml @@ -6,7 +6,6 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -adw = { package = "libadwaita", version = "0.4", features = ["v1_3"] } cairo-rs = { version = "0.17" } chrono = { version = "0.4", features = ["serde"] } fluent-ergonomics = { path = "../fluent-ergonomics/" } @@ -18,7 +17,7 @@ glib = { version = "0.17" } gtk = { version = "0.6", package = "gtk4" } ifc = { path = "../ifc/" } lazy_static = { version = "1.4" } -memorycache = { path = "../memorycache" } +memorycache = { path = "../memorycache/" } reqwest = { version = "0.11", features = ["json"] } serde_derive = { version = "1" } serde_json = { version = "1" } diff --git a/dashboard/src/main.rs b/dashboard/src/main.rs index fee8214..3eaa100 100644 --- a/dashboard/src/main.rs +++ b/dashboard/src/main.rs @@ -139,45 +139,4 @@ pub fn main() { 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 - ); - */ } -- 2.44.1