Separated ListViewMenuItem from Menu (former ListViewItemData)

This commit is contained in:
Peter Kirmeier 2023-05-18 22:48:18 +02:00
parent 4a11f48f10
commit e9675685ef
5 changed files with 268 additions and 252 deletions

View file

@ -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<ListViewItemData?>? RowSelectionChanged;
internal event Action<ListViewMenuItem?>? RowSelectionChanged;
internal event Action<ListViewItemData>? EnterPressed;
internal event Action<ListViewMenuItem>? 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;

View file

@ -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<RowData> 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<ListViewItemData> rowsToRemove = new();
List<ListViewMenuItem> 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<RowData> rowDatas = new(items.Count + 1) { rowData };
foreach (ListViewItemData item in items)
foreach (ListViewMenuItem item in items)
{
rowDatas.Add(item.data);
}

View file

@ -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<ListViewItemData>? MouseSelect;
internal event Action<ListViewMenuItem>? 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)
{

View file

@ -0,0 +1,224 @@
// <copyright file="ListViewMenuItem.cs" company="PlaceholderCompany">
// Copyright (c) PlaceholderCompany. All rights reserved.
// </copyright>
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;
/// <summary>
/// Type for ListView items.
/// </summary>
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");
/// <summary>
/// Triggers an PropertyChanged event of INotifyPropertyChanged.
/// </summary>
/// <param name="propertyName">Name of the changing property.</param>
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;
}
}
}
}

View file

@ -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<Menu, bool, bool>? SearchTextChanged;
internal event Action<ListViewItemData>? RowSelectionChanged;
internal event Action<ListViewMenuItem>? RowSelectionChanged;
internal event Action<ListViewItemData>? CellMouseEnter;
internal event Action<ListViewMenuItem>? CellMouseEnter;
internal event Action? CellMouseLeave;
internal event Action<ListViewItemData>? CellMouseDown;
internal event Action<ListViewMenuItem>? CellMouseDown;
internal event Action<ListViewItemData>? CellOpenOnClick;
internal event Action<ListViewMenuItem>? 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<ListViewItemData> items = new();
List<ListViewMenuItem> 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);
/// <summary>
/// Type for ListView items.
/// </summary>
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");
/// <summary>
/// Triggers an PropertyChanged event of INotifyPropertyChanged.
/// </summary>
/// <param name="propertyName">Name of the changing property.</param>
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);
}
}