Compare commits

..

No commits in common. "bed75dc669f870a7efb2e4744bd76e23a3eeacaf" and "d9a10c76b0296b11aa556794e0fe1a60a54d5c2d" have entirely different histories.

View File

@ -1,23 +1,15 @@
use cairo::{ use cairo::{Context, FontFace, Format, ImageSurface, LineCap, Pattern};
Context, FontSlant, FontWeight, Format, ImageSurface, LineCap, LinearGradient, Pattern,
TextExtents,
};
use glib::Object; use glib::Object;
use gtk::{prelude::*, subclass::prelude::*}; use gtk::{prelude::*, subclass::prelude::*};
use std::{ use std::{cell::RefCell, rc::Rc};
cell::RefCell,
rc::Rc,
time::{Duration, Instant},
};
const WIDTH: i32 = 1920; const WIDTH: i32 = 800;
const HEIGHT: i32 = 1280; const HEIGHT: i32 = 800;
pub struct SplashPrivate { pub struct SplashPrivate {
text: Rc<RefCell<String>>, text: Rc<RefCell<String>>,
time: Rc<RefCell<Duration>>, time: Rc<RefCell<String>>,
background: Rc<RefCell<Pattern>>, background: Rc<RefCell<Pattern>>,
time_extents: Rc<RefCell<Option<TextExtents>>>,
} }
impl SplashPrivate { impl SplashPrivate {
@ -25,7 +17,7 @@ impl SplashPrivate {
*self.text.borrow_mut() = text; *self.text.borrow_mut() = text;
} }
fn set_time(&self, time: Duration) { fn set_time(&self, time: String) {
*self.time.borrow_mut() = time; *self.time.borrow_mut() = time;
} }
@ -36,83 +28,24 @@ impl SplashPrivate {
context.set_source_rgb(0., 0., 0.); context.set_source_rgb(0., 0., 0.);
let _ = context.paint(); let _ = context.paint();
context.select_font_face("Alegreya Sans SC", FontSlant::Normal, FontWeight::Bold); context.set_font_size(64.);
context.set_font_size(128.);
let center_x = WIDTH as f64 / 2.;
let center_y = HEIGHT as f64 / 2.;
let title_extents = context.text_extents(&self.text.borrow()).unwrap(); let title_extents = context.text_extents(&self.text.borrow()).unwrap();
let title_width = title_extents.width();
let title_height = title_extents.height();
{ context.set_source_rgb(0.7, 0., 1.);
let start_length = center_x - title_width / 2. - title_height - 20.; context.set_line_width(5.);
context.set_line_cap(LineCap::Round);
let title_cutout = AsymLineCutout { AsymLineReturn {
orientation: gtk::Orientation::Horizontal, orientation: gtk::Orientation::Horizontal,
start_x: 20., start_x: 20.,
start_y: center_y - 20. - title_height / 2., start_y: 20.,
start_length, start_length: 200.,
total_length: WIDTH as f64 - 120., center_length: title_extents.y_advance(),
cutout_length: title_width, end_length: 100.,
height: title_height, width: title_extents.height(),
invert: false,
};
context.set_line_cap(LineCap::Round);
context.set_source_rgb(0.7, 0., 1.);
context.set_line_width(2.);
title_cutout.draw(&context);
let _ = context.stroke();
}
{
let title_baseline_x = center_x - title_width / 2.;
let title_baseline_y = center_y - 20.;
let gradient = LinearGradient::new(
title_baseline_x,
title_baseline_y - title_height,
title_baseline_x,
title_baseline_y,
);
gradient.add_color_stop_rgb(0.2, 0.7, 0.0, 1.0);
gradient.add_color_stop_rgb(0.8, 0.2, 0.0, 1.0);
context.move_to(title_baseline_x, title_baseline_y);
let _ = context.set_source(gradient);
let _ = context.show_text(&self.text.borrow());
}
{
context.set_source_rgb(0.7, 0., 1.);
AsymLine {
orientation: gtk::Orientation::Horizontal,
start_x: 100.,
start_y: HEIGHT as f64 / 2. + 100.,
start_length: 400.,
height: 50.,
total_length: 650.,
invert: true,
}
.draw(&context);
let _ = context.stroke();
}
{
context.set_source_rgb(0.7, 0., 1.);
AsymLine {
orientation: gtk::Orientation::Horizontal,
start_x: WIDTH as f64 / 2. + 100.,
start_y: HEIGHT as f64 / 2. + 200.,
start_length: 600.,
height: 50.,
total_length: 650.,
invert: false,
}
.draw(&context);
let _ = context.stroke();
} }
.draw(&context);
let _ = context.stroke();
let background = context.pop_group().unwrap(); let background = context.pop_group().unwrap();
@ -137,9 +70,8 @@ impl ObjectSubclass for SplashPrivate {
SplashPrivate { SplashPrivate {
text: Rc::new(RefCell::new(String::from(""))), text: Rc::new(RefCell::new(String::from(""))),
time: Rc::new(RefCell::new(Duration::ZERO)), time: Rc::new(RefCell::new(String::from(""))),
background: Rc::new(RefCell::new(background)), background: Rc::new(RefCell::new(background)),
time_extents: Rc::new(RefCell::new(None)),
} }
} }
} }
@ -152,7 +84,7 @@ glib::wrapper! {
} }
impl Splash { impl Splash {
pub fn new(text: String, time: Duration) -> Self { pub fn new(text: String, time: String) -> Self {
let s: Self = Object::builder().build(); let s: Self = Object::builder().build();
s.set_width_request(WIDTH); s.set_width_request(WIDTH);
s.set_height_request(HEIGHT); s.set_height_request(HEIGHT);
@ -163,213 +95,88 @@ impl Splash {
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 background = s.imp().background.borrow(); let background = s.imp().background.borrow();
let _ = context.set_source(&*background); let _ = context.set_source(&*background);
let _ = context.paint(); let _ = context.paint();
let time = s.imp().time.borrow().clone();
let minutes = time.as_secs() / 60;
let seconds = time.as_secs() % 60;
let center_x = width as f64 / 2.;
let center_y = height as f64 / 2.;
{
context.select_font_face(
"Alegreya Sans SC",
FontSlant::Normal,
FontWeight::Bold,
);
context.set_font_size(128.);
let time = format!("{:02}' {:02}\"", minutes, seconds);
let time_extents = context.text_extents(&time).unwrap();
let mut saved_extents = s.imp().time_extents.borrow_mut();
if saved_extents.is_none() {
*saved_extents = Some(time_extents.clone());
}
let time_baseline_x = center_x - time_extents.width() / 2.;
let time_baseline_y = center_y + 100.;
let gradient = LinearGradient::new(
time_baseline_x,
time_baseline_y - time_extents.height(),
time_baseline_x,
time_baseline_y,
);
gradient.add_color_stop_rgb(0.2, 0.2, 0.0, 1.0);
gradient.add_color_stop_rgb(0.8, 0.7, 0.0, 1.0);
context.move_to(time_baseline_x, time_baseline_y);
let _ = context.set_source(gradient);
let _ = context.show_text(&time);
};
match *s.imp().time_extents.borrow() {
Some(extents) => {
context.set_source_rgb(0.7, 0.0, 1.0);
let time_meter = SlashMeter {
orientation: gtk::Orientation::Horizontal,
start_x: center_x + extents.width() / 2. + 50.,
start_y: center_y + 100.,
count: 5,
fill_count: minutes as u8,
height: 60.,
length: 100.,
};
time_meter.draw(&context);
}
None => {}
}
} }
}); });
s s
} }
pub fn set_time(&self, time: Duration) {
self.imp().set_time(time);
self.queue_draw();
}
} }
struct AsymLineCutout { struct AsymLineReturn {
orientation: gtk::Orientation, orientation: gtk::Orientation,
start_x: f64, start_x: f64,
start_y: f64, start_y: f64,
start_length: f64, start_length: f64,
total_length: f64, center_length: f64,
cutout_length: f64, end_length: f64,
height: f64, width: f64,
invert: bool,
} }
impl AsymLineCutout { impl AsymLineReturn {
fn draw(&self, context: &Context) { fn draw(&self, context: &Context) {
let dodge = if self.invert {
self.height
} else {
-self.height
};
match self.orientation { match self.orientation {
gtk::Orientation::Horizontal => { gtk::Orientation::Horizontal => {
context.move_to(self.start_x, self.start_y); context.move_to(self.start_x, self.start_y);
context.line_to(self.start_x + self.start_length, self.start_y); context.line_to(self.start_x + self.start_length, self.start_y);
context.line_to( context.line_to(
self.start_x + self.start_length + self.height, self.start_x + self.start_length + self.width.abs(),
self.start_y + dodge, self.start_y + self.width,
); );
context.line_to( context.line_to(
self.start_x + self.start_length + self.height + self.cutout_length, self.start_x + self.start_length + self.width.abs() + self.center_length,
self.start_y + dodge, self.start_y + self.width,
); );
context.line_to( context.line_to(
self.start_x self.start_x
+ self.start_length + self.start_length
+ self.height + self.width.abs()
+ self.cutout_length + self.center_length
+ (self.height / 2.), + (self.width.abs() / 2.),
self.start_y + dodge / 2., self.start_y + self.width / 2.,
);
context.line_to(
self.start_x
+ self.start_length
+ self.width.abs()
+ self.center_length
+ (self.width.abs() / 2.)
+ self.end_length,
self.start_y + self.width / 2.,
); );
context.line_to(self.total_length, self.start_y + dodge / 2.);
} }
gtk::Orientation::Vertical => { gtk::Orientation::Vertical => {
context.move_to(self.start_x, self.start_y); context.move_to(self.start_x, self.start_y);
context.line_to(self.start_x, self.start_y + self.start_length); context.line_to(self.start_x, self.start_y + self.start_length);
context.line_to( context.line_to(
self.start_x + dodge, self.start_x + self.width,
self.start_y + self.start_length + self.height, self.start_y + self.start_length + self.width.abs(),
); );
context.line_to( context.line_to(
self.start_x + dodge, self.start_x + self.width,
self.start_y + self.start_length + self.height + self.cutout_length, self.start_y + self.start_length + self.width.abs() + self.center_length,
); );
context.line_to( context.line_to(
self.start_x + dodge / 2., self.start_x + self.width / 2.,
self.start_y self.start_y
+ self.start_length + self.start_length
+ self.height + self.width.abs()
+ self.cutout_length + self.center_length
+ (self.height / 2.), + (self.width.abs() / 2.),
); );
context.line_to(self.start_x + dodge / 2., self.total_length);
}
_ => panic!("unknown orientation"),
}
}
}
struct AsymLine {
orientation: gtk::Orientation,
start_x: f64,
start_y: f64,
start_length: f64,
height: f64,
total_length: f64,
invert: bool,
}
impl AsymLine {
fn draw(&self, context: &Context) {
let dodge = if self.invert {
self.height
} else {
-self.height
};
match self.orientation {
gtk::Orientation::Horizontal => {
context.move_to(self.start_x, self.start_y);
context.line_to(self.start_x + self.start_length, self.start_y);
context.line_to( context.line_to(
self.start_x + self.start_length + self.height, self.start_x + self.width / 2.,
self.start_y + dodge, self.start_y
+ self.start_length
+ self.width.abs()
+ self.center_length
+ (self.width.abs() / 2.)
+ self.end_length,
); );
context.line_to(self.start_x + self.total_length, self.start_y + dodge);
} }
gtk::Orientation::Vertical => {}
_ => panic!("unknown orientation"),
}
}
}
struct SlashMeter {
orientation: gtk::Orientation,
start_x: f64,
start_y: f64,
count: u8,
fill_count: u8,
height: f64,
length: f64,
}
impl SlashMeter {
fn draw(&self, context: &Context) {
match self.orientation {
gtk::Orientation::Horizontal => {
let angle: f64 = 0.8;
let run = self.height / angle.tan();
let width = self.length as f64 / (self.count as f64 * 2.);
for c in 0..self.count {
context.set_line_width(1.);
let start_x = self.start_x + c as f64 * width * 2.;
context.move_to(start_x, self.start_y);
context.line_to(start_x + run, self.start_y - self.height);
context.line_to(start_x + run + width, self.start_y - self.height);
context.line_to(start_x + width, self.start_y);
context.line_to(start_x, self.start_y);
if c < self.fill_count {
let _ = context.fill();
} else {
let _ = context.stroke();
}
}
}
gtk::Orientation::Vertical => {}
_ => panic!("unknown orientation"), _ => panic!("unknown orientation"),
} }
} }
@ -381,31 +188,10 @@ fn main() {
.build(); .build();
app.connect_activate(move |app| { app.connect_activate(move |app| {
let (gtk_tx, gtk_rx) =
gtk::glib::MainContext::channel::<Duration>(gtk::glib::PRIORITY_DEFAULT);
let window = gtk::ApplicationWindow::new(app); let window = gtk::ApplicationWindow::new(app);
window.present(); window.present();
let mut countdown = Duration::from_secs(5 * 60); window.set_child(Some(&Splash::new("Kifu".to_owned(), "4:23".to_owned())));
let mut next_tick = Instant::now() + Duration::from_secs(1);
let splash = Splash::new("GTK Kifu".to_owned(), countdown.clone());
window.set_child(Some(&splash));
gtk_rx.attach(None, move |time| {
splash.set_time(time);
Continue(true)
});
std::thread::spawn(move || loop {
std::thread::sleep(Duration::from_secs(1));
if Instant::now() >= next_tick {
countdown = countdown - Duration::from_secs(1);
let _ = gtk_tx.send(countdown);
next_tick = next_tick + Duration::from_secs(1);
}
});
}); });
app.run(); app.run();