Create a cyberpunk-style splash screen with a title and a countdown #39

Merged
savanni merged 11 commits from cyberpunk-display into main 2023-04-13 23:14:29 +00:00
1 changed files with 70 additions and 26 deletions
Showing only changes of commit e7bf84a744 - Show all commits

View File

@ -3,14 +3,18 @@ use cairo::{
}; };
use glib::Object; use glib::Object;
use gtk::{prelude::*, subclass::prelude::*}; use gtk::{prelude::*, subclass::prelude::*};
use std::{cell::RefCell, rc::Rc}; use std::{
cell::RefCell,
rc::Rc,
time::{Duration, Instant},
};
const WIDTH: i32 = 1920; const WIDTH: i32 = 1920;
const HEIGHT: i32 = 1280; const HEIGHT: i32 = 1280;
pub struct SplashPrivate { pub struct SplashPrivate {
text: Rc<RefCell<String>>, text: Rc<RefCell<String>>,
time: Rc<RefCell<String>>, time: Rc<RefCell<Duration>>,
background: Rc<RefCell<Pattern>>, background: Rc<RefCell<Pattern>>,
} }
@ -19,7 +23,7 @@ impl SplashPrivate {
*self.text.borrow_mut() = text; *self.text.borrow_mut() = text;
} }
fn set_time(&self, time: String) { fn set_time(&self, time: Duration) {
*self.time.borrow_mut() = time; *self.time.borrow_mut() = time;
} }
@ -108,25 +112,6 @@ impl SplashPrivate {
let _ = context.stroke(); let _ = context.stroke();
} }
{
context.set_font_size(128.);
let time_extents = context.text_extents(&self.time.borrow()).unwrap();
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(&self.time.borrow());
}
let background = context.pop_group().unwrap(); let background = context.pop_group().unwrap();
*self.background.borrow_mut() = background; *self.background.borrow_mut() = background;
@ -150,7 +135,7 @@ 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(String::from(""))), time: Rc::new(RefCell::new(Duration::ZERO)),
background: Rc::new(RefCell::new(background)), background: Rc::new(RefCell::new(background)),
} }
} }
@ -164,7 +149,7 @@ glib::wrapper! {
} }
impl Splash { impl Splash {
pub fn new(text: String, time: String) -> Self { pub fn new(text: String, time: Duration) -> 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);
@ -175,15 +160,53 @@ 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 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 = s.imp().time.borrow().clone();
let minutes = time.as_secs() / 60;
let seconds = time.as_secs() % 60;
let time = format!("{:02}' {:02}\"", minutes, seconds);
let time_extents = context.text_extents(&time).unwrap();
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);
}
} }
}); });
s s
} }
pub fn set_time(&self, time: Duration) {
self.imp().set_time(time);
self.queue_draw();
}
} }
struct AsymLineCutout { struct AsymLineCutout {
@ -291,10 +314,31 @@ 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();
window.set_child(Some(&Splash::new("GTK Kifu".to_owned(), "4:23".to_owned()))); let mut countdown = Duration::from_secs(5 * 60);
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();