Skip to content

Commit

Permalink
Improve keyboard navigation
Browse files Browse the repository at this point in the history
The Return key was mapped to View Files, but it's better if it acts
the same as double-click, which descends into directories and opens
embedded archives and disk images.

We now put the focus back on the file list after moving to a new
directory or moving up a level.  This allows keyboard navigation
through the directory tree while in single-dir view mode.

(issue #25)
  • Loading branch information
fadden committed Oct 15, 2024
1 parent bfc5ad2 commit 75a4cef
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 21 deletions.
4 changes: 3 additions & 1 deletion cp2_wpf/MainController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,7 @@ public void NavToParent(bool dirOnly) {
return;
}
if (dirSel.Parent != null) {
mSwitchFocusToFileList = true; // keep focus on file list, for keyboard nav
dirSel.Parent.IsSelected = true;
DirectoryTreeItem.BringItemIntoView(mMainWin.directoryTree, dirSel.Parent);
} else if (!dirOnly) {
Expand Down Expand Up @@ -973,7 +974,8 @@ public bool GetFileSelection(bool omitDir, bool omitOpenArc, bool closeOpenArc,
// Get the list of selected items. The behavior is a little strange: if you select
// an item in the middle, and then use select-all, the item you selected will be at
// the top (unless your selection was at the very bottom). Everything else will be
// in the expected order.
// in the expected order. Control-clicking multiple items appears to add them to
// the selection set in the order in which they are clicked.
DataGrid dg = mMainWin.fileListDataGrid;
IList listSel = dg.SelectedItems;
if (listSel.Count == 0) {
Expand Down
28 changes: 19 additions & 9 deletions cp2_wpf/MainController_Panels.cs
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,8 @@ internal void DirectoryTree_SelectionChanged(DirectoryTreeItem? newSel) {
/// should be set for all file operations, but not during archive or directory
/// tree traversal.</param>
internal void RefreshDirAndFileList(bool focusOnFileList = true) {
//Debug.WriteLine("RefreshDirAndFileList: focus=" + focusOnFileList +
// " mSwitch=" + mSwitchFocusToFileList);
mSwitchFocusToFileList |= focusOnFileList;
if (CurrentWorkObject == null) {
return;
Expand Down Expand Up @@ -690,9 +692,11 @@ private bool VerifyFileList() {
/// have strange effects.)</param>
internal void PopulateFileList(IFileEntry selEntry, bool focusOnFileList) {
if (selEntry != IFileEntry.NO_ENTRY) {
Debug.WriteLine("Populate: current item is " + selEntry.FileName);
Debug.WriteLine("Populate: current item is " + selEntry.FileName +
" (focus=" + focusOnFileList + " mSwitch=" + mSwitchFocusToFileList + ")");
} else {
Debug.WriteLine("Populate: no selected item in file list");
Debug.WriteLine("Populate: no selected item in file list " +
" (focus=" + focusOnFileList + " mSwitch=" + mSwitchFocusToFileList + ")");
}
ObservableCollection<FileListItem> fileList = mMainWin.FileList;

Expand Down Expand Up @@ -919,9 +923,14 @@ private void ClearEntryCounts() {
/// <summary>
/// Handles a double-click on an item in the file list grid.
/// </summary>
public void HandleFileListDoubleClick(FileListItem item, int row, int col,
ArchiveTreeItem arcTreeSel, DirectoryTreeItem dirTreeSel) {
//Debug.WriteLine("DCLICK: r=" + row + " c=" + col + " item=" + item);
public void HandleFileListDoubleClick() {
//Debug.WriteLine("HandleFileListDoubleClick");
ArchiveTreeItem? arcTreeSel = mMainWin.SelectedArchiveTreeItem;
DirectoryTreeItem? dirTreeSel = mMainWin.SelectedDirectoryTreeItem;
if (arcTreeSel == null || dirTreeSel == null) {
Debug.Assert(false, "tree is missing selection");
return;
}

//
// Something has been double-clicked. If it's a single entry:
Expand Down Expand Up @@ -958,15 +967,16 @@ public void HandleFileListDoubleClick(FileListItem item, int row, int col,
// or disk image and requires special handling. Otherwise we'll just hand everything
// over to the file viewer.
if (dg.SelectedItems.Count == 1) {
IFileEntry entry = item.FileEntry;
FileListItem fli = (FileListItem)dg.SelectedItems[0]!;
IFileEntry entry = fli.FileEntry;
if (entry.IsDirectory) {
if (fs != null) {
// Select the entry in the dir tree. This may rewrite the file list.
// We want to keep the focus on the file list to support keyboard nav.
mSwitchFocusToFileList = true;
if (!DirectoryTreeItem.SelectItemByEntry(mMainWin, entry)) {
Debug.WriteLine("Unable to find dir tree entry for " + entry);
} else {
// TODO: the focus will move to the directory tree. We may want
// to put it on the first item in the file list instead.
mSwitchFocusToFileList = false;
}
} else {
// Directory file in a file archive. Nothing for us to do.
Expand Down
13 changes: 4 additions & 9 deletions cp2_wpf/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -674,7 +674,8 @@ private void ToggleInfoCmd_Executed(object sender, ExecutedRoutedEventArgs e) {
SetShowCenterInfo(CenterPanelChange.Toggle);
}
private void ViewFilesCmd_Executed(object sender, ExecutedRoutedEventArgs e) {
mMainCtrl.ViewFiles();
//mMainCtrl.ViewFiles();
mMainCtrl.HandleFileListDoubleClick();
}

private void Debug_BulkCompressTestCmd_Executed(object sender, ExecutedRoutedEventArgs e) {
Expand Down Expand Up @@ -1212,14 +1213,8 @@ private void FileList_MouseDoubleClick(object sender, MouseButtonEventArgs e) {
return;
}
FileListItem fli = (FileListItem)item;

ArchiveTreeItem? arcTreeSel = archiveTree.SelectedItem as ArchiveTreeItem;
DirectoryTreeItem? dirTreeSel = directoryTree.SelectedItem as DirectoryTreeItem;
if (arcTreeSel == null || dirTreeSel == null) {
Debug.Assert(false, "tree is missing selection");
return;
}
mMainCtrl.HandleFileListDoubleClick(fli, row, col, arcTreeSel, dirTreeSel);
//Debug.WriteLine("Double-click on " + fli.FileEntry);
mMainCtrl.HandleFileListDoubleClick();
}

/// <summary>
Expand Down
18 changes: 16 additions & 2 deletions cp2_wpf/WPFCommon/WPFExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,8 @@ public static int GetClickEventColumn(this ListView lv, MouseButtonEventArgs e)
/// Helper functions for working with DataGrids.
/// </summary>
/// <remarks>
/// <para>It's tempting to handle double-click actions by using the selected row. This gets a
/// little weird, though, because double-clicking on a header or blank area doesn't
/// <para>It's tempting to simply handle double-click actions by using the selected row. This
/// gets a little weird, though, because double-clicking on a header or blank area doesn't
/// clear the selection.</para>
/// <para>Great explanation of row/cell selection in a DataGrid:
/// <see href="https://blog.magnusmontin.net/2013/11/08/how-to-programmatically-select-and-focus-a-row-or-cell-in-a-datagrid-in-wpf/"/>;
Expand All @@ -194,13 +194,21 @@ public static class DataGridExtensions {
/// Based on
/// <see href="https://blog.scottlogic.com/2008/12/02/wpf-datagrid-detecting-clicked-cell-and-row.html"/>.
/// </remarks>
/// <param name="e">Arguments received from event.</param>
/// <param name="rowIndex">Result: index of clicked row.</param>
/// <param name="colIndex">Result: index of clicked column.</param>
/// <param name="item">Result: if a data item was clicked, the data item; otherwise
/// null.</param>
/// <returns>True if the click was on a data item.</returns>
public static bool GetClickRowColItem(this DataGrid dg, MouseButtonEventArgs e,
out int rowIndex, out int colIndex, [NotNullWhen(true)] out object? item) {
return GetRowColItem(dg, (DependencyObject)e.OriginalSource, out rowIndex,
out colIndex, out item);
}

/// <summary>
/// Determines which row and column was the target of a drop action.
/// </summary>
public static bool GetDropRowColItem(this DataGrid dg, DragEventArgs e,
out int rowIndex, out int colIndex, [NotNullWhen(true)] out object? item) {
return GetRowColItem(dg, (DependencyObject)e.OriginalSource, out rowIndex,
Expand Down Expand Up @@ -370,6 +378,9 @@ public static void ResetSort(this DataGrid grid) {
}
}

/// <summary>
/// Scrolls the DataGrid to the top.
/// </summary>
public static void ScrollToTop(this DataGrid dg) {
VirtualizingStackPanel? virtPanel =
VisualHelper.GetVisualChild<VirtualizingStackPanel>(dg);
Expand Down Expand Up @@ -468,6 +479,9 @@ public static void BringIndexIntoView_Public(this VirtualizingPanel virtPanel, i
/// TreeView extensions.
/// </summary>
public static class TreeViewExtensions {
/// <summary>
/// Scrolls the TreeView to the top.
/// </summary>
public static void ScrollToTop(this TreeView tv) {
VirtualizingStackPanel? virtPanel =
VisualHelper.GetVisualChild<VirtualizingStackPanel>(tv);
Expand Down

0 comments on commit 75a4cef

Please sign in to comment.