Totally rewrite dashboard as a GTK application #54

Merged
savanni merged 14 commits from dashboard-gtk into main 2023-08-09 18:14:01 +00:00
4 changed files with 147 additions and 170 deletions
Showing only changes of commit 28192ff8d7 - Show all commits

View File

@ -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<Wedge>,
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<Item = Wedge>) -> Self {
let mut wedges: Vec<Wedge> = 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();
}
}

View File

@ -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<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())),
}
}
}
@ -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<TransitClockPrivate>) @extends gtk::Box, gtk::Widget;
pub struct TransitClock(ObjectSubclass<TransitClockPrivate>) @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
}
}

View File

@ -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<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)
}
}