Skip to content

Commit dcd056e

Browse files
authored
Improve keyboard navigation in column view (#7425)
1 parent 0762468 commit dcd056e

File tree

8 files changed

+132
-196
lines changed

8 files changed

+132
-196
lines changed

src/Files/UserControls/Selection/RectangleSelection_ListViewBase.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,13 +186,16 @@ private void RectangleSelection_PointerReleased(object sender, PointerRoutedEven
186186
selectionChanged(sender, null);
187187
}
188188
}
189-
if (selectionState == SelectionState.Active)
189+
//if (selectionState == SelectionState.Active)
190190
{
191+
// Always trigger SelectionEnded to focus the file list when clicking on the empty space (#2977)
191192
OnSelectionEnded();
192193
}
193194

194195
selectionStrategy = null;
195196
selectionState = SelectionState.Inactive;
197+
198+
e.Handled = true;
196199
}
197200

198201
private void RectangleSelection_LayoutUpdated(object sender, object e)

src/Files/Views/ColumnShellPage.xaml.cs

Lines changed: 12 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ public sealed partial class ColumnShellPage : Page, IShellPage, INotifyPropertyC
5151
public IFilesystemHelpers FilesystemHelpers { get; private set; }
5252
private CancellationTokenSource cancellationTokenSource;
5353

54-
public bool CanNavigateBackward => ItemDisplayFrame.CanGoBack;
55-
public bool CanNavigateForward => ItemDisplayFrame.CanGoForward;
54+
public bool CanNavigateBackward => false;
55+
public bool CanNavigateForward => false;
5656

5757
public FolderSettingsViewModel FolderSettings => InstanceViewModel?.FolderSettings;
5858

@@ -375,7 +375,8 @@ private void ColumnShellPage_BackRequested(object sender, BackRequestedEventArgs
375375
{
376376
if (IsCurrentInstance)
377377
{
378-
if (ItemDisplayFrame.CanGoBack)
378+
var browser = this.FindAscendant<ColumnViewBrowser>();
379+
if (browser.ParentShellPageInstance.CanNavigateBackward)
379380
{
380381
e.Handled = true;
381382
Back_Click();
@@ -744,51 +745,17 @@ await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPrio
744745

745746
public void Back_Click()
746747
{
747-
NavToolbarViewModel.CanGoBack = false;
748-
if (ItemDisplayFrame.CanGoBack)
749-
{
750-
var previousPageContent = ItemDisplayFrame.BackStack[ItemDisplayFrame.BackStack.Count - 1];
751-
var previousPageNavPath = previousPageContent.Parameter as NavigationArguments;
752-
previousPageNavPath.IsLayoutSwitch = false;
753-
if (previousPageContent.SourcePageType != typeof(WidgetsPage))
754-
{
755-
// Update layout type
756-
InstanceViewModel.FolderSettings.GetLayoutType(previousPageNavPath.IsSearchResultPage ? previousPageNavPath.SearchPathParam : previousPageNavPath.NavPathParam);
757-
}
758-
SelectSidebarItemFromPath(previousPageContent.SourcePageType);
759-
760-
if (previousPageContent.SourcePageType == typeof(WidgetsPage))
761-
{
762-
ItemDisplayFrame.GoBack(new EntranceNavigationTransitionInfo());
763-
}
764-
else
765-
{
766-
ItemDisplayFrame.GoBack();
767-
}
768-
}
748+
this.FindAscendant<ColumnViewBrowser>().NavigateBack();
769749
}
770750

771751
public void Forward_Click()
772752
{
773-
NavToolbarViewModel.CanGoForward = false;
774-
if (ItemDisplayFrame.CanGoForward)
775-
{
776-
var incomingPageContent = ItemDisplayFrame.ForwardStack[ItemDisplayFrame.ForwardStack.Count - 1];
777-
var incomingPageNavPath = incomingPageContent.Parameter as NavigationArguments;
778-
incomingPageNavPath.IsLayoutSwitch = false;
779-
if (incomingPageContent.SourcePageType != typeof(WidgetsPage))
780-
{
781-
// Update layout type
782-
InstanceViewModel.FolderSettings.GetLayoutType(incomingPageNavPath.IsSearchResultPage ? incomingPageNavPath.SearchPathParam : incomingPageNavPath.NavPathParam);
783-
}
784-
SelectSidebarItemFromPath(incomingPageContent.SourcePageType);
785-
ItemDisplayFrame.GoForward();
786-
}
753+
this.FindAscendant<ColumnViewBrowser>().NavigateForward();
787754
}
788755

789756
public void Up_Click()
790757
{
791-
this.FindAscendant<ColumnViewBrowser>().UpColumn();
758+
this.FindAscendant<ColumnViewBrowser>().NavigateUp();
792759
}
793760

794761
private void SelectSidebarItemFromPath(Type incomingSourcePageType = null)
@@ -851,14 +818,17 @@ private void FilesystemViewModel_ItemLoadStatusChanged(object sender, ItemLoadSt
851818
break;
852819

853820
case ItemLoadStatusChangedEventArgs.ItemLoadStatus.InProgress:
854-
NavToolbarViewModel.CanGoBack = ItemDisplayFrame.CanGoBack;
855-
NavToolbarViewModel.CanGoForward = ItemDisplayFrame.CanGoForward;
821+
var browser = this.FindAscendant<ColumnViewBrowser>();
822+
NavToolbarViewModel.CanGoBack = browser.ParentShellPageInstance.CanNavigateBackward;
823+
NavToolbarViewModel.CanGoForward = browser.ParentShellPageInstance.CanNavigateForward;
856824
SetLoadingIndicatorForTabs(true);
857825
break;
858826

859827
case ItemLoadStatusChangedEventArgs.ItemLoadStatus.Complete:
860828
SetLoadingIndicatorForTabs(false);
861829
NavToolbarViewModel.CanRefresh = true;
830+
// Set focus to the file list to allow arrow navigation
831+
ContentPage?.ItemManipulationModel.FocusFileList();
862832
// Select previous directory
863833
if (!string.IsNullOrWhiteSpace(e.PreviousDirectory))
864834
{

src/Files/Views/LayoutModes/ColumnViewBase.xaml.cs

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System;
99
using System.Collections.Generic;
1010
using System.Linq;
11+
using System.Threading.Tasks;
1112
using Windows.Storage;
1213
using Windows.System;
1314
using Windows.UI.Core;
@@ -24,8 +25,8 @@ public sealed partial class ColumnViewBase : BaseLayout
2425
public ColumnViewBase() : base()
2526
{
2627
this.InitializeComponent();
27-
CurrentColumn = this;
2828
var selectionRectangle = RectangleSelection.Create(FileList, SelectionRectangle, FileList_SelectionChanged);
29+
selectionRectangle.SelectionEnded += SelectionRectangle_SelectionEnded;
2930
}
3031

3132
protected override void HookEvents()
@@ -160,6 +161,12 @@ protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
160161
base.OnNavigatingFrom(e);
161162
}
162163

164+
private async void SelectionRectangle_SelectionEnded(object sender, EventArgs e)
165+
{
166+
await Task.Delay(200);
167+
FileList.Focus(FocusState.Programmatic);
168+
}
169+
163170
private async void ReloadItemIcons()
164171
{
165172
ParentShellPageInstance.FilesystemViewModel.CancelExtendedPropertiesLoading();
@@ -288,9 +295,6 @@ public override void Dispose()
288295

289296
#endregion IDisposable
290297

291-
public static ColumnViewBase CurrentColumn;
292-
private ListViewItem listViewItem;
293-
294298
private async void FileList_SelectionChanged(object sender, SelectionChangedEventArgs e)
295299
{
296300
SelectedItems = FileList.SelectedItems.Cast<ListedItem>().Where(x => x != null).ToList();
@@ -339,7 +343,14 @@ private async void FileList_PreviewKeyDown(object sender, KeyRoutedEventArgs e)
339343
{
340344
if (!IsRenamingItem)
341345
{
342-
NavigationHelpers.OpenSelectedItems(ParentShellPageInstance, false);
346+
if (IsItemSelected && SelectedItem.PrimaryItemAttribute == StorageItemTypes.Folder)
347+
{
348+
ItemInvoked?.Invoke(new ColumnParam { NavPathParam = (SelectedItem is ShortcutItem sht ? sht.TargetPath : SelectedItem.ItemPath), ListView = FileList }, EventArgs.Empty);
349+
}
350+
else
351+
{
352+
NavigationHelpers.OpenSelectedItems(ParentShellPageInstance, false);
353+
}
343354
e.Handled = true;
344355
}
345356
}
@@ -374,6 +385,37 @@ private async void FileList_PreviewKeyDown(object sender, KeyRoutedEventArgs e)
374385
// Unfocus the ListView so keyboard shortcut can be handled (alt + shift + "+")
375386
NavToolbar?.Focus(FocusState.Pointer);
376387
}
388+
else if (e.Key == VirtualKey.Up || e.Key == VirtualKey.Down)
389+
{
390+
// If list has only one item, select it on arrow down/up (#5681)
391+
if (!IsItemSelected && FileList.Items.Count == 1)
392+
{
393+
FileList.SelectedIndex = 0;
394+
e.Handled = true;
395+
}
396+
}
397+
else if (e.Key == VirtualKey.Left) // Left arrow: select parent folder (previous column)
398+
{
399+
if (!IsRenamingItem && !ParentShellPageInstance.NavToolbarViewModel.IsEditModeEnabled)
400+
{
401+
if ((ParentShellPageInstance as ColumnShellPage).ColumnParams.Column > 0)
402+
{
403+
FocusManager.TryMoveFocus(FocusNavigationDirection.Previous);
404+
}
405+
e.Handled = true;
406+
}
407+
}
408+
else if (e.Key == VirtualKey.Right) // Right arrow: open selected folder
409+
{
410+
if (!IsRenamingItem && !ParentShellPageInstance.NavToolbarViewModel.IsEditModeEnabled)
411+
{
412+
if (IsItemSelected && SelectedItem.PrimaryItemAttribute == StorageItemTypes.Folder)
413+
{
414+
ItemInvoked?.Invoke(new ColumnParam { NavPathParam = (SelectedItem is ShortcutItem sht ? sht.TargetPath : SelectedItem.ItemPath), ListView = FileList }, EventArgs.Empty);
415+
}
416+
e.Handled = true;
417+
}
418+
}
377419
}
378420

379421
private void FileList_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
@@ -385,7 +427,6 @@ private void FileList_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
385427
{
386428
if (item.PrimaryItemAttribute == StorageItemTypes.Folder)
387429
{
388-
listViewItem = FileList.ContainerFromItem(item) as ListViewItem;
389430
ItemInvoked?.Invoke(new ColumnParam { NavPathParam = (item is ShortcutItem sht ? sht.TargetPath : item.ItemPath), ListView = FileList }, EventArgs.Empty);
390431
}
391432
else
@@ -442,7 +483,6 @@ private void FileList_ItemTapped(object sender, TappedRoutedEventArgs e)
442483

443484
if (item.PrimaryItemAttribute == StorageItemTypes.Folder)
444485
{
445-
listViewItem = FileList.ContainerFromItem(item) as ListViewItem;
446486
ItemInvoked?.Invoke(new ColumnParam { NavPathParam = (item is ShortcutItem sht ? sht.TargetPath : item.ItemPath), ListView = FileList }, EventArgs.Empty);
447487
}
448488
else

src/Files/Views/LayoutModes/ColumnViewBrowser.xaml

Lines changed: 0 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -19,116 +19,13 @@
1919
</icore:EventTriggerBehavior>
2020
</i:Interaction.Behaviors>
2121
<local:BaseLayout.Resources>
22-
<converters:BoolNegationConverter x:Key="BoolNegationConverter" />
23-
<converters:BoolToVisibilityConverter
24-
x:Key="NegatedBoolToVisibilityConverter"
25-
FalseValue="Visible"
26-
TrueValue="Collapsed" />
27-
<converters:EmptyObjectToObjectConverter
28-
x:Key="EmptyObjectToObjectConverter"
29-
EmptyValue="Collapsed"
30-
NotEmptyValue="Visible" />
31-
32-
<converters:BoolToVisibilityConverter
33-
x:Key="BoolToVisibilityConverter"
34-
FalseValue="Collapsed"
35-
TrueValue="Visible" />
36-
37-
<converters1:BoolToSelectionMode x:Key="BoolToSelectionModeConverter" />
3822
<Style TargetType="controls:BladeItem">
3923
<Setter Property="Background" Value="Transparent" />
4024
<Setter Property="TitleBarVisibility" Value="Collapsed" />
4125
<Setter Property="BorderThickness" Value="0,0,1,0" />
4226
<Setter Property="BorderBrush" Value="{ThemeResource ControlStrokeColorDefault}" />
4327
<Setter Property="Width" Value="300" />
4428
</Style>
45-
<Style TargetType="ListViewHeaderItem">
46-
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
47-
<Setter Property="FontSize" Value="{ThemeResource GridViewHeaderItemThemeFontSize}" />
48-
<Setter Property="Background" Value="{ThemeResource GridViewHeaderItemBackground}" />
49-
<Setter Property="Margin" Value="0,0,0,4" />
50-
<Setter Property="Padding" Value="12,8,12,0" />
51-
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
52-
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}" />
53-
<Setter Property="VerticalContentAlignment" Value="Stretch" />
54-
<Setter Property="MinHeight" Value="{ThemeResource GridViewHeaderItemMinHeight}" />
55-
<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}" />
56-
<Setter Property="HorizontalAlignment" Value="Stretch" />
57-
<Setter Property="Template">
58-
<Setter.Value>
59-
<ControlTemplate TargetType="ListViewHeaderItem">
60-
<Grid
61-
x:Name="HeaderItemRootGrid"
62-
Margin="0,0,4,0"
63-
HorizontalAlignment="Stretch"
64-
Background="{TemplateBinding Background}"
65-
BorderBrush="{TemplateBinding BorderBrush}"
66-
BorderThickness="{TemplateBinding BorderThickness}"
67-
CornerRadius="{TemplateBinding CornerRadius}"
68-
PointerCanceled="StackPanel_PointerCanceled"
69-
PointerEntered="StackPanel_PointerEntered"
70-
PointerExited="StackPanel_PointerCanceled"
71-
PointerPressed="RootPanel_PointerPressed"
72-
PointerReleased="StackPanel_PointerCanceled">
73-
<Grid.ColumnDefinitions>
74-
<ColumnDefinition Width="Auto" />
75-
<ColumnDefinition Width="*" />
76-
</Grid.ColumnDefinitions>
77-
<ContentPresenter
78-
x:Name="ContentPresenter"
79-
Grid.Column="0"
80-
Margin="{TemplateBinding Padding}"
81-
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
82-
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
83-
Content="{TemplateBinding Content}"
84-
ContentTemplate="{TemplateBinding ContentTemplate}"
85-
ContentTransitions="{TemplateBinding ContentTransitions}" />
86-
<Rectangle
87-
Grid.Column="1"
88-
Height="1"
89-
HorizontalAlignment="Stretch"
90-
VerticalAlignment="Center"
91-
Stroke="{ThemeResource GridViewHeaderItemDividerStroke}"
92-
StrokeThickness="0.5" />
93-
94-
<VisualStateManager.VisualStateGroups>
95-
<VisualStateGroup x:Name="CommonStates">
96-
<VisualState x:Name="Normal" />
97-
98-
<VisualState x:Name="PointerOver">
99-
<Storyboard>
100-
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HeaderItemRootGrid" Storyboard.TargetProperty="Background">
101-
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonBackgroundPointerOver}" />
102-
</ObjectAnimationUsingKeyFrames>
103-
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HeaderItemRootGrid" Storyboard.TargetProperty="BorderBrush">
104-
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonBorderBrushPointerOver}" />
105-
</ObjectAnimationUsingKeyFrames>
106-
</Storyboard>
107-
<VisualState.Setters>
108-
<!--<Setter Target="ContentPresenter.(local:AnimatedIcon.State)" Value="PointerOver" />-->
109-
</VisualState.Setters>
110-
</VisualState>
111-
112-
<VisualState x:Name="Pressed">
113-
<Storyboard>
114-
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HeaderItemRootGrid" Storyboard.TargetProperty="Background">
115-
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonBackgroundPressed}" />
116-
</ObjectAnimationUsingKeyFrames>
117-
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HeaderItemRootGrid" Storyboard.TargetProperty="BorderBrush">
118-
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonBorderBrushPressed}" />
119-
</ObjectAnimationUsingKeyFrames>
120-
</Storyboard>
121-
<VisualState.Setters>
122-
<!--<Setter Target="ContentPresenter.(local:AnimatedIcon.State)" Value="Pressed" />-->
123-
</VisualState.Setters>
124-
</VisualState>
125-
</VisualStateGroup>
126-
</VisualStateManager.VisualStateGroups>
127-
</Grid>
128-
</ControlTemplate>
129-
</Setter.Value>
130-
</Setter>
131-
</Style>
13229
</local:BaseLayout.Resources>
13330
<Grid x:Name="RootGrid" ContextFlyout="{x:Bind BaseContextMenuFlyout}">
13431
<controls:BladeView x:Name="ColumnHost">

0 commit comments

Comments
 (0)