image: More fine-grained zoom instructions

Add parameters to use zoom_to without animation or
snapping to best fit. Hopefully fixing 100%/200% snapping
to best fit when close to filling the window and
fixing high res scroll wheel bugs.

Closes #120, #134

Part-of: <https://gitlab.gnome.org/Incubator/loupe/-/merge_requests/177>
This commit is contained in:
Sophie Herold 2023-04-13 04:52:14 +02:00 • committed by Marge Bot
parent 44e265374b
commit e96b9ccac9
4 changed files with 55 additions and 47 deletions

View file

@ -147,8 +147,14 @@ impl LpApplication {
self.set_accels_for_action("win.first", &["Home"]); self.set_accels_for_action("win.first", &["Home"]);
self.set_accels_for_action("win.last", &["End"]); self.set_accels_for_action("win.last", &["End"]);
self.set_accels_for_action("win.zoom-to(1.0)", &["1", "KP_1", "<Ctrl>1", "<Ctrl>KP_1"]); self.set_accels_for_action(
self.set_accels_for_action("win.zoom-to(2.0)", &["2", "KP_2", "<Ctrl>2", "<Ctrl>KP_2"]); "win.zoom-to-exact(1.0)",
&["1", "KP_1", "<Ctrl>1", "<Ctrl>KP_1"],
);
self.set_accels_for_action(
"win.zoom-to-exact(2.0)",
&["2", "KP_2", "<Ctrl>2", "<Ctrl>KP_2"],
);
self.set_accels_for_action("win.zoom-best-fit", &["0", "KP_0", "<Ctrl>0", "<Ctrl>KP_0"]); self.set_accels_for_action("win.zoom-best-fit", &["0", "KP_0", "<Ctrl>0", "<Ctrl>KP_0"]);
self.set_accels_for_action( self.set_accels_for_action(
"win.zoom-in", "win.zoom-in",

View file

@ -370,23 +370,11 @@ mod imp {
let zoom = let zoom =
obj.imp().zoom_target.get() * zoom_factor; obj.imp().zoom_target.get() * zoom_factor;
if zoom > obj.zoom_level_best_fit() {
obj.set_best_fit(false);
if animated { if animated {
obj.zoom_to(zoom); obj.zoom_to(zoom);
} else { } else {
obj.set_zoom(zoom); obj.zoom_to_full(zoom, false, false);
obj.imp().zoom_target.set(zoom);
} }
} else {
obj.set_best_fit(true);
if animated {
obj.zoom_to(obj.zoom_level_best_fit());
} else {
obj.set_zoom(obj.zoom_level_best_fit());
obj.imp().zoom_target.set(obj.zoom_level_best_fit());
}
}
// do not propagate event to scrolled window // do not propagate event to scrolled window
gtk::Inhibit(true) gtk::Inhibit(true)
@ -1311,7 +1299,18 @@ impl LpImage {
} }
/// Zoom to specific level with animation /// Zoom to specific level with animation
pub fn zoom_to(&self, mut zoom: f64) { pub fn zoom_to(&self, zoom: f64) {
self.zoom_to_full(zoom, true, true);
}
/// Zoom to specific level with animation not snapping to best-fit
///
/// Used for zooming to 100% or 200%
pub fn zoom_to_exact(&self, zoom: f64) {
self.zoom_to_full(zoom, true, false);
}
fn zoom_to_full(&self, mut zoom: f64, animated: bool, snap_best_fit: bool) {
let max_zoom = self.max_zoom(); let max_zoom = self.max_zoom();
if zoom >= max_zoom { if zoom >= max_zoom {
zoom = max_zoom; zoom = max_zoom;
@ -1320,11 +1319,14 @@ impl LpImage {
self.set_max_zoom(false); self.set_max_zoom(false);
} }
// If image is only 1/4 of a zoom step away from best-fit, also let extended_best_fit_threshold = if snap_best_fit {
// activate best-fit. This avoids bugs with floating point precision // If image is only 1/4 of a zoom step away from best-fit, also
// and removes awkward minimal zoom steps. // activate best-fit. This avoids bugs with floating point precision
let extended_best_fit_threshold = // and removes awkward minimal zoom steps.
self.zoom_level_best_fit() * (1. + (ZOOM_FACTOR_BUTTON - 1.) / 4.); self.zoom_level_best_fit() * (1. + (ZOOM_FACTOR_BUTTON - 1.) / 4.)
} else {
self.zoom_level_best_fit()
};
if zoom <= extended_best_fit_threshold { if zoom <= extended_best_fit_threshold {
zoom = self.zoom_level_best_fit(); zoom = self.zoom_level_best_fit();
@ -1343,26 +1345,30 @@ impl LpImage {
return; return;
} }
// wild code if animated {
let current_hborder = self.widget_width() - self.image_displayed_width(); // wild code
let target_hborder = self.widget_width() - self.image_size().0 as f64 * zoom; let current_hborder = self.widget_width() - self.image_displayed_width();
let target_hborder = self.widget_width() - self.image_size().0 as f64 * zoom;
self.imp() self.imp()
.zoom_hscrollbar_transition .zoom_hscrollbar_transition
.set(current_hborder.signum() != target_hborder.signum() && current_hborder != 0.); .set(current_hborder.signum() != target_hborder.signum() && current_hborder != 0.);
let current_vborder = self.widget_height() - self.image_displayed_height(); let current_vborder = self.widget_height() - self.image_displayed_height();
let target_vborder = self.widget_height() - self.image_size().1 as f64 * zoom; let target_vborder = self.widget_height() - self.image_size().1 as f64 * zoom;
self.imp() self.imp()
.zoom_hscrollbar_transition .zoom_hscrollbar_transition
.set(current_vborder.signum() != target_vborder.signum() && current_vborder != 0.); .set(current_vborder.signum() != target_vborder.signum() && current_vborder != 0.);
let animation = self.zoom_animation(); let animation = self.zoom_animation();
animation.set_value_from(self.zoom()); animation.set_value_from(self.zoom());
animation.set_value_to(zoom); animation.set_value_to(zoom);
animation.play(); animation.play();
} else {
self.set_zoom(zoom)
}
} }
/// Image size of original image with EXIF rotation applied /// Image size of original image with EXIF rotation applied

View file

@ -564,12 +564,6 @@ impl LpImageView {
} }
} }
pub fn zoom_to(&self, level: f64) {
if let Some(current_page) = self.current_page() {
current_page.image().zoom_to(level);
}
}
pub fn rotate_image(&self, angle: f64) { pub fn rotate_image(&self, angle: f64) {
if let Some(current_page) = self.current_page() { if let Some(current_page) = self.current_page() {
current_page.image().rotate_by(angle); current_page.image().rotate_by(angle);

View file

@ -134,8 +134,8 @@ mod imp {
win.zoom_in(); win.zoom_in();
}); });
klass.install_action("win.zoom-to", Some("d"), move |win, _, level| { klass.install_action("win.zoom-to-exact", Some("d"), move |win, _, level| {
win.zoom_to(level.unwrap().get().unwrap()); win.zoom_to_exact(level.unwrap().get().unwrap());
}); });
klass.install_action("win.zoom-best-fit", None, move |win, _, _| { klass.install_action("win.zoom-best-fit", None, move |win, _, _| {
@ -391,8 +391,10 @@ impl LpWindow {
self.imp().image_view.zoom_in(); self.imp().image_view.zoom_in();
} }
fn zoom_to(&self, level: f64) { fn zoom_to_exact(&self, level: f64) {
self.imp().image_view.zoom_to(level); if let Some(image) = self.imp().image_view.current_image() {
image.zoom_to_exact(level);
}
} }
fn zoom_best_fit(&self) { fn zoom_best_fit(&self) {
@ -579,7 +581,7 @@ impl LpWindow {
self.action_set_enabled("win.copy", enabled); self.action_set_enabled("win.copy", enabled);
self.action_set_enabled("win.trash", enabled); self.action_set_enabled("win.trash", enabled);
self.action_set_enabled("win.zoom-best-fit", enabled); self.action_set_enabled("win.zoom-best-fit", enabled);
self.action_set_enabled("win.zoom-to", enabled); self.action_set_enabled("win.zoom-to-exact", enabled);
self.action_set_enabled("win.toggle-properties", enabled); self.action_set_enabled("win.toggle-properties", enabled);
} }