Totally rewrite dashboard as a GTK application #54
|
@ -341,6 +341,7 @@ dependencies = [
|
||||||
"gtk4",
|
"gtk4",
|
||||||
"ifc",
|
"ifc",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
"libadwaita",
|
||||||
"memorycache",
|
"memorycache",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -1263,6 +1264,39 @@ version = "0.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
|
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]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.147"
|
version = "0.2.147"
|
||||||
|
|
|
@ -6,6 +6,7 @@ edition = "2018"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
adw = { package = "libadwaita", version = "0.4", features = ["v1_3"] }
|
||||||
cairo-rs = { version = "0.17" }
|
cairo-rs = { version = "0.17" }
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
fluent-ergonomics = { path = "../fluent-ergonomics/" }
|
fluent-ergonomics = { path = "../fluent-ergonomics/" }
|
||||||
|
|
|
@ -9,7 +9,10 @@ use chrono::{Datelike, Duration, NaiveTime};
|
||||||
use glib::Object;
|
use glib::Object;
|
||||||
use gtk::{prelude::*, subclass::prelude::*, Orientation};
|
use gtk::{prelude::*, subclass::prelude::*, Orientation};
|
||||||
use ifc::IFC;
|
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};
|
use geo_types::{Latitude, Longitude};
|
||||||
|
@ -187,6 +190,12 @@ glib::wrapper! {
|
||||||
impl Date {
|
impl Date {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
let s: Self = Object::builder().build();
|
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());
|
let dt = IFC::from(chrono::Local::now().date_naive().with_year(12023).unwrap());
|
||||||
s.append(>k::Label::new(Some(
|
s.append(>k::Label::new(Some(
|
||||||
|
@ -203,56 +212,79 @@ impl Date {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
pub struct TransitClockPrivate {
|
||||||
pub struct ClockPrivate {
|
|
||||||
info: Rc<RefCell<Option<SunMoon>>>,
|
info: Rc<RefCell<Option<SunMoon>>>,
|
||||||
|
chart: Rc<RefCell<PieChart>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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]
|
#[glib::object_subclass]
|
||||||
impl ObjectSubclass for ClockPrivate {
|
impl ObjectSubclass for TransitClockPrivate {
|
||||||
const NAME: &'static str = "Clock";
|
const NAME: &'static str = "TransitClock";
|
||||||
type Type = Clock;
|
type Type = TransitClock;
|
||||||
type ParentType = gtk::DrawingArea;
|
type ParentType = gtk::Box;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for ClockPrivate {}
|
impl ObjectImpl for TransitClockPrivate {}
|
||||||
impl WidgetImpl for ClockPrivate {}
|
impl WidgetImpl for TransitClockPrivate {}
|
||||||
impl DrawingAreaImpl for ClockPrivate {}
|
impl BoxImpl for TransitClockPrivate {}
|
||||||
|
|
||||||
glib::wrapper! {
|
glib::wrapper! {
|
||||||
pub struct Clock(ObjectSubclass<ClockPrivate>) @extends gtk::DrawingArea, gtk::Widget;
|
pub struct TransitClock(ObjectSubclass<TransitClockPrivate>) @extends gtk::Box, gtk::Widget;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clock {
|
impl TransitClock {
|
||||||
pub fn new(sun_moon_info: SunMoon) -> Self {
|
pub fn new(sun_moon_info: SunMoon) -> Self {
|
||||||
let s: Self = Object::builder().build();
|
let s: Self = Object::builder().build();
|
||||||
s.set_width_request(500);
|
s.set_width_request(500);
|
||||||
s.set_height_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.imp().info.borrow_mut() = Some(sun_moon_info);
|
||||||
|
|
||||||
|
/*
|
||||||
s.set_draw_func({
|
s.set_draw_func({
|
||||||
let s = s.clone();
|
let s = s.clone();
|
||||||
move |_, context, width, height| {
|
move |_, context, width, height| {
|
||||||
let info = s.imp().info.borrow();
|
let info = s.imp().info.borrow();
|
||||||
|
|
||||||
// context.set_source_rgb(240., 240., 240.);
|
context.set_source_rgba(0., 0., 0., 0.);
|
||||||
context.set_source_rgb(0., 0., 0.);
|
|
||||||
let _ = context.paint();
|
let _ = context.paint();
|
||||||
|
|
||||||
context.set_line_width(5.);
|
context.set_line_width(2.);
|
||||||
context.set_source_rgb(0.7, 0., 0.9);
|
context.set_source_rgb(0.7, 0., 0.9);
|
||||||
context.arc(width as f64 / 2., height as f64 / 2., 200., 0., PI * 2.);
|
context.arc(width as f64 / 2., height as f64 / 2., 200., 0., PI * 2.);
|
||||||
let _ = context.stroke();
|
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() {
|
if let Some(info) = info.deref() {
|
||||||
context.move_to(width as f64 / 2., height as f64 / 2.);
|
context.move_to(width as f64 / 2., height as f64 / 2.);
|
||||||
let full_day = Duration::days(1).num_seconds() as f64;
|
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);
|
context.arc(width as f64 / 2., height as f64 / 2., 200., start, end);
|
||||||
let _ = context.fill();
|
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
|
s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
pub struct AppPrivate {
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AppPrivate {
|
||||||
|
const NAME: &'static str = "App";
|
||||||
|
type Type = App;
|
||||||
|
type ParentType = adw::Application;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
/*
|
/*
|
||||||
let runtime = Arc::new(
|
let runtime = Arc::new(
|
||||||
|
@ -284,6 +337,7 @@ pub fn main() {
|
||||||
*/
|
*/
|
||||||
let app = gtk::Application::builder()
|
let app = gtk::Application::builder()
|
||||||
.application_id("com.luminescent-dreams.dashboard")
|
.application_id("com.luminescent-dreams.dashboard")
|
||||||
|
.resource_base_path("/com/luminescent-dreams/dashboard")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
app.connect_activate(
|
app.connect_activate(
|
||||||
|
@ -294,22 +348,36 @@ pub fn main() {
|
||||||
|
|
||||||
let layout = gtk::Box::builder()
|
let layout = gtk::Box::builder()
|
||||||
.orientation(Orientation::Vertical)
|
.orientation(Orientation::Vertical)
|
||||||
|
.hexpand(true)
|
||||||
|
.vexpand(true)
|
||||||
.build();
|
.build();
|
||||||
let date = Date::new();
|
let date = Date::new();
|
||||||
let day = Clock::new(SunMoon {
|
layout.append(&date);
|
||||||
sunrise: NaiveTime::from_hms_opt(6, 0, 0).unwrap(),
|
|
||||||
sunset: NaiveTime::from_hms_opt(0, 0, 0).unwrap(),
|
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),
|
moonrise: NaiveTime::from_hms_opt(15, 0, 0),
|
||||||
moonset: NaiveTime::from_hms_opt(5, 0, 0),
|
moonset: NaiveTime::from_hms_opt(5, 0, 0),
|
||||||
moon_phase: LunarPhase::WaningCrescent,
|
moon_phase: LunarPhase::WaningCrescent,
|
||||||
});
|
});
|
||||||
layout.append(&date);
|
|
||||||
layout.append(&day);
|
layout.append(&day);
|
||||||
|
|
||||||
window.set_child(Some(&layout));
|
window.set_child(Some(&layout));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
app.run();
|
let args: Vec<String> = env::args().collect();
|
||||||
|
ApplicationExtManual::run_with_args(&app, &args);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
let now = Local::now();
|
let now = Local::now();
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
mod pie_chart;
|
||||||
|
pub use pie_chart::{Color, PieChart, Wedge};
|
|
@ -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<RefCell<f64>>,
|
||||||
|
wedges: Rc<RefCell<Vec<Wedge>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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<PieChartPrivate>) @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<Wedge>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PieChartBuilder {
|
||||||
|
pub fn rotation(mut self, rotation: f64) -> Self {
|
||||||
|
self.rotation = rotation;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wedges(mut self, wedges: Vec<Wedge>) -> Self {
|
||||||
|
self.wedges = wedges;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self) -> PieChart {
|
||||||
|
PieChart::new(self)
|
||||||
|
}
|
||||||
|
}
|
|
@ -51,16 +51,16 @@
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1688392541,
|
"lastModified": 1691421349,
|
||||||
"narHash": "sha256-lHrKvEkCPTUO+7tPfjIcb7Trk6k31rz18vkyqmkeJfY=",
|
"narHash": "sha256-RRJyX0CUrs4uW4gMhd/X4rcDG8PTgaaCQM5rXEJOx6g=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "ea4c80b39be4c09702b0cb3b42eab59e2ba4f24b",
|
"rev": "011567f35433879aae5024fc6ec53f2a0568a6c4",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"id": "nixpkgs",
|
"id": "nixpkgs",
|
||||||
"ref": "nixos-22.11",
|
"ref": "nixos-23.05",
|
||||||
"type": "indirect"
|
"type": "indirect"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
description = "Lumenescent Dreams Tools";
|
description = "Lumenescent Dreams Tools";
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "nixpkgs/nixos-22.11";
|
nixpkgs.url = "nixpkgs/nixos-23.05";
|
||||||
unstable.url = "nixpkgs/nixos-unstable";
|
unstable.url = "nixpkgs/nixos-unstable";
|
||||||
pkgs-cargo2nix.url = "github:cargo2nix/cargo2nix";
|
pkgs-cargo2nix.url = "github:cargo2nix/cargo2nix";
|
||||||
typeshare.url = "github:1Password/typeshare";
|
typeshare.url = "github:1Password/typeshare";
|
||||||
|
|
Loading…
Reference in New Issue