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

View file

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

View file

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

View file

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

View file

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