Replaced internal "menus" array with chained instances.

This commit is contained in:
Peter Kirmeier 2023-05-08 00:14:23 +02:00
parent 56476e095d
commit 14d0896419
7 changed files with 288 additions and 267 deletions

View file

@ -16,17 +16,11 @@ namespace SystemTrayMenu.Handler
internal class KeyboardInput : IDisposable
{
private readonly Menu?[] menus;
private readonly KeyboardHook hook = new();
private Menu? focussedMenu;
private ListViewItemData? focussedRow;
public KeyboardInput(Menu?[] menus)
{
this.menus = menus;
}
internal event Action? HotKeyPressed;
internal event Action? ClosePressed;
@ -44,9 +38,9 @@ namespace SystemTrayMenu.Handler
hook.Dispose();
}
internal void RegisterHotKey()
internal bool RegisterHotKey(string hotKey)
{
if (!string.IsNullOrEmpty(Properties.Settings.Default.HotKey))
if (!string.IsNullOrEmpty(hotKey))
{
try
{
@ -55,11 +49,12 @@ namespace SystemTrayMenu.Handler
}
catch (InvalidOperationException ex)
{
Log.Warn($"key:'{Properties.Settings.Default.HotKey}'", ex);
Properties.Settings.Default.HotKey = string.Empty;
Properties.Settings.Default.Save();
Log.Warn($"Hotkey cannot be set: '{hotKey}'", ex);
return false;
}
}
return true;
}
internal void ResetSelectedByKey()
@ -110,35 +105,24 @@ namespace SystemTrayMenu.Handler
case Key.Tab:
if (modifiers == ModifierKeys.None)
{
int indexOfTheCurrentMenu = GetMenuIndex(sender);
int indexMax = menus.Where(m => m != null).Count() - 1;
int indexNew = 0;
if (indexOfTheCurrentMenu > 0)
// Walk to previous text box and warp around when main menu reached
Menu? menu = sender.ParentMenu;
if (menu == null)
{
indexNew = indexOfTheCurrentMenu - 1;
}
else
{
indexNew = indexMax;
menu = sender;
while (menu.SubMenu != null)
{
menu = menu.SubMenu;
}
}
menus[indexNew]?.FocusTextBox();
menu.FocusTextBox();
}
else if (modifiers == ModifierKeys.Shift)
{
int indexOfTheCurrentMenu = GetMenuIndex(sender);
int indexMax = menus.Where(m => m != null).Count() - 1;
int indexNew = 0;
if (indexOfTheCurrentMenu < indexMax)
{
indexNew = indexOfTheCurrentMenu + 1;
}
else
{
indexNew = 0;
}
menus[indexNew]?.FocusTextBox();
// Walk to next text box and warp around back to main menu on last sub menu
Menu? menu = sender.SubMenu ?? sender.MainMenu;
menu.FocusTextBox();
}
break;
@ -164,22 +148,6 @@ namespace SystemTrayMenu.Handler
default:
break;
}
int GetMenuIndex(in Menu? currentMenu)
{
int index = 0;
foreach (Menu? menuFindIndex in menus.Where(m => m != null))
{
if (currentMenu == menuFindIndex)
{
break;
}
index++;
}
return index;
}
}
internal void SearchTextChanged(Menu menu, bool isSearchStringEmpty)
@ -343,7 +311,6 @@ namespace SystemTrayMenu.Handler
}
else
{
focussedMenu = menus[0];
while (focussedMenu?.SubMenu != null)
{
focussedMenu = focussedMenu.SubMenu;

View file

@ -8,6 +8,7 @@ namespace SystemTrayMenu.Business
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Windows;
@ -20,6 +21,7 @@ namespace SystemTrayMenu.Business
using SystemTrayMenu.Handler;
using SystemTrayMenu.Helpers;
using SystemTrayMenu.Properties;
using SystemTrayMenu.UserInterface;
using SystemTrayMenu.Utilities;
using static SystemTrayMenu.UserInterface.Menu;
using Menu = SystemTrayMenu.UserInterface.Menu;
@ -27,7 +29,6 @@ namespace SystemTrayMenu.Business
internal class Menus : IDisposable
{
private readonly Dispatcher dispatchter = Dispatcher.CurrentDispatcher;
private readonly Menu?[] menus = new Menu?[MenuDefines.MenusMax];
private readonly BackgroundWorker workerMainMenu = new();
private readonly List<BackgroundWorker> workersSubMenu = new();
private readonly WaitToLoadMenu waitToOpenMenu = new();
@ -43,11 +44,17 @@ namespace SystemTrayMenu.Business
private TaskbarPosition taskbarPosition = new WindowsTaskbar().Position;
private bool searchTextChanging;
private bool showMenuAfterMainPreload;
private Menu? mainMenu;
public Menus()
{
keyboardInput = new(menus);
keyboardInput.RegisterHotKey();
keyboardInput = new();
if (!keyboardInput.RegisterHotKey(Settings.Default.HotKey))
{
Settings.Default.HotKey = string.Empty;
Settings.Default.Save();
}
keyboardInput.HotKeyPressed += () => SwitchOpenClose(false, false);
keyboardInput.ClosePressed += MenusFadeOut;
keyboardInput.RowDeselected += waitToOpenMenu.RowDeselected;
@ -73,9 +80,25 @@ namespace SystemTrayMenu.Business
waitToOpenMenu.StartLoadMenu += StartLoadMenu;
void StartLoadMenu(RowData rowData)
{
if (IsMainUsable &&
(menus[rowData.Level + 1] == null ||
menus[rowData.Level + 1]?.RowDataParent != rowData))
if (!IsMainUsable)
{
return;
}
Menu? menu = mainMenu?.SubMenu;
int nextLevel = rowData.Level + 1;
while (menu != null)
{
if (menu.Level == nextLevel)
{
break;
}
menu = menu.SubMenu;
}
// sanity check not creating same sub menu twice
if (menu?.RowDataParent != rowData)
{
Create(new(rowData), rowData.Path); // Level 1+ Sub Menu (loading)
@ -96,19 +119,8 @@ namespace SystemTrayMenu.Business
}
}
waitToOpenMenu.MouseEnterOk += (menu, itemData) => MouseEnterOk(menu, itemData);
waitToOpenMenu.CloseMenu += CloseMenu;
void CloseMenu(int level)
{
if (level < menus.Length)
{
Menu? menu = menus[level];
if (menu != null)
{
HideOldMenu(menu);
}
}
}
waitToOpenMenu.MouseEnterOk += MouseEnterOk;
waitToOpenMenu.CloseMenu += (menu) => HideOldMenu(menu);
if (Settings.Default.SupportGamepad)
{
@ -117,7 +129,7 @@ namespace SystemTrayMenu.Business
{
if (IsMainUsable)
{
Menu? menu = AsEnumerable.FirstOrDefault(m => m != null && (m.IsActive || m.IsKeyboardFocusWithin), MainMenu);
Menu? menu = GetActiveMenu(mainMenu) ?? mainMenu;
menu?.Dispatcher.Invoke(keyboardInput.CmdKeyProcessed, new object[] { menu, key, modifiers });
}
};
@ -129,7 +141,7 @@ namespace SystemTrayMenu.Business
void StillActiveTick()
{
timerStillActiveCheck.Stop();
if (!IsActive())
if (!IsActiveApp())
{
FadeHalfOrOutIfNeeded();
}
@ -189,13 +201,8 @@ namespace SystemTrayMenu.Business
Closing,
}
private Menu? MainMenu => menus[0];
private bool IsMainUsable => MainMenu?.IsUsable ?? false;
private IEnumerable<Menu> AsEnumerable => menus.Where(m => m != null && !m.IsClosed)!;
private List<Menu> AsList => AsEnumerable.ToList();
[MemberNotNullWhen(true, nameof(mainMenu))]
private bool IsMainUsable => mainMenu?.IsUsable ?? false;
public void Dispose()
{
@ -206,14 +213,6 @@ namespace SystemTrayMenu.Business
worker.Dispose();
}
waitToOpenMenu.Dispose();
keyboardInput.Dispose();
joystickHelper?.Dispose();
timerShowProcessStartedAsLoadingIcon.Stop();
timerStillActiveCheck.Stop();
waitLeave.Stop();
MainMenu?.Close();
foreach (FileSystemWatcher watcher in watchers)
{
watcher.Created -= WatcherProcessItem;
@ -222,6 +221,14 @@ namespace SystemTrayMenu.Business
watcher.Changed -= WatcherProcessItem;
watcher.Dispose();
}
waitToOpenMenu.Dispose();
keyboardInput.Dispose();
joystickHelper?.Dispose();
timerShowProcessStartedAsLoadingIcon.Stop();
timerStillActiveCheck.Stop();
waitLeave.Stop();
mainMenu?.Close();
}
internal static void OpenFolder(string? path = null)
@ -271,12 +278,12 @@ namespace SystemTrayMenu.Business
else
{
if (openCloseState == OpenCloseState.Opening ||
((MainMenu?.Visibility ?? Visibility.Collapsed) == Visibility.Visible && openCloseState == OpenCloseState.Default))
((mainMenu?.Visibility ?? Visibility.Collapsed) == Visibility.Visible && openCloseState == OpenCloseState.Default))
{
openCloseState = OpenCloseState.Closing;
MenusFadeOut();
StopWorker();
if (!AsEnumerable.Any(m => m.Visibility == Visibility.Visible))
if (IsVisibleAnyMenu(mainMenu) == null)
{
openCloseState = OpenCloseState.Default;
}
@ -313,6 +320,51 @@ namespace SystemTrayMenu.Business
}
}
private static Menu? IsVisibleAnyMenu(Menu? menu)
{
while (menu != null)
{
if (menu.Visibility == Visibility.Visible)
{
break;
}
menu = menu.SubMenu;
}
return menu;
}
private static Menu? IsMouseOverAnyMenu(Menu? menu)
{
while (menu != null)
{
if (menu.IsMouseOver())
{
break;
}
menu = menu.SubMenu;
}
return menu;
}
private static Menu? GetActiveMenu(Menu? menu)
{
while (menu != null)
{
if (menu.IsActive || menu.IsKeyboardFocusWithin)
{
break;
}
menu = menu.SubMenu;
}
return menu;
}
private static void LoadMenu(object? sender, DoWorkEventArgs eDoWork)
{
BackgroundWorker? workerSelf = sender as BackgroundWorker;
@ -338,7 +390,7 @@ namespace SystemTrayMenu.Business
if (e.Result == null)
{
Menu? menu = MainMenu;
Menu? menu = mainMenu;
if (menu != null)
{
// The main menu gets loaded again
@ -355,9 +407,9 @@ namespace SystemTrayMenu.Business
RefreshSelection(dgvMainMenu);
menu.RelocateOnNextShow = true;
}
AsEnumerable.ToList().ForEach(m => { m.ShowWithFade(); });
menu.ShowWithFade(false, true);
}
}
else
{
@ -368,17 +420,17 @@ namespace SystemTrayMenu.Business
case MenuDataDirectoryState.Valid:
if (IconReader.IsPreloading)
{
Create(menuData, Config.Path); // Level 0 Main Menu
Menu menu = Create(menuData, Config.Path); // Level 0 Main Menu
IconReader.IsPreloading = false;
if (showMenuAfterMainPreload)
{
MainMenu?.ShowWithFade();
menu.ShowWithFade(false, false);
}
}
else
{
AsEnumerable.ToList().ForEach(m => { m.ShowWithFade(); });
mainMenu?.ShowWithFade(false, true);
}
break;
@ -407,44 +459,48 @@ namespace SystemTrayMenu.Business
private void LoadSubMenuCompleted(object? senderCompleted, RunWorkerCompletedEventArgs e)
{
if (e.Result == null)
if (e.Result == null || !IsMainUsable)
{
return;
}
MenuData menuData = (MenuData)e.Result;
Menu? menu = menus[menuData.Level];
Menu? menu = mainMenu.SubMenu;
while (menu != null)
{
if (menu.Level == menuData.Level)
{
break;
}
menu = menu.SubMenu;
}
if (menu == null)
{
return;
}
if (IsMainUsable)
if (menuData.DirectoryState != MenuDataDirectoryState.Undefined)
{
if (menuData.DirectoryState != MenuDataDirectoryState.Undefined)
{
// Sub Menu (completed)
menu.AddItemsToMenu(menuData.RowDatas, menuData.DirectoryState, true);
AdjustMenusSizeAndLocation(menu.Level);
}
else
{
menu.HideWithFade();
menus[menu.Level] = null;
// Sub Menu (completed)
menu.AddItemsToMenu(menuData.RowDatas, menuData.DirectoryState, true);
AdjustMenusSizeAndLocation(menu.Level);
}
else
{
// TODO: Main menu should destroy sub menu(s?) when it becomes unusable
menu.HideWithFade(false);
ListView? lv = menus[menuData.Level - 1]?.GetDataGridView();
if (lv != null)
{
RefreshSelection(lv);
}
ListView? lv = menu.ParentMenu?.GetDataGridView();
if (lv != null)
{
RefreshSelection(lv);
}
}
}
private bool IsActive()
{
return menus.Where(m => m != null && (m.IsActive || m.IsKeyboardFocusWithin)).FirstOrDefault() != null || (App.TaskbarLogo?.IsActive ?? false);
}
private bool IsActiveApp() => GetActiveMenu(mainMenu) != null || (App.TaskbarLogo?.IsActive ?? false);
private Menu Create(MenuData menuData, string path)
{
@ -475,9 +531,9 @@ namespace SystemTrayMenu.Business
searchTextChanging = false;
// if any open menu close
if (!causedByWatcherUpdate && menu.Level + 1 < menus.Length)
if (!causedByWatcherUpdate)
{
Menu? menuToClose = menus[menu.Level + 1];
Menu? menuToClose = menu.SubMenu;
if (menuToClose != null)
{
HideOldMenu(menuToClose);
@ -486,11 +542,12 @@ namespace SystemTrayMenu.Business
}
menu.UserDragsMenu += Menu_UserDragsMenu;
void Menu_UserDragsMenu()
void Menu_UserDragsMenu(Menu mainMenu)
{
Menu? menu = menus[1];
Menu? menu = mainMenu.SubMenu;
if (menu != null)
{
// TODO: menus array not updated? Remove any way? (Call HideOldMenu within Menu_MouseDown direcly?)
HideOldMenu(menu);
}
}
@ -505,7 +562,7 @@ namespace SystemTrayMenu.Business
else if (!Settings.Default.StaysOpenWhenFocusLostAfterEnterPressed)
{
FadeHalfOrOutIfNeeded();
if (!IsActive())
if (!IsActiveApp())
{
deactivatedTime = DateTime.Now;
}
@ -516,10 +573,7 @@ namespace SystemTrayMenu.Business
void Activated()
{
// Bring transparent menus back
foreach (Menu? menu in menus.Where(m => m != null && m.Opacity != 1D))
{
menu!.ActivateWithFade();
}
mainMenu?.ActivateWithFade(true);
timerStillActiveCheck.Stop();
timerStillActiveCheck.Start();
@ -538,7 +592,7 @@ namespace SystemTrayMenu.Business
if (menu.Level == 0)
{
// Main Menu
menus[menu.Level] = menu;
mainMenu = menu;
menu.Loaded += (s, e) => ExecuteWatcherHistory();
}
else
@ -546,9 +600,10 @@ namespace SystemTrayMenu.Business
// Sub Menu (loading)
if (IsMainUsable)
{
HideOldMenu(menu, true);
menus[menu.Level] = menu;
menu.ShowWithFade(!IsActive());
RefreshSelection(menu.GetDataGridView());
// TODO: Re-enable again? HideOldMenu(menu, true);
menu.ShowWithFade(!IsActiveApp(), false);
}
}
@ -573,7 +628,7 @@ namespace SystemTrayMenu.Business
menu.Close();
}
if (!AsEnumerable.Any(m => m.Visibility == Visibility.Visible))
if (IsVisibleAnyMenu(mainMenu) == null)
{
IconReader.ClearCacheWhenLimitReached();
@ -638,7 +693,7 @@ namespace SystemTrayMenu.Business
{
if (IsMainUsable)
{
Menu? menu = MainMenu;
Menu? menu = mainMenu;
if (menu != null)
{
menu.RelocateOnNextShow = true;
@ -649,7 +704,7 @@ namespace SystemTrayMenu.Business
private void HideOldMenu(Menu menuToShow, bool keepOrSetIsMenuOpen = false)
{
Menu? menuPrevious = menus[menuToShow.Level - 1];
Menu? menuPrevious = menuToShow.ParentMenu;
if (menuPrevious != null)
{
// Clean up menu status IsMenuOpen for previous one
@ -669,13 +724,7 @@ namespace SystemTrayMenu.Business
RefreshSelection(dgvPrevious);
// Hide old menu
foreach (Menu? menuToClose in menus.Where(
m => m != null && m.Level > menuPrevious.Level))
{
menuToClose!.HideWithFade();
menus[menuToClose.Level] = null;
}
menuPrevious.SubMenu?.HideWithFade(true);
}
}
@ -683,19 +732,18 @@ namespace SystemTrayMenu.Business
{
if (IsMainUsable)
{
if (!IsActive())
if (!IsActiveApp())
{
if (Settings.Default.StaysOpenWhenFocusLost &&
AsList.Any(m => m.IsMouseOn()))
if (Settings.Default.StaysOpenWhenFocusLost && IsMouseOverAnyMenu(mainMenu) != null)
{
if (!keyboardInput.InUse)
{
AsList.ForEach(menu => menu.ShowWithFade(true));
mainMenu?.ShowWithFade(true, true);
}
}
else if (Config.AlwaysOpenByPin)
{
AsList.ForEach(menu => menu.ShowWithFade(true));
mainMenu?.ShowWithFade(true, true);
}
else
{
@ -708,15 +756,8 @@ namespace SystemTrayMenu.Business
private void MenusFadeOut()
{
openCloseState = OpenCloseState.Closing;
AsList.ForEach(menu =>
{
if (menu.Level > 0)
{
menus[menu.Level] = null;
}
menu.HideWithFade();
});
mainMenu?.HideWithFade(true);
Config.AlwaysOpenByPin = false;
}
@ -744,9 +785,8 @@ namespace SystemTrayMenu.Business
}
// Only apply taskbar position change when no menu is currently open
List<Menu> list = AsList;
WindowsTaskbar taskbar = new();
if (list.Count == 1)
if (IsMainUsable && mainMenu.SubMenu == null)
{
taskbarPosition = taskbar.Position;
}
@ -785,14 +825,12 @@ namespace SystemTrayMenu.Business
{
GetScreenBounds(out Rect screenBounds, out bool useCustomLocation, out StartLocation startLocation);
Menu menu;
Menu? menu = mainMenu;
Menu? menuPredecessor = null;
List<Menu> list = AsList;
for (int i = 0; i < list.Count; i++)
{
menu = list[i];
if (startLevel <= i)
while (menu != null)
{
if (startLevel <= menu.Level)
{
menu.AdjustSizeAndLocation(screenBounds, menuPredecessor, startLocation, useCustomLocation);
}
@ -805,7 +843,7 @@ namespace SystemTrayMenu.Business
if (!Settings.Default.AppearAtTheBottomLeft &&
!Settings.Default.AppearAtMouseLocation &&
!Settings.Default.UseCustomLocation &&
i == 0)
menu.Level == 0)
{
const double overlapTolerance = 4D;
@ -819,6 +857,7 @@ namespace SystemTrayMenu.Business
}
menuPredecessor = menu;
menu = menu.SubMenu;
}
}
@ -834,18 +873,10 @@ namespace SystemTrayMenu.Business
private void WatcherProcessItem(object sender, EventArgs e)
{
Menu? menu = MainMenu;
bool useHistory = false;
if (menu == null)
{
useHistory = true;
}
else
{
menu.Dispatcher.Invoke(() => useHistory = !menu.IsLoaded);
}
Menu? menu = mainMenu;
if (useHistory)
// Store event in history as long as menu is not loaded
if (menu?.Dispatcher.Invoke(() => !menu.IsLoaded) ?? true)
{
watcherHistory.Add(e);
return;
@ -853,71 +884,67 @@ namespace SystemTrayMenu.Business
if (e is RenamedEventArgs renamedEventArgs)
{
MainMenu?.Dispatcher.Invoke(() => RenameItem(renamedEventArgs));
menu.Dispatcher.Invoke(() => RenameItem(menu, renamedEventArgs));
}
else if (e is FileSystemEventArgs fileSystemEventArgs)
{
if (fileSystemEventArgs.ChangeType == WatcherChangeTypes.Deleted)
{
MainMenu?.Dispatcher.Invoke(() => DeleteItem(fileSystemEventArgs));
menu.Dispatcher.Invoke(() => DeleteItem(menu, fileSystemEventArgs));
}
else if (fileSystemEventArgs.ChangeType == WatcherChangeTypes.Created)
{
MainMenu?.Dispatcher.Invoke(() => CreateItem(fileSystemEventArgs));
menu.Dispatcher.Invoke(() => CreateItem(menu, fileSystemEventArgs));
}
}
}
private void RenameItem(RenamedEventArgs e)
private void RenameItem(Menu menu, RenamedEventArgs e)
{
try
{
List<RowData> rowDatas = new();
ListView? dgv = MainMenu?.GetDataGridView();
if (dgv != null)
foreach (ListViewItemData item in menu.GetDataGridView().Items)
{
foreach (ListViewItemData item in dgv.Items)
RowData rowData = item.data;
if (rowData.Path.StartsWith($"{e.OldFullPath}"))
{
RowData rowData = item.data;
if (rowData.Path.StartsWith($"{e.OldFullPath}"))
string path = rowData.Path.Replace(e.OldFullPath, e.FullPath);
FileAttributes attr = File.GetAttributes(path);
bool isFolder = (attr & FileAttributes.Directory) == FileAttributes.Directory;
if (isFolder)
{
string path = rowData.Path.Replace(e.OldFullPath, e.FullPath);
FileAttributes attr = File.GetAttributes(path);
bool isFolder = (attr & FileAttributes.Directory) == FileAttributes.Directory;
if (isFolder)
{
string? dirpath = Path.GetDirectoryName(path);
if (string.IsNullOrEmpty(dirpath))
{
continue;
}
path = dirpath;
}
RowData rowDataRenamed = new(isFolder, rowData.IsAdditionalItem, 0, path);
FolderOptions.ReadHiddenAttributes(rowDataRenamed.Path, out bool hasHiddenFlag, out bool isDirectoryToHide);
if (isDirectoryToHide)
string? dirpath = Path.GetDirectoryName(path);
if (string.IsNullOrEmpty(dirpath))
{
continue;
}
IconReader.RemoveIconFromCache(rowData.Path);
rowDataRenamed.HiddenEntry = hasHiddenFlag;
rowDataRenamed.ReadIcon(true);
rowDatas.Add(rowDataRenamed);
path = dirpath;
}
else
RowData rowDataRenamed = new(isFolder, rowData.IsAdditionalItem, 0, path);
FolderOptions.ReadHiddenAttributes(rowDataRenamed.Path, out bool hasHiddenFlag, out bool isDirectoryToHide);
if (isDirectoryToHide)
{
rowDatas.Add(rowData);
continue;
}
IconReader.RemoveIconFromCache(rowData.Path);
rowDataRenamed.HiddenEntry = hasHiddenFlag;
rowDataRenamed.ReadIcon(true);
rowDatas.Add(rowDataRenamed);
}
else
{
rowDatas.Add(rowData);
}
}
rowDatas = DirectoryHelpers.SortItems(rowDatas);
keyboardInput.ClearIsSelectedByKey();
MainMenu?.AddItemsToMenu(rowDatas, null, true);
MainMenu?.OnWatcherUpdate();
menu.AddItemsToMenu(rowDatas, null, true);
menu.OnWatcherUpdate();
}
catch (Exception ex)
{
@ -925,34 +952,31 @@ namespace SystemTrayMenu.Business
}
}
private void DeleteItem(FileSystemEventArgs e)
private void DeleteItem(Menu menu, FileSystemEventArgs e)
{
try
{
ListView? dgv = MainMenu?.GetDataGridView();
if (dgv != null)
ListView? dgv = menu.GetDataGridView();
List<ListViewItemData> rowsToRemove = new();
foreach (ListViewItemData item in dgv.ItemsSource)
{
List<ListViewItemData> rowsToRemove = new();
foreach (ListViewItemData item in dgv.ItemsSource)
RowData rowData = item.data;
if (rowData.Path == e.FullPath ||
rowData.Path.StartsWith($"{e.FullPath}\\"))
{
RowData rowData = item.data;
if (rowData.Path == e.FullPath ||
rowData.Path.StartsWith($"{e.FullPath}\\"))
{
IconReader.RemoveIconFromCache(rowData.Path);
rowsToRemove.Add(item);
}
}
foreach (ListViewItemData rowToRemove in rowsToRemove)
{
((IEditableCollectionView)dgv.Items).Remove(rowToRemove);
IconReader.RemoveIconFromCache(rowData.Path);
rowsToRemove.Add(item);
}
}
foreach (ListViewItemData rowToRemove in rowsToRemove)
{
((IEditableCollectionView)dgv.Items).Remove(rowToRemove);
}
keyboardInput.ClearIsSelectedByKey();
MainMenu?.OnWatcherUpdate();
menu.OnWatcherUpdate();
}
catch (Exception ex)
{
@ -960,7 +984,7 @@ namespace SystemTrayMenu.Business
}
}
private void CreateItem(FileSystemEventArgs e)
private void CreateItem(Menu menu, FileSystemEventArgs e)
{
try
{
@ -977,24 +1001,17 @@ namespace SystemTrayMenu.Business
rowData.HiddenEntry = hasHiddenFlag;
rowData.ReadIcon(true);
List<RowData> rowDatas = new()
var items = menu.GetDataGridView().Items;
List<RowData> rowDatas = new(items.Count + 1) { rowData };
foreach (ListViewItemData item in items)
{
rowData,
};
ListView? dgv = MainMenu?.GetDataGridView();
if (dgv != null)
{
foreach (ListViewItemData item in dgv.Items)
{
rowDatas.Add(item.data);
}
rowDatas.Add(item.data);
}
rowDatas = DirectoryHelpers.SortItems(rowDatas);
keyboardInput.ClearIsSelectedByKey();
MainMenu?.AddItemsToMenu(rowDatas, null, true);
MainMenu?.OnWatcherUpdate();
menu.AddItemsToMenu(rowDatas, null, true);
menu.OnWatcherUpdate();
}
catch (Exception ex)
{

View file

@ -33,13 +33,11 @@ namespace SystemTrayMenu
Scaling.Initialize();
FolderOptions.Initialize();
using (App app = new App())
{
app.InitializeComponent();
isStartup = false;
Log.WriteApplicationRuns();
app.Run();
}
using App app = new ();
app.InitializeComponent();
isStartup = false;
Log.WriteApplicationRuns();
app.Run();
}
}
catch (Exception ex)

View file

@ -28,7 +28,7 @@ namespace SystemTrayMenu.Handler
internal event Action<RowData>? StartLoadMenu;
internal event Action<int>? CloseMenu;
internal event Action<Menu>? CloseMenu;
internal event Action? StopLoadMenu;
@ -129,10 +129,13 @@ namespace SystemTrayMenu.Handler
menu?.Activate();
menu?.FocusTextBox();
CloseMenu?.Invoke(rowData.Level + 1);
Menu? menuToClose = menu?.SubMenu;
if (menuToClose != null)
{
CloseMenu?.Invoke(menuToClose);
}
if (rowData.IsPointingToFolder &&
rowData.Level + 1 < MenuDefines.MenusMax)
if (rowData.IsPointingToFolder)
{
StartLoadMenu?.Invoke(rowData);
}

View file

@ -110,7 +110,7 @@ namespace SystemTrayMenu.DataClasses
internal Menu? SubMenu { get; set; }
internal bool IsMenuOpen { get; set; }
internal bool IsMenuOpen { get; set; } // TODO: Implicitly set when SubMenu != null?
internal bool IsClicking { get; set; }

View file

@ -92,6 +92,7 @@ namespace SystemTrayMenu.UserInterface
{
// This will be a main menu
Level = 0;
MainMenu = this;
// Use Main Menu DPI for all further calculations
Scaling.CalculateFactorByDpi(this);
@ -99,8 +100,16 @@ namespace SystemTrayMenu.UserInterface
else
{
// This will be a sub menu
if (ParentMenu == null)
{
// Should never happen as each parent menu must have a valid entry which's owner is set
throw new ArgumentNullException(new (nameof(ParentMenu)));
}
Level = RowDataParent.Level + 1;
MainMenu = ParentMenu.MainMenu;
RowDataParent.SubMenu = this;
RowDataParent.IsMenuOpen = true;
buttonOpenFolder.Visibility = Visibility.Collapsed;
buttonSettings.Visibility = Visibility.Collapsed;
@ -203,6 +212,7 @@ namespace SystemTrayMenu.UserInterface
if (RowDataParent?.SubMenu == this)
{
RowDataParent.SubMenu = null;
RowDataParent.IsMenuOpen = false;
}
foreach (ListViewItemData item in dgv.Items)
@ -224,7 +234,7 @@ namespace SystemTrayMenu.UserInterface
internal event Action<Menu, bool, bool>? SearchTextChanged;
internal event Action? UserDragsMenu;
internal event Action<Menu>? UserDragsMenu;
internal event Action<Menu, ListViewItemData>? CellMouseEnter;
@ -269,6 +279,8 @@ namespace SystemTrayMenu.UserInterface
internal RowData? RowDataParent { get; set; }
internal Menu MainMenu { get; init; }
internal Menu? ParentMenu => RowDataParent?.Owner;
internal Menu? SubMenu
@ -364,13 +376,14 @@ namespace SystemTrayMenu.UserInterface
}
}
internal bool IsMouseOn()
// TODO: Check if we can just use original IsMouseOver instead? (Check if it requires Mouse.Capture(this))
internal new bool IsMouseOver()
{
Point mousePos = NativeMethods.Screen.CursorPosition;
bool isMouseOn = Visibility == Visibility.Visible &&
bool isMouseOver = Visibility == Visibility.Visible &&
mousePos.X >= 0 && mousePos.X < Width &&
mousePos.Y >= 0 && mousePos.Y < Height;
return isMouseOn;
return isMouseOver;
}
internal ListView GetDataGridView() => dgv; // TODO WPF Replace Forms wrapper
@ -427,24 +440,37 @@ namespace SystemTrayMenu.UserInterface
}
}
internal void ActivateWithFade()
internal void ActivateWithFade(bool recursive)
{
if (Settings.Default.UseFading)
if (recursive)
{
isFading = true;
RaiseEvent(new(routedEvent: FadeInEvent));
SubMenu?.ActivateWithFade(true);
}
else
if (Opacity != 1D)
{
Opacity = 1D;
FadeIn_Completed(this, new());
if (Settings.Default.UseFading)
{
isFading = true;
RaiseEvent(new(routedEvent: FadeInEvent));
}
else
{
Opacity = 1D;
FadeIn_Completed(this, new());
}
}
}
internal void ShowWithFade(bool transparency = false)
internal void ShowWithFade(bool transparency, bool recursive)
{
timerUpdateIcons.Start();
if (recursive)
{
SubMenu?.ShowWithFade(transparency, true);
}
if (Level > 0)
{
ShowActivated = false;
@ -472,8 +498,18 @@ namespace SystemTrayMenu.UserInterface
}
}
internal void HideWithFade()
internal void HideWithFade(bool recursive)
{
if (recursive)
{
SubMenu?.HideWithFade(true);
}
if (RowDataParent != null)
{
RowDataParent.SubMenu = null;
}
if (Settings.Default.UseFading)
{
isFading = true;
@ -1108,7 +1144,7 @@ namespace SystemTrayMenu.UserInterface
{
mouseDown = true;
lastLocation = NativeMethods.Screen.CursorPosition;
UserDragsMenu?.Invoke();
UserDragsMenu?.Invoke(this);
Mouse.Capture(this);
}
}

View file

@ -11,7 +11,7 @@ namespace SystemTrayMenu.Utilities
{
public class NativeWindow : HwndSource
{
private HwndSourceHook hook;
private readonly HwndSourceHook hook;
public NativeWindow()
: base(new())