mirror of
https://github.com/LorenzCK/OnTopReplica.git
synced 2024-05-21 04:43:55 +12:00
Window/Region selection: refactored menu creation.
This commit is contained in:
parent
89f10cd894
commit
2ebd15fac2
|
@ -19,10 +19,8 @@ namespace OnTopReplica {
|
|||
ThumbnailPanel _thumbnailPanel;
|
||||
|
||||
//Managers
|
||||
BaseWindowSeeker _windowSeeker = new TaskWindowSeeker {
|
||||
SkipNotVisibleWindows = true
|
||||
};
|
||||
MessagePumpManager _msgPumpManager = new MessagePumpManager();
|
||||
WindowListMenuManager _windowListManager;
|
||||
|
||||
Options _startupOptions;
|
||||
|
||||
|
@ -63,9 +61,12 @@ namespace OnTopReplica {
|
|||
GlassEnabled = true;
|
||||
GlassMargins = new Margins(-1);
|
||||
|
||||
//Window handlers
|
||||
_windowSeeker.OwnerHandle = this.Handle;
|
||||
//Managers
|
||||
_msgPumpManager.Initialize(this);
|
||||
_windowListManager = new WindowListMenuManager(this, menuWindows);
|
||||
_windowListManager.ParentMenus = new System.Windows.Forms.ContextMenuStrip[] {
|
||||
menuContext, menuFullscreenContext
|
||||
};
|
||||
|
||||
//Platform specific form initialization
|
||||
Program.Platform.PostHandleFormInit(this);
|
||||
|
|
|
@ -26,14 +26,12 @@ namespace OnTopReplica {
|
|||
chromeToolStripMenuItem.Enabled = showing;
|
||||
clickThroughToolStripMenuItem.Enabled = showing;
|
||||
clickForwardingToolStripMenuItem.Enabled = showing;
|
||||
|
||||
}
|
||||
|
||||
private void Menu_Windows_opening(object sender, CancelEventArgs e) {
|
||||
_windowSeeker.Refresh();
|
||||
var menu = (ToolStrip)sender;
|
||||
menu.PopulateMenu(this, _windowSeeker,
|
||||
CurrentThumbnailWindowHandle, new EventHandler(Menu_Windows_itemclick));
|
||||
//_windowSeeker.Refresh();
|
||||
//var menu = (ToolStrip)sender;
|
||||
//menu.PopulateMenu(_windowSeeker, CurrentThumbnailWindowHandle, new EventHandler(Menu_Windows_itemclick));
|
||||
}
|
||||
|
||||
void Menu_Windows_itemclick(object sender, EventArgs e) {
|
||||
|
|
|
@ -173,6 +173,8 @@
|
|||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>Strings.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Tuples.cs" />
|
||||
<Compile Include="WindowListMenuManager.cs" />
|
||||
<Compile Include="WindowSeekers\BaseWindowSeeker.cs" />
|
||||
<Compile Include="WindowSeekers\ByClassWindowSeeker.cs" />
|
||||
<Compile Include="WindowSeekers\ByTitleWindowSeeker.cs" />
|
||||
|
|
20
OnTopReplica/Tuples.cs
Normal file
20
OnTopReplica/Tuples.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace OnTopReplica {
|
||||
|
||||
public class Tuple<T1, T2> {
|
||||
|
||||
public Tuple(T1 item1, T2 item2) {
|
||||
Item1 = item1;
|
||||
Item2 = item2;
|
||||
}
|
||||
|
||||
public T1 Item1 { get; set; }
|
||||
|
||||
public T2 Item2 { get; set; }
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -8,7 +8,9 @@ using System.Windows.Forms;
|
|||
namespace OnTopReplica {
|
||||
public static class Win32Helper {
|
||||
|
||||
/// <summary>Inject a fake left mouse click on a target window, on a location expressed in client coordinates.</summary>
|
||||
#region Injection
|
||||
|
||||
/// <summary>Inject a fake left mouse click on a target window, on a location expressed in client coordinates.</summary>
|
||||
/// <param name="window">Target window to click on.</param>
|
||||
/// <param name="clickLocation">Location of the mouse click expressed in client coordiantes of the target window.</param>
|
||||
/// <param name="doubleClick">True if a double click should be injected.</param>
|
||||
|
@ -82,7 +84,9 @@ namespace OnTopReplica {
|
|||
#endif
|
||||
}
|
||||
|
||||
/// <summary>Returns the child control of a window corresponding to a screen location.</summary>
|
||||
#endregion
|
||||
|
||||
/// <summary>Returns the child control of a window corresponding to a screen location.</summary>
|
||||
/// <param name="parent">Parent window to explore.</param>
|
||||
/// <param name="scrClickLocation">Child control location in screen coordinates.</param>
|
||||
private static IntPtr GetRealChildControlFromPoint(IntPtr parent, NPoint scrClickLocation) {
|
||||
|
@ -115,7 +119,7 @@ namespace OnTopReplica {
|
|||
if (handle == IntPtr.Zero)
|
||||
return null;
|
||||
|
||||
return new WindowHandle(handle, null);
|
||||
return new WindowHandle(handle);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -24,6 +24,15 @@ namespace OnTopReplica {
|
|||
_title = title;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new WindowHandle instance. Additional features of the handle will be queried as needed.
|
||||
/// </summary>
|
||||
/// <param name="p"></param>
|
||||
public WindowHandle(IntPtr p) {
|
||||
_handle = p;
|
||||
_title = null;
|
||||
}
|
||||
|
||||
public string Title {
|
||||
get {
|
||||
if (_title == null) {
|
||||
|
|
|
@ -1,123 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using OnTopReplica.Properties;
|
||||
using OnTopReplica.WindowSeekers;
|
||||
|
||||
namespace OnTopReplica {
|
||||
/// <summary>
|
||||
/// Extension methods used to apply a window list to a menu.
|
||||
/// </summary>
|
||||
static class WindowListHelper {
|
||||
|
||||
public class WindowSelectionData {
|
||||
public WindowHandle Handle { get; set; }
|
||||
public StoredRegion Region { get; set; }
|
||||
}
|
||||
|
||||
const int MaxWindowTitleLength = 55;
|
||||
|
||||
/// <summary>
|
||||
/// Populates the menu with a list of windows.
|
||||
/// </summary>
|
||||
/// <param name="menu">The menu to populate.</param>
|
||||
/// <param name="ownerForm">The owning form.</param>
|
||||
/// <param name="windowManager">The window manager that provides the windows list.</param>
|
||||
/// <param name="currentHandle">The currently used window (will be checked in the list).</param>
|
||||
/// <param name="clickHandler">Event handler for clicks on window items.</param>
|
||||
public static void PopulateMenu(this ToolStrip menu, Form ownerForm, BaseWindowSeeker windowManager,
|
||||
WindowHandle currentHandle, EventHandler clickHandler) {
|
||||
var regions = GetRegions();
|
||||
|
||||
//Clear
|
||||
menu.Items.Clear();
|
||||
|
||||
//"None" selection
|
||||
var nullTsi = new ToolStripMenuItem(Strings.MenuWindowsNone);
|
||||
nullTsi.Tag = null;
|
||||
nullTsi.Click += clickHandler;
|
||||
nullTsi.Checked = (currentHandle == null);
|
||||
menu.Items.Add(nullTsi);
|
||||
|
||||
//Add an item for each window
|
||||
foreach (WindowHandle h in windowManager.Windows) {
|
||||
//Skip if in the same process
|
||||
if (h.Handle.Equals(ownerForm.Handle))
|
||||
continue;
|
||||
|
||||
var tsi = new ToolStripMenuItem();
|
||||
|
||||
//Window title
|
||||
if (h.Title.Length > MaxWindowTitleLength) {
|
||||
tsi.Text = h.Title.Substring(0, MaxWindowTitleLength) + "...";
|
||||
tsi.ToolTipText = h.Title;
|
||||
}
|
||||
else
|
||||
tsi.Text = h.Title;
|
||||
|
||||
//Icon
|
||||
if (h.Icon != null) {
|
||||
tsi.Image = h.Icon.ToBitmap();
|
||||
}
|
||||
|
||||
//Check if this is the currently displayed window
|
||||
tsi.Checked = h.Equals(currentHandle);
|
||||
|
||||
//Add direct click if no stored regions
|
||||
tsi.Tag = new WindowSelectionData {
|
||||
Handle = h,
|
||||
Region = null
|
||||
};
|
||||
tsi.Click += clickHandler;
|
||||
|
||||
PopulateRegions(tsi, h, clickHandler, regions);
|
||||
|
||||
menu.Items.Add(tsi);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void PopulateRegions(ToolStripMenuItem tsi, WindowHandle handle,
|
||||
EventHandler clickHandler, IEnumerable<StoredRegion> regions) {
|
||||
|
||||
if (regions != null) {
|
||||
//Add subitem for no region
|
||||
var nullRegionItem = new ToolStripMenuItem(Strings.MenuWindowsWholeRegion);
|
||||
nullRegionItem.Tag = new WindowSelectionData {
|
||||
Handle = handle,
|
||||
Region = null
|
||||
};
|
||||
nullRegionItem.Image = Resources.regions;
|
||||
nullRegionItem.Click += clickHandler;
|
||||
tsi.DropDownItems.Add(nullRegionItem);
|
||||
|
||||
foreach (StoredRegion region in regions) {
|
||||
var regionItem = new ToolStripMenuItem(region.Name);
|
||||
regionItem.Tag = new WindowSelectionData {
|
||||
Handle = handle,
|
||||
Region = region
|
||||
};
|
||||
regionItem.Click += clickHandler;
|
||||
|
||||
tsi.DropDownItems.Add(regionItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<StoredRegion> GetRegions() {
|
||||
if (Settings.Default.SavedRegions == null || Settings.Default.SavedRegions.Count == 0)
|
||||
return null;
|
||||
|
||||
StoredRegion[] regions = new StoredRegion[Settings.Default.SavedRegions.Count];
|
||||
Settings.Default.SavedRegions.CopyTo(regions);
|
||||
|
||||
Array.Sort<StoredRegion>(regions, new Comparison<StoredRegion>((a, b) => {
|
||||
return a.Name.CompareTo(b.Name);
|
||||
}));
|
||||
|
||||
return regions;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
182
OnTopReplica/WindowListMenuManager.cs
Normal file
182
OnTopReplica/WindowListMenuManager.cs
Normal file
|
@ -0,0 +1,182 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using OnTopReplica.WindowSeekers;
|
||||
using OnTopReplica.Properties;
|
||||
|
||||
namespace OnTopReplica {
|
||||
/// <summary>
|
||||
/// Manages the window list displayed when allowing the user to select a window to clone.
|
||||
/// </summary>
|
||||
class WindowListMenuManager {
|
||||
|
||||
const int MaxWindowTitleLength = 55;
|
||||
|
||||
readonly MainForm _owner;
|
||||
readonly ContextMenuStrip _windowsMenu;
|
||||
|
||||
public WindowListMenuManager(MainForm owner, ContextMenuStrip windowsMenu) {
|
||||
_owner = owner;
|
||||
_windowsMenu = windowsMenu;
|
||||
|
||||
WindowSeeker = new TaskWindowSeeker() {
|
||||
OwnerHandle = owner.Handle,
|
||||
SkipNotVisibleWindows = true
|
||||
};
|
||||
|
||||
//Bind events
|
||||
windowsMenu.Opening += new System.ComponentModel.CancelEventHandler(WindowsMenu_opening);
|
||||
}
|
||||
|
||||
void WindowsMenu_opening(object sender, System.ComponentModel.CancelEventArgs e) {
|
||||
WindowSeeker.Refresh();
|
||||
PopulateMenu(_owner.CurrentThumbnailWindowHandle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populates the menu with windows from the window seeker instance.
|
||||
/// </summary>
|
||||
/// <param name="currentSelection">Handle of the currently selected window or null if none selected.</param>
|
||||
private void PopulateMenu(WindowHandle currentSelection) {
|
||||
var regions = GetStoredRegions();
|
||||
|
||||
_windowsMenu.Items.Clear();
|
||||
|
||||
//"None" selection
|
||||
var nullTsi = new ToolStripMenuItem(Strings.MenuWindowsNone);
|
||||
nullTsi.Tag = null;
|
||||
nullTsi.Click += MenuWindowClickHandler;
|
||||
nullTsi.Checked = (currentSelection == null);
|
||||
_windowsMenu.Items.Add(nullTsi);
|
||||
|
||||
//Add an item for each window
|
||||
foreach (WindowHandle h in WindowSeeker.Windows) {
|
||||
var tsi = new ToolStripMenuItem();
|
||||
|
||||
//Window title
|
||||
if (h.Title.Length > MaxWindowTitleLength) {
|
||||
tsi.Text = h.Title.Substring(0, MaxWindowTitleLength) + "...";
|
||||
tsi.ToolTipText = h.Title;
|
||||
}
|
||||
else
|
||||
tsi.Text = h.Title;
|
||||
|
||||
//Icon
|
||||
if (h.Icon != null) {
|
||||
tsi.Image = h.Icon.ToBitmap();
|
||||
}
|
||||
|
||||
//Check if this is the currently displayed window
|
||||
tsi.Checked = h.Equals(currentSelection);
|
||||
|
||||
//Click handler
|
||||
tsi.Tag = h;
|
||||
tsi.Click += MenuWindowClickHandler;
|
||||
|
||||
PopulateRegionsDropdown(tsi, h, regions);
|
||||
|
||||
_windowsMenu.Items.Add(tsi);
|
||||
}
|
||||
}
|
||||
|
||||
private void PopulateRegionsDropdown(ToolStripMenuItem parent, WindowHandle parentHandle, StoredRegion[] regions) {
|
||||
parent.DropDownItems.Clear();
|
||||
|
||||
//No region
|
||||
var nullRegionItem = new ToolStripMenuItem(Strings.MenuWindowsWholeRegion);
|
||||
nullRegionItem.Tag = new Tuple<WindowHandle, StoredRegion>(parentHandle, null);
|
||||
nullRegionItem.Image = Resources.regions;
|
||||
nullRegionItem.Click += MenuRegionWindowClickHandler;
|
||||
parent.DropDownItems.Add(nullRegionItem);
|
||||
|
||||
//Video detector
|
||||
|
||||
//Regions (if any)
|
||||
if (regions == null || regions.Length == 0)
|
||||
return;
|
||||
|
||||
parent.DropDownItems.Add(new ToolStripSeparator());
|
||||
|
||||
foreach (StoredRegion region in regions) {
|
||||
var regionItem = new ToolStripMenuItem(region.Name);
|
||||
regionItem.Tag = new Tuple<WindowHandle, StoredRegion>(parentHandle, region);
|
||||
regionItem.Click += MenuRegionWindowClickHandler;
|
||||
|
||||
parent.DropDownItems.Add(regionItem);
|
||||
}
|
||||
}
|
||||
|
||||
private void MenuWindowClickHandler(object sender, EventArgs args) {
|
||||
CommonClickHandler();
|
||||
|
||||
var tsi = (ToolStripMenuItem)sender;
|
||||
if (tsi.Tag == null) {
|
||||
_owner.UnsetThumbnail();
|
||||
}
|
||||
else {
|
||||
var handle = (WindowHandle)tsi.Tag;
|
||||
_owner.SetThumbnail(handle, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void MenuRegionWindowClickHandler(object sender, EventArgs args) {
|
||||
CommonClickHandler();
|
||||
|
||||
var tsi = (ToolStripMenuItem)sender;
|
||||
var tuple = (Tuple<WindowHandle, StoredRegion>)tsi.Tag;
|
||||
_owner.SetThumbnail(tuple.Item1,
|
||||
(tuple.Item2 != null) ? (System.Drawing.Rectangle?)tuple.Item2.Bounds : null);
|
||||
}
|
||||
|
||||
private void MenuVideoCropperClickHandler(object sender, EventArgs args){
|
||||
CommonClickHandler();
|
||||
}
|
||||
|
||||
private void CommonClickHandler() {
|
||||
_windowsMenu.Close();
|
||||
foreach (ContextMenuStrip menu in _parentMenus)
|
||||
menu.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an array of stored regions.
|
||||
/// </summary>
|
||||
private StoredRegion[] GetStoredRegions() {
|
||||
if (Settings.Default.SavedRegions == null || Settings.Default.SavedRegions.Count == 0)
|
||||
return null;
|
||||
|
||||
StoredRegion[] ret = new StoredRegion[Settings.Default.SavedRegions.Count];
|
||||
Settings.Default.SavedRegions.CopyTo(ret);
|
||||
|
||||
Array.Sort<StoredRegion>(ret, (a, b) => {
|
||||
return a.Name.CompareTo(b.Name);
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the window seeker instance used to list windows.
|
||||
/// </summary>
|
||||
public BaseWindowSeeker WindowSeeker { get; set; }
|
||||
|
||||
ContextMenuStrip[] _parentMenus = new ContextMenuStrip[0];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent menus which are bound to the context menu handled by this manager.
|
||||
/// </summary>
|
||||
public ContextMenuStrip[] ParentMenus {
|
||||
get {
|
||||
return (ContextMenuStrip[])_parentMenus.Clone();
|
||||
}
|
||||
set {
|
||||
if(value == null)
|
||||
_parentMenus = new ContextMenuStrip[0];
|
||||
else
|
||||
_parentMenus = (ContextMenuStrip[])value.Clone();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -53,22 +53,25 @@ namespace OnTopReplica.WindowSeekers {
|
|||
if (title.StartsWith(Title, StringComparison.InvariantCultureIgnoreCase)) {
|
||||
points += 10;
|
||||
}
|
||||
if (title.Equals(Title, StringComparison.InvariantCultureIgnoreCase)) {
|
||||
points += 5;
|
||||
}
|
||||
}
|
||||
|
||||
//Handle match (will probably not work, but anyhow)
|
||||
if (Handle != IntPtr.Zero) {
|
||||
if (Handle == hwnd) {
|
||||
points += 5;
|
||||
points += 10;
|
||||
}
|
||||
}
|
||||
|
||||
//Store handle if it matches
|
||||
if (points > 0) {
|
||||
_points.Add(hwnd.ToInt64(), points);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
//Never store windows in base class list
|
||||
return false;
|
||||
}
|
||||
|
||||
public override IList<WindowHandle> Windows {
|
||||
|
|
|
@ -9,17 +9,15 @@ namespace OnTopReplica.WindowSeekers {
|
|||
protected override bool InspectWindow(IntPtr hwnd, string title, ref bool terminate) {
|
||||
//Code taken from: http://www.thescarms.com/VBasic/alttab.aspx
|
||||
|
||||
//Reject empty titles
|
||||
if (string.IsNullOrEmpty(title))
|
||||
return false;
|
||||
|
||||
//Accept windows that
|
||||
// - are visible
|
||||
// - do not have a parent
|
||||
// - have no owner and are not Tool windows OR
|
||||
// - have an owner and are App windows
|
||||
|
||||
//Reject empty titles
|
||||
|
||||
if (string.IsNullOrEmpty(title))
|
||||
return false;
|
||||
|
||||
if ((long)WindowManagerMethods.GetParent(hwnd) == 0) {
|
||||
bool hasOwner = (long)WindowManagerMethods.GetWindow(hwnd, WindowManagerMethods.GetWindowMode.GW_OWNER) != 0;
|
||||
WindowMethods.WindowExStyles exStyle = (WindowMethods.WindowExStyles)WindowMethods.GetWindowLong(hwnd, WindowMethods.WindowLong.ExStyle);
|
||||
|
|
Loading…
Reference in a new issue