imageWindow: Fix shortcuts not always working

This commit is contained in:
Sophie Herold 2024-12-21 03:09:15 +01:00
parent bbf9534bdd
commit cb31b93ee1
5 changed files with 118 additions and 141 deletions

View file

@ -23,8 +23,9 @@ use adw::{glib, gtk};
use glycin::Operation; use glycin::Operation;
use crate::editing::preview::EditingError; use crate::editing::preview::EditingError;
use crate::util::gettext::*;
use crate::util::root::ParentWindow; use crate::util::root::ParentWindow;
use crate::util::{gettext::*, ErrorType}; use crate::util::ErrorType;
use crate::widgets::edit::LpEditCropSelection; use crate::widgets::edit::LpEditCropSelection;
use crate::widgets::{LpEditWindow, LpImage}; use crate::widgets::{LpEditWindow, LpImage};

View file

@ -18,9 +18,8 @@
use decoder::DecoderError; use decoder::DecoderError;
use crate::widgets::LpEditWindow;
use super::*; use super::*;
use crate::widgets::LpEditWindow;
impl imp::LpImage { impl imp::LpImage {
// Set filename when (re)loading image // Set filename when (re)loading image
@ -290,14 +289,14 @@ impl imp::LpImage {
async fn check_editable(&self) { async fn check_editable(&self) {
let obj = self.obj(); let obj = self.obj();
if let Some(mime_type) = dbg!(obj.metadata().mime_type()) { if let Some(mime_type) = obj.metadata().mime_type() {
if let Ok(supported_formats) = dbg!(glycin::config::Config::cached() if let Ok(supported_formats) = glycin::config::Config::cached()
.await .await
.editor(&mime_type.as_str().into())) .editor(&mime_type.as_str().into())
{ {
if dbg!(LpEditWindow::REQUIRED_OPERATIONS if LpEditWindow::REQUIRED_OPERATIONS
.iter() .iter()
.all(|x| supported_formats.operations.contains(x))) .all(|x| supported_formats.operations.contains(x))
{ {
self.editable.set(true); self.editable.set(true);
obj.notify_editable(); obj.notify_editable();

View file

@ -61,6 +61,9 @@ mod imp {
#[properties(wrapper_type = super::LpImageWindow)] #[properties(wrapper_type = super::LpImageWindow)]
#[template(file = "image_window.ui")] #[template(file = "image_window.ui")]
pub struct LpImageWindow { pub struct LpImageWindow {
#[template_child]
pub(super) shortcut_controller: TemplateChild<gtk::ShortcutController>,
#[template_child] #[template_child]
pub(super) headerbar: TemplateChild<adw::HeaderBar>, pub(super) headerbar: TemplateChild<adw::HeaderBar>,
#[template_child] #[template_child]
@ -140,7 +143,7 @@ mod imp {
); );
// Set up actions // Set up actions
Action::init_actions_and_bindings(klass); Action::init_actions(klass);
} }
} }
@ -153,6 +156,8 @@ mod imp {
let obj = self.obj(); let obj = self.obj();
Action::add_bindings(&obj);
self.forward_click_gesture.connect_pressed(glib::clone!( self.forward_click_gesture.connect_pressed(glib::clone!(
#[weak] #[weak]
obj, obj,

View file

@ -59,6 +59,11 @@
<template class="LpImageWindow" parent="AdwBin"> <template class="LpImageWindow" parent="AdwBin">
<property name="child"> <property name="child">
<object class="AdwToolbarView" id="toolbar_view"> <object class="AdwToolbarView" id="toolbar_view">
<child>
<object class="GtkShortcutController" id="shortcut_controller">
<property name="scope">global</property>
</object>
</child>
<child type="top"> <child type="top">
<object class="LpShyBin"> <object class="LpShyBin">
<property name="child"> <property name="child">

View file

@ -18,9 +18,11 @@
use std::ops::Deref; use std::ops::Deref;
use adw::subclass::prelude::*; use adw::subclass::prelude::*;
use gdk::{Key, ModifierType};
use gtk::prelude::*; use gtk::prelude::*;
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
use super::LpImageWindow;
use crate::deps::*; use crate::deps::*;
use crate::util::root::ParentWindow; use crate::util::root::ParentWindow;
use crate::util::{Direction, Position}; use crate::util::{Direction, Position};
@ -91,9 +93,25 @@ pub enum Action {
} }
impl Action { impl Action {
pub fn init_actions_and_bindings( pub fn add_bindings(win: &LpImageWindow) {
klass: &mut <super::imp::LpImageWindow as ObjectSubclass>::Class, for action in Self::iter() {
) { for (key, modifiers) in action.keybindings() {
let mut modifier = ModifierType::empty();
for m in *modifiers {
modifier = modifier.union(*m);
}
let shortcut = gtk::Shortcut::new(
Some(gtk::KeyvalTrigger::new(*key, modifier)),
Some(gtk::NamedAction::new(&action)),
);
win.imp().shortcut_controller.add_shortcut(shortcut);
}
}
}
pub fn init_actions(klass: &mut <super::imp::LpImageWindow as ObjectSubclass>::Class) {
for action in Self::iter() { for action in Self::iter() {
match action { match action {
// Navigation // Navigation
@ -125,14 +143,12 @@ impl Action {
klass.install_action(&action, None, move |win, _, _| { klass.install_action(&action, None, move |win, _, _| {
win.imp().image_view.jump(Position::First); win.imp().image_view.jump(Position::First);
}); });
klass.add_binding_action(gdk::Key::Home, gdk::ModifierType::empty(), &action);
} }
Action::Last => { Action::Last => {
klass.install_action(&action, None, move |win, _, _| { klass.install_action(&action, None, move |win, _, _| {
win.imp().image_view.jump(Position::Last); win.imp().image_view.jump(Position::Last);
}); });
klass.add_binding_action(gdk::Key::End, gdk::ModifierType::empty(), &action);
} }
// Rotation/Zoom // Rotation/Zoom
@ -140,18 +156,12 @@ impl Action {
klass.install_action(&action, None, |win, _, _| { klass.install_action(&action, None, |win, _, _| {
win.rotate_image(90.0); win.rotate_image(90.0);
}); });
klass.add_binding_action(gdk::Key::r, gdk::ModifierType::CONTROL_MASK, &action);
} }
Action::RotateCcw => { Action::RotateCcw => {
klass.install_action(&action, None, |win, _, _| { klass.install_action(&action, None, |win, _, _| {
win.rotate_image(-90.0); win.rotate_image(-90.0);
}); });
klass.add_binding_action(
gdk::Key::r,
gdk::ModifierType::CONTROL_MASK.union(gdk::ModifierType::SHIFT_MASK),
&action,
);
} }
Action::ZoomOutCenter => klass.install_action(&action, None, move |win, _, _| { Action::ZoomOutCenter => klass.install_action(&action, None, move |win, _, _| {
@ -166,118 +176,36 @@ impl Action {
klass.install_action(&action, None, move |win, _, _| { klass.install_action(&action, None, move |win, _, _| {
win.zoom_out_cursor(); win.zoom_out_cursor();
}); });
klass.add_binding_action(gdk::Key::minus, gdk::ModifierType::empty(), &action);
klass.add_binding_action(
gdk::Key::minus,
gdk::ModifierType::CONTROL_MASK,
&action,
);
klass.add_binding_action(
gdk::Key::KP_Subtract,
gdk::ModifierType::empty(),
&action,
);
klass.add_binding_action(
gdk::Key::KP_Subtract,
gdk::ModifierType::CONTROL_MASK,
&action,
);
} }
Action::ZoomInCursor => { Action::ZoomInCursor => {
klass.install_action(&action, None, move |win, _, _| { klass.install_action(&action, None, move |win, _, _| {
win.zoom_in_cursor(); win.zoom_in_cursor();
}); });
klass.add_binding_action(gdk::Key::equal, gdk::ModifierType::empty(), &action);
klass.add_binding_action(
gdk::Key::equal,
gdk::ModifierType::CONTROL_MASK,
&action,
);
klass.add_binding_action(gdk::Key::plus, gdk::ModifierType::empty(), &action);
klass.add_binding_action(
gdk::Key::plus,
gdk::ModifierType::CONTROL_MASK,
&action,
);
klass.add_binding_action(gdk::Key::KP_Add, gdk::ModifierType::empty(), &action);
klass.add_binding_action(
gdk::Key::KP_Add,
gdk::ModifierType::CONTROL_MASK,
&action,
);
} }
Action::ZoomBestFit => { Action::ZoomBestFit => {
klass.install_action(&action, None, move |win, _, _| { klass.install_action(&action, None, move |win, _, _| {
win.zoom_best_fit(); win.zoom_best_fit();
}); });
klass.add_binding_action(gdk::Key::_0, gdk::ModifierType::empty(), &action);
klass.add_binding_action(
gdk::Key::_0,
gdk::ModifierType::CONTROL_MASK,
&action,
);
klass.add_binding_action(gdk::Key::KP_0, gdk::ModifierType::empty(), &action);
klass.add_binding_action(
gdk::Key::KP_0,
gdk::ModifierType::CONTROL_MASK,
&action,
);
} }
Action::ZoomToExact100 => { Action::ZoomToExact100 => {
klass.install_action(&action, None, move |win, _, _| { klass.install_action(&action, None, move |win, _, _| {
win.zoom_to_exact(1.); win.zoom_to_exact(1.);
}); });
klass.add_binding_action(gdk::Key::_1, gdk::ModifierType::empty(), &action);
klass.add_binding_action(gdk::Key::KP_1, gdk::ModifierType::empty(), &action);
klass.add_binding_action(
gdk::Key::_1,
gdk::ModifierType::CONTROL_MASK,
&action,
);
klass.add_binding_action(
gdk::Key::KP_1,
gdk::ModifierType::CONTROL_MASK,
&action,
);
} }
Action::ZoomToExact200 => { Action::ZoomToExact200 => {
klass.install_action(&action, None, move |win, _, _| { klass.install_action(&action, None, move |win, _, _| {
win.zoom_to_exact(2.); win.zoom_to_exact(2.);
}); });
klass.add_binding_action(gdk::Key::_2, gdk::ModifierType::empty(), &action);
klass.add_binding_action(gdk::Key::KP_2, gdk::ModifierType::empty(), &action);
klass.add_binding_action(
gdk::Key::_2,
gdk::ModifierType::CONTROL_MASK,
&action,
);
klass.add_binding_action(
gdk::Key::KP_2,
gdk::ModifierType::CONTROL_MASK,
&action,
);
} }
Action::ZoomToExact300 => { Action::ZoomToExact300 => {
klass.install_action(&action, None, move |win, _, _| { klass.install_action(&action, None, move |win, _, _| {
win.zoom_to_exact(2.); win.zoom_to_exact(2.);
}); });
klass.add_binding_action(gdk::Key::_3, gdk::ModifierType::empty(), &action);
klass.add_binding_action(gdk::Key::KP_3, gdk::ModifierType::empty(), &action);
klass.add_binding_action(
gdk::Key::_3,
gdk::ModifierType::CONTROL_MASK,
&action,
);
klass.add_binding_action(
gdk::Key::KP_3,
gdk::ModifierType::CONTROL_MASK,
&action,
);
} }
// Misc // Misc
@ -285,25 +213,18 @@ impl Action {
klass.install_action_async(&action, None, |win, _, _| async move { klass.install_action_async(&action, None, |win, _, _| async move {
win.pick_file().await; win.pick_file().await;
}); });
klass.add_binding_action(gdk::Key::O, gdk::ModifierType::CONTROL_MASK, &action);
} }
Action::OpenWith => { Action::OpenWith => {
klass.install_action_async(&action, None, |win, _, _| async move { klass.install_action_async(&action, None, |win, _, _| async move {
win.open_with().await; win.open_with().await;
}); });
klass.add_binding_action(
gdk::Key::O,
gdk::ModifierType::CONTROL_MASK.union(gdk::ModifierType::SHIFT_MASK),
&action,
);
} }
Action::Print => { Action::Print => {
klass.install_action(&action, None, move |win, _, _| { klass.install_action(&action, None, move |win, _, _| {
win.print(); win.print();
}); });
klass.add_binding_action(gdk::Key::P, gdk::ModifierType::CONTROL_MASK, &action);
} }
Action::CopyImage => klass.install_action(&action, None, move |win, _, _| { Action::CopyImage => klass.install_action(&action, None, move |win, _, _| {
@ -314,11 +235,6 @@ impl Action {
klass.install_action_async(&action, None, |win, _, _| async move { klass.install_action_async(&action, None, |win, _, _| async move {
win.set_background().await; win.set_background().await;
}); });
klass.add_binding_action(
gdk::Key::F8,
gdk::ModifierType::CONTROL_MASK,
&action,
);
} }
Action::ToggleFullscreen => { Action::ToggleFullscreen => {
@ -326,43 +242,24 @@ impl Action {
let win = obj.window(); let win = obj.window();
win.toggle_fullscreen(!win.is_fullscreen()); win.toggle_fullscreen(!win.is_fullscreen());
}); });
klass.add_binding_action(gdk::Key::F11, gdk::ModifierType::empty(), &action);
} }
Action::LeaveFullscreen => { Action::LeaveFullscreen => {
klass.install_action(&action, None, move |obj, _, _| { klass.install_action(&action, None, move |obj, _, _| {
obj.window().toggle_fullscreen(false); obj.window().toggle_fullscreen(false);
}); });
klass.add_binding_action(gdk::Key::Escape, gdk::ModifierType::empty(), &action);
} }
Action::Trash => { Action::Trash => {
klass.install_action_async(&action, None, |win, _, _| async move { klass.install_action_async(&action, None, |win, _, _| async move {
win.trash().await; win.trash().await;
}); });
klass.add_binding_action(
gdk::Key::KP_Delete,
gdk::ModifierType::empty(),
&action,
);
// The binding added last is shown in menus
klass.add_binding_action(gdk::Key::Delete, gdk::ModifierType::empty(), &action);
} }
Action::Delete => { Action::Delete => {
klass.install_action_async(&action, None, |win, _, _| async move { klass.install_action_async(&action, None, |win, _, _| async move {
win.delete().await; win.delete().await;
}); });
klass.add_binding_action(
gdk::Key::Delete,
gdk::ModifierType::SHIFT_MASK,
&action,
);
klass.add_binding_action(
gdk::Key::KP_Delete,
gdk::ModifierType::SHIFT_MASK,
&action,
);
} }
Action::ToggleProperties => { Action::ToggleProperties => {
@ -371,12 +268,6 @@ impl Action {
.properties_button .properties_button
.set_active(!win.imp().properties_button.is_active()); .set_active(!win.imp().properties_button.is_active());
}); });
klass.add_binding_action(gdk::Key::F9, gdk::ModifierType::empty(), &action);
klass.add_binding_action(
gdk::Key::Return,
gdk::ModifierType::ALT_MASK,
&action,
);
} }
Action::About => { Action::About => {
@ -393,18 +284,94 @@ impl Action {
log::error!("No current image to reload"); log::error!("No current image to reload");
} }
}); });
klass.add_binding_action(gdk::Key::F5, gdk::ModifierType::empty(), &action);
} }
Action::Edit => { Action::Edit => {
klass.install_action_async(&action, None, move |obj, _, _| async move { klass.install_action_async(&action, None, move |obj, _, _| async move {
obj.window().show_edit(); obj.window().show_edit();
}); });
klass.add_binding_action(gdk::Key::e, gdk::ModifierType::empty(), &action);
} }
} }
} }
} }
pub fn keybindings(&self) -> &[(gdk::Key, &[gdk::ModifierType])] {
match self {
Action::First => &[(Key::Home, &[])],
Action::Last => &[(Key::End, &[])],
// Rotation/Zoom
Action::RotateCw => &[(Key::r, &[ModifierType::CONTROL_MASK])],
Action::RotateCcw => &[(
Key::r,
&[ModifierType::CONTROL_MASK, ModifierType::SHIFT_MASK],
)],
Action::ZoomOutCursor => &[
(Key::minus, &[]),
(Key::minus, &[ModifierType::CONTROL_MASK]),
(Key::KP_Subtract, &[]),
(Key::KP_Subtract, &[ModifierType::CONTROL_MASK]),
],
Action::ZoomInCursor => &[
(Key::equal, &[]),
(Key::equal, &[ModifierType::CONTROL_MASK]),
(Key::plus, &[]),
(Key::plus, &[ModifierType::CONTROL_MASK]),
(Key::KP_Add, &[]),
(Key::KP_Add, &[ModifierType::CONTROL_MASK]),
],
Action::ZoomBestFit => &[
(Key::_0, &[]),
(Key::_0, &[ModifierType::CONTROL_MASK]),
(Key::KP_0, &[]),
(Key::KP_0, &[ModifierType::CONTROL_MASK]),
],
Action::ZoomToExact100 => &[
(Key::_1, &[]),
(Key::_1, &[ModifierType::CONTROL_MASK]),
(Key::KP_1, &[]),
(Key::KP_1, &[ModifierType::CONTROL_MASK]),
],
Action::ZoomToExact200 => &[
(Key::_2, &[]),
(Key::_2, &[ModifierType::CONTROL_MASK]),
(Key::KP_2, &[]),
(Key::KP_2, &[ModifierType::CONTROL_MASK]),
],
Action::ZoomToExact300 => &[
(Key::_3, &[]),
(Key::_3, &[ModifierType::CONTROL_MASK]),
(Key::KP_3, &[]),
(Key::KP_3, &[ModifierType::CONTROL_MASK]),
],
// Misc
Action::Open => &[(Key::o, &[ModifierType::CONTROL_MASK])],
Action::OpenWith => &[(
Key::o,
&[ModifierType::CONTROL_MASK, gdk::ModifierType::SHIFT_MASK],
)],
Action::Print => &[(Key::p, &[ModifierType::CONTROL_MASK])],
Action::SetBackground => &[(Key::F8, &[ModifierType::CONTROL_MASK])],
Action::ToggleFullscreen => &[(Key::F11, &[])],
Action::LeaveFullscreen => &[(Key::Escape, &[])],
Action::Trash => &[
(Key::KP_Delete, &[]),
// The binding added last is shown in menus
(Key::Delete, &[]),
],
Action::Delete => &[
(Key::Delete, &[ModifierType::SHIFT_MASK]),
(Key::KP_Delete, &[ModifierType::SHIFT_MASK]),
],
Action::ToggleProperties => &[(Key::F9, &[]), (Key::Return, &[ModifierType::ALT_MASK])],
Action::Reload => &[(Key::F5, &[])],
Action::Edit => &[(Key::e, &[])],
_ => &[],
}
}
} }
impl Deref for Action { impl Deref for Action {