using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Threading.Tasks.Dataflow; using Windows; using Widow.Properties; namespace Widow { public class WindowManipulation : IDisposable { #region Public Enums, Properties and Fields public bool OnWindowCreate { get; set; } public int ApplyEveryTime { get; set; } public bool ApplyEveryTimeEnabled { get; set; } #endregion #region Private Delegates, Events, Enums, Properties, Indexers and Fields private MainForm Form { get; } private Windows.Windows WindowsToManipulate { get; set; } private CancellationTokenSource CancellationTokenSource { get; set; } private Task ApplyEveryTask { get; } private BufferBlock WindowsBufferBlock { get; } private ActionBlock WindowsActionBlock { get; } private IDisposable WindowsLink { get; set; } #endregion #region Constructors, Destructors and Finalizers public WindowManipulation() { CancellationTokenSource = new CancellationTokenSource(); WindowsBufferBlock = new BufferBlock(); WindowsActionBlock = new ActionBlock(ManipulateWindows); WindowsLink = WindowsBufferBlock.LinkTo(WindowsActionBlock); ApplyEveryTask = ApplyEvery(CancellationTokenSource.Token); } public WindowManipulation(MainForm mainForm) : this() { Form = mainForm; Form.WindowCreated += Form_WindowCreated; } public WindowManipulation(Settings settings, MainForm mainForm) : this(mainForm) { OnWindowCreate = settings.OnWindowCreate; ApplyEveryTimeEnabled = settings.ApplyEveryTimeEnabled; if (int.TryParse(settings.ApplyEveryTime, out var time)) { ApplyEveryTime = time; } } public void Dispose() { WindowsLink?.Dispose(); WindowsLink = null; Form.WindowCreated -= Form_WindowCreated; CancellationTokenSource.Cancel(); ApplyEveryTask.Wait(); CancellationTokenSource.Dispose(); CancellationTokenSource = null; } #endregion #region Event Handlers private async void Form_WindowCreated(object sender, WindowCreatedEventArgs e) { if (!OnWindowCreate) { return; } if (WindowsToManipulate == null) { return; } var @class = Helpers.GetWindowClass(e.Handle); if (!WindowsToManipulate.TryGetWindow(new WindowHash(e.Title, @class), out _)) { return; } await Apply(); } #endregion #region Public Methods public void AddWindows(Windows.Windows windows) { WindowsToManipulate = windows; } public async Task Apply() { if (WindowsToManipulate == null) { return; } var windows = new List(); foreach (var hWnd in Helpers.FindWindows((wnd, param) => true)) { var windowTitle = Helpers.GetWindowTitle(hWnd); if (string.IsNullOrEmpty(windowTitle)) { continue; } var windowClass = Helpers.GetWindowClass(hWnd); if (string.IsNullOrEmpty(windowClass)) { continue; } var windowHash = new WindowHash(windowTitle, windowClass); if (!WindowsToManipulate.TryGetWindow(windowHash, out var window)) { continue; } windows.Add(new WindowModification(hWnd, window)); } await Task.WhenAll(windows.Select(modification => WindowsBufferBlock.SendAsync(modification))); } #endregion #region Private Methods private static void ManipulateWindows(WindowModification modification) { if (modification.Handle == IntPtr.Zero) { return; } if (!Natives.GetWindowRect(modification.Handle, out var rect)) { return; } var left = modification.Window.Left; if (modification.Window.IgnoreLeft) { left = rect.Left; } var top = modification.Window.Top; if (modification.Window.IgnoreTop) { top = rect.Top; } var width = modification.Window.Width; if (modification.Window.IgnoreWidth) { width = rect.Left - rect.Right; } var height = modification.Window.Height; if (modification.Window.IgnoreHeight) { height = rect.Top - rect.Bottom; } Natives.MoveWindow(modification.Handle, left, top, width, height, true); } private async Task ApplyEvery(CancellationToken cancellationToken) { do { if (!ApplyEveryTimeEnabled) { await Task.Delay(1000, cancellationToken); continue; } await Task.Delay(ApplyEveryTime, cancellationToken); await Apply(); } while (!cancellationToken.IsCancellationRequested); } #endregion } }