Skip to content

Commit

Permalink
Merge branch 'master' into ignore-control-chars
Browse files Browse the repository at this point in the history
  • Loading branch information
maxkatz6 authored Aug 13, 2024
2 parents 83ea1a8 + fb96820 commit 207531a
Show file tree
Hide file tree
Showing 7 changed files with 212 additions and 63 deletions.
5 changes: 5 additions & 0 deletions samples/TreeDataGridDemo/MainWindow.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@
DockPanel.Dock="Right">
Cell Selection
</CheckBox>
<CheckBox IsChecked="{Binding Files.FlatList}"
Margin="4 0 0 0"
DockPanel.Dock="Right">
Flat
</CheckBox>
<TextBox Text="{Binding Files.SelectedPath, Mode=OneWay}"
Margin="4 0 0 0"
VerticalContentAlignment="Center"
Expand Down
192 changes: 137 additions & 55 deletions samples/TreeDataGridDemo/ViewModels/FilesPageViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using System.Linq;
using System.Reactive.Linq;
using System.Runtime.InteropServices;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Models.TreeDataGrid;
using Avalonia.Controls.Selection;
Expand All @@ -20,6 +19,9 @@ namespace TreeDataGridDemo.ViewModels
public class FilesPageViewModel : ReactiveObject
{
private static IconConverter? s_iconConverter;
private readonly HierarchicalTreeDataGridSource<FileTreeNodeModel>? _treeSource;
private FlatTreeDataGridSource<FileTreeNodeModel>? _flatSource;
private ITreeDataGridSource<FileTreeNodeModel> _source;
private bool _cellSelection;
private FileTreeNodeModel? _root;
private string _selectedDrive;
Expand All @@ -38,61 +40,17 @@ public FilesPageViewModel()
_selectedDrive = Drives.FirstOrDefault() ?? "/";
}

Source = new HierarchicalTreeDataGridSource<FileTreeNodeModel>(Array.Empty<FileTreeNodeModel>())
{
Columns =
{
new CheckBoxColumn<FileTreeNodeModel>(
null,
x => x.IsChecked,
(o, v) => o.IsChecked = v,
options: new()
{
CanUserResizeColumn = false,
}),
new HierarchicalExpanderColumn<FileTreeNodeModel>(
new TemplateColumn<FileTreeNodeModel>(
"Name",
"FileNameCell",
"FileNameEditCell",
new GridLength(1, GridUnitType.Star),
new()
{
CompareAscending = FileTreeNodeModel.SortAscending(x => x.Name),
CompareDescending = FileTreeNodeModel.SortDescending(x => x.Name),
IsTextSearchEnabled = true,
TextSearchValueSelector = x => x.Name
}),
x => x.Children,
x => x.HasChildren,
x => x.IsExpanded),
new TextColumn<FileTreeNodeModel, long?>(
"Size",
x => x.Size,
options: new()
{
CompareAscending = FileTreeNodeModel.SortAscending(x => x.Size),
CompareDescending = FileTreeNodeModel.SortDescending(x => x.Size),
}),
new TextColumn<FileTreeNodeModel, DateTimeOffset?>(
"Modified",
x => x.Modified,
options: new()
{
CompareAscending = FileTreeNodeModel.SortAscending(x => x.Modified),
CompareDescending = FileTreeNodeModel.SortDescending(x => x.Modified),
}),
}
};

Source.RowSelection!.SingleSelect = false;
Source.RowSelection.SelectionChanged += SelectionChanged;
_source = _treeSource = CreateTreeSource();

this.WhenAnyValue(x => x.SelectedDrive)
.Subscribe(x =>
{
_root = new FileTreeNodeModel(_selectedDrive, isDirectory: true, isRoot: true);
Source.Items = new[] { _root };

if (_treeSource is not null)
_treeSource.Items = new[] { _root };
else if (_flatSource is not null)
_flatSource.Items = _root.Children;
});
}

Expand All @@ -115,6 +73,16 @@ public bool CellSelection

public IList<string> Drives { get; }

public bool FlatList
{
get => Source != _treeSource;
set
{
if (value != FlatList)
Source = value ? _flatSource ??= CreateFlatSource() : _treeSource!;
}
}

public string SelectedDrive
{
get => _selectedDrive;
Expand All @@ -127,7 +95,11 @@ public string? SelectedPath
set => SetSelectedPath(value);
}

public HierarchicalTreeDataGridSource<FileTreeNodeModel> Source { get; }
public ITreeDataGridSource<FileTreeNodeModel> Source
{
get => _source;
private set => this.RaiseAndSetIfChanged(ref _source, value);
}

public static IMultiValueConverter FileIconConverter
{
Expand All @@ -151,11 +123,115 @@ public static IMultiValueConverter FileIconConverter
}
}

private FlatTreeDataGridSource<FileTreeNodeModel> CreateFlatSource()
{
var result = new FlatTreeDataGridSource<FileTreeNodeModel>(_root!.Children)
{
Columns =
{
new CheckBoxColumn<FileTreeNodeModel>(
null,
x => x.IsChecked,
(o, v) => o.IsChecked = v,
options: new()
{
CanUserResizeColumn = false,
}),
new TemplateColumn<FileTreeNodeModel>(
"Name",
"FileNameCell",
"FileNameEditCell",
new GridLength(1, GridUnitType.Star),
new()
{
CompareAscending = FileTreeNodeModel.SortAscending(x => x.Name),
CompareDescending = FileTreeNodeModel.SortDescending(x => x.Name),
IsTextSearchEnabled = true,
TextSearchValueSelector = x => x.Name
}),
new TextColumn<FileTreeNodeModel, long?>(
"Size",
x => x.Size,
options: new()
{
CompareAscending = FileTreeNodeModel.SortAscending(x => x.Size),
CompareDescending = FileTreeNodeModel.SortDescending(x => x.Size),
}),
new TextColumn<FileTreeNodeModel, DateTimeOffset?>(
"Modified",
x => x.Modified,
options: new()
{
CompareAscending = FileTreeNodeModel.SortAscending(x => x.Modified),
CompareDescending = FileTreeNodeModel.SortDescending(x => x.Modified),
}),
}
};

result.RowSelection!.SingleSelect = false;
result.RowSelection.SelectionChanged += SelectionChanged;
return result;
}

private HierarchicalTreeDataGridSource<FileTreeNodeModel> CreateTreeSource()
{
var result = new HierarchicalTreeDataGridSource<FileTreeNodeModel>(Array.Empty<FileTreeNodeModel>())
{
Columns =
{
new CheckBoxColumn<FileTreeNodeModel>(
null,
x => x.IsChecked,
(o, v) => o.IsChecked = v,
options: new()
{
CanUserResizeColumn = false,
}),
new HierarchicalExpanderColumn<FileTreeNodeModel>(
new TemplateColumn<FileTreeNodeModel>(
"Name",
"FileNameCell",
"FileNameEditCell",
new GridLength(1, GridUnitType.Star),
new()
{
CompareAscending = FileTreeNodeModel.SortAscending(x => x.Name),
CompareDescending = FileTreeNodeModel.SortDescending(x => x.Name),
IsTextSearchEnabled = true,
TextSearchValueSelector = x => x.Name
}),
x => x.Children,
x => x.HasChildren,
x => x.IsExpanded),
new TextColumn<FileTreeNodeModel, long?>(
"Size",
x => x.Size,
options: new()
{
CompareAscending = FileTreeNodeModel.SortAscending(x => x.Size),
CompareDescending = FileTreeNodeModel.SortDescending(x => x.Size),
}),
new TextColumn<FileTreeNodeModel, DateTimeOffset?>(
"Modified",
x => x.Modified,
options: new()
{
CompareAscending = FileTreeNodeModel.SortAscending(x => x.Modified),
CompareDescending = FileTreeNodeModel.SortDescending(x => x.Modified),
}),
}
};

result.RowSelection!.SingleSelect = false;
result.RowSelection.SelectionChanged += SelectionChanged;
return result;
}

private void SetSelectedPath(string? value)
{
if (string.IsNullOrEmpty(value))
{
Source.RowSelection!.Clear();
GetRowSelection(Source).Clear();
return;
}

Expand Down Expand Up @@ -204,12 +280,18 @@ private void SetSelectedPath(string? value)
}
}

Source.RowSelection!.SelectedIndex = index;
GetRowSelection(Source).SelectedIndex = index;
}

private ITreeDataGridRowSelectionModel<FileTreeNodeModel> GetRowSelection(ITreeDataGridSource source)
{
return source.Selection as ITreeDataGridRowSelectionModel<FileTreeNodeModel> ??
throw new InvalidOperationException("Expected a row selection model.");
}

private void SelectionChanged(object? sender, TreeSelectionModelSelectionChangedEventArgs<FileTreeNodeModel> e)
{
var selectedPath = Source.RowSelection?.SelectedItem?.Path;
var selectedPath = GetRowSelection(Source).SelectedItem?.Path;
this.RaiseAndSetIfChanged(ref _selectedPath, selectedPath, nameof(SelectedPath));

foreach (var i in e.DeselectedItems)
Expand Down
8 changes: 4 additions & 4 deletions src/Avalonia.Controls.TreeDataGrid/ITreeDataGridSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ public interface ITreeDataGridSource : INotifyPropertyChanged
IRows Rows { get; }

/// <summary>
/// Gets the selection model.
/// Gets or sets the selection model.
/// </summary>
ITreeDataGridSelection? Selection { get; }
ITreeDataGridSelection? Selection { get; set; }

/// <summary>
/// Gets a value indicating whether the data source is hierarchical.
Expand Down Expand Up @@ -84,8 +84,8 @@ void DragDropRows(
public interface ITreeDataGridSource<TModel> : ITreeDataGridSource
{
/// <summary>
/// Gets the items in the data source.
/// Gets or sets the items in the data source.
/// </summary>
new IEnumerable<TModel> Items { get; }
new IEnumerable<TModel> Items { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace Avalonia.Controls.Models.TreeDataGrid
/// </summary>
public class ColumnList<TModel> : NotifyingListBase<IColumn<TModel>>, IColumns
{
private bool _initialized;
private double _viewportWidth;

public event EventHandler? LayoutInvalidated;
Expand All @@ -22,6 +23,7 @@ public void AddRange(IEnumerable<IColumn<TModel>> items)
public Size CellMeasured(int columnIndex, int rowIndex, Size size)
{
var column = (IUpdateColumnLayout)this[columnIndex];
_initialized = true;
return new Size(column.CellMeasured(size.Width, rowIndex), size.Height);
}

Expand Down Expand Up @@ -103,7 +105,8 @@ public void ViewportChanged(Rect viewport)
if (_viewportWidth != viewport.Width)
{
_viewportWidth = viewport.Width;
UpdateColumnSizes();
if (_initialized)
UpdateColumnSizes();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@ protected virtual void OnEffectiveViewportChanged(object? sender, EffectiveViewp
// viewport.
Viewport = e.EffectiveViewport.Size == default ?
s_invalidViewport :
e.EffectiveViewport.Intersect(new(Bounds.Size));
Intersect(e.EffectiveViewport, new(Bounds.Size));

_isWaitingForViewportUpdate = false;

Expand Down Expand Up @@ -730,6 +730,24 @@ private void OnUnrealizedFocusedElementLostFocus(object? sender, RoutedEventArgs

private static bool HasInfinity(Size s) => double.IsInfinity(s.Width) || double.IsInfinity(s.Height);

private static Rect Intersect(Rect a, Rect b)
{
// Hack fix for https://github.com/AvaloniaUI/Avalonia/issues/15075
var newLeft = (a.X > b.X) ? a.X : b.X;
var newTop = (a.Y > b.Y) ? a.Y : b.Y;
var newRight = (a.Right < b.Right) ? a.Right : b.Right;
var newBottom = (a.Bottom < b.Bottom) ? a.Bottom : b.Bottom;

if ((newRight >= newLeft) && (newBottom >= newTop))
{
return new Rect(newLeft, newTop, newRight - newLeft, newBottom - newTop);
}
else
{
return default;
}
}

private struct MeasureViewport
{
public int anchorIndex;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ public T? this[int index]
{
for (var i = range.Begin; i <= range.End; ++i)
{
yield return node.ItemsView![i];
if (node.ItemsView is not null)
yield return node.ItemsView[i];
}
}

Expand Down Expand Up @@ -110,4 +111,4 @@ public TreeSelectedItems(TreeSelectionModelBase<T> root) : base(root) { }
yield return i;
}
}
}
}
Loading

0 comments on commit 207531a

Please sign in to comment.