Added support for customizable keyboard shortcuts. Keyboard shortcuts can be added in flat_layout.xml by adding <shortcut> tags around the desired clients (No changes regarding the other xml files because I'm not sure what they do exactly...). Keyboard shortcuts are parsed as strings using KeysConverter without much sophistication. Some keys won't work, and there's no error reporting. Here's on that does work: Ctrl+Alt+F12. Also, added additional code to prevent preview windows and overlays from being shown when alt+tabbing.
This commit is contained in:
302
Hotkey.cs
Normal file
302
Hotkey.cs
Normal file
@@ -0,0 +1,302 @@
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using System.ComponentModel;
|
||||
using System.Xml.Serialization;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MovablePython
|
||||
{
|
||||
public class Hotkey : IMessageFilter
|
||||
{
|
||||
#region Interop
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
private static extern int RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, Keys vk);
|
||||
|
||||
[DllImport("user32.dll", SetLastError=true)]
|
||||
private static extern int UnregisterHotKey(IntPtr hWnd, int id);
|
||||
|
||||
private const uint WM_HOTKEY = 0x312;
|
||||
|
||||
private const uint MOD_ALT = 0x1;
|
||||
private const uint MOD_CONTROL = 0x2;
|
||||
private const uint MOD_SHIFT = 0x4;
|
||||
private const uint MOD_WIN = 0x8;
|
||||
|
||||
private const uint ERROR_HOTKEY_ALREADY_REGISTERED = 1409;
|
||||
|
||||
#endregion
|
||||
|
||||
private static int currentID;
|
||||
private const int maximumID = 0xBFFF;
|
||||
|
||||
private Keys keyCode;
|
||||
private bool shift;
|
||||
private bool control;
|
||||
private bool alt;
|
||||
private bool windows;
|
||||
|
||||
[XmlIgnore]
|
||||
private int id;
|
||||
[XmlIgnore]
|
||||
private bool registered;
|
||||
[XmlIgnore]
|
||||
private Control windowControl;
|
||||
|
||||
public event HandledEventHandler Pressed;
|
||||
|
||||
public Hotkey() : this(Keys.None, false, false, false, false)
|
||||
{
|
||||
// No work done here!
|
||||
}
|
||||
|
||||
public Hotkey(Keys keyCode, bool shift, bool control, bool alt, bool windows)
|
||||
{
|
||||
// Assign properties
|
||||
this.KeyCode = keyCode;
|
||||
this.Shift = shift;
|
||||
this.Control = control;
|
||||
this.Alt = alt;
|
||||
this.Windows = windows;
|
||||
|
||||
// Register us as a message filter
|
||||
Application.AddMessageFilter(this);
|
||||
}
|
||||
|
||||
~Hotkey()
|
||||
{
|
||||
// Unregister the hotkey if necessary
|
||||
if (this.Registered)
|
||||
{ this.Unregister(); }
|
||||
}
|
||||
|
||||
public Hotkey Clone()
|
||||
{
|
||||
// Clone the whole object
|
||||
return new Hotkey(this.keyCode, this.shift, this.control, this.alt, this.windows);
|
||||
}
|
||||
|
||||
public bool GetCanRegister(Control windowControl)
|
||||
{
|
||||
// Handle any exceptions: they mean "no, you can't register" :)
|
||||
try
|
||||
{
|
||||
// Attempt to register
|
||||
if (!this.Register(windowControl))
|
||||
{ return false; }
|
||||
|
||||
// Unregister and say we managed it
|
||||
this.Unregister();
|
||||
return true;
|
||||
}
|
||||
catch (Win32Exception)
|
||||
{ return false; }
|
||||
catch (NotSupportedException)
|
||||
{ return false; }
|
||||
}
|
||||
|
||||
public bool Register(Control windowControl)
|
||||
{
|
||||
// Check that we have not registered
|
||||
if (this.registered)
|
||||
{ throw new NotSupportedException("You cannot register a hotkey that is already registered"); }
|
||||
|
||||
// We can't register an empty hotkey
|
||||
if (this.Empty)
|
||||
{ throw new NotSupportedException("You cannot register an empty hotkey"); }
|
||||
|
||||
// Get an ID for the hotkey and increase current ID
|
||||
this.id = Hotkey.currentID;
|
||||
Hotkey.currentID = Hotkey.currentID + 1 % Hotkey.maximumID;
|
||||
|
||||
// Translate modifier keys into unmanaged version
|
||||
uint modifiers = (this.Alt ? Hotkey.MOD_ALT : 0) | (this.Control ? Hotkey.MOD_CONTROL : 0) |
|
||||
(this.Shift ? Hotkey.MOD_SHIFT : 0) | (this.Windows ? Hotkey.MOD_WIN : 0);
|
||||
|
||||
// Register the hotkey
|
||||
if (Hotkey.RegisterHotKey(windowControl.Handle, this.id, modifiers, keyCode) == 0)
|
||||
{
|
||||
// Is the error that the hotkey is registered?
|
||||
if (Marshal.GetLastWin32Error() == ERROR_HOTKEY_ALREADY_REGISTERED)
|
||||
{ return false; }
|
||||
else
|
||||
{ throw new Win32Exception(); }
|
||||
}
|
||||
|
||||
// Save the control reference and register state
|
||||
this.registered = true;
|
||||
this.windowControl = windowControl;
|
||||
|
||||
// We successfully registered
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Unregister()
|
||||
{
|
||||
// Check that we have registered
|
||||
if (!this.registered)
|
||||
{ throw new NotSupportedException("You cannot unregister a hotkey that is not registered"); }
|
||||
|
||||
// It's possible that the control itself has died: in that case, no need to unregister!
|
||||
if (!this.windowControl.IsDisposed)
|
||||
{
|
||||
// Clean up after ourselves
|
||||
if (Hotkey.UnregisterHotKey(this.windowControl.Handle, this.id) == 0)
|
||||
{ throw new Win32Exception(); }
|
||||
}
|
||||
|
||||
// Clear the control reference and register state
|
||||
this.registered = false;
|
||||
this.windowControl = null;
|
||||
}
|
||||
|
||||
private void Reregister()
|
||||
{
|
||||
// Only do something if the key is already registered
|
||||
if (!this.registered)
|
||||
{ return; }
|
||||
|
||||
// Save control reference
|
||||
Control windowControl = this.windowControl;
|
||||
|
||||
// Unregister and then reregister again
|
||||
this.Unregister();
|
||||
this.Register(windowControl);
|
||||
}
|
||||
|
||||
public bool PreFilterMessage(ref Message message)
|
||||
{
|
||||
// Only process WM_HOTKEY messages
|
||||
if (message.Msg != Hotkey.WM_HOTKEY)
|
||||
{ return false; }
|
||||
|
||||
// Check that the ID is our key and we are registerd
|
||||
if (this.registered && (message.WParam.ToInt32() == this.id))
|
||||
{
|
||||
// Fire the event and pass on the event if our handlers didn't handle it
|
||||
return this.OnPressed();
|
||||
}
|
||||
else
|
||||
{ return false; }
|
||||
}
|
||||
|
||||
private bool OnPressed()
|
||||
{
|
||||
// Fire the event if we can
|
||||
HandledEventArgs handledEventArgs = new HandledEventArgs(false);
|
||||
if (this.Pressed != null)
|
||||
{ this.Pressed(this, handledEventArgs); }
|
||||
|
||||
// Return whether we handled the event or not
|
||||
return handledEventArgs.Handled;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
// We can be empty
|
||||
if (this.Empty)
|
||||
{ return "(none)"; }
|
||||
|
||||
// Build key name
|
||||
string keyName = Enum.GetName(typeof(Keys), this.keyCode);;
|
||||
switch (this.keyCode)
|
||||
{
|
||||
case Keys.D0:
|
||||
case Keys.D1:
|
||||
case Keys.D2:
|
||||
case Keys.D3:
|
||||
case Keys.D4:
|
||||
case Keys.D5:
|
||||
case Keys.D6:
|
||||
case Keys.D7:
|
||||
case Keys.D8:
|
||||
case Keys.D9:
|
||||
// Strip the first character
|
||||
keyName = keyName.Substring(1);
|
||||
break;
|
||||
default:
|
||||
// Leave everything alone
|
||||
break;
|
||||
}
|
||||
|
||||
// Build modifiers
|
||||
string modifiers = "";
|
||||
if (this.shift)
|
||||
{ modifiers += "Shift+"; }
|
||||
if (this.control)
|
||||
{ modifiers += "Control+"; }
|
||||
if (this.alt)
|
||||
{ modifiers += "Alt+"; }
|
||||
if (this.windows)
|
||||
{ modifiers += "Windows+"; }
|
||||
|
||||
// Return result
|
||||
return modifiers + keyName;
|
||||
}
|
||||
|
||||
public bool Empty
|
||||
{
|
||||
get { return this.keyCode == Keys.None; }
|
||||
}
|
||||
|
||||
public bool Registered
|
||||
{
|
||||
get { return this.registered; }
|
||||
}
|
||||
|
||||
public Keys KeyCode
|
||||
{
|
||||
get { return this.keyCode; }
|
||||
set
|
||||
{
|
||||
// Save and reregister
|
||||
this.keyCode = value;
|
||||
this.Reregister();
|
||||
}
|
||||
}
|
||||
|
||||
public bool Shift
|
||||
{
|
||||
get { return this.shift; }
|
||||
set
|
||||
{
|
||||
// Save and reregister
|
||||
this.shift = value;
|
||||
this.Reregister();
|
||||
}
|
||||
}
|
||||
|
||||
public bool Control
|
||||
{
|
||||
get { return this.control; }
|
||||
set
|
||||
{
|
||||
// Save and reregister
|
||||
this.control = value;
|
||||
this.Reregister();
|
||||
}
|
||||
}
|
||||
|
||||
public bool Alt
|
||||
{
|
||||
get { return this.alt; }
|
||||
set
|
||||
{
|
||||
// Save and reregister
|
||||
this.alt = value;
|
||||
this.Reregister();
|
||||
}
|
||||
}
|
||||
|
||||
public bool Windows
|
||||
{
|
||||
get { return this.windows; }
|
||||
set
|
||||
{
|
||||
// Save and reregister
|
||||
this.windows = value;
|
||||
this.Reregister();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
46
Preview.cs
46
Preview.cs
@@ -6,6 +6,7 @@ using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Diagnostics;
|
||||
using MovablePython;
|
||||
|
||||
namespace PreviewToy
|
||||
{
|
||||
@@ -26,6 +27,7 @@ namespace PreviewToy
|
||||
private bool has_been_set_up = false;
|
||||
private bool thumbnail_has_been_set_up = false;
|
||||
private PreviewToyHandler spawner;
|
||||
private Hotkey hotkey;
|
||||
|
||||
private bool hide = false;
|
||||
|
||||
@@ -41,6 +43,8 @@ namespace PreviewToy
|
||||
return this.Text;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void MakeTopMost(bool topmost)
|
||||
{
|
||||
this.TopMost = topmost && !(this.hide);
|
||||
@@ -66,6 +70,8 @@ namespace PreviewToy
|
||||
this.old_position = this.Location;
|
||||
|
||||
has_been_set_up = true;
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void preview_MouseHover(object sender, System.EventArgs e)
|
||||
@@ -84,6 +90,46 @@ namespace PreviewToy
|
||||
RefreshPreview();
|
||||
}
|
||||
|
||||
protected override CreateParams CreateParams
|
||||
{
|
||||
get
|
||||
{
|
||||
var Params = base.CreateParams;
|
||||
Params.ExStyle |= 0x80;
|
||||
return Params;
|
||||
}
|
||||
}
|
||||
|
||||
public void registerShortcut(string shortcut)
|
||||
{
|
||||
if (shortcut == "")
|
||||
return;
|
||||
var cvt = new KeysConverter();
|
||||
var key = (Keys)cvt.ConvertFrom(shortcut);
|
||||
|
||||
Hotkey hotkey = new Hotkey();
|
||||
|
||||
if ((key & Keys.Shift) == Keys.Shift)
|
||||
{
|
||||
hotkey.Shift = true;
|
||||
}
|
||||
if ((key & Keys.Alt) == Keys.Alt)
|
||||
{
|
||||
hotkey.Alt = true;
|
||||
}
|
||||
if ((key & Keys.Control) == Keys.Control)
|
||||
{
|
||||
hotkey.Control = true;
|
||||
}
|
||||
|
||||
key = key & ~Keys.Shift & ~Keys.Alt & ~Keys.Control;
|
||||
hotkey.KeyCode = key;
|
||||
hotkey.Register(this);
|
||||
hotkey.Pressed += delegate { bring_client_to_foreground(); spawner.preview_did_switch(); };
|
||||
|
||||
this.hotkey = hotkey;
|
||||
}
|
||||
|
||||
public void doZoom()
|
||||
{
|
||||
if (is_zoomed)
|
||||
|
||||
@@ -27,6 +27,7 @@ namespace PreviewToy
|
||||
|
||||
private Dictionary<String, Dictionary<String, Point>> unique_layouts;
|
||||
private Dictionary<String, Point> flat_layout;
|
||||
private Dictionary<String, String> flat_layout_shortcuts;
|
||||
private Dictionary<String, ClientLocation> client_layout;
|
||||
|
||||
private bool is_initialized;
|
||||
@@ -89,6 +90,7 @@ namespace PreviewToy
|
||||
|
||||
unique_layouts = new Dictionary<String, Dictionary<String, Point>>();
|
||||
flat_layout = new Dictionary<String, Point>();
|
||||
flat_layout_shortcuts = new Dictionary<String, String>();
|
||||
client_layout = new Dictionary<String, ClientLocation>();
|
||||
|
||||
ignoring_size_sync = new Stopwatch();
|
||||
@@ -197,6 +199,8 @@ namespace PreviewToy
|
||||
else if (previews.ContainsKey(process.MainWindowHandle) && process.MainWindowTitle != previews[process.MainWindowHandle].Text) //or update the preview titles
|
||||
{
|
||||
previews[process.MainWindowHandle].SetLabel(process.MainWindowTitle);
|
||||
string key = previews[process.MainWindowHandle].Text;
|
||||
previews[process.MainWindowHandle].registerShortcut(flat_layout_shortcuts[key]);
|
||||
refresh_client_window_locations(process);
|
||||
}
|
||||
|
||||
@@ -299,6 +303,12 @@ namespace PreviewToy
|
||||
foreach (var el in rootElement.Elements())
|
||||
{
|
||||
flat_layout[ParseXElement(el)] = new Point(Convert.ToInt32(el.Element("x").Value), Convert.ToInt32(el.Element("y").Value));
|
||||
flat_layout_shortcuts[ParseXElement(el)] = "";
|
||||
|
||||
if (el.Element("shortcut") != null)
|
||||
{
|
||||
flat_layout_shortcuts[ParseXElement(el)] = el.Element("shortcut").Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,6 +365,7 @@ namespace PreviewToy
|
||||
XElement layout = MakeXElement(clientKV.Key);
|
||||
layout.Add(new XElement("x", clientKV.Value.X));
|
||||
layout.Add(new XElement("y", clientKV.Value.Y));
|
||||
layout.Add(new XElement("shortcut", flat_layout_shortcuts[clientKV.Key]));
|
||||
el2.Add(layout);
|
||||
}
|
||||
|
||||
|
||||
@@ -41,5 +41,15 @@ namespace PreviewToy
|
||||
|
||||
}
|
||||
|
||||
protected override CreateParams CreateParams
|
||||
{
|
||||
get
|
||||
{
|
||||
var Params = base.CreateParams;
|
||||
Params.ExStyle |= 0x80;
|
||||
return Params;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,6 +95,7 @@
|
||||
<Reference Include="WindowsBase" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Hotkey.cs" />
|
||||
<Compile Include="PreviewHandler.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
|
||||
Reference in New Issue
Block a user