Totally rewrite dashboard as a GTK application #54
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,8 +11,8 @@ use gtk::{prelude::*, subclass::prelude::*, Orientation};
|
||||||
use ifc::IFC;
|
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};
|
||||||
|
|
||||||
mod ui;
|
mod drawing;
|
||||||
use ui::{Color, PieChart, Wedge};
|
use drawing::{Color, PieChart, Wedge};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
use geo_types::{Latitude, Longitude};
|
use geo_types::{Latitude, Longitude};
|
||||||
|
@ -214,14 +214,12 @@ impl Date {
|
||||||
|
|
||||||
pub struct TransitClockPrivate {
|
pub struct TransitClockPrivate {
|
||||||
info: Rc<RefCell<Option<SunMoon>>>,
|
info: Rc<RefCell<Option<SunMoon>>>,
|
||||||
chart: Rc<RefCell<PieChart>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TransitClockPrivate {
|
impl Default for TransitClockPrivate {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
info: Rc::new(RefCell::new(None)),
|
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 {
|
impl ObjectSubclass for TransitClockPrivate {
|
||||||
const NAME: &'static str = "TransitClock";
|
const NAME: &'static str = "TransitClock";
|
||||||
type Type = TransitClock;
|
type Type = TransitClock;
|
||||||
type ParentType = gtk::Box;
|
type ParentType = gtk::DrawingArea;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for TransitClockPrivate {}
|
impl ObjectImpl for TransitClockPrivate {}
|
||||||
impl WidgetImpl for TransitClockPrivate {}
|
impl WidgetImpl for TransitClockPrivate {}
|
||||||
impl BoxImpl for TransitClockPrivate {}
|
impl DrawingAreaImpl for TransitClockPrivate {}
|
||||||
|
|
||||||
glib::wrapper! {
|
glib::wrapper! {
|
||||||
pub struct TransitClock(ObjectSubclass<TransitClockPrivate>) @extends gtk::Box, gtk::Widget;
|
pub struct TransitClock(ObjectSubclass<TransitClockPrivate>) @extends gtk::DrawingArea, gtk::Widget;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TransitClock {
|
impl TransitClock {
|
||||||
|
@ -247,30 +245,50 @@ impl TransitClock {
|
||||||
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({
|
||||||
|
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
|
s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue