Skip to content
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 48 additions & 3 deletions src/Extensions/ItemIndexRangeExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.UI.Xaml.Data;
using System.Runtime.CompilerServices;

namespace WinUI.TableView.Extensions;

Expand All @@ -21,11 +22,55 @@ public static bool IsInRange(this ItemIndexRange range, int index)
/// <summary>
/// Determines whether the given item index range is valid within the TableView.
/// </summary>
/// <param name="itemIndexRange">The ItemIndexRange to check.</param>
/// <param name="range">The ItemIndexRange to check.</param>
/// <param name="tableView">The TableView to check against.</param>
/// <returns>True if the item index range of TableView is valid; otherwise, false.</returns>
public static bool IsValid(this ItemIndexRange itemIndexRange, TableView tableView)
public static bool IsValid(this ItemIndexRange range, TableView tableView)
{
return range.FirstIndex >= 0 && range.LastIndex < tableView?.Items.Count;
}

/// <summary>
/// Determines whether the specified range completely contains another range.
/// </summary>
/// <param name="range">The range to check.</param>
/// <param name="other">The range to check against.</param>
/// <returns>True if the range completely contains the other range; otherwise, false.</returns>
public static bool Contains(this ItemIndexRange range, ItemIndexRange other)
{
return itemIndexRange.FirstIndex >= 0 && itemIndexRange.LastIndex < tableView?.Items.Count;
return other.FirstIndex >= range.FirstIndex && other.LastIndex <= range.LastIndex;
}

/// <summary>
/// Subtracts another ItemIndexRange from the current range and returns the resulting range.
/// </summary>
/// <param name="range">The range to subtract from.</param>
/// <param name="other">The range to subtract.</param>
/// <returns>The resulting range after subtraction.</returns>
public static IEnumerable<ItemIndexRange> Subtract(this ItemIndexRange range, ItemIndexRange other)
{
var start = range.FirstIndex;
var end = start + (int)range.Length - 1;

var otherStart = other.FirstIndex;
var otherEnd = otherStart + (int)other.Length - 1;

// No overlap.
if (otherEnd < start || otherStart > end)
{
yield break;
}

// Left remainder.
if (otherStart > start)
{
yield return new ItemIndexRange(start, (uint)(otherStart - start));
}

// Right remainder.
if (otherEnd < end)
{
yield return new ItemIndexRange(otherEnd + 1, (uint)(end - otherEnd));
}
}
}
189 changes: 189 additions & 0 deletions src/Extensions/TableViewCellSlotRangeExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
using Microsoft.UI.Xaml.Data;

namespace WinUI.TableView.Extensions;

/// <summary>
/// Provides extension methods for the TableViewCellSlotRange type.
/// </summary>
internal static class TableViewCellSlotRangeExtensions
{
/// <summary>
/// Determines whether a specified cell slot is within the range.
/// </summary>
/// <param name="range">The TableViewCellSlotRange to check.</param>
/// <param name="slot">The cell slot to check.</param>
/// <returns>True if the slot is within the range; otherwise, false.</returns>
public static bool IsInRange(this TableViewCellSlotRange? range, TableViewCellSlot slot)
{
if (range is null || range.Length <= 0) return false;

var minRow = Math.Min(range.FirstRow, range.LastRow);
var maxRow = Math.Max(range.FirstRow, range.LastRow);
var minColumn = Math.Min(range.FirstColumn, range.LastColumn);
var maxColumn = Math.Max(range.FirstColumn, range.LastColumn);

return slot.Row >= minRow && slot.Row <= maxRow
&& slot.Column >= minColumn && slot.Column <= maxColumn;
}

/// <summary>
/// Determines whether a specified row index is within the range.
/// </summary>
/// <param name="range">The TableViewCellSlotRange to check.</param>
/// <param name="row">The row index to check.</param>
/// <returns>True if the row index is within the range; otherwise, false.</returns>
public static bool IsRowInRange(this TableViewCellSlotRange? range, int row)
{
return range?.Length > 0
&& row >= Math.Min(range.FirstRow, range.LastRow)
&& row <= Math.Max(range.FirstRow, range.LastRow);
}

/// <summary>
/// Determines whether a specified column index is within the range.
/// </summary>
/// <param name="range">The TableViewCellSlotRange to check.</param>
/// <param name="column">The column index to check.</param>
/// <returns>True if the column index is within the range; otherwise, false.</returns>
public static bool IsColumnInRange(this TableViewCellSlotRange? range, int column)
{
return range?.Length > 0
&& column >= Math.Min(range.FirstColumn, range.LastColumn)
&& column <= Math.Max(range.FirstColumn, range.LastColumn);
}

/// <summary>
/// Determines whether the given cell slot range is valid within the TableView.
/// </summary>
/// <param name="range">The TableViewCellSlotRange to check.</param>
/// <param name="tableView">The TableView to check against.</param>
/// <returns>True if the cell slot range of TableView is valid; otherwise, false.</returns>
public static bool IsValid(this TableViewCellSlotRange range, TableView tableView)
{
return range.FirstSlot.IsValid(tableView) && range.LastSlot.IsValid(tableView);
}

/// <summary>
/// Returns all cell slots contained within this range, enumerated row by row.
/// </summary>
public static IEnumerable<TableViewCellSlot> GetSlots(this TableViewCellSlotRange range)
{
for (var row = range.FirstRow; row <= range.LastRow; row++)
{
for (var col = range.FirstColumn; col <= range.LastColumn; col++)
{
yield return new TableViewCellSlot(row, col);
}
}
}

/// <summary>
/// Determines whether a specific cell slot falls within this range.
/// </summary>
public static bool Contains(this TableViewCellSlotRange range, int rowIndex, int columnIndex)
{
return rowIndex >= range.FirstRow && rowIndex <= range.LastRow &&
columnIndex >= range.FirstColumn && columnIndex <= range.LastColumn;
}

/// <summary>
/// Determines whether another TableViewCellSlotRange is completely contained within this range.
/// </summary>
public static bool Contains(this TableViewCellSlotRange? range, TableViewCellSlotRange? other)
{
if (range == null || other == null) return false;

return range.Contains(other.FirstRow, other.FirstColumn) &&
range.Contains(other.LastRow, other.LastColumn);
}

/// <summary>
/// Determines whether another range intersects with this range.
/// </summary>
public static bool IntersectsWith(this TableViewCellSlotRange range, TableViewCellSlotRange other)
{
if (other == null) return false;

return range.FirstRow <= other.LastRow && range.LastRow >= other.FirstRow &&
range.FirstColumn <= other.LastColumn && range.LastColumn >= other.FirstColumn;
}

/// <summary>
/// Subtracts another range from this range and returns the resulting ranges.
/// </summary>
/// <param name="range">The range to subtract from.</param>
/// <param name="other">The range to subtract.</param>
/// <returns>An enumerable of resulting ranges after subtraction.</returns>
public static IEnumerable<TableViewCellSlotRange> Subtract(this TableViewCellSlotRange range, TableViewCellSlotRange other)
{
// No overlap.
if (!range.IntersectsWith(other))
{
yield return range;
yield break;
}

// Intersection rectangle.
var top = Math.Max(range.FirstRow, other.FirstRow);
var left = Math.Max(range.FirstColumn, other.FirstColumn);
var bottom = Math.Min(range.LastRow, other.LastRow);
var right = Math.Min(range.LastColumn, other.LastColumn);

// Top strip.
if (range.FirstRow < top)
{
yield return new TableViewCellSlotRange(
range.FirstRow,
range.FirstColumn,
top - range.FirstRow,
range.Columns);
}

// Bottom strip.
if (bottom < range.LastRow)
{
yield return new TableViewCellSlotRange(
bottom + 1,
range.FirstColumn,
range.LastRow - bottom,
range.Columns);
}

// Left strip.
if (range.FirstColumn < left)
{
yield return new TableViewCellSlotRange(
top,
range.FirstColumn,
bottom - top + 1,
left - range.FirstColumn);
}

// Right strip.
if (right < range.LastColumn)
{
yield return new TableViewCellSlotRange(
top,
right + 1,
bottom - top + 1,
range.LastColumn - right);
}
}

/// <summary>
/// Merges two TableViewCellSlotRanges into a single range that encompasses both.
/// </summary>
public static TableViewCellSlotRange Merge(this TableViewCellSlotRange range, TableViewCellSlotRange other)
{
var firstRow = Math.Min(range.FirstRow, other.FirstRow);
var firstColumn = Math.Min(range.FirstColumn, other.FirstColumn);
var lastRow = Math.Max(range.LastRow, other.LastRow);
var lastColumn = Math.Max(range.LastColumn, other.LastColumn);

return TableViewCellSlotRange.FromCoordinates(
firstRow,
firstColumn,
lastRow,
lastColumn);
}
}
44 changes: 44 additions & 0 deletions src/Helpers/IndexRangeHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using Microsoft.UI.Xaml.Data;

namespace WinUI.TableView.Helpers;

/// <summary>
/// Provides helper methods for working with index ranges in a TableView.
/// </summary>
internal static class IndexRangeHelper
{
/// <summary>
/// Gets a list of contiguous index ranges from a collection of indexes.
/// </summary>
/// <param name="indexes">The collection of indexes to process.</param>
/// <returns>A list of contiguous index ranges.</returns>
public static List<ItemIndexRange> GetRanges(IEnumerable<int> indexes)
{
var sorted = indexes.Order().ToArray();

if (sorted.Length == 0)
return [];

List<ItemIndexRange> ranges = [];

var first = sorted[0];
var previous = sorted[0];

for (var i = 1; i < sorted.Length; i++)
{
if (sorted[i] == previous + 1)
{
previous = sorted[i];
continue;
}

ranges.Add(new ItemIndexRange(first, (uint)(previous - first + 1)));

first = previous = sorted[i];
}

ranges.Add(new ItemIndexRange(first, (uint)(previous - first + 1)));

return ranges;
}
}
12 changes: 12 additions & 0 deletions src/Helpers/TableViewTrace.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Diagnostics;

namespace WinUI.TableView.Helpers;

internal static class TableViewTrace
{
[Conditional("DEBUG")]
public static void Write(string message)
{
Debug.WriteLine($"[TableView] {message}");
}
}
5 changes: 3 additions & 2 deletions src/TableView.Events.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using Microsoft.UI.Xaml;
using System;
using System.ComponentModel;
using System.Diagnostics;
using WinUI.TableView.Helpers;

namespace WinUI.TableView;

Expand Down Expand Up @@ -186,6 +186,7 @@ protected internal virtual void OnClearSorting(TableViewClearSortingEventArgs ar
/// </summary>
protected virtual void OnCellSelectionChanged(TableViewCellSelectionChangedEventArgs args)
{
TableViewTrace.Write($"TableViewCellSelectionChanged: Added={args.AddedCells.Count}, Removed={args.RemovedCells.Count}");
CellSelectionChanged?.Invoke(this, args);
}

Expand Down
4 changes: 2 additions & 2 deletions src/TableView.Properties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ public bool ForceRowOrCellSelectionOnContextRequested
/// <summary>
/// Gets the selected cell ranges.
/// </summary>
internal HashSet<HashSet<TableViewCellSlot>> SelectedCellRanges { get; } = [];
internal HashSet<TableViewCellSlotRange> SelectedCellRanges { get; } = [];

/// <summary>
/// Gets or sets a value indicating whether the TableView is in editing mode.
Expand Down Expand Up @@ -897,7 +897,7 @@ private static void OnSelectionModeChanged(DependencyObject d, DependencyPropert

if (tableView.SelectionMode is ListViewSelectionMode.Single && tableView.CurrentCellSlot.HasValue)
{
tableView.SelectedCellRanges.Add([tableView.CurrentCellSlot.Value]);
tableView.SelectedCellRanges.Add(TableViewCellSlotRange.FromSlots(tableView.CurrentCellSlot.Value));
}

tableView.OnCellSelectionChanged();
Expand Down
Loading
Loading