From f50a8bbe79cb6d9e532b50e1b3e7d7b241e1b284 Mon Sep 17 00:00:00 2001 From: niksedk Date: Fri, 31 Jul 2026 11:58:56 +0200 Subject: [PATCH 1/2] Convert 45 nominal-sorting dialog grids from DataGrid to TableView Batch 2 of the TableView sweep (#13017, #13018): every remaining DataGrid with CanUserSortColumns=true but no deliberate SortMemberPath sorting. Rows take keyboard focus, so the screen-reader fix (#13015) now covers these dialogs too. Sorting was decided per grid. In the DataGrid only plain text columns ever sorted (a view-level sort); TableViewHeaderSorter reorders the backing collection, so it is wired only where the list order is presentation-only and nothing consumes collection order: - Wired (12 grids): FontCollector, FindRule, ManualChosenEncoding, PickFontName, PickLayerFilter, PickRuleProfile, PickOnlineSubtitle, and the track lists in PickMatroskaTrack/PickMp4Track/PickTsTrack/ PickVobSubLanguage - with typed keys (numeric stream ids, nullable comparers) and header-double-click guards where double-tap accepted the dialog. - Dropped everywhere else: subtitle/fix previews in timeline order, ASSA/SSA style and attachment lists (order is written to the file), MultipleReplace exports, batch queues, join/split file lists, and embedded-track lists (order is the output track order). Each carries a code comment with the reason. Other notable mappings: - DataGridCheckboxMultiSelect replaced by native extended selection + TableViewExtras.AddSpaceToggle (AiReview, ApplyDurationLimits, ConvertActors, FixNetflixErrors incl. its canToggle semantics, PickLayerFilter, RemoveTextForHearingImpaired; RemoveUnicodeCharacters hand-rolls it to coexist with its in-cell ReplaceWith TextBox editor). - Home/End first/last-row navigation hoisted into TableViewExtras.AttachHomeEndNavigation (sync + Assa/Ssa windows). - AssaStyles' DataGridCollectionView category filter replaced by a plain rebuilt ObservableCollection view. - IsFocused checks on grids became IsKeyboardFocusWithin (focus lands on row containers now) in CutVideo/PickMatroskaTrack/PickVobSubLanguage. - Sync-point lists show a column header now (TableView cannot hide headers). Remaining DataGrids: Shortcuts, BatchConvert and FixCommonErrors (real SortMemberPath sorting) - batch 3. Co-Authored-By: Claude Fable 5 --- src/ui/Features/Assa/AssaAttachmentsWindow.cs | 82 +++----- src/ui/Features/Assa/AssaStylePickerWindow.cs | 115 +++++----- src/ui/Features/Assa/AssaStylesViewModel.cs | 73 ++++--- src/ui/Features/Assa/AssaStylesWindow.cs | 196 ++++++++---------- .../Assa/FontCollector/FontCollectorWindow.cs | 87 ++++---- .../ModifySelection/ModifySelectionWindow.cs | 174 ++++++++-------- .../MultipleReplace/CategoryExportWindow.cs | 68 +++--- .../Edit/MultipleReplace/FindRuleWindow.cs | 122 ++++++----- .../MultipleReplace/MultipleReplaceWindow.cs | 130 ++++++------ .../Files/ImportImages/ImportImagesWindow.cs | 108 +++++----- .../ImportPlainText/ImportPlainTextWindow.cs | 160 +++++++------- .../ManualChosenEncodingWindow.cs | 86 ++++---- .../Options/Settings/ProfilesExportWindow.cs | 68 +++--- .../Options/Settings/ProfilesWindow.cs | 68 +++--- .../PickFontName/PickFontNameViewModel.cs | 2 +- .../Shared/PickFontName/PickFontNameWindow.cs | 45 ++-- .../PickLayerFilterViewModel.cs | 32 ++- .../PickLayerFilter/PickLayerFilterWindow.cs | 107 ++++++---- .../PickMatroskaTrackViewModel.cs | 14 +- .../PickMatroskaTrackWindow.cs | 188 +++++++++-------- .../PickMp4Track/PickMp4TrackViewModel.cs | 12 +- .../Shared/PickMp4Track/PickMp4TrackWindow.cs | 168 ++++++++------- .../PickRuleProfileViewModel.cs | 4 +- .../PickRuleProfile/PickRuleProfileWindow.cs | 102 +++++---- .../PickTsTrack/PickTsTrackViewModel.cs | 12 +- .../Shared/PickTsTrack/PickTsTrackWindow.cs | 183 ++++++++-------- .../PickVobSubLanguageViewModel.cs | 14 +- .../PickVobSubLanguageWindow.cs | 137 ++++++------ src/ui/Features/Ssa/SsaAttachmentsWindow.cs | 82 +++----- src/ui/Features/Ssa/SsaStylesViewModel.cs | 34 +-- src/ui/Features/Ssa/SsaStylesWindow.cs | 181 +++++++--------- .../Sync/PointSync/PointSyncWindow.cs | 123 ++++++----- .../PointSyncViaOtherWindow.cs | 179 ++++++++-------- .../Features/Tools/AiReview/AiReviewWindow.cs | 68 +++--- .../ApplyDurationLimitsWindow.cs | 179 ++++++++-------- .../Tools/ApplyMinGap/ApplyMinGapWindow.cs | 104 +++++----- ...tchConvertFixCommonErrorsSettingsWindow.cs | 89 ++++---- .../Tools/BridgeGaps/BridgeGapsWindow.cs | 104 +++++----- .../ChangeFormattingWindow.cs | 173 ++++++++-------- .../ConvertActors/ConvertActorsWindow.cs | 68 +++--- .../FixNetflixErrorsWindow.cs | 121 ++++++----- .../JoinSubtitles/JoinSubtitlesViewModel.cs | 2 +- .../JoinSubtitles/JoinSubtitlesWindow.cs | 67 +++--- .../MergeShortLines/MergeShortLinesWindow.cs | 46 ++-- .../MergeSameTextViewModel.cs | 13 +- .../MergeSameTextWindow.cs | 139 ++++++------- .../MergeSameTimeCodesViewModel.cs | 13 +- .../MergeSameTimeCodesWindow.cs | 139 ++++++------- .../RemoveTextForHearingImpairedWindow.cs | 63 +++--- .../RemoveUnicodeCharactersWindow.cs | 115 ++++++---- .../SplitBreakLongLinesWindow.cs | 56 +++-- .../SplitSubtitle/SplitSubtitleWindow.cs | 55 +++-- .../Features/Video/BurnIn/BurnInViewModel.cs | 4 +- src/ui/Features/Video/BurnIn/BurnInWindow.cs | 84 ++++---- .../Video/CutVideo/CutVideoViewModel.cs | 19 +- .../Features/Video/CutVideo/CutVideoWindow.cs | 84 ++++---- .../EmbedTrackPreviewWindow.cs | 145 +++++++------ .../EmbeddedSubtitlesEditMp4ViewModel.cs | 9 +- .../EmbeddedSubtitlesEditMp4Window.cs | 158 +++++++------- .../EmbeddedSubtitlesEditViewModel.cs | 9 +- .../EmbeddedSubtitlesEditWindow.cs | 137 ++++++------ .../PickOnlineSubtitleViewModel.cs | 6 +- .../PickOnlineSubtitleWindow.cs | 168 ++++++++------- .../SpeechToText/SpeechToTextViewModel.cs | 8 +- .../Video/SpeechToText/SpeechToTextWindow.cs | 70 +++---- .../TransparentSubtitlesWindow.cs | 84 ++++---- src/ui/Logic/TableViewExtras.cs | 31 +++ 67 files changed, 2917 insertions(+), 2869 deletions(-) diff --git a/src/ui/Features/Assa/AssaAttachmentsWindow.cs b/src/ui/Features/Assa/AssaAttachmentsWindow.cs index 9854238192c..1527e6a0d8e 100644 --- a/src/ui/Features/Assa/AssaAttachmentsWindow.cs +++ b/src/ui/Features/Assa/AssaAttachmentsWindow.cs @@ -92,58 +92,42 @@ private static Border MakeLeftView(AssaAttachmentsViewModel vm) HorizontalAlignment = HorizontalAlignment.Stretch, }; - var dataGrid = new DataGrid + // No header sorting: the attachment order is written back to the subtitle + // footer ([Fonts]/[Graphics] sections) in list order on OK, so the collection + // order is not presentation-only. + var dataGrid = TableViewExtras.MakeTableView(multiSelect: false); + dataGrid.DataContext = vm; + dataGrid.ItemsSource = vm.Attachments; + + dataGrid.Columns.Add(new SeTableViewColumn { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.Attachments, - Columns = - { - new DataGridTextColumn - { - Header = Se.Language.General.FileName, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(AssaAttachmentItem.FileName)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Type, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(AssaAttachmentItem.Category)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, - new DataGridTextColumn - { - Header = Se.Language.General.Size, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(AssaAttachmentItem.Size)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, - }, - }; - dataGrid.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedAttachment)) { Source = vm }); + Header = Se.Language.General.FileName, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(AssaAttachmentItem.FileName)), + Width = new GridLength(1, GridUnitType.Star), + }); + dataGrid.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.Type, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(AssaAttachmentItem.Category)), + Width = new GridLength(140), + }); + dataGrid.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.Size, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(AssaAttachmentItem.Size)), + Width = new GridLength(100), + }); + + dataGrid.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedAttachment)) { Source = vm }); dataGrid.SelectionChanged += vm.DataGridSelectionChanged; dataGrid.KeyDown += vm.AttachmentsDataGridKeyDown; - dataGrid.AddHandler(InputElement.KeyDownEvent, (object? _, KeyEventArgs e) => - { - if (e.Key is Key.Home or Key.End && dataGrid.ItemsSource is IList items && items.Count > 0) - { - var target = e.Key == Key.Home ? items[0] : items[^1]; - dataGrid.SelectedItem = target; - dataGrid.ScrollIntoView(target, null); - e.Handled = true; - } - }, Avalonia.Interactivity.RoutingStrategies.Tunnel); + TableViewExtras.AttachHomeEndNavigation(dataGrid); var flyout = new MenuFlyout(); flyout.Opening += vm.AttachmentsContextMenuOpening; diff --git a/src/ui/Features/Assa/AssaStylePickerWindow.cs b/src/ui/Features/Assa/AssaStylePickerWindow.cs index 3b991613a4b..d0b815473db 100644 --- a/src/ui/Features/Assa/AssaStylePickerWindow.cs +++ b/src/ui/Features/Assa/AssaStylePickerWindow.cs @@ -66,75 +66,76 @@ public AssaStylePickerWindow(AssaStylePickerViewModel vm) private static Border MakeDataGrid(AssaStylePickerViewModel vm) { - var usagesColumn = new DataGridTextColumn + var usagesColumn = new SeTableViewColumn { Header = Se.Language.General.Usages, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(StyleDisplay.FontSize)), - IsReadOnly = true, + Width = new GridLength(90), }; - usagesColumn.Bind(DataGridTextColumn.IsVisibleProperty, new Binding(nameof(vm.ShowUsageCount)) + usagesColumn.Bind(SeTableViewColumn.IsVisibleProperty, new Binding(nameof(vm.ShowUsageCount)) { Mode = BindingMode.OneWay, Source = vm, }); - var dataGrid = new DataGrid + // No header sorting: the checked styles are imported/applied in list order + // (e.g. appended to the file's style list, which is written to the header), + // so the collection order is not presentation-only. + var dataGrid = TableViewExtras.MakeTableView(multiSelect: false); + dataGrid.DataContext = vm; + dataGrid.ItemsSource = vm.Styles; + + // The usages column has a bound visibility, so all columns go through a + // TableViewColumnManager (TableView itself has no column IsVisible). + var columnManager = new TableViewColumnManager(dataGrid); + columnManager.Add(new SeTableViewColumn { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.Styles, - Columns = + Header = Se.Language.General.Enabled, + CellTheme = UiUtil.TableViewNoPaddingCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + CellTemplate = new FuncDataTemplate((item, _) => + new Border { - new DataGridTemplateColumn - { - Header = Se.Language.General.Enabled, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - CellTemplate = new FuncDataTemplate((item, _) => - new Border - { - Background = Brushes.Transparent, // Prevents highlighting - Padding = new Thickness(4), - Child = new CheckBox - { - [!ToggleButton.IsCheckedProperty] = new Binding(nameof(StyleDisplay.IsSelected)), - HorizontalAlignment = HorizontalAlignment.Center - } - }), - Width = new DataGridLength(1, DataGridLengthUnitType.Auto) - }, - new DataGridTextColumn - { - Header = Se.Language.General.Name, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(StyleDisplay.Name)), - IsReadOnly = true, - }, - new DataGridTextColumn + Background = Brushes.Transparent, // Prevents highlighting + Padding = new Thickness(4), + Child = new CheckBox { - Header = Se.Language.General.FontName, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(StyleDisplay.FontName)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.FontSize, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(StyleDisplay.FontSize)), - IsReadOnly = true, - }, - usagesColumn, - }, - }; - dataGrid.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedStyle)) { Source = vm }); + [!ToggleButton.IsCheckedProperty] = new Binding(nameof(StyleDisplay.IsSelected)), + HorizontalAlignment = HorizontalAlignment.Center + } + }), + // Content-sized (Auto) on the DataGrid; TableView treats Auto as star. + Width = new GridLength(80), + }); + columnManager.Add(new SeTableViewColumn + { + Header = Se.Language.General.Name, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(StyleDisplay.Name)), + Width = new GridLength(1, GridUnitType.Star), + }); + columnManager.Add(new SeTableViewColumn + { + Header = Se.Language.General.FontName, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(StyleDisplay.FontName)), + Width = new GridLength(180), + }); + columnManager.Add(new SeTableViewColumn + { + Header = Se.Language.General.FontSize, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(StyleDisplay.FontSize)), + Width = new GridLength(90), + }); + columnManager.Add(usagesColumn); + + dataGrid.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedStyle)) { Source = vm }); return UiUtil.MakeBorderForControl(dataGrid); } diff --git a/src/ui/Features/Assa/AssaStylesViewModel.cs b/src/ui/Features/Assa/AssaStylesViewModel.cs index c4528d009ef..1ec4be4d554 100644 --- a/src/ui/Features/Assa/AssaStylesViewModel.cs +++ b/src/ui/Features/Assa/AssaStylesViewModel.cs @@ -1,4 +1,3 @@ -using Avalonia.Collections; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Media.Imaging; @@ -53,9 +52,16 @@ public partial class AssaStylesViewModel : ObservableObject, IClosingCleanup public Window? Window { get; set; } public bool OkPressed { get; private set; } public string Header { get; set; } - public DataGrid FileStyleGrid { get; set; } - public DataGrid StorageStyleGrid { get; set; } - public DataGridCollectionView StorageStylesView { get; } + public TableView FileStyleGrid { get; set; } + public TableView StorageStyleGrid { get; set; } + + /// + /// The storage styles filtered by the selected category. The DataGrid-era + /// DataGridCollectionView is gone (TableView binds a plain collection), so this + /// view is rebuilt from - which stays the source of + /// truth for saving - whenever the source or the category filter changes. + /// + public ObservableCollection StorageStylesView { get; } public Subtitle ResultSubtitle => _subtitle; private readonly IFileHelper _fileHelper; @@ -81,8 +87,8 @@ public AssaStylesViewModel(IFileHelper fileHelper, IWindowService windowService) BorderTypes = new ObservableCollection(BorderStyleItem.List()); SelectedBorderType = BorderTypes[0]; CurrentTitle = string.Empty; - FileStyleGrid = new DataGrid(); - StorageStyleGrid = new DataGrid(); + FileStyleGrid = new TableView(); + StorageStyleGrid = new TableView(); Header = string.Empty; _subtitle = new Subtitle(); @@ -90,10 +96,9 @@ public AssaStylesViewModel(IFileHelper fileHelper, IWindowService windowService) LoadSettings(); - StorageStylesView = new DataGridCollectionView(StorageStyles) - { - Filter = o => o is StyleDisplay s && IsStyleInSelectedCategory(s), - }; + StorageStylesView = new ObservableCollection(); + StorageStyles.CollectionChanged += (_, _) => RefreshStorageStylesView(); + RefreshStorageStylesView(); RebuildStorageCategories(); _timerUpdatePreview = new System.Timers.Timer(500); @@ -278,7 +283,7 @@ private void FileNew() [RelayCommand] private void FileRemove() { - var selectedItems = FileStyleGrid.SelectedItems.Cast().ToList(); + var selectedItems = FileStyleGrid.SelectedItems?.Cast().ToList() ?? new List(); if (Window == null || selectedItems.Count == 0) { return; @@ -302,7 +307,7 @@ private void FileRemoveAll() [RelayCommand] private void FilesDuplicate() { - var selectedItems = FileStyleGrid.SelectedItems.Cast().ToList(); + var selectedItems = FileStyleGrid.SelectedItems?.Cast().ToList() ?? new List(); if (Window == null || selectedItems.Count == 0) { return; @@ -362,7 +367,7 @@ private async Task FileExport() [RelayCommand] private void FileCopyToStorage() { - var selectedItems = FileStyleGrid.SelectedItems.Cast().ToList(); + var selectedItems = FileStyleGrid.SelectedItems?.Cast().ToList() ?? new List(); if (Window == null || selectedItems.Count == 0) { return; @@ -413,7 +418,7 @@ private async Task FileTakeUsagesFrom() [RelayCommand] private async Task FileReplaceWith() { - var selectedItems = FileStyleGrid.SelectedItems.Cast().ToList(); + var selectedItems = FileStyleGrid.SelectedItems?.Cast().ToList() ?? new List(); if (Window == null || selectedItems.Count == 0) { return; @@ -538,7 +543,7 @@ private void StorageNew() [RelayCommand] private void StorageRemove() { - var selectedItems = StorageStyleGrid.SelectedItems.Cast().ToList(); + var selectedItems = StorageStyleGrid.SelectedItems?.Cast().ToList() ?? new List(); if (Window == null || selectedItems.Count == 0) { return; @@ -594,7 +599,7 @@ private void StorageRemove() } } - StorageStyleGrid.Focus(); + TableViewExtras.FocusRow(StorageStyleGrid); }); } @@ -607,7 +612,7 @@ private void StorageRemoveAll() [RelayCommand] private void StorageDuplicate() { - var selectedItems = StorageStyleGrid.SelectedItems.Cast().ToList(); + var selectedItems = StorageStyleGrid.SelectedItems?.Cast().ToList() ?? new List(); if (Window == null || selectedItems.Count == 0) { return; @@ -665,7 +670,7 @@ private async Task StorageExport() [RelayCommand] private void StorageCopyToFiles() { - var selectedItems = StorageStyleGrid.SelectedItems.Cast().ToList(); + var selectedItems = StorageStyleGrid.SelectedItems?.Cast().ToList() ?? new List(); if (Window == null || selectedItems.Count == 0) { return; @@ -737,10 +742,24 @@ private void RebuildStorageCategories() partial void OnSelectedStorageCategoryChanged(string value) { - StorageStylesView?.Refresh(); + RefreshStorageStylesView(); IsCategoryActionVisible = value != AllCategoriesLabel && value != DefaultCategoryLabel; } + private void RefreshStorageStylesView() + { + if (StorageStylesView is null) + { + return; // category can change while the constructor is still initializing + } + + StorageStylesView.Clear(); + foreach (var style in StorageStyles.Where(IsStyleInSelectedCategory)) + { + StorageStylesView.Add(style); + } + } + [RelayCommand] private async Task NewCategory() { @@ -852,7 +871,7 @@ private void DeleteCategory() [RelayCommand] private async Task MoveToCategory() { - var selectedItems = StorageStyleGrid.SelectedItems.Cast().ToList(); + var selectedItems = StorageStyleGrid.SelectedItems?.Cast().ToList() ?? new List(); if (Window == null || selectedItems.Count == 0) { return; @@ -882,7 +901,7 @@ private async Task MoveToCategory() RebuildStorageCategories(); SelectedStorageCategory = StorageCategories.Contains(label) ? label : SelectedStorageCategory; - StorageStylesView.Refresh(); + RefreshStorageStylesView(); } private void Close() @@ -965,7 +984,7 @@ public void Initialize( } IsFileStyleSelected = SelectedFileStyle != null; - IsTakeUsagesFromVisible = FileStyleGrid.SelectedItems.Count == 1; + IsTakeUsagesFromVisible = FileStyleGrid.SelectedItems?.Count == 1; _timerUpdatePreview.Start(); } @@ -1199,7 +1218,7 @@ private void SwitchToFileStyle() CurrentTitle = Se.Language.Assa.StylesInFile; SelectedBorderType = selectedStyle?.BorderStyle ?? BorderTypes[0]; IsFileStyleSelected = selectedStyle != null; - IsTakeUsagesFromVisible = FileStyleGrid.SelectedItems.Count == 1; + IsTakeUsagesFromVisible = FileStyleGrid.SelectedItems?.Count == 1; } private void SwitchToStorageStyle() @@ -1209,8 +1228,8 @@ private void SwitchToStorageStyle() CurrentTitle = Se.Language.Assa.StylesSaved; SelectedBorderType = selectedStyle?.BorderStyle ?? BorderTypes[0]; IsStorageStyleSelected = selectedStyle != null; - IsSetStyleAsDefaultVisible = StorageStyleGrid.SelectedItems.Count == 1; - IsCopyToFileStylesVisible = StorageStyleGrid.SelectedItems.Count > 0; + IsSetStyleAsDefaultVisible = StorageStyleGrid.SelectedItems?.Count == 1; + IsCopyToFileStylesVisible = StorageStyleGrid.SelectedItems?.Count > 0; } internal void BorderTypeChanged(object? sender, SelectionChangedEventArgs e) @@ -1280,7 +1299,7 @@ private void DeleteFileStyle(StyleDisplay? selectedStyle) UpdateUsages(); } - FileStyleGrid.Focus(); + TableViewExtras.FocusRow(FileStyleGrid); }); } @@ -1330,7 +1349,7 @@ private void DeleteFileStyles(List selectedStyles) } UpdateUsages(); - FileStyleGrid.Focus(); + TableViewExtras.FocusRow(FileStyleGrid); }); } diff --git a/src/ui/Features/Assa/AssaStylesWindow.cs b/src/ui/Features/Assa/AssaStylesWindow.cs index 9bb52c605b1..02f63e83209 100644 --- a/src/ui/Features/Assa/AssaStylesWindow.cs +++ b/src/ui/Features/Assa/AssaStylesWindow.cs @@ -111,67 +111,50 @@ private static Border MakeFileStyles(AssaStylesViewModel vm) var label = UiUtil.MakeLabel(Se.Language.Assa.StylesInFile).WithBold(); - var dataGrid = new DataGrid + // No header sorting: ASSA styles are written to the file header in list + // order on OK, so the collection order is not presentation-only. + var dataGrid = TableViewExtras.MakeTableView(); + dataGrid.DataContext = vm; + dataGrid.ItemsSource = vm.FileStyles; + + dataGrid.Columns.Add(new SeTableViewColumn { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Extended, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.FileStyles, - Columns = - { - new DataGridTextColumn - { - Header = Se.Language.General.Name, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(StyleDisplay.Name)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.FontName, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(StyleDisplay.FontName)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, - new DataGridTextColumn - { - Header = Se.Language.General.FontSize, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(StyleDisplay.FontSize)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, - new DataGridTextColumn - { - Header = Se.Language.General.Usages, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(StyleDisplay.UsageCount)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, - }, - }; - dataGrid.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedFileStyle)) { Source = vm }); + Header = Se.Language.General.Name, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(StyleDisplay.Name)), + Width = new GridLength(1, GridUnitType.Star), + }); + dataGrid.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.FontName, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(StyleDisplay.FontName)), + Width = new GridLength(150), + }); + dataGrid.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.FontSize, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(StyleDisplay.FontSize)), + Width = new GridLength(90), + }); + dataGrid.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.Usages, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(StyleDisplay.UsageCount)), + Width = new GridLength(90), + }); + + dataGrid.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedFileStyle)) { Source = vm }); dataGrid.SelectionChanged += vm.FileStylesChanged; dataGrid.GotFocus += vm.FileStylesGotFocus; dataGrid.KeyDown += vm.FileStylesKeyDown; - dataGrid.AddHandler(InputElement.KeyDownEvent, (object? _, KeyEventArgs e) => - { - if (e.Key is Key.Home or Key.End && dataGrid.ItemsSource is IList items && items.Count > 0) - { - var target = e.Key == Key.Home ? items[0] : items[^1]; - dataGrid.SelectedItem = target; - dataGrid.ScrollIntoView(target, null); - e.Handled = true; - } - }, Avalonia.Interactivity.RoutingStrategies.Tunnel); + TableViewExtras.AttachHomeEndNavigation(dataGrid); vm.FileStyleGrid = dataGrid; var flyout = new MenuFlyout(); @@ -277,59 +260,54 @@ private static Border MakeStorageStyles(AssaStylesViewModel vm) var panelCategory = UiUtil.MakeHorizontalPanel(labelCategory, comboBoxCategory, buttonNewCategory, buttonRenameCategory, buttonDeleteCategory) .WithAlignmentLeft(); - var dataGrid = new DataGrid + // No header sorting: the storage style order is persisted to settings in + // list order on OK, so the collection order is not presentation-only. + var dataGrid = TableViewExtras.MakeTableView(); + dataGrid.DataContext = vm; + dataGrid.ItemsSource = vm.StorageStylesView; + + dataGrid.Columns.Add(new SeTableViewColumn { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Extended, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.StorageStylesView, - Columns = - { - new DataGridTextColumn - { - Header = Se.Language.General.Name, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(StyleDisplay.Name)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Category, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(StyleDisplay.Category)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.FontName, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(StyleDisplay.FontName)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.FontSize, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(StyleDisplay.FontSize)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.IsDefault, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(StyleDisplay.IsDefault)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, - }, - }; - dataGrid.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedStorageStyle)) { Source = vm }); + Header = Se.Language.General.Name, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(StyleDisplay.Name)), + Width = new GridLength(1, GridUnitType.Star), + }); + dataGrid.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.Category, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(StyleDisplay.Category)), + Width = new GridLength(120), + }); + dataGrid.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.FontName, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(StyleDisplay.FontName)), + Width = new GridLength(150), + }); + dataGrid.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.FontSize, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(StyleDisplay.FontSize)), + Width = new GridLength(90), + }); + dataGrid.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.IsDefault, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(StyleDisplay.IsDefault)), + Width = new GridLength(90), + }); + + dataGrid.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedStorageStyle)) { Source = vm }); dataGrid.SelectionChanged += vm.StorageStylesChanged; dataGrid.GotFocus += vm.StorageStylesGotFocus; vm.StorageStyleGrid = dataGrid; diff --git a/src/ui/Features/Assa/FontCollector/FontCollectorWindow.cs b/src/ui/Features/Assa/FontCollector/FontCollectorWindow.cs index 79e09f9c166..d2bc4990b96 100644 --- a/src/ui/Features/Assa/FontCollector/FontCollectorWindow.cs +++ b/src/ui/Features/Assa/FontCollector/FontCollectorWindow.cs @@ -27,48 +27,55 @@ public FontCollectorWindow(FontCollectorViewModel vm) vm.Window = this; DataContext = vm; - var dataGrid = new DataGrid + var dataGrid = TableViewExtras.MakeTableView(); + dataGrid.DataContext = vm; + dataGrid.ItemsSource = vm.FontItems; + + var fontNameColumn = new SeTableViewColumn { - AutoGenerateColumns = false, - CanUserResizeColumns = true, - CanUserSortColumns = true, - IsReadOnly = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.FontItems, - Columns = - { - new DataGridTextColumn - { - Header = Se.Language.General.FontName, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(FontCollectorItem.FontName)), - }, - new DataGridTextColumn - { - Header = Se.Language.Assa.FontCollectorUsedIn, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(FontCollectorItem.UsedIn)), - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, - new DataGridTextColumn - { - Header = Se.Language.General.Status, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(FontCollectorItem.Status)), - }, - new DataGridTextColumn - { - Header = Se.Language.Assa.FontCollectorFontFiles, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(FontCollectorItem.FileDisplay)), - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, - }, + Header = Se.Language.General.FontName, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(FontCollectorItem.FontName)), + Width = new GridLength(180), + }; + var usedInColumn = new SeTableViewColumn + { + Header = Se.Language.Assa.FontCollectorUsedIn, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(FontCollectorItem.UsedIn)), + Width = new GridLength(1, GridUnitType.Star), + }; + var statusColumn = new SeTableViewColumn + { + Header = Se.Language.General.Status, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(FontCollectorItem.Status)), + Width = new GridLength(120), }; + var fontFilesColumn = new SeTableViewColumn + { + Header = Se.Language.Assa.FontCollectorFontFiles, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(FontCollectorItem.FileDisplay)), + Width = new GridLength(1, GridUnitType.Star), + }; + dataGrid.Columns.Add(fontNameColumn); + dataGrid.Columns.Add(usedInColumn); + dataGrid.Columns.Add(statusColumn); + dataGrid.Columns.Add(fontFilesColumn); + + // Header sorting is safe here: the font list is presentation-only (copying + // fonts to a folder uses the found-file set, not the row order, and the + // background scan holds item references, not indexes). + var sorter = new TableViewHeaderSorter(dataGrid); + sorter.AddSortable(fontNameColumn, x => x.FontName) + .AddSortable(usedInColumn, x => x.UsedIn) + .AddSortable(statusColumn, x => x.Status) + .AddSortable(fontFilesColumn, x => x.FileDisplay); var statusText = new TextBlock { diff --git a/src/ui/Features/Edit/ModifySelection/ModifySelectionWindow.cs b/src/ui/Features/Edit/ModifySelection/ModifySelectionWindow.cs index 5ac3c848d90..9122a8f1343 100644 --- a/src/ui/Features/Edit/ModifySelection/ModifySelectionWindow.cs +++ b/src/ui/Features/Edit/ModifySelection/ModifySelectionWindow.cs @@ -107,42 +107,36 @@ private static Border MakeRulesView(ModifySelectionViewModel vm, out TextBox tex numericUpDownRuleNumber.BindIsVisible(vm, nameof(vm.SelectedRule) + "." + nameof(vm.SelectedRule.HasNumber)); numericUpDownRuleNumber.ValueChanged += (sender, args) => vm.OnRuleChanged(); - var dataGridMultiSelect = new DataGrid + var dataGridMultiSelect = TableViewExtras.MakeTableView(multiSelect: false); + dataGridMultiSelect.CanUserResizeColumns = false; + dataGridMultiSelect.Width = 280; + dataGridMultiSelect.MaxHeight = 200; + dataGridMultiSelect.DataContext = vm; + dataGridMultiSelect.Columns.Add(new SeTableViewColumn { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - MaxHeight = 200, - Columns = + Header = Se.Language.General.Enabled, + CellTheme = UiUtil.TableViewNoPaddingCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + CellTemplate = new FuncDataTemplate((item, _) => + new Border { - new DataGridTemplateColumn - { - Header = Se.Language.General.Enabled, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - CellTemplate = new FuncDataTemplate((item, _) => - new Border - { - Background = Brushes.Transparent, // Prevents highlighting - Padding = new Thickness(4), - Child = MakeCheckBox(vm) - }), - Width = new DataGridLength(1, DataGridLengthUnitType.Auto) - }, - new DataGridTextColumn - { - Header = Se.Language.General.Name, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(StyleDisplay.Name)), - IsReadOnly = true, - }, - }, - }; + Background = Brushes.Transparent, // Prevents highlighting + Padding = new Thickness(4), + Child = MakeCheckBox(vm) + }), + // Content-sized (Auto) on the DataGrid; TableView treats Auto as star. + Width = new GridLength(80) + }); + dataGridMultiSelect.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.Name, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(StyleDisplay.Name)), + Width = new GridLength(1, GridUnitType.Star), + }); dataGridMultiSelect.BindIsVisible(vm, nameof(vm.SelectedRule) + "." + nameof(vm.SelectedRule.HasMultiSelect)); - dataGridMultiSelect.Bind(DataGrid.ItemsSourceProperty, new Binding(nameof(vm.SelectedRule) + "." + nameof(vm.SelectedRule.MultiSelectItems)) { Source = vm }); + dataGridMultiSelect.Bind(TableView.ItemsSourceProperty, new Binding(nameof(vm.SelectedRule) + "." + nameof(vm.SelectedRule.MultiSelectItems)) { Source = vm }); var panelRule = new StackPanel { @@ -216,71 +210,69 @@ private static Border MakeSubtitleView(ModifySelectionViewModel vm) var fullTimeConverter = new TimeSpanToDisplayFullConverter(); var shortTimeConverter = new TimeSpanToDisplayShortConverter(); - var dataGrid = new DataGrid + // No header sorting (the DataGrid's CanUserSortColumns is not carried over): + // this is a subtitle-line preview in subtitle order, and Ok() iterates the + // collection in order to build the selection. + var dataGrid = TableViewExtras.MakeTableView(multiSelect: false); + dataGrid.DataContext = vm; + dataGrid.Columns.AddRange(new TableViewColumn[] { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - Columns = + new SeTableViewColumn { - new DataGridTemplateColumn - { - Header = Se.Language.General.Apply, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - CellTemplate = new FuncDataTemplate((item, _) => - new Border + Header = Se.Language.General.Apply, + CellTheme = UiUtil.TableViewNoPaddingCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + CellTemplate = new FuncDataTemplate((item, _) => + new Border + { + Background = Brushes.Transparent, // Prevents highlighting + Padding = new Thickness(4), + Child = new CheckBox { - Background = Brushes.Transparent, // Prevents highlighting - Padding = new Thickness(4), - Child = new CheckBox - { - [!ToggleButton.IsCheckedProperty] = new Binding(nameof(PreviewItem.Apply)), - HorizontalAlignment = HorizontalAlignment.Center - } - }), - Width = new DataGridLength(1, DataGridLengthUnitType.Auto) - }, - new DataGridTextColumn - { - Header = Se.Language.General.NumberSymbol, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(PreviewItem.Number)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Show, - Binding = new Binding(nameof(PreviewItem.Show)) { Converter = fullTimeConverter }, - Width = new DataGridLength(120), - CellTheme = UiUtil.DataGridNoBorderCellTheme, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Duration, - Binding = new Binding(nameof(PreviewItem.Duration)) { Converter = shortTimeConverter }, - Width = new DataGridLength(120), - CellTheme = UiUtil.DataGridNoBorderCellTheme, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Text, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(PreviewItem.Text)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, + [!ToggleButton.IsCheckedProperty] = new Binding(nameof(PreviewItem.Apply)), + HorizontalAlignment = HorizontalAlignment.Center + } + }), + // Content-sized (Auto) on the DataGrid; TableView treats Auto as star. + Width = new GridLength(80) }, - }; + new SeTableViewColumn + { + Header = Se.Language.General.NumberSymbol, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(PreviewItem.Number)), + Width = new GridLength(60), + }, + new SeTableViewColumn + { + Header = Se.Language.General.Show, + Binding = new Binding(nameof(PreviewItem.Show)) { Converter = fullTimeConverter }, + Width = new GridLength(120), + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + }, + new SeTableViewColumn + { + Header = Se.Language.General.Duration, + Binding = new Binding(nameof(PreviewItem.Duration)) { Converter = shortTimeConverter }, + Width = new GridLength(120), + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + }, + new SeTableViewColumn + { + Header = Se.Language.General.Text, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(PreviewItem.Text)), + Width = new GridLength(1, GridUnitType.Star), + }, + }); // Bind ItemsSource to the property (rather than assigning the instance once) // so the grid follows the collection when the view model swaps it on preview. - dataGrid.Bind(DataGrid.ItemsSourceProperty, new Binding(nameof(vm.Subtitles)) { Source = vm }); + dataGrid.Bind(TableView.ItemsSourceProperty, new Binding(nameof(vm.Subtitles)) { Source = vm }); return UiUtil.MakeBorderForControlNoPadding(dataGrid); } diff --git a/src/ui/Features/Edit/MultipleReplace/CategoryExportWindow.cs b/src/ui/Features/Edit/MultipleReplace/CategoryExportWindow.cs index 57e56c17323..02ec251a97d 100644 --- a/src/ui/Features/Edit/MultipleReplace/CategoryExportWindow.cs +++ b/src/ui/Features/Edit/MultipleReplace/CategoryExportWindow.cs @@ -74,47 +74,43 @@ private static Border MakeDataGrid(CategoryExportViewModel vm) Width = double.NaN, }; - var dataGrid = new DataGrid + // No header sorting (the DataGrid's CanUserSortColumns is not carried over): + // the caller writes result.Rules to the exported file in collection order, so + // reordering the backing collection would reorder the export. + var dataGrid = TableViewExtras.MakeTableView(multiSelect: false); + dataGrid.DataContext = vm; + dataGrid.Columns.AddRange(new TableViewColumn[] { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - Columns = + new SeTableViewColumn { - new DataGridTemplateColumn + Header = Se.Language.General.Enabled, + CellTheme = UiUtil.TableViewNoPaddingCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + CellTemplate = new FuncDataTemplate((item, _) => + new Border { - Header = Se.Language.General.Enabled, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - CellTemplate = new FuncDataTemplate((item, _) => - new Border + Background = Brushes.Transparent, // Prevents highlighting + Padding = new Thickness(4), + Child = new CheckBox { - Background = Brushes.Transparent, // Prevents highlighting - Padding = new Thickness(4), - Child = new CheckBox - { - [!CheckBox.IsCheckedProperty] = new Binding(nameof(RuleTreeNode.IsSelected)), - HorizontalAlignment = HorizontalAlignment.Center - } - }), - Width = new DataGridLength(1, DataGridLengthUnitType.Auto) - }, - new DataGridTextColumn - { - Header = Se.Language.General.Name, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(RuleTreeNode.CategoryName)), - IsReadOnly = true, - }, + [!CheckBox.IsCheckedProperty] = new Binding(nameof(RuleTreeNode.IsSelected)), + HorizontalAlignment = HorizontalAlignment.Center + } + }), + // Content-sized (Auto) on the DataGrid; TableView treats Auto as star. + Width = new GridLength(80) }, - }; - dataGrid.Bind(DataGrid.ItemsSourceProperty, new Binding(nameof(vm.Rules)) { Source = vm }); - dataGrid.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedRule)) { Source = vm }); + new SeTableViewColumn + { + Header = Se.Language.General.Name, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(RuleTreeNode.CategoryName)), + Width = new GridLength(1, GridUnitType.Star), + }, + }); + dataGrid.Bind(TableView.ItemsSourceProperty, new Binding(nameof(vm.Rules)) { Source = vm }); + dataGrid.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedRule)) { Source = vm }); grid.Add(dataGrid, 0); diff --git a/src/ui/Features/Edit/MultipleReplace/FindRuleWindow.cs b/src/ui/Features/Edit/MultipleReplace/FindRuleWindow.cs index 9a3192b9279..df28e31fe38 100644 --- a/src/ui/Features/Edit/MultipleReplace/FindRuleWindow.cs +++ b/src/ui/Features/Edit/MultipleReplace/FindRuleWindow.cs @@ -66,66 +66,74 @@ public FindRuleWindow(FindRuleViewModel vm) private static Border MakeDataGrid(FindRuleViewModel vm) { - var dataGrid = new DataGrid + var dataGrid = TableViewExtras.MakeTableView(multiSelect: false); + dataGrid.DataContext = vm; + + var columnCategory = new SeTableViewColumn { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - IsReadOnly = true, - DataContext = vm, - Columns = - { - new DataGridTextColumn - { - Header = Se.Language.General.Category, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding("Parent.CategoryName"), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, - new DataGridTextColumn - { - Header = Se.Language.General.Find, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(RuleTreeNode.Find)), - IsReadOnly = true, - Width = new DataGridLength(2, DataGridLengthUnitType.Star), - }, - new DataGridTextColumn - { - Header = Se.Language.General.ReplaceWith, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(RuleTreeNode.ReplaceWith)), - IsReadOnly = true, - Width = new DataGridLength(2, DataGridLengthUnitType.Star), - }, - new DataGridTextColumn - { - Header = Se.Language.General.Description, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(RuleTreeNode.Description)), - IsReadOnly = true, - Width = new DataGridLength(2, DataGridLengthUnitType.Star), - }, - new DataGridTextColumn - { - Header = Se.Language.General.Type, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(RuleTreeNode.SearchType)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Auto), - }, - }, + Header = Se.Language.General.Category, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding("Parent.CategoryName"), + Width = new GridLength(1, GridUnitType.Star), }; + var columnFind = new SeTableViewColumn + { + Header = Se.Language.General.Find, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(RuleTreeNode.Find)), + Width = new GridLength(2, GridUnitType.Star), + }; + var columnReplaceWith = new SeTableViewColumn + { + Header = Se.Language.General.ReplaceWith, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(RuleTreeNode.ReplaceWith)), + Width = new GridLength(2, GridUnitType.Star), + }; + var columnDescription = new SeTableViewColumn + { + Header = Se.Language.General.Description, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(RuleTreeNode.Description)), + Width = new GridLength(2, GridUnitType.Star), + }; + var columnType = new SeTableViewColumn + { + Header = Se.Language.General.Type, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(RuleTreeNode.SearchType)), + // Content-sized (Auto) on the DataGrid; TableView treats Auto as star. + Width = new GridLength(140), + }; + dataGrid.Columns.AddRange(new TableViewColumn[] + { + columnCategory, columnFind, columnReplaceWith, columnDescription, columnType, + }); + + // Search results whose order is presentation-only (the caller consumes just + // SelectedRule), so header sorting is safe to wire. + var sorter = new TableViewHeaderSorter(dataGrid); + sorter.AddSortable(columnCategory, x => x.Parent?.CategoryName ?? string.Empty) + .AddSortable(columnFind, x => x.Find) + .AddSortable(columnReplaceWith, x => x.ReplaceWith) + .AddSortable(columnDescription, x => x.Description) + .AddSortable(columnType, x => x.SearchType); - dataGrid.Bind(DataGrid.ItemsSourceProperty, new Binding(nameof(vm.Rules)) { Source = vm }); - dataGrid.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedRule)) { Source = vm, Mode = BindingMode.TwoWay }); - dataGrid.DoubleTapped += vm.DataGridDoubleTapped; + dataGrid.Bind(TableView.ItemsSourceProperty, new Binding(nameof(vm.Rules)) { Source = vm }); + dataGrid.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedRule)) { Source = vm, Mode = BindingMode.TwoWay }); + dataGrid.DoubleTapped += (sender, e) => + { + // A fast double-click on a sortable header must sort, not accept the dialog. + if (!TableViewExtras.IsInColumnHeader(e.Source as Avalonia.Visual)) + { + vm.DataGridDoubleTapped(sender, e); + } + }; return UiUtil.MakeBorderForControlNoPadding(dataGrid); } diff --git a/src/ui/Features/Edit/MultipleReplace/MultipleReplaceWindow.cs b/src/ui/Features/Edit/MultipleReplace/MultipleReplaceWindow.cs index ee43224b341..b1f2ae5d6c8 100644 --- a/src/ui/Features/Edit/MultipleReplace/MultipleReplaceWindow.cs +++ b/src/ui/Features/Edit/MultipleReplace/MultipleReplaceWindow.cs @@ -390,81 +390,77 @@ private static Border MakeFixesView(MultipleReplaceViewModel vm) editGrid.Add(labelType, 0, 2); editGrid.Add(comboBoxType, 1, 2); - var dataGrid = new DataGrid + // No header sorting (the DataGrid's CanUserSortColumns is not carried over): + // this is a fix preview in subtitle order, and the replace rules themselves run + // in list order - reordering the backing collection would scramble the preview. + var dataGrid = TableViewExtras.MakeTableView(multiSelect: false); + dataGrid.DataContext = vm; + dataGrid.ItemsSource = vm.Fixes; + dataGrid.Columns.AddRange(new TableViewColumn[] { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.Fixes, - Columns = + new SeTableViewColumn { - new DataGridTemplateColumn - { - Header = Se.Language.General.Apply, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - CellTemplate = new FuncDataTemplate((item, _) => - new Border - { - Background = Brushes.Transparent, // Prevents highlighting - Padding = new Thickness(4), - Child = new CheckBox - { - [!ToggleButton.IsCheckedProperty] = new Binding(nameof(MultipleReplaceFix.Apply)), - HorizontalAlignment = HorizontalAlignment.Center - } - }), - Width = new DataGridLength(1, DataGridLengthUnitType.Auto) - }, - new DataGridTextColumn - { - Header = Se.Language.General.NumberSymbol, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(MultipleReplaceFix.Number)), - IsReadOnly = true, - }, - new DataGridTemplateColumn - { - Header = Se.Language.General.Before, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - CellTemplate = new FuncDataTemplate((item, _) => + Header = Se.Language.General.Apply, + CellTheme = UiUtil.TableViewNoPaddingCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + CellTemplate = new FuncDataTemplate((item, _) => + new Border { - var (beforeBlock, _) = TextDiffHighlighter.CompareReplacement(item.Before, item.After); - return new Border + Background = Brushes.Transparent, // Prevents highlighting + Padding = new Thickness(4), + Child = new CheckBox { - Background = Brushes.Transparent, - Padding = new Thickness(4), - Child = beforeBlock, - }; + [!ToggleButton.IsCheckedProperty] = new Binding(nameof(MultipleReplaceFix.Apply)), + HorizontalAlignment = HorizontalAlignment.Center + } }), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, - new DataGridTemplateColumn + // Content-sized (Auto) on the DataGrid; TableView treats Auto as star. + Width = new GridLength(70) + }, + new SeTableViewColumn + { + Header = Se.Language.General.NumberSymbol, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(MultipleReplaceFix.Number)), + Width = new GridLength(60), + }, + new SeTableViewColumn + { + Header = Se.Language.General.Before, + CellTheme = UiUtil.TableViewNoPaddingCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + CellTemplate = new FuncDataTemplate((item, _) => { - Header = Se.Language.General.After, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - CellTemplate = new FuncDataTemplate((item, _) => + var (beforeBlock, _) = TextDiffHighlighter.CompareReplacement(item.Before, item.After); + return new Border { - var (_, afterBlock) = TextDiffHighlighter.CompareReplacement(item.Before, item.After); - return new Border - { - Background = Brushes.Transparent, - Padding = new Thickness(4), - Child = afterBlock, - }; - }), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, + Background = Brushes.Transparent, + Padding = new Thickness(4), + Child = beforeBlock, + }; + }), + Width = new GridLength(1, GridUnitType.Star), }, - }; - dataGrid.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedFix)) { Source = vm }); + new SeTableViewColumn + { + Header = Se.Language.General.After, + CellTheme = UiUtil.TableViewNoPaddingCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + CellTemplate = new FuncDataTemplate((item, _) => + { + var (_, afterBlock) = TextDiffHighlighter.CompareReplacement(item.Before, item.After); + return new Border + { + Background = Brushes.Transparent, + Padding = new Thickness(4), + Child = afterBlock, + }; + }), + Width = new GridLength(1, GridUnitType.Star), + }, + }); + dataGrid.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedFix)) { Source = vm }); var hitsItemsControl = new ItemsControl { diff --git a/src/ui/Features/Files/ImportImages/ImportImagesWindow.cs b/src/ui/Features/Files/ImportImages/ImportImagesWindow.cs index 45e7c96fd8c..22b51e7acf9 100644 --- a/src/ui/Features/Files/ImportImages/ImportImagesWindow.cs +++ b/src/ui/Features/Files/ImportImages/ImportImagesWindow.cs @@ -91,62 +91,56 @@ private static Border MakeImagesView(ImportImagesViewModel vm) var fullTimeConverter = new TimeSpanToDisplayFullConverter(); var shortTimeConverter = new TimeSpanToDisplayShortConverter(); var fileSizeConverter = new FileSizeConverter(); - var dataGrid = new DataGrid + // No header sorting (the DataGrid's CanUserSortColumns is not carried over): + // the caller feeds result.Images to OCR in collection order, so reordering the + // backing collection would reorder the resulting subtitle. + var dataGrid = TableViewExtras.MakeTableView(multiSelect: false); + dataGrid.DataContext = vm; + dataGrid.ItemsSource = vm.Images; + dataGrid.Columns.AddRange(new TableViewColumn[] { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.Images, - Columns = + new SeTableViewColumn { - new DataGridTextColumn - { - Header = Se.Language.General.FileName, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(ImportImageItem.FileName)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Size, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(ImportImageItem.Size)) { Converter = fileSizeConverter, Mode = BindingMode.OneWay }, - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, - new DataGridTextColumn - { - Header = Se.Language.General.Show, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(ImportImageItem.Start)) { Converter = fullTimeConverter, Mode = BindingMode.OneWay }, - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, - new DataGridTextColumn - { - Header = Se.Language.General.Hide, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(ImportImageItem.End)){ Converter = fullTimeConverter, Mode = BindingMode.OneWay }, - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, - new DataGridTextColumn - { - Header = Se.Language.General.Duration, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(ImportImageItem.Duration)){ Converter = shortTimeConverter, Mode = BindingMode.OneWay }, - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, + Header = Se.Language.General.FileName, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(ImportImageItem.FileName)), + Width = new GridLength(1, GridUnitType.Star), }, - }; - dataGrid.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedImage)) { Source = vm }); + new SeTableViewColumn + { + Header = Se.Language.General.Size, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(ImportImageItem.Size)) { Converter = fileSizeConverter, Mode = BindingMode.OneWay }, + Width = new GridLength(100), + }, + new SeTableViewColumn + { + Header = Se.Language.General.Show, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(ImportImageItem.Start)) { Converter = fullTimeConverter, Mode = BindingMode.OneWay }, + Width = new GridLength(130), + }, + new SeTableViewColumn + { + Header = Se.Language.General.Hide, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(ImportImageItem.End)){ Converter = fullTimeConverter, Mode = BindingMode.OneWay }, + Width = new GridLength(130), + }, + new SeTableViewColumn + { + Header = Se.Language.General.Duration, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(ImportImageItem.Duration)){ Converter = shortTimeConverter, Mode = BindingMode.OneWay }, + Width = new GridLength(110), + }, + }); + dataGrid.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedImage)) { Source = vm }); dataGrid.SelectionChanged += vm.DataGridSelectionChanged; dataGrid.KeyDown += vm.AttachmentsDataGridKeyDown; dataGrid.AddHandler(InputElement.KeyDownEvent, (object? _, KeyEventArgs e) => @@ -155,12 +149,16 @@ private static Border MakeImagesView(ImportImagesViewModel vm) { var target = e.Key == Key.Home ? items[0] : items[^1]; dataGrid.SelectedItem = target; - dataGrid.ScrollIntoView(target, null); + if (target != null) + { + dataGrid.ScrollIntoView(target); + } + e.Handled = true; } }, Avalonia.Interactivity.RoutingStrategies.Tunnel); - // hack to make drag and drop work on the DataGrid - also on empty rows + // hack to make drag and drop work on the grid - also on empty rows var dropHost = new Border { Background = Brushes.Transparent, diff --git a/src/ui/Features/Files/ImportPlainText/ImportPlainTextWindow.cs b/src/ui/Features/Files/ImportPlainText/ImportPlainTextWindow.cs index e403328bdc4..aab451ca8db 100644 --- a/src/ui/Features/Files/ImportPlainText/ImportPlainTextWindow.cs +++ b/src/ui/Features/Files/ImportPlainText/ImportPlainTextWindow.cs @@ -156,41 +156,36 @@ private static Grid MakeTextBoxAndControlsView(ImportPlainTextViewModel vm) textBox.TextChanged += (s, e) => vm.PlainTextChanged(); var sizeConverter = new FileSizeConverter(); - var dataGrid = new DataGrid + // No header sorting (the DataGrid's CanUserSortColumns is not carried over): + // the preview builds one subtitle line per file in collection order, so + // reordering the backing collection would reorder the imported lines. + var dataGrid = TableViewExtras.MakeTableView(multiSelect: false); + dataGrid.Height = 348; + dataGrid.DataContext = vm; + dataGrid.ItemsSource = vm.Files; + dataGrid.Columns.AddRange(new TableViewColumn[] { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = 348, - DataContext = vm, - ItemsSource = vm.Files, - Columns = + new SeTableViewColumn { - new DataGridTextColumn - { - Header = Se.Language.General.FileName, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(DisplayFile.FileName)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Auto), - }, - new DataGridTextColumn - { - Header = Se.Language.General.Size, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(DisplayFile.Size)) { Converter = sizeConverter, Mode = BindingMode.OneWay }, - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Auto), - }, + Header = Se.Language.General.FileName, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(DisplayFile.FileName)), + // Content-sized (Auto) on the DataGrid; TableView treats Auto as star. + Width = new GridLength(1, GridUnitType.Star), }, - }; - dataGrid.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedFile)) { Source = vm }); + new SeTableViewColumn + { + Header = Se.Language.General.Size, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(DisplayFile.Size)) { Converter = sizeConverter, Mode = BindingMode.OneWay }, + Width = new GridLength(100), + }, + }); + dataGrid.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedFile)) { Source = vm }); - // hack to make drag and drop work on the DataGrid - also on empty rows + // hack to make drag and drop work on the grid - also on empty rows var dropHost = new Border { Background = Brushes.Transparent, @@ -299,64 +294,57 @@ private static Border MakeSubtitleGridView(ImportPlainTextViewModel vm) var fullTimeConverter = new TimeSpanToDisplayFullConverter(); var shortTimeConverter = new TimeSpanToDisplayShortConverter(); - var fileSizeConverter = new FileSizeConverter(); - var dataGrid = new DataGrid + + // No header sorting (the DataGrid's CanUserSortColumns is not carried over): + // this is the subtitle preview, consumed by the caller in collection order. + var dataGrid = TableViewExtras.MakeTableView(multiSelect: false); + dataGrid.DataContext = vm; + dataGrid.ItemsSource = vm.Subtitles; + dataGrid.Columns.AddRange(new TableViewColumn[] { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.Subtitles, - Columns = + new SeTableViewColumn { - new DataGridTextColumn - { - Header = Se.Language.General.NumberSymbol, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(SubtitleLineViewModel.Number)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Auto), - }, - new DataGridTextColumn - { - Header = Se.Language.General.Show, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(SubtitleLineViewModel.StartTime)) { Converter = fullTimeConverter, Mode = BindingMode.OneWay }, - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Auto), - }, - new DataGridTextColumn - { - Header = Se.Language.General.Hide, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(SubtitleLineViewModel.EndTime)) { Converter = fullTimeConverter, Mode = BindingMode.OneWay }, - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Auto), - }, - new DataGridTextColumn - { - Header = Se.Language.General.Duration, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(SubtitleLineViewModel.Duration)) { Converter = shortTimeConverter, Mode = BindingMode.OneWay }, - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Auto), - }, - new DataGridTextColumn - { - Header = Se.Language.General.Text, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(SubtitleLineViewModel.Text)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, + Header = Se.Language.General.NumberSymbol, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(SubtitleLineViewModel.Number)), + // Content-sized (Auto) on the DataGrid; TableView treats Auto as star. + Width = new GridLength(60), }, - }; - dataGrid.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedSubtitle)) { Source = vm }); + new SeTableViewColumn + { + Header = Se.Language.General.Show, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(SubtitleLineViewModel.StartTime)) { Converter = fullTimeConverter, Mode = BindingMode.OneWay }, + Width = new GridLength(130), + }, + new SeTableViewColumn + { + Header = Se.Language.General.Hide, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(SubtitleLineViewModel.EndTime)) { Converter = fullTimeConverter, Mode = BindingMode.OneWay }, + Width = new GridLength(130), + }, + new SeTableViewColumn + { + Header = Se.Language.General.Duration, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(SubtitleLineViewModel.Duration)) { Converter = shortTimeConverter, Mode = BindingMode.OneWay }, + Width = new GridLength(110), + }, + new SeTableViewColumn + { + Header = Se.Language.General.Text, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(SubtitleLineViewModel.Text)), + Width = new GridLength(1, GridUnitType.Star), + }, + }); + dataGrid.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedSubtitle)) { Source = vm }); grid.Add(dataGrid, 0); diff --git a/src/ui/Features/Files/ManualChosenEncoding/ManualChosenEncodingWindow.cs b/src/ui/Features/Files/ManualChosenEncoding/ManualChosenEncodingWindow.cs index 7d340ef5247..a8576b06075 100644 --- a/src/ui/Features/Files/ManualChosenEncoding/ManualChosenEncodingWindow.cs +++ b/src/ui/Features/Files/ManualChosenEncoding/ManualChosenEncodingWindow.cs @@ -80,45 +80,57 @@ public ManualChosenEncodingWindow(ManualChosenEncodingViewModel vm) private static Border MakeEncodingsView(ManualChosenEncodingViewModel vm) { - var dataGrid = new DataGrid + var dataGrid = TableViewExtras.MakeTableView(multiSelect: false); + dataGrid.DataContext = vm; + dataGrid.ItemsSource = vm.Encodings; + + var columnCodePage = new SeTableViewColumn { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.Encodings, - Columns = - { - new DataGridTextColumn - { - Header = Se.Language.File.ManualChosenEncoding.CodePage, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding("Encoding.CodePage"), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Name, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(TextEncoding.DisplayName)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Group, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding("Encoding.BodyName"), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, - }, + Header = Se.Language.File.ManualChosenEncoding.CodePage, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding("Encoding.CodePage"), + // Content-sized (Auto) on the DataGrid; TableView treats Auto as star. + Width = new GridLength(100), }; - dataGrid.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedEncoding)) { Source = vm }); + var columnName = new SeTableViewColumn + { + Header = Se.Language.General.Name, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(TextEncoding.DisplayName)), + Width = new GridLength(1, GridUnitType.Star), + }; + var columnGroup = new SeTableViewColumn + { + Header = Se.Language.General.Group, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding("Encoding.BodyName"), + Width = new GridLength(180), + }; + dataGrid.Columns.AddRange(new TableViewColumn[] { columnCodePage, columnName, columnGroup }); + + // An encoding pick list whose order is presentation-only (the caller consumes + // just SelectedEncoding), so header sorting is safe to wire. + static string GetBodyName(TextEncoding encoding) + { + try + { + return encoding.Encoding.BodyName; + } + catch + { + return string.Empty; // some code pages have no body name + } + } + + var sorter = new TableViewHeaderSorter(dataGrid); + sorter.AddSortable(columnCodePage, x => x.Encoding.CodePage) + .AddSortable(columnName, x => x.DisplayName) + .AddSortable(columnGroup, GetBodyName); + + dataGrid.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedEncoding)) { Source = vm }); dataGrid.SelectionChanged += vm.EncodingChanged; return UiUtil.MakeBorderForControlNoPadding(dataGrid); diff --git a/src/ui/Features/Options/Settings/ProfilesExportWindow.cs b/src/ui/Features/Options/Settings/ProfilesExportWindow.cs index 680d012a095..061c1d87dd7 100644 --- a/src/ui/Features/Options/Settings/ProfilesExportWindow.cs +++ b/src/ui/Features/Options/Settings/ProfilesExportWindow.cs @@ -81,47 +81,43 @@ private static Border MakeDataGrid(ProfilesExportViewModel vm) Width = double.NaN, }; - var dataGrid = new DataGrid + // No header sorting (the DataGrid's CanUserSortColumns is not carried over): + // the caller writes result.Profiles to the exported .profile file in collection + // order, so reordering the backing collection would reorder the export. + var dataGrid = TableViewExtras.MakeTableView(multiSelect: false); + dataGrid.DataContext = vm; + dataGrid.Columns.AddRange(new TableViewColumn[] { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - Columns = + new SeTableViewColumn { - new DataGridTemplateColumn + Header = Se.Language.General.Enabled, + CellTheme = UiUtil.TableViewNoPaddingCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + CellTemplate = new FuncDataTemplate((item, _) => + new Border { - Header = Se.Language.General.Enabled, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - CellTemplate = new FuncDataTemplate((item, _) => - new Border + Background = Brushes.Transparent, // Prevents highlighting + Padding = new Thickness(4), + Child = new CheckBox { - Background = Brushes.Transparent, // Prevents highlighting - Padding = new Thickness(4), - Child = new CheckBox - { - [!CheckBox.IsCheckedProperty] = new Binding(nameof(ProfileDisplay.IsSelected)), - HorizontalAlignment = HorizontalAlignment.Center - } - }), - Width = new DataGridLength(1, DataGridLengthUnitType.Auto) - }, - new DataGridTextColumn - { - Header = Se.Language.General.Name, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(ProfileDisplay.Name)), - IsReadOnly = true, - }, + [!CheckBox.IsCheckedProperty] = new Binding(nameof(ProfileDisplay.IsSelected)), + HorizontalAlignment = HorizontalAlignment.Center + } + }), + // Content-sized (Auto) on the DataGrid; TableView treats Auto as star. + Width = new GridLength(80) }, - }; - dataGrid.Bind(DataGrid.ItemsSourceProperty, new Binding(nameof(vm.Profiles)) { Source = vm }); - dataGrid.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedProfile)) { Source = vm }); + new SeTableViewColumn + { + Header = Se.Language.General.Name, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(ProfileDisplay.Name)), + Width = new GridLength(1, GridUnitType.Star), + }, + }); + dataGrid.Bind(TableView.ItemsSourceProperty, new Binding(nameof(vm.Profiles)) { Source = vm }); + dataGrid.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedProfile)) { Source = vm }); grid.Add(dataGrid, 0); diff --git a/src/ui/Features/Options/Settings/ProfilesWindow.cs b/src/ui/Features/Options/Settings/ProfilesWindow.cs index eebdb589d5f..b11a21dccd3 100644 --- a/src/ui/Features/Options/Settings/ProfilesWindow.cs +++ b/src/ui/Features/Options/Settings/ProfilesWindow.cs @@ -81,45 +81,41 @@ private static Border MakeDataGrid(ProfilesViewModel vm) Width = double.NaN, }; - var dataGrid = new DataGrid + // No header sorting (the DataGrid's CanUserSortColumns is not carried over): + // the caller takes result.Profiles in collection order and saves it back to the + // settings, so reordering the backing collection would reorder the saved profiles. + var dataGrid = TableViewExtras.MakeTableView(multiSelect: false); + dataGrid.DataContext = vm; + dataGrid.Columns.AddRange(new TableViewColumn[] { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - Columns = + new SeTableViewColumn { - new DataGridTextColumn - { - Header = Se.Language.General.Name, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(ProfileDisplay.Name)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.Options.Settings.SingleLineMaxLength, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(ProfileDisplay.SingleLineMaxLength)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.MaxCharactersPerSecond, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(ProfileDisplay.MaxCharsPerSec)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, + Header = Se.Language.General.Name, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(ProfileDisplay.Name)), + // Content-sized (Auto) on the DataGrid; TableView treats Auto as star. + Width = new GridLength(1, GridUnitType.Star), }, - }; - dataGrid.Bind(DataGrid.ItemsSourceProperty, new Binding(nameof(vm.Profiles)) { Source = vm }); - dataGrid.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedProfile)) { Source = vm }); + new SeTableViewColumn + { + Header = Se.Language.Options.Settings.SingleLineMaxLength, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(ProfileDisplay.SingleLineMaxLength)), + Width = new GridLength(180), + }, + new SeTableViewColumn + { + Header = Se.Language.General.MaxCharactersPerSecond, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(ProfileDisplay.MaxCharsPerSec)), + Width = new GridLength(200), + }, + }); + dataGrid.Bind(TableView.ItemsSourceProperty, new Binding(nameof(vm.Profiles)) { Source = vm }); + dataGrid.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedProfile)) { Source = vm }); var buttonNew = UiUtil.MakeButton(vm.NewCommand, IconNames.New, Se.Language.General.NewProfile); var buttonExport = UiUtil.MakeButton(vm.ExportCommand, IconNames.Export, Se.Language.General.ExportDotDotDot); diff --git a/src/ui/Features/Shared/PickFontName/PickFontNameViewModel.cs b/src/ui/Features/Shared/PickFontName/PickFontNameViewModel.cs index c0432acd342..76cf8141251 100644 --- a/src/ui/Features/Shared/PickFontName/PickFontNameViewModel.cs +++ b/src/ui/Features/Shared/PickFontName/PickFontNameViewModel.cs @@ -194,7 +194,7 @@ internal void OnKeyDown(KeyEventArgs e) } } - internal void DataGridFontNameSelectionChanged(object? sender, SelectionChangedEventArgs e) + internal void FontNameGridSelectionChanged(object? sender, SelectionChangedEventArgs e) { _dirtyPreview = true; } diff --git a/src/ui/Features/Shared/PickFontName/PickFontNameWindow.cs b/src/ui/Features/Shared/PickFontName/PickFontNameWindow.cs index abb4e962a9c..9f3d54065ec 100644 --- a/src/ui/Features/Shared/PickFontName/PickFontNameWindow.cs +++ b/src/ui/Features/Shared/PickFontName/PickFontNameWindow.cs @@ -114,32 +114,29 @@ public PickFontNameWindow(PickFontNameViewModel vm) private static Border MakeFontsView(PickFontNameViewModel vm) { - var dataGrid = new DataGrid + var dataGrid = TableViewExtras.MakeTableView(multiSelect: false); + dataGrid.Width = double.NaN; + dataGrid.Height = double.NaN; + dataGrid.DataContext = vm; + dataGrid.ItemsSource = vm.FontNames; + + var fontNameColumn = new SeTableViewColumn { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.FontNames, - Columns = - { - new DataGridTextColumn - { - Header = Se.Language.General.FontName, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding("."), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, - }, + Header = Se.Language.General.FontName, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding("."), + Width = new GridLength(1, GridUnitType.Star), }; - dataGrid.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedFontName))); - dataGrid.SelectionChanged += vm.DataGridFontNameSelectionChanged; + dataGrid.Columns.Add(fontNameColumn); + + dataGrid.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedFontName))); + dataGrid.SelectionChanged += vm.FontNameGridSelectionChanged; + + // Font list order is presentation-only (OK uses the selected item), so the + // in-place header sorter is safe. Note a new search resets the order. + new TableViewHeaderSorter(dataGrid) + .AddSortable(fontNameColumn, x => x); return UiUtil.MakeBorderForControlNoPadding(dataGrid); } diff --git a/src/ui/Features/Shared/PickLayerFilter/PickLayerFilterViewModel.cs b/src/ui/Features/Shared/PickLayerFilter/PickLayerFilterViewModel.cs index a339ffb4f78..22fb3c530eb 100644 --- a/src/ui/Features/Shared/PickLayerFilter/PickLayerFilterViewModel.cs +++ b/src/ui/Features/Shared/PickLayerFilter/PickLayerFilterViewModel.cs @@ -4,6 +4,7 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Nikse.SubtitleEdit.Features.Main; +using Nikse.SubtitleEdit.Logic; using Nikse.SubtitleEdit.Logic.Config; using System; using System.Collections.Generic; @@ -24,13 +25,13 @@ public partial class PickLayerFilterViewModel : ObservableObject public bool OkPressed { get; private set; } public List? SelectedLayers { get; private set; } - public DataGrid LayerGrid { get; set; } + public TableView LayerGrid { get; set; } public PickLayerFilterViewModel() { Layers = new ObservableCollection(); SelectedLayers = new List(); - LayerGrid = new DataGrid(); + LayerGrid = new TableView(); HideFromWaveform = Se.Settings.Assa.HideLayersFromWaveform; HideFromGridView = Se.Settings.Assa.HideLayersFromSubtitleGrid; HideFromVideoPreview = Se.Settings.Assa.HideLayersFromVideoPreview; @@ -130,38 +131,33 @@ internal void Loaded() SelectAndScrollToRow(LayerGrid, 0); } - private void SelectAndScrollToRow(DataGrid? datagrid, int index) + private void SelectAndScrollToRow(TableView? tableView, int index) { - if (index < 0 || datagrid == null) + if (index < 0 || tableView == null) { return; } Dispatcher.UIThread.Post(() => { - datagrid.Focus(); + if (tableView.SelectedIndex != index) + { + tableView.SelectedIndex = index; + } - if (datagrid.SelectedIndex != index) + if (tableView.SelectedItem is { } selectedItem) { - datagrid.SelectedIndex = index; + tableView.ScrollIntoView(selectedItem); } - datagrid.ScrollIntoView(datagrid.SelectedItem, null); + TableViewExtras.FocusRow(tableView); }); } + // Space (toggle checkbox) is handled by TableViewExtras.AddSpaceToggle in the window. internal void LayerGridKeyDown(KeyEventArgs e) { - if (e.Key == Key.Space) - { - e.Handled = true; - var selectedItem = LayerGrid.SelectedItem as LayerItem; - if (selectedItem != null) - { - selectedItem.IsSelected = !selectedItem.IsSelected; - } - } - else if (e.Key == Key.Enter || e.Key == Key.Return) + if (e.Key == Key.Enter || e.Key == Key.Return) { e.Handled = true; Ok(); diff --git a/src/ui/Features/Shared/PickLayerFilter/PickLayerFilterWindow.cs b/src/ui/Features/Shared/PickLayerFilter/PickLayerFilterWindow.cs index bb4de804822..c9614d4b992 100644 --- a/src/ui/Features/Shared/PickLayerFilter/PickLayerFilterWindow.cs +++ b/src/ui/Features/Shared/PickLayerFilter/PickLayerFilterWindow.cs @@ -97,67 +97,84 @@ private static Border MakeSettings(PickLayerFilterViewModel vm) private static Border MakeLayersView(PickLayerFilterViewModel vm) { - var dataGrid = new DataGrid + var dataGrid = TableViewExtras.MakeTableView(multiSelect: false); + dataGrid.Width = double.NaN; + dataGrid.Height = double.NaN; + dataGrid.DataContext = vm; + dataGrid.ItemsSource = vm.Layers; + + var visibleColumn = new SeTableViewColumn { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.Layers, - Columns = + Header = Se.Language.General.Visible, + CellTheme = UiUtil.TableViewNoPaddingCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + CellTemplate = new FuncDataTemplate((item, _) => + new Border { - new DataGridTemplateColumn - { - Header = Se.Language.General.Visible, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - CellTemplate = new FuncDataTemplate((item, _) => - new Border - { - Background = Brushes.Transparent, // Prevents highlighting - Padding = new Thickness(4), - Child = new CheckBox - { - [!ToggleButton.IsCheckedProperty] = new Binding(nameof(LayerItem.IsSelected)), - HorizontalAlignment = HorizontalAlignment.Center - } - }), - Width = new DataGridLength(1, DataGridLengthUnitType.Auto) - }, - new DataGridTextColumn - { - Header = Se.Language.General.Layer, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(LayerItem.Layer)), - IsReadOnly = true, - }, - new DataGridTextColumn + Background = Brushes.Transparent, // Prevents highlighting + Padding = new Thickness(4), + Child = new CheckBox { - Header = Se.Language.General.Usages, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(LayerItem.UsageCount)), - IsReadOnly = true, - }, - }, + Focusable = false, + [!ToggleButton.IsCheckedProperty] = new Binding(nameof(LayerItem.IsSelected)), + HorizontalAlignment = HorizontalAlignment.Center + } + }), + // Content-sized (Auto) on the DataGrid; TableView treats Auto as star. + Width = new GridLength(80), + }; + dataGrid.Columns.Add(visibleColumn); + + var layerColumn = new SeTableViewColumn + { + Header = Se.Language.General.Layer, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(LayerItem.Layer)), + Width = new GridLength(100), }; - dataGrid.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedLayer))); + dataGrid.Columns.Add(layerColumn); + + var usagesColumn = new SeTableViewColumn + { + Header = Se.Language.General.Usages, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(LayerItem.UsageCount)), + Width = new GridLength(1, GridUnitType.Star), + }; + dataGrid.Columns.Add(usagesColumn); + + dataGrid.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedLayer))); vm.LayerGrid = dataGrid; dataGrid.KeyDown += (s, e) => vm.LayerGridKeyDown(e); + + // Space toggles the checkbox (tunnel handler, so it runs before the ListBox + // machinery can swallow the key). + TableViewExtras.AddSpaceToggle(dataGrid, + item => item.IsSelected, (item, v) => item.IsSelected = v); + dataGrid.AddHandler(InputElement.KeyDownEvent, (object? _, KeyEventArgs e) => { if (e.Key is Key.Home or Key.End && dataGrid.ItemsSource is IList items && items.Count > 0) { var target = e.Key == Key.Home ? items[0] : items[^1]; dataGrid.SelectedItem = target; - dataGrid.ScrollIntoView(target, null); + if (target != null) + { + dataGrid.ScrollIntoView(target); + } + e.Handled = true; } }, Avalonia.Interactivity.RoutingStrategies.Tunnel); + // Layer list order is presentation-only: OK collects the checked layers as a + // set of layer numbers, so in-place sorting is safe. + new TableViewHeaderSorter(dataGrid) + .AddSortable(layerColumn, x => x.Layer) + .AddSortable(usagesColumn, x => x.UsageCount); + return UiUtil.MakeBorderForControlNoPadding(dataGrid); } } diff --git a/src/ui/Features/Shared/PickMatroskaTrack/PickMatroskaTrackViewModel.cs b/src/ui/Features/Shared/PickMatroskaTrack/PickMatroskaTrackViewModel.cs index f5d537e6f08..d81038b9872 100644 --- a/src/ui/Features/Shared/PickMatroskaTrack/PickMatroskaTrackViewModel.cs +++ b/src/ui/Features/Shared/PickMatroskaTrack/PickMatroskaTrackViewModel.cs @@ -33,7 +33,7 @@ public partial class PickMatroskaTrackViewModel : ObservableObject [ObservableProperty] private string _subtitleCountText; public Window? Window { get; set; } - public DataGrid TracksGrid { get; set; } + public TableView TracksGrid { get; set; } public MatroskaTrackInfo? SelectedMatroskaTrack { get; set; } public bool OkPressed { get; private set; } public string WindowTitle { get; private set; } @@ -62,7 +62,7 @@ public PickMatroskaTrackViewModel(IFileHelper fileHelper, IWindowService windowS _fileHelper = fileHelper; _windowService = windowService; Tracks = new ObservableCollection(); - TracksGrid = new DataGrid(); + TracksGrid = new TableView(); WindowTitle = string.Empty; SubtitleCountText = string.Empty; Rows = new ObservableCollection(); @@ -217,14 +217,14 @@ internal void OnKeyDownHandler(object? sender, KeyEventArgs e) Cancel(); e.Handled = true; } - else if (e.Key == Key.Enter && TracksGrid.IsFocused) + else if (e.Key == Key.Enter && TracksGrid.IsKeyboardFocusWithin) { Ok(); e.Handled = true; } } - internal void DataGridTracksSelectionChanged(object? sender, SelectionChangedEventArgs e) + internal void TracksGridSelectionChanged(object? sender, SelectionChangedEventArgs e) { _ = TrackChangedAsync(); } @@ -415,7 +415,11 @@ internal void SelectAndScrollToRow(int index) Dispatcher.UIThread.Post(() => { TracksGrid.SelectedIndex = index; - TracksGrid.ScrollIntoView(TracksGrid.SelectedItem, null); + if (TracksGrid.SelectedItem is { } selectedItem) + { + TracksGrid.ScrollIntoView(selectedItem); + } + _ = TrackChangedAsync(); }, DispatcherPriority.Background); } diff --git a/src/ui/Features/Shared/PickMatroskaTrack/PickMatroskaTrackWindow.cs b/src/ui/Features/Shared/PickMatroskaTrack/PickMatroskaTrackWindow.cs index 10f76dacfc9..5895fc07d93 100644 --- a/src/ui/Features/Shared/PickMatroskaTrack/PickMatroskaTrackWindow.cs +++ b/src/ui/Features/Shared/PickMatroskaTrack/PickMatroskaTrackWindow.cs @@ -70,7 +70,7 @@ public PickMatroskaTrackWindow(PickMatroskaTrackViewModel vm) Dispatcher.UIThread.InvokeAsync(() => { vm.SelectAndScrollToRow(0); - vm.TracksGrid.Focus(); + TableViewExtras.FocusRow(vm.TracksGrid); }, DispatcherPriority.Input); }; } @@ -78,70 +78,86 @@ public PickMatroskaTrackWindow(PickMatroskaTrackViewModel vm) private static Border MakeTracksView(PickMatroskaTrackViewModel vm) { var booleanToCheckMarkConverter = new BooleanToCheckMarkConverter(); - var dataGridTracks = new DataGrid + var dataGridTracks = TableViewExtras.MakeTableView(multiSelect: false); + dataGridTracks.Width = double.NaN; + dataGridTracks.Height = double.NaN; + dataGridTracks.DataContext = vm; + dataGridTracks.ItemsSource = vm.Tracks; + + // Content-sized (Auto) on the DataGrid; TableView treats Auto as star, so the + // narrow columns get fixed widths and Name becomes the star column (the old + // grid's star was on the trailing Forced column, which only fills space). + var numberColumn = new SeTableViewColumn { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.Tracks, - Columns = - { - new DataGridTextColumn - { - Header = Se.Language.General.NumberSymbol, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(MatroskaTrackInfoDisplay.TrackNumber)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Name , - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(MatroskaTrackInfoDisplay.Name)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Language, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(MatroskaTrackInfoDisplay.Language)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Codec, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(MatroskaTrackInfoDisplay.Codec)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Default, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(MatroskaTrackInfoDisplay.IsDefault)) { Mode = BindingMode.OneWay, Converter = booleanToCheckMarkConverter }, - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Forced, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(MatroskaTrackInfoDisplay.IsForced)) { Mode = BindingMode.OneWay, Converter = booleanToCheckMarkConverter}, - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, - }, + Header = Se.Language.General.NumberSymbol, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(MatroskaTrackInfoDisplay.TrackNumber)), + Width = new GridLength(60), }; - dataGridTracks.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedTrack))); - dataGridTracks.SelectionChanged += vm.DataGridTracksSelectionChanged; + var nameColumn = new SeTableViewColumn + { + Header = Se.Language.General.Name, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(MatroskaTrackInfoDisplay.Name)), + Width = new GridLength(1, GridUnitType.Star), + }; + var languageColumn = new SeTableViewColumn + { + Header = Se.Language.General.Language, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(MatroskaTrackInfoDisplay.Language)), + Width = new GridLength(100), + }; + var codecColumn = new SeTableViewColumn + { + Header = Se.Language.General.Codec, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(MatroskaTrackInfoDisplay.Codec)), + Width = new GridLength(140), + }; + var defaultColumn = new SeTableViewColumn + { + Header = Se.Language.General.Default, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(MatroskaTrackInfoDisplay.IsDefault)) { Mode = BindingMode.OneWay, Converter = booleanToCheckMarkConverter }, + Width = new GridLength(80), + }; + var forcedColumn = new SeTableViewColumn + { + Header = Se.Language.General.Forced, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(MatroskaTrackInfoDisplay.IsForced)) { Mode = BindingMode.OneWay, Converter = booleanToCheckMarkConverter }, + Width = new GridLength(80), + }; + + dataGridTracks.Columns.Add(numberColumn); + dataGridTracks.Columns.Add(nameColumn); + dataGridTracks.Columns.Add(languageColumn); + dataGridTracks.Columns.Add(codecColumn); + dataGridTracks.Columns.Add(defaultColumn); + dataGridTracks.Columns.Add(forcedColumn); + + dataGridTracks.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedTrack))); + dataGridTracks.SelectionChanged += vm.TracksGridSelectionChanged; dataGridTracks.DoubleTapped += (_, _) => vm.OkCommand.Execute(null); vm.TracksGrid = dataGridTracks; + // Track order is presentation-only (OK uses the selected item), so the + // in-place header sorter is safe. + new TableViewHeaderSorter(dataGridTracks) + .AddSortable(numberColumn, x => x.TrackNumber) + .AddSortable(nameColumn, x => x.Name) + .AddSortable(languageColumn, x => x.Language) + .AddSortable(codecColumn, x => x.Codec) + .AddSortable(defaultColumn, x => x.IsDefault) + .AddSortable(forcedColumn, x => x.IsForced); + return UiUtil.MakeBorderForControlNoPadding(dataGridTracks); } @@ -149,46 +165,45 @@ private static Border MakeSubtitleView(PickMatroskaTrackViewModel vm) { var fullTimeConverter = new TimeSpanToDisplayFullConverter(); var shortTimeConverter = new TimeSpanToDisplayShortConverter(); - var dataGridSubtitle = new DataGrid + var dataGridSubtitle = TableViewExtras.MakeTableView(multiSelect: false); + dataGridSubtitle.Width = double.NaN; + dataGridSubtitle.Height = double.NaN; + dataGridSubtitle.DataContext = vm; + dataGridSubtitle.ItemsSource = vm.Rows; + + // No sorter here: the preview shows subtitle cues in subtitle order. + dataGridSubtitle.Columns.AddRange(new TableViewColumn[] { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.Rows, - Columns = - { - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.NumberSymbol, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(MatroskaSubtitleCueDisplay.Number)), - IsReadOnly = true, + Width = new GridLength(60), }, - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.Show, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(MatroskaSubtitleCueDisplay.Show)) { Converter = fullTimeConverter }, - IsReadOnly = true, + Width = new GridLength(120), }, - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.Duration, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(MatroskaSubtitleCueDisplay.Duration)) { Converter = shortTimeConverter }, - IsReadOnly = true, + Width = new GridLength(90), }, - new DataGridTemplateColumn + new SeTableViewColumn { Header = Se.Language.General.TextOrImage, - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Width = new GridLength(1, GridUnitType.Star), CellTemplate = new FuncDataTemplate((item, _) => { var stackPanel = new StackPanel @@ -225,8 +240,7 @@ private static Border MakeSubtitleView(PickMatroskaTrackViewModel vm) return stackPanel; }) }, - }, - }; + }); return UiUtil.MakeBorderForControlNoPadding(dataGridSubtitle); } diff --git a/src/ui/Features/Shared/PickMp4Track/PickMp4TrackViewModel.cs b/src/ui/Features/Shared/PickMp4Track/PickMp4TrackViewModel.cs index 44b669cc8da..97865b19805 100644 --- a/src/ui/Features/Shared/PickMp4Track/PickMp4TrackViewModel.cs +++ b/src/ui/Features/Shared/PickMp4Track/PickMp4TrackViewModel.cs @@ -29,7 +29,7 @@ public partial class PickMp4TrackViewModel : ObservableObject [ObservableProperty] private string _subtitleCountText; public Window? Window { get; set; } - public DataGrid TracksGrid { get; set; } + public TableView TracksGrid { get; set; } public Mp4TrackInfoDisplay? SelectedMatroskaTrack { get; set; } public bool OkPressed { get; private set; } public string WindowTitle { get; private set; } @@ -45,7 +45,7 @@ public PickMp4TrackViewModel(IFileHelper fileHelper, IWindowService windowServic _fileHelper = fileHelper; _windowService = windowService; Tracks = new ObservableCollection(); - TracksGrid = new DataGrid(); + TracksGrid = new TableView(); WindowTitle = string.Empty; SubtitleCountText = string.Empty; Rows = new ObservableCollection(); @@ -192,7 +192,7 @@ internal void OnKeyDown(KeyEventArgs e) } } - internal void DataGridTracksSelectionChanged(object? sender, SelectionChangedEventArgs e) + internal void TracksGridSelectionChanged(object? sender, SelectionChangedEventArgs e) { bool flowControl = TrackChanged(); if (!flowControl) @@ -251,7 +251,11 @@ internal void SelectAndScrollToRow(int index) Dispatcher.UIThread.Post(() => { TracksGrid.SelectedIndex = index; - TracksGrid.ScrollIntoView(TracksGrid.SelectedItem, null); + if (TracksGrid.SelectedItem is { } selectedItem) + { + TracksGrid.ScrollIntoView(selectedItem); + } + TrackChanged(); }, DispatcherPriority.Background); } diff --git a/src/ui/Features/Shared/PickMp4Track/PickMp4TrackWindow.cs b/src/ui/Features/Shared/PickMp4Track/PickMp4TrackWindow.cs index afc21125be4..d1db293c992 100644 --- a/src/ui/Features/Shared/PickMp4Track/PickMp4TrackWindow.cs +++ b/src/ui/Features/Shared/PickMp4Track/PickMp4TrackWindow.cs @@ -80,60 +80,73 @@ protected override void OnLoaded(RoutedEventArgs e) private Border MakeTracksView(PickMp4TrackViewModel vm) { - var dataGridTracks = new DataGrid + var dataGridTracks = TableViewExtras.MakeTableView(multiSelect: false); + dataGridTracks.Width = double.NaN; + dataGridTracks.Height = double.NaN; + dataGridTracks.DataContext = _vm; + dataGridTracks.ItemsSource = _vm.Tracks; + + // Content-sized (Auto) on the DataGrid; TableView treats Auto as star, so the + // narrow columns get fixed widths and Name becomes the star column. + var handlerColumn = new SeTableViewColumn { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = _vm, - ItemsSource = _vm.Tracks, - Columns = - { - new DataGridTextColumn - { - Header = "HandlerName", - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(Mp4TrackInfoDisplay.HandlerType)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = "Name", - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(Mp4TrackInfoDisplay.Name)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = "Duration", - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(Mp4TrackInfoDisplay.Duration)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = "IsVobSubSubtitle", - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(Mp4TrackInfoDisplay.IsVobSubSubtitle)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = "StartPosition", - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(Mp4TrackInfoDisplay.StartPosition)), - IsReadOnly = true, - }, - }, + Header = "HandlerName", + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(Mp4TrackInfoDisplay.HandlerType)), + Width = new GridLength(120), }; - dataGridTracks.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(_vm.SelectedTrack))); - dataGridTracks.SelectionChanged += vm.DataGridTracksSelectionChanged; - vm.TracksGrid = dataGridTracks; + var nameColumn = new SeTableViewColumn + { + Header = "Name", + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(Mp4TrackInfoDisplay.Name)), + Width = new GridLength(1, GridUnitType.Star), + }; + var durationColumn = new SeTableViewColumn + { + Header = "Duration", + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(Mp4TrackInfoDisplay.Duration)), + Width = new GridLength(100), + }; + var vobSubColumn = new SeTableViewColumn + { + Header = "IsVobSubSubtitle", + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(Mp4TrackInfoDisplay.IsVobSubSubtitle)), + Width = new GridLength(130), + }; + var startPositionColumn = new SeTableViewColumn + { + Header = "StartPosition", + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(Mp4TrackInfoDisplay.StartPosition)), + Width = new GridLength(110), + }; + + dataGridTracks.Columns.Add(handlerColumn); + dataGridTracks.Columns.Add(nameColumn); + dataGridTracks.Columns.Add(durationColumn); + dataGridTracks.Columns.Add(vobSubColumn); + dataGridTracks.Columns.Add(startPositionColumn); + + dataGridTracks.Bind(TableView.SelectedItemProperty, new Binding(nameof(_vm.SelectedTrack))); + dataGridTracks.SelectionChanged += vm.TracksGridSelectionChanged; + vm.TracksGrid = dataGridTracks; + + // Track order is presentation-only (OK uses the selected item), so the + // in-place header sorter is safe. + new TableViewHeaderSorter(dataGridTracks) + .AddSortable(handlerColumn, x => x.HandlerType) + .AddSortable(nameColumn, x => x.Name) + .AddSortable(durationColumn, x => x.Duration) + .AddSortable(vobSubColumn, x => x.IsVobSubSubtitle) + .AddSortable(startPositionColumn, x => x.StartPosition); return UiUtil.MakeBorderForControlNoPadding(dataGridTracks); } @@ -142,45 +155,45 @@ private Border MakeSubtitleView(PickMp4TrackViewModel vm) { var fullTimeConverter = new TimeSpanToDisplayFullConverter(); var shortTimeConverter = new TimeSpanToDisplayShortConverter(); - var dataGridSubtitle = new DataGrid + var dataGridSubtitle = TableViewExtras.MakeTableView(multiSelect: false); + dataGridSubtitle.Width = double.NaN; + dataGridSubtitle.Height = double.NaN; + dataGridSubtitle.DataContext = _vm; + dataGridSubtitle.ItemsSource = _vm.Rows; + + // No sorter here: the preview shows subtitle cues in subtitle order. + dataGridSubtitle.Columns.AddRange(new TableViewColumn[] { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = _vm, - ItemsSource = _vm.Rows, - Columns = - { - new DataGridTextColumn + new SeTableViewColumn { Header = "#", - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(Mp4SubtitleCueDisplay.Number)), - IsReadOnly = true, + Width = new GridLength(60), }, - new DataGridTextColumn + new SeTableViewColumn { Header = "Show", - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(Mp4SubtitleCueDisplay.Show)) { Converter = fullTimeConverter }, - IsReadOnly = true, + Width = new GridLength(120), }, - new DataGridTextColumn + new SeTableViewColumn { Header = "Duration", - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(Mp4SubtitleCueDisplay.Duration)) { Converter = shortTimeConverter }, - IsReadOnly = true, + Width = new GridLength(90), }, - new DataGridTemplateColumn + new SeTableViewColumn { Header = "Text/Image", - IsReadOnly = true, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Width = new GridLength(1, GridUnitType.Star), CellTemplate = new FuncDataTemplate((item, _) => { var stackPanel = new StackPanel @@ -217,8 +230,7 @@ private Border MakeSubtitleView(PickMp4TrackViewModel vm) return stackPanel; }) }, - }, - }; + }); return UiUtil.MakeBorderForControlNoPadding(dataGridSubtitle); } diff --git a/src/ui/Features/Shared/PickRuleProfile/PickRuleProfileViewModel.cs b/src/ui/Features/Shared/PickRuleProfile/PickRuleProfileViewModel.cs index 6d675c4a82f..8d977cf47c3 100644 --- a/src/ui/Features/Shared/PickRuleProfile/PickRuleProfileViewModel.cs +++ b/src/ui/Features/Shared/PickRuleProfile/PickRuleProfileViewModel.cs @@ -71,7 +71,7 @@ internal void KeyDown(object? sender, KeyEventArgs e) } } - internal void DataGridDown(object? sender, KeyEventArgs e) + internal void ProfileGridKeyDown(object? sender, KeyEventArgs e) { if (e.Key == Key.Enter) { @@ -80,7 +80,7 @@ internal void DataGridDown(object? sender, KeyEventArgs e) } } - public void DataGridDoubleTapped(object? sender, TappedEventArgs e) + public void ProfileGridDoubleTapped(object? sender, TappedEventArgs e) { if (SelectedProfile != null) { diff --git a/src/ui/Features/Shared/PickRuleProfile/PickRuleProfileWindow.cs b/src/ui/Features/Shared/PickRuleProfile/PickRuleProfileWindow.cs index 2e6e34bd1ee..37c2427a88f 100644 --- a/src/ui/Features/Shared/PickRuleProfile/PickRuleProfileWindow.cs +++ b/src/ui/Features/Shared/PickRuleProfile/PickRuleProfileWindow.cs @@ -11,7 +11,7 @@ namespace Nikse.SubtitleEdit.Features.Shared.PickRuleProfile; public class PickRuleProfileWindow : Window { - private static DataGrid? _profileDataGrid; + private static TableView? _profileGrid; public PickRuleProfileWindow(PickRuleProfileViewModel vm) { @@ -54,60 +54,72 @@ public PickRuleProfileWindow(PickRuleProfileViewModel vm) HorizontalAlignment = HorizontalAlignment.Stretch, }; - grid.Add(MakeDataGrid(vm), 0); + grid.Add(MakeProfilesView(vm), 0); grid.Add(panelButtons, 1); Content = grid; - Activated += delegate { _profileDataGrid?.Focus(); }; // hack to make OnKeyDown work + Activated += delegate + { + if (_profileGrid != null) + { + TableViewExtras.FocusRow(_profileGrid); // hack to make OnKeyDown work + } + }; KeyDown += vm.KeyDown; } - private static Border MakeDataGrid(PickRuleProfileViewModel vm) + private static Border MakeProfilesView(PickRuleProfileViewModel vm) { - var dataGrid = new DataGrid + var dataGrid = TableViewExtras.MakeTableView(multiSelect: false); + dataGrid.Width = double.NaN; + dataGrid.Height = double.NaN; + dataGrid.DataContext = vm; + + // Content-sized (Auto) on the DataGrid; TableView treats Auto as star, so the + // number columns get fixed widths and Name becomes the star column (the old + // grid's star was on the trailing max-CPS column, which only fills space). + var nameColumn = new SeTableViewColumn { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - IsReadOnly = true, - DataContext = vm, - Columns = - { - new DataGridTextColumn - { - Header = Se.Language.General.Name, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(ProfileDisplay.Name)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.Options.Settings.SingleLineMaxLength, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(ProfileDisplay.SingleLineMaxLength)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.MaxCharactersPerSecond, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(ProfileDisplay.MaxCharsPerSec)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, - }, + Header = Se.Language.General.Name, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(ProfileDisplay.Name)), + Width = new GridLength(1, GridUnitType.Star), }; - dataGrid.Bind(DataGrid.ItemsSourceProperty, new Binding(nameof(vm.Profiles)) { Source = vm }); - dataGrid.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedProfile)) { Source = vm }); - dataGrid.AddHandler(KeyDownEvent, vm.DataGridDown, RoutingStrategies.Tunnel); - dataGrid.DoubleTapped += vm.DataGridDoubleTapped; - _profileDataGrid = dataGrid; + var singleLineMaxLengthColumn = new SeTableViewColumn + { + Header = Se.Language.Options.Settings.SingleLineMaxLength, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(ProfileDisplay.SingleLineMaxLength)), + Width = new GridLength(180), + }; + var maxCpsColumn = new SeTableViewColumn + { + Header = Se.Language.General.MaxCharactersPerSecond, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(ProfileDisplay.MaxCharsPerSec)), + Width = new GridLength(180), + }; + + dataGrid.Columns.Add(nameColumn); + dataGrid.Columns.Add(singleLineMaxLengthColumn); + dataGrid.Columns.Add(maxCpsColumn); + + dataGrid.Bind(TableView.ItemsSourceProperty, new Binding(nameof(vm.Profiles)) { Source = vm }); + dataGrid.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedProfile)) { Source = vm }); + dataGrid.AddHandler(KeyDownEvent, vm.ProfileGridKeyDown, RoutingStrategies.Tunnel); + dataGrid.DoubleTapped += vm.ProfileGridDoubleTapped; + _profileGrid = dataGrid; + + // Profile list order is presentation-only (OK uses the selected item), so the + // in-place header sorter is safe. + new TableViewHeaderSorter(dataGrid) + .AddSortable(nameColumn, x => x.Name) + .AddSortable(singleLineMaxLengthColumn, x => x.SingleLineMaxLength) + .AddSortable(maxCpsColumn, x => x.MaxCharsPerSec); return UiUtil.MakeBorderForControlNoPadding(dataGrid); } diff --git a/src/ui/Features/Shared/PickTsTrack/PickTsTrackViewModel.cs b/src/ui/Features/Shared/PickTsTrack/PickTsTrackViewModel.cs index dee43fd6121..355a70f1053 100644 --- a/src/ui/Features/Shared/PickTsTrack/PickTsTrackViewModel.cs +++ b/src/ui/Features/Shared/PickTsTrack/PickTsTrackViewModel.cs @@ -22,7 +22,7 @@ public partial class PickTsTrackViewModel : ObservableObject [ObservableProperty] private string _subtitleCountText; public Window? Window { get; set; } - public DataGrid TracksGrid { get; set; } + public TableView TracksGrid { get; set; } public bool OkPressed { get; private set; } public string WindowTitle { get; private set; } public Subtitle TeletextSubtitle { get; private set; } @@ -33,7 +33,7 @@ public partial class PickTsTrackViewModel : ObservableObject public PickTsTrackViewModel() { Tracks = new ObservableCollection(); - TracksGrid = new DataGrid(); + TracksGrid = new TableView(); WindowTitle = string.Empty; SubtitleCountText = string.Empty; Rows = new ObservableCollection(); @@ -119,7 +119,7 @@ internal void OnKeyDown(KeyEventArgs e) } } - internal void DataGridTracksSelectionChanged(object? sender, SelectionChangedEventArgs e) + internal void TracksGridSelectionChanged(object? sender, SelectionChangedEventArgs e) { bool flowControl = TrackChanged(); if (!flowControl) @@ -190,7 +190,11 @@ internal void SelectAndScrollToRow(int index) Dispatcher.UIThread.Post(() => { TracksGrid.SelectedIndex = index; - TracksGrid.ScrollIntoView(TracksGrid.SelectedItem, null); + if (TracksGrid.SelectedItem is { } selectedItem) + { + TracksGrid.ScrollIntoView(selectedItem); + } + TrackChanged(); }, DispatcherPriority.Background); } diff --git a/src/ui/Features/Shared/PickTsTrack/PickTsTrackWindow.cs b/src/ui/Features/Shared/PickTsTrack/PickTsTrackWindow.cs index 407928135ff..84da95ae51e 100644 --- a/src/ui/Features/Shared/PickTsTrack/PickTsTrackWindow.cs +++ b/src/ui/Features/Shared/PickTsTrack/PickTsTrackWindow.cs @@ -71,69 +71,85 @@ public PickTsTrackWindow(PickTsTrackViewModel vm) private Border MakeTracksView(PickTsTrackViewModel vm) { - var dataGridTracks = new DataGrid + var dataGridTracks = TableViewExtras.MakeTableView(multiSelect: false); + dataGridTracks.Width = double.NaN; + dataGridTracks.Height = double.NaN; + dataGridTracks.DataContext = vm; + dataGridTracks.ItemsSource = vm.Tracks; + + // Content-sized (Auto) on the DataGrid; TableView treats Auto as star, so the + // narrow columns get fixed widths and Name becomes the star column. + var numberColumn = new SeTableViewColumn { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.Tracks, - Columns = - { - new DataGridTextColumn - { - Header = Se.Language.General.NumberSymbol, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(TsTrackInfoDisplay.TrackNumber)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Name , - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(TsTrackInfoDisplay.Name)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Language, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(TsTrackInfoDisplay.Language)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Codec, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(TsTrackInfoDisplay.Codec)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Default, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(TsTrackInfoDisplay.IsDefault)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Forced, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(TsTrackInfoDisplay.IsForced)), - IsReadOnly = true, - }, - }, + Header = Se.Language.General.NumberSymbol, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(TsTrackInfoDisplay.TrackNumber)), + Width = new GridLength(60), }; - dataGridTracks.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedTrack))); - dataGridTracks.SelectionChanged += vm.DataGridTracksSelectionChanged; + var nameColumn = new SeTableViewColumn + { + Header = Se.Language.General.Name, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(TsTrackInfoDisplay.Name)), + Width = new GridLength(1, GridUnitType.Star), + }; + var languageColumn = new SeTableViewColumn + { + Header = Se.Language.General.Language, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(TsTrackInfoDisplay.Language)), + Width = new GridLength(100), + }; + var codecColumn = new SeTableViewColumn + { + Header = Se.Language.General.Codec, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(TsTrackInfoDisplay.Codec)), + Width = new GridLength(140), + }; + var defaultColumn = new SeTableViewColumn + { + Header = Se.Language.General.Default, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(TsTrackInfoDisplay.IsDefault)), + Width = new GridLength(80), + }; + var forcedColumn = new SeTableViewColumn + { + Header = Se.Language.General.Forced, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(TsTrackInfoDisplay.IsForced)), + Width = new GridLength(80), + }; + + dataGridTracks.Columns.Add(numberColumn); + dataGridTracks.Columns.Add(nameColumn); + dataGridTracks.Columns.Add(languageColumn); + dataGridTracks.Columns.Add(codecColumn); + dataGridTracks.Columns.Add(defaultColumn); + dataGridTracks.Columns.Add(forcedColumn); + + dataGridTracks.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedTrack))); + dataGridTracks.SelectionChanged += vm.TracksGridSelectionChanged; dataGridTracks.DoubleTapped += (s, e) => vm.OkCommand.Execute(null); vm.TracksGrid = dataGridTracks; + // Track order is presentation-only (OK uses the selected item), so the + // in-place header sorter is safe. + new TableViewHeaderSorter(dataGridTracks) + .AddSortable(numberColumn, x => x.TrackNumber) + .AddSortable(nameColumn, x => x.Name) + .AddSortable(languageColumn, x => x.Language) + .AddSortable(codecColumn, x => x.Codec) + .AddSortable(defaultColumn, x => x.IsDefault) + .AddSortable(forcedColumn, x => x.IsForced); + return UiUtil.MakeBorderForControlNoPadding(dataGridTracks); } @@ -141,45 +157,45 @@ private static Border MakeSubtitleView(PickTsTrackViewModel vm) { var fullTimeConverter = new TimeSpanToDisplayFullConverter(); var shortTimeConverter = new TimeSpanToDisplayShortConverter(); - var dataGridSubtitle = new DataGrid + var dataGridSubtitle = TableViewExtras.MakeTableView(multiSelect: false); + dataGridSubtitle.Width = double.NaN; + dataGridSubtitle.Height = double.NaN; + dataGridSubtitle.DataContext = vm; + dataGridSubtitle.ItemsSource = vm.Rows; + + // No sorter here: the preview shows subtitle cues in subtitle order. + dataGridSubtitle.Columns.AddRange(new TableViewColumn[] { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.Rows, - Columns = - { - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.NumberSymbol, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(TsSubtitleCueDisplay.Number)), - IsReadOnly = true, + Width = new GridLength(60), }, - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.Show, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(TsSubtitleCueDisplay.Show)) { Converter = fullTimeConverter }, - IsReadOnly = true, + Width = new GridLength(120), }, - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.Duration, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(TsSubtitleCueDisplay.Duration)) { Converter = shortTimeConverter }, - IsReadOnly = true, + Width = new GridLength(90), }, - new DataGridTemplateColumn + new SeTableViewColumn { Header = Se.Language.General.TextOrImage, - IsReadOnly = true, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Width = new GridLength(1, GridUnitType.Star), CellTemplate = new FuncDataTemplate((item, _) => { var stackPanel = new StackPanel @@ -216,8 +232,7 @@ private static Border MakeSubtitleView(PickTsTrackViewModel vm) return stackPanel; }) }, - }, - }; + }); return UiUtil.MakeBorderForControlNoPadding(dataGridSubtitle); } diff --git a/src/ui/Features/Shared/PickVobSubLanguage/PickVobSubLanguageViewModel.cs b/src/ui/Features/Shared/PickVobSubLanguage/PickVobSubLanguageViewModel.cs index b33c91b64d2..9664e315fb9 100644 --- a/src/ui/Features/Shared/PickVobSubLanguage/PickVobSubLanguageViewModel.cs +++ b/src/ui/Features/Shared/PickVobSubLanguage/PickVobSubLanguageViewModel.cs @@ -21,7 +21,7 @@ public partial class PickVobSubLanguageViewModel : ObservableObject [ObservableProperty] private ObservableCollection _rows; public Window? Window { get; set; } - public DataGrid LanguagesGrid { get; set; } + public TableView LanguagesGrid { get; set; } public bool OkPressed { get; private set; } public string WindowTitle { get; private set; } @@ -37,7 +37,7 @@ public PickVobSubLanguageViewModel() { Languages = new ObservableCollection(); Rows = new ObservableCollection(); - LanguagesGrid = new DataGrid(); + LanguagesGrid = new TableView(); WindowTitle = string.Empty; SelectedLanguageString = string.Empty; _streamIdDictionary = new Dictionary>(); @@ -101,14 +101,14 @@ internal void OnKeyDownHandler(object? sender, KeyEventArgs e) Cancel(); e.Handled = true; } - else if (e.Key == Key.Enter && LanguagesGrid.IsFocused) + else if (e.Key == Key.Enter && LanguagesGrid.IsKeyboardFocusWithin) { Ok(); e.Handled = true; } } - internal void DataGridLanguagesSelectionChanged(object? sender, SelectionChangedEventArgs e) + internal void LanguagesGridSelectionChanged(object? sender, SelectionChangedEventArgs e) { LanguageChanged(); } @@ -152,7 +152,11 @@ internal void SelectAndScrollToRow(int index) Dispatcher.UIThread.Post(() => { LanguagesGrid.SelectedIndex = index; - LanguagesGrid.ScrollIntoView(LanguagesGrid.SelectedItem, null); + if (LanguagesGrid.SelectedItem is { } selectedItem) + { + LanguagesGrid.ScrollIntoView(selectedItem); + } + LanguageChanged(); }, DispatcherPriority.Background); } diff --git a/src/ui/Features/Shared/PickVobSubLanguage/PickVobSubLanguageWindow.cs b/src/ui/Features/Shared/PickVobSubLanguage/PickVobSubLanguageWindow.cs index 93e99d87206..ae166d96167 100644 --- a/src/ui/Features/Shared/PickVobSubLanguage/PickVobSubLanguageWindow.cs +++ b/src/ui/Features/Shared/PickVobSubLanguage/PickVobSubLanguageWindow.cs @@ -62,56 +62,62 @@ public PickVobSubLanguageWindow(PickVobSubLanguageViewModel vm) Dispatcher.UIThread.InvokeAsync(() => { vm.SelectAndScrollToRow(0); - vm.LanguagesGrid.Focus(); + TableViewExtras.FocusRow(vm.LanguagesGrid); }, DispatcherPriority.Input); }; } private static Border MakeLanguagesView(PickVobSubLanguageViewModel vm) { - var dataGrid = new DataGrid + var dataGrid = TableViewExtras.MakeTableView(multiSelect: false); + dataGrid.Width = double.NaN; + dataGrid.Height = double.NaN; + dataGrid.DataContext = vm; + dataGrid.ItemsSource = vm.Languages; + + // Content-sized (Auto) on the DataGrid; TableView treats Auto as star, so the + // stream-id and count columns get fixed widths (Language keeps the star). + var streamIdColumn = new SeTableViewColumn { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.Languages, - Columns = - { - new DataGridTextColumn - { - Header = Se.Language.General.NumberSymbol, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(VobSubLanguageDisplay.StreamIdHex)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Language, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(VobSubLanguageDisplay.Language)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, - new DataGridTextColumn - { - Header = Se.Language.General.Count, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(VobSubLanguageDisplay.Count)), - IsReadOnly = true, - }, - }, + Header = Se.Language.General.NumberSymbol, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(VobSubLanguageDisplay.StreamIdHex)), + Width = new GridLength(80), }; - dataGrid.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedLanguage))); - dataGrid.SelectionChanged += vm.DataGridLanguagesSelectionChanged; + var languageColumn = new SeTableViewColumn + { + Header = Se.Language.General.Language, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(VobSubLanguageDisplay.Language)), + Width = new GridLength(1, GridUnitType.Star), + }; + var countColumn = new SeTableViewColumn + { + Header = Se.Language.General.Count, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(VobSubLanguageDisplay.Count)), + Width = new GridLength(80), + }; + + dataGrid.Columns.Add(streamIdColumn); + dataGrid.Columns.Add(languageColumn); + dataGrid.Columns.Add(countColumn); + + dataGrid.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedLanguage))); + dataGrid.SelectionChanged += vm.LanguagesGridSelectionChanged; dataGrid.DoubleTapped += (_, _) => vm.OkCommand.Execute(null); vm.LanguagesGrid = dataGrid; + // Language list order is presentation-only (OK uses the selected item), so the + // in-place header sorter is safe. The hex column sorts by the numeric stream id. + new TableViewHeaderSorter(dataGrid) + .AddSortable(streamIdColumn, x => x.StreamId) + .AddSortable(languageColumn, x => x.Language) + .AddSortable(countColumn, x => x.Count); + return UiUtil.MakeBorderForControlNoPadding(dataGrid); } @@ -119,46 +125,46 @@ private static Border MakePreviewView(PickVobSubLanguageViewModel vm) { var fullTimeConverter = new TimeSpanToDisplayFullConverter(); var shortTimeConverter = new TimeSpanToDisplayShortConverter(); - var dataGrid = new DataGrid + var dataGrid = TableViewExtras.MakeTableView(multiSelect: false); + dataGrid.Width = double.NaN; + dataGrid.Height = double.NaN; + dataGrid.DataContext = vm; + dataGrid.ItemsSource = vm.Rows; + + // No sorter here: the preview shows subtitle cues in subtitle order (the old + // DataGrid had sorting disabled too). + dataGrid.Columns.AddRange(new TableViewColumn[] { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = false, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.Rows, - Columns = - { - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.NumberSymbol, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(VobSubLanguageCueDisplay.Number)), - IsReadOnly = true, + Width = new GridLength(60), }, - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.Show, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(VobSubLanguageCueDisplay.Show)) { Converter = fullTimeConverter }, - IsReadOnly = true, + Width = new GridLength(120), }, - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.Duration, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(VobSubLanguageCueDisplay.Duration)) { Converter = shortTimeConverter }, - IsReadOnly = true, + Width = new GridLength(90), }, - new DataGridTemplateColumn + new SeTableViewColumn { Header = Se.Language.General.Image, - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Width = new GridLength(1, GridUnitType.Star), CellTemplate = new Avalonia.Controls.Templates.FuncDataTemplate((item, _) => { if (item.Image == null) @@ -175,8 +181,7 @@ private static Border MakePreviewView(PickVobSubLanguageViewModel vm) }; }), }, - }, - }; + }); return UiUtil.MakeBorderForControlNoPadding(dataGrid); } diff --git a/src/ui/Features/Ssa/SsaAttachmentsWindow.cs b/src/ui/Features/Ssa/SsaAttachmentsWindow.cs index 5706e52469c..58a993a4a1a 100644 --- a/src/ui/Features/Ssa/SsaAttachmentsWindow.cs +++ b/src/ui/Features/Ssa/SsaAttachmentsWindow.cs @@ -92,58 +92,42 @@ private static Border MakeLeftView(SsaAttachmentsViewModel vm) HorizontalAlignment = HorizontalAlignment.Stretch, }; - var dataGrid = new DataGrid + // No header sorting: the attachment order is written back to the subtitle + // footer ([Fonts]/[Graphics] sections) in list order on OK, so the collection + // order is not presentation-only. + var dataGrid = TableViewExtras.MakeTableView(multiSelect: false); + dataGrid.DataContext = vm; + dataGrid.ItemsSource = vm.Attachments; + + dataGrid.Columns.Add(new SeTableViewColumn { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.Attachments, - Columns = - { - new DataGridTextColumn - { - Header = Se.Language.General.FileName, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(SsaAttachmentItem.FileName)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Type, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(SsaAttachmentItem.Category)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, - new DataGridTextColumn - { - Header = Se.Language.General.Size, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(SsaAttachmentItem.Size)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, - }, - }; - dataGrid.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedAttachment)) { Source = vm }); + Header = Se.Language.General.FileName, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(SsaAttachmentItem.FileName)), + Width = new GridLength(1, GridUnitType.Star), + }); + dataGrid.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.Type, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(SsaAttachmentItem.Category)), + Width = new GridLength(140), + }); + dataGrid.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.Size, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(SsaAttachmentItem.Size)), + Width = new GridLength(100), + }); + + dataGrid.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedAttachment)) { Source = vm }); dataGrid.SelectionChanged += vm.DataGridSelectionChanged; dataGrid.KeyDown += vm.AttachmentsDataGridKeyDown; - dataGrid.AddHandler(InputElement.KeyDownEvent, (object? _, KeyEventArgs e) => - { - if (e.Key is Key.Home or Key.End && dataGrid.ItemsSource is IList items && items.Count > 0) - { - var target = e.Key == Key.Home ? items[0] : items[^1]; - dataGrid.SelectedItem = target; - dataGrid.ScrollIntoView(target, null); - e.Handled = true; - } - }, Avalonia.Interactivity.RoutingStrategies.Tunnel); + TableViewExtras.AttachHomeEndNavigation(dataGrid); var flyout = new MenuFlyout(); flyout.Opening += vm.AttachmentsContextMenuOpening; diff --git a/src/ui/Features/Ssa/SsaStylesViewModel.cs b/src/ui/Features/Ssa/SsaStylesViewModel.cs index 0a2a20b3055..50310a8da6e 100644 --- a/src/ui/Features/Ssa/SsaStylesViewModel.cs +++ b/src/ui/Features/Ssa/SsaStylesViewModel.cs @@ -49,8 +49,8 @@ public partial class SsaStylesViewModel : ObservableObject, IClosingCleanup public Window? Window { get; set; } public bool OkPressed { get; private set; } public string Header { get; set; } - public DataGrid FileStyleGrid { get; set; } - public DataGrid StorageStyleGrid { get; set; } + public TableView FileStyleGrid { get; set; } + public TableView StorageStyleGrid { get; set; } public Subtitle ResultSubtitle => _subtitle; private readonly IFileHelper _fileHelper; @@ -73,8 +73,8 @@ public SsaStylesViewModel(IFileHelper fileHelper, IWindowService windowService) BorderTypes = new ObservableCollection(BorderStyleItem.List()); SelectedBorderType = BorderTypes[0]; CurrentTitle = string.Empty; - FileStyleGrid = new DataGrid(); - StorageStyleGrid = new DataGrid(); + FileStyleGrid = new TableView(); + StorageStyleGrid = new TableView(); Header = string.Empty; _subtitle = new Subtitle(); @@ -216,7 +216,7 @@ private void FileNew() [RelayCommand] private void FileRemove() { - var selectedItems = FileStyleGrid.SelectedItems.Cast().ToList(); + var selectedItems = FileStyleGrid.SelectedItems?.Cast().ToList() ?? new List(); if (Window == null || selectedItems.Count == 0) { return; @@ -240,7 +240,7 @@ private void FileRemoveAll() [RelayCommand] private void FilesDuplicate() { - var selectedItems = FileStyleGrid.SelectedItems.Cast().ToList(); + var selectedItems = FileStyleGrid.SelectedItems?.Cast().ToList() ?? new List(); if (Window == null || selectedItems.Count == 0) { return; @@ -302,7 +302,7 @@ private async Task FileExport() [RelayCommand] private void FileCopyToStorage() { - var selectedItems = FileStyleGrid.SelectedItems.Cast().ToList(); + var selectedItems = FileStyleGrid.SelectedItems?.Cast().ToList() ?? new List(); if (Window == null || selectedItems.Count == 0) { return; @@ -418,7 +418,7 @@ private void StorageNew() [RelayCommand] private void StorageRemove() { - var selectedItems = StorageStyleGrid.SelectedItems.Cast().ToList(); + var selectedItems = StorageStyleGrid.SelectedItems?.Cast().ToList() ?? new List(); if (Window == null || selectedItems.Count == 0) { return; @@ -474,7 +474,7 @@ private void StorageRemove() } } - StorageStyleGrid.Focus(); + TableViewExtras.FocusRow(StorageStyleGrid); }); } @@ -487,7 +487,7 @@ private void StorageRemoveAll() [RelayCommand] private void StorageDuplicate() { - var selectedItems = StorageStyleGrid.SelectedItems.Cast().ToList(); + var selectedItems = StorageStyleGrid.SelectedItems?.Cast().ToList() ?? new List(); if (Window == null || selectedItems.Count == 0) { return; @@ -547,7 +547,7 @@ private async Task StorageExport() [RelayCommand] private void StorageCopyToFiles() { - var selectedItems = StorageStyleGrid.SelectedItems.Cast().ToList(); + var selectedItems = StorageStyleGrid.SelectedItems?.Cast().ToList() ?? new List(); if (Window == null || selectedItems.Count == 0) { return; @@ -644,7 +644,7 @@ public void Initialize( } IsFileStyleSelected = SelectedFileStyle != null; - IsTakeUsagesFromVisible = FileStyleGrid.SelectedItems.Count == 1; + IsTakeUsagesFromVisible = FileStyleGrid.SelectedItems?.Count == 1; _timerUpdatePreview.Start(); } @@ -895,7 +895,7 @@ private void SwitchToFileStyle() CurrentTitle = Se.Language.Assa.StylesInFile; SelectedBorderType = selectedStyle?.BorderStyle ?? BorderTypes[0]; IsFileStyleSelected = selectedStyle != null; - IsTakeUsagesFromVisible = FileStyleGrid.SelectedItems.Count == 1; + IsTakeUsagesFromVisible = FileStyleGrid.SelectedItems?.Count == 1; } private void SwitchToStorageStyle() @@ -905,8 +905,8 @@ private void SwitchToStorageStyle() CurrentTitle = Se.Language.Assa.StylesSaved; SelectedBorderType = selectedStyle?.BorderStyle ?? BorderTypes[0]; IsStorageStyleSelected = selectedStyle != null; - IsSetStyleAsDefaultVisible = StorageStyleGrid.SelectedItems.Count == 1; - IsCopyToFileStylesVisible = StorageStyleGrid.SelectedItems.Count > 0; + IsSetStyleAsDefaultVisible = StorageStyleGrid.SelectedItems?.Count == 1; + IsCopyToFileStylesVisible = StorageStyleGrid.SelectedItems?.Count > 0; } internal void BorderTypeChanged(object? sender, SelectionChangedEventArgs e) @@ -976,7 +976,7 @@ private void DeleteFileStyle(StyleDisplay? selectedStyle) UpdateUsages(); } - FileStyleGrid.Focus(); + TableViewExtras.FocusRow(FileStyleGrid); }); } @@ -1026,7 +1026,7 @@ private void DeleteFileStyles(List selectedStyles) } UpdateUsages(); - FileStyleGrid.Focus(); + TableViewExtras.FocusRow(FileStyleGrid); }); } diff --git a/src/ui/Features/Ssa/SsaStylesWindow.cs b/src/ui/Features/Ssa/SsaStylesWindow.cs index 4503548141e..a8c4ad8716d 100644 --- a/src/ui/Features/Ssa/SsaStylesWindow.cs +++ b/src/ui/Features/Ssa/SsaStylesWindow.cs @@ -112,67 +112,50 @@ private static Border MakeFileStyles(SsaStylesViewModel vm) var label = UiUtil.MakeLabel(Se.Language.Assa.StylesInFile).WithBold(); - var dataGrid = new DataGrid + // No header sorting: SSA styles are written to the file header in list + // order on OK, so the collection order is not presentation-only. + var dataGrid = TableViewExtras.MakeTableView(); + dataGrid.DataContext = vm; + dataGrid.ItemsSource = vm.FileStyles; + + dataGrid.Columns.Add(new SeTableViewColumn { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Extended, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.FileStyles, - Columns = - { - new DataGridTextColumn - { - Header = Se.Language.General.Name, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(StyleDisplay.Name)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.FontName, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(StyleDisplay.FontName)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, - new DataGridTextColumn - { - Header = Se.Language.General.FontSize, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(StyleDisplay.FontSize)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, - new DataGridTextColumn - { - Header = Se.Language.General.Usages, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(StyleDisplay.UsageCount)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, - }, - }; - dataGrid.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedFileStyle)) { Source = vm }); + Header = Se.Language.General.Name, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(StyleDisplay.Name)), + Width = new GridLength(1, GridUnitType.Star), + }); + dataGrid.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.FontName, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(StyleDisplay.FontName)), + Width = new GridLength(150), + }); + dataGrid.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.FontSize, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(StyleDisplay.FontSize)), + Width = new GridLength(90), + }); + dataGrid.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.Usages, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(StyleDisplay.UsageCount)), + Width = new GridLength(90), + }); + + dataGrid.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedFileStyle)) { Source = vm }); dataGrid.SelectionChanged += vm.FileStylesChanged; dataGrid.GotFocus += vm.FileStylesGotFocus; dataGrid.KeyDown += vm.FileStylesKeyDown; - dataGrid.AddHandler(InputElement.KeyDownEvent, (object? _, KeyEventArgs e) => - { - if (e.Key is Key.Home or Key.End && dataGrid.ItemsSource is IList items && items.Count > 0) - { - var target = e.Key == Key.Home ? items[0] : items[^1]; - dataGrid.SelectedItem = target; - dataGrid.ScrollIntoView(target, null); - e.Handled = true; - } - }, Avalonia.Interactivity.RoutingStrategies.Tunnel); + TableViewExtras.AttachHomeEndNavigation(dataGrid); vm.FileStyleGrid = dataGrid; var flyout = new MenuFlyout(); @@ -258,52 +241,46 @@ private static Border MakeStorageStyles(SsaStylesViewModel vm) var label = UiUtil.MakeLabel(Se.Language.Assa.StylesSaved).WithBold(); - var dataGrid = new DataGrid + // No header sorting: the storage style order is persisted to settings in + // list order on OK, so the collection order is not presentation-only. + var dataGrid = TableViewExtras.MakeTableView(); + dataGrid.DataContext = vm; + dataGrid.ItemsSource = vm.StorageStyles; + + dataGrid.Columns.Add(new SeTableViewColumn { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Extended, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.StorageStyles, - Columns = - { - new DataGridTextColumn - { - Header = Se.Language.General.Name, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(StyleDisplay.Name)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.FontName, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(StyleDisplay.FontName)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.FontSize, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(StyleDisplay.FontSize)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.IsDefault, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(StyleDisplay.IsDefault)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, - }, - }; - dataGrid.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedStorageStyle)) { Source = vm }); + Header = Se.Language.General.Name, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(StyleDisplay.Name)), + Width = new GridLength(1, GridUnitType.Star), + }); + dataGrid.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.FontName, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(StyleDisplay.FontName)), + Width = new GridLength(150), + }); + dataGrid.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.FontSize, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(StyleDisplay.FontSize)), + Width = new GridLength(90), + }); + dataGrid.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.IsDefault, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(StyleDisplay.IsDefault)), + Width = new GridLength(90), + }); + + dataGrid.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedStorageStyle)) { Source = vm }); dataGrid.SelectionChanged += vm.StorageStylesChanged; dataGrid.GotFocus += vm.StorageStylesGotFocus; vm.StorageStyleGrid = dataGrid; diff --git a/src/ui/Features/Sync/PointSync/PointSyncWindow.cs b/src/ui/Features/Sync/PointSync/PointSyncWindow.cs index f9a41e062e3..9fc730dc36b 100644 --- a/src/ui/Features/Sync/PointSync/PointSyncWindow.cs +++ b/src/ui/Features/Sync/PointSync/PointSyncWindow.cs @@ -2,12 +2,14 @@ using Avalonia.Controls; using Avalonia.Data; using Avalonia.Input; +using Avalonia.Interactivity; using Avalonia.Layout; using Nikse.SubtitleEdit.Features.Main; using Nikse.SubtitleEdit.Features.Sync.PointSyncViaOther; using Nikse.SubtitleEdit.Logic; using Nikse.SubtitleEdit.Logic.Config; using Nikse.SubtitleEdit.Logic.ValueConverters; +using System.Collections; namespace Nikse.SubtitleEdit.Features.Sync.PointSync; @@ -84,27 +86,23 @@ private static Control MakeControlView(PointSyncViewModel vm) Margin = new Thickness(0, 60, 0, 0), }; - var dataGrid = new DataGrid + // The DataGrid this replaces hid its header (HeadersVisibility.None); TableView + // has no such switch, so the single column's header now doubles as the panel title. + var dataGrid = TableViewExtras.MakeTableView(multiSelect: false); + dataGrid.CanUserResizeColumns = false; // single star column, nothing to resize + dataGrid.DataContext = vm; + dataGrid.ItemsSource = vm.SyncPoints; + dataGrid.Columns.Add(new SeTableViewColumn { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - DataContext = vm, - ItemsSource = vm.SyncPoints, - HeadersVisibility = DataGridHeadersVisibility.None, - Columns = - { - new DataGridTextColumn - { - Header = Se.Language.Sync.SyncPoints, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(SyncPoint.Text)), - IsReadOnly = true, - }, - }, - }; + Header = Se.Language.Sync.SyncPoints, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(SyncPoint.Text)), + Width = new GridLength(1, GridUnitType.Star), + }); - dataGrid.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedSyncPoint))); - UiUtil.AttachHomeEndNavigation(dataGrid); + dataGrid.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedSyncPoint))); + TableViewExtras.AttachHomeEndNavigation(dataGrid); var menuItemDelete = new MenuItem { @@ -138,54 +136,53 @@ private static Border MakeSubtitleView(PointSyncViewModel vm) { var fullTimeConverter = new TimeSpanToDisplayFullConverter(); var shortTimeConverter = new TimeSpanToDisplayShortConverter(); - var dataGrid = new DataGrid + // No header-click sorting (the DataGrid's CanUserSortColumns is not carried + // over): the lines are shown in timeline order, which the sync-point logic + // relies on. + var dataGrid = TableViewExtras.MakeTableView(multiSelect: false); + dataGrid.Width = double.NaN; + dataGrid.Height = double.NaN; + dataGrid.DataContext = vm; + dataGrid.ItemsSource = vm.Subtitles; + dataGrid.Columns.AddRange(new[] { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.Subtitles, - Columns = + new SeTableViewColumn { - new DataGridTextColumn - { - Header = Se.Language.General.NumberSymbol, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(SubtitleLineViewModel.Number)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Show, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(SubtitleLineViewModel.StartTime)) { Converter = fullTimeConverter }, - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Duration, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(SubtitleLineViewModel.Duration)) { Converter = shortTimeConverter }, - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Text, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(SubtitleLineViewModel.Text)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, + Header = Se.Language.General.NumberSymbol, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(SubtitleLineViewModel.Number)), + Width = new GridLength(60), // content-sized (Auto) on the DataGrid; TableView treats Auto as star }, - }; - dataGrid.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedSubtitle))); - UiUtil.AttachHomeEndNavigation(dataGrid); + new SeTableViewColumn + { + Header = Se.Language.General.Show, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(SubtitleLineViewModel.StartTime)) { Converter = fullTimeConverter }, + Width = new GridLength(115), + }, + new SeTableViewColumn + { + Header = Se.Language.General.Duration, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(SubtitleLineViewModel.Duration)) { Converter = shortTimeConverter }, + Width = new GridLength(90), + }, + new SeTableViewColumn + { + Header = Se.Language.General.Text, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(SubtitleLineViewModel.Text)), + Width = new GridLength(1, GridUnitType.Star), + }, + }); + dataGrid.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedSubtitle))); + TableViewExtras.AttachHomeEndNavigation(dataGrid); return UiUtil.MakeBorderForControlNoPadding(dataGrid); } + } \ No newline at end of file diff --git a/src/ui/Features/Sync/PointSyncViaOther/PointSyncViaOtherWindow.cs b/src/ui/Features/Sync/PointSyncViaOther/PointSyncViaOtherWindow.cs index 3642fc91aad..05731e00dbf 100644 --- a/src/ui/Features/Sync/PointSyncViaOther/PointSyncViaOtherWindow.cs +++ b/src/ui/Features/Sync/PointSyncViaOther/PointSyncViaOtherWindow.cs @@ -2,8 +2,10 @@ using Avalonia.Controls; using Avalonia.Data; using Avalonia.Input; +using Avalonia.Interactivity; using Avalonia.Layout; using Avalonia.Media; +using System.Collections; using Nikse.SubtitleEdit.Features.Main; using Nikse.SubtitleEdit.Logic; using Nikse.SubtitleEdit.Logic.Config; @@ -88,26 +90,22 @@ private static Control MakeControlView(PointSyncViaOtherViewModel vm) Margin = new Thickness(0, 60, 0, 0), }; - var dataGrid = new DataGrid + // The DataGrid this replaces hid its header (HeadersVisibility.None); TableView + // has no such switch, so the single column's header now doubles as the panel title. + var dataGrid = TableViewExtras.MakeTableView(multiSelect: false); + dataGrid.CanUserResizeColumns = false; // single star column, nothing to resize + dataGrid.DataContext = vm; + dataGrid.ItemsSource = vm.SyncPoints; + dataGrid.Columns.Add(new SeTableViewColumn { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - DataContext = vm, - ItemsSource = vm.SyncPoints, - HeadersVisibility = DataGridHeadersVisibility.None, - Columns = - { - new DataGridTextColumn - { - Header = Se.Language.Sync.SyncPoints, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(SyncPoint.Text)), - IsReadOnly = true, - }, - }, - }; - dataGrid.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedSyncPoint))); - UiUtil.AttachHomeEndNavigation(dataGrid); + Header = Se.Language.Sync.SyncPoints, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(SyncPoint.Text)), + Width = new GridLength(1, GridUnitType.Star), + }); + dataGrid.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedSyncPoint))); + TableViewExtras.AttachHomeEndNavigation(dataGrid); var menuItemDelete = new MenuItem { @@ -171,46 +169,43 @@ private static Grid MakeSubtitleView(PointSyncViaOtherViewModel vm) var fullTimeConverter = new TimeSpanToDisplayFullConverter(); var shortTimeConverter = new TimeSpanToDisplayShortConverter(); - var dataGrid = new DataGrid + // No header-click sorting (the DataGrid's CanUserSortColumns is not carried + // over): both grids show lines in timeline order, which the sync-point + // matching relies on. + var dataGrid = TableViewExtras.MakeTableView(multiSelect: false); + dataGrid.Width = double.NaN; + dataGrid.Height = double.NaN; + dataGrid.DataContext = vm; + dataGrid.ItemsSource = vm.Subtitles; + dataGrid.Columns.AddRange(new[] { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.Subtitles, - Columns = + new SeTableViewColumn { - new DataGridTextColumn - { - Header = Se.Language.General.NumberSymbol, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(SubtitleLineViewModel.Number)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Show, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(SubtitleLineViewModel.StartTime)) { Converter = fullTimeConverter }, - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Text, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(SubtitleLineViewModel.Text)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, + Header = Se.Language.General.NumberSymbol, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(SubtitleLineViewModel.Number)), + Width = new GridLength(60), // content-sized (Auto) on the DataGrid; TableView treats Auto as star }, - }; - dataGrid.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedSubtitle))); - UiUtil.AttachHomeEndNavigation(dataGrid); + new SeTableViewColumn + { + Header = Se.Language.General.Show, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(SubtitleLineViewModel.StartTime)) { Converter = fullTimeConverter }, + Width = new GridLength(115), + }, + new SeTableViewColumn + { + Header = Se.Language.General.Text, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(SubtitleLineViewModel.Text)), + Width = new GridLength(1, GridUnitType.Star), + }, + }); + dataGrid.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedSubtitle))); + TableViewExtras.AttachHomeEndNavigation(dataGrid); grid.Add(panelHeader, 0); grid.Add(UiUtil.MakeBorderForControlNoPadding(dataGrid), 1); @@ -270,55 +265,49 @@ private static Grid MakeSubtitleOtherView(PointSyncViaOtherViewModel vm) panelOtherHeader.Add(buttonFindTextOther, 0, 1); var fullTimeConverter = new TimeSpanToDisplayFullConverter(); - var shortTimeConverter = new TimeSpanToDisplayShortConverter(); - var dataGridSubtitle = new DataGrid + var dataGridSubtitle = TableViewExtras.MakeTableView(multiSelect: false); + dataGridSubtitle.Width = double.NaN; + dataGridSubtitle.Height = double.NaN; + dataGridSubtitle.DataContext = vm; + dataGridSubtitle.ItemsSource = vm.Othersubtitles; + dataGridSubtitle.Columns.AddRange(new[] { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.Othersubtitles, - Columns = + new SeTableViewColumn { - new DataGridTextColumn - { - Header = Se.Language.General.NumberSymbol, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(SubtitleLineViewModel.Number)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Show, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(SubtitleLineViewModel.StartTime)) { Converter = fullTimeConverter }, - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Text, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(SubtitleLineViewModel.Text)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, + Header = Se.Language.General.NumberSymbol, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(SubtitleLineViewModel.Number)), + Width = new GridLength(60), // content-sized (Auto) on the DataGrid; TableView treats Auto as star }, - }; - dataGridSubtitle.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedOtherSubtitle))); - UiUtil.AttachHomeEndNavigation(dataGridSubtitle); + new SeTableViewColumn + { + Header = Se.Language.General.Show, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(SubtitleLineViewModel.StartTime)) { Converter = fullTimeConverter }, + Width = new GridLength(115), + }, + new SeTableViewColumn + { + Header = Se.Language.General.Text, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(SubtitleLineViewModel.Text)), + Width = new GridLength(1, GridUnitType.Star), + }, + }); + dataGridSubtitle.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedOtherSubtitle))); + TableViewExtras.AttachHomeEndNavigation(dataGridSubtitle); // Clicking a line in the left grid scrolls this grid to the matching time (#12529) // without touching its selection. - vm.ScrollOtherToLine = line => dataGridSubtitle.ScrollIntoView(line, null); + vm.ScrollOtherToLine = line => dataGridSubtitle.ScrollIntoView(line); grid.Add(panelOtherHeader, 0); grid.Add(UiUtil.MakeBorderForControlNoPadding(dataGridSubtitle), 1); return grid; } + } \ No newline at end of file diff --git a/src/ui/Features/Tools/AiReview/AiReviewWindow.cs b/src/ui/Features/Tools/AiReview/AiReviewWindow.cs index 49ca30efe31..19a49d82bf9 100644 --- a/src/ui/Features/Tools/AiReview/AiReviewWindow.cs +++ b/src/ui/Features/Tools/AiReview/AiReviewWindow.cs @@ -198,24 +198,22 @@ public AiReviewWindow(AiReviewViewModel vm) chipsBar.Add(warningNote, 0, 1); // ---------- suggestions grid ---------- - var dataGrid = new DataGrid + // No header-click sorting (the DataGrid's CanUserSortColumns is not carried + // over): suggestions are fix previews in subtitle order. The DataGrid-era + // DataGridCheckboxMultiSelect is replaced by native extended selection plus + // TableViewExtras.AddSpaceToggle for the Space-toggles-checkbox piece. + var dataGrid = TableViewExtras.MakeTableView(); + dataGrid.Width = double.NaN; + dataGrid.Height = double.NaN; + dataGrid.DataContext = vm; + dataGrid.ItemsSource = vm.Suggestions; + dataGrid.Columns.AddRange(new[] { - AutoGenerateColumns = false, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.Suggestions, - IsReadOnly = false, - Columns = - { - new DataGridTemplateColumn + new SeTableViewColumn { Header = Se.Language.General.Apply, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewNoPaddingCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, CellTemplate = new FuncDataTemplate((item, _) => new Border { Background = Brushes.Transparent, @@ -228,19 +226,21 @@ public AiReviewWindow(AiReviewViewModel vm) HorizontalAlignment = HorizontalAlignment.Center, }, }), - Width = new DataGridLength(1, DataGridLengthUnitType.Auto), + Width = new GridLength(80), // content-sized (Auto) on the DataGrid; TableView treats Auto as star }, - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.NumberSymbol, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(ReviewSuggestionItem.Number)), - IsReadOnly = true, + Width = new GridLength(60), }, - new DataGridTemplateColumn + new SeTableViewColumn { Header = Se.Language.Tools.FixCommonErrors.Action, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewNoPaddingCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, CellTemplate = new FuncDataTemplate((item, _) => { var panel = new StackPanel @@ -299,13 +299,13 @@ public AiReviewWindow(AiReviewViewModel vm) Child = panel, }; }), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Auto), + Width = new GridLength(150), // content-sized (Auto) on the DataGrid; TableView treats Auto as star }, - new DataGridTemplateColumn + new SeTableViewColumn { Header = Se.Language.General.Before, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewNoPaddingCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, CellTemplate = new FuncDataTemplate((item, _) => { if (item == null) @@ -321,13 +321,13 @@ public AiReviewWindow(AiReviewViewModel vm) Child = beforeBlock, }; }), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), + Width = new GridLength(1, GridUnitType.Star), }, - new DataGridTemplateColumn + new SeTableViewColumn { Header = Se.Language.General.After, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewNoPaddingCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, CellTemplate = new FuncDataTemplate((item, _) => { if (item == null) @@ -343,14 +343,12 @@ public AiReviewWindow(AiReviewViewModel vm) Child = afterBlock, }; }), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), + Width = new GridLength(1, GridUnitType.Star), }, - }, - }; + }); AutomationProperties.SetName(dataGrid, l.Title); - dataGrid.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedSuggestion))); - _ = new DataGridCheckboxMultiSelect(dataGrid, + dataGrid.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedSuggestion))); + TableViewExtras.AddSpaceToggle(dataGrid, item => item.IsSelected, (item, v) => item.IsSelected = v); var borderGrid = UiUtil.MakeBorderForControlNoPadding(dataGrid); diff --git a/src/ui/Features/Tools/ApplyDurationLimits/ApplyDurationLimitsWindow.cs b/src/ui/Features/Tools/ApplyDurationLimits/ApplyDurationLimitsWindow.cs index aa132c8a062..ca30ed9908d 100644 --- a/src/ui/Features/Tools/ApplyDurationLimits/ApplyDurationLimitsWindow.cs +++ b/src/ui/Features/Tools/ApplyDurationLimits/ApplyDurationLimitsWindow.cs @@ -137,56 +137,54 @@ private static Grid MakeFixesView(ApplyDurationLimitsViewModel vm) .WithMarginTop(10) .WithMarginLeft(10); - var dataGrid = new DataGrid + // No header-click sorting (the DataGrid's CanUserSortColumns is not carried + // over): the fixes are previews in subtitle order. The DataGrid-era + // DataGridCheckboxMultiSelect is replaced by native extended selection plus + // TableViewExtras.AddSpaceToggle for the Space-toggles-checkbox piece. + var dataGrid = TableViewExtras.MakeTableView(); + dataGrid.Width = double.NaN; + dataGrid.Height = double.NaN; + dataGrid.DataContext = vm; + dataGrid.ItemsSource = vm.Fixes; + dataGrid.Columns.AddRange(new[] { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.Fixes, - Columns = + new SeTableViewColumn { - new DataGridTemplateColumn - { - Header = Se.Language.General.Apply, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - CellTemplate = new FuncDataTemplate((item, _) => - new Border + Header = Se.Language.General.Apply, + CellTheme = UiUtil.TableViewNoPaddingCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + CellTemplate = new FuncDataTemplate((item, _) => + new Border + { + Background = Brushes.Transparent, // Prevents highlighting + Padding = new Thickness(4), + Child = new CheckBox { - Background = Brushes.Transparent, // Prevents highlighting - Padding = new Thickness(4), - Child = new CheckBox - { - Focusable = false, - [!ToggleButton.IsCheckedProperty] = new Binding(nameof(ApplyDurationLimitItem.Apply)), - HorizontalAlignment = HorizontalAlignment.Center - } - }), - Width = new DataGridLength(1, DataGridLengthUnitType.Auto) - }, - new DataGridTextColumn - { - Header = Se.Language.General.NumberSymbol, - Binding = new Binding(nameof(ApplyDurationLimitItem.Number)), - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Fix, - Binding = new Binding(nameof(ApplyDurationLimitItem.Fix)), - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - CellTheme = UiUtil.DataGridNoBorderCellTheme, - IsReadOnly = true, - }, + Focusable = false, + [!ToggleButton.IsCheckedProperty] = new Binding(nameof(ApplyDurationLimitItem.Apply)), + HorizontalAlignment = HorizontalAlignment.Center + } + }), + Width = new GridLength(80), // content-sized (Auto) on the DataGrid; TableView treats Auto as star }, - }; - _ = new DataGridCheckboxMultiSelect(dataGrid, + new SeTableViewColumn + { + Header = Se.Language.General.NumberSymbol, + Binding = new Binding(nameof(ApplyDurationLimitItem.Number)), + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Width = new GridLength(60), + }, + new SeTableViewColumn + { + Header = Se.Language.General.Fix, + Binding = new Binding(nameof(ApplyDurationLimitItem.Fix)), + Width = new GridLength(1, GridUnitType.Star), + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + }, + }); + TableViewExtras.AddSpaceToggle(dataGrid, item => item.Apply, (item, v) => item.Apply = v); grid.Add(labelFixesAvailable, 0); @@ -221,61 +219,60 @@ private static Grid MakeCannotFixView(ApplyDurationLimitsViewModel vm) var fullTimeConverter = new TimeSpanToDisplayFullConverter(); var shortTimeConverter = new TimeSpanToDisplayShortConverter(); - var dataGrid = new DataGrid + // No header-click sorting here either: lines that cannot be fixed, in subtitle order. + var dataGrid = TableViewExtras.MakeTableView(multiSelect: false); + dataGrid.Width = double.NaN; + dataGrid.Height = double.NaN; + dataGrid.DataContext = vm; + dataGrid.ItemsSource = vm.Subtitles; + dataGrid.Columns.AddRange(new[] { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.Subtitles, - Columns = + new SeTableViewColumn { - new DataGridTextColumn - { - Header = Se.Language.General.NumberSymbol, - Binding = new Binding(nameof(SubtitleLineViewModel.Number)), - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Show, - Binding = new Binding(nameof(SubtitleLineViewModel.StartTime)) { Converter = fullTimeConverter }, - Width = new DataGridLength(120), - CellTheme = UiUtil.DataGridNoBorderCellTheme, - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Duration, - Binding = new Binding(nameof(SubtitleLineViewModel.Duration)) { Converter = shortTimeConverter }, - Width = new DataGridLength(120), - CellTheme = UiUtil.DataGridNoBorderCellTheme, - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Text, - Binding = new Binding(nameof(SubtitleLineViewModel.Text)), - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - CellTheme = UiUtil.DataGridNoBorderCellTheme, - IsReadOnly = true, - }, + Header = Se.Language.General.NumberSymbol, + Binding = new Binding(nameof(SubtitleLineViewModel.Number)), + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Width = new GridLength(60), // content-sized (Auto) on the DataGrid; TableView treats Auto as star }, - }; + new SeTableViewColumn + { + Header = Se.Language.General.Show, + Binding = new Binding(nameof(SubtitleLineViewModel.StartTime)) { Converter = fullTimeConverter }, + Width = new GridLength(120), + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + }, + new SeTableViewColumn + { + Header = Se.Language.General.Duration, + Binding = new Binding(nameof(SubtitleLineViewModel.Duration)) { Converter = shortTimeConverter }, + Width = new GridLength(120), + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + }, + new SeTableViewColumn + { + Header = Se.Language.General.Text, + Binding = new Binding(nameof(SubtitleLineViewModel.Text)), + Width = new GridLength(1, GridUnitType.Star), + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + }, + }); dataGrid.AddHandler(InputElement.KeyDownEvent, (object? _, KeyEventArgs e) => { if (e.Key is Key.Home or Key.End && dataGrid.ItemsSource is IList items && items.Count > 0) { var target = e.Key == Key.Home ? items[0] : items[^1]; + if (target == null) + { + return; + } + dataGrid.SelectedItem = target; - dataGrid.ScrollIntoView(target, null); + dataGrid.ScrollIntoView(target); e.Handled = true; } }, RoutingStrategies.Tunnel); diff --git a/src/ui/Features/Tools/ApplyMinGap/ApplyMinGapWindow.cs b/src/ui/Features/Tools/ApplyMinGap/ApplyMinGapWindow.cs index 8c8eee545b6..1fabc7c2439 100644 --- a/src/ui/Features/Tools/ApplyMinGap/ApplyMinGapWindow.cs +++ b/src/ui/Features/Tools/ApplyMinGap/ApplyMinGapWindow.cs @@ -75,67 +75,69 @@ private static Border MakeSubtitleView(ApplyMinGapViewModel vm) { var fullTimeConverter = new TimeSpanToDisplayFullConverter(); var shortTimeConverter = new TimeSpanToDisplayShortConverter(); - var dataGridSubtitle = new DataGrid + // No header-click sorting (the DataGrid's CanUserSortColumns is not carried + // over): gap-change previews in subtitle order. + var dataGridSubtitle = TableViewExtras.MakeTableView(multiSelect: false); + dataGridSubtitle.Width = double.NaN; + dataGridSubtitle.Height = double.NaN; + dataGridSubtitle.DataContext = vm; + dataGridSubtitle.ItemsSource = vm.Subtitles; + dataGridSubtitle.Columns.AddRange(new[] { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.Subtitles, - Columns = + new SeTableViewColumn { - new DataGridTextColumn - { - Header = Se.Language.General.NumberSymbol, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(ApplyMinGapItem.Number)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Show, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(ApplyMinGapItem.StartTime)) { Converter = fullTimeConverter }, - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Duration, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(ApplyMinGapItem.Duration)) { Converter = shortTimeConverter }, - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Text, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(ApplyMinGapItem.Text)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, - new DataGridTextColumn - { - Header = Se.Language.Tools.BridgeGaps.GapChange, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(ApplyMinGapItem.InfoText)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, + Header = Se.Language.General.NumberSymbol, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(ApplyMinGapItem.Number)), + Width = new GridLength(60), // content-sized (Auto) on the DataGrid; TableView treats Auto as star }, - }; + new SeTableViewColumn + { + Header = Se.Language.General.Show, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(ApplyMinGapItem.StartTime)) { Converter = fullTimeConverter }, + Width = new GridLength(115), + }, + new SeTableViewColumn + { + Header = Se.Language.General.Duration, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(ApplyMinGapItem.Duration)) { Converter = shortTimeConverter }, + Width = new GridLength(90), + }, + new SeTableViewColumn + { + Header = Se.Language.General.Text, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(ApplyMinGapItem.Text)), + Width = new GridLength(1, GridUnitType.Star), + }, + new SeTableViewColumn + { + Header = Se.Language.Tools.BridgeGaps.GapChange, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(ApplyMinGapItem.InfoText)), + Width = new GridLength(1, GridUnitType.Star), + }, + }); dataGridSubtitle.AddHandler(InputElement.KeyDownEvent, (object? _, KeyEventArgs e) => { if (e.Key is Key.Home or Key.End && dataGridSubtitle.ItemsSource is IList items && items.Count > 0) { var target = e.Key == Key.Home ? items[0] : items[^1]; + if (target == null) + { + return; + } + dataGridSubtitle.SelectedItem = target; - dataGridSubtitle.ScrollIntoView(target, null); + dataGridSubtitle.ScrollIntoView(target); e.Handled = true; } }, RoutingStrategies.Tunnel); diff --git a/src/ui/Features/Tools/BatchConvert/BatchConvertFixCommonErrorsSettingsWindow.cs b/src/ui/Features/Tools/BatchConvert/BatchConvertFixCommonErrorsSettingsWindow.cs index 161fe2f473c..986718da21f 100644 --- a/src/ui/Features/Tools/BatchConvert/BatchConvertFixCommonErrorsSettingsWindow.cs +++ b/src/ui/Features/Tools/BatchConvert/BatchConvertFixCommonErrorsSettingsWindow.cs @@ -64,56 +64,53 @@ public BatchConvertFixCommonErrorsSettingsWindow(BatchConvertFixCommonErrorsSett private static Border MakeRulesView(BatchConvertFixCommonErrorsSettingsViewModel vm) { - var rulesGrid = new DataGrid + // No header-click sorting (the DataGrid's CanUserSortColumns is not carried + // over): the fix rules are a settings checklist in their fixed rule order. + // The DataGrid-era DataGridCheckboxMultiSelect is replaced by native extended + // selection plus TableViewExtras.AddSpaceToggle for the Space-toggles-checkbox piece. + var rulesGrid = TableViewExtras.MakeTableView(); + rulesGrid.Width = double.NaN; + rulesGrid.Height = double.NaN; + rulesGrid[!TableView.ItemsSourceProperty] = new Binding($"{nameof(vm.SelectedProfile)}.{nameof(ProfileDisplayItem.FixRules)}"); + rulesGrid.Columns.AddRange(new[] { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - [!DataGrid.ItemsSourceProperty] = new Binding($"{nameof(vm.SelectedProfile)}.{nameof(ProfileDisplayItem.FixRules)}"), - IsReadOnly = false, - Columns = + new SeTableViewColumn { - new DataGridTemplateColumn - { - Header = Se.Language.General.Enabled, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - CellTemplate = new FuncDataTemplate((item, _) => - new Border + Header = Se.Language.General.Enabled, + CellTheme = UiUtil.TableViewNoPaddingCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + CellTemplate = new FuncDataTemplate((item, _) => + new Border + { + Background = Brushes.Transparent, // Prevents highlighting + Padding = new Thickness(4), + Child = new CheckBox { - Background = Brushes.Transparent, // Prevents highlighting - Padding = new Thickness(4), - Child = new CheckBox - { - Focusable = false, - [!ToggleButton.IsCheckedProperty] = new Binding(nameof(FixRuleDisplayItem.IsSelected)), - HorizontalAlignment = HorizontalAlignment.Center - } - }), - Width = new DataGridLength(1, DataGridLengthUnitType.Auto) - }, - new DataGridTextColumn - { - Header = Se.Language.General.Name, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(FixRuleDisplayItem.Name)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Example, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(FixRuleDisplayItem.Example)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star) - }, + Focusable = false, + [!ToggleButton.IsCheckedProperty] = new Binding(nameof(FixRuleDisplayItem.IsSelected)), + HorizontalAlignment = HorizontalAlignment.Center + } + }), + Width = new GridLength(80), // content-sized (Auto) on the DataGrid; TableView treats Auto as star }, - }; - _ = new DataGridCheckboxMultiSelect(rulesGrid, + new SeTableViewColumn + { + Header = Se.Language.General.Name, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(FixRuleDisplayItem.Name)), + Width = new GridLength(340), // content-sized (Auto) on the DataGrid; rule names are long + }, + new SeTableViewColumn + { + Header = Se.Language.General.Example, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(FixRuleDisplayItem.Example)), + Width = new GridLength(1, GridUnitType.Star), + }, + }); + TableViewExtras.AddSpaceToggle(rulesGrid, item => item.IsSelected, (item, v) => item.IsSelected = v); return UiUtil.MakeBorderForControl(rulesGrid); diff --git a/src/ui/Features/Tools/BridgeGaps/BridgeGapsWindow.cs b/src/ui/Features/Tools/BridgeGaps/BridgeGapsWindow.cs index 4e7db0bd635..69c6791cd69 100644 --- a/src/ui/Features/Tools/BridgeGaps/BridgeGapsWindow.cs +++ b/src/ui/Features/Tools/BridgeGaps/BridgeGapsWindow.cs @@ -89,67 +89,69 @@ private Border MakeSubtitleView(BridgeGapsViewModel vm) { var fullTimeConverter = new TimeSpanToDisplayFullConverter(); var shortTimeConverter = new TimeSpanToDisplayShortConverter(); - var dataGridSubtitle = new DataGrid + // No header-click sorting (the DataGrid's CanUserSortColumns is not carried + // over): gap-change previews in subtitle order. + var dataGridSubtitle = TableViewExtras.MakeTableView(multiSelect: false); + dataGridSubtitle.Width = double.NaN; + dataGridSubtitle.Height = double.NaN; + dataGridSubtitle.DataContext = vm; + dataGridSubtitle.ItemsSource = vm.Subtitles; + dataGridSubtitle.Columns.AddRange(new[] { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.Subtitles, - Columns = + new SeTableViewColumn { - new DataGridTextColumn - { - Header = Se.Language.General.NumberSymbol, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(BridgeGapDisplayItem.Number)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Show, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(BridgeGapDisplayItem.StartTime)) { Converter = fullTimeConverter }, - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Duration, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(BridgeGapDisplayItem.Duration)) { Converter = shortTimeConverter }, - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Text, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(BridgeGapDisplayItem.Text)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, - new DataGridTextColumn - { - Header = Se.Language.Tools.BridgeGaps.GapChange, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(BridgeGapDisplayItem.InfoText)), - IsReadOnly = true, - Width = new DataGridLength(120), - }, + Header = Se.Language.General.NumberSymbol, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(BridgeGapDisplayItem.Number)), + Width = new GridLength(60), // content-sized (Auto) on the DataGrid; TableView treats Auto as star }, - }; + new SeTableViewColumn + { + Header = Se.Language.General.Show, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(BridgeGapDisplayItem.StartTime)) { Converter = fullTimeConverter }, + Width = new GridLength(115), + }, + new SeTableViewColumn + { + Header = Se.Language.General.Duration, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(BridgeGapDisplayItem.Duration)) { Converter = shortTimeConverter }, + Width = new GridLength(90), + }, + new SeTableViewColumn + { + Header = Se.Language.General.Text, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(BridgeGapDisplayItem.Text)), + Width = new GridLength(1, GridUnitType.Star), + }, + new SeTableViewColumn + { + Header = Se.Language.Tools.BridgeGaps.GapChange, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(BridgeGapDisplayItem.InfoText)), + Width = new GridLength(120), + }, + }); dataGridSubtitle.AddHandler(InputElement.KeyDownEvent, (object? _, KeyEventArgs e) => { if (e.Key is Key.Home or Key.End && dataGridSubtitle.ItemsSource is IList items && items.Count > 0) { var target = e.Key == Key.Home ? items[0] : items[^1]; + if (target == null) + { + return; + } + dataGridSubtitle.SelectedItem = target; - dataGridSubtitle.ScrollIntoView(target, null); + dataGridSubtitle.ScrollIntoView(target); e.Handled = true; } }, RoutingStrategies.Tunnel); diff --git a/src/ui/Features/Tools/ChangeFormatting/ChangeFormattingWindow.cs b/src/ui/Features/Tools/ChangeFormatting/ChangeFormattingWindow.cs index f4d8f4f74ce..847226ff85c 100644 --- a/src/ui/Features/Tools/ChangeFormatting/ChangeFormattingWindow.cs +++ b/src/ui/Features/Tools/ChangeFormatting/ChangeFormattingWindow.cs @@ -90,101 +90,98 @@ private static Border MakeSubtitleView(ChangeFormattingViewModel vm) var fullTimeConverter = new TimeSpanToDisplayFullConverter(); var shortTimeConverter = new TimeSpanToDisplayShortConverter(); var colorConverter = new TextWithSubtitleSyntaxHighlightingConverter(); - var dataGridSubtitle = new DataGrid + // No header-click sorting (the DataGrid's CanUserSortColumns is not carried + // over): before/after formatting previews in subtitle order. + var dataGridSubtitle = TableViewExtras.MakeTableView(multiSelect: false); + dataGridSubtitle.Width = double.NaN; + dataGridSubtitle.Height = double.NaN; + dataGridSubtitle.DataContext = vm; + dataGridSubtitle.ItemsSource = vm.Subtitles; + dataGridSubtitle.Columns.AddRange(new[] { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.Subtitles, - Columns = + new SeTableViewColumn { - new DataGridTextColumn - { - Header = Se.Language.General.NumberSymbol, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(ChangeFormattingDisplayItem.Number)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Show, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(ChangeFormattingDisplayItem.StartTime)) { Converter = fullTimeConverter }, - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Duration, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(ChangeFormattingDisplayItem.Duration)) { Converter = shortTimeConverter }, - IsReadOnly = true, - }, - new DataGridTemplateColumn + Header = Se.Language.General.NumberSymbol, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(ChangeFormattingDisplayItem.Number)), + Width = new GridLength(60), // content-sized (Auto) on the DataGrid; TableView treats Auto as star + }, + new SeTableViewColumn + { + Header = Se.Language.General.Show, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(ChangeFormattingDisplayItem.StartTime)) { Converter = fullTimeConverter }, + Width = new GridLength(115), + }, + new SeTableViewColumn + { + Header = Se.Language.General.Duration, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(ChangeFormattingDisplayItem.Duration)) { Converter = shortTimeConverter }, + Width = new GridLength(90), + }, + new SeTableViewColumn + { + Header = Se.Language.General.Before, + CellTheme = UiUtil.TableViewNoPaddingCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Width = new GridLength(1, GridUnitType.Star), + CellTemplate = new FuncDataTemplate((value, nameScope) => { - Header = Se.Language.General.Before, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - CellTemplate = new FuncDataTemplate((value, nameScope) => + var border = new Border + { + Padding = new Thickness(4, 2), + }; + + var textBlock = new TextBlock + { + VerticalAlignment = VerticalAlignment.Center, + TextWrapping = TextWrapping.NoWrap, + [!TextBlock.InlinesProperty] = new Binding(nameof(ChangeFormattingDisplayItem.Text)) { Converter = colorConverter, Mode = BindingMode.OneWay }, + }; + + if (!string.IsNullOrEmpty(Se.Settings.Appearance.SubtitleTextBoxAndGridFontName)) { - var border = new Border - { - Padding = new Thickness(4, 2), - }; - - var textBlock = new TextBlock - { - VerticalAlignment = VerticalAlignment.Center, - TextWrapping = TextWrapping.NoWrap, - [!TextBlock.InlinesProperty] = new Binding(nameof(ChangeFormattingDisplayItem.Text)) { Converter = colorConverter, Mode = BindingMode.OneWay }, - }; - - if (!string.IsNullOrEmpty(Se.Settings.Appearance.SubtitleTextBoxAndGridFontName)) - { - textBlock.FontFamily = new FontFamily(Se.Settings.Appearance.SubtitleTextBoxAndGridFontName); - } - - border.Child = textBlock; - return border; - }) - }, - new DataGridTemplateColumn + textBlock.FontFamily = new FontFamily(Se.Settings.Appearance.SubtitleTextBoxAndGridFontName); + } + + border.Child = textBlock; + return border; + }) + }, + new SeTableViewColumn + { + Header = Se.Language.General.After, + CellTheme = UiUtil.TableViewNoPaddingCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Width = new GridLength(1, GridUnitType.Star), + CellTemplate = new FuncDataTemplate((value, nameScope) => { - Header = Se.Language.General.After, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - CellTemplate = new FuncDataTemplate((value, nameScope) => + var border = new Border { - var border = new Border - { - Padding = new Thickness(4, 2), - }; - - var textBlock = new TextBlock - { - VerticalAlignment = VerticalAlignment.Center, - TextWrapping = TextWrapping.NoWrap, - [!TextBlock.InlinesProperty] = new Binding(nameof(ChangeFormattingDisplayItem.NewText)) { Converter = colorConverter, Mode = BindingMode.OneWay }, - }; - - if (!string.IsNullOrEmpty(Se.Settings.Appearance.SubtitleTextBoxAndGridFontName)) - { - textBlock.FontFamily = new FontFamily(Se.Settings.Appearance.SubtitleTextBoxAndGridFontName); - } - - border.Child = textBlock; - return border; - }) - }, + Padding = new Thickness(4, 2), + }; + + var textBlock = new TextBlock + { + VerticalAlignment = VerticalAlignment.Center, + TextWrapping = TextWrapping.NoWrap, + [!TextBlock.InlinesProperty] = new Binding(nameof(ChangeFormattingDisplayItem.NewText)) { Converter = colorConverter, Mode = BindingMode.OneWay }, + }; + + if (!string.IsNullOrEmpty(Se.Settings.Appearance.SubtitleTextBoxAndGridFontName)) + { + textBlock.FontFamily = new FontFamily(Se.Settings.Appearance.SubtitleTextBoxAndGridFontName); + } + + border.Child = textBlock; + return border; + }) }, - }; + }); return UiUtil.MakeBorderForControlNoPadding(dataGridSubtitle); } diff --git a/src/ui/Features/Tools/ConvertActors/ConvertActorsWindow.cs b/src/ui/Features/Tools/ConvertActors/ConvertActorsWindow.cs index 635c12eddf4..d75f24ac915 100644 --- a/src/ui/Features/Tools/ConvertActors/ConvertActorsWindow.cs +++ b/src/ui/Features/Tools/ConvertActors/ConvertActorsWindow.cs @@ -143,26 +143,21 @@ private static Border MakeSubtitleView(ConvertActorsViewModel vm) var shortTimeConverter = new TimeSpanToDisplayShortConverter(); var colorConverter = new TextWithSubtitleSyntaxHighlightingConverter(); - var dataGrid = new DataGrid + // Sorting dropped in the DataGrid -> TableView conversion: the grid previews + // subtitle lines in timeline order. + var dataGrid = TableViewExtras.MakeTableView(); + dataGrid.Width = double.NaN; + dataGrid.Height = double.NaN; + dataGrid.DataContext = vm; + dataGrid.ItemsSource = vm.Subtitles; + dataGrid.Columns.AddRange(new TableViewColumn[] { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.Subtitles, - Columns = - { - new DataGridTemplateColumn + new SeTableViewColumn { Header = Se.Language.General.Apply, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - IsReadOnly = false, - Width = new DataGridLength(55), + CellTheme = UiUtil.TableViewNoPaddingCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Width = new GridLength(55), CellTemplate = new FuncDataTemplate((_, _) => new CheckBox { @@ -172,26 +167,29 @@ private static Border MakeSubtitleView(ConvertActorsViewModel vm) [!CheckBox.IsCheckedProperty] = new Binding(nameof(ConvertActorsDisplayItem.IsChecked)) { Mode = BindingMode.TwoWay }, }), }, - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.NumberSymbol, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(ConvertActorsDisplayItem.Number)), - IsReadOnly = true, + // Content-sized (Auto) on the DataGrid; TableView treats Auto as star. + Width = new GridLength(60), }, - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.Show, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(ConvertActorsDisplayItem.StartTime)) { Converter = fullTimeConverter }, - IsReadOnly = true, + Width = new GridLength(120), }, - new DataGridTemplateColumn + new SeTableViewColumn { Header = Se.Language.General.Before, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), + CellTheme = UiUtil.TableViewNoPaddingCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Width = new GridLength(1, GridUnitType.Star), CellTemplate = new FuncDataTemplate((_, _) => { var border = new Border { Padding = new Thickness(4, 2) }; @@ -209,12 +207,12 @@ private static Border MakeSubtitleView(ConvertActorsViewModel vm) return border; }), }, - new DataGridTemplateColumn + new SeTableViewColumn { Header = Se.Language.General.After, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), + CellTheme = UiUtil.TableViewNoPaddingCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Width = new GridLength(1, GridUnitType.Star), CellTemplate = new FuncDataTemplate((_, _) => { var border = new Border { Padding = new Thickness(4, 2) }; @@ -232,9 +230,11 @@ private static Border MakeSubtitleView(ConvertActorsViewModel vm) return border; }), }, - }, - }; - _ = new DataGridCheckboxMultiSelect(dataGrid, + }); + + // Extended selection is native ListBox behavior on TableView; only the + // Space-toggles-checkbox piece of the old CheckboxMultiSelect needs wiring. + TableViewExtras.AddSpaceToggle(dataGrid, item => item.IsChecked, (item, v) => item.IsChecked = v); return UiUtil.MakeBorderForControlNoPadding(dataGrid); diff --git a/src/ui/Features/Tools/FixNetflixErrors/FixNetflixErrorsWindow.cs b/src/ui/Features/Tools/FixNetflixErrors/FixNetflixErrorsWindow.cs index c0ed642eff4..b33311e4a16 100644 --- a/src/ui/Features/Tools/FixNetflixErrors/FixNetflixErrorsWindow.cs +++ b/src/ui/Features/Tools/FixNetflixErrors/FixNetflixErrorsWindow.cs @@ -106,22 +106,19 @@ private static Border MakeSettingsView(FixNetflixErrorsViewModel vm) } }; - // Grid with list of checks - var dataGrid = new DataGrid - { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Left, - VerticalAlignment = VerticalAlignment.Top, - [!DataGrid.ItemsSourceProperty] = new Binding(nameof(vm.Checks)) - }; + // Grid with list of checks. Sorting dropped in the DataGrid -> TableView + // conversion: the checks run in list order (RunChecks gets them in collection + // order), so the list must not be reordered. + // Stretch (the MakeTableView default) instead of the DataGrid's Left/Top: the + // star-sized Name column needs the control to fill its fixed-width grid cell. + var dataGrid = TableViewExtras.MakeTableView(); + dataGrid[!TableView.ItemsSourceProperty] = new Binding(nameof(vm.Checks)); - dataGrid.Columns.Add(new DataGridTemplateColumn + dataGrid.Columns.Add(new SeTableViewColumn { Header = Se.Language.General.Enabled, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewNoPaddingCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, CellTemplate = new FuncDataTemplate((item, _) => { var cb = new CheckBox @@ -138,18 +135,22 @@ private static Border MakeSettingsView(FixNetflixErrorsViewModel vm) Child = cb }; }), - Width = new DataGridLength(1, DataGridLengthUnitType.Auto) + // Content-sized (Auto) on the DataGrid; TableView treats Auto as star. + Width = new GridLength(70) }); - dataGrid.Columns.Add(new DataGridTextColumn + dataGrid.Columns.Add(new SeTableViewColumn { Header = Se.Language.General.Name, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(NetflixCheckDisplayItem.Name)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Auto) + Width = new GridLength(1, GridUnitType.Star) }); - _ = new DataGridCheckboxMultiSelect(dataGrid, + + // Extended selection is native ListBox behavior on TableView; only the + // Space-toggles-checkbox piece of the old CheckboxMultiSelect needs wiring. + TableViewExtras.AddSpaceToggle(dataGrid, item => item.IsSelected, (item, v) => item.IsSelected = v); var grid = new Grid @@ -173,24 +174,20 @@ private static Border MakeSettingsView(FixNetflixErrorsViewModel vm) private Border MakeFixesView(FixNetflixErrorsViewModel vm) { - var dataGrid = new DataGrid + // Sorting dropped in the DataGrid -> TableView conversion: the grid previews + // fixes in subtitle order. + var dataGrid = TableViewExtras.MakeTableView(); + dataGrid.Width = double.NaN; + dataGrid.Height = double.NaN; + dataGrid.DataContext = _vm; + dataGrid.ItemsSource = _vm.Fixes; + dataGrid.Columns.AddRange(new TableViewColumn[] { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = _vm, - ItemsSource = _vm.Fixes, - Columns = - { - new DataGridTemplateColumn + new SeTableViewColumn { Header = Se.Language.General.Apply, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewNoPaddingCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, CellTemplate = new FuncDataTemplate((item, _) => { var cb = new CheckBox @@ -208,19 +205,22 @@ private Border MakeFixesView(FixNetflixErrorsViewModel vm) Child = cb, }; }), - Width = new DataGridLength(1, DataGridLengthUnitType.Auto) + // Content-sized (Auto) on the DataGrid; TableView treats Auto as star. + Width = new GridLength(70) }, - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.NumberSymbol, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(FixNetflixErrorsItem.IndexDisplay)), - IsReadOnly = true, + Width = new GridLength(60), }, - new DataGridTemplateColumn + new SeTableViewColumn { Header = Se.Language.General.Before, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewNoPaddingCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, CellTemplate = new FuncDataTemplate((item, _) => { if (item == null) @@ -236,13 +236,13 @@ private Border MakeFixesView(FixNetflixErrorsViewModel vm) Child = beforeBlock, }; }), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), + Width = new GridLength(1, GridUnitType.Star), }, - new DataGridTemplateColumn + new SeTableViewColumn { Header = Se.Language.General.After, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewNoPaddingCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, CellTemplate = new FuncDataTemplate((item, _) => { if (item == null) @@ -258,23 +258,32 @@ private Border MakeFixesView(FixNetflixErrorsViewModel vm) Child = afterBlock, }; }), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), + Width = new GridLength(1, GridUnitType.Star), }, - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.Reason, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(FixNetflixErrorsItem.Reason)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), + Width = new GridLength(1, GridUnitType.Star), }, - }, - }; - dataGrid.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(_vm.SelectedFix))); - _ = new DataGridCheckboxMultiSelect(dataGrid, - item => item.Apply, (item, v) => item.Apply = v, - canToggle: item => item.CanBeFixed); + }); + dataGrid.Bind(TableView.SelectedItemProperty, new Binding(nameof(_vm.SelectedFix))); + + // Extended selection is native ListBox behavior on TableView; only the + // Space-toggles-checkbox piece of the old CheckboxMultiSelect needs wiring. + // Non-fixable rows count as "checked" so they never block the all-checked + // toggle, and the setter leaves them alone (the old helper's canToggle). + TableViewExtras.AddSpaceToggle(dataGrid, + item => !item.CanBeFixed || item.Apply, + (item, v) => + { + if (item.CanBeFixed) + { + item.Apply = v; + } + }); return UiUtil.MakeBorderForControlNoPadding(dataGrid); } diff --git a/src/ui/Features/Tools/JoinSubtitles/JoinSubtitlesViewModel.cs b/src/ui/Features/Tools/JoinSubtitles/JoinSubtitlesViewModel.cs index f2f854aa963..92db6a84d69 100644 --- a/src/ui/Features/Tools/JoinSubtitles/JoinSubtitlesViewModel.cs +++ b/src/ui/Features/Tools/JoinSubtitles/JoinSubtitlesViewModel.cs @@ -419,7 +419,7 @@ private async Task Revert(int idx, string message) await MessageBox.Show(Window, "", message); } - internal void DataGridKeyDown(object? sender, KeyEventArgs e) + internal void GridKeyDown(object? sender, KeyEventArgs e) { if (e.Key == Key.Delete && SelectedJoinItem != null) { diff --git a/src/ui/Features/Tools/JoinSubtitles/JoinSubtitlesWindow.cs b/src/ui/Features/Tools/JoinSubtitles/JoinSubtitlesWindow.cs index 4aedea2cd4e..5e589dcda1e 100644 --- a/src/ui/Features/Tools/JoinSubtitles/JoinSubtitlesWindow.cs +++ b/src/ui/Features/Tools/JoinSubtitles/JoinSubtitlesWindow.cs @@ -78,62 +78,59 @@ private static Border MakeFilesView(JoinSubtitlesViewModel vm) var fullTimeConverter = new TimeSpanToDisplayFullConverter(); - var dataGrid = new DataGrid + // Sorting dropped in the DataGrid -> TableView conversion: the join is produced + // by iterating this list in order (the VM sorts it by start time itself), so the + // list must not be reordered by clicking a header. + var dataGrid = TableViewExtras.MakeTableView(multiSelect: false); + dataGrid.Width = double.NaN; + dataGrid.Height = double.NaN; + dataGrid.DataContext = vm; + dataGrid.ItemsSource = vm.JoinItems; + dataGrid.Columns.AddRange(new TableViewColumn[] { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.JoinItems, - Columns = - { - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.NoSymbolLines, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(JoinDisplayItem.Lines)), - IsReadOnly = true, + // Content-sized (Auto) on the DataGrid; TableView treats Auto as star. + Width = new GridLength(80), }, - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.StartTime, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(JoinDisplayItem.StartTime)) { Converter = fullTimeConverter }, - IsReadOnly = true, - Width = new DataGridLength(120, DataGridLengthUnitType.Pixel), + Width = new GridLength(120), }, - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.EndTime, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(JoinDisplayItem.EndTime)) { Converter = fullTimeConverter }, - IsReadOnly = true, - Width = new DataGridLength(120, DataGridLengthUnitType.Pixel), + Width = new GridLength(120), }, - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.FileName, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(JoinDisplayItem.FileName)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), + Width = new GridLength(1, GridUnitType.Star), }, - }, - }; - dataGrid.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedJoinItem)) { Source = vm }); - dataGrid.KeyDown += vm.DataGridKeyDown; + }); + dataGrid.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedJoinItem)) { Source = vm }); + dataGrid.KeyDown += vm.GridKeyDown; dataGrid.AddHandler(InputElement.KeyDownEvent, (object? _, KeyEventArgs e) => { if (e.Key is Key.Home or Key.End && dataGrid.ItemsSource is IList items && items.Count > 0) { - var target = e.Key == Key.Home ? items[0] : items[^1]; - dataGrid.SelectedItem = target; - dataGrid.ScrollIntoView(target, null); + var index = e.Key == Key.Home ? 0 : items.Count - 1; + dataGrid.SelectedIndex = index; + dataGrid.ScrollIntoView(index); e.Handled = true; } }, RoutingStrategies.Tunnel); diff --git a/src/ui/Features/Tools/MergeShortLines/MergeShortLinesWindow.cs b/src/ui/Features/Tools/MergeShortLines/MergeShortLinesWindow.cs index 8b08a00ba45..f33b3ecdb9c 100644 --- a/src/ui/Features/Tools/MergeShortLines/MergeShortLinesWindow.cs +++ b/src/ui/Features/Tools/MergeShortLines/MergeShortLinesWindow.cs @@ -125,45 +125,41 @@ private static Grid MakeFixesView(MergeShortLinesViewModel vm) .WithMarginTop(10) .WithMarginLeft(10); - var dataGrid = new DataGrid + // Sorting dropped in the DataGrid -> TableView conversion: the grid previews + // merge candidates in subtitle order. + var dataGrid = TableViewExtras.MakeTableView(multiSelect: false); + dataGrid.Width = double.NaN; + dataGrid.Height = double.NaN; + dataGrid.DataContext = vm; + dataGrid.ItemsSource = vm.Fixes; + dataGrid.Columns.AddRange(new TableViewColumn[] { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.Fixes, - Columns = - { - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.NumberSymbol, Binding = new Binding(nameof(MergeShortLinesItem.Number)), - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - IsReadOnly = true, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + // Content-sized (Auto) on the DataGrid; TableView treats Auto as star. + Width = new GridLength(60), }, - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.Fix, Binding = new Binding(nameof(MergeShortLinesItem.Fix)), - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - CellTheme = UiUtil.DataGridNoBorderCellTheme, - IsReadOnly = true, + Width = new GridLength(1, GridUnitType.Star), + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, }, - }, - }; + }); dataGrid.AddHandler(InputElement.KeyDownEvent, (object? _, KeyEventArgs e) => { if (e.Key is Key.Home or Key.End && dataGrid.ItemsSource is IList items && items.Count > 0) { - var target = e.Key == Key.Home ? items[0] : items[^1]; - dataGrid.SelectedItem = target; - dataGrid.ScrollIntoView(target, null); + var index = e.Key == Key.Home ? 0 : items.Count - 1; + dataGrid.SelectedIndex = index; + dataGrid.ScrollIntoView(index); e.Handled = true; } }, RoutingStrategies.Tunnel); diff --git a/src/ui/Features/Tools/MergeSubtitlesWithSameText/MergeSameTextViewModel.cs b/src/ui/Features/Tools/MergeSubtitlesWithSameText/MergeSameTextViewModel.cs index 727993e3f9b..f05b13a26f4 100644 --- a/src/ui/Features/Tools/MergeSubtitlesWithSameText/MergeSameTextViewModel.cs +++ b/src/ui/Features/Tools/MergeSubtitlesWithSameText/MergeSameTextViewModel.cs @@ -27,7 +27,7 @@ public partial class MergeSameTextViewModel : ObservableObject, IClosingCleanup public bool OkPressed { get; private set; } public List ResultSubtitles { get; set; } - public DataGrid SubtitleGrid { get; set; } + public TableView SubtitleGrid { get; set; } private readonly System.Timers.Timer _timerUpdatePreview; private volatile bool _isClosing; @@ -39,7 +39,7 @@ public MergeSameTextViewModel() MergeItems = new ObservableCollection(); MergeSubtitles = new ObservableCollection(); ResultSubtitles = new List(); - SubtitleGrid = new DataGrid(); + SubtitleGrid = new TableView(); LoadSettings(); @@ -266,20 +266,21 @@ private void Cancel() Window?.Close(); } - internal void DataGridMergeItemChanged(object? sender, SelectionChangedEventArgs e) + internal void MergeItemChanged(object? sender, SelectionChangedEventArgs e) { var selected = SelectedMergeItem; - if (selected == null) + var selectedItems = SubtitleGrid.SelectedItems; + if (selected == null || selectedItems == null) { return; } - SubtitleGrid.SelectedItems.Clear(); + selectedItems.Clear(); foreach (var item in MergeSubtitles) { if (item is SubtitleLineViewModel svm && svm.Extra == selected.MergedGroup) { - SubtitleGrid.SelectedItems.Add(item); + selectedItems.Add(item); } } } diff --git a/src/ui/Features/Tools/MergeSubtitlesWithSameText/MergeSameTextWindow.cs b/src/ui/Features/Tools/MergeSubtitlesWithSameText/MergeSameTextWindow.cs index 0d9aaa5d11b..e017960539f 100644 --- a/src/ui/Features/Tools/MergeSubtitlesWithSameText/MergeSameTextWindow.cs +++ b/src/ui/Features/Tools/MergeSubtitlesWithSameText/MergeSameTextWindow.cs @@ -81,24 +81,20 @@ private static StackPanel MakeControlsView(MergeSameTextViewModel vm) private static Border MakeMergesView(MergeSameTextViewModel vm) { - var dataGrid = new DataGrid + // Sorting dropped in the DataGrid -> TableView conversion: the grid shows + // merge candidates in subtitle order. + var dataGrid = TableViewExtras.MakeTableView(multiSelect: false); + dataGrid.Width = double.NaN; + dataGrid.Height = double.NaN; + dataGrid.DataContext = vm; + dataGrid.ItemsSource = vm.MergeItems; + dataGrid.Columns.AddRange(new TableViewColumn[] { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.MergeItems, - Columns = - { - new DataGridTemplateColumn - { + new SeTableViewColumn + { Header = Se.Language.General.Apply, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewNoPaddingCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, CellTemplate = new FuncDataTemplate((item, _) => new Border { @@ -110,41 +106,45 @@ private static Border MakeMergesView(MergeSameTextViewModel vm) HorizontalAlignment = HorizontalAlignment.Center } }), - Width = new DataGridLength(1, DataGridLengthUnitType.Auto) + // Content-sized (Auto) on the DataGrid; TableView treats Auto as star. + Width = new GridLength(70) }, - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.Lines, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(MergeDisplayItem.Lines)), - IsReadOnly = true, + Width = new GridLength(110), }, - new DataGridTextColumn + new SeTableViewColumn { + // The merged text is the wide content, so it takes the star width + // (the DataGrid content-sized it and gave Group the star). Header = Se.Language.General.Text, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(MergeDisplayItem.MergedText)), - IsReadOnly = true, + Width = new GridLength(1, GridUnitType.Star), }, - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.Group, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(MergeDisplayItem.MergedGroup)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), + Width = new GridLength(90), }, - }, - }; - dataGrid.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedMergeItem)) { Source = vm }); - dataGrid.SelectionChanged += vm.DataGridMergeItemChanged; + }); + dataGrid.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedMergeItem)) { Source = vm }); + dataGrid.SelectionChanged += vm.MergeItemChanged; dataGrid.AddHandler(InputElement.KeyDownEvent, (object? _, KeyEventArgs e) => { if (e.Key is Key.Home or Key.End && dataGrid.ItemsSource is IList items && items.Count > 0) { - var target = e.Key == Key.Home ? items[0] : items[^1]; - dataGrid.SelectedItem = target; - dataGrid.ScrollIntoView(target, null); + var index = e.Key == Key.Home ? 0 : items.Count - 1; + dataGrid.SelectedIndex = index; + dataGrid.ScrollIntoView(index); e.Handled = true; } }, RoutingStrategies.Tunnel); @@ -156,69 +156,66 @@ private static Border MakeSubtitlesView(MergeSameTextViewModel vm) { var fullTimeConverter = new TimeSpanToDisplayFullConverter(); - var dataGrid = new DataGrid + // Sorting dropped in the DataGrid -> TableView conversion: the grid previews + // subtitle lines in timeline order. + var dataGrid = TableViewExtras.MakeTableView(); + dataGrid.Width = double.NaN; + dataGrid.Height = double.NaN; + dataGrid.DataContext = vm; + dataGrid.ItemsSource = vm.MergeSubtitles; + dataGrid.Columns.AddRange(new TableViewColumn[] { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Extended, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.MergeSubtitles, - Columns = - { - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.NumberSymbol, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(SubtitleLineViewModel.Number)), - IsReadOnly = true, + // Content-sized (Auto) on the DataGrid; TableView treats Auto as star. + Width = new GridLength(60), }, - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.Show, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(SubtitleLineViewModel.StartTime)) { Converter = fullTimeConverter }, - IsReadOnly = true, - Width = new DataGridLength(120, DataGridLengthUnitType.Pixel), + Width = new GridLength(120), }, - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.Hide, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(SubtitleLineViewModel.EndTime)) { Converter = fullTimeConverter }, - IsReadOnly = true, - Width = new DataGridLength(120, DataGridLengthUnitType.Pixel), + Width = new GridLength(120), }, - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.Text, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(SubtitleLineViewModel.Text)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), + Width = new GridLength(1, GridUnitType.Star), }, - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.Group, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(SubtitleLineViewModel.Extra)), - IsReadOnly = true, + Width = new GridLength(90), }, - }, - }; - dataGrid.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedMergeSubtitle)) { Source = vm }); + }); + dataGrid.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedMergeSubtitle)) { Source = vm }); vm.SubtitleGrid = dataGrid; dataGrid.AddHandler(InputElement.KeyDownEvent, (object? _, KeyEventArgs e) => { if (e.Key is Key.Home or Key.End && dataGrid.ItemsSource is IList items && items.Count > 0) { - var target = e.Key == Key.Home ? items[0] : items[^1]; - dataGrid.SelectedItem = target; - dataGrid.ScrollIntoView(target, null); + var index = e.Key == Key.Home ? 0 : items.Count - 1; + dataGrid.SelectedIndex = index; + dataGrid.ScrollIntoView(index); e.Handled = true; } }, RoutingStrategies.Tunnel); diff --git a/src/ui/Features/Tools/MergeSubtitlesWithSameTimeCodes/MergeSameTimeCodesViewModel.cs b/src/ui/Features/Tools/MergeSubtitlesWithSameTimeCodes/MergeSameTimeCodesViewModel.cs index 09f9562223f..67b91da8e1e 100644 --- a/src/ui/Features/Tools/MergeSubtitlesWithSameTimeCodes/MergeSameTimeCodesViewModel.cs +++ b/src/ui/Features/Tools/MergeSubtitlesWithSameTimeCodes/MergeSameTimeCodesViewModel.cs @@ -29,7 +29,7 @@ public partial class MergeSameTimeCodesViewModel : ObservableObject, IClosingCle public bool OkPressed { get; private set; } public List ResultSubtitles { get; set; } - public DataGrid SubtitleGrid { get; set; } + public TableView SubtitleGrid { get; set; } private readonly System.Timers.Timer _timerUpdatePreview; private volatile bool _isClosing; @@ -42,7 +42,7 @@ public MergeSameTimeCodesViewModel() MergeItems = new ObservableCollection(); MergeSubtitles = new ObservableCollection(); ResultSubtitles = new List(); - SubtitleGrid = new DataGrid(); + SubtitleGrid = new TableView(); LoadSettings(); @@ -311,20 +311,21 @@ private void Cancel() Window?.Close(); } - internal void DataGridMergeItemChanged(object? sender, SelectionChangedEventArgs e) + internal void MergeItemChanged(object? sender, SelectionChangedEventArgs e) { var selected = SelectedMergeItem; - if (selected == null) + var selectedItems = SubtitleGrid.SelectedItems; + if (selected == null || selectedItems == null) { return; } - SubtitleGrid.SelectedItems.Clear(); + selectedItems.Clear(); foreach (var item in MergeSubtitles) { if (item is SubtitleLineViewModel svm && svm.Extra == selected.MergedGroup) { - SubtitleGrid.SelectedItems.Add(item); + selectedItems.Add(item); } } } diff --git a/src/ui/Features/Tools/MergeSubtitlesWithSameTimeCodes/MergeSameTimeCodesWindow.cs b/src/ui/Features/Tools/MergeSubtitlesWithSameTimeCodes/MergeSameTimeCodesWindow.cs index 0e5355acb89..4029ece5c81 100644 --- a/src/ui/Features/Tools/MergeSubtitlesWithSameTimeCodes/MergeSameTimeCodesWindow.cs +++ b/src/ui/Features/Tools/MergeSubtitlesWithSameTimeCodes/MergeSameTimeCodesWindow.cs @@ -87,24 +87,20 @@ private static StackPanel MakeControlsView(MergeSameTimeCodesViewModel vm) private static Border MakeMergesView(MergeSameTimeCodesViewModel vm) { - var dataGrid = new DataGrid + // Sorting dropped in the DataGrid -> TableView conversion: the grid shows + // merge candidates in subtitle order. + var dataGrid = TableViewExtras.MakeTableView(multiSelect: false); + dataGrid.Width = double.NaN; + dataGrid.Height = double.NaN; + dataGrid.DataContext = vm; + dataGrid.ItemsSource = vm.MergeItems; + dataGrid.Columns.AddRange(new TableViewColumn[] { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.MergeItems, - Columns = - { - new DataGridTemplateColumn - { + new SeTableViewColumn + { Header = Se.Language.General.Apply, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewNoPaddingCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, CellTemplate = new FuncDataTemplate((item, _) => new Border { @@ -116,41 +112,45 @@ private static Border MakeMergesView(MergeSameTimeCodesViewModel vm) HorizontalAlignment = HorizontalAlignment.Center } }), - Width = new DataGridLength(1, DataGridLengthUnitType.Auto) + // Content-sized (Auto) on the DataGrid; TableView treats Auto as star. + Width = new GridLength(70) }, - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.Lines, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(MergeDisplayItem.Lines)), - IsReadOnly = true, + Width = new GridLength(110), }, - new DataGridTextColumn + new SeTableViewColumn { + // The merged text is the wide content, so it takes the star width + // (the DataGrid content-sized it and gave Group the star). Header = Se.Language.General.Text, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(MergeDisplayItem.MergedText)), - IsReadOnly = true, + Width = new GridLength(1, GridUnitType.Star), }, - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.Group, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(MergeDisplayItem.MergedGroup)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), + Width = new GridLength(90), }, - }, - }; - dataGrid.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedMergeItem)) { Source = vm }); - dataGrid.SelectionChanged += vm.DataGridMergeItemChanged; + }); + dataGrid.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedMergeItem)) { Source = vm }); + dataGrid.SelectionChanged += vm.MergeItemChanged; dataGrid.AddHandler(InputElement.KeyDownEvent, (object? _, KeyEventArgs e) => { if (e.Key is Key.Home or Key.End && dataGrid.ItemsSource is IList items && items.Count > 0) { - var target = e.Key == Key.Home ? items[0] : items[^1]; - dataGrid.SelectedItem = target; - dataGrid.ScrollIntoView(target, null); + var index = e.Key == Key.Home ? 0 : items.Count - 1; + dataGrid.SelectedIndex = index; + dataGrid.ScrollIntoView(index); e.Handled = true; } }, RoutingStrategies.Tunnel); @@ -162,69 +162,66 @@ private static Border MakeSubtitlesView(MergeSameTimeCodesViewModel vm) { var fullTimeConverter = new TimeSpanToDisplayFullConverter(); - var dataGrid = new DataGrid + // Sorting dropped in the DataGrid -> TableView conversion: the grid previews + // subtitle lines in timeline order. + var dataGrid = TableViewExtras.MakeTableView(); + dataGrid.Width = double.NaN; + dataGrid.Height = double.NaN; + dataGrid.DataContext = vm; + dataGrid.ItemsSource = vm.MergeSubtitles; + dataGrid.Columns.AddRange(new TableViewColumn[] { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Extended, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.MergeSubtitles, - Columns = - { - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.NumberSymbol, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(SubtitleLineViewModel.Number)), - IsReadOnly = true, + // Content-sized (Auto) on the DataGrid; TableView treats Auto as star. + Width = new GridLength(60), }, - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.Show, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(SubtitleLineViewModel.StartTime)) { Converter = fullTimeConverter }, - IsReadOnly = true, - Width = new DataGridLength(120, DataGridLengthUnitType.Pixel), + Width = new GridLength(120), }, - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.Hide, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(SubtitleLineViewModel.EndTime)) { Converter = fullTimeConverter }, - IsReadOnly = true, - Width = new DataGridLength(120, DataGridLengthUnitType.Pixel), + Width = new GridLength(120), }, - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.Text, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(SubtitleLineViewModel.Text)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), + Width = new GridLength(1, GridUnitType.Star), }, - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.Group, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(SubtitleLineViewModel.Extra)), - IsReadOnly = true, + Width = new GridLength(90), }, - }, - }; - dataGrid.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedMergeSubtitle)) { Source = vm }); + }); + dataGrid.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedMergeSubtitle)) { Source = vm }); vm.SubtitleGrid = dataGrid; dataGrid.AddHandler(InputElement.KeyDownEvent, (object? _, KeyEventArgs e) => { if (e.Key is Key.Home or Key.End && dataGrid.ItemsSource is IList items && items.Count > 0) { - var target = e.Key == Key.Home ? items[0] : items[^1]; - dataGrid.SelectedItem = target; - dataGrid.ScrollIntoView(target, null); + var index = e.Key == Key.Home ? 0 : items.Count - 1; + dataGrid.SelectedIndex = index; + dataGrid.ScrollIntoView(index); e.Handled = true; } }, RoutingStrategies.Tunnel); diff --git a/src/ui/Features/Tools/RemoveTextForHearingImpaired/RemoveTextForHearingImpairedWindow.cs b/src/ui/Features/Tools/RemoveTextForHearingImpaired/RemoveTextForHearingImpairedWindow.cs index 0fed68023a4..f4475675420 100644 --- a/src/ui/Features/Tools/RemoveTextForHearingImpaired/RemoveTextForHearingImpairedWindow.cs +++ b/src/ui/Features/Tools/RemoveTextForHearingImpaired/RemoveTextForHearingImpairedWindow.cs @@ -303,24 +303,20 @@ private static Border MakeInterjectionsView(RemoveTextForHearingImpairedViewMode private Grid MakeFixesView(RemoveTextForHearingImpairedViewModel vm) { - var dataGrid = new DataGrid + // Sorting dropped in the DataGrid -> TableView conversion: the grid previews + // fixes in subtitle order. + var dataGrid = TableViewExtras.MakeTableView(); + dataGrid.Width = double.NaN; + dataGrid.Height = double.NaN; + dataGrid.DataContext = _vm; + dataGrid.ItemsSource = _vm.Fixes; + dataGrid.Columns.AddRange(new TableViewColumn[] { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = _vm, - ItemsSource = _vm.Fixes, - Columns = - { - new DataGridTemplateColumn + new SeTableViewColumn { Header = Se.Language.General.Apply, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewNoPaddingCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, CellTemplate = new FuncDataTemplate((item, _) => new Border { @@ -333,34 +329,41 @@ private Grid MakeFixesView(RemoveTextForHearingImpairedViewModel vm) HorizontalAlignment = HorizontalAlignment.Center } }), - Width = new DataGridLength(1, DataGridLengthUnitType.Auto) + // Content-sized (Auto) on the DataGrid; TableView treats Auto as star. + Width = new GridLength(70) }, - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.NumberSymbol, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(RemoveItem.IndexDisplay)), - IsReadOnly = true, + Width = new GridLength(60), }, - new DataGridTextColumn + new SeTableViewColumn { + // Content-sized (Auto) on the DataGrid; Before holds subtitle text, + // so it shares the star width with After. Header = Se.Language.General.Before, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(RemoveItem.Before)), - IsReadOnly = true, + Width = new GridLength(1, GridUnitType.Star), }, - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.After, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(RemoveItem.After)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), + Width = new GridLength(1, GridUnitType.Star), }, - }, - }; - dataGrid.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(_vm.SelectedFix))); - _ = new DataGridCheckboxMultiSelect(dataGrid, + }); + dataGrid.Bind(TableView.SelectedItemProperty, new Binding(nameof(_vm.SelectedFix))); + + // Extended selection is native ListBox behavior on TableView; only the + // Space-toggles-checkbox piece of the old CheckboxMultiSelect needs wiring. + TableViewExtras.AddSpaceToggle(dataGrid, item => item.Apply, (item, v) => item.Apply = v); var labelLinesFound = UiUtil.MakeLabel().WithBindText(vm, nameof(vm.LinesFoundText)); diff --git a/src/ui/Features/Tools/RemoveUnicodeCharacters/RemoveUnicodeCharactersWindow.cs b/src/ui/Features/Tools/RemoveUnicodeCharacters/RemoveUnicodeCharactersWindow.cs index 22eda150bce..743d458c750 100644 --- a/src/ui/Features/Tools/RemoveUnicodeCharacters/RemoveUnicodeCharactersWindow.cs +++ b/src/ui/Features/Tools/RemoveUnicodeCharacters/RemoveUnicodeCharactersWindow.cs @@ -1,9 +1,13 @@ +using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Templates; using Avalonia.Data; +using Avalonia.Input; using Avalonia.Layout; +using Avalonia.VisualTree; using Nikse.SubtitleEdit.Logic; using Nikse.SubtitleEdit.Logic.Config; +using System.Linq; namespace Nikse.SubtitleEdit.Features.Tools.RemoveUnicodeCharacters; @@ -22,26 +26,22 @@ public RemoveUnicodeCharactersWindow(RemoveUnicodeCharactersViewModel vm) DataContext = vm; var l = Se.Language.Tools.RemoveUnicodeCharacters; - var dataGrid = new DataGrid + + // Sorting dropped in the DataGrid -> TableView conversion: the characters are + // listed in order of first appearance in the subtitle. + var dataGrid = TableViewExtras.MakeTableView(); + dataGrid.Width = double.NaN; + dataGrid.Height = double.NaN; + dataGrid.DataContext = vm; + dataGrid.ItemsSource = vm.Characters; + dataGrid.Columns.AddRange(new TableViewColumn[] { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.Characters, - Columns = - { - new DataGridTemplateColumn + new SeTableViewColumn { Header = Se.Language.General.Apply, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - IsReadOnly = false, - Width = new DataGridLength(55), + CellTheme = UiUtil.TableViewNoPaddingCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Width = new GridLength(55), CellTemplate = new FuncDataTemplate((_, _) => new CheckBox { @@ -51,47 +51,84 @@ public RemoveUnicodeCharactersWindow(RemoveUnicodeCharactersViewModel vm) [!CheckBox.IsCheckedProperty] = new Binding(nameof(RemoveUnicodeCharacterItem.IsChecked)) { Mode = BindingMode.TwoWay }, }), }, - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.Character, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(RemoveUnicodeCharacterItem.Character)), - IsReadOnly = true, + // Content-sized (Auto) on the DataGrid; TableView treats Auto as star. + Width = new GridLength(90), }, - new DataGridTextColumn + new SeTableViewColumn { Header = "Unicode", - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(RemoveUnicodeCharacterItem.CodeDisplay)), - IsReadOnly = true, + Width = new GridLength(100), }, - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.Count, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(RemoveUnicodeCharacterItem.Count)), - IsReadOnly = true, + Width = new GridLength(70), }, - new DataGridTextColumn + new SeTableViewColumn { + // The DataGrid edited this text column in place; TableView has no cell + // editing, so the cell hosts an always-editable TextBox instead. Header = Se.Language.General.ReplaceWith, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(RemoveUnicodeCharacterItem.ReplaceWith)) { Mode = BindingMode.TwoWay }, - IsReadOnly = false, - Width = new DataGridLength(120), + CellTheme = UiUtil.TableViewNoPaddingCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Width = new GridLength(120), + CellTemplate = new FuncDataTemplate((_, _) => + new TextBox + { + [!TextBox.TextProperty] = new Binding(nameof(RemoveUnicodeCharacterItem.ReplaceWith)) { Mode = BindingMode.TwoWay }, + VerticalAlignment = VerticalAlignment.Center, + HorizontalAlignment = HorizontalAlignment.Stretch, + Margin = new Thickness(2), + }), }, - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.Lines, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(RemoveUnicodeCharacterItem.LinesDisplay)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), + Width = new GridLength(1, GridUnitType.Star), }, - }, - }; - _ = new DataGridCheckboxMultiSelect(dataGrid, - item => item.IsChecked, (item, v) => item.IsChecked = v); + }); + + // Extended selection is native ListBox behavior on TableView; only the + // Space-toggles-checkbox piece of the old CheckboxMultiSelect needs wiring. + // Hand-rolled instead of TableViewExtras.AddSpaceToggle because Space typed in + // the ReplaceWith TextBox must keep inserting a space, not toggle checkboxes. + dataGrid.AddHandler(InputElement.KeyDownEvent, (object? _, KeyEventArgs e) => + { + if (e.Key != Key.Space || + (e.Source is Visual source && source.FindAncestorOfType(includeSelf: true) != null)) + { + return; + } + + var selected = dataGrid.SelectedItems?.OfType().ToList(); + if (selected == null || selected.Count == 0) + { + return; + } + + var newValue = !selected.All(item => item.IsChecked); + foreach (var item in selected) + { + item.IsChecked = newValue; + } + + e.Handled = true; + }, Avalonia.Interactivity.RoutingStrategies.Tunnel); var buttonSelectAll = UiUtil.MakeButton(Se.Language.General.SelectAll, vm.SelectAllCommand); var buttonInvertSelection = UiUtil.MakeButton(Se.Language.General.InvertSelection, vm.InvertSelectionCommand); diff --git a/src/ui/Features/Tools/SplitBreakLongLines/SplitBreakLongLinesWindow.cs b/src/ui/Features/Tools/SplitBreakLongLines/SplitBreakLongLinesWindow.cs index d84deeaaeb6..3fadfa3740c 100644 --- a/src/ui/Features/Tools/SplitBreakLongLines/SplitBreakLongLinesWindow.cs +++ b/src/ui/Features/Tools/SplitBreakLongLines/SplitBreakLongLinesWindow.cs @@ -147,31 +147,29 @@ private static Grid MakeFixesView(SplitBreakLongLinesViewModel vm) .WithMarginTop(10) .WithMarginLeft(10); - var dataGrid = new DataGrid + // Sorting dropped in the DataGrid -> TableView conversion: the grid previews + // split/rebalance fixes in subtitle order. + var dataGrid = TableViewExtras.MakeTableView(multiSelect: false); + dataGrid.Width = double.NaN; + dataGrid.Height = double.NaN; + dataGrid.DataContext = vm; + dataGrid.ItemsSource = vm.Fixes; + dataGrid.Columns.AddRange(new TableViewColumn[] { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.Fixes, - Columns = - { - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.NumberSymbol, Binding = new Binding(nameof(SplitBreakLongLinesItem.Number)), - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - IsReadOnly = true, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + // Content-sized (Auto) on the DataGrid; TableView treats Auto as star. + Width = new GridLength(60), }, - new DataGridTemplateColumn + new SeTableViewColumn { Header = Se.Language.General.Name, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewNoPaddingCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, CellTemplate = new FuncDataTemplate((item, _) => { if (item == null) @@ -202,27 +200,27 @@ private static Grid MakeFixesView(SplitBreakLongLinesViewModel vm) }, }; }), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Auto), + // Content-sized (Auto) on the DataGrid; fits the "Split long line" / + // "Rebalance long line" pill. + Width = new GridLength(150), }, - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.Fix, Binding = new Binding(nameof(SplitBreakLongLinesItem.Fix)), - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - CellTheme = UiUtil.DataGridNoBorderCellTheme, - IsReadOnly = true, + Width = new GridLength(1, GridUnitType.Star), + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, }, - }, - }; + }); dataGrid.AddHandler(InputElement.KeyDownEvent, (object? _, KeyEventArgs e) => { if (e.Key is Key.Home or Key.End && dataGrid.ItemsSource is IList items && items.Count > 0) { - var target = e.Key == Key.Home ? items[0] : items[^1]; - dataGrid.SelectedItem = target; - dataGrid.ScrollIntoView(target, null); + var index = e.Key == Key.Home ? 0 : items.Count - 1; + dataGrid.SelectedIndex = index; + dataGrid.ScrollIntoView(index); e.Handled = true; } }, RoutingStrategies.Tunnel); diff --git a/src/ui/Features/Tools/SplitSubtitle/SplitSubtitleWindow.cs b/src/ui/Features/Tools/SplitSubtitle/SplitSubtitleWindow.cs index 7421f703f26..c1f8b5b6ffe 100644 --- a/src/ui/Features/Tools/SplitSubtitle/SplitSubtitleWindow.cs +++ b/src/ui/Features/Tools/SplitSubtitle/SplitSubtitleWindow.cs @@ -194,52 +194,49 @@ private static Border MakeOptionsView(SplitSubtitleViewModel vm) private Control MakeListView(SplitSubtitleViewModel vm) { - var dataGrid = new DataGrid + // Sorting dropped in the DataGrid -> TableView conversion: the list shows the + // split parts in output order (part 1, part 2, ...). + var dataGrid = TableViewExtras.MakeTableView(multiSelect: false); + dataGrid.Width = double.NaN; + dataGrid.Height = double.NaN; + dataGrid.DataContext = vm; + dataGrid.ItemsSource = vm.SplitItems; + dataGrid.Columns.AddRange(new TableViewColumn[] { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.SplitItems, - Columns = - { - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.NoSymbolLines, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(SplitDisplayItem.Lines)), - IsReadOnly = true, + // Content-sized (Auto) on the DataGrid; TableView treats Auto as star. + Width = new GridLength(80), }, - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.Characters, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(SplitDisplayItem.Characters)), - IsReadOnly = true, + Width = new GridLength(100), }, - new DataGridTextColumn + new SeTableViewColumn { Header = Se.Language.General.FileName, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, Binding = new Binding(nameof(SplitDisplayItem.FileName)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), + Width = new GridLength(1, GridUnitType.Star), }, - }, - }; - dataGrid.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedSpiltItem)) { Source = vm }); + }); + dataGrid.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedSpiltItem)) { Source = vm }); dataGrid.AddHandler(InputElement.KeyDownEvent, (object? _, KeyEventArgs e) => { if (e.Key is Key.Home or Key.End && dataGrid.ItemsSource is IList items && items.Count > 0) { - var target = e.Key == Key.Home ? items[0] : items[^1]; - dataGrid.SelectedItem = target; - dataGrid.ScrollIntoView(target, null); + var index = e.Key == Key.Home ? 0 : items.Count - 1; + dataGrid.SelectedIndex = index; + dataGrid.ScrollIntoView(index); e.Handled = true; } }, RoutingStrategies.Tunnel); diff --git a/src/ui/Features/Video/BurnIn/BurnInViewModel.cs b/src/ui/Features/Video/BurnIn/BurnInViewModel.cs index 8e23df4ec99..88ac9a2fcba 100644 --- a/src/ui/Features/Video/BurnIn/BurnInViewModel.cs +++ b/src/ui/Features/Video/BurnIn/BurnInViewModel.cs @@ -106,7 +106,7 @@ public partial class BurnInViewModel : ObservableObject public Window? Window { get; set; } public bool OkPressed { get; private set; } - public DataGrid? BatchGrid { get; internal set; } + public TableView? BatchGrid { get; internal set; } public BurnInLogo BurnInLogo { get; set; } private Subtitle _subtitle = new(); @@ -520,7 +520,7 @@ private async Task InitAndStartJobItem(int index) } BatchGrid.SelectedItem = jobItem; - BatchGrid.ScrollIntoView(jobItem, null); + BatchGrid.ScrollIntoView(jobItem); }); bool result; diff --git a/src/ui/Features/Video/BurnIn/BurnInWindow.cs b/src/ui/Features/Video/BurnIn/BurnInWindow.cs index aa95561f74c..5b0425142d3 100644 --- a/src/ui/Features/Video/BurnIn/BurnInWindow.cs +++ b/src/ui/Features/Video/BurnIn/BurnInWindow.cs @@ -752,51 +752,47 @@ private static Border MakeAudioSettingsView(BurnInViewModel vm) private static Border MakeBatchView(BurnInViewModel vm) { - var dataGrid = new DataGrid + // No header sorting: the batch queue is processed top-to-bottom (jobs are + // selected/scrolled by index while generating), so the list order is meaningful. + var dataGrid = TableViewExtras.MakeTableView(multiSelect: false); + dataGrid.Width = double.NaN; + dataGrid.Height = double.NaN; + dataGrid.MinWidth = 550; // the batch column is Auto while measuring; star columns have no intrinsic width + dataGrid.DataContext = vm; + dataGrid.ItemsSource = vm.JobItems; + dataGrid.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.FileName, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(BurnInJobItem.InputVideoFileNameShort)), + Width = new GridLength(1, GridUnitType.Star), + }); + dataGrid.Columns.Add(new SeTableViewColumn { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.JobItems, - Columns = - { - new DataGridTextColumn - { - Header = Se.Language.General.FileName, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(BurnInJobItem.InputVideoFileNameShort)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Size, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(BurnInJobItem.Resolution)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.SubtitleFile, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(BurnInJobItem.SubtitleFileNameShort)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Status, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(BurnInJobItem.Status)), - IsReadOnly = true, - }, - }, - }; - dataGrid.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedJobItem)) { Source = vm }); + Header = Se.Language.General.Size, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(BurnInJobItem.Resolution)), + Width = new GridLength(90), // was content-sized (Auto) on the DataGrid + }); + dataGrid.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.SubtitleFile, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(BurnInJobItem.SubtitleFileNameShort)), + Width = new GridLength(1, GridUnitType.Star), + }); + dataGrid.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.Status, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(BurnInJobItem.Status)), + Width = new GridLength(110), // was content-sized (Auto) on the DataGrid + }); + dataGrid.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedJobItem)) { Source = vm }); vm.BatchGrid = dataGrid; var buttonAdd = UiUtil.MakeButton(Se.Language.General.AddDotDotDot, vm.AddCommand); diff --git a/src/ui/Features/Video/CutVideo/CutVideoViewModel.cs b/src/ui/Features/Video/CutVideo/CutVideoViewModel.cs index 410ae53b8b8..3f68f112604 100644 --- a/src/ui/Features/Video/CutVideo/CutVideoViewModel.cs +++ b/src/ui/Features/Video/CutVideo/CutVideoViewModel.cs @@ -64,7 +64,7 @@ public partial class CutVideoViewModel : ObservableObject public bool OkPressed { get; private set; } public VideoPlayerControl VideoPlayer { get; internal set; } public AudioVisualizer AudioVisualizer { get; internal set; } - public DataGrid SegmentGrid { get; internal set; } + public TableView SegmentGrid { get; internal set; } private Subtitle _subtitle = new(); private readonly StringBuilder _log; @@ -122,7 +122,7 @@ public CutVideoViewModel(IFolderHelper folderHelper, IFileHelper fileHelper, IWi JobItems = new ObservableCollection(); VideoPlayer = new VideoPlayerControl(new EmptyVideoPlayer()); AudioVisualizer = new AudioVisualizer(); - SegmentGrid = new DataGrid(); + SegmentGrid = new TableView(); Segments = new ObservableCollection(); VideoFileName = string.Empty; VideoFileSize = string.Empty; @@ -668,7 +668,7 @@ private void ImportCurrent() [RelayCommand] private void Delete() { - var selectedSegments = SegmentGrid.SelectedItems.Cast().ToList(); + var selectedSegments = SegmentGrid.SelectedItems?.Cast().ToList() ?? new List(); if (selectedSegments.Count == 0) { return; @@ -830,7 +830,9 @@ internal void OnKeyDownHandler(object? sender, KeyEventArgs keyEventArgs) return; } - if (SegmentGrid.IsFocused) + // Focus sits on the TableView row container (the DataGrid took focus itself), + // so check for focus anywhere inside the grid. + if (SegmentGrid.IsKeyboardFocusWithin) { if (keyEventArgs.Key == Key.Home && keyEventArgs.KeyModifiers == KeyModifiers.None && Segments.Count > 0) { @@ -980,7 +982,10 @@ private void SelectAndScrollToRow(int index) Dispatcher.UIThread.Post(() => { SegmentGrid.SelectedIndex = index; - SegmentGrid.ScrollIntoView(SegmentGrid.SelectedItem, null); + if (SegmentGrid.SelectedItem is { } selectedItem) + { + SegmentGrid.ScrollIntoView(selectedItem); + } UpdateSelection(); }, DispatcherPriority.Background); } @@ -993,8 +998,8 @@ internal void SegmentsGridChanged(object? sender, SelectionChangedEventArgs e) private void UpdateSelection() { IsDeleteEnabled = SelectedSegment != null; - IsSetStartEnabled = SegmentGrid.SelectedItems.Count == 1; - IsSetEndEnabled = SegmentGrid.SelectedItems.Count == 1; + IsSetStartEnabled = SegmentGrid.SelectedItems?.Count == 1; + IsSetEndEnabled = SegmentGrid.SelectedItems?.Count == 1; } internal void SegmentsGridDoubleTapped(object? sender, TappedEventArgs e) diff --git a/src/ui/Features/Video/CutVideo/CutVideoWindow.cs b/src/ui/Features/Video/CutVideo/CutVideoWindow.cs index 8542ece468f..71e8de0d533 100644 --- a/src/ui/Features/Video/CutVideo/CutVideoWindow.cs +++ b/src/ui/Features/Video/CutVideo/CutVideoWindow.cs @@ -106,51 +106,47 @@ private static Border MakeSegmentsView(CutVideoViewModel vm) { var fullTimeConverter = new TimeSpanToDisplayFullConverter(); var shortTimeConverter = new TimeSpanToDisplayShortConverter(); - var dataGridSubtitle = new DataGrid + + // No header sorting: the segment list is kept in time order (segments are + // inserted in position and feed the cut output), so reordering it would be wrong. + var dataGridSubtitle = TableViewExtras.MakeTableView(); + dataGridSubtitle.Width = double.NaN; + dataGridSubtitle.Height = double.NaN; + dataGridSubtitle.DataContext = vm; + dataGridSubtitle.ItemsSource = vm.Segments; + dataGridSubtitle.Columns.Add(new SeTableViewColumn { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Extended, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.Segments, - Columns = - { - new DataGridTextColumn - { - Header = Se.Language.General.NumberSymbol, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(SubtitleLineViewModel.Number)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Show, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(SubtitleLineViewModel.StartTime)) { Converter = fullTimeConverter }, - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Hide, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(SubtitleLineViewModel.EndTime)) { Converter = fullTimeConverter }, - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Duration, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(SubtitleLineViewModel.Duration)) { Converter = shortTimeConverter }, - IsReadOnly = true, - }, - }, - }; - dataGridSubtitle.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedSegment)) { Source = vm }); + Header = Se.Language.General.NumberSymbol, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(SubtitleLineViewModel.Number)), + Width = new GridLength(60), // was content-sized (Auto) on the DataGrid + }); + dataGridSubtitle.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.Show, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(SubtitleLineViewModel.StartTime)) { Converter = fullTimeConverter }, + Width = new GridLength(120), // was content-sized (Auto) on the DataGrid + }); + dataGridSubtitle.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.Hide, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(SubtitleLineViewModel.EndTime)) { Converter = fullTimeConverter }, + Width = new GridLength(120), // was content-sized (Auto) on the DataGrid + }); + dataGridSubtitle.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.Duration, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(SubtitleLineViewModel.Duration)) { Converter = shortTimeConverter }, + Width = new GridLength(1, GridUnitType.Star), + }); + dataGridSubtitle.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedSegment)) { Source = vm }); dataGridSubtitle.SelectionChanged += vm.SegmentsGridChanged; dataGridSubtitle.DoubleTapped += vm.SegmentsGridDoubleTapped; vm.SegmentGrid = dataGridSubtitle; diff --git a/src/ui/Features/Video/EmbeddedSubtitlesEdit/EmbedTrackPreviewWindow.cs b/src/ui/Features/Video/EmbeddedSubtitlesEdit/EmbedTrackPreviewWindow.cs index 4a3a14f8555..721f7fa8a74 100644 --- a/src/ui/Features/Video/EmbeddedSubtitlesEdit/EmbedTrackPreviewWindow.cs +++ b/src/ui/Features/Video/EmbeddedSubtitlesEdit/EmbedTrackPreviewWindow.cs @@ -71,87 +71,82 @@ private static Border MakeSubtitleView(EmbedTrackPreviewViewModel vm) { var fullTimeConverter = new TimeSpanToDisplayFullConverter(); var shortTimeConverter = new TimeSpanToDisplayShortConverter(); - var dataGridSubtitle = new DataGrid + + // No header sorting: this is a read-only preview of the track's cues in + // subtitle order. + var dataGridSubtitle = TableViewExtras.MakeTableView(multiSelect: false); + dataGridSubtitle.Width = double.NaN; + dataGridSubtitle.Height = double.NaN; + dataGridSubtitle.DataContext = vm; + dataGridSubtitle.ItemsSource = vm.Rows; + dataGridSubtitle.Columns.Add(new SeTableViewColumn { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.Rows, - Columns = + Header = Se.Language.General.NumberSymbol, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(MatroskaSubtitleCueDisplay.Number)), + Width = new GridLength(60), // was content-sized (Auto) on the DataGrid + }); + dataGridSubtitle.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.Show, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(MatroskaSubtitleCueDisplay.Show)) { Converter = fullTimeConverter }, + Width = new GridLength(120), // was content-sized (Auto) on the DataGrid + }); + dataGridSubtitle.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.Duration, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(MatroskaSubtitleCueDisplay.Duration)) { Converter = shortTimeConverter }, + Width = new GridLength(90), // was content-sized (Auto) on the DataGrid + }); + dataGridSubtitle.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.TextOrImage, + Width = new GridLength(1, GridUnitType.Star), + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + CellTemplate = new FuncDataTemplate((item, _) => { - new DataGridTextColumn - { - Header = Se.Language.General.NumberSymbol, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(MatroskaSubtitleCueDisplay.Number)), - IsReadOnly = true, - }, - new DataGridTextColumn + var stackPanel = new StackPanel { - Header = Se.Language.General.Show, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(MatroskaSubtitleCueDisplay.Show)) { Converter = fullTimeConverter }, - IsReadOnly = true, - }, - new DataGridTextColumn + Orientation = Orientation.Vertical, + Spacing = 5, + HorizontalAlignment = HorizontalAlignment.Left, + }; + + // Add text if available + if (!string.IsNullOrEmpty(item.Text)) { - Header = Se.Language.General.Duration, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(MatroskaSubtitleCueDisplay.Duration)) { Converter = shortTimeConverter }, - IsReadOnly = true, - }, - new DataGridTemplateColumn + var textBlock = new TextBlock + { + Text = item.Text, + TextWrapping = Avalonia.Media.TextWrapping.Wrap, + HorizontalAlignment = HorizontalAlignment.Left, + MaxWidth = 300 // Adjust as needed + }; + stackPanel.Children.Add(textBlock); + } + + // Add image if available + if (item.Image != null) { - Header = Se.Language.General.TextOrImage, - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - CellTemplate = new FuncDataTemplate((item, _) => + var image = new Image { - var stackPanel = new StackPanel - { - Orientation = Orientation.Vertical, - Spacing = 5, - HorizontalAlignment = HorizontalAlignment.Left, - }; - - // Add text if available - if (!string.IsNullOrEmpty(item.Text)) - { - var textBlock = new TextBlock - { - Text = item.Text, - TextWrapping = Avalonia.Media.TextWrapping.Wrap, - HorizontalAlignment = HorizontalAlignment.Left, - MaxWidth = 300 // Adjust as needed - }; - stackPanel.Children.Add(textBlock); - } - - // Add image if available - if (item.Image != null) - { - var image = new Image - { - Source = item.Image.Source, - MaxHeight = 100, // Adjust as needed - MaxWidth = 200, // Adjust as needed - Stretch = Avalonia.Media.Stretch.Uniform - }; - stackPanel.Children.Add(image); - } - - return stackPanel; - }) - }, - }, - }; + Source = item.Image.Source, + MaxHeight = 100, // Adjust as needed + MaxWidth = 200, // Adjust as needed + Stretch = Avalonia.Media.Stretch.Uniform + }; + stackPanel.Children.Add(image); + } + + return stackPanel; + }) + }); return UiUtil.MakeBorderForControlNoPadding(dataGridSubtitle); } diff --git a/src/ui/Features/Video/EmbeddedSubtitlesEdit/EmbeddedSubtitlesEditMp4ViewModel.cs b/src/ui/Features/Video/EmbeddedSubtitlesEdit/EmbeddedSubtitlesEditMp4ViewModel.cs index 6a2ef1035e1..055debc4c3e 100644 --- a/src/ui/Features/Video/EmbeddedSubtitlesEdit/EmbeddedSubtitlesEditMp4ViewModel.cs +++ b/src/ui/Features/Video/EmbeddedSubtitlesEdit/EmbeddedSubtitlesEditMp4ViewModel.cs @@ -47,7 +47,7 @@ public partial class EmbeddedSubtitlesEditMp4ViewModel : ObservableObject public Window? Window { get; set; } public bool OkPressed { get; private set; } - public DataGrid TracksGrid { get; internal set; } + public TableView TracksGrid { get; internal set; } private readonly StringBuilder _log; private long _startTicks; @@ -83,7 +83,7 @@ public EmbeddedSubtitlesEditMp4ViewModel(IFolderHelper folderHelper, IFileHelper Tracks = new ObservableCollection(); VideoFileName = string.Empty; ProgressText = string.Empty; - TracksGrid = new DataGrid(); + TracksGrid = new TableView(); _log = new StringBuilder(); _timerGenerate = new Timer { Interval = 100 }; @@ -714,7 +714,10 @@ private void SelectAndScrollToRow(int index) Dispatcher.UIThread.Post(() => { TracksGrid.SelectedIndex = index; - TracksGrid.ScrollIntoView(TracksGrid.SelectedItem, null); + if (TracksGrid.SelectedItem is { } selectedItem) + { + TracksGrid.ScrollIntoView(selectedItem); + } }, DispatcherPriority.Background); } diff --git a/src/ui/Features/Video/EmbeddedSubtitlesEdit/EmbeddedSubtitlesEditMp4Window.cs b/src/ui/Features/Video/EmbeddedSubtitlesEdit/EmbeddedSubtitlesEditMp4Window.cs index a24a37a2718..8e06f3bcece 100644 --- a/src/ui/Features/Video/EmbeddedSubtitlesEdit/EmbeddedSubtitlesEditMp4Window.cs +++ b/src/ui/Features/Video/EmbeddedSubtitlesEdit/EmbeddedSubtitlesEditMp4Window.cs @@ -92,84 +92,82 @@ private static Border MakeTracksView(EmbeddedSubtitlesEditMp4ViewModel vm) { var booleanToCheckMarkConverter = new BooleanToCheckMarkConverter(); var booleanToDeleteMarkConverter = new BooleanToDeleteMarkConverter(); - var dataGridTracks = new DataGrid + // No header sorting: the track list's order is the output track order + // (FfmpegGenerator.AlterEmbeddedTracksMp4 consumes Tracks in list order). + var dataGridTracks = TableViewExtras.MakeTableView(multiSelect: false); + dataGridTracks.Width = double.NaN; + dataGridTracks.Height = double.NaN; + dataGridTracks.DataContext = vm; + dataGridTracks.ItemsSource = vm.Tracks; + dataGridTracks.Columns.Add(new SeTableViewColumn { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.Tracks, - Columns = - { - new DataGridTextColumn - { - Header = string.Empty, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(EmbeddedTrack.Deleted)) { Mode = BindingMode.OneWay, Converter = booleanToDeleteMarkConverter }, - IsReadOnly = true, - }, - new DataGridTextColumn - { - // Visual cue distinguishing newly-added tracks from streams already - // present in the MP4. Without this users can't tell which row is - // theirs vs the original after clicking Add. - Header = Se.Language.General.New, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(EmbeddedTrack.New)) { Mode = BindingMode.OneWay, Converter = booleanToCheckMarkConverter }, - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Name, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(EmbeddedTrack.Name)) { Mode = BindingMode.OneWay }, - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Language, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(EmbeddedTrack.LanguageOrTitle)) { Mode = BindingMode.OneWay }, - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Default, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(EmbeddedTrack.Default)) { Converter = booleanToCheckMarkConverter, Mode = BindingMode.OneWay }, - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Forced, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(EmbeddedTrack.Forced)) { Converter = booleanToCheckMarkConverter, Mode = BindingMode.OneWay }, - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Codec, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(EmbeddedTrack.Format)) { Mode = BindingMode.OneWay }, - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.FileName, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(EmbeddedTrack.FileName)) { Mode = BindingMode.OneWay }, - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, - }, - }; - dataGridTracks.Bind(DataGrid.ItemsSourceProperty, new Binding(nameof(vm.Tracks)) { Source = vm }); - dataGridTracks.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedTrack)) { Source = vm }); + Header = string.Empty, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(EmbeddedTrack.Deleted)) { Mode = BindingMode.OneWay, Converter = booleanToDeleteMarkConverter }, + Width = new GridLength(40), // was content-sized (Auto) on the DataGrid + }); + dataGridTracks.Columns.Add(new SeTableViewColumn + { + // Visual cue distinguishing newly-added tracks from streams already + // present in the MP4. Without this users can't tell which row is + // theirs vs the original after clicking Add. + Header = Se.Language.General.New, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(EmbeddedTrack.New)) { Mode = BindingMode.OneWay, Converter = booleanToCheckMarkConverter }, + Width = new GridLength(60), // was content-sized (Auto) on the DataGrid + }); + dataGridTracks.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.Name, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(EmbeddedTrack.Name)) { Mode = BindingMode.OneWay }, + Width = new GridLength(160), // was content-sized (Auto) on the DataGrid + }); + dataGridTracks.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.Language, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(EmbeddedTrack.LanguageOrTitle)) { Mode = BindingMode.OneWay }, + Width = new GridLength(120), // was content-sized (Auto) on the DataGrid + }); + dataGridTracks.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.Default, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(EmbeddedTrack.Default)) { Converter = booleanToCheckMarkConverter, Mode = BindingMode.OneWay }, + Width = new GridLength(80), // was content-sized (Auto) on the DataGrid + }); + dataGridTracks.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.Forced, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(EmbeddedTrack.Forced)) { Converter = booleanToCheckMarkConverter, Mode = BindingMode.OneWay }, + Width = new GridLength(80), // was content-sized (Auto) on the DataGrid + }); + dataGridTracks.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.Codec, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(EmbeddedTrack.Format)) { Mode = BindingMode.OneWay }, + Width = new GridLength(90), // was content-sized (Auto) on the DataGrid + }); + dataGridTracks.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.FileName, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(EmbeddedTrack.FileName)) { Mode = BindingMode.OneWay }, + Width = new GridLength(1, GridUnitType.Star), + }); + dataGridTracks.Bind(TableView.ItemsSourceProperty, new Binding(nameof(vm.Tracks)) { Source = vm }); + dataGridTracks.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedTrack)) { Source = vm }); dataGridTracks.KeyDown += (s, e) => vm.OnTracksGridKeyDown(e); dataGridTracks.AddHandler(InputElement.KeyDownEvent, (object? _, KeyEventArgs e) => { @@ -177,7 +175,11 @@ private static Border MakeTracksView(EmbeddedSubtitlesEditMp4ViewModel vm) { var target = e.Key == Key.Home ? items[0] : items[^1]; dataGridTracks.SelectedItem = target; - dataGridTracks.ScrollIntoView(target, null); + if (target != null) + { + dataGridTracks.ScrollIntoView(target); + } + e.Handled = true; } }, Avalonia.Interactivity.RoutingStrategies.Tunnel); diff --git a/src/ui/Features/Video/EmbeddedSubtitlesEdit/EmbeddedSubtitlesEditViewModel.cs b/src/ui/Features/Video/EmbeddedSubtitlesEdit/EmbeddedSubtitlesEditViewModel.cs index 35132516526..53c4e35cb2e 100644 --- a/src/ui/Features/Video/EmbeddedSubtitlesEdit/EmbeddedSubtitlesEditViewModel.cs +++ b/src/ui/Features/Video/EmbeddedSubtitlesEdit/EmbeddedSubtitlesEditViewModel.cs @@ -39,7 +39,7 @@ public partial class EmbeddedSubtitlesEditViewModel : ObservableObject public Window? Window { get; set; } public bool OkPressed { get; private set; } - public DataGrid TracksGrid { get; internal set; } + public TableView TracksGrid { get; internal set; } private Subtitle _subtitle = new(); private readonly StringBuilder _log; @@ -70,7 +70,7 @@ public EmbeddedSubtitlesEditViewModel(IFolderHelper folderHelper, IFileHelper fi Tracks = new ObservableCollection(); VideoFileName = string.Empty; ProgressText = string.Empty; - TracksGrid = new DataGrid(); + TracksGrid = new TableView(); _log = new StringBuilder(); _timerGenerate = new(); @@ -718,7 +718,10 @@ private void SelectAndScrollToRow(int index) Dispatcher.UIThread.Post(() => { TracksGrid.SelectedIndex = index; - TracksGrid.ScrollIntoView(TracksGrid.SelectedItem, null); + if (TracksGrid.SelectedItem is { } selectedItem) + { + TracksGrid.ScrollIntoView(selectedItem); + } }, DispatcherPriority.Background); } diff --git a/src/ui/Features/Video/EmbeddedSubtitlesEdit/EmbeddedSubtitlesEditWindow.cs b/src/ui/Features/Video/EmbeddedSubtitlesEdit/EmbeddedSubtitlesEditWindow.cs index 9e4762b9afb..f6ed87d9082 100644 --- a/src/ui/Features/Video/EmbeddedSubtitlesEdit/EmbeddedSubtitlesEditWindow.cs +++ b/src/ui/Features/Video/EmbeddedSubtitlesEdit/EmbeddedSubtitlesEditWindow.cs @@ -98,74 +98,71 @@ private static Border MakeTracksView(EmbeddedSubtitlesEditViewModel vm) { var booleanToCheckMarkConverter = new BooleanToCheckMarkConverter(); var booleanToDeleteMarkConverter = new BooleanToDeleteMarkConverter(); - var dataGridTracks = new DataGrid + // No header sorting: the track list's order is the output track order + // (FfmpegGenerator.AlterEmbeddedTracksMatroska consumes Tracks in list order). + var dataGridTracks = TableViewExtras.MakeTableView(multiSelect: false); + dataGridTracks.Width = double.NaN; + dataGridTracks.Height = double.NaN; + dataGridTracks.DataContext = vm; + dataGridTracks.ItemsSource = vm.Tracks; + dataGridTracks.Columns.Add(new SeTableViewColumn { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.Tracks, - Columns = - { - new DataGridTextColumn - { - Header = string.Empty, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(EmbeddedTrack.Deleted)) { Mode = BindingMode.OneWay, Converter = booleanToDeleteMarkConverter }, - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Name, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(EmbeddedTrack.Name)) { Mode = BindingMode.OneWay }, - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Language, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(EmbeddedTrack.LanguageOrTitle)) { Mode = BindingMode.OneWay }, - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Default, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(EmbeddedTrack.Default)) { Converter = booleanToCheckMarkConverter, Mode = BindingMode.OneWay }, - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Forced, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(EmbeddedTrack.Forced)) { Converter = booleanToCheckMarkConverter, Mode = BindingMode.OneWay }, - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Codec, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(EmbeddedTrack.Format)) { Mode = BindingMode.OneWay }, - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.FileName, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(EmbeddedTrack.FileName)) { Mode = BindingMode.OneWay }, - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, - }, - }; - dataGridTracks.Bind(DataGrid.ItemsSourceProperty, new Binding(nameof(vm.Tracks)) { Source = vm }); - dataGridTracks.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedTrck)) { Source = vm }); + Header = string.Empty, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(EmbeddedTrack.Deleted)) { Mode = BindingMode.OneWay, Converter = booleanToDeleteMarkConverter }, + Width = new GridLength(40), // was content-sized (Auto) on the DataGrid + }); + dataGridTracks.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.Name, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(EmbeddedTrack.Name)) { Mode = BindingMode.OneWay }, + Width = new GridLength(160), // was content-sized (Auto) on the DataGrid + }); + dataGridTracks.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.Language, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(EmbeddedTrack.LanguageOrTitle)) { Mode = BindingMode.OneWay }, + Width = new GridLength(120), // was content-sized (Auto) on the DataGrid + }); + dataGridTracks.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.Default, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(EmbeddedTrack.Default)) { Converter = booleanToCheckMarkConverter, Mode = BindingMode.OneWay }, + Width = new GridLength(80), // was content-sized (Auto) on the DataGrid + }); + dataGridTracks.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.Forced, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(EmbeddedTrack.Forced)) { Converter = booleanToCheckMarkConverter, Mode = BindingMode.OneWay }, + Width = new GridLength(80), // was content-sized (Auto) on the DataGrid + }); + dataGridTracks.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.Codec, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(EmbeddedTrack.Format)) { Mode = BindingMode.OneWay }, + Width = new GridLength(90), // was content-sized (Auto) on the DataGrid + }); + dataGridTracks.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.FileName, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(EmbeddedTrack.FileName)) { Mode = BindingMode.OneWay }, + Width = new GridLength(1, GridUnitType.Star), + }); + dataGridTracks.Bind(TableView.ItemsSourceProperty, new Binding(nameof(vm.Tracks)) { Source = vm }); + dataGridTracks.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedTrck)) { Source = vm }); dataGridTracks.KeyDown += (s, e) => vm.OnTracksGridKeyDown(e); dataGridTracks.AddHandler(InputElement.KeyDownEvent, (object? _, KeyEventArgs e) => { @@ -173,7 +170,11 @@ private static Border MakeTracksView(EmbeddedSubtitlesEditViewModel vm) { var target = e.Key == Key.Home ? items[0] : items[^1]; dataGridTracks.SelectedItem = target; - dataGridTracks.ScrollIntoView(target, null); + if (target != null) + { + dataGridTracks.ScrollIntoView(target); + } + e.Handled = true; } }, Avalonia.Interactivity.RoutingStrategies.Tunnel); diff --git a/src/ui/Features/Video/OpenFromUrl/PickOnlineSubtitle/PickOnlineSubtitleViewModel.cs b/src/ui/Features/Video/OpenFromUrl/PickOnlineSubtitle/PickOnlineSubtitleViewModel.cs index b4f4b62877b..3a48eccaded 100644 --- a/src/ui/Features/Video/OpenFromUrl/PickOnlineSubtitle/PickOnlineSubtitleViewModel.cs +++ b/src/ui/Features/Video/OpenFromUrl/PickOnlineSubtitle/PickOnlineSubtitleViewModel.cs @@ -28,7 +28,7 @@ public partial class PickOnlineSubtitleViewModel : ObservableObject [ObservableProperty] private bool _isOkEnabled; public Window? Window { get; set; } - public DataGrid TracksGrid { get; set; } + public TableView TracksGrid { get; set; } public bool OkPressed { get; private set; } public string? SelectedSubtitlePath { get; private set; } @@ -44,7 +44,7 @@ public PickOnlineSubtitleViewModel(IFileHelper fileHelper, IWindowService window Tracks = new ObservableCollection(); PreviewRows = new ObservableCollection(); StatusText = string.Empty; - TracksGrid = new DataGrid(); + TracksGrid = new TableView(); } /// @@ -83,7 +83,7 @@ public void Initialize(IReadOnlyList subtitles, string? var initial = Tracks.First(); SelectedTrack = initial; TracksGrid.SelectedItem = initial; - TracksGrid.ScrollIntoView(initial, null); + TracksGrid.ScrollIntoView(initial); } partial void OnSelectedTrackChanged(OnlineSubtitleTrackDisplay? value) diff --git a/src/ui/Features/Video/OpenFromUrl/PickOnlineSubtitle/PickOnlineSubtitleWindow.cs b/src/ui/Features/Video/OpenFromUrl/PickOnlineSubtitle/PickOnlineSubtitleWindow.cs index 6871f528032..a8281025c67 100644 --- a/src/ui/Features/Video/OpenFromUrl/PickOnlineSubtitle/PickOnlineSubtitleWindow.cs +++ b/src/ui/Features/Video/OpenFromUrl/PickOnlineSubtitle/PickOnlineSubtitleWindow.cs @@ -71,50 +71,53 @@ public PickOnlineSubtitleWindow(PickOnlineSubtitleViewModel vm) private static Border MakeTracksView(PickOnlineSubtitleViewModel vm) { - var dataGridTracks = new DataGrid + var dataGridTracks = TableViewExtras.MakeTableView(multiSelect: false); + dataGridTracks.Width = double.NaN; + dataGridTracks.Height = double.NaN; + dataGridTracks.DataContext = vm; + dataGridTracks.ItemsSource = vm.Tracks; + + var columnLanguage = new SeTableViewColumn { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.Tracks, - Columns = - { - new DataGridTextColumn - { - Header = Se.Language.General.Language, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(OnlineSubtitleTrackDisplay.Language)), - IsReadOnly = true, - Width = new DataGridLength(1.6, DataGridLengthUnitType.Star), - }, - new DataGridTextColumn - { - Header = Se.Language.General.Name, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(OnlineSubtitleTrackDisplay.Name)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, - new DataGridTextColumn - { - Header = Se.Language.General.Format, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(OnlineSubtitleTrackDisplay.Format)), - IsReadOnly = true, - Width = new DataGridLength(0.6, DataGridLengthUnitType.Star), - }, - }, + Header = Se.Language.General.Language, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(OnlineSubtitleTrackDisplay.Language)), + Width = new GridLength(1.6, GridUnitType.Star), + }; + var columnName = new SeTableViewColumn + { + Header = Se.Language.General.Name, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(OnlineSubtitleTrackDisplay.Name)), + Width = new GridLength(1, GridUnitType.Star), + }; + var columnFormat = new SeTableViewColumn + { + Header = Se.Language.General.Format, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(OnlineSubtitleTrackDisplay.Format)), + Width = new GridLength(0.6, GridUnitType.Star), }; - dataGridTracks.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedTrack)) { Mode = BindingMode.TwoWay }); - dataGridTracks.DoubleTapped += (_, _) => + dataGridTracks.Columns.Add(columnLanguage); + dataGridTracks.Columns.Add(columnName); + dataGridTracks.Columns.Add(columnFormat); + + // Header sorting is safe here: the list is a pick list of search results whose + // order is presentation-only - the chosen subtitle is consumed via SelectedTrack, + // never via the collection's order or indexes. + var sorter = new TableViewHeaderSorter(dataGridTracks); + sorter.AddSortable(columnLanguage, x => x.Language) + .AddSortable(columnName, x => x.Name) + .AddSortable(columnFormat, x => x.Format); + + dataGridTracks.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedTrack)) { Mode = BindingMode.TwoWay }); + dataGridTracks.DoubleTapped += (_, e) => { - if (vm.IsOkEnabled) + // A double click on a column header (sorting) must not count as "pick". + if (vm.IsOkEnabled && !TableViewExtras.IsInColumnHeader(e.Source as Avalonia.Visual)) { vm.OkCommand.Execute(null); } @@ -128,51 +131,46 @@ private static Border MakePreviewView(PickOnlineSubtitleViewModel vm) { var fullTimeConverter = new TimeSpanToDisplayFullConverter(); var shortTimeConverter = new TimeSpanToDisplayShortConverter(); - var dataGridPreview = new DataGrid + + // No header sorting: this is a read-only preview of the subtitle's cues in + // subtitle order. + var dataGridPreview = TableViewExtras.MakeTableView(multiSelect: false); + dataGridPreview.Width = double.NaN; + dataGridPreview.Height = double.NaN; + dataGridPreview.DataContext = vm; + dataGridPreview.ItemsSource = vm.PreviewRows; + dataGridPreview.Columns.Add(new SeTableViewColumn { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.PreviewRows, - Columns = - { - new DataGridTextColumn - { - Header = Se.Language.General.NumberSymbol, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(OnlineSubtitleCueDisplay.Number)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Show, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(OnlineSubtitleCueDisplay.Show)) { Converter = fullTimeConverter }, - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Duration, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(OnlineSubtitleCueDisplay.Duration)) { Converter = shortTimeConverter }, - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Text, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(OnlineSubtitleCueDisplay.Text)), - IsReadOnly = true, - Width = new DataGridLength(1, DataGridLengthUnitType.Star), - }, - }, - }; + Header = Se.Language.General.NumberSymbol, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(OnlineSubtitleCueDisplay.Number)), + Width = new GridLength(60), // was content-sized (Auto) on the DataGrid + }); + dataGridPreview.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.Show, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(OnlineSubtitleCueDisplay.Show)) { Converter = fullTimeConverter }, + Width = new GridLength(120), // was content-sized (Auto) on the DataGrid + }); + dataGridPreview.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.Duration, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(OnlineSubtitleCueDisplay.Duration)) { Converter = shortTimeConverter }, + Width = new GridLength(90), // was content-sized (Auto) on the DataGrid + }); + dataGridPreview.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.Text, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(OnlineSubtitleCueDisplay.Text)), + Width = new GridLength(1, GridUnitType.Star), + }); return UiUtil.MakeBorderForControlNoPadding(dataGridPreview); } diff --git a/src/ui/Features/Video/SpeechToText/SpeechToTextViewModel.cs b/src/ui/Features/Video/SpeechToText/SpeechToTextViewModel.cs index 83978823c49..18c3a085c0e 100644 --- a/src/ui/Features/Video/SpeechToText/SpeechToTextViewModel.cs +++ b/src/ui/Features/Video/SpeechToText/SpeechToTextViewModel.cs @@ -138,7 +138,7 @@ public partial class SpeechToTextViewModel : ObservableObject public TextBox TextBoxConsoleLogBatch { get; internal set; } public TextBox TextBoxConsoleLogSingle { get; internal set; } public Button? CopyConsoleLogButton { get; internal set; } - public DataGrid BatchGrid { get; internal set; } + public TableView BatchGrid { get; internal set; } private bool _unknownArgument; private bool _cudaOutOfMemory; @@ -290,7 +290,7 @@ public SpeechToTextViewModel(IWindowService windowService, IFileHelper fileHelpe TranscribedSubtitle = new Subtitle(); TextBoxConsoleLogBatch = new TextBox(); TextBoxConsoleLogSingle = new TextBox(); - BatchGrid = new DataGrid(); + BatchGrid = new TableView(); ReDownloadText = string.Empty; EngineDownloadHint = string.Empty; _audioTrackNumber = -1; @@ -1749,7 +1749,7 @@ private void StartNext(Subtitle? transcribedSubtitle) } BatchGrid.SelectedItem = jobItem; - BatchGrid.ScrollIntoView(jobItem, null); + BatchGrid.ScrollIntoView(jobItem); }); var startGenerateAudioFileOk = GenerateAudioFile(_videoFileName, _audioTrackNumber); @@ -3443,7 +3443,7 @@ private async Task Transcribe() } BatchGrid.SelectedItem = jobItem; - BatchGrid.ScrollIntoView(jobItem, null); + BatchGrid.ScrollIntoView(jobItem); }); } diff --git a/src/ui/Features/Video/SpeechToText/SpeechToTextWindow.cs b/src/ui/Features/Video/SpeechToText/SpeechToTextWindow.cs index da43e6239b1..b4d8787dfec 100644 --- a/src/ui/Features/Video/SpeechToText/SpeechToTextWindow.cs +++ b/src/ui/Features/Video/SpeechToText/SpeechToTextWindow.cs @@ -522,44 +522,38 @@ private static Grid MakeConsoleLogAndBatchView(SpeechToTextViewModel vm) vm.TextBoxConsoleLogBatch = textBoxConsoleLog; - var dataGrid = new DataGrid + // No header sorting: the batch queue is processed top-to-bottom (the run aliases + // BatchItems and walks it in list order), so the list order is meaningful. + var dataGrid = TableViewExtras.MakeTableView(multiSelect: false); + dataGrid.Width = double.NaN; + dataGrid.Height = double.NaN; + dataGrid.DataContext = vm; + dataGrid.ItemsSource = vm.BatchItems; + dataGrid.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.FileName, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(SpeechToTextJobItem.InputVideoFileNameShort)), + Width = new GridLength(1, GridUnitType.Star), + }); + dataGrid.Columns.Add(new SeTableViewColumn { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.BatchItems, - Columns = - { - new DataGridTextColumn - { - Header = Se.Language.General.FileName, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(SpeechToTextJobItem.InputVideoFileNameShort)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Size, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(SpeechToTextJobItem.SizeDisplay)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Status, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(SpeechToTextJobItem.Status)), - IsReadOnly = true, - }, - }, - }; - dataGrid.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedBatchItem)) { Source = vm }); + Header = Se.Language.General.Size, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(SpeechToTextJobItem.SizeDisplay)), + Width = new GridLength(90), // was content-sized (Auto) on the DataGrid + }); + dataGrid.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.Status, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(SpeechToTextJobItem.Status)), + Width = new GridLength(110), // was content-sized (Auto) on the DataGrid + }); + dataGrid.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedBatchItem)) { Source = vm }); AutomationProperties.SetName(dataGrid, Se.Language.General.BatchMode); vm.BatchGrid = dataGrid; @@ -594,7 +588,7 @@ private static Grid MakeConsoleLogAndBatchView(SpeechToTextViewModel vm) HorizontalAlignment = HorizontalAlignment.Stretch, }; - // hack to make drag and drop work on the DataGrid - also on empty rows + // hack to make drag and drop work on the grid - also on empty rows var dropHost = new Border { Background = Brushes.Transparent, diff --git a/src/ui/Features/Video/TransparentSubtitles/TransparentSubtitlesWindow.cs b/src/ui/Features/Video/TransparentSubtitles/TransparentSubtitlesWindow.cs index 45d6ea6b671..8a0b3e1259b 100644 --- a/src/ui/Features/Video/TransparentSubtitles/TransparentSubtitlesWindow.cs +++ b/src/ui/Features/Video/TransparentSubtitles/TransparentSubtitlesWindow.cs @@ -537,51 +537,47 @@ private static Border MakePreviewView(TransparentSubtitlesViewModel vm) private static Border MakeBatchView(TransparentSubtitlesViewModel vm) { - var dataGrid = new DataGrid + // No header sorting: the batch queue is processed top-to-bottom, so the list + // order is meaningful. + var dataGrid = TableViewExtras.MakeTableView(multiSelect: false); + dataGrid.Width = double.NaN; + dataGrid.Height = double.NaN; + dataGrid.MinWidth = 550; // the batch column is Auto while measuring; star columns have no intrinsic width + dataGrid.DataContext = vm; + dataGrid.ItemsSource = vm.JobItems; + dataGrid.Columns.Add(new SeTableViewColumn { - AutoGenerateColumns = false, - SelectionMode = DataGridSelectionMode.Single, - CanUserResizeColumns = true, - CanUserSortColumns = true, - HorizontalAlignment = HorizontalAlignment.Stretch, - VerticalAlignment = VerticalAlignment.Stretch, - Width = double.NaN, - Height = double.NaN, - DataContext = vm, - ItemsSource = vm.JobItems, - Columns = - { - new DataGridTextColumn - { - Header = Se.Language.General.SubtitleFileName, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(BurnInJobItem.SubtitleFileNameShort)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Size, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(BurnInJobItem.Resolution)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.VideoFile, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(BurnInJobItem.InputVideoFileNameShort)), - IsReadOnly = true, - }, - new DataGridTextColumn - { - Header = Se.Language.General.Status, - CellTheme = UiUtil.DataGridNoBorderNoPaddingCellTheme, - Binding = new Binding(nameof(BurnInJobItem.Status)), - IsReadOnly = true, - }, - }, - }; - dataGrid.Bind(DataGrid.SelectedItemProperty, new Binding(nameof(vm.SelectedJobItem)) { Source = vm }); + Header = Se.Language.General.SubtitleFileName, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(BurnInJobItem.SubtitleFileNameShort)), + Width = new GridLength(1, GridUnitType.Star), + }); + dataGrid.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.Size, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(BurnInJobItem.Resolution)), + Width = new GridLength(90), // was content-sized (Auto) on the DataGrid + }); + dataGrid.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.VideoFile, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(BurnInJobItem.InputVideoFileNameShort)), + Width = new GridLength(1, GridUnitType.Star), + }); + dataGrid.Columns.Add(new SeTableViewColumn + { + Header = Se.Language.General.Status, + CellTheme = UiUtil.TableViewCellTheme, + HeaderTheme = UiUtil.TableViewColumnHeaderTheme, + Binding = new Binding(nameof(BurnInJobItem.Status)), + Width = new GridLength(110), // was content-sized (Auto) on the DataGrid + }); + dataGrid.Bind(TableView.SelectedItemProperty, new Binding(nameof(vm.SelectedJobItem)) { Source = vm }); var buttonAdd = UiUtil.MakeButton(Se.Language.General.AddDotDotDot, vm.AddCommand); var buttonRemove = UiUtil.MakeButton(Se.Language.General.Remove, vm.RemoveCommand); diff --git a/src/ui/Logic/TableViewExtras.cs b/src/ui/Logic/TableViewExtras.cs index 85a8177c90e..f66cb720134 100644 --- a/src/ui/Logic/TableViewExtras.cs +++ b/src/ui/Logic/TableViewExtras.cs @@ -296,6 +296,37 @@ static void Apply(Control container, int index, IBrush alternatingBrush) tableView.ContainerIndexChanged += (_, e) => Apply(e.Container, e.NewIndex, brush); } + /// + /// Home/End jump to the first/last row even when the TableView itself (not a row) + /// has keyboard focus - ListBox's native handling only runs with focus on an item. + /// Skipped when the key originates in a TextBox (e.g. an in-cell editor). + /// + public static void AttachHomeEndNavigation(TableView tableView) + { + tableView.AddHandler(InputElement.KeyDownEvent, (object? _, KeyEventArgs e) => + { + if (e.Key is not (Key.Home or Key.End) || e.Source is TextBox) + { + return; + } + + if (tableView.ItemsSource is not System.Collections.IList items || items.Count == 0) + { + return; + } + + var target = e.Key == Key.Home ? items[0] : items[^1]; + if (target == null) + { + return; + } + + tableView.SelectedItem = target; + tableView.ScrollIntoView(target); + e.Handled = true; + }, Avalonia.Interactivity.RoutingStrategies.Tunnel); + } + /// /// Space toggles the checkbox value of every selected row - all rows checked means /// uncheck all, otherwise check all. This is the piece of the DataGrid-era From 9c870f87875f5392f9d545b805dd6f724a2c99b2 Mon Sep 17 00:00:00 2001 From: niksedk Date: Fri, 31 Jul 2026 12:06:00 +0200 Subject: [PATCH 2/2] Fix Point sync layouts: cap the sync-point list width A TableView with a star column measured without a width constraint demands more than the whole window (star columns have no content size to fall back on - verified headless: 1012px desired in a 1000px window), so the sync-point lists in their Auto-sized outer columns blew up the middle panel, squeezed the subtitle grids to slivers and pushed content past the window edge. Give the lists the fixed 280px width the DataGrid version had visually. BurnIn/TransparentSubtitles look like the same pattern but are safe: their batch column switches to star whenever the batch list is visible. Co-Authored-By: Claude Fable 5 --- src/ui/Features/Sync/PointSync/PointSyncWindow.cs | 5 +++++ .../Sync/PointSyncViaOther/PointSyncViaOtherWindow.cs | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/ui/Features/Sync/PointSync/PointSyncWindow.cs b/src/ui/Features/Sync/PointSync/PointSyncWindow.cs index 9fc730dc36b..8e1e6ea95b3 100644 --- a/src/ui/Features/Sync/PointSync/PointSyncWindow.cs +++ b/src/ui/Features/Sync/PointSync/PointSyncWindow.cs @@ -90,6 +90,11 @@ private static Control MakeControlView(PointSyncViewModel vm) // has no such switch, so the single column's header now doubles as the panel title. var dataGrid = TableViewExtras.MakeTableView(multiSelect: false); dataGrid.CanUserResizeColumns = false; // single star column, nothing to resize + // Fixed width: this panel sits in an Auto-sized outer column, and a TableView + // with a star column measured without a width constraint demands more than the + // whole window (star columns have no content-based size), squeezing the + // subtitle grids to slivers and overflowing the right edge. + dataGrid.Width = 280; dataGrid.DataContext = vm; dataGrid.ItemsSource = vm.SyncPoints; dataGrid.Columns.Add(new SeTableViewColumn diff --git a/src/ui/Features/Sync/PointSyncViaOther/PointSyncViaOtherWindow.cs b/src/ui/Features/Sync/PointSyncViaOther/PointSyncViaOtherWindow.cs index 05731e00dbf..e5789d2eccb 100644 --- a/src/ui/Features/Sync/PointSyncViaOther/PointSyncViaOtherWindow.cs +++ b/src/ui/Features/Sync/PointSyncViaOther/PointSyncViaOtherWindow.cs @@ -94,6 +94,11 @@ private static Control MakeControlView(PointSyncViaOtherViewModel vm) // has no such switch, so the single column's header now doubles as the panel title. var dataGrid = TableViewExtras.MakeTableView(multiSelect: false); dataGrid.CanUserResizeColumns = false; // single star column, nothing to resize + // Fixed width: this panel sits in an Auto-sized outer column, and a TableView + // with a star column measured without a width constraint demands more than the + // whole window (star columns have no content-based size), squeezing the + // subtitle grids to slivers and overflowing the right edge. + dataGrid.Width = 280; dataGrid.DataContext = vm; dataGrid.ItemsSource = vm.SyncPoints; dataGrid.Columns.Add(new SeTableViewColumn