layout: allow stretching the layout by a small amount

Due to the way the panel size is calculated, there might be a small
empty space on the sides or top of the layout. This can be an issue,
especially when this empty space is located on the sides, as touch
events in this area are not taken into account.

By allowing a small difference in horizontal and vertical scaling, we
can ensure the panel occupies the whole display width in cases where
this would be problematic.
This commit is contained in:
Arnaud Ferraris
2022-04-08 17:33:05 +02:00
committed by Arnaud Ferraris
parent 323fd7ea14
commit 68087a125c
4 changed files with 84 additions and 18 deletions

View File

@ -418,7 +418,8 @@ eek_gtk_keyboard_new (EekboardContextService *eekservice,
.widget_to_layout = { .widget_to_layout = {
.origin_x = 0, .origin_x = 0,
.origin_y = 0, .origin_y = 0,
.scale = 1, .scale_x = 1,
.scale_y = 1,
}, },
}; };
priv->render_geometry = initial_geometry; priv->render_geometry = initial_geometry;

View File

@ -219,7 +219,7 @@ eek_renderer_render_keyboard (EekRenderer *self,
cairo_save(cr); cairo_save(cr);
cairo_translate (cr, geometry.widget_to_layout.origin_x, geometry.widget_to_layout.origin_y); cairo_translate (cr, geometry.widget_to_layout.origin_x, geometry.widget_to_layout.origin_y);
cairo_scale (cr, geometry.widget_to_layout.scale, geometry.widget_to_layout.scale); cairo_scale (cr, geometry.widget_to_layout.scale_x, geometry.widget_to_layout.scale_y);
squeek_draw_layout_base_view(keyboard->layout, self, cr); squeek_draw_layout_base_view(keyboard->layout, self, cr);
squeek_layout_draw_all_changed(keyboard->layout, self, cr, submission); squeek_layout_draw_all_changed(keyboard->layout, self, cr, submission);

View File

@ -87,7 +87,8 @@ void eek_bounds_free (EekBounds *bounds);
struct transformation { struct transformation {
gdouble origin_x; gdouble origin_x;
gdouble origin_y; gdouble origin_y;
gdouble scale; gdouble scale_x;
gdouble scale_y;
}; };
G_END_DECLS G_END_DECLS

View File

@ -18,6 +18,7 @@
*/ */
use std::cell::RefCell; use std::cell::RefCell;
use std::cmp;
use std::collections::{ HashMap, HashSet }; use std::collections::{ HashMap, HashSet };
use std::ffi::CString; use std::ffi::CString;
use std::fmt; use std::fmt;
@ -26,6 +27,7 @@ use std::vec::Vec;
use ::action::Action; use ::action::Action;
use ::drawing; use ::drawing;
use ::float_ord::FloatOrd;
use ::keyboard::KeyState; use ::keyboard::KeyState;
use ::logging; use ::logging;
use ::manager; use ::manager;
@ -117,28 +119,30 @@ pub mod c {
pub struct Transformation { pub struct Transformation {
pub origin_x: f64, pub origin_x: f64,
pub origin_y: f64, pub origin_y: f64,
pub scale: f64, pub scale_x: f64,
pub scale_y: f64,
} }
impl Transformation { impl Transformation {
/// Applies the new transformation after this one /// Applies the new transformation after this one
pub fn chain(self, next: Transformation) -> Transformation { pub fn chain(self, next: Transformation) -> Transformation {
Transformation { Transformation {
origin_x: self.origin_x + self.scale * next.origin_x, origin_x: self.origin_x + self.scale_x * next.origin_x,
origin_y: self.origin_y + self.scale * next.origin_y, origin_y: self.origin_y + self.scale_y * next.origin_y,
scale: self.scale * next.scale, scale_x: self.scale_x * next.scale_x,
scale_y: self.scale_y * next.scale_y,
} }
} }
fn forward(&self, p: Point) -> Point { fn forward(&self, p: Point) -> Point {
Point { Point {
x: (p.x - self.origin_x) / self.scale, x: (p.x - self.origin_x) / self.scale_x,
y: (p.y - self.origin_y) / self.scale, y: (p.y - self.origin_y) / self.scale_y,
} }
} }
fn reverse(&self, p: Point) -> Point { fn reverse(&self, p: Point) -> Point {
Point { Point {
x: p.x * self.scale + self.origin_x, x: p.x * self.scale_x + self.origin_x,
y: p.y * self.scale + self.origin_y, y: p.y * self.scale_y + self.origin_y,
} }
} }
pub fn reverse_bounds(&self, b: Bounds) -> Bounds { pub fn reverse_bounds(&self, b: Bounds) -> Bounds {
@ -394,7 +398,8 @@ pub mod c {
let transform = Transformation { let transform = Transformation {
origin_x: 10f64, origin_x: 10f64,
origin_y: 11f64, origin_y: 11f64,
scale: 12f64, scale_x: 12f64,
scale_y: 13f64,
}; };
let point = Point { x: 1f64, y: 1f64 }; let point = Point { x: 1f64, y: 1f64 };
let transformed = transform.reverse(transform.forward(point.clone())); let transformed = transform.reverse(transform.forward(point.clone()));
@ -755,16 +760,20 @@ impl Layout {
let size = self.calculate_size(); let size = self.calculate_size();
let h_scale = available.width / size.width; let h_scale = available.width / size.width;
let v_scale = available.height / size.height; let v_scale = available.height / size.height;
let scale = if h_scale < v_scale { h_scale } else { v_scale }; // Allow up to 5% (and a bit more) horizontal stretching for filling up available space
let scale_x = if (h_scale / v_scale) < 1.06 { h_scale } else { v_scale };
let scale_y = cmp::min(FloatOrd(h_scale), FloatOrd(v_scale)).0;
let outside_margins = c::Transformation { let outside_margins = c::Transformation {
origin_x: (available.width - (scale * size.width)) / 2.0, origin_x: (available.width - (scale_x * size.width)) / 2.0,
origin_y: (available.height - (scale * size.height)) / 2.0, origin_y: (available.height - (scale_y * size.height)) / 2.0,
scale: scale, scale_x: scale_x,
scale_y: scale_y,
}; };
outside_margins.chain(c::Transformation { outside_margins.chain(c::Transformation {
origin_x: self.margins.left, origin_x: self.margins.left,
origin_y: self.margins.top, origin_y: self.margins.top,
scale: 1.0, scale_x: 1.0,
scale_y: 1.0,
}) })
} }
@ -1471,8 +1480,63 @@ mod test {
let transformation = layout.calculate_transformation( let transformation = layout.calculate_transformation(
Size { width: 2.0, height: 2.0 } Size { width: 2.0, height: 2.0 }
); );
assert_eq!(transformation.scale, 1.0); assert_eq!(transformation.scale_x, 1.0);
assert_eq!(transformation.scale_y, 1.0);
assert_eq!(transformation.origin_x, 0.5); assert_eq!(transformation.origin_x, 0.5);
assert_eq!(transformation.origin_y, 0.0); assert_eq!(transformation.origin_y, 0.0);
} }
#[test]
fn check_stretching() {
// just one button
let view = View::new(vec![
(
0.0,
Row::new(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(),
view_latched: LatchedState::Not,
keymaps: Vec::new(),
kind: ArrangementKind::Base,
pressed_keys: HashSet::new(),
margins: Margins {
top: 0.0,
left: 0.0,
right: 0.0,
bottom: 0.0,
},
views: hashmap! {
String::new() => (c::Point { x: 0.0, y: 0.0 }, view),
},
purpose: ContentPurpose::Normal,
};
let transformation = layout.calculate_transformation(
Size { width: 100.0, height: 100.0 }
);
assert_eq!(transformation.scale_x, 100.0);
assert_eq!(transformation.scale_y, 100.0);
let transformation = layout.calculate_transformation(
Size { width: 95.0, height: 100.0 }
);
assert_eq!(transformation.scale_x, 95.0);
assert_eq!(transformation.scale_y, 95.0);
let transformation = layout.calculate_transformation(
Size { width: 105.0, height: 100.0 }
);
assert_eq!(transformation.scale_x, 105.0);
assert_eq!(transformation.scale_y, 100.0);
let transformation = layout.calculate_transformation(
Size { width: 106.0, height: 100.0 }
);
assert_eq!(transformation.scale_x, 100.0);
assert_eq!(transformation.scale_y, 100.0);
}
} }