-
-
Notifications
You must be signed in to change notification settings - Fork 543
Feature/multiple selection 407 #740
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
b116d65
14d3416
19b9abe
3ea4dfc
ee7b998
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,7 @@ | |
| xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | ||
| xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" | ||
| xmlns:properties="clr-namespace:EverythingToolbar.Properties" | ||
| xmlns:local="clr-namespace:EverythingToolbar" | ||
| xmlns:d="http://schemas.microsoft.com/expression/blend/2008" | ||
| mc:Ignorable="d" | ||
| d:DesignWidth="300" | ||
|
|
@@ -128,7 +129,6 @@ | |
|
|
||
| <Grid> | ||
| <ListView Name="SearchResultsListView" | ||
| SelectionMode="Single" | ||
| SelectedItem="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=SelectedSearchResult, Mode=TwoWay}" | ||
| BorderThickness="0" | ||
| HorizontalContentAlignment="Stretch" | ||
|
|
@@ -146,7 +146,18 @@ | |
| ScrollViewer.VerticalScrollBarVisibility="Visible" | ||
| ScrollViewer.HorizontalScrollBarVisibility="Disabled" | ||
| ScrollViewer.PanningMode="VerticalOnly" | ||
| FocusVisualStyle="{x:Null}" /> | ||
| FocusVisualStyle="{x:Null}"> | ||
| <ListView.Style> | ||
| <Style TargetType="{x:Type ListView}"> | ||
| <Setter Property="SelectionMode" Value="Extended" /> | ||
| <Style.Triggers> | ||
| <DataTrigger Binding="{Binding Source={x:Static local:ToolbarSettings.User}, Path=IsSelectionModeEnabled}" Value="True"> | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think semantically it would make more sense to make this a property of the SearchResultsView instead of a setting since this is not a user setting but rather a runtime value. You should still be able to bind to it. |
||
| <Setter Property="SelectionMode" Value="Multiple" /> | ||
| </DataTrigger> | ||
| </Style.Triggers> | ||
| </Style> | ||
| </ListView.Style> | ||
| </ListView> | ||
| <Grid x:Name="SpinnerOverlay" | ||
| Visibility="Collapsed" | ||
| IsHitTestVisible="False" | ||
|
|
@@ -176,4 +187,4 @@ | |
| </Viewbox> | ||
| </Grid> | ||
| </Grid> | ||
| </UserControl> | ||
| </UserControl> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -47,6 +47,12 @@ public SearchResult? SelectedSearchResult | |
| } | ||
|
|
||
| private SearchResult? SelectedItem => SelectedSearchResult; | ||
|
|
||
| private IEnumerable<SearchResult> GetSelectedItems() | ||
| { | ||
| return SearchResultsListView.SelectedItems.Cast<SearchResult>(); | ||
| } | ||
|
|
||
| private const int PageSize = 256; | ||
| private Point _dragStart; | ||
| private bool _isScrollBarDragging; | ||
|
|
@@ -219,7 +225,6 @@ private void ResetScrollBarDragging() | |
|
|
||
| private void OnPreviewLeftMouseButtonDown(object sender, MouseButtonEventArgs e) | ||
| { | ||
| // Prevents deselecting an item when Ctrl is held down and clicking on an already selected item | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These comments were probably removed by accident? It happened at multiple places throughout the file. |
||
| if (Keyboard.Modifiers == ModifierKeys.Control) | ||
| { | ||
| if (e.OriginalSource is not DependencyObject source) | ||
|
|
@@ -244,10 +249,10 @@ private void OnKeyPressed(object? sender, KeyEventArgs e) | |
| } | ||
| else if (Keyboard.Modifiers == ModifierKeys.Shift && e.Key == Key.Enter) | ||
| { | ||
| if (SelectedItem == null) | ||
| return; | ||
| var first = GetSelectedItems().FirstOrDefault(); | ||
| if (first == null) return; | ||
|
|
||
| SearchResultProvider.OpenSearchInEverything(SearchState.Instance, SelectedItem.FullPathAndFileName); | ||
| SearchResultProvider.OpenSearchInEverything(SearchState.Instance, first.FullPathAndFileName); | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not sure Everything can be launched with multiple files selected and launching with the first selected file seems a little arbitrary. I think the best approach would be to disable this feature while multiple files are selected, since it doesn't seem to be a supported feature of Everything. This would require disabling the context menu entry as well so it is consistent in the UI. |
||
| SearchResultsListView.SelectedIndex = -1; | ||
| } | ||
| else if (Keyboard.Modifiers == ModifierKeys.Control && e.Key == Key.Enter) | ||
|
|
@@ -274,11 +279,14 @@ private void OnKeyPressed(object? sender, KeyEventArgs e) | |
| } | ||
| else if (Keyboard.Modifiers == (ModifierKeys.Control | ModifierKeys.Shift) && e.Key == Key.C) | ||
| { | ||
| SelectedItem?.CopyPathToClipboard(); | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The two methods |
||
| var paths = string.Join(Environment.NewLine, GetSelectedItems().Select(i => i.FullPathAndFileName)); | ||
| if (!string.IsNullOrEmpty(paths)) Clipboard.SetText(paths); | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could call |
||
| } | ||
| else if (Keyboard.Modifiers == ModifierKeys.Control && e.Key == Key.C) | ||
| { | ||
| SelectedItem?.CopyToClipboard(); | ||
| var paths = new StringCollection(); | ||
| foreach (var item in GetSelectedItems()) paths.Add(item.FullPathAndFileName); | ||
| if (paths.Count > 0) Clipboard.SetFileDropList(paths); | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above: Use |
||
| } | ||
| else if (e.Key == Key.Up) | ||
| { | ||
|
|
@@ -354,7 +362,6 @@ private void SelectNthSearchResult(int n) | |
|
|
||
| private void JumpToEnd() | ||
| { | ||
| // Capture focus before calling Focus() on the ListView so we can restore it afterwards. | ||
| var originalFocus = Keyboard.FocusedElement; | ||
| SearchResultsListView.Focus(); | ||
| ForwardKeyPressToControl(SearchResultsListView, Key.End, originalFocus, restoreFocus: KeepSearchBoxFocused); | ||
|
|
@@ -430,7 +437,6 @@ private bool ForwardKeyPressToControl( | |
| if (presentationSource == null) | ||
| return false; | ||
|
|
||
| // Capture focus state before raising the event | ||
| originalFocus ??= Keyboard.FocusedElement; | ||
| var caretIndex = originalFocus is TextBox textBox ? textBox.CaretIndex : -1; | ||
|
|
||
|
|
@@ -440,7 +446,6 @@ private bool ForwardKeyPressToControl( | |
| }; | ||
| control.RaiseEvent(args); | ||
|
|
||
| // Restore focus to SearchBox if requested and it was previously focused | ||
| if (restoreFocus && originalFocus is TextBox restoredTextBox && caretIndex >= 0) | ||
| { | ||
| Dispatcher.BeginInvoke( | ||
|
|
@@ -460,51 +465,69 @@ private bool ForwardKeyPressToControl( | |
|
|
||
| private void OpenSelectedSearchResult() | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: Rename the method to |
||
| { | ||
| if (SelectedItem == null) | ||
| return; | ||
|
|
||
| if (!CustomActions.HandleAction(SelectedItem)) | ||
| SelectedItem?.Open(); | ||
| var items = GetSelectedItems().ToList(); | ||
| if (items.Count == 0) return; | ||
|
|
||
| foreach (var item in items) | ||
| { | ||
| if (!CustomActions.HandleAction(item)) | ||
| item.Open(); | ||
| } | ||
| SearchWindow.Instance.Hide(); | ||
| } | ||
|
|
||
| private void OpenFilePath(object sender, RoutedEventArgs e) | ||
| { | ||
| SelectedItem?.OpenPath(); | ||
| foreach (var item in GetSelectedItems()) | ||
| item.OpenPath(); | ||
| SearchWindow.Instance.Hide(); | ||
| } | ||
|
|
||
| private void PreviewSelectedFile() | ||
| { | ||
| SelectedItem?.PreviewInQuickLook(); | ||
| SelectedItem?.PreviewInSeer(); | ||
| var first = GetSelectedItems().FirstOrDefault(); | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, not a huge fan of falling back to the first selected item. Maybe QuickLook or Seer even support showing multiple files at once? I can check this later. |
||
| first?.PreviewInQuickLook(); | ||
| first?.PreviewInSeer(); | ||
| } | ||
|
|
||
| private void CopyPathToClipBoard(object sender, RoutedEventArgs e) | ||
| { | ||
| SelectedItem?.CopyPathToClipboard(); | ||
| var paths = string.Join(Environment.NewLine, GetSelectedItems().Select(i => i.FullPathAndFileName)); | ||
| if (!string.IsNullOrEmpty(paths)) | ||
| Clipboard.SetText(paths); | ||
| } | ||
|
|
||
| private void OpenWith(object sender, RoutedEventArgs e) | ||
| { | ||
| SelectedItem?.OpenWith(); | ||
| foreach (var item in GetSelectedItems()) | ||
| item.OpenWith(); | ||
| SearchWindow.Instance.Hide(); | ||
| } | ||
|
|
||
| private void ShowInEverything(object sender, RoutedEventArgs e) | ||
| { | ||
| SelectedItem?.ShowInEverything(); | ||
| foreach (var item in GetSelectedItems()) | ||
| item.ShowInEverything(); | ||
| SearchWindow.Instance.Hide(); | ||
| } | ||
|
|
||
| private void CopyFile(object sender, RoutedEventArgs e) | ||
| { | ||
| SelectedItem?.CopyToClipboard(); | ||
| var paths = new StringCollection(); | ||
| foreach (var item in GetSelectedItems()) paths.Add(item.FullPathAndFileName); | ||
|
|
||
| if (paths.Count > 0) | ||
| Clipboard.SetFileDropList(paths); | ||
| } | ||
|
|
||
| private void SingleClickSearchResult(object sender, MouseEventArgs e) | ||
| { | ||
| if (ToolbarSettings.User.IsSelectionModeEnabled) | ||
| return; | ||
|
|
||
| if (Keyboard.Modifiers.HasFlag(ModifierKeys.Control) || Keyboard.Modifiers.HasFlag(ModifierKeys.Shift)) | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is the check for modifiers needed here? |
||
| return; | ||
|
|
||
| if (!ToolbarSettings.User.IsDoubleClickToOpen) | ||
| OpenWithMouseClick(); | ||
| } | ||
|
|
@@ -525,15 +548,7 @@ private void OpenWithMouseClick() | |
| switch (Keyboard.Modifiers) | ||
| { | ||
| case ModifierKeys.Alt: | ||
| SelectedItem?.ShowProperties(); | ||
| SearchWindow.Instance.Hide(); | ||
| break; | ||
| case ModifierKeys.Control: | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why were these removed? |
||
| SelectedItem?.OpenPath(); | ||
| SearchWindow.Instance.Hide(); | ||
| break; | ||
| case ModifierKeys.Shift: | ||
| SelectedItem?.ShowInEverything(); | ||
| foreach (var item in GetSelectedItems()) item.ShowProperties(); | ||
| SearchWindow.Instance.Hide(); | ||
| break; | ||
| default: | ||
|
|
@@ -545,19 +560,21 @@ private void OpenWithMouseClick() | |
|
|
||
| private void RunAsAdmin(object sender, RoutedEventArgs e) | ||
| { | ||
| SelectedItem?.RunAsAdmin(); | ||
| foreach (var item in GetSelectedItems()) | ||
| item.RunAsAdmin(); | ||
| SearchWindow.Instance.Hide(); | ||
| } | ||
|
|
||
| private void ShowFileProperties(object sender, RoutedEventArgs e) | ||
| { | ||
| SelectedItem?.ShowProperties(); | ||
| foreach (var item in GetSelectedItems()) | ||
| item.ShowProperties(); | ||
| SearchWindow.Instance.Hide(); | ||
| } | ||
|
|
||
| private void ShowFileWindowsContextMenu(object sender, RoutedEventArgs e) | ||
| { | ||
| SelectedItem?.ShowWindowsContextMenu(); | ||
| SearchResult.ShowWindowsContextMenu(GetSelectedItems()); | ||
| } | ||
|
|
||
| private void OnOpenWithMenuLoaded(object sender, RoutedEventArgs e) | ||
|
|
@@ -601,12 +618,16 @@ private void OnOpenWithMenuLoaded(object sender, RoutedEventArgs e) | |
|
|
||
| private void OpenWithCustomAction(object sender, RoutedEventArgs e) | ||
| { | ||
| if (SelectedItem == null) | ||
| return; | ||
| var items = GetSelectedItems().ToList(); | ||
| if (items.Count == 0) return; | ||
|
|
||
| var menuItem = sender as MenuItem; | ||
| var command = menuItem?.Tag?.ToString() ?? ""; | ||
| CustomActions.HandleAction(SelectedItem, command); | ||
|
|
||
| foreach (var item in items) | ||
| { | ||
| CustomActions.HandleAction(item, command); | ||
| } | ||
| } | ||
|
|
||
| private void OnListViewItemMouseDown(object sender, MouseButtonEventArgs e) | ||
|
|
@@ -645,7 +666,8 @@ private void OnListViewItemTouchUp(object sender, TouchEventArgs e) | |
|
|
||
| private bool TryStartDragDrop(Point currentPosition) | ||
| { | ||
| if (SelectedItem == null) | ||
| var items = GetSelectedItems().ToList(); | ||
| if (items.Count == 0) | ||
| return false; | ||
|
|
||
| var diff = _dragStart - currentPosition; | ||
|
|
@@ -656,9 +678,9 @@ private bool TryStartDragDrop(Point currentPosition) | |
| ) | ||
| return false; | ||
|
|
||
| string[] files = [SelectedItem.FullPathAndFileName]; | ||
| string[] files = items.Select(i => i.FullPathAndFileName).ToArray(); | ||
| var data = new DataObject(DataFormats.FileDrop, files); | ||
| data.SetData(DataFormats.Text, files[0]); | ||
| data.SetData(DataFormats.Text, string.Join(Environment.NewLine, files)); | ||
| DragDrop.DoDragDrop(SearchResultsListView, data, DragDropEffects.All); | ||
| return true; | ||
| } | ||
|
|
@@ -670,7 +692,7 @@ private void OnContextMenuOpening(object sender, ContextMenuEventArgs e) | |
|
|
||
| if (Keyboard.Modifiers == ModifierKeys.Shift) | ||
| { | ||
| SelectedItem.ShowWindowsContextMenu(); | ||
| SearchResult.ShowWindowsContextMenu(GetSelectedItems()); | ||
| e.Handled = true; | ||
| } | ||
| } | ||
|
|
@@ -700,4 +722,4 @@ private void FocusSelectedItem() | |
| Keyboard.Focus(selectedItem); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -286,9 +286,22 @@ public void ShowProperties() | |
|
|
||
| public void ShowWindowsContextMenu() | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method is not used anymore and can be removed. |
||
| { | ||
| ShowWindowsContextMenu(new[] { this }); | ||
| } | ||
|
|
||
| public static void ShowWindowsContextMenu(IEnumerable<SearchResult> items) | ||
| { | ||
| var itemsList = items.ToList(); | ||
| if (itemsList.Count == 0) return; | ||
|
|
||
| var firstItemDir = itemsList[0].Path; | ||
| var validItems = itemsList.Where(i => string.Equals(i.Path, firstItemDir, StringComparison.OrdinalIgnoreCase)).ToList(); | ||
|
|
||
| var menu = new ShellContextMenu(); | ||
| var arrFi = new FileInfo[1]; | ||
| arrFi[0] = new FileInfo(FullPathAndFileName); | ||
| var arrFi = validItems.Select<SearchResult, FileSystemInfo>(i => | ||
| i.IsFile ? new FileInfo(i.FullPathAndFileName) : new DirectoryInfo(i.FullPathAndFileName) | ||
| ).ToArray(); | ||
|
|
||
| menu.ShowContextMenu(arrFi, Control.MousePosition); | ||
| } | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think placing the toggle button in the optional quick actions bar is not a good idea. Its visibility depends on whether the quick action are shown or not.
In general I am not sure about having a toggle button for this. Isn't the typical flow in mobile apps to long press any item to switch to a multi-select mode where the user can tap multiple items to select or unselect and then press the back button to cancel the selection?
My suggestion would be to switch to the multi-selection mode by long pressing the first item on touch devices. While this mode is active, show a "Unselect all" button in the main toolbar to the left of the sort by button which unselects all items and switches off the multi-select mode. For regular desktop mode I don't think any UI changes are needed and regular multiselect can be used.
What do you think?