diff --git a/cyberpunk-slideshow/src/main.rs b/cyberpunk-slideshow/src/main.rs index 77e0c4f..3428703 100644 --- a/cyberpunk-slideshow/src/main.rs +++ b/cyberpunk-slideshow/src/main.rs @@ -9,7 +9,7 @@ use std::{ time::{Duration, Instant}, }; -use cairo::Context; +use cairo::{Context, Rectangle}; use cyberpunk::{AsymLine, AsymLineCutout, GlowPen, Pen, Text}; use glib::{GString, Object}; use gtk::{ @@ -73,13 +73,6 @@ impl Index for Script { } } -struct Region { - left: f64, - top: f64, - width: f64, - height: f64, -} - struct Fade { text: String, position: Position, @@ -91,7 +84,7 @@ struct Fade { trait Animation { fn position(&self) -> Position; - fn tick(&self, now: Instant, context: &Context, region: Region); + fn tick(&self, now: Instant, context: &Context, width: f64); } impl Animation for Fade { @@ -99,15 +92,15 @@ impl Animation for Fade { self.position.clone() } - fn tick(&self, now: Instant, context: &Context, region: Region) { + fn tick(&self, now: Instant, context: &Context, width: f64) { let total_frames = self.duration.as_secs() * FPS; let alpha_rate: f64 = 1. / total_frames as f64; let frames = (now - self.start_time).as_secs_f64() * FPS as f64; let alpha = alpha_rate * frames as f64; - let text_display = Text::new(self.text.clone(), context, 32.); - let _ = context.move_to(region.left, region.top + text_display.extents().height()); + let text_display = Text::new(self.text.clone(), context, 32., width); + let _ = context.move_to(0., text_display.extents().height()); let _ = context.set_source_rgba(PURPLE.0, PURPLE.1, PURPLE.2, alpha); text_display.draw(); } @@ -127,20 +120,20 @@ impl Animation for CrossFade { self.position.clone() } - fn tick(&self, now: Instant, context: &Context, region: Region) { + fn tick(&self, now: Instant, context: &Context, width: f64) { let total_frames = self.duration.as_secs() * FPS; let alpha_rate: f64 = 1. / total_frames as f64; let frames = (now - self.start_time).as_secs_f64() * FPS as f64; let alpha = alpha_rate * frames as f64; - let text_display = Text::new(self.old_text.clone(), context, 32.); - let _ = context.move_to(region.left, region.top + text_display.extents().height()); + let text_display = Text::new(self.old_text.clone(), context, 32., width); + let _ = context.move_to(0., text_display.extents().height()); let _ = context.set_source_rgba(PURPLE.0, PURPLE.1, PURPLE.2, 1. - alpha); text_display.draw(); - let text_display = Text::new(self.new_text.clone(), context, 32.); - let _ = context.move_to(region.left, region.top + text_display.extents().height()); + let text_display = Text::new(self.new_text.clone(), context, 32., width); + let _ = context.move_to(0., text_display.extents().height()); let _ = context.set_source_rgba(PURPLE.0, PURPLE.1, PURPLE.2, alpha); text_display.draw(); } @@ -270,23 +263,17 @@ impl CyberScreen { let _ = context.paint(); let pen = GlowPen::new(width, height, 2., 8., (0.7, 0., 1.)); - /* - for i in 0..6 { - pen.move_to(0., height as f64 * i as f64 / 5.); - pen.line_to(width as f64, height as f64 * i as f64 / 5.); - } - pen.stroke(); - */ AsymLineCutout { orientation: gtk::Orientation::Horizontal, start_x: 25., start_y: height as f64 / 7., start_length: width as f64 / 3., - cutout_length: width as f64 / 3. - 25., - height: 25., + cutout_length: width as f64 / 3. - 100., + height: 50., end_length: width as f64 / 3. - 50., invert: false, - }.draw(&pen); + } + .draw(&pen); pen.stroke(); AsymLine { @@ -297,7 +284,8 @@ impl CyberScreen { height: 50., end_length: 0., invert: false, - }.draw(&pen); + } + .draw(&pen); pen.stroke(); let tracery = pen.finish(); @@ -312,33 +300,36 @@ impl CyberScreen { if let Some(animation) = animations.get(&Position::Top) { let y = height as f64 * 1. / 5.; - let region = Region { - left: 20., - top: y, - height: region_height, - width: max_width, - }; - animation.tick(now, context, region); + let surface = context.target().create_for_rectangle(Rectangle::new( + 20., + y, + max_width, + region_height, + )).unwrap(); + let ctx = Context::new(&surface).unwrap(); + animation.tick(now, &ctx, max_width); } if let Some(animation) = animations.get(&Position::Middle) { let y = height as f64 * 2. / 5.; - let region = Region { - left: 20., - top: y, - height: region_height, - width: max_width, - }; - animation.tick(now, context, region); + let surface = context.target().create_for_rectangle(Rectangle::new( + 20., + y, + max_width, + region_height, + )).unwrap(); + let ctx = Context::new(&surface).unwrap(); + animation.tick(now, &ctx, max_width); } if let Some(animation) = animations.get(&Position::Bottom) { let y = height as f64 * 3. / 5.; - let region = Region { - left: 20., - top: y, - height: region_height, - width: max_width, - }; - animation.tick(now, context, region); + let surface = context.target().create_for_rectangle(Rectangle::new( + 20., + y, + max_width, + region_height, + )).unwrap(); + let ctx = Context::new(&surface).unwrap(); + animation.tick(now, &ctx, max_width); } } }); diff --git a/cyberpunk/src/lib.rs b/cyberpunk/src/lib.rs index 211620c..95fc05d 100644 --- a/cyberpunk/src/lib.rs +++ b/cyberpunk/src/lib.rs @@ -251,22 +251,53 @@ impl Pen for GlowPen { } pub struct Text<'a> { - content: String, + content: Vec, context: &'a Context, } impl<'a> Text<'a> { - pub fn new(content: String, context: &'a Context, size: f64) -> Self { + pub fn new(content: String, context: &'a Context, size: f64, width: f64) -> Self { context.select_font_face("Alegreya Sans SC", FontSlant::Normal, FontWeight::Bold); context.set_font_size(size); - Self { content, context } + + let lines = word_wrap(content, context, width); + + Self { content: lines, context } } pub fn extents(&self) -> TextExtents { - self.context.text_extents(&self.content).unwrap() + self.context.text_extents(&self.content[0]).unwrap() } pub fn draw(&self) { - let _ = self.context.show_text(&self.content); + let mut baseline = 0.; + for line in self.content.iter() { + baseline += self.context.text_extents(line).unwrap().height() + 10.; + self.context.move_to(0., baseline); + let _ = self.context.show_text(&line); + } } } + +fn word_wrap(content: String, context: &Context, max_width: f64) -> Vec { + let mut lines = vec![]; + let words: Vec<&str> = content.split_whitespace().collect(); + let mut start: usize = 0; + let mut line = String::new(); + + for idx in 0..words.len() + 1 { + line = words[start..idx].join(" "); + let extents = context.text_extents(&line).unwrap(); + if extents.width() > max_width { + let line = words[start..idx-1].join(" "); + println!("line: {}", line); + start = idx-1; + lines.push(line.clone()); + } + } + if line.len() > 0 { + println!("line: {}", line); + lines.push(line); + } + lines +}