From 257b97e9ca8c205fec9b9372b19ee41b3987bf79 Mon Sep 17 00:00:00 2001 From: Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm> Date: Sun, 15 Dec 2019 18:39:49 +0000 Subject: [PATCH] layout: Respect margins --- src/data.rs | 17 ++++++ src/layout.rs | 119 +++++++++++++++++++++++++++++++++---- tests/layout_margins.yaml | 9 +++ tests/layout_position.yaml | 9 +++ 4 files changed, 141 insertions(+), 13 deletions(-) create mode 100644 tests/layout_margins.yaml create mode 100644 tests/layout_position.yaml diff --git a/src/data.rs b/src/data.rs index baa62e26..9aa1fc4b 100644 --- a/src/data.rs +++ b/src/data.rs @@ -850,4 +850,21 @@ mod tests { }, ); } + + #[test] + fn test_layout_margins() { + let out = Layout::from_file(PathBuf::from("tests/layout_margins.yaml")) + .unwrap() + .build(PanicWarn).0 + .unwrap(); + assert_eq!( + out.margins, + layout::Margins { + top: 1.0, + bottom: 3.0, + left: 2.0, + right: 2.0, + } + ); + } } diff --git a/src/layout.rs b/src/layout.rs index b1d654fd..950b2349 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -99,7 +99,7 @@ pub mod c { } } - /// Scale + translate + /// Translate and then scale #[repr(C)] pub struct Transformation { pub origin_x: f64, @@ -108,6 +108,14 @@ pub mod c { } impl Transformation { + /// Applies the new transformation after this one + pub fn chain(self, next: Transformation) -> Transformation { + Transformation { + origin_x: self.origin_x + self.scale * next.origin_x, + origin_y: self.origin_y + self.scale * next.origin_y, + scale: self.scale * next.scale, + } + } fn forward(&self, p: Point) -> Point { Point { x: (p.x - self.origin_x) / self.scale, @@ -195,7 +203,8 @@ pub mod c { println!("{:?}", button); } - /// Positions the layout within the available space + /// Positions the layout contents within the available space. + /// The origin of the transformation is the point inside the margins. #[no_mangle] pub extern "C" fn squeek_layout_calculate_transformation( @@ -204,15 +213,10 @@ pub mod c { allocation_height: f64, ) -> Transformation { let layout = unsafe { &*layout }; - let size = layout.calculate_size(); - let h_scale = allocation_width / size.width; - let v_scale = allocation_height / size.height; - let scale = if h_scale < v_scale { h_scale } else { v_scale }; - Transformation { - origin_x: (allocation_width - (scale * size.width)) / 2.0, - origin_y: (allocation_height - (scale * size.height)) / 2.0, - scale: scale, - } + layout.calculate_transformation(Size { + width: allocation_width, + height: allocation_height, + }) } #[no_mangle] @@ -427,7 +431,7 @@ pub struct ButtonPlace<'a> { offset: c::Point, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct Size { pub width: f64, pub height: f64, @@ -560,6 +564,7 @@ pub enum ArrangementKind { Wide = 1, } +#[derive(Debug, PartialEq)] pub struct Margins { pub top: f64, pub bottom: f64, @@ -713,7 +718,8 @@ impl Layout { }; } - fn calculate_size(&self) -> Size { + /// Calculates size without margins + fn calculate_inner_size(&self) -> Size { Size { height: find_max_double( self.views.iter(), @@ -725,6 +731,39 @@ impl Layout { ), } } + + /// Size including margins + fn calculate_size(&self) -> Size { + let inner_size = self.calculate_inner_size(); + Size { + width: self.margins.left + inner_size.width + self.margins.right, + height: ( + self.margins.top + + inner_size.height + + self.margins.bottom + ), + } + } + + pub fn calculate_transformation( + &self, + available: Size, + ) -> c::Transformation { + let size = self.calculate_size(); + let h_scale = available.width / size.width; + let v_scale = available.height / size.height; + let scale = if h_scale < v_scale { h_scale } else { v_scale }; + let outside_margins = c::Transformation { + origin_x: (available.width - (scale * size.width)) / 2.0, + origin_y: (available.height - (scale * size.height)) / 2.0, + scale: scale, + }; + outside_margins.chain(c::Transformation { + origin_x: self.margins.left, + origin_y: self.margins.top, + scale: 1.0, + }) + } } mod procedures { @@ -907,4 +946,58 @@ mod test { .is_none() ); } + + #[test] + fn check_bottom_margin() { + // just one button + let view = View::new(vec![ + ( + 0.0, + Row { + angle: 0, + buttons: vec![( + 0.0, + Box::new(Button { + size: Size { width: 1.0, height: 1.0 }, + ..*make_button_with_state("foo".into(), make_state()) + }), + )] + }, + ), + ]); + let layout = Layout { + current_view: String::new(), + keymap_str: CString::new("").unwrap(), + kind: ArrangementKind::Base, + locked_keys: HashSet::new(), + pressed_keys: HashSet::new(), + // Lots of bottom margin + margins: Margins { + top: 0.0, + left: 0.0, + right: 0.0, + bottom: 1.0, + }, + views: hashmap! { + String::new() => view, + }, + }; + assert_eq!( + layout.calculate_inner_size(), + Size { width: 1.0, height: 1.0 } + ); + assert_eq!( + layout.calculate_size(), + Size { width: 1.0, height: 2.0 } + ); + // Don't change those values randomly! + // They take advantage of incidental precise float representation + // to even be comparable. + let transformation = layout.calculate_transformation( + Size { width: 2.0, height: 2.0 } + ); + assert_eq!(transformation.scale, 1.0); + assert_eq!(transformation.origin_x, 0.5); + assert_eq!(transformation.origin_y, 0.0); + } } diff --git a/tests/layout_margins.yaml b/tests/layout_margins.yaml new file mode 100644 index 00000000..3a704da7 --- /dev/null +++ b/tests/layout_margins.yaml @@ -0,0 +1,9 @@ +--- +# Margins present +margins: { top: 1, side: 2, bottom: 3 } +views: + base: + - "test" +outlines: + default: { width: 1, height: 1 } + diff --git a/tests/layout_position.yaml b/tests/layout_position.yaml new file mode 100644 index 00000000..3a704da7 --- /dev/null +++ b/tests/layout_position.yaml @@ -0,0 +1,9 @@ +--- +# Margins present +margins: { top: 1, side: 2, bottom: 3 } +views: + base: + - "test" +outlines: + default: { width: 1, height: 1 } + -- GitLab