Simplified code

Fix potential issue of loading from wrong thread during startup
This commit is contained in:
Peter Kirmeier 2023-05-15 22:34:17 +02:00
parent ddf91131cb
commit cd5ab5aa5c
5 changed files with 88 additions and 173 deletions

View file

@ -18,6 +18,7 @@ namespace SystemTrayMenu
/// </summary> /// </summary>
public partial class App : Application, IDisposable public partial class App : Application, IDisposable
{ {
private static TaskbarLogo? taskbarLogo;
private readonly AppNotifyIcon menuNotifyIcon = new(); private readonly AppNotifyIcon menuNotifyIcon = new();
private readonly Menus menus = new(); private readonly Menus menus = new();
private bool isDisposed; private bool isDisposed;
@ -29,26 +30,32 @@ namespace SystemTrayMenu
menus.LoadStopped += menuNotifyIcon.LoadingStop; menus.LoadStopped += menuNotifyIcon.LoadingStop;
menuNotifyIcon.Click += () => menus.SwitchOpenClose(true, false); menuNotifyIcon.Click += () => menus.SwitchOpenClose(true, false);
if (Settings.Default.ShowInTaskbar) Activated += (_, _) => IsActiveApp = true;
{ Deactivated += (_, _) => IsActiveApp = false;
TaskbarLogo = new ();
TaskbarLogo.Activated += (_, _) => menus.SwitchOpenCloseByTaskbarItem();
TaskbarLogo.Show();
}
else
{
menus.FirstStartInBackground();
}
if (Settings.Default.CheckForUpdates) Startup += (_, _) =>
{ {
_ = Dispatcher.InvokeAsync( if (Settings.Default.ShowInTaskbar)
() => GitHubUpdate.ActivateNewVersionFormOrCheckForUpdates(showWhenUpToDate: false), {
DispatcherPriority.ApplicationIdle); taskbarLogo = new();
} taskbarLogo.Activated += (_, _) => menus.SwitchOpenCloseByTaskbarItem();
taskbarLogo.Show();
}
else
{
menus.SwitchOpenClose(false, true);
}
if (Settings.Default.CheckForUpdates)
{
_ = Dispatcher.InvokeAsync(
() => GitHubUpdate.ActivateNewVersionFormOrCheckForUpdates(showWhenUpToDate: false),
DispatcherPriority.ApplicationIdle);
}
};
} }
public static TaskbarLogo? TaskbarLogo { get; private set; } = null; internal static bool IsActiveApp { get; private set; }
public void Dispose() public void Dispose()
{ {
@ -60,8 +67,8 @@ namespace SystemTrayMenu
{ {
if (!isDisposed) if (!isDisposed)
{ {
TaskbarLogo?.Close(); taskbarLogo?.Close();
TaskbarLogo = null; taskbarLogo = null;
menus.Dispose(); menus.Dispose();
menuNotifyIcon.Dispose(); menuNotifyIcon.Dispose();

View file

@ -26,7 +26,6 @@ namespace SystemTrayMenu.Business
internal class Menus : IDisposable internal class Menus : IDisposable
{ {
private readonly Dispatcher dispatchter = Dispatcher.CurrentDispatcher;
private readonly BackgroundWorker workerMainMenu = new(); private readonly BackgroundWorker workerMainMenu = new();
private readonly List<BackgroundWorker> workersSubMenu = new(); private readonly List<BackgroundWorker> workersSubMenu = new();
private readonly WaitToLoadMenu waitToOpenMenu = new(); private readonly WaitToLoadMenu waitToOpenMenu = new();
@ -37,10 +36,10 @@ namespace SystemTrayMenu.Business
private readonly DispatcherTimer timerShowProcessStartedAsLoadingIcon = new(); private readonly DispatcherTimer timerShowProcessStartedAsLoadingIcon = new();
private readonly DispatcherTimer timerStillActiveCheck = new(); private readonly DispatcherTimer timerStillActiveCheck = new();
private readonly DispatcherTimer waitLeave = new(); private readonly DispatcherTimer waitLeave = new();
private DateTime deactivatedTime = DateTime.MinValue;
private OpenCloseState openCloseState = OpenCloseState.Default; private OpenCloseState openCloseState = OpenCloseState.Default;
private TaskbarPosition taskbarPosition = new WindowsTaskbar().Position; private TaskbarPosition taskbarPosition = TaskbarPosition.Unknown;
private bool showMenuAfterMainPreload; private bool showMenuAfterMainPreload;
private bool wasDeactivated;
private Menu? mainMenu; private Menu? mainMenu;
public Menus() public Menus()
@ -115,7 +114,7 @@ namespace SystemTrayMenu.Business
} }
} }
waitToOpenMenu.MouseEnterOk += MouseEnterOk; waitToOpenMenu.MouseSelect += keyboardInput.SelectByMouse;
waitToOpenMenu.CloseMenu += (menu) => HideOldMenu(menu); waitToOpenMenu.CloseMenu += (menu) => HideOldMenu(menu);
if (Settings.Default.SupportGamepad) if (Settings.Default.SupportGamepad)
@ -125,7 +124,7 @@ namespace SystemTrayMenu.Business
{ {
if (IsMainUsable) if (IsMainUsable)
{ {
Menu? menu = GetActiveMenu(mainMenu) ?? mainMenu; Menu? menu = GetActiveMenu(mainMenu) ?? mainMenu; // TODO: Do we really need to provide the menu? doesn't keyboardInput already know this?
menu?.Dispatcher.Invoke(keyboardInput.CmdKeyProcessed, new object[] { menu, key, modifiers }); menu?.Dispatcher.Invoke(keyboardInput.CmdKeyProcessed, new object[] { menu, key, modifiers });
} }
}; };
@ -137,10 +136,7 @@ namespace SystemTrayMenu.Business
void StillActiveTick() void StillActiveTick()
{ {
timerStillActiveCheck.Stop(); timerStillActiveCheck.Stop();
if (!IsActiveApp()) FadeHalfOrOutIfNeeded();
{
FadeHalfOrOutIfNeeded();
}
} }
waitLeave.Interval = TimeSpan.FromMilliseconds(Settings.Default.TimeUntilCloses); waitLeave.Interval = TimeSpan.FromMilliseconds(Settings.Default.TimeUntilCloses);
@ -198,7 +194,7 @@ namespace SystemTrayMenu.Business
} }
[MemberNotNullWhen(true, nameof(mainMenu))] [MemberNotNullWhen(true, nameof(mainMenu))]
private bool IsMainUsable => mainMenu?.IsUsable ?? false; private bool IsMainUsable => mainMenu != null && mainMenu.Visibility == Visibility.Visible;
public void Dispose() public void Dispose()
{ {
@ -227,15 +223,7 @@ namespace SystemTrayMenu.Business
mainMenu?.Close(); mainMenu?.Close();
} }
internal static void OpenFolder(string? path = null) internal static void OpenFolder(string path) => Log.ProcessStart(path);
{
if (string.IsNullOrEmpty(path))
{
path = Config.Path;
}
Log.ProcessStart(path);
}
internal void SwitchOpenCloseByTaskbarItem() internal void SwitchOpenCloseByTaskbarItem()
{ {
@ -245,11 +233,6 @@ namespace SystemTrayMenu.Business
timerStillActiveCheck.Start(); timerStillActiveCheck.Start();
} }
internal void FirstStartInBackground()
{
dispatchter.Invoke(() => SwitchOpenClose(false, true));
}
internal void SwitchOpenClose(bool byClick, bool allowPreloading) internal void SwitchOpenClose(bool byClick, bool allowPreloading)
{ {
// Ignore open close events during main preload #248 // Ignore open close events during main preload #248
@ -261,9 +244,7 @@ namespace SystemTrayMenu.Business
} }
waitToOpenMenu.MouseActive = byClick; waitToOpenMenu.MouseActive = byClick;
if (byClick && if (byClick && !Config.AlwaysOpenByPin && wasDeactivated)
!Config.AlwaysOpenByPin &&
(DateTime.Now - deactivatedTime).TotalMilliseconds < 200)
{ {
// By click on notifyicon the menu gets deactivated and closed // By click on notifyicon the menu gets deactivated and closed
} }
@ -278,7 +259,12 @@ namespace SystemTrayMenu.Business
{ {
openCloseState = OpenCloseState.Closing; openCloseState = OpenCloseState.Closing;
MenusFadeOut(); MenusFadeOut();
StopWorker();
if (workerMainMenu.IsBusy)
{
workerMainMenu.CancelAsync();
}
if (IsVisibleAnyMenu(mainMenu) == null) if (IsVisibleAnyMenu(mainMenu) == null)
{ {
openCloseState = OpenCloseState.Default; openCloseState = OpenCloseState.Default;
@ -287,33 +273,21 @@ namespace SystemTrayMenu.Business
else else
{ {
openCloseState = OpenCloseState.Opening; openCloseState = OpenCloseState.Opening;
StartWorker();
if (Settings.Default.GenerateShortcutsToDrives)
{
GenerateDriveShortcuts.Start();
}
if (!workerMainMenu.IsBusy)
{
LoadStarted?.Invoke();
workerMainMenu.RunWorkerAsync(null);
}
} }
} }
deactivatedTime = DateTime.MinValue; wasDeactivated = false;
}
internal void StartWorker()
{
if (Settings.Default.GenerateShortcutsToDrives)
{
GenerateDriveShortcuts.Start();
}
if (!workerMainMenu.IsBusy)
{
LoadStarted?.Invoke();
workerMainMenu.RunWorkerAsync(null);
}
}
internal void StopWorker()
{
if (workerMainMenu.IsBusy)
{
workerMainMenu.CancelAsync();
}
} }
private static Menu? IsVisibleAnyMenu(Menu? menu) private static Menu? IsVisibleAnyMenu(Menu? menu)
@ -432,13 +406,13 @@ namespace SystemTrayMenu.Business
break; break;
case MenuDataDirectoryState.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.")); 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(); OpenFolder(Config.Path);
Config.SetFolderByUser(); Config.SetFolderByUser();
AppRestart.ByConfigChange(); AppRestart.ByConfigChange();
break; break;
case MenuDataDirectoryState.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.")); 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(); OpenFolder(Config.Path);
Config.SetFolderByUser(); Config.SetFolderByUser();
AppRestart.ByConfigChange(); AppRestart.ByConfigChange();
break; break;
@ -493,8 +467,6 @@ namespace SystemTrayMenu.Business
} }
} }
private bool IsActiveApp() => GetActiveMenu(mainMenu) != null || (App.TaskbarLogo?.IsActive ?? false);
private Menu Create(MenuData menuData, string path) private Menu Create(MenuData menuData, string path)
{ {
Menu menu = new(menuData, path); Menu menu = new(menuData, path);
@ -562,9 +534,9 @@ namespace SystemTrayMenu.Business
else if (!Settings.Default.StaysOpenWhenFocusLostAfterEnterPressed) else if (!Settings.Default.StaysOpenWhenFocusLostAfterEnterPressed)
{ {
FadeHalfOrOutIfNeeded(); FadeHalfOrOutIfNeeded();
if (!IsActiveApp()) if (!App.IsActiveApp)
{ {
deactivatedTime = DateTime.Now; wasDeactivated = true;
} }
} }
} }
@ -583,7 +555,7 @@ namespace SystemTrayMenu.Business
menu.RowSelectionChanged += waitToOpenMenu.RowSelectionChanged; menu.RowSelectionChanged += waitToOpenMenu.RowSelectionChanged;
menu.CellMouseEnter += waitToOpenMenu.MouseEnter; menu.CellMouseEnter += waitToOpenMenu.MouseEnter;
menu.CellMouseLeave += waitToOpenMenu.MouseLeave; menu.CellMouseLeave += waitToOpenMenu.MouseLeave;
menu.CellMouseDown += (menu, itemData) => MouseEnterOk(menu, itemData); menu.CellMouseDown += keyboardInput.SelectByMouse;
menu.CellOpenOnClick += waitToOpenMenu.ClickOpensInstantly; menu.CellOpenOnClick += waitToOpenMenu.ClickOpensInstantly;
menu.ClosePressed += MenusFadeOut; menu.ClosePressed += MenusFadeOut;
@ -596,11 +568,8 @@ namespace SystemTrayMenu.Business
else else
{ {
// Sub Menu (loading) // Sub Menu (loading)
if (IsMainUsable) menu.ShowWithFade(!App.IsActiveApp, false);
{ menu.RefreshSelection();
menu.ShowWithFade(!IsActiveApp(), false);
menu.RefreshSelection();
}
} }
return menu; return menu;
@ -608,7 +577,7 @@ namespace SystemTrayMenu.Business
private void MenuVisibleChanged(Menu menu) private void MenuVisibleChanged(Menu menu)
{ {
if (menu.IsUsable) if (menu.Visibility == Visibility.Visible)
{ {
AdjustMenusSizeAndLocation(menu.Level); AdjustMenusSizeAndLocation(menu.Level);
@ -632,34 +601,14 @@ namespace SystemTrayMenu.Business
} }
} }
private void MouseEnterOk(Menu menu, ListViewItemData itemData) private void SystemEvents_DisplaySettingsChanged(object? sender, EventArgs e) =>
{ mainMenu?.Dispatcher.Invoke(() => mainMenu.RelocateOnNextShow = true);
if (IsMainUsable)
{
keyboardInput.SelectByMouse(menu, itemData);
}
}
private void SystemEvents_DisplaySettingsChanged(object? sender, EventArgs e)
{
dispatchter.Invoke(() =>
{
if (IsMainUsable)
{
Menu? menu = mainMenu;
if (menu != null)
{
menu.RelocateOnNextShow = true;
}
}
});
}
private void FadeHalfOrOutIfNeeded() private void FadeHalfOrOutIfNeeded()
{ {
if (IsMainUsable) if (IsMainUsable)
{ {
if (!IsActiveApp()) if (!App.IsActiveApp)
{ {
if (Settings.Default.StaysOpenWhenFocusLost && IsMouseOverAnyMenu(mainMenu) != null) if (Settings.Default.StaysOpenWhenFocusLost && IsMouseOverAnyMenu(mainMenu) != null)
{ {
@ -711,14 +660,9 @@ namespace SystemTrayMenu.Business
useCustomLocation = false; useCustomLocation = false;
} }
// Only apply taskbar position change when no menu is currently open
WindowsTaskbar taskbar = new();
if (IsMainUsable && mainMenu.SubMenu == null)
{
taskbarPosition = taskbar.Position;
}
// Shrink the usable space depending on taskbar location // Shrink the usable space depending on taskbar location
WindowsTaskbar taskbar = new();
taskbarPosition = taskbar.Position;
switch (taskbarPosition) switch (taskbarPosition)
{ {
case TaskbarPosition.Left: case TaskbarPosition.Left:

View file

@ -16,7 +16,6 @@ namespace SystemTrayMenu.Handler
private Menu? currentMenu; private Menu? currentMenu;
private ListViewItemData? currentItemData; private ListViewItemData? currentItemData;
private bool alreadyOpened; private bool alreadyOpened;
private bool checkForMouseActive = true;
internal WaitToLoadMenu() internal WaitToLoadMenu()
{ {
@ -30,28 +29,34 @@ namespace SystemTrayMenu.Handler
internal event Action? StopLoadMenu; internal event Action? StopLoadMenu;
internal event Action<Menu, ListViewItemData>? MouseEnterOk; internal event Action<Menu, ListViewItemData>? MouseSelect;
internal bool MouseActive { get; set; } internal bool MouseActive { get; set; }
public void Dispose() public void Dispose() => timerStartLoad.Stop();
{
timerStartLoad.Stop();
}
internal void MouseEnter(Menu menu, ListViewItemData itemData) internal void MouseEnter(Menu menu, ListViewItemData itemData)
{ {
if (MouseActive) if (MouseActive)
{ {
MouseEnterOk?.Invoke(menu, itemData); MouseSelect?.Invoke(menu, itemData);
timerStartLoad.Stop(); timerStartLoad.Stop();
StopLoadMenu?.Invoke(); StopLoadMenu?.Invoke();
checkForMouseActive = true;
SetData(menu, itemData); SetData(menu, itemData);
timerStartLoad.Start(); timerStartLoad.Start();
} }
} }
internal void MouseLeave()
{
if (MouseActive)
{
timerStartLoad.Stop();
StopLoadMenu?.Invoke();
ResetData();
}
}
internal void RowSelectionChanged(Menu? menu) internal void RowSelectionChanged(Menu? menu)
{ {
// Deselect // Deselect
@ -64,28 +69,16 @@ namespace SystemTrayMenu.Handler
if (menu?.SelectedItem != null) if (menu?.SelectedItem != null)
{ {
SetData(menu, menu.SelectedItem); SetData(menu, menu.SelectedItem);
checkForMouseActive = false;
timerStartLoad.Start(); timerStartLoad.Start();
} }
} }
internal void MouseLeave()
{
if (MouseActive)
{
timerStartLoad.Stop();
StopLoadMenu?.Invoke();
ResetData();
}
}
internal void ClickOpensInstantly(Menu menu, ListViewItemData itemData) internal void ClickOpensInstantly(Menu menu, ListViewItemData itemData)
{ {
timerStartLoad.Stop(); timerStartLoad.Stop();
menu.SelectedItem = itemData; menu.SelectedItem = itemData;
SetData(menu, itemData); SetData(menu, itemData);
MouseActive = true; MouseActive = true;
checkForMouseActive = false;
CallOpenMenuNow(); CallOpenMenuNow();
} }
@ -95,17 +88,13 @@ namespace SystemTrayMenu.Handler
StopLoadMenu?.Invoke(); StopLoadMenu?.Invoke();
SetData(menu, itemData); SetData(menu, itemData);
MouseActive = false; MouseActive = false;
checkForMouseActive = false;
CallOpenMenuNow(); CallOpenMenuNow();
} }
private void WaitStartLoad_Tick(object? sender, EventArgs e) private void WaitStartLoad_Tick(object? sender, EventArgs e)
{ {
timerStartLoad.Stop(); timerStartLoad.Stop();
if (!checkForMouseActive || MouseActive) CallOpenMenuNow();
{
CallOpenMenuNow();
}
} }
private void CallOpenMenuNow() private void CallOpenMenuNow()

View file

@ -21,8 +21,7 @@
<Storyboard> <Storyboard>
<DoubleAnimation <DoubleAnimation
Storyboard.TargetProperty="Opacity" Storyboard.TargetProperty="Opacity"
From="{Binding Opacity}" To="0.8" Duration="0:0:0.4" From="{Binding Opacity}" To="0.8" Duration="0:0:0.4"/>
Completed="FadeIn_Completed"/>
</Storyboard> </Storyboard>
</BeginStoryboard> </BeginStoryboard>
</EventTrigger> </EventTrigger>
@ -31,8 +30,7 @@
<Storyboard> <Storyboard>
<DoubleAnimation <DoubleAnimation
Storyboard.TargetProperty="Opacity" Storyboard.TargetProperty="Opacity"
From="{Binding Opacity}" To="1.0" Duration="0:0:0.5" From="{Binding Opacity}" To="1.0" Duration="0:0:0.5"/>
Completed="FadeIn_Completed"/>
</Storyboard> </Storyboard>
</BeginStoryboard> </BeginStoryboard>
</EventTrigger> </EventTrigger>

View file

@ -44,7 +44,6 @@ namespace SystemTrayMenu.UserInterface
#if TODO // SEARCH #if TODO // SEARCH
public const string RowFilterShowAll = "[SortIndex] LIKE '%0%'"; public const string RowFilterShowAll = "[SortIndex] LIKE '%0%'";
#endif #endif
private bool isFading;
private bool directionToRight; private bool directionToRight;
private bool mouseDown; private bool mouseDown;
private Point lastLocation; private Point lastLocation;
@ -207,7 +206,6 @@ namespace SystemTrayMenu.UserInterface
Closed += (_, _) => Closed += (_, _) =>
{ {
timerUpdateIcons.Stop(); timerUpdateIcons.Stop();
IsClosed = true; // TODO WPF Replace Forms wrapper
if (RowDataParent?.SubMenu == this) if (RowDataParent?.SubMenu == this)
{ {
@ -308,10 +306,6 @@ namespace SystemTrayMenu.UserInterface
internal bool RelocateOnNextShow { get; set; } = true; internal bool RelocateOnNextShow { get; set; } = true;
internal bool IsClosed { get; private set; } = false;
internal bool IsUsable => Visibility == Visibility.Visible && !isFading && !IsClosed;
public override string ToString() => nameof(Menu) + " L" + Level.ToString() + ": " + Title; public override string ToString() => nameof(Menu) + " L" + Level.ToString() + ": " + Title;
internal void ResetSearchText() internal void ResetSearchText()
@ -485,13 +479,11 @@ namespace SystemTrayMenu.UserInterface
{ {
if (Settings.Default.UseFading) if (Settings.Default.UseFading)
{ {
isFading = true;
RaiseEvent(new(routedEvent: FadeInEvent)); RaiseEvent(new(routedEvent: FadeInEvent));
} }
else else
{ {
Opacity = 1D; Opacity = 1D;
FadeIn_Completed(this, new());
} }
} }
} }
@ -513,22 +505,17 @@ namespace SystemTrayMenu.UserInterface
Opacity = 0D; Opacity = 0D;
Show(); Show();
if (Settings.Default.UseFading) if (!Settings.Default.UseFading)
{ {
isFading = true; Opacity = transparency ? 0.80D : 1D;
if (transparency) }
{ else if (transparency)
RaiseEvent(new(routedEvent: FadeToTransparentEvent)); {
} RaiseEvent(new(routedEvent: FadeToTransparentEvent));
else
{
RaiseEvent(new(routedEvent: FadeInEvent));
}
} }
else else
{ {
Opacity = transparency ? 0.80D : 1D; RaiseEvent(new(routedEvent: FadeInEvent));
FadeIn_Completed(this, new());
} }
} }
@ -546,7 +533,6 @@ namespace SystemTrayMenu.UserInterface
if (Settings.Default.UseFading) if (Settings.Default.UseFading)
{ {
isFading = true;
RaiseEvent(new(routedEvent: FadeOutEvent)); RaiseEvent(new(routedEvent: FadeOutEvent));
} }
else else
@ -867,16 +853,7 @@ namespace SystemTrayMenu.UserInterface
labelStatus.Content = $"{filesAndFoldersCount} {Translator.GetText(elements)}"; labelStatus.Content = $"{filesAndFoldersCount} {Translator.GetText(elements)}";
} }
private void FadeIn_Completed(object sender, EventArgs e) private void FadeOut_Completed(object sender, EventArgs e) => Hide();
{
isFading = false;
}
private void FadeOut_Completed(object sender, EventArgs e)
{
isFading = false;
Hide();
}
private void HandlePreviewKeyDown(object sender, KeyEventArgs e) private void HandlePreviewKeyDown(object sender, KeyEventArgs e)
{ {