diff --git a/CenterTaskbar/CenterTaskbar.csproj b/CenterTaskbar/CenterTaskbar.csproj index a748f65..614b73e 100644 --- a/CenterTaskbar/CenterTaskbar.csproj +++ b/CenterTaskbar/CenterTaskbar.csproj @@ -31,7 +31,7 @@ Michael Higgins Center Taskbar false - 5 + 9 1.1.0.%2a false true @@ -72,17 +72,15 @@ false + + CenterTaskbar.Program + - - - - - diff --git a/CenterTaskbar/DisplaySettings.cs b/CenterTaskbar/DisplaySettings.cs index 970d26c..3196aa5 100644 --- a/CenterTaskbar/DisplaySettings.cs +++ b/CenterTaskbar/DisplaySettings.cs @@ -1,24 +1,22 @@ -using System; -using System.Runtime.InteropServices; +using System.Runtime.InteropServices; using System.Windows.Forms; namespace CenterTaskbar { - static class DisplaySettings + internal static class DisplaySettings { [DllImport("user32.dll")] private static extern bool EnumDisplaySettings(string deviceName, int modeNum, ref DEVMODE devMode); const int ENUM_CURRENT_SETTINGS = -1; - //const int ENUM_REGISTRY_SETTINGS = -2; + const int ENUM_REGISTRY_SETTINGS = -2; - [StructLayout(LayoutKind.Sequential)] + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] private struct DEVMODE { - - private const int CCHDEVICENAME = 0x20; - private const int CCHFORMNAME = 0x20; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)] + private const int CCHDEVICENAME = 32; + private const int CCHFORMNAME = 32; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHDEVICENAME)] public string dmDeviceName; public short dmSpecVersion; public short dmDriverVersion; @@ -34,7 +32,7 @@ private struct DEVMODE public short dmYResolution; public short dmTTOption; public short dmCollate; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)] + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHFORMNAME)] public string dmFormName; public short dmLogPixels; public int dmBitsPerPel; @@ -50,33 +48,27 @@ private struct DEVMODE public int dmReserved2; public int dmPanningWidth; public int dmPanningHeight; - } - public static void ListAllDisplayModes() - { - DEVMODE vDevMode = new DEVMODE(); - int i = 0; - while (EnumDisplaySettings(null, i, ref vDevMode)) - { - Console.WriteLine("Width:{0} Height:{1} Color:{2} Frequency:{3}", - vDevMode.dmPelsWidth, - vDevMode.dmPelsHeight, - 1 << vDevMode.dmBitsPerPel, vDevMode.dmDisplayFrequency - ); - i++; - } - } + //public static void ListAllDisplayModes() + //{ + // DEVMODE vDevMode = new DEVMODE(); + // int i = 0; + // while (EnumDisplaySettings(null, i, ref vDevMode)) + // { + // Console.WriteLine("Width:{0} Height:{1} Color:{2} Frequency:{3}", + // vDevMode.dmPelsWidth, + // vDevMode.dmPelsHeight, + // 1 << vDevMode.dmBitsPerPel, vDevMode.dmDisplayFrequency + // ); + // i++; + // } + //} public static int CurrentRefreshRate() { - DEVMODE vDevMode = new DEVMODE(); - if (EnumDisplaySettings(null, ENUM_CURRENT_SETTINGS, ref vDevMode)) { - return vDevMode.dmDisplayFrequency; - } else - { - return 60; - } + var vDevMode = new DEVMODE(); + return EnumDisplaySettings(null, ENUM_CURRENT_SETTINGS, ref vDevMode) ? vDevMode.dmDisplayFrequency : 60; } } } diff --git a/CenterTaskbar/Program.cs b/CenterTaskbar/Program.cs index 59f3495..817f08e 100644 --- a/CenterTaskbar/Program.cs +++ b/CenterTaskbar/Program.cs @@ -1,31 +1,34 @@ using System; -using System.Windows; -using System.Windows.Forms; -using System.Windows.Automation; -using Microsoft.Win32; -using System.Runtime.InteropServices; -using System.Diagnostics; -using System.Threading; using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; using System.Reflection; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Automation; +using System.Windows.Forms; +using CenterTaskbar.Properties; +using Microsoft.Win32; namespace CenterTaskbar { - static class Program + internal static class Program { /// /// The main entry point for the application. /// [STAThread] - static void Main(string[] args) + private static void Main(string[] args) { // Only allow one instance of this application to run at a time using GUID - string assyGuid = Assembly.GetExecutingAssembly().GetCustomAttribute().Value.ToUpper(); - using (Mutex mutex = new Mutex(true, assyGuid, out bool firstInstance)) + var assyGuid = Assembly.GetExecutingAssembly().GetCustomAttribute().Value.ToUpper(); + using (new Mutex(true, assyGuid, out var firstInstance)) { if (!firstInstance) { - MessageBox.Show("Another instance is already running.", "CenterTaskbar", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); + MessageBox.Show("Another instance is already running.", "CenterTaskbar", MessageBoxButtons.OK, + MessageBoxIcon.Exclamation); return; } @@ -38,65 +41,69 @@ static void Main(string[] args) public class TrayApplication : ApplicationContext { - private const String appName = "CenterTaskbar"; - private const String runRegkey = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run"; + private const string AppName = "CenterTaskbar"; + private const string RunRegkey = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run"; + private const int OneSecond = 1000; private const int SWP_NOSIZE = 0x0001; private const int SWP_NOZORDER = 0x0004; //private const int SWP_SHOWWINDOW = 0x0040; private const int SWP_ASYNCWINDOWPOS = 0x4000; - private const String MSTaskListWClass = "MSTaskListWClass"; + private const string MSTaskListWClass = "MSTaskListWClass"; //private const String ReBarWindow32 = "ReBarWindow32"; - private const String Shell_TrayWnd = "Shell_TrayWnd"; - private const String Shell_SecondaryTrayWnd = "Shell_SecondaryTrayWnd"; - private static readonly String ExecutablePath = "\"" + Application.ExecutablePath + "\""; + private const string ShellTrayWnd = "Shell_TrayWnd"; + private const string ShellSecondaryTrayWnd = "Shell_SecondaryTrayWnd"; - private static bool disposed = false; + private static readonly string ExecutablePath = "\"" + Application.ExecutablePath + "\""; + private static bool _disposed; + private CancellationTokenSource _loopCancellationTokenSource = new CancellationTokenSource(); - private readonly NotifyIcon trayIcon; - private static readonly AutomationElement desktop = AutomationElement.RootElement; - private static AutomationEventHandler UIAeventHandler; - private static AutomationPropertyChangedEventHandler propChangeHandler; - private readonly Dictionary lasts = new Dictionary(); - private readonly Dictionary children = new Dictionary(); - private readonly List bars = new List(); + private static readonly AutomationElement Desktop = AutomationElement.RootElement; + private static AutomationEventHandler _uiaEventHandler; + private static AutomationPropertyChangedEventHandler _propChangeHandler; - private readonly int activeFramerate = DisplaySettings.CurrentRefreshRate(); - // private Thread positionThread; - private Dictionary positionThreads = new Dictionary(); + private readonly int _activeFramerate = DisplaySettings.CurrentRefreshRate(); + private readonly List _bars = new List(); - [DllImport("user32.dll", SetLastError = true)] - private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags); + private readonly Dictionary _children = + new Dictionary(); + + private readonly Dictionary _lasts = new Dictionary(); + + private readonly NotifyIcon _trayIcon; - public TrayApplication(string[] args) + // private Thread positionThread; + private readonly Dictionary _positionThreads = + new Dictionary(); + + public TrayApplication(IReadOnlyList args) { - if (args.Length > 0) - { + if (args.Count > 0) try { - activeFramerate = Int32.Parse(args[0]); - Debug.WriteLine("Active refresh rate: " + activeFramerate); + _activeFramerate = int.Parse(args[0]); + Debug.WriteLine("Active refresh rate: " + _activeFramerate); } catch (FormatException e) { - Debug.WriteLine(e.Message); + Debug.WriteLine(e.Message); } - } - MenuItem header = new MenuItem("CenterTaskbar (" + activeFramerate + ")", Exit) + var header = new MenuItem("CenterTaskbar (" + _activeFramerate + ")", Exit) { Enabled = false }; - MenuItem startup = new MenuItem("Start with Windows", ToggleStartup) + var startup = new MenuItem("Start with Windows", ToggleStartup) { - Checked = IsApplicationInStatup() + Checked = IsApplicationInStartup() }; // Setup Tray Icon - trayIcon = new NotifyIcon() + _trayIcon = new NotifyIcon { - Icon = Properties.Resources.TrayIcon, - ContextMenu = new ContextMenu(new MenuItem[] { + Icon = Resources.TrayIcon, + ContextMenu = new ContextMenu(new[] + { header, new MenuItem("Scan for screens", Restart), startup, @@ -108,66 +115,69 @@ public TrayApplication(string[] args) Start(); } + [DllImport("user32.dll", SetLastError = true)] + private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, int uFlags); + public void ToggleStartup(object sender, EventArgs e) { - if (IsApplicationInStatup()) + if (IsApplicationInStartup()) { RemoveApplicationFromStartup(); - (sender as MenuItem).Checked = false; - } else + ((MenuItem) sender).Checked = false; + } + else { AddApplicationToStartup(); - (sender as MenuItem).Checked = true; + ((MenuItem) sender).Checked = true; } } - public bool IsApplicationInStatup() + public bool IsApplicationInStartup() { - using (RegistryKey key = Registry.CurrentUser.OpenSubKey(runRegkey, true)) + using (var key = Registry.CurrentUser.OpenSubKey(RunRegkey, true)) { - if (key == null) return false; - - object value = key.GetValue(appName); - if (value is String) - { - ; - return ((value as String).StartsWith(ExecutablePath)); - } - - return false; + var value = key?.GetValue(AppName); + return value is string startValue && startValue.StartsWith(ExecutablePath); } } public void AddApplicationToStartup() { - using (RegistryKey key = Registry.CurrentUser.OpenSubKey(runRegkey, true)) + using (var key = Registry.CurrentUser.OpenSubKey(RunRegkey, true)) { - key.SetValue(appName, ExecutablePath); + key?.SetValue(AppName, ExecutablePath); } } public void RemoveApplicationFromStartup() { - using (RegistryKey key = Registry.CurrentUser.OpenSubKey(runRegkey, true)) + using (var key = Registry.CurrentUser.OpenSubKey(RunRegkey, true)) { - key.DeleteValue(appName, false); + key?.DeleteValue(AppName, false); } } - void Exit(object sender, EventArgs e) + private static void Exit(object sender, EventArgs e) { Application.ExitThread(); } private void CancelPositionThread() { - foreach (Thread thread in positionThreads.Values) + _loopCancellationTokenSource.Cancel(); + Parallel.ForEach(_positionThreads.Values.ToList(), theTask => { - thread.Abort(); - } + // Give the thread time to exit gracefully. + if (theTask.Wait(OneSecond * 3)) return; + + Debug.WriteLine("Timeout waiting for task to cancel!"); + theTask.Dispose(); + }); + + _loopCancellationTokenSource = new CancellationTokenSource(); } - void Restart(object sender, EventArgs e) + private void Restart(object sender, EventArgs e) { CancelPositionThread(); Start(); @@ -176,49 +186,48 @@ void Restart(object sender, EventArgs e) private void ResetAll() { CancelPositionThread(); - foreach (AutomationElement trayWnd in bars) - { - Reset(trayWnd); - } + Parallel.ForEach(_bars.ToList(), Reset); } - private void Reset(AutomationElement trayWnd) + private static void Reset(AutomationElement trayWnd) { Debug.WriteLine("Begin Reset Calculation"); - AutomationElement tasklist = trayWnd.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.ClassNameProperty, MSTaskListWClass)); - if (tasklist == null) + var taskList = trayWnd.FindFirst(TreeScope.Descendants, + new PropertyCondition(AutomationElement.ClassNameProperty, MSTaskListWClass)); + if (taskList == null) { Debug.WriteLine("Null values found, aborting reset"); return; } - AutomationElement tasklistcontainer = TreeWalker.ControlViewWalker.GetParent(tasklist); - if (tasklistcontainer == null) + var taskListContainer = TreeWalker.ControlViewWalker.GetParent(taskList); + if (taskListContainer == null) { Debug.WriteLine("Null values found, aborting reset"); return; } - IntPtr tasklistPtr = (IntPtr)tasklist.Current.NativeWindowHandle; - - SetWindowPos(tasklistPtr, IntPtr.Zero, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_ASYNCWINDOWPOS); + var taskListPtr = (IntPtr) taskList.Current.NativeWindowHandle; + + SetWindowPos(taskListPtr, IntPtr.Zero, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_ASYNCWINDOWPOS); } private void Start() { - OrCondition condition = new OrCondition(new PropertyCondition(AutomationElement.ClassNameProperty, Shell_TrayWnd), new PropertyCondition(AutomationElement.ClassNameProperty, Shell_SecondaryTrayWnd)); - CacheRequest cacheRequest = new CacheRequest(); + var condition = new OrCondition(new PropertyCondition(AutomationElement.ClassNameProperty, ShellTrayWnd), + new PropertyCondition(AutomationElement.ClassNameProperty, ShellSecondaryTrayWnd)); + var cacheRequest = new CacheRequest(); cacheRequest.Add(AutomationElement.NameProperty); cacheRequest.Add(AutomationElement.BoundingRectangleProperty); - bars.Clear(); - children.Clear(); - lasts.Clear(); + _bars.Clear(); + _children.Clear(); + _lasts.Clear(); using (cacheRequest.Activate()) { - AutomationElementCollection lists = desktop.FindAll(TreeScope.Children, condition); + var lists = Desktop.FindAll(TreeScope.Children, condition); if (lists == null) { Debug.WriteLine("Null values found, aborting"); @@ -226,61 +235,61 @@ private void Start() } Debug.WriteLine(lists.Count + " bar(s) detected"); - lasts.Clear(); - foreach (AutomationElement trayWnd in lists) + _lasts.Clear(); + Parallel.ForEach(lists.OfType(), trayWnd => { - AutomationElement tasklist = trayWnd.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.ClassNameProperty, MSTaskListWClass)); - if (tasklist == null) + var taskList = trayWnd.FindFirst(TreeScope.Descendants, + new PropertyCondition(AutomationElement.ClassNameProperty, MSTaskListWClass)); + if (taskList == null) { Debug.WriteLine("Null values found, aborting"); - continue; } + else + { + _propChangeHandler = OnUIAutomationEvent; + Automation.AddAutomationPropertyChangedEventHandler(taskList, TreeScope.Element, _propChangeHandler, + AutomationElement.BoundingRectangleProperty); - propChangeHandler = new AutomationPropertyChangedEventHandler(OnUIAutomationEvent); - Automation.AddAutomationPropertyChangedEventHandler(tasklist, TreeScope.Element, propChangeHandler, AutomationElement.BoundingRectangleProperty); + _bars.Add(trayWnd); + _children.Add(trayWnd, taskList); - bars.Add(trayWnd); - children.Add(trayWnd, tasklist); - positionThreads[trayWnd] = new Thread(new ParameterizedThreadStart(LoopForPosition)); - positionThreads[trayWnd].Start(trayWnd); - } + _positionThreads[trayWnd] = Task.Run(() => LoopForPosition(trayWnd), _loopCancellationTokenSource.Token); + } + }); } - UIAeventHandler = new AutomationEventHandler(OnUIAutomationEvent); - Automation.AddAutomationEventHandler(WindowPattern.WindowOpenedEvent, desktop, TreeScope.Subtree, UIAeventHandler); - Automation.AddAutomationEventHandler(WindowPattern.WindowClosedEvent, desktop, TreeScope.Subtree, UIAeventHandler); + _uiaEventHandler = OnUIAutomationEvent; + Automation.AddAutomationEventHandler(WindowPattern.WindowOpenedEvent, Desktop, TreeScope.Subtree, _uiaEventHandler); + Automation.AddAutomationEventHandler(WindowPattern.WindowClosedEvent, Desktop, TreeScope.Subtree, _uiaEventHandler); } private void OnUIAutomationEvent(object src, AutomationEventArgs e) { Debug.Print("Event occured: {0}", e.EventId.ProgrammaticName); - foreach (AutomationElement trayWnd in bars) + Parallel.ForEach(_bars.ToList(), trayWnd => { - if (!positionThreads[trayWnd].IsAlive) + if (_positionThreads[trayWnd].IsCompleted) { Debug.WriteLine("Starting new thead"); - positionThreads[trayWnd] = new Thread(new ParameterizedThreadStart(LoopForPosition)); - positionThreads[trayWnd].Start(trayWnd); - } else + _positionThreads[trayWnd] = Task.Run(() => LoopForPosition(trayWnd), _loopCancellationTokenSource.Token); + } + else { Debug.WriteLine("Thread already exists"); } - } - + }); } private void LoopForPosition(object trayWndObj) { - AutomationElement trayWnd = (AutomationElement)trayWndObj; - int numberOfLoops = activeFramerate / 10; - int keepGoing = 0; + var trayWnd = (AutomationElement) trayWndObj; + var numberOfLoops = _activeFramerate / 10; + var keepGoing = 0; while (keepGoing < numberOfLoops) { - if (!PositionLoop(trayWnd)) - { - keepGoing += 1; - } - System.Threading.Tasks.Task.Delay(1000 / activeFramerate).Wait(); + if (!PositionLoop(trayWnd)) keepGoing += 1; + if (_loopCancellationTokenSource.IsCancellationRequested) break; + Task.Delay(OneSecond / _activeFramerate).Wait(); } Debug.WriteLine("LoopForPosition Thread ended."); @@ -290,123 +299,130 @@ private bool PositionLoop(AutomationElement trayWnd) { Debug.WriteLine("Begin Reposition Calculation"); - AutomationElement tasklist = children[trayWnd]; - AutomationElement last = TreeWalker.ControlViewWalker.GetLastChild(tasklist); + var taskList = _children[trayWnd]; + var last = TreeWalker.ControlViewWalker.GetLastChild(taskList); if (last == null) { Debug.WriteLine("Null values found for items, aborting"); return true; } - Rect trayBounds = trayWnd.Cached.BoundingRectangle; - bool horizontal = trayBounds.Width > trayBounds.Height; + var trayBounds = trayWnd.Cached.BoundingRectangle; + var horizontal = trayBounds.Width > trayBounds.Height; // Use the left/top bounds because there is an empty element as the last child with a nonzero width - double lastChildPos = horizontal ? last.Current.BoundingRectangle.Left : last.Current.BoundingRectangle.Top; + var lastChildPos = horizontal ? last.Current.BoundingRectangle.Left : last.Current.BoundingRectangle.Top; Debug.WriteLine("Last child position: " + lastChildPos); - if (lasts.ContainsKey(trayWnd) && lastChildPos == lasts[trayWnd]) + if (_lasts.ContainsKey(trayWnd) && lastChildPos == _lasts[trayWnd]) { Debug.WriteLine("Size/location unchanged, sleeping"); return false; - } else - { - Debug.WriteLine("Size/location changed, recalculating center"); - lasts[trayWnd] = lastChildPos; + } - AutomationElement first = TreeWalker.ControlViewWalker.GetFirstChild(tasklist); - if (first == null) - { - Debug.WriteLine("Null values found for first child item, aborting"); - return true; - } + Debug.WriteLine("Size/location changed, recalculating center"); + _lasts[trayWnd] = lastChildPos; - double scale = horizontal ? (last.Current.BoundingRectangle.Height / trayBounds.Height) : (last.Current.BoundingRectangle.Width / trayBounds.Width); - Debug.WriteLine("UI Scale: " + scale); - double size = (lastChildPos - (horizontal ? first.Current.BoundingRectangle.Left : first.Current.BoundingRectangle.Top)) / scale; - if (size < 0) - { - Debug.WriteLine("Size calculation failed"); - return true; - } + var first = TreeWalker.ControlViewWalker.GetFirstChild(taskList); + if (first == null) + { + Debug.WriteLine("Null values found for first child item, aborting"); + return true; + } - AutomationElement tasklistcontainer = TreeWalker.ControlViewWalker.GetParent(tasklist); - if (tasklistcontainer == null) - { - Debug.WriteLine("Null values found for parent, aborting"); - return true; - } + var scale = horizontal + ? last.Current.BoundingRectangle.Height / trayBounds.Height + : last.Current.BoundingRectangle.Width / trayBounds.Width; + Debug.WriteLine("UI Scale: " + scale); + var size = (lastChildPos - (horizontal + ? first.Current.BoundingRectangle.Left + : first.Current.BoundingRectangle.Top) + ) / scale; + if (size < 0) + { + Debug.WriteLine("Size calculation failed"); + return true; + } - Rect tasklistBounds = tasklist.Current.BoundingRectangle; + var taskListContainer = TreeWalker.ControlViewWalker.GetParent(taskList); + if (taskListContainer == null) + { + Debug.WriteLine("Null values found for parent, aborting"); + return true; + } - double barSize = horizontal ? trayWnd.Cached.BoundingRectangle.Width : trayWnd.Cached.BoundingRectangle.Height; - double targetPos = Math.Round((barSize - size) / 2) + (horizontal ? trayBounds.X : trayBounds.Y); + var taskListBounds = taskList.Current.BoundingRectangle; - Debug.Write("Bar size: "); - Debug.WriteLine(barSize); - Debug.Write("Total icon size: "); - Debug.WriteLine(size); - Debug.Write("Target abs " + (horizontal ? "X":"Y") + " position: "); - Debug.WriteLine(targetPos); + var barSize = horizontal ? trayWnd.Cached.BoundingRectangle.Width : trayWnd.Cached.BoundingRectangle.Height; + var targetPos = Math.Round((barSize - size) / 2) + (horizontal ? trayBounds.X : trayBounds.Y); - double delta = Math.Abs(targetPos - (horizontal ? tasklistBounds.X : tasklistBounds.Y)); - // Previous bounds check - if (delta <= 1) - { - // Already positioned within margin of error, avoid the unneeded MoveWindow call - Debug.WriteLine("Already positioned, ending to avoid the unneeded MoveWindow call (Delta: " + delta + ")"); - return false; - } + Debug.Write("Bar size: "); + Debug.WriteLine(barSize); + Debug.Write("Total icon size: "); + Debug.WriteLine(size); + Debug.Write("Target abs " + (horizontal ? "X" : "Y") + " position: "); + Debug.WriteLine(targetPos); - // Right bounds check - int rightBounds = SideBoundary(false, horizontal, tasklist); - if ((targetPos + size) > rightBounds) - { - // Shift off center when the bar is too big - double extra = (targetPos + size) - rightBounds; - Debug.WriteLine("Shifting off center, too big and hitting right/bottom boundary (" + (targetPos + size) + " > " + rightBounds + ") // " + extra); - targetPos -= extra; - } - - // Left bounds check - int leftBounds = SideBoundary(true, horizontal, tasklist); - if (targetPos <= leftBounds) - { - // Prevent X position ending up beyond the normal left aligned position - Debug.WriteLine("Target is more left than left/top aligned default, left/top aligning (" + targetPos + " <= " + leftBounds + ")"); - Reset(trayWnd); - return true; - } + var delta = Math.Abs(targetPos - (horizontal ? taskListBounds.X : taskListBounds.Y)); + // Previous bounds check + if (delta <= 1) + { + // Already positioned within margin of error, avoid the unneeded MoveWindow call + Debug.WriteLine("Already positioned, ending to avoid the unneeded MoveWindow call (Delta: " + delta + ")"); + return false; + } - IntPtr tasklistPtr = (IntPtr)tasklist.Current.NativeWindowHandle; + // Right bounds check + var rightBounds = SideBoundary(false, horizontal, taskList); + if (targetPos + size > rightBounds) + { + // Shift off center when the bar is too big + var extra = targetPos + size - rightBounds; + Debug.WriteLine("Shifting off center, too big and hitting right/bottom boundary (" + (targetPos + size) + " > " + rightBounds + ") // " + extra); + targetPos -= extra; + } - if (horizontal) - { - SetWindowPos(tasklistPtr, IntPtr.Zero, (RelativePos(targetPos, horizontal, tasklist)), 0, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_ASYNCWINDOWPOS); - Debug.Write("Final X Position: "); - Debug.WriteLine(tasklist.Current.BoundingRectangle.X); - Debug.Write((tasklist.Current.BoundingRectangle.X == targetPos) ? "Move hit target" : "Move missed target"); - Debug.WriteLine(" (diff: " + Math.Abs(tasklist.Current.BoundingRectangle.X - targetPos) + ")"); - } else - { - SetWindowPos(tasklistPtr, IntPtr.Zero, 0, (RelativePos(targetPos, horizontal, tasklist)), 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_ASYNCWINDOWPOS); - Debug.Write("Final Y Position: "); - Debug.WriteLine(tasklist.Current.BoundingRectangle.Y); - Debug.Write((tasklist.Current.BoundingRectangle.Y == targetPos) ? "Move hit target" : "Move missed target"); - Debug.WriteLine(" (diff: " + Math.Abs(tasklist.Current.BoundingRectangle.Y - targetPos) + ")"); - } + // Left bounds check + var leftBounds = SideBoundary(true, horizontal, taskList); + if (targetPos <= leftBounds) + { + // Prevent X position ending up beyond the normal left aligned position + Debug.WriteLine("Target is more left than left/top aligned default, left/top aligning (" + targetPos + " <= " + leftBounds + ")"); + Reset(trayWnd); + return true; + } - lasts[trayWnd] = horizontal ? last.Current.BoundingRectangle.Left : last.Current.BoundingRectangle.Top; + var taskListPtr = (IntPtr) taskList.Current.NativeWindowHandle; - return true; + if (horizontal) + { + SetWindowPos(taskListPtr, IntPtr.Zero, RelativePos(targetPos, horizontal, taskList), 0, 0, 0, + SWP_NOZORDER | SWP_NOSIZE | SWP_ASYNCWINDOWPOS); + Debug.Write("Final X Position: "); + Debug.WriteLine(taskList.Current.BoundingRectangle.X); + Debug.Write(taskList.Current.BoundingRectangle.X == targetPos ? "Move hit target" : "Move missed target"); + Debug.WriteLine(" (diff: " + Math.Abs(taskList.Current.BoundingRectangle.X - targetPos) + ")"); } + else + { + SetWindowPos(taskListPtr, IntPtr.Zero, 0, RelativePos(targetPos, horizontal, taskList), 0, 0, + SWP_NOZORDER | SWP_NOSIZE | SWP_ASYNCWINDOWPOS); + Debug.Write("Final Y Position: "); + Debug.WriteLine(taskList.Current.BoundingRectangle.Y); + Debug.Write(taskList.Current.BoundingRectangle.Y == targetPos ? "Move hit target" : "Move missed target"); + Debug.WriteLine(" (diff: " + Math.Abs(taskList.Current.BoundingRectangle.Y - targetPos) + ")"); + } + + _lasts[trayWnd] = horizontal ? last.Current.BoundingRectangle.Left : last.Current.BoundingRectangle.Top; + + return true; } - private int RelativePos(double x, bool horizontal, AutomationElement element) + private static int RelativePos(double x, bool horizontal, AutomationElement element) { - int adjustment = SideBoundary(true, horizontal, element); + var adjustment = SideBoundary(true, horizontal, element); - double newPos = x - adjustment; + var newPos = x - adjustment; if (newPos < 0) { @@ -414,70 +430,72 @@ private int RelativePos(double x, bool horizontal, AutomationElement element) newPos = 0; } - return (int)newPos; + return (int) newPos; } - private int SideBoundary(bool left, bool horizontal, AutomationElement element) + private static int SideBoundary(bool left, bool horizontal, AutomationElement element) { double adjustment = 0; - AutomationElement prevSibling = TreeWalker.ControlViewWalker.GetPreviousSibling(element); - AutomationElement nextSibling = TreeWalker.ControlViewWalker.GetNextSibling(element); - AutomationElement parent = TreeWalker.ControlViewWalker.GetParent(element); - if ((left && prevSibling != null)) + var prevSibling = TreeWalker.ControlViewWalker.GetPreviousSibling(element); + var nextSibling = TreeWalker.ControlViewWalker.GetNextSibling(element); + var parent = TreeWalker.ControlViewWalker.GetParent(element); + if (left && prevSibling != null) { - adjustment = horizontal ? prevSibling.Current.BoundingRectangle.Right : prevSibling.Current.BoundingRectangle.Bottom; - } else if (!left && nextSibling != null) + adjustment = horizontal + ? prevSibling.Current.BoundingRectangle.Right + : prevSibling.Current.BoundingRectangle.Bottom; + } + else if (!left && nextSibling != null) { - adjustment = horizontal ? nextSibling.Current.BoundingRectangle.Left : nextSibling.Current.BoundingRectangle.Top; + adjustment = horizontal + ? nextSibling.Current.BoundingRectangle.Left + : nextSibling.Current.BoundingRectangle.Top; } else if (parent != null) { if (horizontal) - { adjustment = left ? parent.Current.BoundingRectangle.Left : parent.Current.BoundingRectangle.Right; - } else - { + else adjustment = left ? parent.Current.BoundingRectangle.Top : parent.Current.BoundingRectangle.Bottom; - } - } if (horizontal) - { - Debug.WriteLine((left ? "Left" : "Right") + " side boundary calulcated at " + adjustment); - } else - { - Debug.WriteLine((left ? "Top" : "Bottom") + " side boundary calulcated at " + adjustment); - } - - return (int)adjustment; + Debug.WriteLine((left ? "Left" : "Right") + " side boundary calculated at " + adjustment); + else + Debug.WriteLine((left ? "Top" : "Bottom") + " side boundary calculated at " + adjustment); + + return (int) adjustment; } // Protected implementation of Dispose pattern. protected override void Dispose(bool disposing) { - if (disposed) + if (_disposed) return; if (disposing) { // Stop listening for new events - if (UIAeventHandler != null) + if (_uiaEventHandler != null) { - //Automation.RemoveAutomationPropertyChangedEventHandler(tasklist, propChangeHandler); //TODO: Remove these from each taskbar - Automation.RemoveAutomationEventHandler(WindowPattern.WindowOpenedEvent, desktop, UIAeventHandler); - Automation.RemoveAutomationEventHandler(WindowPattern.WindowClosedEvent, desktop, UIAeventHandler); + foreach (var taskBar in _children) + { + Automation.RemoveAutomationPropertyChangedEventHandler(taskBar.Value, _propChangeHandler); + } + + Automation.RemoveAutomationEventHandler(WindowPattern.WindowOpenedEvent, Desktop, _uiaEventHandler); + Automation.RemoveAutomationEventHandler(WindowPattern.WindowClosedEvent, Desktop, _uiaEventHandler); } // Put icons back ResetAll(); // Hide tray icon, otherwise it will remain shown until user mouses over it - trayIcon.Visible = false; - trayIcon.Dispose(); + _trayIcon.Visible = false; + _trayIcon.Dispose(); } - disposed = true; + _disposed = true; } } -} +} \ No newline at end of file