Simplified menu creation

This commit is contained in:
Peter Kirmeier 2022-12-03 21:23:19 +01:00
parent 389ae28b99
commit ece80ebc3f
7 changed files with 151 additions and 212 deletions

View file

@ -90,15 +90,14 @@ namespace SystemTrayMenu.Business
{
// First time the main menu gets loaded
MenuData menuData = (MenuData)e.Result;
switch (menuData.Validity)
switch (menuData.DirectoryState)
{
case MenuDataValidity.Valid:
case MenuDataDirectoryState.Valid:
if (IconReader.MainPreload)
{
workerMainMenu.DoWork -= LoadMenu;
menus[0] = Create(menuData, new DirectoryInfo(Config.Path).Name);
menus[0].HandleCreated += (s, e) => ExecuteWatcherHistory();
Scaling.CalculateFactorByDpi(menus[0]);
menus[0] = Create(menuData, new DirectoryInfo(Config.Path).Name, Config.Path);
menus[0].Loaded += (s, e) => ExecuteWatcherHistory();
IconReader.MainPreload = false;
if (showMenuAfterMainPreload)
@ -112,20 +111,20 @@ namespace SystemTrayMenu.Business
}
break;
case MenuDataValidity.Empty:
case MenuDataDirectoryState.Empty:
MessageBox.Show(Translator.GetText("Your root directory for the app does not exist or is empty! Change the root directory or put some files, directories or shortcuts into the root directory."));
OpenFolder();
Config.SetFolderByUser();
AppRestart.ByConfigChange();
break;
case MenuDataValidity.NoAccess:
case MenuDataDirectoryState.NoAccess:
MessageBox.Show(Translator.GetText("You have no access to the root directory of the app. Grant access to the directory or change the root directory."));
OpenFolder();
Config.SetFolderByUser();
AppRestart.ByConfigChange();
break;
case MenuDataValidity.Undefined:
Log.Info($"{nameof(MenuDataValidity)}.{nameof(MenuDataValidity.Undefined)}");
case MenuDataDirectoryState.Undefined:
Log.Info($"{nameof(MenuDataDirectoryState)}.{nameof(MenuDataDirectoryState.Undefined)}");
break;
default:
break;
@ -159,15 +158,15 @@ namespace SystemTrayMenu.Business
{
MenuData menuDataLoading = new(rowData.Level + 1)
{
Validity = MenuDataValidity.Valid,
DirectoryState = MenuDataDirectoryState.Valid,
};
Menu menuLoading = Create(menuDataLoading, new DirectoryInfo(rowData.Path).Name);
menuLoading.IsLoadingMenu = true;
// TODO: Check: Is Config.Path as path parameter correct? (topeterk: should be rowData.Path?)
Menu menuLoading = Create(menuDataLoading, new DirectoryInfo(rowData.Path).Name, Config.Path);
menus[rowData.Level + 1] = menuLoading;
menuLoading.Tag = menuDataLoading.RowDataParent = rowData;
menuDataLoading.RowDataParent.SubMenu = menuLoading;
menuLoading.SetTypeLoading();
menuLoading.SetBehavior(MenuDataDirectoryState.Undefined);
ShowSubMenu(menuLoading);
}
@ -202,22 +201,11 @@ namespace SystemTrayMenu.Business
closedLoadingMenu = true;
}
if (menuData.Validity != MenuDataValidity.Undefined &&
if (menuData.DirectoryState != MenuDataDirectoryState.Undefined &&
menus[0].IsUsable)
{
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 menu = Create(menuData, new DirectoryInfo(menuData.RowDataParent.ResolvedPath).Name, menuData.RowDataParent.ResolvedPath);
menu.SetBehavior(menuData.DirectoryState);
menu.Tag = menuData.RowDataParent;
menuData.RowDataParent.SubMenu = menu;
@ -598,22 +586,11 @@ namespace SystemTrayMenu.Business
return (App.TaskbarLogo != null && App.TaskbarLogo.IsActive) || IsShellContextMenuOpen();
}
private Menu Create(MenuData menuData, string title = null)
private Menu Create(MenuData menuData, string title, string path)
{
Menu menu = new();
Menu menu = new(title, menuData.Level, menuData.DirectoryState);
string path = Config.Path;
if (title == null)
{
title = new DirectoryInfo(menuData.RowDataParent.ResolvedPath).Name;
path = menuData.RowDataParent.ResolvedPath;
}
title ??= Path.GetPathRoot(path);
menu.AdjustControls(title, menuData.Validity);
menu.UserClickedOpenFolder += () => OpenFolder(path);
menu.Level = menuData.Level;
menu.MenuScrolled += AdjustMenusSizeAndLocation; // TODO: Only update vertical location while scrolling?
#if TODO // Misc MouseEvents
menu.MouseLeave += waitLeave.Start;
@ -701,7 +678,7 @@ namespace SystemTrayMenu.Business
if (menu.Level == 0)
{
menu.SetType(Menu.MenuType.Main);
menu.SetBehavior(MenuDataDirectoryState.Valid);
menu.ResetSearchText();
menu.ResetHeight();
}
@ -1229,7 +1206,7 @@ namespace SystemTrayMenu.Business
private void WatcherProcessItem(object sender, EventArgs e)
{
if (menus[0] == null || !menus[0].IsHandleCreated)
if (menus[0] == null || !menus[0].IsLoaded)
{
watcherHistory.Add(e);
return;

View file

@ -84,22 +84,22 @@ namespace SystemTrayMenu.Business
internal static void CheckIfValid(ref MenuData menuData)
{
if (menuData.Validity == MenuDataValidity.Undefined)
if (menuData.DirectoryState == MenuDataDirectoryState.Undefined)
{
if (menuData.RowDatas.Count == 0)
{
menuData.Validity = MenuDataValidity.Empty;
menuData.DirectoryState = MenuDataDirectoryState.Empty;
}
else
{
menuData.Validity = MenuDataValidity.Valid;
menuData.DirectoryState = MenuDataDirectoryState.Valid;
}
}
}
internal static void SortItemsWhenValid(ref MenuData menuData)
{
if (menuData.Validity != MenuDataValidity.Valid)
if (menuData.DirectoryState != MenuDataDirectoryState.Valid)
{
return;
}
@ -181,7 +181,7 @@ namespace SystemTrayMenu.Business
Log.Warn($"path:'{path}'", ex);
if (ex is UnauthorizedAccessException)
{
menuData.Validity = MenuDataValidity.NoAccess;
menuData.DirectoryState = MenuDataDirectoryState.NoAccess;
}
}
}
@ -205,7 +205,7 @@ namespace SystemTrayMenu.Business
Log.Warn($"path:'{path}'", ex);
if (ex is UnauthorizedAccessException)
{
menuData.Validity = MenuDataValidity.NoAccess;
menuData.DirectoryState = MenuDataDirectoryState.NoAccess;
}
}
}
@ -229,7 +229,7 @@ namespace SystemTrayMenu.Business
Log.Warn($"path:'{path}'", ex);
if (ex is UnauthorizedAccessException)
{
menuData.Validity = MenuDataValidity.NoAccess;
menuData.DirectoryState = MenuDataDirectoryState.NoAccess;
}
}
}

View file

@ -6,11 +6,26 @@ namespace SystemTrayMenu.DataClasses
{
using System.Collections.Generic;
internal enum MenuDataValidity
{
Undefined,
Valid,
Empty,
internal enum MenuDataDirectoryState
{
/// <summary>
/// State not defined or data still loading
/// </summary>
Undefined,
/// <summary>
/// Data is available
/// </summary>
Valid,
/// <summary>
/// Loading finished but no data available
/// </summary>
Empty,
/// <summary>
/// Loading failed, so no data available
/// </summary>
NoAccess,
}
@ -19,7 +34,7 @@ namespace SystemTrayMenu.DataClasses
public MenuData(int level)
{
RowDatas = new List<RowData>();
Validity = MenuDataValidity.Undefined;
DirectoryState = MenuDataDirectoryState.Undefined;
Level = level;
RowDataParent = null;
IsNetworkRoot = false;
@ -27,7 +42,7 @@ namespace SystemTrayMenu.DataClasses
internal List<RowData> RowDatas { get; set; }
internal MenuDataValidity Validity { get; set; }
internal MenuDataDirectoryState DirectoryState { get; set; }
internal int Level { get; }

View file

@ -4,13 +4,19 @@
namespace SystemTrayMenu.Helper
{
using System.Collections.Generic;
using System.Collections.Generic;
using SystemTrayMenu.DllImports;
internal class WindowsExplorerSort : IComparer<string>
{
public int Compare(string x, string y)
{
return DllImports.NativeMethods.ShlwapiStrCmpLogicalW(x, y);
public int Compare(string? x, string? y)
{
if (x != null && y != null)
{
return NativeMethods.ShlwapiStrCmpLogicalW(x, y);
}
return 0;
}
}
}

View file

@ -4,8 +4,9 @@
namespace SystemTrayMenu.DllImports
{
using System.Runtime.InteropServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
/// <summary>
/// wraps the methodcalls to native windows dll's.
/// </summary>
@ -14,8 +15,9 @@ namespace SystemTrayMenu.DllImports
public static int ShlwapiStrCmpLogicalW(string x, string y)
{
return StrCmpLogicalW(x, y);
}
}
[SupportedOSPlatform("windows")]
[DllImport("shlwapi.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Unicode)]
[DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
private static extern int StrCmpLogicalW(string x, string y);

View file

@ -107,7 +107,7 @@
</DockPanel>
<DockPanel x:Name="tableLayoutPanelBottom" DockPanel.Dock="Bottom" Margin="12,4">
<Label x:Name="labelItems" Content="0 items" Padding="0" DockPanel.Dock="Left" FontWeight="Bold" VerticalAlignment="Center" Margin="0,0,10,0"/>
<Label x:Name="labelStatus" Content="0 items" Padding="0" DockPanel.Dock="Left" FontWeight="Bold" VerticalAlignment="Center" Margin="0,0,10,0"/>
<Image x:Name="pictureBoxLoading" Width="18" Height="18" DockPanel.Dock="Right"
HorizontalAlignment="Right" RenderTransformOrigin="0.5,0.5" d:Visibility="Visible" Visibility="Collapsed">

View file

@ -7,7 +7,6 @@ namespace SystemTrayMenu.UserInterface
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
@ -42,7 +41,7 @@ namespace SystemTrayMenu.UserInterface
private bool isClosed = false; // TODO WPF Replace Forms wrapper
private DispatcherTimer timerUpdateIcons = new DispatcherTimer(DispatcherPriority.Render, Dispatcher.CurrentDispatcher);
internal Menu()
internal Menu(string title, int level, MenuDataDirectoryState directoryState)
{
timerUpdateIcons.Tick += TimerUpdateIcons_Tick;
Closed += (_, _) =>
@ -99,10 +98,19 @@ namespace SystemTrayMenu.UserInterface
InitializeComponent();
Assembly myassembly = Assembly.GetExecutingAssembly();
string myname = myassembly.GetName().Name ?? string.Empty;
Level = level;
if (Level == 0)
{
// Use Main Menu DPI for all further calculations
Scaling.CalculateFactorByDpi(this);
}
txtTitle.Text = myname;
if (title.Length > MenuDefines.LengthMax)
{
title = $"{title[..MenuDefines.LengthMax]}...";
}
txtTitle.Text = title;
foreach (FrameworkElement control in
new List<FrameworkElement>()
@ -125,7 +133,7 @@ namespace SystemTrayMenu.UserInterface
labelTitle.FontSize = Scaling.ScaleFontByPoints(8.25F);
textBoxSearch.FontSize = Scaling.ScaleFontByPoints(8.25F);
labelItems.FontSize = Scaling.ScaleFontByPoints(7F);
labelStatus.FontSize = Scaling.ScaleFontByPoints(7F);
dgv.FontSize = Scaling.ScaleFontByPoints(9F);
MouseDown += Menu_MouseDown;
@ -148,7 +156,7 @@ namespace SystemTrayMenu.UserInterface
labelTitle.Foreground = foreColor;
textBoxSearch.Foreground = foreColor;
dgv.Foreground = foreColor;
labelItems.Foreground = MenuDefines.ColorIcons.ToSolidColorBrush();
labelStatus.Foreground = MenuDefines.ColorIcons.ToSolidColorBrush();
windowFrame.BorderBrush = backgroundBorder;
windowFrame.Background = backColor;
@ -168,7 +176,7 @@ namespace SystemTrayMenu.UserInterface
tableLayoutPanelMenu.MouseEnter += ControlsMouseEnter;
tableLayoutPanelDgvAndScrollbar.MouseEnter += ControlsMouseEnter;
tableLayoutPanelBottom.MouseEnter += ControlsMouseEnter;
labelItems.MouseEnter += ControlsMouseEnter;
labelStatus.MouseEnter += ControlsMouseEnter;
void ControlsMouseEnter(object sender, EventArgs e)
{
MouseEnter?.Invoke();
@ -185,7 +193,7 @@ namespace SystemTrayMenu.UserInterface
tableLayoutPanelMenu.MouseLeave += ControlsMouseLeave;
tableLayoutPanelDgvAndScrollbar.MouseLeave += ControlsMouseLeave;
tableLayoutPanelBottom.MouseLeave += ControlsMouseLeave;
labelItems.MouseLeave += ControlsMouseLeave;
labelStatus.MouseLeave += ControlsMouseLeave;
void ControlsMouseLeave(object sender, EventArgs e)
{
MouseLeave?.Invoke();
@ -205,10 +213,6 @@ namespace SystemTrayMenu.UserInterface
Loaded += (sender, e) =>
{
NativeMethods.HideFromAltTab(this);
// TODO WPF Replace Forms wrapper
IsHandleCreated = true;
HandleCreated?.Invoke(sender, e);
};
Closed += (sender, e) =>
@ -220,7 +224,7 @@ namespace SystemTrayMenu.UserInterface
};
}
internal new event Action MenuScrolled;
internal event Action MenuScrolled;
#if TODO // Misc MouseEvents
internal new event Action MouseEnter;
@ -244,8 +248,6 @@ namespace SystemTrayMenu.UserInterface
internal event Action? UserDragsMenu;
internal event RoutedEventHandler? HandleCreated; // TODO WPF Replace Forms wrapper
internal event Action<ListView, int>? CellMouseEnter;
internal event Action<ListView, int>? CellMouseLeave;
@ -256,39 +258,6 @@ namespace SystemTrayMenu.UserInterface
internal event Action<ListView, int, MouseButtonEventArgs>? CellMouseClick;
internal enum MenuType
{
/// <summary>
/// Root menu
/// </summary>
Main,
/// <summary>
/// Sub menu
/// </summary>
Sub,
/// <summary>
/// Sub menu with no content
/// </summary>
Empty,
/// <summary>
/// Sub menu with no access
/// </summary>
NoAccess,
/// <summary>
/// TODO: Not used - remove?
/// </summary>
MaxReached,
/// <summary>
/// Sub menu but with yet unknown content
/// </summary>
Loading,
}
internal enum StartLocation
{
Predecessor,
@ -297,8 +266,6 @@ namespace SystemTrayMenu.UserInterface
TopRight,
}
public bool IsHandleCreated { get; internal set; } // TODO State out of window
public bool IsLoadingMenu { get; internal set; } // TODO State out of window
public bool IsDisposed => isClosed; // TODO WPF Replace Forms wrapper
@ -353,64 +320,84 @@ namespace SystemTrayMenu.UserInterface
#endif
}
internal void SetTypeSub()
internal void SetBehavior(MenuDataDirectoryState state)
{
SetType(MenuType.Sub);
}
if (!Config.ShowDirectoryTitleAtTop)
{
txtTitle.Visibility = Visibility.Hidden;
}
internal void SetTypeEmpty()
{
SetType(MenuType.Empty);
}
if (!Config.ShowSearchBar)
{
searchPanel.Visibility = Visibility.Collapsed;
}
internal void SetTypeNoAccess()
{
SetType(MenuType.NoAccess);
}
if (!(Config.ShowCountOfElementsBelow || state != MenuDataDirectoryState.Valid))
{
// Hide status when neither config is set nor an error message must be shown
labelStatus.Visibility = Visibility.Collapsed;
}
internal void SetTypeLoading()
{
SetType(MenuType.Loading);
}
if (!Config.ShowFunctionKeyOpenFolder)
{
buttonOpenFolder.Visibility = Visibility.Collapsed;
}
internal void SetType(MenuType type)
{
if (type != MenuType.Main)
if (!Config.ShowFunctionKeyPinMenu)
{
buttonMenuAlwaysOpen.Visibility = Visibility.Collapsed;
}
if (!Config.ShowFunctionKeySettings)
{
buttonSettings.Visibility = Visibility.Collapsed;
}
if (!Config.ShowFunctionKeyRestart)
{
buttonRestart.Visibility = Visibility.Collapsed;
}
switch (type)
if (Level == 0)
{
case MenuType.Main:
textBoxSearch.TextChanged += (_, _) => TextBoxSearch_TextChanged();
break;
case MenuType.Sub:
textBoxSearch.TextChanged += (_, _) => TextBoxSearch_TextChanged();
buttonMenuAlwaysOpen.Visibility = Visibility.Collapsed;
break;
case MenuType.Empty:
searchPanel.Visibility = Visibility.Collapsed;
labelItems.Content = Translator.GetText("Directory empty");
buttonMenuAlwaysOpen.Visibility = Visibility.Collapsed;
break;
case MenuType.NoAccess:
searchPanel.Visibility = Visibility.Collapsed;
labelItems.Content = Translator.GetText("Directory inaccessible");
buttonMenuAlwaysOpen.Visibility = Visibility.Collapsed;
break;
case MenuType.Loading:
labelItems.Content = Translator.GetText("loading");
buttonMenuAlwaysOpen.Visibility = Visibility.Visible;
buttonOpenFolder.Visibility = Visibility.Collapsed;
// Main Menu
textBoxSearch.TextChanged += (_, _) => TextBoxSearch_TextChanged();
}
else
{
// SubMenu
buttonSettings.Visibility = Visibility.Collapsed;
buttonRestart.Visibility = Visibility.Collapsed;
// Todo: use embedded resources that we can assign image in XAML already
pictureBoxLoading.Source = SystemTrayMenu.Resources.StaticResources.LoadingIcon.ToImageSource();
pictureBoxLoading.Visibility = Visibility.Visible;
break;
default:
break;
switch (state)
{
case MenuDataDirectoryState.Valid:
textBoxSearch.TextChanged += (_, _) => TextBoxSearch_TextChanged();
buttonMenuAlwaysOpen.Visibility = Visibility.Collapsed;
break;
case MenuDataDirectoryState.Empty:
searchPanel.Visibility = Visibility.Collapsed;
labelStatus.Content = Translator.GetText("Directory empty");
buttonMenuAlwaysOpen.Visibility = Visibility.Collapsed;
break;
case MenuDataDirectoryState.NoAccess:
searchPanel.Visibility = Visibility.Collapsed;
labelStatus.Content = Translator.GetText("Directory inaccessible");
buttonMenuAlwaysOpen.Visibility = Visibility.Collapsed;
break;
case MenuDataDirectoryState.Undefined:
IsLoadingMenu = true;
labelStatus.Content = Translator.GetText("loading");
buttonMenuAlwaysOpen.Visibility = Visibility.Visible;
buttonOpenFolder.Visibility = Visibility.Collapsed;
// Todo: use embedded resources that we can assign image in XAML already
pictureBoxLoading.Source = SystemTrayMenu.Resources.StaticResources.LoadingIcon.ToImageSource();
pictureBoxLoading.Visibility = Visibility.Visible;
break;
default:
break;
}
}
}
@ -444,54 +431,6 @@ namespace SystemTrayMenu.UserInterface
return dgv;
}
internal void AdjustControls(string title, MenuDataValidity menuDataValidity)
{
if (!string.IsNullOrEmpty(title) && Config.ShowDirectoryTitleAtTop)
{
if (title.Length > MenuDefines.LengthMax)
{
title = $"{title[..MenuDefines.LengthMax]}...";
}
txtTitle.Text = title;
}
else
{
txtTitle.Text = string.Empty;
}
if (!Config.ShowSearchBar)
{
searchPanel.Visibility = Visibility.Collapsed;
}
if (!Config.ShowCountOfElementsBelow &&
menuDataValidity == MenuDataValidity.Valid)
{
labelItems.Visibility = Visibility.Collapsed;
}
if (!Config.ShowFunctionKeyOpenFolder)
{
buttonOpenFolder.Visibility = Visibility.Collapsed;
}
if (!Config.ShowFunctionKeyPinMenu)
{
buttonMenuAlwaysOpen.Visibility = Visibility.Collapsed;
}
if (!Config.ShowFunctionKeySettings)
{
buttonSettings.Visibility = Visibility.Collapsed;
}
if (!Config.ShowFunctionKeyRestart)
{
buttonRestart.Visibility = Visibility.Collapsed;
}
}
internal void ShowWithFadeOrTransparent(bool formActiveFormIsMenu)
{
if (formActiveFormIsMenu)
@ -821,7 +760,7 @@ namespace SystemTrayMenu.UserInterface
{
int filesAndFoldersCount = foldersCount + filesCount;
string elements = filesAndFoldersCount == 1 ? "element" : "elements";
labelItems.Content = $"{filesAndFoldersCount} {Translator.GetText(elements)}";
labelStatus.Content = $"{filesAndFoldersCount} {Translator.GetText(elements)}";
}
private void HandlePreviewKeyDown(object sender, KeyEventArgs e)