Skip to content

Commit

Permalink
Merge pull request #2143 from erri120/tree-columns
Browse files Browse the repository at this point in the history
TreeDataGrid column definitions
  • Loading branch information
erri120 authored Oct 8, 2024
2 parents 91f2e97 + c184346 commit 6f6f23d
Show file tree
Hide file tree
Showing 13 changed files with 341 additions and 6 deletions.
14 changes: 11 additions & 3 deletions src/NexusMods.App.UI/Controls/ReactiveR3Object.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,26 @@

namespace NexusMods.App.UI.Controls;

[PublicAPI]
public interface IReactiveR3Object : ReactiveUI.IReactiveObject, IDisposable
{
Observable<bool> Activation { get; }
void Activate();
void Deactivate();
}

/// <summary>
/// Base class using R3 with support for activation/deactivation,
/// <see cref="INotifyPropertyChanged"/>, and <see cref="INotifyPropertyChanging"/>.
/// </summary>
[PublicAPI]
public class ReactiveR3Object : ReactiveUI.IReactiveObject, IDisposable
public class ReactiveR3Object : IReactiveR3Object
{
private readonly BehaviorSubject<bool> _activation = new(initialValue: false);
public Observable<bool> Activation => _activation;

internal void Activate() => _activation.OnNext(true);
internal void Deactivate()
public void Activate() => _activation.OnNext(true);
public void Deactivate()
{
// NOTE(erri120): no need to deactivate disposed objects, as
// any subscriptions and WhenActivated-blocks are already disposed
Expand Down
70 changes: 70 additions & 0 deletions src/NexusMods.App.UI/Controls/TreeDataGrid/IColumnDefinition.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using Avalonia.Controls.Models.TreeDataGrid;
using JetBrains.Annotations;

namespace NexusMods.App.UI.Controls;

/// <summary>
/// Static interface for column definitions
/// </summary>
[PublicAPI]
public interface IColumnDefinition<TModel, TSelf>
where TModel : class
where TSelf : IColumnDefinition<TModel, TSelf>, IComparable<TSelf>
{
static virtual int Compare(TModel? a, TModel? b)
{
return (a, b) switch
{
(TSelf itemA, TSelf itemB) => itemA.CompareTo(itemB),

// b precedes a
(TSelf, _) => 1,

// a precedes b
(_, TSelf) => -1,

// a and b are in the same position
(_, _) => 0,
};
}

static abstract string GetColumnHeader();
static abstract string GetColumnTemplateResourceKey();
static virtual string GetColumnId() => TSelf.GetColumnTemplateResourceKey();

static virtual IColumn<TModel> CreateColumn()
{
return new CustomTemplateColumn<TModel>(
header: TSelf.GetColumnHeader(),
cellTemplateResourceKey: TSelf.GetColumnTemplateResourceKey(),
options: new TemplateColumnOptions<TModel>
{
CanUserSortColumn = true,
CanUserResizeColumn = true,
CompareAscending = static (a, b) => TSelf.Compare(a, b),
CompareDescending = static (a, b) => TSelf.Compare(b, a),
}
)
{
Id = TSelf.GetColumnId(),
};
}
}

/// <summary>
/// Column helper.
/// </summary>
[PublicAPI]
public static class ColumnCreator
{
/// <summary>
/// Creates a column from a static interface definition.
/// </summary>
public static IColumn<TModel> CreateColumn<TModel, TColumnInterface>()
where TModel : class
where TColumnInterface : IColumnDefinition<TModel, TColumnInterface>, IComparable<TColumnInterface>
{
return TColumnInterface.CreateColumn();
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@

namespace NexusMods.App.UI.Controls;

[PublicAPI]
public interface ITreeDataGridItemModel : IReactiveR3Object;

/// <summary>
/// Base class for models of <see cref="Avalonia.Controls.TreeDataGrid"/> items.
/// </summary>
public class TreeDataGridItemModel : ReactiveR3Object;
public class TreeDataGridItemModel : ReactiveR3Object, ITreeDataGridItemModel;

/// <summary>
/// Generic variant of <see cref="TreeDataGridItemModel"/>.
Expand Down Expand Up @@ -145,7 +148,7 @@ protected override void Dispose(bool disposing)
}

[MustDisposeResource] protected static IDisposable WhenModelActivated<TItemModel>(TItemModel model, Action<TItemModel, CompositeDisposable> block)
where TItemModel : TreeDataGridItemModel
where TItemModel : ITreeDataGridItemModel
{
return model.WhenActivated(block);
}
Expand Down
2 changes: 1 addition & 1 deletion src/NexusMods.App.UI/Extensions/R3Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public static class R3Extensions
[MustDisposeResource] public static IDisposable WhenActivated<T>(
this T obj,
Action<T, CompositeDisposable> block)
where T : ReactiveR3Object
where T : IReactiveR3Object
{
var d = Disposable.CreateBuilder();

Expand Down
18 changes: 18 additions & 0 deletions src/NexusMods.App.UI/NexusMods.App.UI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,24 @@
<Compile Update="Pages\LibraryPage\Collections\CollectionCardViewModel.cs">
<DependentUpon>ICollectionCardViewModel.cs</DependentUpon>
</Compile>
<Compile Update="Pages\LibraryPage\ILibraryItemWithName.cs">
<DependentUpon>ILibraryItemModel.cs</DependentUpon>
</Compile>
<Compile Update="Pages\LibraryPage\ILibraryItemWithVersion.cs">
<DependentUpon>ILibraryItemModel.cs</DependentUpon>
</Compile>
<Compile Update="Pages\LibraryPage\ILibraryItemWithSize.cs">
<DependentUpon>ILibraryItemModel.cs</DependentUpon>
</Compile>
<Compile Update="Pages\LibraryPage\ILibraryItemWithDownloadedDate.cs">
<DependentUpon>ILibraryItemModel.cs</DependentUpon>
</Compile>
<Compile Update="Pages\LibraryPage\ILibraryItemWithInstalledDate.cs">
<DependentUpon>ILibraryItemModel.cs</DependentUpon>
</Compile>
<Compile Update="Pages\LibraryPage\ILibraryItemWithAction.cs">
<DependentUpon>ILibraryItemModel.cs</DependentUpon>
</Compile>
</ItemGroup>

<ItemGroup>
Expand Down
5 changes: 5 additions & 0 deletions src/NexusMods.App.UI/Pages/LibraryPage/ILibraryItemModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using NexusMods.App.UI.Controls;

namespace NexusMods.App.UI.Pages.LibraryPage;

public interface ILibraryItemModel : ITreeDataGridItemModel;
35 changes: 35 additions & 0 deletions src/NexusMods.App.UI/Pages/LibraryPage/ILibraryItemWithAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using NexusMods.App.UI.Controls;
using R3;

namespace NexusMods.App.UI.Pages.LibraryPage;

public interface ILibraryItemWithAction : ILibraryItemModel, IComparable<ILibraryItemWithAction>, IColumnDefinition<ILibraryItemModel, ILibraryItemWithAction>
{
int IComparable<ILibraryItemWithAction>.CompareTo(ILibraryItemWithAction? other)
{
return (this, other) switch
{
(ILibraryItemWithInstallAction, ILibraryItemWithDownloadAction) => -1,
(ILibraryItemWithDownloadAction, ILibraryItemWithInstallAction) => 1,
_ => 0,
};
}

public const string ColumnTemplateResourceKey = "LibraryItemActionColumn";
static string IColumnDefinition<ILibraryItemModel, ILibraryItemWithAction>.GetColumnHeader() => "Action";
static string IColumnDefinition<ILibraryItemModel, ILibraryItemWithAction>.GetColumnTemplateResourceKey() => ColumnTemplateResourceKey;
}

public interface ILibraryItemWithInstallAction : ILibraryItemWithAction
{
ReactiveCommand<Unit, Unit> InstallItemCommand { get; }

BindableReactiveProperty<bool> IsInstalled { get; }

BindableReactiveProperty<string> InstallButtonText { get; }
}

public interface ILibraryItemWithDownloadAction : ILibraryItemWithAction
{
ReactiveCommand<Unit, Unit> DownloadItemCommand { get; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using NexusMods.App.UI.Controls;
using R3;

namespace NexusMods.App.UI.Pages.LibraryPage;

public interface ILibraryItemWithDownloadedDate : ILibraryItemModel, IComparable<ILibraryItemWithDownloadedDate>, IColumnDefinition<ILibraryItemModel, ILibraryItemWithDownloadedDate>
{
ReactiveProperty<DateTime> DownloadedDate { get; }
BindableReactiveProperty<string> FormattedDownloadedDate { get; }

int IComparable<ILibraryItemWithDownloadedDate>.CompareTo(ILibraryItemWithDownloadedDate? other) => other is null ? 1 : DateTime.Compare(DownloadedDate.Value, other.DownloadedDate.Value);

public const string ColumnTemplateResourceKey = "LibraryItemDownloadedDateColumn";
static string IColumnDefinition<ILibraryItemModel, ILibraryItemWithDownloadedDate>.GetColumnHeader() => "Downloaded";
static string IColumnDefinition<ILibraryItemModel, ILibraryItemWithDownloadedDate>.GetColumnTemplateResourceKey() => ColumnTemplateResourceKey;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using NexusMods.App.UI.Controls;
using R3;

namespace NexusMods.App.UI.Pages.LibraryPage;

public interface ILibraryItemWithInstalledDate : ILibraryItemModel, IComparable<ILibraryItemWithInstalledDate>, IColumnDefinition<ILibraryItemModel, ILibraryItemWithInstalledDate>
{
ReactiveProperty<DateTime> InstalledDate { get; }
BindableReactiveProperty<string> FormattedInstalledDate { get; }

int IComparable<ILibraryItemWithInstalledDate>.CompareTo(ILibraryItemWithInstalledDate? other) => other is null ? 1 : DateTime.Compare(InstalledDate.Value, other.InstalledDate.Value);

public const string ColumnTemplateResourceKey = "LibraryItemInstalledDateColumn";
static string IColumnDefinition<ILibraryItemModel, ILibraryItemWithInstalledDate>.GetColumnHeader() => "Installed";
static string IColumnDefinition<ILibraryItemModel, ILibraryItemWithInstalledDate>.GetColumnTemplateResourceKey() => ColumnTemplateResourceKey;
}
15 changes: 15 additions & 0 deletions src/NexusMods.App.UI/Pages/LibraryPage/ILibraryItemWithName.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using NexusMods.App.UI.Controls;
using R3;

namespace NexusMods.App.UI.Pages.LibraryPage;

public interface ILibraryItemWithName : ILibraryItemModel, IComparable<ILibraryItemWithName>, IColumnDefinition<ILibraryItemModel, ILibraryItemWithName>
{
BindableReactiveProperty<string> Name { get; }

int IComparable<ILibraryItemWithName>.CompareTo(ILibraryItemWithName? other) => string.CompareOrdinal(Name.Value, other?.Name.Value);

public const string ColumnTemplateResourceKey = "LibraryItemNameColumn";
static string IColumnDefinition<ILibraryItemModel, ILibraryItemWithName>.GetColumnHeader() => "Name";
static string IColumnDefinition<ILibraryItemModel, ILibraryItemWithName>.GetColumnTemplateResourceKey() => ColumnTemplateResourceKey;
}
17 changes: 17 additions & 0 deletions src/NexusMods.App.UI/Pages/LibraryPage/ILibraryItemWithSize.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using NexusMods.App.UI.Controls;
using NexusMods.Paths;
using R3;

namespace NexusMods.App.UI.Pages.LibraryPage;

public interface ILibraryItemWithSize : ILibraryItemModel, IComparable<ILibraryItemWithSize>, IColumnDefinition<ILibraryItemModel, ILibraryItemWithSize>
{
ReactiveProperty<Size> ItemSize { get; }
BindableReactiveProperty<string> FormattedSize { get; }

int IComparable<ILibraryItemWithSize>.CompareTo(ILibraryItemWithSize? other) => other is null ? 1 : ItemSize.Value.CompareTo(other.ItemSize.Value);

public const string ColumnTemplateResourceKey = "LibraryItemSizeColumn";
static string IColumnDefinition<ILibraryItemModel, ILibraryItemWithSize>.GetColumnHeader() => "Size";
static string IColumnDefinition<ILibraryItemModel, ILibraryItemWithSize>.GetColumnTemplateResourceKey() => ColumnTemplateResourceKey;
}
15 changes: 15 additions & 0 deletions src/NexusMods.App.UI/Pages/LibraryPage/ILibraryItemWithVersion.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using NexusMods.App.UI.Controls;
using R3;

namespace NexusMods.App.UI.Pages.LibraryPage;

public interface ILibraryItemWithVersion : ILibraryItemModel, IComparable<ILibraryItemWithVersion>, IColumnDefinition<ILibraryItemModel, ILibraryItemWithVersion>
{
BindableReactiveProperty<string> Version { get; }

int IComparable<ILibraryItemWithVersion>.CompareTo(ILibraryItemWithVersion? other) => string.CompareOrdinal(Version.Value, other?.Version.Value);

public const string ColumnTemplateResourceKey = "LibraryItemVersionColumn";
static string IColumnDefinition<ILibraryItemModel, ILibraryItemWithVersion>.GetColumnHeader() => "Version";
static string IColumnDefinition<ILibraryItemModel, ILibraryItemWithVersion>.GetColumnTemplateResourceKey() => ColumnTemplateResourceKey;
}
Loading

0 comments on commit 6f6f23d

Please sign in to comment.