Simplified and improved selection via keys

Reduce complexity of key handlers
Combine deselect and select events
This commit is contained in:
Peter Kirmeier 2023-05-12 01:13:02 +02:00
parent d8a146e8ed
commit 5bd2c44930
4 changed files with 113 additions and 179 deletions

View file

@ -18,15 +18,12 @@ namespace SystemTrayMenu.Handler
private readonly KeyboardHook hook = new();
private Menu? focussedMenu;
private ListViewItemData? focussedRow;
internal event Action? HotKeyPressed;
internal event Action? ClosePressed;
internal event Action<Menu, ListViewItemData>? RowSelected;
internal event Action? RowDeselected;
internal event Action<Menu?>? RowSelectionChanged;
internal event Action<Menu, ListViewItemData>? EnterPressed;
@ -56,11 +53,7 @@ namespace SystemTrayMenu.Handler
return true;
}
internal void ResetSelectedByKey()
{
focussedMenu = null;
focussedRow = null;
}
internal void ResetSelectedByKey() => focussedMenu = null;
internal void CmdKeyProcessed(Menu sender, Key key, ModifierKeys modifiers)
{
@ -86,13 +79,6 @@ namespace SystemTrayMenu.Handler
SelectByKey(key, modifiers);
}
break;
case Key.F4:
if (modifiers == ModifierKeys.Alt)
{
SelectByKey(key, modifiers);
}
break;
case Key.F:
if (modifiers == ModifierKeys.Control)
@ -128,18 +114,13 @@ namespace SystemTrayMenu.Handler
case Key.Apps:
if (modifiers == ModifierKeys.None)
{
ListView? dgv = focussedMenu?.GetDataGridView();
if (dgv != null)
Menu? menu = focussedMenu;
ListViewItemData? itemData = menu?.SelectedItem;
if (menu != null && itemData != null)
{
if (focussedRow != null)
{
#if TODO // WPF: Better way to open context menu (as it looks like this is the code's intention)
Point point = dgv.GetCellDisplayRectangle(2, iRowKey, false).Location;
RowData trigger = (RowData)dgv.Rows[iRowKey].Cells[2].Value;
MouseEventArgs mouseEventArgs = new(MouseButtons.Right, 1, point.X, point.Y, 0);
trigger.MouseDown(dgv, mouseEventArgs);
#endif
}
var position = Mouse.GetPosition(menu);
position.Offset(menu.Left, menu.Top);
itemData.OpenShellContextMenu(position);
}
}
@ -178,24 +159,24 @@ namespace SystemTrayMenu.Handler
DeselectFoccussedRow();
focussedMenu = menu;
Select(menu.GetDataGridView(), itemData);
menu.GetDataGridView().SelectedItem = itemData;
}
private void SelectByKey(Key key, ModifierKeys modifiers)
{
Menu? menuFromSelected;
Menu? menuBefore;
ListViewItemData? rowBefore = focussedRow;
bool wasSelected = focussedRow?.IsSelected ?? false;
ListViewItemData? rowBefore = focussedMenu?.SelectedItem;
bool wasSelected = rowBefore != null;
if (wasSelected)
{
menuFromSelected = focussedRow?.data.SubMenu;
menuFromSelected = rowBefore!.data.SubMenu;
menuBefore = focussedMenu;
}
else
{
ResetSelectedByKey();
focussedMenu = null;
menuFromSelected = null;
menuBefore = null;
}
@ -228,8 +209,7 @@ namespace SystemTrayMenu.Handler
case Key.Up:
if ((modifiers == ModifierKeys.None) &&
menuBefore != null &&
(TrySelectPrevious(menuBefore, menuBefore.GetDataGridView().Items.IndexOf(rowBefore)) ||
TrySelectPrevious(menuBefore, menuBefore.GetDataGridView().Items.Count - 1)))
menuBefore.TrySelectAt(menuBefore.GetDataGridView().Items.IndexOf(menuBefore.SelectedItem) - 1, menuBefore.GetDataGridView().Items.Count - 1))
{
RaiseRowSelectionChanged();
}
@ -238,8 +218,7 @@ namespace SystemTrayMenu.Handler
case Key.Down:
if ((modifiers == ModifierKeys.None) &&
menuBefore != null &&
(TrySelectNext(menuBefore, menuBefore.GetDataGridView().Items.IndexOf(rowBefore)) ||
TrySelectNext(menuBefore, 0)))
menuBefore.TrySelectAt(menuBefore.GetDataGridView().Items.IndexOf(menuBefore.SelectedItem) + 1, 0))
{
RaiseRowSelectionChanged();
}
@ -248,7 +227,7 @@ namespace SystemTrayMenu.Handler
case Key.Home:
if ((modifiers == ModifierKeys.None) &&
menuBefore != null &&
TrySelectNext(menuBefore, 0))
menuBefore.TrySelectAt(0, -1))
{
RaiseRowSelectionChanged();
}
@ -257,7 +236,7 @@ namespace SystemTrayMenu.Handler
case Key.End:
if ((modifiers == ModifierKeys.None) &&
menuBefore != null &&
TrySelectPrevious(menuBefore, menuBefore.GetDataGridView().Items.Count - 1))
menuBefore.TrySelectAt(menuBefore.GetDataGridView().Items.Count - 1, -1))
{
RaiseRowSelectionChanged();
}
@ -269,54 +248,48 @@ namespace SystemTrayMenu.Handler
menuBefore != null &&
focussedMenu != null)
{
// True, when next is left and key is left = true OR next is right (=not left) and key is right (not left)
bool nextMenuInKeyDirection = (focussedMenu?.SubMenu?.Location.X < focussedMenu?.Location.X) == (key == Key.Left);
Menu? next = focussedMenu.SubMenu;
Menu? prev = focussedMenu.ParentMenu;
bool nextLeft = next != null && next.Location.X < focussedMenu.Location.X;
bool prevLeft = prev != null && prev.Location.X < focussedMenu.Location.X;
// TODO: Check what this actually does as it is only true for wrap arounds on screen corners
// but why not simply just select prev menu instead?
// True, when prev is right (=not left) but key is left = true OR prev is left but key is right (not left)
bool prevMenuAgainstKeyDirection = (focussedMenu?.Location.X < focussedMenu?.ParentMenu?.Location.X) == (key == Key.Left);
if (nextMenuInKeyDirection || prevMenuAgainstKeyDirection)
// P = Parent, N = Next ..
// Menues come from right in examples
//
// Key Pressed: <-
// [N][ ][P] - Select next as in key direction
// [ ][P/N] - Select prev going left as going right would select next
// [ ][P] - don't do anything
// [N][ ] - Select next as in key direction
// [P/N][ ] - Select next as next has priority
//
// Key Pressed: ->
// [N][ ][P] - Select prev as in key direction
// [ ][P/N] - Select next as next has priority
// [ ][P] - Select prev as in key direction
// [N][ ] - don't do anything
// [P/N][ ] - Select prev going right as going left would select next
if (next != null && nextLeft == (key == Key.Left))
{
// Next is in key direction or prev is opposite of key direction ==> Select sub/next menu
if (wasSelected)
// Select next sub menu, when next exists and
// next and key point in the same direction
if (next!.TrySelectAt(0, -1))
{
if (menuFromSelected != null &&
menuFromSelected == focussedMenu?.SubMenu)
{
focussedMenu = menuFromSelected;
focussedRow = null;
if (TrySelectNext(menuFromSelected, 0))
{
RaiseRowSelectionChanged();
}
}
}
else
{
while (focussedMenu?.SubMenu != null)
{
focussedMenu = focussedMenu.SubMenu;
}
focussedRow = null;
Menu? lastMenu = focussedMenu;
if (lastMenu != null && TrySelectNext(lastMenu, 0))
{
RaiseRowSelectionChanged();
}
focussedMenu = next;
RaiseRowSelectionChanged();
}
}
else if (focussedMenu?.ParentMenu != null)
else if (prev != null &&
((prevLeft == (key == Key.Left)) ||
(next != null && nextLeft == prevLeft)))
{
// Next is in opposite key direction and prev is in key direction ==> Select parent/prev menu
// Select previous/parent menu, when prev exists and
// either prev and key point in the same direction
// or when next exists while overlapping with prev
int index = focussedMenu.RowDataParent?.RowIndex ?? -1;
focussedMenu = focussedMenu.ParentMenu;
focussedRow = null;
if (TrySelectNext(focussedMenu, index) ||
TrySelectNext(focussedMenu, 0))
if (focussedMenu.TrySelectAt(index, 0))
{
focussedMenu = focussedMenu.ParentMenu;
RaiseRowSelectionChanged();
}
}
@ -324,22 +297,11 @@ namespace SystemTrayMenu.Handler
break;
case Key.Escape:
case Key.F4:
if ((key == Key.Escape && modifiers == ModifierKeys.None) ||
(key == Key.F4 && modifiers == ModifierKeys.Alt))
if (modifiers == ModifierKeys.None)
{
ResetSelectedByKey();
if (menuBefore != null)
{
RaiseRowSelectionChanged();
}
focussedMenu = null;
RaiseRowSelectionChanged();
ClosePressed?.Invoke();
if (focussedMenu != null)
{
focussedMenu.SelectedItem = null;
}
}
break;
@ -350,68 +312,12 @@ namespace SystemTrayMenu.Handler
private void RaiseRowSelectionChanged()
{
RowDeselected?.Invoke();
RowSelectionChanged?.Invoke(focussedMenu);
if (focussedMenu != null && focussedRow != null)
if (focussedMenu?.SelectedItem != null)
{
IsSelectedByKey = true;
RowSelected?.Invoke(focussedMenu, focussedRow);
}
}
private bool TrySelectNext(Menu menu, int indexStart)
{
bool found = false;
if (indexStart >= 0)
{
ListView dgv = menu.GetDataGridView();
for (uint i = (uint)indexStart; i < dgv.Items.Count; i++)
{
ListViewItemData itemData = (ListViewItemData)dgv.Items[(int)i];
if (itemData != focussedRow)
{
Select(dgv, itemData);
dgv.ScrollIntoView(itemData);
found = true;
break;
}
}
}
return found;
}
private bool TrySelectPrevious(Menu menu, int indexStart)
{
bool found = false;
if (indexStart > 0)
{
ListView dgv = menu.GetDataGridView();
if (dgv.Items.Count <= indexStart)
{
indexStart = dgv.Items.Count - 1;
}
for (int i = indexStart; i > -1; i--)
{
ListViewItemData itemData = (ListViewItemData)dgv.Items[i];
if (itemData != focussedRow)
{
Select(dgv, itemData);
dgv.ScrollIntoView(itemData);
found = true;
break;
}
}
}
return found;
}
private void Select(ListView dgv, ListViewItemData itemData)
{
focussedRow = itemData;
dgv.SelectedItem = itemData;
}
}
}

View file

@ -54,9 +54,8 @@ namespace SystemTrayMenu.Business
keyboardInput.HotKeyPressed += () => SwitchOpenClose(false, false);
keyboardInput.ClosePressed += MenusFadeOut;
keyboardInput.RowDeselected += waitToOpenMenu.RowDeselected;
keyboardInput.RowSelectionChanged += waitToOpenMenu.RowSelectionChanged;
keyboardInput.EnterPressed += waitToOpenMenu.EnterOpensInstantly;
keyboardInput.RowSelected += waitToOpenMenu.RowSelectedByKey;
workerMainMenu.WorkerSupportsCancellation = true;
workerMainMenu.DoWork += LoadMenu;
@ -362,6 +361,17 @@ namespace SystemTrayMenu.Business
return menu;
}
private static void HideOldMenu(Menu menuToShow)
{
Menu? menuPrevious = menuToShow.ParentMenu;
if (menuPrevious != null)
{
menuPrevious.SubMenu?.HideWithFade(true);
menuPrevious.RefreshSelection();
}
}
private static void LoadMenu(object? sender, DoWorkEventArgs eDoWork)
{
BackgroundWorker? workerSelf = sender as BackgroundWorker;
@ -635,17 +645,6 @@ namespace SystemTrayMenu.Business
});
}
private void HideOldMenu(Menu menuToShow)
{
Menu? menuPrevious = menuToShow.ParentMenu;
if (menuPrevious != null)
{
menuPrevious.SubMenu?.HideWithFade(true);
menuPrevious.RefreshSelection();
}
}
private void FadeHalfOrOutIfNeeded()
{
if (IsMainUsable)

View file

@ -53,14 +53,13 @@ namespace SystemTrayMenu.Handler
}
}
internal void RowSelectedByKey(Menu menu, ListViewItemData itemData)
internal void RowSelectionChanged(Menu? menu)
{
timerStartLoad.Stop();
StopLoadMenu?.Invoke();
SetData(menu, itemData);
MouseActive = false;
checkForMouseActive = false;
timerStartLoad.Start();
RowDeselected();
if (menu?.SelectedItem != null)
{
RowSelected(menu, menu.SelectedItem);
}
}
internal void MouseLeave()
@ -73,14 +72,6 @@ namespace SystemTrayMenu.Handler
}
}
internal void RowDeselected()
{
timerStartLoad.Stop();
StopLoadMenu?.Invoke();
ResetData();
MouseActive = false;
}
internal void ClickOpensInstantly(Menu menu, ListViewItemData itemData)
{
timerStartLoad.Stop();
@ -134,6 +125,24 @@ namespace SystemTrayMenu.Handler
}
}
private void RowDeselected()
{
timerStartLoad.Stop();
StopLoadMenu?.Invoke();
ResetData();
MouseActive = false;
}
private void RowSelected(Menu menu, ListViewItemData itemData)
{
timerStartLoad.Stop();
StopLoadMenu?.Invoke();
SetData(menu, itemData);
MouseActive = false;
checkForMouseActive = false;
timerStartLoad.Start();
}
private void SetData(Menu menu, ListViewItemData itemData)
{
if (currentMenu != menu || currentItemData != itemData)

View file

@ -272,7 +272,7 @@ namespace SystemTrayMenu.UserInterface
TopRight,
}
public Point Location => new (Left, Top); // TODO WPF Replace Forms wrapper
internal Point Location => new (Left, Top); // TODO WPF Replace Forms wrapper
internal int Level { get; set; }
@ -403,6 +403,26 @@ namespace SystemTrayMenu.UserInterface
// TODO: Check if it is implicitly already running due to SelectionChanged event
internal void RefreshSelection() => ListView_SelectionChanged(GetDataGridView(), null);
internal bool TrySelectAt(int index, int indexAlternative = -1)
{
if (index >= 0 && dgv.Items.Count > index)
{
ListViewItemData itemData = (ListViewItemData)dgv.Items[index];
dgv.SelectedItem = itemData;
dgv.ScrollIntoView(itemData);
return true;
}
else if (indexAlternative >= 0 && dgv.Items.Count > indexAlternative)
{
ListViewItemData itemData = (ListViewItemData)dgv.Items[indexAlternative];
dgv.SelectedItem = itemData;
dgv.ScrollIntoView(itemData);
return true;
}
return false;
}
internal void AddItemsToMenu(List<RowData> data, MenuDataDirectoryState? state, bool startIconLoading)
{
int foldersCount = 0;