Keyboard shortcut fixes

#228
This commit is contained in:
Ben Olden-Cooligan 2024-04-07 11:19:51 -07:00
parent 5e7748cb32
commit af300170ab
23 changed files with 231 additions and 194 deletions

View File

@ -143,6 +143,22 @@ public class MacEtoPlatform : EtoPlatform
} }
} }
public override void HandleKeyDown(Control control, Func<Keys, bool> handle)
{
var view = control.ToNative();
var monitor = NSEvent.AddLocalMonitorForEventsMatchingMask(NSEventMask.KeyDown, evt =>
{
if (ReferenceEquals(evt.Window, view.Window) &&
view.HitTest(evt.LocationInWindow) != null!)
{
var args = evt.ToEtoKeyEventArgs();
return handle(args.KeyData) ? null! : evt;
}
return evt;
});
control.UnLoad += (_, _) => NSEvent.RemoveMonitor(monitor);
}
public override void AttachMouseWheelEvent(Control control, EventHandler<MouseEventArgs> eventHandler) public override void AttachMouseWheelEvent(Control control, EventHandler<MouseEventArgs> eventHandler)
{ {
var view = control.ToNative(); var view = control.ToNative();

View File

@ -50,10 +50,6 @@ public class MacDesktopForm : DesktopForm
{ {
Commands.MoveDown.ToolBarText = ""; Commands.MoveDown.ToolBarText = "";
Commands.MoveUp.ToolBarText = ""; Commands.MoveUp.ToolBarText = "";
Commands.SaveAllPdf.Shortcut = Application.Instance.CommonModifier | Keys.S;
Commands.SaveSelectedPdf.Shortcut = Application.Instance.CommonModifier | Keys.Shift | Keys.S;
Commands.SaveAllImages.Shortcut = Application.Instance.CommonModifier | Keys.M;
Commands.SaveSelectedImages.Shortcut = Application.Instance.CommonModifier | Keys.Shift | Keys.M;
Menu = new MenuBar Menu = new MenuBar
{ {

View File

@ -9,8 +9,8 @@ public class MacPreviewForm : PreviewForm
private readonly NSSlider _zoomSlider; private readonly NSSlider _zoomSlider;
public MacPreviewForm(Naps2Config config, DesktopCommands desktopCommands, UiImageList imageList, public MacPreviewForm(Naps2Config config, DesktopCommands desktopCommands, UiImageList imageList,
IIconProvider iconProvider, KeyboardShortcutManager ksm, ColorScheme colorScheme) : base(config, IIconProvider iconProvider, ColorScheme colorScheme) : base(config,
desktopCommands, imageList, iconProvider, ksm, colorScheme) desktopCommands, imageList, iconProvider, colorScheme)
{ {
_zoomSlider = new NSSlider _zoomSlider = new NSSlider
{ {

View File

@ -277,6 +277,9 @@
&lt;OcrDefaultAfterScanning&gt;true&lt;/OcrDefaultAfterScanning&gt; &lt;OcrDefaultAfterScanning&gt;true&lt;/OcrDefaultAfterScanning&gt;
&lt;ForcePdfCompat&gt;PdfA1B&lt;/ForcePdfCompat&gt; &lt;ForcePdfCompat&gt;PdfA1B&lt;/ForcePdfCompat&gt;
&lt;EventLogging&gt;None&lt;/EventLogging&gt; &lt;EventLogging&gt;None&lt;/EventLogging&gt;
&lt;KeyboardShortcuts&gt;
&lt;ScanDefault&gt;Ctrl+Enter&lt;/ScanDefault&gt;
&lt;/KeyboardShortcuts&gt;
&lt;/AppConfig&gt;</value> &lt;/AppConfig&gt;</value>
</data> </data>
<data name="NewAppSettings" xml:space="preserve"> <data name="NewAppSettings" xml:space="preserve">

View File

@ -127,6 +127,10 @@ public class FileConfigScopeTests : ContextualTests
Assert.False(defaultsScope.Has(c => c.PdfSettings.Compat)); Assert.False(defaultsScope.Has(c => c.PdfSettings.Compat));
Assert.True(defaultsScope.TryGet(c => c.AlwaysRememberDevice, out var alwaysRememberDevice)); Assert.True(defaultsScope.TryGet(c => c.AlwaysRememberDevice, out var alwaysRememberDevice));
Assert.True(alwaysRememberDevice); Assert.True(alwaysRememberDevice);
Assert.False(lockedScope.Has(c => c.KeyboardShortcuts.ScanDefault));
Assert.True(defaultsScope.TryGet(c => c.KeyboardShortcuts.ScanDefault, out var scanDefault));
Assert.Equal("Mod+Enter", scanDefault);
} }
[Fact] [Fact]

View File

@ -268,6 +268,11 @@ public class WinFormsEtoPlatform : EtoPlatform
return SD.Icon.ExtractAssociatedIcon(exePath)?.ToBitmap().ToEto(); return SD.Icon.ExtractAssociatedIcon(exePath)?.ToBitmap().ToEto();
} }
public override void HandleKeyDown(Control control, Func<Keys, bool> handle)
{
control.ToNative().KeyDown += (_, args) => args.Handled = handle(args.KeyData.ToEto());
}
public override void AttachMouseWheelEvent(Control control, EventHandler<MouseEventArgs> eventHandler) public override void AttachMouseWheelEvent(Control control, EventHandler<MouseEventArgs> eventHandler)
{ {
if (control is Scrollable scrollable) if (control is Scrollable scrollable)

View File

@ -124,7 +124,7 @@ public class ConfigSerializer : VersionedSerializer<ConfigStorage<CommonConfig>>
storage.Set(x => x.EsclSecurityPolicy, c.EsclSecurityPolicy); storage.Set(x => x.EsclSecurityPolicy, c.EsclSecurityPolicy);
storage.Set(x => x.EsclServerCertificatePath, c.EsclServerCertificatePath); storage.Set(x => x.EsclServerCertificatePath, c.EsclServerCertificatePath);
storage.Set(x => x.EventLogging, c.EventLogging); storage.Set(x => x.EventLogging, c.EventLogging);
storage.Set(x => x.KeyboardShortcuts, c.KeyboardShortcuts ?? new KeyboardShortcuts()); storage.Set(x => x.KeyboardShortcuts, MapKeyboardShortcuts(c.KeyboardShortcuts ?? new KeyboardShortcuts()));
return storage; return storage;
} }
@ -191,6 +191,18 @@ public class ConfigSerializer : VersionedSerializer<ConfigStorage<CommonConfig>>
return flags; return flags;
} }
private KeyboardShortcuts MapKeyboardShortcuts(KeyboardShortcuts keyboardShortcuts)
{
// To be platform-independent, replace "Ctrl+" with "Mod+", which means "Ctrl" on Window/Linux and "Cmd" on Mac
var serializer = new XmlSerializer<KeyboardShortcuts>();
var doc = serializer.SerializeToXDocument(keyboardShortcuts);
foreach (var node in doc.Root!.Elements())
{
node.Value = node.Value.Replace("Ctrl+", "Mod+");
}
return serializer.DeserializeFromXDocument(doc)!;
}
private static ConfigStorage<CommonConfig> UserConfigV0ToCommonConfig(UserConfigV0 c) private static ConfigStorage<CommonConfig> UserConfigV0ToCommonConfig(UserConfigV0 c)
{ {
var storage = new ConfigStorage<CommonConfig>(); var storage = new ConfigStorage<CommonConfig>();

View File

@ -129,7 +129,7 @@ public static class InternalDefaults
}, },
KeyboardShortcuts = new KeyboardShortcuts KeyboardShortcuts = new KeyboardShortcuts
{ {
ScanDefault = "Ctrl+Enter", ScanDefault = "Mod+Enter",
ScanProfile1 = "F2", ScanProfile1 = "F2",
ScanProfile2 = "F3", ScanProfile2 = "F3",
ScanProfile3 = "F4", ScanProfile3 = "F4",
@ -142,21 +142,25 @@ public static class InternalDefaults
ScanProfile10 = "F11", ScanProfile10 = "F11",
ScanProfile11 = "F12", ScanProfile11 = "F12",
ScanProfile12 = "", ScanProfile12 = "",
NewProfile = "", NewProfile = "Mod+N",
BatchScan = "Ctrl+B", BatchScan = "Mod+B",
Profiles = "", Profiles = "Mod+L",
Ocr = "", ScannerSharing = "Mod+G",
Import = "Ctrl+O", Ocr = "Mod+Alt+O",
SavePDF = "Ctrl+S", Import = "Mod+O",
SavePDFAll = "", SavePDF = "",
SavePDFSelected = "", SavePDFAll = "Mod+S",
SavePDFSelected = "Mod+Shift+S",
PDFSettings = "Mod+Alt+P",
SaveImages = "", SaveImages = "",
SaveImagesAll = "", SaveImagesAll = "Mod+I",
SaveImagesSelected = "", SaveImagesSelected = "Mod+Shift+I",
ImageSettings = "Mod+Alt+I",
EmailPDF = "", EmailPDF = "",
EmailPDFAll = "", EmailPDFAll = "Mod+E",
EmailPDFSelected = "", EmailPDFSelected = "Mod+Shift+E",
Print = "Ctrl+P", EmailSettings = "Mod+Alt+E",
Print = "Mod+P",
ImageView = "", ImageView = "",
ImageBlackWhite = "", ImageBlackWhite = "",
ImageBrightness = "", ImageBrightness = "",
@ -166,12 +170,12 @@ public static class InternalDefaults
ImageReset = "", ImageReset = "",
ImageSaturation = "", ImageSaturation = "",
ImageSharpen = "", ImageSharpen = "",
RotateLeft = "", RotateLeft = "Mod+Shift+Left",
RotateRight = "", RotateRight = "Mod+Shift+Right",
RotateFlip = "", RotateFlip = "Mod+Shift+Down",
RotateCustom = "", RotateCustom = "",
MoveUp = "Ctrl+Up", MoveUp = "Mod+Up",
MoveDown = "Ctrl+Down", MoveDown = "Mod+Down",
ReorderInterleave = "", ReorderInterleave = "",
ReorderDeinterleave = "", ReorderDeinterleave = "",
ReorderAltInterleave = "", ReorderAltInterleave = "",
@ -179,10 +183,10 @@ public static class InternalDefaults
ReorderReverseAll = "", ReorderReverseAll = "",
ReorderReverseSelected = "", ReorderReverseSelected = "",
Delete = "", Delete = "",
Clear = "Ctrl+Shift+Del", Clear = "Mod+Shift+Del",
About = "F1", About = "F1",
ZoomIn = "Ctrl+Oemplus", ZoomIn = "Mod+Oemplus",
ZoomOut = "Ctrl+OemMinus" ZoomOut = "Mod+OemMinus"
}, },
DefaultProfileSettings = new ScanProfile { Version = ScanProfile.CURRENT_VERSION } DefaultProfileSettings = new ScanProfile { Version = ScanProfile.CURRENT_VERSION }
}; };

View File

@ -19,22 +19,24 @@ public class KeyboardShortcuts
public string? BatchScan { get; set; } public string? BatchScan { get; set; }
public string? Profiles { get; set; } public string? Profiles { get; set; }
public string? ScannerSharing { get; set; }
public string? Ocr { get; set; } public string? Ocr { get; set; }
public string? Import { get; set; } public string? Import { get; set; }
public string? SavePDF { get; set; } public string? SavePDF { get; set; }
public string? SavePDFAll { get; set; } public string? SavePDFAll { get; set; }
public string? SavePDFSelected { get; set; } public string? SavePDFSelected { get; set; }
public string? PDFSettings { get; set; }
public string? SaveImages { get; set; } public string? SaveImages { get; set; }
public string? SaveImagesAll { get; set; } public string? SaveImagesAll { get; set; }
public string? SaveImagesSelected { get; set; } public string? SaveImagesSelected { get; set; }
public string? ImageSettings { get; set; }
public string? EmailPDF { get; set; } public string? EmailPDF { get; set; }
public string? EmailPDFAll { get; set; } public string? EmailPDFAll { get; set; }
public string? EmailPDFSelected { get; set; } public string? EmailPDFSelected { get; set; }
public string? EmailSettings { get; set; }
public string? Print { get; set; } public string? Print { get; set; }

View File

@ -16,28 +16,20 @@ public class DesktopKeyboardShortcuts
public void Assign(DesktopCommands commands) public void Assign(DesktopCommands commands)
{ {
// Defaults // Unconfigurable defaults
_ksm.Assign("Ctrl+Enter", commands.Scan); _ksm.Assign("Mod+.", commands.Scan);
_ksm.Assign("Ctrl+B", commands.BatchScan); _ksm.Assign("Mod+Up", commands.MoveUp);
_ksm.Assign("Ctrl+O", commands.Import); _ksm.Assign("Mod+Left", commands.MoveUp);
_ksm.Assign("Ctrl+S", commands.SavePdf); _ksm.Assign("Mod+Down", commands.MoveDown);
_ksm.Assign("Ctrl+P", commands.Print); _ksm.Assign("Mod+Right", commands.MoveDown);
_ksm.Assign("Ctrl+Up", commands.MoveUp);
_ksm.Assign("Ctrl+Left", commands.MoveUp);
_ksm.Assign("Ctrl+Down", commands.MoveDown);
_ksm.Assign("Ctrl+Right", commands.MoveDown);
_ksm.Assign("Ctrl+Shift+Del", commands.ClearAll);
_ksm.Assign("F1", commands.About);
_ksm.Assign("Ctrl+OemMinus", commands.ZoomOut);
_ksm.Assign("Ctrl+Oemplus", commands.ZoomIn);
_ksm.Assign("Del", commands.Delete); _ksm.Assign("Del", commands.Delete);
_ksm.Assign("Ctrl+A", commands.SelectAll); _ksm.Assign("Mod+A", commands.SelectAll);
_ksm.Assign("Ctrl+C", commands.Copy); _ksm.Assign("Mod+C", commands.Copy);
_ksm.Assign("Ctrl+V", commands.Paste); _ksm.Assign("Mod+V", commands.Paste);
_ksm.Assign("Ctrl+Z", commands.Undo); _ksm.Assign("Mod+Z", commands.Undo);
_ksm.Assign(EtoPlatform.Current.IsGtk ? "Ctrl+Shift+Z" : "Ctrl+Y", commands.Redo); _ksm.Assign(EtoPlatform.Current.IsWinForms ? "Mod+Y" : "Mod+Shift+Z", commands.Redo);
// Configured // Configured defaults
var ks = _config.Get(c => c.KeyboardShortcuts); var ks = _config.Get(c => c.KeyboardShortcuts);
@ -50,6 +42,7 @@ public class DesktopKeyboardShortcuts
_ksm.Assign(ks.EmailPDF, commands.EmailPdf); _ksm.Assign(ks.EmailPDF, commands.EmailPdf);
_ksm.Assign(ks.EmailPDFAll, commands.EmailAll); _ksm.Assign(ks.EmailPDFAll, commands.EmailAll);
_ksm.Assign(ks.EmailPDFSelected, commands.EmailSelected); _ksm.Assign(ks.EmailPDFSelected, commands.EmailSelected);
_ksm.Assign(ks.EmailSettings, commands.EmailSettings);
} }
_ksm.Assign(ks.ImageBlackWhite, commands.BlackWhite); _ksm.Assign(ks.ImageBlackWhite, commands.BlackWhite);
_ksm.Assign(ks.ImageBrightness, commands.BrightCont); _ksm.Assign(ks.ImageBrightness, commands.BrightCont);
@ -84,13 +77,24 @@ public class DesktopKeyboardShortcuts
_ksm.Assign(ks.RotateFlip, commands.Flip); _ksm.Assign(ks.RotateFlip, commands.Flip);
_ksm.Assign(ks.RotateLeft, commands.RotateLeft); _ksm.Assign(ks.RotateLeft, commands.RotateLeft);
_ksm.Assign(ks.RotateRight, commands.RotateRight); _ksm.Assign(ks.RotateRight, commands.RotateRight);
_ksm.Assign(ks.SaveImages, commands.SaveImages); if (PlatformCompat.System.CombinedPdfAndImageSaving)
_ksm.Assign(ks.SaveImagesAll, commands.SaveAllImages); {
_ksm.Assign(ks.SaveImagesSelected, commands.SaveSelectedImages); _ksm.Assign(ks.SavePDFAll, commands.SaveAll);
_ksm.Assign(ks.SavePDF, commands.SavePdf); _ksm.Assign(ks.SavePDFSelected, commands.SaveSelected);
_ksm.Assign(ks.SavePDFAll, commands.SaveAllPdf); }
_ksm.Assign(ks.SavePDFSelected, commands.SaveSelectedPdf); else
{
_ksm.Assign(ks.SaveImages, commands.SaveImages);
_ksm.Assign(ks.SaveImagesAll, commands.SaveAllImages);
_ksm.Assign(ks.SaveImagesSelected, commands.SaveSelectedImages);
_ksm.Assign(ks.SavePDF, commands.SavePdf);
_ksm.Assign(ks.SavePDFAll, commands.SaveAllPdf);
_ksm.Assign(ks.SavePDFSelected, commands.SaveSelectedPdf);
}
_ksm.Assign(ks.PDFSettings, commands.PdfSettings);
_ksm.Assign(ks.ImageSettings, commands.ImageSettings);
_ksm.Assign(ks.ScanDefault, commands.Scan); _ksm.Assign(ks.ScanDefault, commands.Scan);
_ksm.Assign(ks.ScannerSharing, commands.ScannerSharing);
_ksm.Assign(ks.ZoomIn, commands.ZoomIn); _ksm.Assign(ks.ZoomIn, commands.ZoomIn);
_ksm.Assign(ks.ZoomOut, commands.ZoomOut); _ksm.Assign(ks.ZoomOut, commands.ZoomOut);

View File

@ -16,6 +16,11 @@ public abstract class EtoDialogBase : Dialog, IFormBase
ShowInTaskbar = true; ShowInTaskbar = true;
LayoutController.Bind(this); LayoutController.Bind(this);
LayoutController.Invalidated += (_, _) => FormStateController.UpdateLayoutSize(LayoutController); LayoutController.Invalidated += (_, _) => FormStateController.UpdateLayoutSize(LayoutController);
if (EtoPlatform.Current.IsMac)
{
// Always have a basic menu on Mac, otherwise system keyboard shortcuts like Copy/Paste don't work
Menu = new MenuBar();
}
} }
protected abstract void BuildLayout(); protected abstract void BuildLayout();

View File

@ -131,6 +131,11 @@ public abstract class EtoPlatform
public virtual Bitmap? ExtractAssociatedIcon(string exePath) => throw new NotSupportedException(); public virtual Bitmap? ExtractAssociatedIcon(string exePath) => throw new NotSupportedException();
public virtual void HandleKeyDown(Control control, Func<Keys, bool> handle)
{
control.KeyDown += (_, args) => args.Handled = handle(args.KeyData);
}
public virtual void AttachMouseWheelEvent(Control control, EventHandler<MouseEventArgs> eventHandler) public virtual void AttachMouseWheelEvent(Control control, EventHandler<MouseEventArgs> eventHandler)
{ {
control.MouseWheel += eventHandler; control.MouseWheel += eventHandler;

View File

@ -1,6 +1,6 @@
using Eto.Forms; using Eto.Forms;
namespace NAPS2.EtoForms.Desktop; namespace NAPS2.EtoForms;
/// <summary> /// <summary>
/// A helper class to assign keyboard shortcuts to commands. /// A helper class to assign keyboard shortcuts to commands.
@ -12,12 +12,16 @@ public class KeyboardShortcutManager
private readonly Dictionary<string, Keys> _customMap = new() private readonly Dictionary<string, Keys> _customMap = new()
{ {
{ "mod", Application.Instance.CommonModifier },
{ "cmd", Keys.Application },
{ "ctrl", Keys.Control }, { "ctrl", Keys.Control },
{ "del", Keys.Delete }, { "del", Keys.Delete },
{ "ins", Keys.Insert }, { "ins", Keys.Insert },
{ "break", Keys.Pause }, { "break", Keys.Pause },
{ "oemplus", Keys.Equal }, { "oemplus", Keys.Equal },
{ "oemminus", Keys.Minus }, { "oemminus", Keys.Minus },
{ "esc", Keys.Escape },
{ ".", Keys.Period },
{ "0", Keys.D0 }, { "0", Keys.D0 },
{ "1", Keys.D1 }, { "1", Keys.D1 },
{ "2", Keys.D2 }, { "2", Keys.D2 },

View File

@ -32,56 +32,47 @@ public class DesktopCommands
Scan = new ActionCommand(desktopScanController.ScanDefault) Scan = new ActionCommand(desktopScanController.ScanDefault)
{ {
Text = UiStrings.Scan, Text = UiStrings.Scan,
Image = iconProvider.GetIcon("control_play_blue"), Image = iconProvider.GetIcon("control_play_blue")
Shortcut = Application.Instance.CommonModifier | Keys.Period
}; };
NewProfile = new ActionCommand(desktopScanController.ScanWithNewProfile) NewProfile = new ActionCommand(desktopScanController.ScanWithNewProfile)
{ {
Text = UiStrings.NewProfile, Text = UiStrings.NewProfile,
Image = iconProvider.GetIcon("add_small"), Image = iconProvider.GetIcon("add_small")
Shortcut = Application.Instance.CommonModifier | Keys.N
}; };
BatchScan = new ActionCommand(desktopSubFormController.ShowBatchScanForm) BatchScan = new ActionCommand(desktopSubFormController.ShowBatchScanForm)
{ {
Text = UiStrings.BatchScan, Text = UiStrings.BatchScan,
Image = iconProvider.GetIcon("application_cascade"), Image = iconProvider.GetIcon("application_cascade")
Shortcut = Application.Instance.CommonModifier | Keys.B
}; };
ScannerSharing = new ActionCommand(desktopSubFormController.ShowScannerSharingForm) ScannerSharing = new ActionCommand(desktopSubFormController.ShowScannerSharingForm)
{ {
Text = UiStrings.ScannerSharing, Text = UiStrings.ScannerSharing,
Image = iconProvider.GetIcon("wireless16"), Image = iconProvider.GetIcon("wireless16")
Shortcut = Application.Instance.CommonModifier | Keys.G
}; };
Profiles = new ActionCommand(desktopSubFormController.ShowProfilesForm) Profiles = new ActionCommand(desktopSubFormController.ShowProfilesForm)
{ {
Text = UiStrings.Profiles, Text = UiStrings.Profiles,
Image = iconProvider.GetIcon("blueprints"), Image = iconProvider.GetIcon("blueprints")
Shortcut = Application.Instance.CommonModifier | Keys.L
}; };
Ocr = new ActionCommand(desktopSubFormController.ShowOcrForm) Ocr = new ActionCommand(desktopSubFormController.ShowOcrForm)
{ {
Text = UiStrings.Ocr, Text = UiStrings.Ocr,
Image = iconProvider.GetIcon("text"), Image = iconProvider.GetIcon("text")
Shortcut = Application.Instance.CommonModifier | Keys.Alt | Keys.O
}; };
Import = new ActionCommand(desktopController.Import) Import = new ActionCommand(desktopController.Import)
{ {
Text = UiStrings.Import, Text = UiStrings.Import,
Image = iconProvider.GetIcon("folder_picture"), Image = iconProvider.GetIcon("folder_picture")
Shortcut = Application.Instance.CommonModifier | Keys.O
}; };
SaveAll = new ActionCommand(_imageListActions.SaveAllAsPdfOrImages) SaveAll = new ActionCommand(_imageListActions.SaveAllAsPdfOrImages)
{ {
Text = UiStrings.SaveAll, Text = UiStrings.SaveAll,
Image = iconProvider.GetIcon("diskette"), Image = iconProvider.GetIcon("diskette")
Shortcut = Application.Instance.CommonModifier | Keys.S
}; };
SaveSelected = new ActionCommand(_imageListActions.SaveSelectedAsPdfOrImages) SaveSelected = new ActionCommand(_imageListActions.SaveSelectedAsPdfOrImages)
{ {
Text = UiStrings.SaveSelected, Text = UiStrings.SaveSelected,
Image = iconProvider.GetIcon("diskette"), Image = iconProvider.GetIcon("diskette")
Shortcut = Application.Instance.CommonModifier | Keys.Shift | Keys.S
}; };
SavePdf = new ActionCommand(desktopController.SavePdf) SavePdf = new ActionCommand(desktopController.SavePdf)
{ {
@ -98,8 +89,7 @@ public class DesktopCommands
}; };
PdfSettings = new ActionCommand(desktopSubFormController.ShowPdfSettingsForm) PdfSettings = new ActionCommand(desktopSubFormController.ShowPdfSettingsForm)
{ {
Text = UiStrings.PdfSettings, Text = UiStrings.PdfSettings
Shortcut = Application.Instance.CommonModifier | Keys.Alt | Keys.P
}; };
SaveImages = new ActionCommand(desktopController.SaveImages) SaveImages = new ActionCommand(desktopController.SaveImages)
{ {
@ -116,8 +106,7 @@ public class DesktopCommands
}; };
ImageSettings = new ActionCommand(desktopSubFormController.ShowImageSettingsForm) ImageSettings = new ActionCommand(desktopSubFormController.ShowImageSettingsForm)
{ {
Text = UiStrings.ImageSettings, Text = UiStrings.ImageSettings
Shortcut = Application.Instance.CommonModifier | Keys.Alt | Keys.I
}; };
EmailPdf = new ActionCommand(desktopController.EmailPdf) EmailPdf = new ActionCommand(desktopController.EmailPdf)
{ {
@ -126,24 +115,20 @@ public class DesktopCommands
}; };
EmailAll = new ActionCommand(imageListActions.EmailAllAsPdf) EmailAll = new ActionCommand(imageListActions.EmailAllAsPdf)
{ {
Text = UiStrings.EmailAll, Text = UiStrings.EmailAll
Shortcut = Application.Instance.CommonModifier | Keys.E
}; };
EmailSelected = new ActionCommand(imageListActions.EmailSelectedAsPdf) EmailSelected = new ActionCommand(imageListActions.EmailSelectedAsPdf)
{ {
Text = UiStrings.EmailSelected, Text = UiStrings.EmailSelected
Shortcut = Application.Instance.CommonModifier | Keys.Shift | Keys.E
}; };
EmailSettings = new ActionCommand(desktopSubFormController.ShowEmailSettingsForm) EmailSettings = new ActionCommand(desktopSubFormController.ShowEmailSettingsForm)
{ {
Text = UiStrings.EmailSettings, Text = UiStrings.EmailSettings
Shortcut = Application.Instance.CommonModifier | Keys.Alt | Keys.E
}; };
Print = new ActionCommand(desktopController.Print) Print = new ActionCommand(desktopController.Print)
{ {
Text = UiStrings.Print, Text = UiStrings.Print,
Image = iconProvider.GetIcon("printer"), Image = iconProvider.GetIcon("printer")
Shortcut = Application.Instance.CommonModifier | Keys.P
}; };
ImageMenu = new ActionCommand ImageMenu = new ActionCommand
{ {
@ -274,15 +259,13 @@ public class DesktopCommands
Delete = new ActionCommand(desktopController.Delete) Delete = new ActionCommand(desktopController.Delete)
{ {
Text = UiStrings.Delete, Text = UiStrings.Delete,
Image = iconProvider.GetIcon("cross"), Image = iconProvider.GetIcon("cross")
Shortcut = Keys.Delete
}; };
ClearAll = new ActionCommand(desktopController.Clear) ClearAll = new ActionCommand(desktopController.Clear)
{ {
ToolBarText = UiStrings.Clear, ToolBarText = UiStrings.Clear,
MenuText = UiStrings.ClearAll, MenuText = UiStrings.ClearAll,
Image = iconProvider.GetIcon("cancel"), Image = iconProvider.GetIcon("cancel")
Shortcut = Application.Instance.CommonModifier | Keys.Shift | Keys.Delete
}; };
LanguageMenu = new ActionCommand LanguageMenu = new ActionCommand
{ {
@ -313,34 +296,27 @@ public class DesktopCommands
}; };
SelectAll = new ActionCommand(imageListActions.SelectAll) SelectAll = new ActionCommand(imageListActions.SelectAll)
{ {
Text = UiStrings.SelectAll, Text = UiStrings.SelectAll
Shortcut = Application.Instance.CommonModifier | Keys.A
}; };
Copy = new ActionCommand(desktopController.Copy) Copy = new ActionCommand(desktopController.Copy)
{ {
Text = UiStrings.Copy, Text = UiStrings.Copy,
Image = iconProvider.GetIcon("copy"), Image = iconProvider.GetIcon("copy")
Shortcut = Application.Instance.CommonModifier | Keys.C
}; };
Paste = new ActionCommand(desktopController.Paste) Paste = new ActionCommand(desktopController.Paste)
{ {
Text = UiStrings.Paste, Text = UiStrings.Paste,
Image = iconProvider.GetIcon("paste"), Image = iconProvider.GetIcon("paste")
Shortcut = Application.Instance.CommonModifier | Keys.V
}; };
Undo = new ActionCommand(imageListActions.Undo) Undo = new ActionCommand(imageListActions.Undo)
{ {
Text = UiStrings.Undo, Text = UiStrings.Undo,
Image = iconProvider.GetIcon("undo"), Image = iconProvider.GetIcon("undo")
Shortcut = Application.Instance.CommonModifier | Keys.Z
}; };
Redo = new ActionCommand(imageListActions.Redo) Redo = new ActionCommand(imageListActions.Redo)
{ {
Text = UiStrings.Redo, Text = UiStrings.Redo,
Image = iconProvider.GetIcon("redo"), Image = iconProvider.GetIcon("redo")
Shortcut = EtoPlatform.Current.IsWinForms
? Application.Instance.CommonModifier | Keys.Y
: Application.Instance.CommonModifier | Keys.Shift | Keys.Z
}; };
} }

View File

@ -69,12 +69,7 @@ public abstract class DesktopForm : EtoFormBase
_desktopSubFormController = desktopSubFormController; _desktopSubFormController = desktopSubFormController;
Commands = commands; Commands = commands;
if (!EtoPlatform.Current.IsMac) _keyboardShortcuts.Assign(Commands);
{
// For Mac the menu shortcuts work without needing manual hooks
// Maybe at some point we can support custom assignment on Mac, though we'll need to fix Ctrl vs Command
_keyboardShortcuts.Assign(Commands);
}
CreateToolbarsAndMenus(); CreateToolbarsAndMenus();
UpdateScanButton(); UpdateScanButton();
UpdateProfilesToolbar(); UpdateProfilesToolbar();
@ -91,10 +86,10 @@ public abstract class DesktopForm : EtoFormBase
// TODO: Fix Eto so that we don't need to set an item here (otherwise the first time we right click nothing happens) // TODO: Fix Eto so that we don't need to set an item here (otherwise the first time we right click nothing happens)
_contextMenu.Items.Add(Commands.SelectAll); _contextMenu.Items.Add(Commands.SelectAll);
_contextMenu.Opening += OpeningContextMenu; _contextMenu.Opening += OpeningContextMenu;
KeyDown += OnKeyDown;
_listView.Control.KeyDown += OnKeyDown;
_listView.Control.MouseWheel += ListViewMouseWheel; _listView.Control.MouseWheel += ListViewMouseWheel;
_listView.Control.MouseMove += ListViewMouseMove; _listView.Control.MouseMove += ListViewMouseMove;
EtoPlatform.Current.HandleKeyDown(this, _keyboardShortcuts.Perform);
EtoPlatform.Current.HandleKeyDown(_listView.Control, _keyboardShortcuts.Perform);
// //
// Shown += FDesktop_Shown; // Shown += FDesktop_Shown;
@ -527,11 +522,6 @@ public abstract class DesktopForm : EtoFormBase
_scanMenuCommands.Value = commandList; _scanMenuCommands.Value = commandList;
} }
private void OnKeyDown(object? sender, KeyEventArgs e)
{
e.Handled = _keyboardShortcuts.Perform(e.KeyData);
}
protected virtual void UpdateTitle(ScanProfile? defaultProfile) protected virtual void UpdateTitle(ScanProfile? defaultProfile)
{ {
Title = string.Format(UiStrings.Naps2TitleFormat, defaultProfile?.DisplayName ?? UiStrings.Naps2FullName); Title = string.Format(UiStrings.Naps2TitleFormat, defaultProfile?.DisplayName ?? UiStrings.Naps2FullName);

View File

@ -9,19 +9,18 @@ public class PreviewForm : EtoDialogBase
{ {
private readonly DesktopCommands _desktopCommands; private readonly DesktopCommands _desktopCommands;
private readonly IIconProvider _iconProvider; private readonly IIconProvider _iconProvider;
private readonly KeyboardShortcutManager _ksm; private readonly KeyboardShortcutManager _previewKsm;
private readonly ButtonToolItem _pageNumberButton = new(); private readonly ButtonToolItem _pageNumberButton = new();
private readonly ButtonToolItem _zoomPercentButton = new(); private readonly ButtonToolItem _zoomPercentButton = new();
private UiImage? _currentImage; private UiImage? _currentImage;
public PreviewForm(Naps2Config config, DesktopCommands desktopCommands, UiImageList imageList, public PreviewForm(Naps2Config config, DesktopCommands desktopCommands, UiImageList imageList,
IIconProvider iconProvider, KeyboardShortcutManager ksm, ColorScheme colorScheme) : base(config) IIconProvider iconProvider, ColorScheme colorScheme) : base(config)
{ {
_desktopCommands = desktopCommands; _desktopCommands = desktopCommands;
ImageList = imageList; ImageList = imageList;
_iconProvider = iconProvider; _iconProvider = iconProvider;
_ksm = ksm;
ImageViewer.ColorScheme = colorScheme; ImageViewer.ColorScheme = colorScheme;
ImageViewer.ZoomChanged += ImageViewerZoomChanged; ImageViewer.ZoomChanged += ImageViewerZoomChanged;
@ -63,6 +62,9 @@ public class PreviewForm : EtoDialogBase
Text = UiStrings.Delete, Text = UiStrings.Delete,
Image = iconProvider.GetIcon("cross") Image = iconProvider.GetIcon("cross")
}; };
_previewKsm = new KeyboardShortcutManager();
EtoPlatform.Current.HandleKeyDown(this, _previewKsm.Perform);
} }
private void ImageList_ImagesUpdated(object? sender, ImageListEventArgs e) private void ImageList_ImagesUpdated(object? sender, ImageListEventArgs e)
@ -313,65 +315,59 @@ public class PreviewForm : EtoDialogBase
} }
} }
protected override async void OnKeyDown(KeyEventArgs e)
{
if (!(e.Control || e.Shift || e.Alt))
{
switch (e.Key)
{
case Keys.Escape:
Close();
return;
// TODO: Left/right should maybe not change page if we're not at max zoom out (i.e. if we can pan)
case Keys.PageDown:
case Keys.Right:
case Keys.Down:
await GoTo(ImageIndex + 1);
return;
case Keys.PageUp:
case Keys.Left:
case Keys.Up:
await GoTo(ImageIndex - 1);
return;
}
}
e.Handled = _ksm.Perform(e.KeyData);
}
private void AssignKeyboardShortcuts() private void AssignKeyboardShortcuts()
{ {
// Defaults // Unconfigurable defaults
_ksm.Assign("Del", DeleteCurrentImageCommand); _previewKsm.Assign("Esc", Close);
_ksm.Assign("Ctrl+Oemplus", ZoomInCommand); _previewKsm.Assign("Del", DeleteCurrentImageCommand);
_ksm.Assign("Ctrl+OemMinus", ZoomOutCommand); _previewKsm.Assign("Mod+0", ZoomActualCommand);
_ksm.Assign("Ctrl+0", ZoomActualCommand); _previewKsm.Assign("Mod+Z", Commands.Undo);
_ksm.Assign("Ctrl+Z", Commands.Undo); _previewKsm.Assign(EtoPlatform.Current.IsWinForms ? "Mod+Y" : "Mod+Shift+Z", Commands.Redo);
_ksm.Assign(EtoPlatform.Current.IsGtk ? "Ctrl+Shift+Z" : "Ctrl+Y", Commands.Redo);
// Configured _previewKsm.Assign("PageDown", () => _ = GoTo(ImageIndex + 1));
_previewKsm.Assign("Right", () => _ = GoTo(ImageIndex + 1));
_previewKsm.Assign("Down", () => _ = GoTo(ImageIndex + 1));
_previewKsm.Assign("PageUp", () => _ = GoTo(ImageIndex - 1));
_previewKsm.Assign("Left", () => _ = GoTo(ImageIndex - 1));
_previewKsm.Assign("Up", () => _ = GoTo(ImageIndex - 1));
// Configured defaults
var ks = Config.Get(c => c.KeyboardShortcuts); var ks = Config.Get(c => c.KeyboardShortcuts);
_ksm.Assign(ks.Delete, DeleteCurrentImageCommand); _previewKsm.Assign(ks.Delete, DeleteCurrentImageCommand);
_ksm.Assign(ks.ImageBlackWhite, Commands.BlackWhite); _previewKsm.Assign(ks.ImageBlackWhite, Commands.BlackWhite);
_ksm.Assign(ks.ImageBrightness, Commands.BrightCont); _previewKsm.Assign(ks.ImageBrightness, Commands.BrightCont);
_ksm.Assign(ks.ImageContrast, Commands.BrightCont); _previewKsm.Assign(ks.ImageContrast, Commands.BrightCont);
_ksm.Assign(ks.ImageCrop, Commands.Crop); _previewKsm.Assign(ks.ImageCrop, Commands.Crop);
_ksm.Assign(ks.ImageHue, Commands.HueSat); _previewKsm.Assign(ks.ImageHue, Commands.HueSat);
_ksm.Assign(ks.ImageSaturation, Commands.HueSat); _previewKsm.Assign(ks.ImageSaturation, Commands.HueSat);
_ksm.Assign(ks.ImageSharpen, Commands.Sharpen); _previewKsm.Assign(ks.ImageSharpen, Commands.Sharpen);
_ksm.Assign(ks.ImageDocumentCorrection, Commands.DocumentCorrection); _previewKsm.Assign(ks.ImageDocumentCorrection, Commands.DocumentCorrection);
_ksm.Assign(ks.ImageSplit, Commands.Split); _previewKsm.Assign(ks.ImageSplit, Commands.Split);
_ksm.Assign(ks.ImageCombine, Commands.Combine); _previewKsm.Assign(ks.ImageCombine, Commands.Combine);
_ksm.Assign(ks.RotateCustom, Commands.CustomRotate); _previewKsm.Assign(ks.RotateCustom, Commands.CustomRotate);
_ksm.Assign(ks.RotateFlip, Commands.Flip); _previewKsm.Assign(ks.RotateFlip, Commands.Flip);
_ksm.Assign(ks.RotateLeft, Commands.RotateLeft); _previewKsm.Assign(ks.RotateLeft, Commands.RotateLeft);
_ksm.Assign(ks.RotateRight, Commands.RotateRight); _previewKsm.Assign(ks.RotateRight, Commands.RotateRight);
_ksm.Assign(ks.SaveImages, Commands.SaveSelectedImages);
_ksm.Assign(ks.SavePDF, Commands.SaveSelectedPdf); if (PlatformCompat.System.CombinedPdfAndImageSaving)
{
_previewKsm.Assign(ks.SavePDFAll, Commands.SaveSelected);
}
else
{
_previewKsm.Assign(ks.SavePDF, Commands.SaveSelectedPdf);
_previewKsm.Assign(ks.SaveImages, Commands.SaveSelectedImages);
_previewKsm.Assign(ks.SavePDFAll, Commands.SaveSelectedPdf);
_previewKsm.Assign(ks.SaveImagesAll, Commands.SaveSelectedImages);
}
_previewKsm.Assign(ks.ZoomIn, ZoomInCommand);
_previewKsm.Assign(ks.ZoomOut, ZoomOutCommand);
} }
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)

View File

@ -65,8 +65,7 @@ public class ProfilesForm : EtoDialogBase
_deleteCommand = new ActionCommand(DoDelete) _deleteCommand = new ActionCommand(DoDelete)
{ {
MenuText = UiStrings.Delete, MenuText = UiStrings.Delete,
Image = Icons.cross_small.ToEtoImage(), Image = Icons.cross_small.ToEtoImage()
Shortcut = Keys.Delete
}; };
_setDefaultCommand = new ActionCommand(DoSetDefault) _setDefaultCommand = new ActionCommand(DoSetDefault)
{ {
@ -75,13 +74,11 @@ public class ProfilesForm : EtoDialogBase
}; };
_copyCommand = new ActionCommand(DoCopy) _copyCommand = new ActionCommand(DoCopy)
{ {
MenuText = UiStrings.Copy, MenuText = UiStrings.Copy
Shortcut = Application.Instance.CommonModifier | Keys.C
}; };
_pasteCommand = new ActionCommand(DoPaste) _pasteCommand = new ActionCommand(DoPaste)
{ {
MenuText = UiStrings.Paste, MenuText = UiStrings.Paste
Shortcut = Application.Instance.CommonModifier | Keys.V
}; };
_scannerSharingCommand = new ActionCommand(OpenScannerSharingForm) _scannerSharingCommand = new ActionCommand(OpenScannerSharingForm)
{ {
@ -89,6 +86,12 @@ public class ProfilesForm : EtoDialogBase
Image = Icons.wireless16.ToEtoImage() Image = Icons.wireless16.ToEtoImage()
}; };
var profilesKsm = new KeyboardShortcutManager();
profilesKsm.Assign("Del", _deleteCommand);
profilesKsm.Assign("Mod+C", _copyCommand);
profilesKsm.Assign("Mod+V", _pasteCommand);
EtoPlatform.Current.HandleKeyDown(_listView.Control, profilesKsm.Perform);
_listView.ImageSize = 48; _listView.ImageSize = 48;
_listView.ItemClicked += ItemClicked; _listView.ItemClicked += ItemClicked;
_listView.SelectionChanged += SelectionChanged; _listView.SelectionChanged += SelectionChanged;

View File

@ -43,10 +43,13 @@ public class ScannerSharingForm : EtoDialogBase
_deleteCommand = new ActionCommand(DoDelete) _deleteCommand = new ActionCommand(DoDelete)
{ {
MenuText = UiStrings.Delete, MenuText = UiStrings.Delete,
Image = Icons.cross_small.ToEtoImage(), Image = Icons.cross_small.ToEtoImage()
Shortcut = Keys.Delete
}; };
var profilesKsm = new KeyboardShortcutManager();
profilesKsm.Assign("Del", _deleteCommand);
EtoPlatform.Current.HandleKeyDown(_listView.Control, profilesKsm.Perform);
// TODO: Enable // TODO: Enable
// _shareAsService.Checked = _osServiceManager.IsRegistered; // _shareAsService.Checked = _osServiceManager.IsRegistered;
// _shareAsService.CheckedChanged += ShareAsServiceCheckedChanged; // _shareAsService.CheckedChanged += ShareAsServiceCheckedChanged;

View File

@ -26,6 +26,8 @@ internal interface ISystemCompat
bool CanPrint { get; } bool CanPrint { get; }
bool CombinedPdfAndImageSaving { get; }
bool ShouldRememberBackgroundOperations { get; } bool ShouldRememberBackgroundOperations { get; }
bool RenderInWorker { get; } bool RenderInWorker { get; }

View File

@ -32,6 +32,8 @@ internal class LinuxSystemCompat : ISystemCompat
public bool CanPrint => true; public bool CanPrint => true;
public bool CombinedPdfAndImageSaving => false;
public bool ShouldRememberBackgroundOperations => true; public bool ShouldRememberBackgroundOperations => true;
public bool RenderInWorker => false; public bool RenderInWorker => false;

View File

@ -32,6 +32,8 @@ internal class MacSystemCompat : ISystemCompat
public bool CanPrint => true; public bool CanPrint => true;
public bool CombinedPdfAndImageSaving => true;
public bool ShouldRememberBackgroundOperations => true; public bool ShouldRememberBackgroundOperations => true;
public bool RenderInWorker => false; public bool RenderInWorker => false;

View File

@ -29,6 +29,8 @@ internal abstract class WindowsSystemCompat : ISystemCompat
public bool CanPrint => true; public bool CanPrint => true;
public bool CombinedPdfAndImageSaving => false;
public bool ShouldRememberBackgroundOperations => true; public bool ShouldRememberBackgroundOperations => true;
public bool RenderInWorker => true; public bool RenderInWorker => true;

View File

@ -98,20 +98,21 @@
<ScanProfile10>F11</ScanProfile10> <ScanProfile10>F11</ScanProfile10>
<ScanProfile11>F12</ScanProfile11> <ScanProfile11>F12</ScanProfile11>
<ScanProfile12></ScanProfile12> <ScanProfile12></ScanProfile12>
<NewProfile></NewProfile> <NewProfile>Ctrl+N</NewProfile>
<BatchScan>Ctrl+B</BatchScan> <BatchScan>Ctrl+B</BatchScan>
<Profiles></Profiles> <Profiles>Ctrl+L</Profiles>
<Ocr></Ocr> <ScannerSharing>Ctrl+G</ScannerSharing>
<Ocr>Ctrl+Alt+O</Ocr>
<Import>Ctrl+O</Import> <Import>Ctrl+O</Import>
<SavePDF>Ctrl+S</SavePDF> <SavePDFAll>Ctrl+S</SavePDFAll>
<SavePDFAll></SavePDFAll> <SavePDFSelected>Ctrl+Shift+S</SavePDFSelected>
<SavePDFSelected></SavePDFSelected> <PDFSettings>Ctrl+Alt+P</PDFSettings>
<SaveImages></SaveImages> <SaveImagesAll>Ctrl+I</SaveImagesAll>
<SaveImagesAll></SaveImagesAll> <SaveImagesSelected>Ctrl+Shift+I</SaveImagesSelected>
<SaveImagesSelected></SaveImagesSelected> <ImageSettings>Ctrl+Alt+I</ImageSettings>
<EmailPDF></EmailPDF> <EmailPDFAll>Ctrl+E</EmailPDFAll>
<EmailPDFAll></EmailPDFAll> <EmailPDFSelected>Ctrl+Shift+E</EmailPDFSelected>
<EmailPDFSelected></EmailPDFSelected> <EmailSettings>Ctrl+Alt+E</EmailSettings>
<Print>Ctrl+P</Print> <Print>Ctrl+P</Print>
<ImageView></ImageView> <ImageView></ImageView>
<ImageBlackWhite></ImageBlackWhite> <ImageBlackWhite></ImageBlackWhite>
@ -122,9 +123,9 @@
<ImageSaturation></ImageSaturation> <ImageSaturation></ImageSaturation>
<ImageSharpen></ImageSharpen> <ImageSharpen></ImageSharpen>
<ImageReset></ImageReset> <ImageReset></ImageReset>
<RotateLeft></RotateLeft> <RotateLeft>Ctrl+Shift+Left</RotateLeft>
<RotateRight></RotateRight> <RotateRight>Ctrl+Shift+Right</RotateRight>
<RotateFlip></RotateFlip> <RotateFlip>Ctrl+Shift+Down</RotateFlip>
<RotateCustom></RotateCustom> <RotateCustom></RotateCustom>
<MoveUp>Ctrl+Up</MoveUp> <MoveUp>Ctrl+Up</MoveUp>
<MoveDown>Ctrl+Down</MoveDown> <MoveDown>Ctrl+Down</MoveDown>