diff --git a/src/Sic/MainWindow.Designer.cs b/src/Sic/MainWindow.Designer.cs index 0740829..c427573 100644 --- a/src/Sic/MainWindow.Designer.cs +++ b/src/Sic/MainWindow.Designer.cs @@ -119,7 +119,7 @@ private void InitializeComponent() { } imageListView.Dock = DockStyle.Fill; - imageListView.MultiSelect = false; + imageListView.MultiSelect = true; imageListView.Name = "imageListView"; imageListView.AccessibleName = "Images list"; imageListView.AllowDrop = true; diff --git a/src/Sic/MainWindow.cs b/src/Sic/MainWindow.cs index 4e7ff64..9bf1d9d 100644 --- a/src/Sic/MainWindow.cs +++ b/src/Sic/MainWindow.cs @@ -401,13 +401,34 @@ private async void AddByLinkMenuItem_Click(object? sender, EventArgs e) { await PasteFromUrlAsync(dialog.Url); } + /// + /// The row a single-image command should act on: the one carrying the focus rectangle when + /// it is selected, and otherwise the first selected row. + /// + private int FocusedOrFirstSelectedIndex() => + imageListView.FocusedItem is { Selected: true } focused + ? focused.Index + : imageListView.SelectedItems[0].Index; + private void RemoveMenuItem_Click(object? sender, EventArgs e) { if (_imageItems.Count == 0 || imageListView.SelectedItems.Count == 0) return; - var index = imageListView.SelectedItems[0].Index; - _imageItems.RemoveAt(index); - imageListView.Items.RemoveAt(index); + // Descending, so removing one cannot shift the index of another still to go. + var indices = imageListView.SelectedItems + .Select(row => row.Index) + .OrderByDescending(i => i) + .ToList(); + + foreach (var removed in indices) { + _imageItems.RemoveAt(removed); + imageListView.Items.RemoveAt(removed); + } + + // Land on the row that took the place of the first one removed, so the selection ends + // up somewhere predictable rather than nowhere - a list with nothing selected announces + // nothing when focus returns to it. + var index = indices[^1]; if (imageListView.Items.Count > 0) { var newIndex = index < imageListView.Items.Count ? index : imageListView.Items.Count - 1; @@ -703,6 +724,11 @@ private async void ConvertSelectedMenuItem_Click(object? sender, EventArgs e) { await ConvertItemsAsync(selectedIndices); } + /// + /// One image in, one .ico out, so this works on the focused row even when several are + /// selected - taking the topmost of a multiple selection would act on an image the user is + /// not looking at. + /// private async void CreateMultiSizeIcoMenuItem_Click(object? sender, EventArgs e) { if (_imageItems.Count == 0 || imageListView.SelectedItems.Count == 0) return; @@ -712,7 +738,7 @@ private async void CreateMultiSizeIcoMenuItem_Click(object? sender, EventArgs e) return; var sizes = presetDialog.SelectedSizes; - var index = imageListView.SelectedItems[0].Index; + var index = FocusedOrFirstSelectedIndex(); var item = _imageItems[index]; var outputFolder = ValidateOutputFolder(); var outputPath = ImageConverter.GenerateOutputPath(item, "ICO", outputFolder, Config.General.SaveToSourceFolder); @@ -789,7 +815,7 @@ private async void FitToFileSizeMenuItem_Click(object? sender, EventArgs e) { var maxBytes = fitDialog.MaxBytes; var maxWidth = fitDialog.MaxWidth; - var index = imageListView.SelectedItems[0].Index; + var index = FocusedOrFirstSelectedIndex(); var item = _imageItems[index]; var formats = ImageConverter.GetSizeFitFormats(ImageConverter.GetEnabledFormats(Config.General.GetEnabledFormatKeys())); @@ -1119,7 +1145,13 @@ private void ImageListView_SelectedIndexChanged(object? sender, EventArgs e) { return; } - var index = imageListView.SelectedItems[0].Index; + // The focused row when it is part of the selection, otherwise the first selected one. + // Extending a selection moves the focus, and previewing where the user actually is + // beats previewing where they started. + var index = imageListView.FocusedItem is { Selected: true } focused + ? focused.Index + : imageListView.SelectedItems[0].Index; + var item = _imageItems[index]; _selectedItem = item;