From 0d6cd2a2472f8215d4394077783f1d322aa0bdda Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Sun, 9 Apr 2023 23:55:15 -0400 Subject: [PATCH] Add screen resize support and animation timeout --- cyberpunk-splash/src/main.rs | 169 +++++++++++++++++++++++++++++++---- 1 file changed, 150 insertions(+), 19 deletions(-) diff --git a/cyberpunk-splash/src/main.rs b/cyberpunk-splash/src/main.rs index 70776ca..b070c3a 100644 --- a/cyberpunk-splash/src/main.rs +++ b/cyberpunk-splash/src/main.rs @@ -10,14 +10,49 @@ use std::{ time::{Duration, Instant}, }; -const WIDTH: i32 = 1920; -const HEIGHT: i32 = 1280; +const WIDTH: i32 = 1600; +const HEIGHT: i32 = 600; + +#[derive(Clone, Copy, Debug)] +enum Event { + Frames(u8), + Time(Duration), +} + +struct TimeoutAnimation { + intensity: f64, + duration: f64, + ascending: bool, +} + +impl TimeoutAnimation { + fn tick(&mut self, frames_elapsed: u8) { + let step_size = 1. / (self.duration * 60.); + if self.ascending { + self.intensity = self.intensity + step_size * frames_elapsed as f64; + if self.intensity > 1. { + self.intensity = 1.0; + self.ascending = false; + } + } else { + self.intensity = self.intensity - step_size * frames_elapsed as f64; + if self.intensity < 0. { + self.intensity = 0.0; + self.ascending = true; + } + } + } +} pub struct SplashPrivate { text: Rc>, time: Rc>, background: Rc>, time_extents: Rc>>, + width: Rc>, + height: Rc>, + + timeout_animation: Rc>>, } impl SplashPrivate { @@ -30,7 +65,9 @@ impl SplashPrivate { } fn redraw_background(&self) { - let background = ImageSurface::create(Format::Rgb24, WIDTH, HEIGHT).unwrap(); + let background = + ImageSurface::create(Format::Rgb24, *self.width.borrow(), *self.height.borrow()) + .unwrap(); let context = Context::new(background).unwrap(); context.push_group(); context.set_source_rgb(0., 0., 0.); @@ -39,8 +76,8 @@ impl SplashPrivate { context.select_font_face("Alegreya Sans SC", FontSlant::Normal, FontWeight::Bold); context.set_font_size(128.); - let center_x = WIDTH as f64 / 2.; - let center_y = HEIGHT as f64 / 2.; + let center_x = *self.width.borrow() as f64 / 2.; + let center_y = *self.height.borrow() as f64 / 2.; let title_extents = context.text_extents(&self.text.borrow()).unwrap(); let title_width = title_extents.width(); @@ -54,7 +91,7 @@ impl SplashPrivate { start_x: 20., start_y: center_y - 20. - title_height / 2., start_length, - total_length: WIDTH as f64 - 120., + total_length: *self.width.borrow() as f64 - 120., cutout_length: title_width, height: title_height, invert: false, @@ -89,7 +126,7 @@ impl SplashPrivate { AsymLine { orientation: gtk::Orientation::Horizontal, start_x: 100., - start_y: HEIGHT as f64 / 2. + 100., + start_y: *self.height.borrow() as f64 / 2. + 100., start_length: 400., height: 50., total_length: 650., @@ -103,8 +140,8 @@ impl SplashPrivate { 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_x: *self.width.borrow() as f64 / 2. + 100., + start_y: *self.height.borrow() as f64 / 2. + 200., start_length: 600., height: 50., total_length: 650., @@ -140,6 +177,9 @@ impl ObjectSubclass for SplashPrivate { time: Rc::new(RefCell::new(Duration::ZERO)), background: Rc::new(RefCell::new(background)), time_extents: Rc::new(RefCell::new(None)), + width: Rc::new(RefCell::new(WIDTH)), + height: Rc::new(RefCell::new(HEIGHT)), + timeout_animation: Rc::new(RefCell::new(None)), } } } @@ -175,6 +215,8 @@ impl Splash { let center_x = width as f64 / 2.; let center_y = height as f64 / 2.; + {} + { context.select_font_face( "Alegreya Sans SC", @@ -195,16 +237,42 @@ impl Splash { let time_baseline_x = center_x - time_extents.width() / 2.; let time_baseline_y = center_y + 100.; + /* + match *s.imp().timeout_animation.borrow() { + Some(ref animation) => { + context.set_source_rgb(animation.intensity / 2., 0., 0.); + RoundedRectangle { + x: time_baseline_x - 5., + y: time_baseline_y + 10., + width: time_extents.width() + 15., + height: time_extents.height() + 15., + } + .draw(&context); + // let _ = context.fill(); + } + None => {} + } + */ + 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); + match *s.imp().timeout_animation.borrow() { + Some(ref animation) => { + gradient.add_color_stop_rgba(0.2, 0.2, 0.0, 1.0, animation.intensity); + gradient.add_color_stop_rgba(0.8, 0.7, 0.0, 1.0, animation.intensity); + let _ = context.set_source(gradient); + } + None => { + 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); + let _ = context.set_source(gradient); + } + } context.move_to(time_baseline_x, time_baseline_y); - let _ = context.set_source(gradient); let _ = context.show_text(&time); }; @@ -227,13 +295,37 @@ impl Splash { } }); + s.connect_resize(|s, width, height| { + *s.imp().width.borrow_mut() = width; + *s.imp().height.borrow_mut() = height; + s.imp().redraw_background(); + }); + s } pub fn set_time(&self, time: Duration) { self.imp().set_time(time); + if time == Duration::ZERO { + *self.imp().timeout_animation.borrow_mut() = Some(TimeoutAnimation { + intensity: 1., + duration: 1., + ascending: false, + }); + } self.queue_draw(); } + + pub fn tick(&self, frames_elapsed: u8) { + let mut animation = self.imp().timeout_animation.borrow_mut(); + match *animation { + Some(ref mut animation) => { + animation.tick(frames_elapsed); + self.queue_draw(); + } + None => {} + } + } } struct AsymLineCutout { @@ -335,6 +427,36 @@ impl AsymLine { } } +struct RoundedRectangle { + x: f64, + y: f64, + width: f64, + height: f64, +} + +impl RoundedRectangle { + fn draw(&self, context: &Context) { + context.arc( + self.x, + self.y - self.height / 2., + self.height / 2., + 0.5 * std::f64::consts::PI, + 1.5 * std::f64::consts::PI, + ); + let _ = context.fill(); + context.arc( + self.x + self.width, + self.y - self.height / 2., + self.height / 2., + 1.5 * std::f64::consts::PI, + 0.5 * std::f64::consts::PI, + ); + let _ = context.fill(); + context.rectangle(self.x, self.y, self.width, -self.height); + let _ = context.fill(); + } +} + struct SlashMeter { orientation: gtk::Orientation, start_x: f64, @@ -382,27 +504,36 @@ fn main() { app.connect_activate(move |app| { let (gtk_tx, gtk_rx) = - gtk::glib::MainContext::channel::(gtk::glib::PRIORITY_DEFAULT); + gtk::glib::MainContext::channel::(gtk::glib::PRIORITY_DEFAULT); let window = gtk::ApplicationWindow::new(app); window.present(); - let mut countdown = Duration::from_secs(5 * 60); + let mut countdown = Duration::from_secs(5); 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)); + // window.fullscreen(); - gtk_rx.attach(None, move |time| { - splash.set_time(time); + window.connect_maximized_notify(|window| { + window.fullscreen(); + }); + + gtk_rx.attach(None, move |event| { + match event { + Event::Frames(frames) => splash.tick(frames), + Event::Time(time) => splash.set_time(time), + }; Continue(true) }); std::thread::spawn(move || loop { - std::thread::sleep(Duration::from_secs(1)); - if Instant::now() >= next_tick { + std::thread::sleep(Duration::from_millis(1000 / 60)); + let _ = gtk_tx.send(Event::Frames(1)); + if Instant::now() >= next_tick && countdown > Duration::from_secs(0) { countdown = countdown - Duration::from_secs(1); - let _ = gtk_tx.send(countdown); + let _ = gtk_tx.send(Event::Time(countdown)); next_tick = next_tick + Duration::from_secs(1); } });