SystemTrayMenu/Business/Menus.cs
Peter Kirmeier 09e9751ae8 Fixed start location of first menu
Additionally fixed potential null pointer exception when no predecessor is found and menu is attempted to be drawn out of bounds
2020-09-20 19:19:21 +02:00

952 lines
34 KiB
C#

// <copyright file="Menus.cs" company="PlaceholderCompany">
// Copyright (c) PlaceholderCompany. All rights reserved.
// </copyright>
namespace SystemTrayMenu.Business
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Security;
using System.Windows.Forms;
using SystemTrayMenu.DataClasses;
using SystemTrayMenu.Handler;
using SystemTrayMenu.Helper;
using SystemTrayMenu.Utilities;
using Menu = SystemTrayMenu.UserInterface.Menu;
using Timer = System.Windows.Forms.Timer;
internal class Menus : IDisposable
{
private readonly Menu[] menus = new Menu[MenuDefines.MenusMax];
private readonly BackgroundWorker workerMainMenu = new BackgroundWorker();
private readonly List<BackgroundWorker> workersSubMenu = new List<BackgroundWorker>();
private readonly DgvMouseRow dgvMouseRow = new DgvMouseRow();
private readonly WaitToLoadMenu waitToOpenMenu = new WaitToLoadMenu();
private readonly KeyboardInput keyboardInput = null;
private readonly Timer timerStillActiveCheck = new Timer();
private readonly WaitLeave waitLeave = new WaitLeave(MenuDefines.TimeUntilClose);
private DateTime deactivatedTime = DateTime.MinValue;
private OpenCloseState openCloseState = OpenCloseState.Default;
private RowData loadingRowData = null;
private bool showingMessageBox = false;
private TaskbarPosition taskbarPosition = new WindowsTaskbar().Position;
public Menus()
{
workerMainMenu.WorkerSupportsCancellation = true;
workerMainMenu.DoWork += LoadMenu;
static void LoadMenu(object senderDoWork, DoWorkEventArgs eDoWork)
{
string path = Config.Path;
int level = 0;
RowData rowData = eDoWork.Argument as RowData;
if (rowData != null)
{
path = rowData.TargetFilePath;
level = rowData.MenuLevel + 1;
}
MenuData menuData = GetData((BackgroundWorker)senderDoWork, path, level);
menuData.RowDataParent = rowData;
eDoWork.Result = menuData;
}
workerMainMenu.RunWorkerCompleted += LoadMainMenuCompleted;
void LoadMainMenuCompleted(object sender, RunWorkerCompletedEventArgs e)
{
keyboardInput.ResetSelectedByKey();
LoadStopped();
MenuData menuData = (MenuData)e.Result;
switch (menuData.Validity)
{
case MenuDataValidity.Valid:
DisposeMenu(menus[menuData.Level]);
menus[0] = Create(menuData, Path.GetFileName(Config.Path));
AsEnumerable.ToList().ForEach(m => { m.ShowWithFade(); });
break;
case MenuDataValidity.Empty:
if (!showingMessageBox)
{
showingMessageBox = true;
MessageBox.Show(Translator.GetText(
"MessageRootFolderEmpty"));
OpenFolder();
showingMessageBox = false;
}
break;
case MenuDataValidity.NoAccess:
if (!showingMessageBox)
{
showingMessageBox = true;
MessageBox.Show(Translator.GetText(
"MessageRootFolderNoAccess"));
OpenFolder();
showingMessageBox = false;
}
break;
case MenuDataValidity.AbortedOrUnknown:
Log.Info("MenuDataValidity.AbortedOrUnknown");
break;
default:
break;
}
}
waitToOpenMenu.StopLoadMenu += WaitToOpenMenu_StopLoadMenu;
void WaitToOpenMenu_StopLoadMenu()
{
foreach (BackgroundWorker workerSubMenu in workersSubMenu.
Where(w => w.IsBusy))
{
workerSubMenu.CancelAsync();
}
LoadStopped();
}
waitToOpenMenu.StartLoadMenu += StartLoadMenu;
void StartLoadMenu(RowData rowData)
{
if (menus[0].IsUsable &&
loadingRowData != rowData &&
(menus[rowData.MenuLevel + 1] == null ||
menus[rowData.MenuLevel + 1].Tag as RowData != rowData))
{
loadingRowData = rowData;
LoadStarted();
BackgroundWorker workerSubMenu = workersSubMenu.
Where(w => !w.IsBusy).FirstOrDefault();
if (workerSubMenu == null)
{
workerSubMenu = new BackgroundWorker
{
WorkerSupportsCancellation = true,
};
workerSubMenu.DoWork += LoadMenu;
workerSubMenu.RunWorkerCompleted += LoadSubMenuCompleted;
workersSubMenu.Add(workerSubMenu);
}
workerSubMenu.RunWorkerAsync(rowData);
}
void LoadSubMenuCompleted(object senderCompleted, RunWorkerCompletedEventArgs e)
{
LoadStopped();
MenuData menuData = (MenuData)e.Result;
if (menus[0].IsUsable &&
menuData.Validity != MenuDataValidity.AbortedOrUnknown)
{
Menu menu = Create(menuData);
switch (menuData.Validity)
{
case MenuDataValidity.Valid:
menu.SetTypeSub();
break;
case MenuDataValidity.Empty:
menu.SetTypeEmpty();
break;
case MenuDataValidity.NoAccess:
menu.SetTypeNoAccess();
break;
}
menu.Tag = menuData.RowDataParent;
menuData.RowDataParent.SubMenu = menu;
if (menus[0].IsUsable)
{
ShowSubMenu(menu);
}
}
loadingRowData = null;
}
}
waitToOpenMenu.CloseMenu += CloseMenu;
void CloseMenu(int level)
{
if (menus[level] != null)
{
menus[level - 1].FocusTextBox();
HideOldMenu(menus[level]);
}
}
waitToOpenMenu.MouseEnterOk += MouseEnterOk;
void MouseEnterOk(DataGridView dgv, int rowIndex)
{
if (menus[0].IsUsable)
{
if (keyboardInput.InUse)
{
keyboardInput.ClearIsSelectedByKey();
keyboardInput.InUse = false;
}
keyboardInput.Select(dgv, rowIndex, false);
}
}
dgvMouseRow.RowMouseEnter += waitToOpenMenu.MouseEnter;
dgvMouseRow.RowMouseLeave += waitToOpenMenu.MouseLeave;
keyboardInput = new KeyboardInput(menus);
keyboardInput.RegisterHotKey();
keyboardInput.HotKeyPressed += KeyboardInput_HotKeyPressed;
void KeyboardInput_HotKeyPressed()
{
SwitchOpenClose(false);
}
keyboardInput.ClosePressed += MenusFadeOut;
keyboardInput.RowDeselected += waitToOpenMenu.RowDeselected;
keyboardInput.RowSelected += waitToOpenMenu.RowSelected;
keyboardInput.EnterPressed += waitToOpenMenu.EnterOpensInstantly;
timerStillActiveCheck.Interval = 1000;
timerStillActiveCheck.Tick += StillActiveTick;
void StillActiveTick(object senderTimer, EventArgs eTimer)
{
if (!IsActive())
{
FadeHalfOrOutIfNeeded();
timerStillActiveCheck.Stop();
}
}
waitLeave.LeaveTriggered += LeaveTriggered;
void LeaveTriggered()
{
FadeHalfOrOutIfNeeded();
}
}
internal event EventHandlerEmpty LoadStarted;
internal event EventHandlerEmpty LoadStopped;
private enum OpenCloseState
{
Default,
Opening,
Closing,
}
private IEnumerable<Menu> AsEnumerable => menus.Where(m => m != null && !m.IsDisposed);
private List<Menu> AsList => AsEnumerable.ToList();
public void Dispose()
{
workerMainMenu.Dispose();
foreach (BackgroundWorker worker in workersSubMenu)
{
worker.Dispose();
}
waitToOpenMenu.Dispose();
keyboardInput.Dispose();
timerStillActiveCheck.Dispose();
waitLeave.Dispose();
IconReader.Dispose();
DisposeMenu(menus[0]);
loadingRowData?.Dispose();
dgvMouseRow.Dispose();
}
internal static MenuData GetData(BackgroundWorker worker, string path, int level)
{
MenuData menuData = new MenuData
{
RowDatas = new List<RowData>(),
Validity = MenuDataValidity.AbortedOrUnknown,
Level = level,
};
if (!worker.CancellationPending)
{
string[] directories = Array.Empty<string>();
try
{
if (FileLnk.IsNetworkRoot(path))
{
directories = GetDirectoriesInNetworkLocation(path);
static string[] GetDirectoriesInNetworkLocation(string networkLocationRootPath)
{
List<string> directories = new List<string>();
try
{
Process cmd = new Process();
cmd.StartInfo.FileName = "cmd.exe";
cmd.StartInfo.RedirectStandardInput = true;
cmd.StartInfo.RedirectStandardOutput = true;
cmd.StartInfo.CreateNoWindow = true;
cmd.StartInfo.UseShellExecute = false;
cmd.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
cmd.Start();
cmd.StandardInput.WriteLine($"net view {networkLocationRootPath}");
cmd.StandardInput.Flush();
cmd.StandardInput.Close();
string output = cmd.StandardOutput.ReadToEnd();
cmd.WaitForExit();
cmd.Close();
output = output.Substring(output.LastIndexOf('-') + 2);
output = output.Substring(0, output.IndexOf("The command completed successfully.", StringComparison.InvariantCulture));
foreach (string line in output
.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries))
{
int indexOfSecondColumnTypeDisk = line.IndexOf("Disk", StringComparison.InvariantCulture);
if (indexOfSecondColumnTypeDisk > 0)
{
string directory = Path.Combine(
networkLocationRootPath,
line.Substring(0, indexOfSecondColumnTypeDisk));
directories.Add(directory);
}
}
}
catch (ArgumentOutOfRangeException ex)
{
Log.Warn($"Could not resolve network root folder: {networkLocationRootPath}", ex);
}
return directories.ToArray();
}
}
else
{
directories = Directory.GetDirectories(path);
}
Array.Sort(directories, new WindowsExplorerSort());
}
catch (UnauthorizedAccessException ex)
{
Log.Warn($"path:'{path}'", ex);
menuData.Validity = MenuDataValidity.NoAccess;
}
catch (IOException ex)
{
Log.Warn($"path:'{path}'", ex);
}
foreach (string directory in directories)
{
if (worker != null && worker.CancellationPending)
{
break;
}
bool hiddenEntry = false;
if (FolderOptions.IsHidden(directory, ref hiddenEntry))
{
continue;
}
RowData rowData = ReadRowData(directory, false);
rowData.ContainsMenu = true;
rowData.HiddenEntry = hiddenEntry;
string resolvedLnkPath = string.Empty;
rowData.ReadIcon(true, ref resolvedLnkPath);
rowData.MenuLevel = level;
menuData.RowDatas.Add(rowData);
}
}
if (!worker.CancellationPending)
{
string[] files = Array.Empty<string>();
try
{
if (!FileLnk.IsNetworkRoot(path))
{
files = Directory.GetFiles(path);
}
Array.Sort(files, new WindowsExplorerSort());
}
catch (UnauthorizedAccessException ex)
{
Log.Warn($"path:'{path}'", ex);
menuData.Validity = MenuDataValidity.NoAccess;
}
catch (IOException ex)
{
Log.Warn($"path:'{path}'", ex);
}
foreach (string file in files)
{
if (worker != null && worker.CancellationPending)
{
break;
}
bool hiddenEntry = false;
if (FolderOptions.IsHidden(file, ref hiddenEntry))
{
continue;
}
RowData rowData = ReadRowData(file, false);
string resolvedLnkPath = string.Empty;
if (rowData.ReadIcon(false, ref resolvedLnkPath))
{
rowData = ReadRowData(resolvedLnkPath, true, rowData);
rowData.ContainsMenu = true;
rowData.HiddenEntry = hiddenEntry;
}
menuData.RowDatas.Add(rowData);
}
}
if (!worker.CancellationPending)
{
if (menuData.Validity == MenuDataValidity.AbortedOrUnknown)
{
if (menuData.RowDatas.Count == 0)
{
menuData.Validity = MenuDataValidity.Empty;
}
else
{
menuData.Validity = MenuDataValidity.Valid;
}
}
}
return menuData;
}
internal void SwitchOpenCloseByTaskbarItem()
{
SwitchOpenClose(true);
timerStillActiveCheck.Start();
}
internal void SwitchOpenClose(bool byClick)
{
waitToOpenMenu.MouseActive = byClick;
if (byClick && (DateTime.Now - deactivatedTime).TotalMilliseconds < 200)
{
// By click on notifyicon the menu gets deactivated and closed
}
else if (string.IsNullOrEmpty(Config.Path))
{
// Case when Folder Dialog open
}
else if (openCloseState == OpenCloseState.Opening ||
(menus[0] != null && menus[0].Visible && openCloseState == OpenCloseState.Default))
{
openCloseState = OpenCloseState.Closing;
MenusFadeOut();
StopWorker();
if (!AsEnumerable.Any(m => m.Visible))
{
openCloseState = OpenCloseState.Default;
}
}
else
{
openCloseState = OpenCloseState.Opening;
StartWorker();
}
deactivatedTime = DateTime.MinValue;
}
internal void DisposeMenu(Menu menuToDispose)
{
if (menuToDispose != null)
{
menuToDispose.MouseWheel -= AdjustMenusSizeAndLocation;
menuToDispose.MouseLeave -= waitLeave.Start;
menuToDispose.MouseEnter -= waitLeave.Stop;
menuToDispose.KeyPress -= keyboardInput.KeyPress;
menuToDispose.CmdKeyProcessed -= keyboardInput.CmdKeyProcessed;
menuToDispose.SearchTextChanging -= keyboardInput.SearchTextChanging;
menuToDispose.SearchTextChanged -= Menu_SearchTextChanged;
DataGridView dgv = menuToDispose.GetDataGridView();
dgv.CellMouseEnter -= dgvMouseRow.CellMouseEnter;
dgv.CellMouseLeave -= dgvMouseRow.CellMouseLeave;
dgv.MouseLeave -= dgvMouseRow.MouseLeave;
dgv.MouseMove -= waitToOpenMenu.MouseMove;
dgv.MouseDown -= Dgv_MouseDown;
dgv.MouseDoubleClick -= Dgv_MouseDoubleClick;
dgv.SelectionChanged -= Dgv_SelectionChanged;
dgv.RowPostPaint -= Dgv_RowPostPaint;
dgv.ClearSelection();
foreach (DataGridViewRow row in dgv.Rows)
{
RowData rowData = (RowData)row.Cells[2].Value;
rowData.Dispose();
DisposeMenu(rowData.SubMenu);
}
menuToDispose.Dispose();
}
}
internal void MainPreload()
{
menus[0] = Create(
GetData(workerMainMenu, Config.Path, 0),
Path.GetFileName(Config.Path));
AdjustMenusSizeAndLocation();
DisposeMenu(menus[0]);
}
internal void StartWorker()
{
if (!workerMainMenu.IsBusy)
{
LoadStarted();
workerMainMenu.RunWorkerAsync(
new object[] { Config.Path, 0 });
}
}
internal void StopWorker()
{
if (workerMainMenu.IsBusy)
{
workerMainMenu.CancelAsync();
}
}
private static RowData ReadRowData(string fileName, bool isResolvedLnk, RowData rowData = null)
{
if (rowData == null)
{
rowData = new RowData();
}
rowData.IsResolvedLnk = isResolvedLnk;
try
{
rowData.FileInfo = new FileInfo(fileName);
rowData.TargetFilePath = rowData.FileInfo.FullName;
if (!isResolvedLnk)
{
if (string.IsNullOrEmpty(rowData.FileInfo.Name))
{
string path = rowData.FileInfo.FullName;
int directoryNameBegin = path.LastIndexOf(@"\", StringComparison.InvariantCulture) + 1;
rowData.SetText(path.Substring(directoryNameBegin));
}
else
{
rowData.SetText(rowData.FileInfo.Name);
}
rowData.TargetFilePathOrig = rowData.FileInfo.FullName;
}
}
catch (Exception ex)
{
if (ex is SecurityException ||
ex is ArgumentException ||
ex is UnauthorizedAccessException ||
ex is PathTooLongException ||
ex is NotSupportedException)
{
Log.Warn($"fileName:'{fileName}'", ex);
}
else
{
throw;
}
}
return rowData;
}
private static bool IsActive()
{
return Form.ActiveForm is Menu ||
Form.ActiveForm is UserInterface.TaskbarForm;
}
private static void OpenFolder()
{
Log.ProcessStart(Config.Path, null, true);
}
private Menu Create(MenuData menuData, string title = null)
{
Menu menu = new Menu();
if (title != null)
{
if (string.IsNullOrEmpty(title))
{
title = Path.GetPathRoot(Config.Path);
}
menu.SetTitle(title);
menu.UserClickedOpenFolder += OpenFolder;
}
menu.Level = menuData.Level;
menu.MouseWheel += AdjustMenusSizeAndLocation;
menu.MouseLeave += waitLeave.Start;
menu.MouseEnter += waitLeave.Stop;
menu.KeyPress += keyboardInput.KeyPress;
menu.CmdKeyProcessed += keyboardInput.CmdKeyProcessed;
menu.SearchTextChanging += keyboardInput.SearchTextChanging;
menu.SearchTextChanged += Menu_SearchTextChanged;
menu.Deactivate += Deactivate;
void Deactivate(object sender, EventArgs e)
{
FadeHalfOrOutIfNeeded();
if (!IsActive())
{
deactivatedTime = DateTime.Now;
}
}
menu.Activated += Activated;
void Activated(object sender, EventArgs e)
{
if (IsActive() &&
menus[0].IsUsable)
{
AsList.ForEach(m => m.ShowWithFade());
timerStillActiveCheck.Start();
}
}
menu.VisibleChanged += MenuVisibleChanged;
AddItemsToMenu(menuData.RowDatas, menu);
static void AddItemsToMenu(List<RowData> data, Menu menu)
{
DataGridView dgv = menu.GetDataGridView();
DataTable dataTable = new DataTable();
dataTable.Columns.Add(dgv.Columns[0].Name, typeof(Icon));
dataTable.Columns.Add(dgv.Columns[1].Name, typeof(string));
dataTable.Columns.Add("data", typeof(RowData));
foreach (RowData rowData in data)
{
rowData.SetData(rowData, dataTable);
}
dgv.DataSource = dataTable;
}
DataGridView dgv = menu.GetDataGridView();
dgv.CellMouseEnter += dgvMouseRow.CellMouseEnter;
dgv.CellMouseLeave += dgvMouseRow.CellMouseLeave;
dgv.MouseLeave += dgvMouseRow.MouseLeave;
dgv.MouseMove += waitToOpenMenu.MouseMove;
dgv.MouseDown += Dgv_MouseDown;
dgv.MouseDoubleClick += Dgv_MouseDoubleClick;
dgv.SelectionChanged += Dgv_SelectionChanged;
dgv.RowPostPaint += Dgv_RowPostPaint;
return menu;
}
private void MenuVisibleChanged(object sender, EventArgs e)
{
Menu menu = (Menu)sender;
if (menu.IsUsable)
{
AdjustMenusSizeAndLocation();
}
if (!menu.Visible)
{
DisposeMenu(menu);
}
if (!AsEnumerable.Any(m => m.Visible))
{
openCloseState = OpenCloseState.Default;
}
}
private void Dgv_MouseDown(object sender, MouseEventArgs e)
{
DataGridView dgv = (DataGridView)sender;
DataGridView.HitTestInfo hitTestInfo;
hitTestInfo = dgv.HitTest(e.X, e.Y);
if (hitTestInfo.RowIndex > -1 &&
dgv.Rows.Count > hitTestInfo.RowIndex)
{
RowData rowData = (RowData)dgv.Rows[hitTestInfo.RowIndex].Cells[2].Value;
rowData.MouseDown(dgv, e);
waitToOpenMenu.ClickOpensInstantly(dgv, hitTestInfo.RowIndex);
}
}
private void Dgv_MouseDoubleClick(object sender, MouseEventArgs e)
{
DataGridView dgv = (DataGridView)sender;
DataGridView.HitTestInfo hitTestInfo;
hitTestInfo = dgv.HitTest(e.X, e.Y);
if (hitTestInfo.RowIndex > -1 &&
dgv.Rows.Count > hitTestInfo.RowIndex)
{
RowData trigger = (RowData)dgv.Rows[hitTestInfo.RowIndex].Cells[2].Value;
trigger.DoubleClick(e);
}
}
private void Dgv_SelectionChanged(object sender, EventArgs e)
{
RefreshSelection((DataGridView)sender);
}
private void RefreshSelection(DataGridView dgv)
{
foreach (DataGridViewRow row in dgv.Rows)
{
RowData rowData = (RowData)row.Cells[2].Value;
if (rowData == null)
{
// Case when filtering a previous menu
}
else if (!menus[0].IsUsable)
{
row.DefaultCellStyle.SelectionBackColor = Color.White;
row.Selected = false;
}
else if (rowData.IsContextMenuOpen || (rowData.IsMenuOpen && rowData.IsSelected))
{
row.Selected = true;
}
else if (rowData.IsMenuOpen)
{
row.Selected = true;
}
else if (rowData.IsSelected)
{
row.DefaultCellStyle.SelectionBackColor = MenuDefines.ColorSelectedItem;
row.Selected = true;
}
else
{
row.DefaultCellStyle.SelectionBackColor = Color.White;
row.Selected = false;
}
}
dgv.Refresh();
}
private void Dgv_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e)
{
DataGridView dgv = (DataGridView)sender;
DataGridViewRow row = dgv.Rows[e.RowIndex];
if (row.Selected)
{
RowData rowData = (RowData)row.Cells[2].Value;
int width = dgv.Columns[0].Width + dgv.Columns[1].Width;
Rectangle rowBounds = new Rectangle(0, e.RowBounds.Top, width, e.RowBounds.Height);
if (rowData.IsContextMenuOpen || (rowData.IsMenuOpen && rowData.IsSelected))
{
ControlPaint.DrawBorder(e.Graphics, rowBounds, MenuDefines.ColorSelectedItemBorder, ButtonBorderStyle.Solid);
row.DefaultCellStyle.SelectionBackColor = MenuDefines.ColorSelectedItem;
}
else if (rowData.IsMenuOpen)
{
ControlPaint.DrawBorder(e.Graphics, rowBounds, MenuDefines.ColorOpenFolderBorder, ButtonBorderStyle.Solid);
row.DefaultCellStyle.SelectionBackColor = MenuDefines.ColorOpenFolder;
}
}
}
private void ShowSubMenu(Menu menuToShow)
{
HideOldMenu(menuToShow, true);
menus[menuToShow.Level] = menuToShow;
AdjustMenusSizeAndLocation();
menus[menuToShow.Level].ShowWithFadeOrTransparent(IsActive());
}
private void HideOldMenu(Menu menuToShow, bool keepOrSetIsMenuOpen = false)
{
// Clean up menu status IsMenuOpen for previous one
Menu menuPrevious = menus[menuToShow.Level - 1];
DataGridView dgvPrevious = menuPrevious.GetDataGridView();
foreach (DataRow row in ((DataTable)dgvPrevious.DataSource).Rows)
{
RowData rowDataToClear = (RowData)row[2];
if (rowDataToClear == (RowData)menuToShow.Tag)
{
rowDataToClear.IsMenuOpen = keepOrSetIsMenuOpen;
}
else
{
rowDataToClear.IsMenuOpen = false;
}
}
RefreshSelection(dgvPrevious);
// Hide old menu
foreach (Menu menuToClose in menus.Where(
m => m != null && m.Level > menuPrevious.Level))
{
menuToClose.VisibleChanged += MenuVisibleChanged;
menuToClose.HideWithFade();
menus[menuToClose.Level] = null;
}
}
private void FadeHalfOrOutIfNeeded()
{
if (menus[0].IsUsable)
{
if (!IsActive())
{
Point position = Control.MousePosition;
if (AsList.Any(m => m.IsMouseOn(position)))
{
if (!keyboardInput.InUse)
{
AsList.ForEach(menu => menu.ShowTransparent());
}
}
else
{
MenusFadeOut();
}
}
}
}
private void MenusFadeOut()
{
openCloseState = OpenCloseState.Closing;
AsList.ForEach(menu =>
{
if (menu.Level > 0)
{
menus[menu.Level] = null;
}
menu.HideWithFade();
});
}
private void AdjustMenusSizeAndLocation()
{
WindowsTaskbar taskbar = new WindowsTaskbar();
Menu menuPredecessor = null;
int widthPredecessors = -1; // -1 padding
List<Menu> list = AsList;
bool directionToRight;
Menu menu;
Rectangle screenBounds = Screen.PrimaryScreen.Bounds;
Menu.StartLocation startLocation;
// Only apply taskbar position change when no menu is currently open
if (list.Count == 1)
{
taskbarPosition = taskbar.Position;
}
// Shrink the usable space depending on taskbar location
switch (taskbarPosition)
{
case TaskbarPosition.Left:
screenBounds.X += taskbar.Size.Width;
screenBounds.Width -= taskbar.Size.Width;
startLocation = Menu.StartLocation.BottomLeft;
directionToRight = true;
break;
case TaskbarPosition.Right:
screenBounds.Width -= taskbar.Size.Width;
startLocation = Menu.StartLocation.BottomRight;
directionToRight = false;
break;
case TaskbarPosition.Top:
screenBounds.Y += taskbar.Size.Height;
screenBounds.Height -= taskbar.Size.Height;
startLocation = Menu.StartLocation.TopRight;
directionToRight = false;
break;
case TaskbarPosition.Bottom:
default:
screenBounds.Height -= taskbar.Size.Height;
startLocation = Menu.StartLocation.BottomRight;
directionToRight = false;
break;
}
for (int i = 0; i < list.Count; i++)
{
menu = list[i];
// Skip calculation based on the predecessor for the very first menu
if (menuPredecessor != null)
{
int newWidth = menu.Width - menu.Padding.Horizontal + menuPredecessor.Width;
if (directionToRight)
{
if (widthPredecessors - menu.Width < 0)
{
directionToRight = false;
}
else
{
widthPredecessors -= newWidth;
}
}
else if (screenBounds.Width < widthPredecessors + menu.Width)
{
directionToRight = true;
widthPredecessors -= newWidth;
}
widthPredecessors += menu.Width - menu.Padding.Left;
}
// Only last one has to be updated as all previous one were already updated in the past
if (list.Count - 1 == i)
{
menu.AdjustSizeAndLocation(screenBounds, menuPredecessor, directionToRight, startLocation);
}
if (i == 0)
{
// Remember width of the initial menu as we don't want to overlap with it
screenBounds.Width -= menu.Width - menu.Padding.Horizontal;
}
menuPredecessor = menu;
}
}
private void Menu_SearchTextChanged(object sender, EventArgs e)
{
keyboardInput.SearchTextChanged(sender, e);
AdjustMenusSizeAndLocation();
}
}
}