use cairo::{ Context, FontSlant, FontWeight, Format, ImageSurface, LineCap, LinearGradient, Pattern, }; use glib::Object; use gtk::{prelude::*, subclass::prelude::*}; use std::{cell::RefCell, rc::Rc}; const WIDTH: i32 = 800; const HEIGHT: i32 = 800; pub struct SplashPrivate { text: Rc>, time: Rc>, background: Rc>, } impl SplashPrivate { fn set_text(&self, text: String) { *self.text.borrow_mut() = text; } fn set_time(&self, time: String) { *self.time.borrow_mut() = time; } fn redraw_background(&self) { let background = ImageSurface::create(Format::Rgb24, WIDTH, HEIGHT).unwrap(); let context = Context::new(background).unwrap(); context.push_group(); context.set_source_rgb(0., 0., 0.); let _ = context.paint(); context.select_font_face("Alegreya Sans SC", FontSlant::Normal, FontWeight::Bold); context.set_font_size(64.); let center_x = WIDTH as f64 / 2.; let center_y = HEIGHT as f64 / 2.; let (title_width, title_height) = { let title_extents = context.text_extents(&self.text.borrow()).unwrap(); let title_baseline_x = center_x - title_extents.width() / 2.; let title_baseline_y = center_y - 20.; let gradient = LinearGradient::new( title_baseline_x, title_baseline_y - title_extents.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()); (title_extents.width(), title_extents.height()) }; { context.set_source_rgb(0.7, 0., 1.); context.set_line_width(5.); context.set_line_cap(LineCap::Round); let start_length = center_x - title_width / 2. - title_height - 20.; AsymLineCutout { orientation: gtk::Orientation::Horizontal, start_x: 20., start_y: center_y - 20. - title_height / 2., start_length, total_length: WIDTH as f64 - 120., cutout_length: title_width, height: title_height, invert: false, } .draw(&context); let _ = context.stroke(); } let background = context.pop_group().unwrap(); *self.background.borrow_mut() = background; } } #[glib::object_subclass] impl ObjectSubclass for SplashPrivate { const NAME: &'static str = "Splash"; type Type = Splash; type ParentType = gtk::DrawingArea; fn new() -> SplashPrivate { // Set up a default plain black background let background = ImageSurface::create(Format::Rgb24, WIDTH, HEIGHT).unwrap(); let context = Context::new(background).unwrap(); context.push_group(); context.set_source_rgb(0., 0., 0.); let _ = context.paint(); let background = context.pop_group().unwrap(); SplashPrivate { text: Rc::new(RefCell::new(String::from(""))), time: Rc::new(RefCell::new(String::from(""))), background: Rc::new(RefCell::new(background)), } } } impl ObjectImpl for SplashPrivate {} impl WidgetImpl for SplashPrivate {} impl DrawingAreaImpl for SplashPrivate {} glib::wrapper! { pub struct Splash(ObjectSubclass) @extends gtk::DrawingArea, gtk::Widget; } impl Splash { pub fn new(text: String, time: String) -> Self { let s: Self = Object::builder().build(); s.set_width_request(WIDTH); s.set_height_request(HEIGHT); s.imp().set_text(text); s.imp().set_time(time); s.imp().redraw_background(); s.set_draw_func({ let s = s.clone(); move |_, context, _width, _height| { let background = s.imp().background.borrow(); let _ = context.set_source(&*background); let _ = context.paint(); } }); s } } struct AsymLineCutout { orientation: gtk::Orientation, start_x: f64, start_y: f64, start_length: f64, total_length: f64, cutout_length: f64, height: f64, invert: bool, } impl AsymLineCutout { 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( self.start_x + self.start_length + self.height, self.start_y + dodge, ); context.line_to( self.start_x + self.start_length + self.height + self.cutout_length, self.start_y + dodge, ); context.line_to( self.start_x + self.start_length + self.height + self.cutout_length + (self.height / 2.), self.start_y + dodge / 2., ); context.line_to(self.total_length, self.start_y + dodge / 2.); } gtk::Orientation::Vertical => { /* 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.height, self.start_y + self.start_length + self.height, ); context.line_to( self.start_x + self.height, self.start_y + self.start_length + self.height + self.center_length, ); context.line_to( self.start_x + self.height / 2., self.start_y + self.start_length + self.height + self.center_length + (self.height / 2.), ); context.line_to( self.start_x + self.height / 2., self.start_y + self.start_length + self.height + self.center_length + (self.height / 2.) + self.end_length, ); */ } _ => panic!("unknown orientation"), } } } struct AsymLineReturn { orientation: gtk::Orientation, start_x: f64, start_y: f64, start_length: f64, center_length: f64, end_length: f64, width: f64, } impl AsymLineReturn { fn draw(&self, context: &Context) { 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( self.start_x + self.start_length + self.width.abs(), self.start_y + self.width, ); context.line_to( self.start_x + self.start_length + self.width.abs() + self.center_length, self.start_y + self.width, ); context.line_to( self.start_x + self.start_length + self.width.abs() + self.center_length + (self.width.abs() / 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., ); } gtk::Orientation::Vertical => { 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.width, self.start_y + self.start_length + self.width.abs(), ); context.line_to( self.start_x + self.width, self.start_y + self.start_length + self.width.abs() + self.center_length, ); context.line_to( self.start_x + self.width / 2., self.start_y + self.start_length + self.width.abs() + self.center_length + (self.width.abs() / 2.), ); context.line_to( self.start_x + self.width / 2., self.start_y + self.start_length + self.width.abs() + self.center_length + (self.width.abs() / 2.) + self.end_length, ); } _ => panic!("unknown orientation"), } } } fn main() { let app = gtk::Application::builder() .application_id("com.luminescent-dreams.cyberpunk-splash") .build(); app.connect_activate(move |app| { let window = gtk::ApplicationWindow::new(app); window.present(); window.set_child(Some(&Splash::new("GTK Kifu".to_owned(), "4:23".to_owned()))); }); app.run(); }