From 96bc3efc4ce6f700e71ce4201296325813a5957c Mon Sep 17 00:00:00 2001 From: Waheed Ahmad Date: Thu, 22 May 2025 16:31:55 +0500 Subject: [PATCH] fixed row and cell selection on Uno platform --- src/Extensions/ItemIndexRangeExtensions.cs | 11 ++ src/TableView.cs | 32 ++---- src/TableViewCell.cs | 7 ++ src/TableViewHeaderRow.cs | 5 +- src/TableViewRow.cs | 16 ++- src/Tableview.Uno.cs | 116 +++++++++++++++++++++ src/Themes/TableViewCellsPresenter.xaml | 6 +- src/Themes/TableViewRow.xaml | 104 +++++++++--------- 8 files changed, 218 insertions(+), 79 deletions(-) create mode 100644 src/Tableview.Uno.cs diff --git a/src/Extensions/ItemIndexRangeExtensions.cs b/src/Extensions/ItemIndexRangeExtensions.cs index 66ded56..7afae48 100644 --- a/src/Extensions/ItemIndexRangeExtensions.cs +++ b/src/Extensions/ItemIndexRangeExtensions.cs @@ -17,4 +17,15 @@ public static bool IsInRange(this ItemIndexRange range, int index) { return index >= range.FirstIndex && index <= range.LastIndex; } + + /// + /// Determines whether the given item index range is valid within the TableView. + /// + /// The ItemIndexRange to check. + /// The TableView to check against. + /// True if the item index range of TableView is valid; otherwise, false. + public static bool IsValid(this ItemIndexRange itemIndexRange, TableView tableView) + { + return itemIndexRange.FirstIndex >= 0 && itemIndexRange.LastIndex < tableView?.Items.Count; + } } diff --git a/src/TableView.cs b/src/TableView.cs index a99bb1b..db74321 100644 --- a/src/TableView.cs +++ b/src/TableView.cs @@ -70,9 +70,7 @@ private void TableView_SelectionChanged(object sender, SelectionChangedEventArgs { SelectedCellRanges.RemoveWhere(slots => { -#if WINDOWS slots.RemoveWhere(slot => SelectedRanges.Any(range => range.IsInRange(slot.Row))); -#endif return slots.Count == 0; }); } @@ -360,14 +358,12 @@ public string GetSelectedContent(bool includeHeaders, char separator = '\t') if (SelectedItems.Any() || SelectedCells.Count != 0) { -#if WINDOWS slots = SelectedRanges.SelectMany(x => Enumerable.Range(x.FirstIndex, (int)x.Length)) - .SelectMany(r => Enumerable.Range(0, Columns.VisibleColumns.Count) - .Select(c => new TableViewCellSlot(r, c))) - .Concat(SelectedCells) - .OrderBy(x => x.Row) - .ThenByDescending(x => x.Column); -#endif + .SelectMany(r => Enumerable.Range(0, Columns.VisibleColumns.Count) + .Select(c => new TableViewCellSlot(r, c))) + .Concat(SelectedCells) + .OrderBy(x => x.Row) + .ThenByDescending(x => x.Column); } else if (CurrentCellSlot.HasValue) { @@ -813,9 +809,7 @@ public void RefreshFilter() break; case ListViewSelectionMode.Multiple: case ListViewSelectionMode.Extended: -#if WINDOWS SelectRange(new ItemIndexRange(0, (uint)Items.Count)); -#endif break; } } @@ -875,9 +869,7 @@ private void DeselectAllItems() break; case ListViewSelectionMode.Multiple: case ListViewSelectionMode.Extended: -#if WINDOWS DeselectRange(new ItemIndexRange(0, (uint)Items.Count)); -#endif break; } } @@ -925,9 +917,9 @@ internal void MakeSelection(TableViewCellSlot slot, bool shiftKey, bool ctrlKey { SelectRows(slot, shiftKey); LastSelectionUnit = TableViewSelectionUnit.Row; - } - else - { + } + else + { SelectCells(slot, shiftKey); LastSelectionUnit = TableViewSelectionUnit.Cell; } @@ -944,7 +936,6 @@ internal void MakeSelection(TableViewCellSlot slot, bool shiftKey, bool ctrlKey /// private void SelectRows(TableViewCellSlot slot, bool shiftKey) { -#if WINDOWS var selectionRange = SelectedRanges.FirstOrDefault(x => x.IsInRange(slot.Row)); SelectionStartRowIndex ??= slot.Row; CurrentRowIndex = slot.Row; @@ -952,14 +943,14 @@ private void SelectRows(TableViewCellSlot slot, bool shiftKey) if (selectionRange is not null) { DeselectRange(selectionRange); - } + } if (shiftKey && SelectionMode is ListViewSelectionMode.Multiple or ListViewSelectionMode.Extended) { var min = Math.Min(SelectionStartRowIndex.Value, slot.Row); var max = Math.Max(SelectionStartRowIndex.Value, slot.Row); - SelectRange(new ItemIndexRange(min, (uint)(max - min) + 1)); + SelectRange(new ItemIndexRange(min, (uint)(max - min) + 1)); } else { @@ -972,7 +963,7 @@ private void SelectRows(TableViewCellSlot slot, bool shiftKey) { SelectRange(new ItemIndexRange(slot.Row, 1)); } - } + } if (!IsReadOnly && slot.IsValid(this)) { @@ -986,7 +977,6 @@ private void SelectRows(TableViewCellSlot slot, bool shiftKey) row?.Focus(FocusState.Programmatic); }); } -#endif } /// diff --git a/src/TableViewCell.cs b/src/TableViewCell.cs index 2af1ac5..0fc52ed 100644 --- a/src/TableViewCell.cs +++ b/src/TableViewCell.cs @@ -236,10 +236,17 @@ protected override void OnManipulationDelta(ManipulationDeltaRoutedEventArgs e) if (_scrollViewer is { }) { +#if WINDOWS var transform = _scrollViewer.TransformToVisual(this).Inverse; var point = transform.TransformPoint(position); var transformedPoint = _scrollViewer.TransformToVisual(null).TransformPoint(point); return VisualTreeHelper.FindElementsInHostCoordinates(transformedPoint, _scrollViewer) +#else + return VisualTreeHelper.FindElementsInHostCoordinates(position, _scrollViewer, true) + .OfType() + .Where(x => x.Name is "Content") + .Select(x => x.FindAscendant() is { } cell ? cell : default) +#endif .OfType() .FirstOrDefault(); } diff --git a/src/TableViewHeaderRow.cs b/src/TableViewHeaderRow.cs index 6ffee87..e916af4 100644 --- a/src/TableViewHeaderRow.cs +++ b/src/TableViewHeaderRow.cs @@ -362,14 +362,11 @@ internal void SetCornerButtonState() { var stateName = VisualStates.StateNoButton; -#if WINDOWS if (TableView is ListView { SelectionMode: ListViewSelectionMode.Multiple }) { stateName = TableView.IsEditing ? VisualStates.StateSelectAllCheckBoxDisabled : VisualStates.StateSelectAllCheckBox; } - else -#endif - if (TableView is { CornerButtonMode: TableViewCornerButtonMode.Options }) + else if (TableView is { CornerButtonMode: TableViewCornerButtonMode.Options }) { stateName = TableView.IsEditing ? VisualStates.StateOptionsButtonDisabled : VisualStates.StateOptionsButton; } diff --git a/src/TableViewRow.cs b/src/TableViewRow.cs index 15bb404..a3a3d75 100644 --- a/src/TableViewRow.cs +++ b/src/TableViewRow.cs @@ -500,14 +500,26 @@ internal void EnsureGridLines() /// internal void EnsureLayout() { -#if WINDOWS if (CellPresenter is not null && TableView is not null) { CellPresenter.Padding = ((ListView)TableView).SelectionMode is ListViewSelectionMode.Multiple +#if WINDOWS ? new Thickness(16, 0, 16, 0) +#else + ? new Thickness(8, 0, 16, 0) +#endif : new Thickness(20, 0, 16, 0); - } +#if !WINDOWS + var multiSelectSquare = this.FindDescendant(x => x.Name is "MultiSelectSquare"); + if (multiSelectSquare is not null) + { + multiSelectSquare.Opacity = 0.5; + multiSelectSquare.CornerRadius = new CornerRadius(4); + multiSelectSquare.BorderThickness = new Thickness(1); + multiSelectSquare.Margin = new Thickness(10, 0, 0, 0); + } #endif + } } /// diff --git a/src/Tableview.Uno.cs b/src/Tableview.Uno.cs new file mode 100644 index 0000000..91fa2fc --- /dev/null +++ b/src/Tableview.Uno.cs @@ -0,0 +1,116 @@ +#if !WINDOWS +using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Data; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using WinUI.TableView.Extensions; + +namespace WinUI.TableView; + +/// +/// Partial class for TableView that contains Uno stuff. +/// +partial class TableView +{ + private const BindingFlags BindingAttr = BindingFlags.NonPublic | BindingFlags.Instance; + private PropertyInfo? _disableRaiseSelectionChangedPropertyInfo; + private MethodInfo? _invokeSelectionChangedMethodInfo; + + private void SetDisableRaiseSelectionChanged(bool value) + { + _disableRaiseSelectionChangedPropertyInfo ??= typeof(Selector).GetProperty("DisableRaiseSelectionChanged", BindingAttr); + _disableRaiseSelectionChangedPropertyInfo?.SetValue(this, value); + } + + private void InvokeSelectionChanged(object[] removedItems, object[] addedItems) + { + _invokeSelectionChangedMethodInfo ??= typeof(Selector).GetMethod("InvokeSelectionChanged", BindingAttr); + _invokeSelectionChangedMethodInfo?.Invoke(this, [removedItems, addedItems]); + } + + private new void DeselectRange(ItemIndexRange itemIndexRange) + { + var removedItems = new List(); + + SetDisableRaiseSelectionChanged(true); + { + if (!itemIndexRange.IsValid(this)) + { + throw new IndexOutOfRangeException("The given item index range bounds are not valid."); + } + + for (var index = itemIndexRange.FirstIndex; index <= itemIndexRange.LastIndex; index++) + { + var item = Items[index]; + if (SelectedItems.Contains(item)) + { + removedItems.Add(item); + SelectedItems.Remove(item); + } + } + + AdjustSelectedRanges(); + } + SetDisableRaiseSelectionChanged(false); + + InvokeSelectionChanged([.. removedItems], []); + } + + private void AdjustSelectedRanges() + { + SelectedRanges.Clear(); + + if (SelectedItems.Count == 0) return; + + var selectedIndexes = SelectedItems.Select(Items.IndexOf).Order(); + var start = selectedIndexes.First(); + var prev = start; + + foreach (var index in selectedIndexes) + { + if (index != prev + 1) + { + var length = (uint)(prev - start + 1); + SelectedRanges.Add(new ItemIndexRange(start, length)); + start = index; + } + prev = index; + } + + var finalLength = (uint)(prev - start + 1); + SelectedRanges.Add(new ItemIndexRange(start, finalLength)); + } + + private new void SelectRange(ItemIndexRange itemIndexRange) + { + var addedItems = new List(); + + SetDisableRaiseSelectionChanged(true); + { + if (!itemIndexRange.IsValid(this)) + { + throw new IndexOutOfRangeException("The given item index range bounds are not valid."); + } + + for (var index = itemIndexRange.FirstIndex; index <= itemIndexRange.LastIndex; index++) + { + var item = Items[index]; + if (!SelectedItems.Contains(item)) + { + addedItems.Add(item); + SelectedItems.Add(item); + } + } + + AdjustSelectedRanges(); + } + SetDisableRaiseSelectionChanged(false); + + InvokeSelectionChanged([], [.. addedItems]); + } + + private new IList SelectedRanges { get; } = []; +} +#endif \ No newline at end of file diff --git a/src/Themes/TableViewCellsPresenter.xaml b/src/Themes/TableViewCellsPresenter.xaml index 61f5e2a..850a71f 100644 --- a/src/Themes/TableViewCellsPresenter.xaml +++ b/src/Themes/TableViewCellsPresenter.xaml @@ -34,13 +34,15 @@ + VerticalAlignment="Stretch" + IsHitTestVisible="False" /> + HorizontalAlignment="Stretch" + IsHitTestVisible="False" /> diff --git a/src/Themes/TableViewRow.xaml b/src/Themes/TableViewRow.xaml index eda61ea..dbfaf3e 100644 --- a/src/Themes/TableViewRow.xaml +++ b/src/Themes/TableViewRow.xaml @@ -1,7 +1,10 @@ + xmlns:win="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:not_win="http://uno.ui/not_win" + mc:Ignorable="not_win"> @@ -9,7 +12,8 @@