From e9675685ef0c24047b68037cadbb56ef45c8ff86 Mon Sep 17 00:00:00 2001 From: Peter Kirmeier Date: Thu, 18 May 2023 22:48:18 +0200 Subject: [PATCH] Separated ListViewMenuItem from Menu (former ListViewItemData) --- Business/KeyboardInput.cs | 11 +- Business/Menus.cs | 12 +- Business/WaitToLoadMenu.cs | 16 +- DataClasses/ListViewMenuItem.cs | 224 ++++++++++++++++++++++++++++ UserInterface/Menu.xaml.cs | 257 ++++---------------------------- 5 files changed, 268 insertions(+), 252 deletions(-) create mode 100644 DataClasses/ListViewMenuItem.cs diff --git a/Business/KeyboardInput.cs b/Business/KeyboardInput.cs index d375857..cff8c68 100644 --- a/Business/KeyboardInput.cs +++ b/Business/KeyboardInput.cs @@ -9,7 +9,6 @@ namespace SystemTrayMenu.Handler using SystemTrayMenu.DataClasses; using SystemTrayMenu.Helpers; using SystemTrayMenu.Utilities; - using static SystemTrayMenu.UserInterface.Menu; using Menu = SystemTrayMenu.UserInterface.Menu; internal class KeyboardInput : IDisposable @@ -20,9 +19,9 @@ namespace SystemTrayMenu.Handler internal event Action? HotKeyPressed; - internal event Action? RowSelectionChanged; + internal event Action? RowSelectionChanged; - internal event Action? EnterPressed; + internal event Action? EnterPressed; internal bool IsSelectedByKey { get; set; } @@ -107,7 +106,7 @@ namespace SystemTrayMenu.Handler if (modifiers == ModifierKeys.None) { Menu? menu = focussedMenu; - ListViewItemData? itemData = menu?.SelectedItem; + ListViewMenuItem? itemData = menu?.SelectedItem; if (menu != null && itemData != null) { var position = Mouse.GetPosition(menu); @@ -131,7 +130,7 @@ namespace SystemTrayMenu.Handler } } - internal void SelectByMouse(ListViewItemData itemData) + internal void SelectByMouse(ListViewMenuItem itemData) { IsSelectedByKey = false; @@ -141,7 +140,7 @@ namespace SystemTrayMenu.Handler private void SelectByKey(Key key, Menu menuBefore) { - ListViewItemData? rowBefore = menuBefore.SelectedItem; + ListViewMenuItem? rowBefore = menuBefore.SelectedItem; if (rowBefore == null) { focussedMenu = null; diff --git a/Business/Menus.cs b/Business/Menus.cs index a6c8be8..4405d67 100644 --- a/Business/Menus.cs +++ b/Business/Menus.cs @@ -392,7 +392,7 @@ namespace SystemTrayMenu.Business ListView dgv = menu.GetDataGridView(); if (dgv.Items.Count > 0) { - keyboardInput.SelectByMouse((ListViewItemData)dgv.Items[0]); + keyboardInput.SelectByMouse((ListViewMenuItem)dgv.Items[0]); } } @@ -672,7 +672,7 @@ namespace SystemTrayMenu.Business try { List rowDatas = new(); - foreach (ListViewItemData item in menu.GetDataGridView().Items) + foreach (ListViewMenuItem item in menu.GetDataGridView().Items) { RowData rowData = item.data; if (rowData.Path.StartsWith($"{e.OldFullPath}")) @@ -725,9 +725,9 @@ namespace SystemTrayMenu.Business try { ListView? dgv = menu.GetDataGridView(); - List rowsToRemove = new(); + List rowsToRemove = new(); - foreach (ListViewItemData item in dgv.ItemsSource) + foreach (ListViewMenuItem item in dgv.ItemsSource) { RowData rowData = item.data; if (rowData.Path == e.FullPath || @@ -738,7 +738,7 @@ namespace SystemTrayMenu.Business } } - foreach (ListViewItemData rowToRemove in rowsToRemove) + foreach (ListViewMenuItem rowToRemove in rowsToRemove) { ((IEditableCollectionView)dgv.Items).Remove(rowToRemove); } @@ -771,7 +771,7 @@ namespace SystemTrayMenu.Business var items = menu.GetDataGridView().Items; List rowDatas = new(items.Count + 1) { rowData }; - foreach (ListViewItemData item in items) + foreach (ListViewMenuItem item in items) { rowDatas.Add(item.data); } diff --git a/Business/WaitToLoadMenu.cs b/Business/WaitToLoadMenu.cs index 874b0c5..6a7d624 100644 --- a/Business/WaitToLoadMenu.cs +++ b/Business/WaitToLoadMenu.cs @@ -6,12 +6,12 @@ namespace SystemTrayMenu.Handler { using System; using System.Windows.Threading; - using static SystemTrayMenu.UserInterface.Menu; + using SystemTrayMenu.DataClasses; internal class WaitToLoadMenu : IDisposable { private readonly DispatcherTimer timerStartLoad = new(); - private ListViewItemData? currentItemData; + private ListViewMenuItem? currentItemData; private bool alreadyOpened; internal WaitToLoadMenu() @@ -22,13 +22,13 @@ namespace SystemTrayMenu.Handler internal event Action? StopLoadMenu; - internal event Action? MouseSelect; + internal event Action? MouseSelect; internal bool MouseActive { get; set; } public void Dispose() => timerStartLoad.Stop(); - internal void MouseEnter(ListViewItemData itemData) + internal void MouseEnter(ListViewMenuItem itemData) { if (MouseActive) { @@ -50,7 +50,7 @@ namespace SystemTrayMenu.Handler } } - internal void RowSelectionChanged(ListViewItemData? itemData) + internal void RowSelectionChanged(ListViewMenuItem? itemData) { // Deselect timerStartLoad.Stop(); @@ -66,7 +66,7 @@ namespace SystemTrayMenu.Handler } } - internal void OpenSubMenuByMouse(ListViewItemData itemData) + internal void OpenSubMenuByMouse(ListViewMenuItem itemData) { timerStartLoad.Stop(); StopLoadMenu?.Invoke(); // TODO: Missing in v1 ? @@ -75,7 +75,7 @@ namespace SystemTrayMenu.Handler OpenSubMenu(); } - internal void OpenSubMenuByKey(ListViewItemData itemData) + internal void OpenSubMenuByKey(ListViewMenuItem itemData) { timerStartLoad.Stop(); StopLoadMenu?.Invoke(); @@ -101,7 +101,7 @@ namespace SystemTrayMenu.Handler } } - private void SetData(ListViewItemData itemData) + private void SetData(ListViewMenuItem itemData) { if (currentItemData != itemData) { diff --git a/DataClasses/ListViewMenuItem.cs b/DataClasses/ListViewMenuItem.cs new file mode 100644 index 0000000..3b9074a --- /dev/null +++ b/DataClasses/ListViewMenuItem.cs @@ -0,0 +1,224 @@ +// +// Copyright (c) PlaceholderCompany. All rights reserved. +// + +namespace SystemTrayMenu.DataClasses +{ + using System.ComponentModel; + using System.IO; + using System.Runtime.CompilerServices; + using System.Windows; + using System.Windows.Media; + using SystemTrayMenu.Properties; + using SystemTrayMenu.UserInterface; + using SystemTrayMenu.Utilities; + + /// + /// Type for ListView items. + /// + internal class ListViewMenuItem : INotifyPropertyChanged + { + private Brush? backgroundBrush; + private Brush? borderBrush; + private ImageSource? columnIcon; + private bool isSelected; + + internal ListViewMenuItem(ImageSource? columnIcon, string columnText, RowData rowData, int sortIndex) + { + this.columnIcon = columnIcon; + ColumnText = columnText; + data = rowData; + SortIndex = sortIndex; + } + + public event PropertyChangedEventHandler? PropertyChanged; + + public Brush? BackgroundBrush + { + get => backgroundBrush; + private set + { + if (value != backgroundBrush) + { + backgroundBrush = value; + CallPropertyChanged(); + } + } + } + + public Brush? BorderBrush + { + get => borderBrush; + private set + { + if (value != borderBrush) + { + borderBrush = value; + CallPropertyChanged(); + } + } + } + + public ImageSource? ColumnIcon + { + get => columnIcon; + set + { + if (value != columnIcon) + { + columnIcon = value; + CallPropertyChanged(); + } + } + } + + public string ColumnText { get; } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Benennungsstile", Justification = "Temporarily retained for compatibility reasons")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "Temporarily retained for compatibility reasons")] + internal RowData data { get; set; } + + internal int SortIndex { get; set; } + + internal bool IsPendingOpenItem { get; set; } + + internal bool IsSelected + { + get => isSelected; + set + { + if (value != isSelected) + { + isSelected = value; + CallPropertyChanged(); + } + } + } + + public override string ToString() => nameof(ListViewMenuItem) + ": " + ColumnText + ", Owner: " + (data.Owner?.ToString() ?? "null"); + + /// + /// Triggers an PropertyChanged event of INotifyPropertyChanged. + /// + /// Name of the changing property. + public void CallPropertyChanged([CallerMemberName] string? propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + + internal void OpenItem(int clickCount) + { + bool doCloseAfterOpen = false; + + if (!data.IsPointingToFolder) + { + if (clickCount == -1 || + (clickCount == 1 && Settings.Default.OpenItemWithOneClick) || + (clickCount == 2 && !Settings.Default.OpenItemWithOneClick)) + { + string? workingDirectory = System.IO.Path.GetDirectoryName(data.ResolvedPath); + Log.ProcessStart(data.Path, string.Empty, false, workingDirectory, true, data.ResolvedPath); + if (!Settings.Default.StaysOpenWhenItemClicked) + { + doCloseAfterOpen = true; + } + } + } + else + { + if (clickCount == -1 || + (clickCount == 1 && Settings.Default.OpenDirectoryWithOneClick) || + (clickCount == 2 && !Settings.Default.OpenDirectoryWithOneClick)) + { + Log.ProcessStart(data.Path); + if (!Settings.Default.StaysOpenWhenItemClicked) + { + doCloseAfterOpen = true; + } + } + } + + if (data.Owner != null) + { + if (clickCount == 1) + { + data.Owner.RiseItemOpened(this); + } + + if (doCloseAfterOpen) + { + data.Owner.HideAllMenus(); + } + } + } + + internal void OpenShellContextMenu(Point position) + { + if (data.IsPointingToFolder) + { + ShellContextMenu.OpenShellContextMenu(new DirectoryInfo(data.Path), position); + } + else + { + ShellContextMenu.OpenShellContextMenu(data.FileInfo, position); + } + } + + internal void OpenSubMenu() + { + Menu? owner = data.Owner; + + // TODO: always true? maybe only when cached in WaitToLoadMenu or keyboardInput? + if (owner?.GetDataGridView().Items.Contains(this) ?? false) + { + Menu? openSubMenu = owner.SubMenu; + + // only re-open when the menu is not already open + if (data.SubMenu != null && data.SubMenu == openSubMenu) + { + // Close second level sub menus when already opened + openSubMenu.SelectedItem = null; + if (openSubMenu.SubMenu != null) + { + openSubMenu.SubMenu.HideWithFade(true); + openSubMenu.RefreshSelection(); + } + } + else + { + // In case another menu exists, close it + if (openSubMenu != null) + { + // Give the opening window focus + // if closing window lose focus, no window would have focus any more + owner.Activate(); + owner.FocusTextBox(); + openSubMenu.HideWithFade(true); + owner.RefreshSelection(); + } + + if (data.IsPointingToFolder) + { + owner.RiseStartLoadSubMenu(data); + } + } + } + } + + internal void UpdateColors() + { + if (data.SubMenu != null) + { + BorderBrush = MenuDefines.ColorOpenFolderBorder; + BackgroundBrush = MenuDefines.ColorOpenFolder; + } + else if (IsSelected) + { + BorderBrush = MenuDefines.ColorSelectedItemBorder; + BackgroundBrush = MenuDefines.ColorSelectedItem; + } + else + { + BorderBrush = Brushes.White; + BackgroundBrush = Brushes.White; + } + } + } +} diff --git a/UserInterface/Menu.xaml.cs b/UserInterface/Menu.xaml.cs index 9675e65..858e6b0 100644 --- a/UserInterface/Menu.xaml.cs +++ b/UserInterface/Menu.xaml.cs @@ -6,10 +6,8 @@ namespace SystemTrayMenu.UserInterface { using System; using System.Collections.Generic; - using System.ComponentModel; using System.Globalization; using System.IO; - using System.Runtime.CompilerServices; using System.Windows; using System.Windows.Controls; using System.Windows.Data; @@ -210,7 +208,7 @@ namespace SystemTrayMenu.UserInterface RowDataParent.SubMenu = null; } - foreach (ListViewItemData item in dgv.Items) + foreach (ListViewMenuItem item in dgv.Items) { item.data.SubMenu?.Close(); } @@ -229,15 +227,15 @@ namespace SystemTrayMenu.UserInterface internal event Action? SearchTextChanged; - internal event Action? RowSelectionChanged; + internal event Action? RowSelectionChanged; - internal event Action? CellMouseEnter; + internal event Action? CellMouseEnter; internal event Action? CellMouseLeave; - internal event Action? CellMouseDown; + internal event Action? CellMouseDown; - internal event Action? CellOpenOnClick; + internal event Action? CellOpenOnClick; internal event RoutedEventHandler FadeToTransparent { @@ -272,9 +270,9 @@ namespace SystemTrayMenu.UserInterface internal RowData? RowDataParent { get; set; } - internal ListViewItemData? SelectedItem + internal ListViewMenuItem? SelectedItem { - get => (ListViewItemData?)dgv.SelectedItem; + get => (ListViewMenuItem?)dgv.SelectedItem; set => dgv.SelectedItem = value; } @@ -286,7 +284,7 @@ namespace SystemTrayMenu.UserInterface { get { - foreach (ListViewItemData item in dgv.Items) + foreach (ListViewMenuItem item in dgv.Items) { if (item.data.SubMenu != null) { @@ -302,6 +300,10 @@ namespace SystemTrayMenu.UserInterface public override string ToString() => nameof(Menu) + " L" + Level.ToString() + ": " + Title; + internal void RiseItemOpened(ListViewMenuItem item) => CellOpenOnClick?.Invoke(item); + + internal void RiseStartLoadSubMenu(RowData rowData) => StartLoadSubMenu?.Invoke(rowData); + internal void ResetSearchText() { textBoxSearch.Text = string.Empty; @@ -396,14 +398,14 @@ namespace SystemTrayMenu.UserInterface internal bool TrySelectAt(int index, int indexAlternative = -1) { - ListViewItemData itemData; + ListViewMenuItem itemData; if (index >= 0 && dgv.Items.Count > index) { - itemData = (ListViewItemData)dgv.Items[index]; + itemData = (ListViewMenuItem)dgv.Items[index]; } else if (indexAlternative >= 0 && dgv.Items.Count > indexAlternative) { - itemData = (ListViewItemData)dgv.Items[indexAlternative]; + itemData = (ListViewMenuItem)dgv.Items[indexAlternative]; } else { @@ -423,7 +425,7 @@ namespace SystemTrayMenu.UserInterface int foldersCount = 0; int filesCount = 0; - List items = new(); + List items = new(); foreach (RowData rowData in data) { @@ -961,7 +963,7 @@ namespace SystemTrayMenu.UserInterface Resources["ColumnIconWidth"] = (double)(int)((icoWidth * factorIconSizeInPercent * Scaling.Factor) + 0.5); double renderedMaxWidth = 0D; - foreach (ListViewItemData item in dgv.Items) + foreach (ListViewMenuItem item in dgv.Items) { double renderedWidth = new FormattedText( item.ColumnText, @@ -1010,7 +1012,7 @@ namespace SystemTrayMenu.UserInterface view.Filter = (object item) => { // Look for each space separated string if it is part of an entries text (case insensitive) - ListViewItemData itemData = (ListViewItemData)item; + ListViewMenuItem itemData = (ListViewMenuItem)item; foreach (string pattern in userPattern.Split(' ', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries)) { if (!itemData.ColumnText.ToLower().Contains(pattern)) @@ -1134,7 +1136,7 @@ namespace SystemTrayMenu.UserInterface { int iconsToUpdate = 0; - foreach (ListViewItemData itemData in dgv.Items) + foreach (ListViewMenuItem itemData in dgv.Items) { RowData rowData = itemData.data; rowData.RowIndex = dgv.Items.IndexOf(itemData); @@ -1203,13 +1205,13 @@ namespace SystemTrayMenu.UserInterface { if (e != null) { - foreach (ListViewItemData itemData in e.AddedItems) + foreach (ListViewMenuItem itemData in e.AddedItems) { itemData.IsSelected = true; itemData.UpdateColors(); } - foreach (ListViewItemData itemData in e.RemovedItems) + foreach (ListViewMenuItem itemData in e.RemovedItems) { itemData.IsSelected = false; itemData.UpdateColors(); @@ -1219,7 +1221,7 @@ namespace SystemTrayMenu.UserInterface { // TODO: Refactor item selection to prevent running this loop ListView lv = (ListView)sender; - foreach (ListViewItemData itemData in lv.Items) + foreach (ListViewMenuItem itemData in lv.Items) { itemData.IsSelected = lv.SelectedItem == itemData; itemData.UpdateColors(); @@ -1228,13 +1230,13 @@ namespace SystemTrayMenu.UserInterface } private void ListViewItem_MouseEnter(object sender, MouseEventArgs e) => - CellMouseEnter?.Invoke((ListViewItemData)((ListViewItem)sender).Content); + CellMouseEnter?.Invoke((ListViewMenuItem)((ListViewItem)sender).Content); private void ListViewItem_MouseLeave(object sender, MouseEventArgs e) => CellMouseLeave?.Invoke(); private void ListViewItem_PreviewMouseDown(object sender, MouseButtonEventArgs e) { - ListViewItemData itemData = (ListViewItemData)((ListViewItem)sender).Content; + ListViewMenuItem itemData = (ListViewMenuItem)((ListViewItem)sender).Content; CellMouseDown?.Invoke(itemData); @@ -1247,215 +1249,6 @@ namespace SystemTrayMenu.UserInterface } private void ListViewItem_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) => - ((ListViewItemData)((ListViewItem)sender).Content).OpenItem(e.ClickCount); - - /// - /// Type for ListView items. - /// - internal class ListViewItemData : INotifyPropertyChanged - { - private Brush? backgroundBrush; - private Brush? borderBrush; - private ImageSource? columnIcon; - private bool isSelected; - - internal ListViewItemData(ImageSource? columnIcon, string columnText, RowData rowData, int sortIndex) - { - this.columnIcon = columnIcon; - ColumnText = columnText; - data = rowData; - SortIndex = sortIndex; - } - - public event PropertyChangedEventHandler? PropertyChanged; - - public Brush? BackgroundBrush - { - get => backgroundBrush; - private set - { - if (value != backgroundBrush) - { - backgroundBrush = value; - CallPropertyChanged(); - } - } - } - - public Brush? BorderBrush - { - get => borderBrush; - private set - { - if (value != borderBrush) - { - borderBrush = value; - CallPropertyChanged(); - } - } - } - - public ImageSource? ColumnIcon - { - get => columnIcon; - set - { - if (value != columnIcon) - { - columnIcon = value; - CallPropertyChanged(); - } - } - } - - public string ColumnText { get; } - - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Benennungsstile", Justification = "Temporarily retained for compatibility reasons")] - [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "Temporarily retained for compatibility reasons")] - internal RowData data { get; set; } - - internal int SortIndex { get; set; } - - internal bool IsPendingOpenItem { get; set; } - - internal bool IsSelected - { - get => isSelected; - set - { - if (value != isSelected) - { - isSelected = value; - CallPropertyChanged(); - } - } - } - - public override string ToString() => nameof(ListViewItemData) + ": " + ColumnText + ", Owner: " + (data.Owner?.ToString() ?? "null"); - - /// - /// Triggers an PropertyChanged event of INotifyPropertyChanged. - /// - /// Name of the changing property. - public void CallPropertyChanged([CallerMemberName] string? propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - - internal void OpenItem(int clickCount) - { - bool doCloseAfterOpen = false; - - if (!data.IsPointingToFolder) - { - if (clickCount == -1 || - (clickCount == 1 && Settings.Default.OpenItemWithOneClick) || - (clickCount == 2 && !Settings.Default.OpenItemWithOneClick)) - { - string? workingDirectory = System.IO.Path.GetDirectoryName(data.ResolvedPath); - Log.ProcessStart(data.Path, string.Empty, false, workingDirectory, true, data.ResolvedPath); - if (!Settings.Default.StaysOpenWhenItemClicked) - { - doCloseAfterOpen = true; - } - } - } - else - { - if (clickCount == -1 || - (clickCount == 1 && Settings.Default.OpenDirectoryWithOneClick) || - (clickCount == 2 && !Settings.Default.OpenDirectoryWithOneClick)) - { - Log.ProcessStart(data.Path); - if (!Settings.Default.StaysOpenWhenItemClicked) - { - doCloseAfterOpen = true; - } - } - } - - if (data.Owner != null) - { - if (clickCount == 1) - { - data.Owner.CellOpenOnClick?.Invoke(this); - } - - if (doCloseAfterOpen) - { - data.Owner.HideAllMenus(); - } - } - } - - internal void OpenShellContextMenu(Point position) - { - if (data.IsPointingToFolder) - { - ShellContextMenu.OpenShellContextMenu(new DirectoryInfo(data.Path), position); - } - else - { - ShellContextMenu.OpenShellContextMenu(data.FileInfo, position); - } - } - - internal void OpenSubMenu() - { - Menu? owner = data.Owner; - - // TODO: always true? maybe only when cached in WaitToLoadMenu or keyboardInput? - if (owner?.GetDataGridView().Items.Contains(this) ?? false) - { - Menu? openSubMenu = owner.SubMenu; - - // only re-open when the menu is not already open - if (data.SubMenu != null && data.SubMenu == openSubMenu) - { - // Close second level sub menus when already opened - openSubMenu.SelectedItem = null; - if (openSubMenu.SubMenu != null) - { - openSubMenu.SubMenu.HideWithFade(true); - openSubMenu.RefreshSelection(); - } - } - else - { - // In case another menu exists, close it - if (openSubMenu != null) - { - // Give the opening window focus - // if closing window lose focus, no window would have focus any more - owner.Activate(); - owner.FocusTextBox(); - openSubMenu.HideWithFade(true); - owner.RefreshSelection(); - } - - if (data.IsPointingToFolder) - { - owner.StartLoadSubMenu?.Invoke(data); - } - } - } - } - - internal void UpdateColors() - { - if (data.SubMenu != null) - { - BorderBrush = MenuDefines.ColorOpenFolderBorder; - BackgroundBrush = MenuDefines.ColorOpenFolder; - } - else if (IsSelected) - { - BorderBrush = MenuDefines.ColorSelectedItemBorder; - BackgroundBrush = MenuDefines.ColorSelectedItem; - } - else - { - BorderBrush = Brushes.White; - BackgroundBrush = Brushes.White; - } - } - } + ((ListViewMenuItem)((ListViewItem)sender).Content).OpenItem(e.ClickCount); } }