using System;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
using Avalonia.Controls;
using FluentAvalonia.UI.Controls;
using Ninject;
using Ninject.Parameters;
using ReactiveUI;
namespace Artemis.UI.Shared.Services.Builders;
///
/// Represents a builder that can be used to create Fluent UI dialogs.
///
public class ContentDialogBuilder
{
private readonly ContentDialog _contentDialog;
private readonly IKernel _kernel;
private readonly Window _parent;
private ContentDialogViewModelBase? _viewModel;
internal ContentDialogBuilder(IKernel kernel, Window parent)
{
_kernel = kernel;
_parent = parent;
_contentDialog = new ContentDialog
{
CloseButtonText = "Close"
};
}
///
/// Changes the title of the dialog.
///
/// The new title.
/// The builder that can be used to further build the dialog.
public ContentDialogBuilder WithTitle(string? title)
{
_contentDialog.Title = title;
return this;
}
///
/// Changes the content of the dialog.
///
/// The new content.
/// The builder that can be used to further build the dialog.
public ContentDialogBuilder WithContent(string? content)
{
_contentDialog.Content = content;
return this;
}
///
/// Changes the default button of the dialog that is pressed on enter.
///
/// The default button.
/// The builder that can be used to further build the dialog.
public ContentDialogBuilder WithDefaultButton(ContentDialogButton defaultButton)
{
_contentDialog.DefaultButton = (FluentAvalonia.UI.Controls.ContentDialogButton) defaultButton;
return this;
}
///
/// Changes the primary button of the dialog.
///
/// An action to configure the button.
/// The builder that can be used to further build the dialog.
public ContentDialogBuilder HavingPrimaryButton(Action configure)
{
ContentDialogButtonBuilder builder = new();
configure(builder);
_contentDialog.IsPrimaryButtonEnabled = true;
_contentDialog.PrimaryButtonText = builder.Text;
_contentDialog.PrimaryButtonCommand = builder.Command;
_contentDialog.PrimaryButtonCommandParameter = builder.CommandParameter;
// I feel like this isn't my responsibility...
if (builder.Command != null)
{
_contentDialog.IsPrimaryButtonEnabled = builder.Command.CanExecute(builder.CommandParameter);
builder.Command.CanExecuteChanged += (_, _) => _contentDialog.IsPrimaryButtonEnabled = builder.Command.CanExecute(builder.CommandParameter);
}
return this;
}
///
/// Changes the secondary button of the dialog.
///
/// An action to configure the button.
/// The builder that can be used to further build the dialog.
public ContentDialogBuilder HavingSecondaryButton(Action configure)
{
ContentDialogButtonBuilder builder = new();
configure(builder);
_contentDialog.IsSecondaryButtonEnabled = true;
_contentDialog.SecondaryButtonText = builder.Text;
_contentDialog.SecondaryButtonCommand = builder.Command;
_contentDialog.SecondaryButtonCommandParameter = builder.CommandParameter;
// I feel like this isn't my responsibility...
if (builder.Command != null)
{
_contentDialog.IsSecondaryButtonEnabled = builder.Command.CanExecute(builder.CommandParameter);
builder.Command.CanExecuteChanged += (_, _) => _contentDialog.IsSecondaryButtonEnabled = builder.Command.CanExecute(builder.CommandParameter);
}
return this;
}
///
/// Changes the text of the close button of the dialog.
///
/// The new text.
/// The builder that can be used to further build the dialog.
public ContentDialogBuilder WithCloseButtonText(string? text)
{
_contentDialog.CloseButtonText = text;
return this;
}
///
/// Changes the view model of the content dialog, hosting it inside the dialog.
///
/// The type of the view model to host.
/// The resulting view model.
/// Optional parameters to pass to the constructor of the view model, case and order sensitive.
/// The builder that can be used to further build the dialog.
public ContentDialogBuilder WithViewModel(out T viewModel, params (string name, object? value)[] parameters) where T : ContentDialogViewModelBase
{
IParameter[] paramsArray = parameters.Select(kv => new ConstructorArgument(kv.name, kv.value)).Cast().ToArray();
viewModel = _kernel.Get(paramsArray);
viewModel.ContentDialog = _contentDialog;
_contentDialog.Content = viewModel;
_viewModel = viewModel;
return this;
}
///
/// Changes the dialog to take the full height of the window it's being hosted in.
///
/// The builder that can be used to further build the dialog.
public ContentDialogBuilder WithFullSize()
{
_contentDialog.FullSizeDesired = true;
return this;
}
///
/// Asynchronously shows the content dialog.
///
/// A task containing the result of the content dialog.
/// Thrown when the parent window does not contain a panel at its root.
public async Task ShowAsync()
{
if (_parent.Content is not Panel panel)
throw new ArtemisSharedUIException($"The parent window {_parent.GetType().FullName} should contain a panel at its root");
try
{
panel.Children.Add(_contentDialog);
ContentDialogResult result = await _contentDialog.ShowAsync();
// Take the dialog away from the VM in case it's going to try to hide it again or whatever...
if (_viewModel != null)
_viewModel.ContentDialog = null;
return result;
}
finally
{
panel.Children.Remove(_contentDialog);
}
}
}
///
/// Represents a content dialog button.
///
public enum ContentDialogButton
{
///
/// No button.
///
None,
///
/// The primary button.
///
Primary,
///
/// The secondary button.
///
Secondary,
///
/// The close button.
///
Close
}
///
/// Represents a builder that can be used to create buttons inside content dialogs.
///
public class ContentDialogButtonBuilder
{
internal ContentDialogButtonBuilder()
{
}
internal string? Text { get; set; }
internal ICommand? Command { get; set; }
internal Action? Action { get; set; }
internal object? CommandParameter { get; set; }
///
/// Changes text message of the button.
///
/// The new text.
/// The notification builder that can be used to further build the button.
public ContentDialogButtonBuilder WithText(string? text)
{
Text = text;
return this;
}
///
/// Changes action that is called when the button is clicked.
///
/// The action to call when the button is clicked.
/// The builder that can be used to further build the button.
public ContentDialogButtonBuilder WithAction(Action action)
{
Action = action;
Command = ReactiveCommand.Create(() => Action());
return this;
}
///
/// Changes command that is called when the button is clicked.
///
/// The command to call when the button is clicked.
/// The builder that can be used to further build the button.
public ContentDialogButtonBuilder WithCommand(ICommand? command)
{
Command = command;
return this;
}
///
/// Changes parameter of the command that is called when the button is clicked.
///
/// The parameter of the command to call when the button is clicked.
/// The builder that can be used to further build the button.
public ContentDialogButtonBuilder WithCommandParameter(object? commandParameter)
{
CommandParameter = commandParameter;
return this;
}
}