diff --git a/src/draw/input_text.rs b/src/draw/input_text.rs index 65c6c8f..3ee734e 100644 --- a/src/draw/input_text.rs +++ b/src/draw/input_text.rs @@ -96,7 +96,7 @@ impl<'a> Drawable for InputText<'a> { let pos = Point::new(rect_point.x + padding.left, rect_point.y + padding.top); let end_pos = Point::new( - dt.width() as f32 - self.params.padding.right - self.params.margin.right, + point.x + space.width - self.params.padding.right - self.params.margin.right, pos.y, ); diff --git a/src/draw/list_view.rs b/src/draw/list_view.rs index 39415e4..9bc5dfb 100644 --- a/src/draw/list_view.rs +++ b/src/draw/list_view.rs @@ -163,7 +163,7 @@ where } let pos = Point::new(x_offset + icon_size_f32 + icon_spacing, y_offset); - let end_pos = Point::new(dt.width() as f32 - self.params.margin.right, y_offset); + let end_pos = Point::new(point.x + space.width - margin.right, y_offset); let color = if i == selected_item { self.params.selected_font_color diff --git a/src/lib.rs b/src/lib.rs index 1ed0488..2d89134 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,22 +24,43 @@ macro_rules! prog_name { }; } +pub struct Viewport { + buf_size: (i32, i32), + content_offset: (f32, f32), + content_size: (f32, f32), +} + +impl Viewport { + pub fn full(width: i32, height: i32) -> Self { + Self { + buf_size: (width, height), + content_offset: (0.0, 0.0), + content_size: (width as f32, height as f32), + } + } + + pub fn inset(mut self, offset: (f32, f32), content: (f32, f32)) -> Self { + self.content_offset = offset; + self.content_size = content; + self + } +} + pub fn render_to_buffer( config: &config::Config, state: &mut state::State, scale: u16, - width: i32, - height: i32, buffer: &mut [u32], + viewport: Viewport, ) { use draw::Drawable; - let mut dt = DrawTarget::from_backing(width, height, buffer); + let mut dt = DrawTarget::from_backing(viewport.buf_size.0, viewport.buf_size.1, buffer); let mut space_left = draw::Space { - width: width as f32, - height: height as f32, + width: viewport.content_size.0, + height: viewport.content_size.1, }; - let mut point = draw::Point::new(0., 0.); + let mut point = draw::Point::new(viewport.content_offset.0, viewport.content_offset.1); let (mut drawables, dyn_space) = draw::make_drawables(config, state, scale); if let Some(dyn_space) = dyn_space { @@ -47,6 +68,7 @@ pub fn render_to_buffer( } while let Some(d) = drawables.borrowed_next() { let occupied = d.draw(&mut dt, scale, space_left, point); + debug_assert!(occupied.width <= space_left.width && occupied.height <= space_left.height); point.y += occupied.height; space_left.height -= occupied.height; } diff --git a/src/window.rs b/src/window.rs index e1f48fc..a171cec 100644 --- a/src/window.rs +++ b/src/window.rs @@ -273,8 +273,7 @@ impl Window { } }; - use crate::draw::*; - let mut dt = { + let canvas = { #[allow(clippy::needless_lifetimes)] fn transmute_slice<'a>(a: &'a mut [u8]) -> &'a mut [u32] { assert_eq!(a.as_ptr().align_offset(std::mem::align_of::()), 0); @@ -289,30 +288,17 @@ impl Window { &mut *std::ptr::slice_from_raw_parts_mut(a.as_mut_ptr().cast(), a.len() / 4) } } - let canvas = transmute_slice(canvas); - DrawTarget::from_backing(width, height, canvas) + transmute_slice(canvas) }; - let mut space_left = Space { - width: content_w as f32, - height: content_h as f32, - }; - let mut point = Point::new(scaled_offset_x, scaled_offset_y); - - let (mut drawables, dyn_space) = - crate::draw::make_drawables(&self.config, &mut self.state, self.scale); - if let Some(dyn_space) = dyn_space { - space_left.height = space_left.height.min(dyn_space.height); - } - while let Some(d) = drawables.borrowed_next() { - let occupied = d.draw(&mut dt, self.scale, space_left, point); - debug_assert!( - occupied.width <= space_left.width && occupied.height <= space_left.height + let mut viewport = crate::Viewport::full(width, height); + if overlay { + viewport = viewport.inset( + (scaled_offset_x, scaled_offset_y), + (content_w as f32, content_h as f32), ); - - point.y += occupied.height; - space_left.height -= occupied.height; } + crate::render_to_buffer(&self.config, &mut self.state, self.scale, canvas, viewport); self.surface.damage_buffer(0, 0, width, height); self.surface.frame(qh, self.surface.clone()); diff --git a/tests/fixtures/long_input.png b/tests/fixtures/long_input.png new file mode 100644 index 0000000..d27b003 Binary files /dev/null and b/tests/fixtures/long_input.png differ diff --git a/tests/fixtures/long_name.png b/tests/fixtures/long_name.png new file mode 100644 index 0000000..e5ef49d Binary files /dev/null and b/tests/fixtures/long_name.png differ diff --git a/tests/regression/main.rs b/tests/regression/main.rs index 288e4bb..39c70e4 100644 --- a/tests/regression/main.rs +++ b/tests/regression/main.rs @@ -8,7 +8,9 @@ //! inspection. The diff highlights changed pixels in red. mod snap; -use snap::{run_regression, test_entries, Action}; +use snap::{ + run_regression, run_regression_with_padding, test_entries, test_entries_with_long_name, Action, +}; #[test] fn initial() { @@ -37,3 +39,20 @@ fn search_then_nav() { &[Action::Type("te"), Action::NextItem], ); } + +#[test] +fn long_name() { + run_regression_with_padding("long_name", test_entries_with_long_name(), &[], (20, 10)); +} + +#[test] +fn long_input() { + run_regression_with_padding( + "long_input", + test_entries(), + &[Action::Type( + "very long input string that goes well past the input width", + )], + (20, 10), + ); +} diff --git a/tests/regression/snap.rs b/tests/regression/snap.rs index 735ee26..ead2090 100644 --- a/tests/regression/snap.rs +++ b/tests/regression/snap.rs @@ -8,8 +8,12 @@ pub enum Action { NextItem, } +fn to_entries(items: &[&str]) -> Vec { + items.iter().map(|s| s.to_string()).collect() +} + pub fn test_entries() -> Vec { - [ + to_entries(&[ "Firefox", "Chromium", "Terminal", @@ -18,10 +22,16 @@ pub fn test_entries() -> Vec { "Calculator", "Text Editor", "Music Player", - ] - .iter() - .map(|s| s.to_string()) - .collect() + ]) +} + +pub fn test_entries_with_long_name() -> Vec { + to_entries(&[ + "Very Long Application Name That Definitely Does Not Fit In The Window", + "Firefox", + "Chromium", + "Terminal", + ]) } fn unpremultiply_to_rgba(buffer: &[u32]) -> Vec { @@ -85,6 +95,15 @@ fn load_png_rgba(path: &str) -> (u32, u32, Vec) { } pub fn run_regression(name: &str, entries: Vec, actions: &[Action]) { + run_regression_with_padding(name, entries, actions, (0, 0)); +} + +pub fn run_regression_with_padding( + name: &str, + entries: Vec, + actions: &[Action], + padding: (u32, u32), +) { let config = Config::default(); let mode = Mode::dialog_from_lines(entries); @@ -99,14 +118,20 @@ pub fn run_regression(name: &str, entries: Vec, actions: &[Action]) { let params: Params = config.param(); let scale = params.scale.unwrap_or(1); - let w = (params.width * u32::from(scale)) as i32; - let h = (params.height * u32::from(scale)) as i32; - let mut buffer = vec![0u32; (w * h) as usize]; - yofi::render_to_buffer(&config, &mut state, scale, w, h, &mut buffer); + let content_w = params.width * u32::from(scale); + let content_h = params.height * u32::from(scale); + let buf_w = (content_w + 2 * padding.0) as i32; + let buf_h = (content_h + 2 * padding.1) as i32; + + let mut buffer = vec![0u32; (buf_w * buf_h) as usize]; + let viewport = yofi::Viewport::full(buf_w, buf_h).inset( + (padding.0 as f32, padding.1 as f32), + (content_w as f32, content_h as f32), + ); + yofi::render_to_buffer(&config, &mut state, scale, &mut buffer, viewport); let actual_rgba = unpremultiply_to_rgba(&buffer); - let w = w as u32; - let h = h as u32; + let (w, h) = (buf_w as u32, buf_h as u32); let fixture = format!("tests/fixtures/{name}.png"); let new_file = format!("tests/fixtures/{name}.new.png");