diff --git a/Business/Menus.cs b/Business/Menus.cs index c43e389..145b06d 100644 --- a/Business/Menus.cs +++ b/Business/Menus.cs @@ -60,88 +60,6 @@ namespace SystemTrayMenu.Business workerMainMenu.WorkerSupportsCancellation = true; workerMainMenu.DoWork += LoadMenu; workerMainMenu.RunWorkerCompleted += LoadMainMenuCompleted; - void LoadMainMenuCompleted(object? sender, RunWorkerCompletedEventArgs e) - { - keyboardInput.ResetSelectedByKey(); - LoadStopped?.Invoke(); - - if (e.Result == null) - { - // The main menu gets loaded again - // Clean up menu status of previous one - ListView? dgvMainMenu = menus[0]?.GetDataGridView(); - if (dgvMainMenu != null) - { - foreach (ListViewItemData item in dgvMainMenu.Items) - { - RowData rowDataToClear = item.data; - rowDataToClear.IsMenuOpen = false; - rowDataToClear.IsClicking = false; - rowDataToClear.IsSelected = false; - rowDataToClear.IsContextMenuOpen = false; - } - - RefreshSelection(dgvMainMenu); - } - - if (Settings.Default.AppearAtMouseLocation) - { - Menu? menu = menus[0]; - if (menu != null) - { - menu.RowDataParent = null; - } - } - - AsEnumerable.ToList().ForEach(m => { m.ShowWithFade(); }); - } - else - { - // First time the main menu gets loaded - MenuData menuData = (MenuData)e.Result; - switch (menuData.DirectoryState) - { - case MenuDataDirectoryState.Valid: - if (IconReader.IsPreloading) - { - workerMainMenu.DoWork -= LoadMenu; - workerMainMenu.CancelAsync(); - Create(menuData, Config.Path); // Level 0 Main Menu - - IconReader.IsPreloading = false; - if (showMenuAfterMainPreload) - { - AsEnumerable.ToList().ForEach(m => { m.ShowWithFade(); }); - } - } - else - { - AsEnumerable.ToList().ForEach(m => { m.ShowWithFade(); }); - } - - 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(); - 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(); - Config.SetFolderByUser(); - AppRestart.ByConfigChange(); - break; - case MenuDataDirectoryState.Undefined: - Log.Info($"{nameof(MenuDataDirectoryState)}.{nameof(MenuDataDirectoryState.Undefined)}"); - break; - default: - break; - } - } - - openCloseState = OpenCloseState.Default; - } waitToOpenMenu.StopLoadMenu += WaitToOpenMenu_StopLoadMenu; void WaitToOpenMenu_StopLoadMenu() @@ -446,11 +364,12 @@ namespace SystemTrayMenu.Business } } - private static void LoadMenu(object? senderDoWork, DoWorkEventArgs eDoWork) + private static void LoadMenu(object? sender, DoWorkEventArgs eDoWork) { - string? path; - int level = 0; + BackgroundWorker? workerSelf = sender as BackgroundWorker; RowData? rowData = eDoWork.Argument as RowData; + string? path; + int level; if (rowData != null) { path = rowData.ResolvedPath; @@ -464,13 +383,99 @@ namespace SystemTrayMenu.Business else { path = Config.Path; + level = 0; } MenuData menuData = new(level, rowData); - DirectoryHelpers.DiscoverItems((BackgroundWorker?)senderDoWork, path, ref menuData); + DirectoryHelpers.DiscoverItems(workerSelf, path, ref menuData); + if (menuData.DirectoryState != MenuDataDirectoryState.Undefined && + workerSelf != null && level == 0) + { + // After success of MainMenu loading: never run again + workerSelf.DoWork -= LoadMenu; + } + eDoWork.Result = menuData; } + private void LoadMainMenuCompleted(object? sender, RunWorkerCompletedEventArgs e) + { + keyboardInput.ResetSelectedByKey(); + LoadStopped?.Invoke(); + + if (e.Result == null) + { + Menu? menu = menus[0]; + if (menu != null) + { + // The main menu gets loaded again + // Clean up menu status of previous one + ListView? dgvMainMenu = menu.GetDataGridView(); + if (dgvMainMenu != null) + { + foreach (ListViewItemData item in dgvMainMenu.Items) + { + RowData rowDataToClear = item.data; + rowDataToClear.IsMenuOpen = false; + rowDataToClear.IsClicking = false; + rowDataToClear.IsSelected = false; + rowDataToClear.IsContextMenuOpen = false; + } + + RefreshSelection(dgvMainMenu); + } + + menu.RelocateOnNextShow = true; + } + + AsEnumerable.ToList().ForEach(m => { m.ShowWithFade(); }); + } + else + { + // First time the main menu gets loaded + MenuData menuData = (MenuData)e.Result; + switch (menuData.DirectoryState) + { + case MenuDataDirectoryState.Valid: + if (IconReader.IsPreloading) + { + Create(menuData, Config.Path); // Level 0 Main Menu + + IconReader.IsPreloading = false; + if (showMenuAfterMainPreload) + { + AsEnumerable.ToList().ForEach(m => { m.ShowWithFade(); }); + } + } + else + { + AsEnumerable.ToList().ForEach(m => { m.ShowWithFade(); }); + } + + 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(); + 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(); + Config.SetFolderByUser(); + AppRestart.ByConfigChange(); + break; + case MenuDataDirectoryState.Undefined: + Log.Info($"{nameof(MenuDataDirectoryState)}.{nameof(MenuDataDirectoryState.Undefined)}"); + break; + default: + break; + } + } + + openCloseState = OpenCloseState.Default; + } + private bool IsActive() { bool IsShellContextMenuOpen() @@ -860,8 +865,7 @@ namespace SystemTrayMenu.Business Menu? menu = menus[0]; if (menu != null) { - // TODO: What does this do??? - menu.RowDataParent = null; + menu.RelocateOnNextShow = true; } } }); @@ -958,7 +962,7 @@ namespace SystemTrayMenu.Business Settings.Default.CustomLocationX, Settings.Default.CustomLocationY)); - useCustomLocation = !screenBounds.Contains( + useCustomLocation = screenBounds.Contains( new Point(Settings.Default.CustomLocationX, Settings.Default.CustomLocationY)); } else diff --git a/UserInterface/Menu.xaml.cs b/UserInterface/Menu.xaml.cs index 93d7d06..78c4796 100644 --- a/UserInterface/Menu.xaml.cs +++ b/UserInterface/Menu.xaml.cs @@ -323,14 +323,17 @@ namespace SystemTrayMenu.UserInterface BottomLeft, BottomRight, TopRight, + Point, } - public System.Drawing.Point Location => new ((int)Left, (int)Top); // TODO WPF Replace Forms wrapper + public Point Location => new (Left, Top); // TODO WPF Replace Forms wrapper internal int Level { get; set; } internal RowData? RowDataParent { get; set; } + internal bool RelocateOnNextShow { get; set; } = true; + internal bool IsClosed { get; private set; } = false; internal bool IsUsable => Visibility == Visibility.Visible && !isFading && !IsClosed; @@ -542,86 +545,99 @@ namespace SystemTrayMenu.UserInterface StartLocation startLocation, bool useCustomLocation) { + Point originLocation = new(0, 0); + // Update the height and width AdjustDataGridViewHeight(menuPredecessor, bounds.Height); AdjustDataGridViewWidth(); - bool changeDirectionWhenOutOfBounds = true; - if (Level > 0) { + if (menuPredecessor == null) + { + // should never happen + return; + } + // Sub Menu location depends on the location of its predecessor startLocation = StartLocation.Predecessor; + originLocation = menuPredecessor.Location; } else if (useCustomLocation) { - // Do not adjust location again because Cursor.Postion changed - if (RowDataParent != null) + if (!RelocateOnNextShow) { return; } - // Use this menu as predecessor and overwrite location with CustomLocation - menuPredecessor = this; - RowDataParent = new RowData(); - Left = Settings.Default.CustomLocationX; - Top = Settings.Default.CustomLocationY; - directionToRight = true; - startLocation = StartLocation.Predecessor; - changeDirectionWhenOutOfBounds = false; + RelocateOnNextShow = false; + originLocation = new(Settings.Default.CustomLocationX, Settings.Default.CustomLocationY); + startLocation = StartLocation.Point; } else if (Settings.Default.AppearAtMouseLocation) { - // Do not adjust location again because Cursor.Postion changed - if (RowDataParent != null) + if (!RelocateOnNextShow) { return; } - // Use this menu as predecessor and overwrite location with Cursor.Postion - menuPredecessor = this; - RowDataParent = new RowData(); - var position = Mouse.GetPosition(this); - Left = position.X; - Top = position.Y - labelTitle.Height; - directionToRight = true; - startLocation = StartLocation.Predecessor; - changeDirectionWhenOutOfBounds = false; + RelocateOnNextShow = false; + originLocation = Mouse.GetPosition(this); + originLocation.Y -= labelTitle.Height; + startLocation = StartLocation.Point; } if (IsLoaded) { - AdjustWindowPositionInternal(); + AdjustWindowPositionInternal(originLocation); } else { // Layout cannot be calculated during loading, postpone this event - Loaded += (_, _) => AdjustWindowPositionInternal(); + Loaded += (_, _) => AdjustWindowPositionInternal(originLocation); } - void AdjustWindowPositionInternal() + void AdjustWindowPositionInternal(Point originLocation) { + RowData? trigger; + // Make sure we have latest values of own window size UpdateLayout(); + // Prepare parameters + if (startLocation == StartLocation.Predecessor && menuPredecessor != null) + { + directionToRight = menuPredecessor.directionToRight; // try keeping same direction from predecessor + trigger = RowDataParent; + } + else + { + // Use own menu as predecessor for calculations (Left and Top were set beforehand) + menuPredecessor = this; + directionToRight = true; // use right as default direction + trigger = new(); + } + // Calculate X position double x; switch (startLocation) { + case StartLocation.Point: case StartLocation.Predecessor: double scaling = Math.Round(Scaling.Factor, 0, MidpointRounding.AwayFromZero); - directionToRight = menuPredecessor!.directionToRight; // try keeping same direction + if (directionToRight) { - x = menuPredecessor.Location.X + menuPredecessor.Width - scaling; + x = originLocation.X + menuPredecessor.Width - scaling; - if (changeDirectionWhenOutOfBounds && + // Change direction when out of bounds (predecessor only) + if (startLocation == StartLocation.Predecessor && bounds.X + bounds.Width <= x + Width - scaling) { - x = menuPredecessor.Location.X - Width + scaling; + x = originLocation.X - Width + scaling; if (x < bounds.X && - menuPredecessor.Location.X + menuPredecessor.Width < bounds.X + bounds.Width && - bounds.X + (bounds.Width / 2) > menuPredecessor.Location.X + (Width / 2)) + originLocation.X + menuPredecessor.Width < bounds.X + bounds.Width && + bounds.X + (bounds.Width / 2) > originLocation.X + (Width / 2)) { x = bounds.X + bounds.Width - Width + scaling; } @@ -638,15 +654,16 @@ namespace SystemTrayMenu.UserInterface } else { - x = menuPredecessor.Location.X - Width + scaling; + x = originLocation.X - Width + scaling; - if (changeDirectionWhenOutOfBounds && + // Change direction when out of bounds (predecessor only) + if (startLocation == StartLocation.Predecessor && x < bounds.X) { - x = menuPredecessor.Location.X + menuPredecessor.Width - scaling; + x = originLocation.X + menuPredecessor.Width - scaling; if (x + Width > bounds.X + bounds.Width && - menuPredecessor.Location.X > bounds.X && - bounds.X + (bounds.Width / 2) < menuPredecessor.Location.X + (Width / 2)) + originLocation.X > bounds.X && + bounds.X + (bounds.Width / 2) < originLocation.X + (Width / 2)) { x = bounds.X; } @@ -699,13 +716,12 @@ namespace SystemTrayMenu.UserInterface double y; switch (startLocation) { + case StartLocation.Point: case StartLocation.Predecessor: - - RowData? trigger = RowDataParent; ListView dgv = menuPredecessor!.GetDataGridView()!; // Set position on same height as the selected row from predecessor - y = menuPredecessor.Location.Y; + y = originLocation.Y; if (trigger != null && dgv.Items.Count > trigger.RowIndex) { // When item is not found, it might be invalidated due to resizing or moving