Skip to content

Commit

Permalink
Initial implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
YuriyDurov committed Jan 8, 2024
1 parent bb23fbe commit 0de3e45
Show file tree
Hide file tree
Showing 16 changed files with 253 additions and 81 deletions.
20 changes: 20 additions & 0 deletions sample/BitzArt.Console.TestApp/Menus/FruitsMenu.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Spectre.Console;

namespace BitzArt.Console;

internal class FruitsMenu : ConsoleSelectionMenu
{
public override string Title => "Fruits";

public FruitsMenu()
{
AddSelection("apple", pauseOnComplete: true);
AddSelection("pear", pauseOnComplete: true);
AddSelection("banana", pauseOnComplete: true);
}

public override void OnSelection(ConsoleSelectionMenuItem selection)
{
AnsiConsole.WriteLine($"You selected '{selection.Name}'");
}
}
9 changes: 7 additions & 2 deletions sample/BitzArt.Console.TestApp/Menus/MainMenu.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
namespace BitzArt.Console;

internal class MainMenu(IConsoleAppNavigationManager navigation) : ConsoleMenuBase
internal class MainMenu : ConsoleSelectionMenu
{
public override string Title => "Main Menu";
protected override bool IsMainMenu => true;

public IConsoleAppNavigationManager Navigation { get; } = navigation;
public MainMenu()
{
AddSubmenu<FruitsMenu>("Fruits");
AddSubmenu<VeggiesMenu>("Veggies");
}
}
6 changes: 0 additions & 6 deletions sample/BitzArt.Console.TestApp/Menus/SubMenu1.cs

This file was deleted.

6 changes: 0 additions & 6 deletions sample/BitzArt.Console.TestApp/Menus/SubMenu2.cs

This file was deleted.

31 changes: 31 additions & 0 deletions sample/BitzArt.Console.TestApp/Menus/VeggiesMenu.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Spectre.Console;

namespace BitzArt.Console;

internal class VeggiesMenu : ConsoleSelectionMenu
{
public override string Title => "Veggies";

private bool _selectedPotato = false;

public VeggiesMenu()
{
AddSelection("tomato", pauseOnComplete: true);
AddSelection("cucumber", pauseOnComplete: true);
AddSelection("is potato a vegetable?", () => _selectedPotato = true, pauseOnComplete: true);
}

public override void OnSelection(ConsoleSelectionMenuItem selection)
{
AnsiConsole.WriteLine($"You selected '{selection.Name}'");
}

public override void OnSelectionInvoked(ConsoleSelectionMenuItem selection)
{
if (_selectedPotato)
{
AnsiConsole.WriteLine($"Potato is a vegetable.");
return;
}
}
}
14 changes: 10 additions & 4 deletions src/BitzArt.Console/App/ConsoleApp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,19 @@

namespace BitzArt.Console;

public class ConsoleApp(ConsoleAppBuilder builder)
public class ConsoleApp
{
public IServiceProvider Services { get; private set; } = builder.Services.BuildServiceProvider();
public IServiceProvider Services { get; private set; }

public ConsoleApp(ConsoleAppBuilder builder)
{
builder.Services.AddSingleton(this);
Services = builder.Services.BuildServiceProvider();
}

public void Run<TConsoleMenu>(CancellationToken cancellationToken = default) where TConsoleMenu : IConsoleMenu
{
var consoleMenu = Services.GetRequiredService<TConsoleMenu>();
consoleMenu.RunAsync().Wait(cancellationToken);
var navigationManager = Services.GetRequiredService<IConsoleAppNavigationManager>();
navigationManager.NavigateAsync<TConsoleMenu>().Wait(cancellationToken);
}
}
5 changes: 4 additions & 1 deletion src/BitzArt.Console/App/ConsoleAppBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,8 @@ public ConsoleAppBuilder()
Services.AddSingleton<IConsoleAppNavigationManager, ConsoleAppNavigationManager>();
}

public ConsoleApp Build() => new(this);
public ConsoleApp Build()
{
return new ConsoleApp(this);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace BitzArt.Console;

internal class SelectionItemDuplicateException(string actionName) : Exception($"Duplicate selection item '{actionName}'")
{
}
15 changes: 15 additions & 0 deletions src/BitzArt.Console/Extensions/AddConsoleMenuExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,21 @@ namespace BitzArt.Console;

public static class AddConsoleMenuExtensions
{
/// <summary>
/// Registers all console menus defined in the assembly containing this type in the <see cref="IServiceCollection"/>
/// </summary>
public static IServiceCollection AddConsoleMenusFromAssemblyContaining<TAssemblyPointer>(this IServiceCollection services)
=> services.AddConsoleMenusFromAssemblyContaining(typeof(TAssemblyPointer));

/// <summary>
/// Registers all console menus defined in the assembly containing this type in the <see cref="IServiceCollection"/>
/// </summary>
public static IServiceCollection AddConsoleMenusFromAssemblyContaining(this IServiceCollection services, Type type)
=> services.AddConsoleMenusFromAssembly(type.Assembly);

/// <summary>
/// Registers all console menus defined in the assembly in the <see cref="IServiceCollection"/>
/// </summary>
public static IServiceCollection AddConsoleMenusFromAssembly(this IServiceCollection services, Assembly assembly)
{
var tools = assembly
Expand All @@ -23,6 +32,9 @@ public static IServiceCollection AddConsoleMenusFromAssembly(this IServiceCollec
return services;
}

/// <summary>
/// Registers a Console Menu of type <paramref name="TConsoleMenu"/> in the <see cref="IServiceCollection"/>
/// </summary>
public static IServiceCollection AddConsoleMenu<TConsoleMenu>(this IServiceCollection services)
where TConsoleMenu : class, IConsoleMenu
{
Expand All @@ -32,6 +44,9 @@ public static IServiceCollection AddConsoleMenu<TConsoleMenu>(this IServiceColle
return services;
}

/// <summary>
/// Registers a Console Menu of <paramref name="type"/> in the <see cref="IServiceCollection"/>
/// </summary>
public static IServiceCollection AddConsoleMenu(this IServiceCollection services, Type type)
{
if (type is null) throw new ArgumentException($"{nameof(type)} must not be null");
Expand Down
24 changes: 18 additions & 6 deletions src/BitzArt.Console/Extensions/RunConsoleMenuExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,32 @@ namespace BitzArt.Console;

public static class RunConsoleMenuExtension
{
public static async Task RunConsoleMenuAsync<TConsoleMenu>(this IServiceProvider serviceProvider)
internal static async Task RunConsoleMenuAsync<TConsoleMenu>(this IServiceProvider serviceProvider)
where TConsoleMenu : IConsoleMenu
{
var tool = serviceProvider.GetRequiredService<TConsoleMenu>();
var menu = serviceProvider.GetRequiredService<TConsoleMenu>();

await tool.RunAsync();
AttachApp(menu, serviceProvider);

await menu.RunAsync();
}

public static async Task RunConsoleMenuAsync(this IServiceProvider serviceProvider, Type menuType)
internal static async Task RunConsoleMenuAsync(this IServiceProvider serviceProvider, Type menuType)
{
if (!menuType.IsAssignableTo(typeof(IConsoleMenu))) throw new ArgumentException($"Menu Type must implement {nameof(IConsoleMenu)}", nameof(menuType));

var tool = (IConsoleMenu)serviceProvider.GetRequiredService(menuType);
var menu = (IConsoleMenu)serviceProvider.GetRequiredService(menuType);

AttachApp(menu, serviceProvider);

await menu.RunAsync();
}

private static void AttachApp(this IConsoleMenu menu, IServiceProvider serviceProvider)
{
if (menu is not ConsoleMenu consoleMenu) return;

await tool.RunAsync();
var app = serviceProvider.GetService<ConsoleApp>();
consoleMenu.App = app;
}
}
29 changes: 29 additions & 0 deletions src/BitzArt.Console/Menus/ConsoleMenu.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Microsoft.Extensions.DependencyInjection;
using Spectre.Console;

namespace BitzArt.Console;

public abstract class ConsoleMenu : IConsoleMenu
{
public ConsoleApp? App { get; set; }

public virtual string Title => "Menu";

public Task RunAsync()
{
Render();
return Task.CompletedTask;
}

public virtual void Render()
{
AnsiConsole.Clear();
AnsiConsoleMenu.WriteTitle(Title);
}

public async Task RunAsync<TConsoleMenu>() where TConsoleMenu : IConsoleMenu
{
var navigationManager = App!.Services.GetRequiredService<IConsoleAppNavigationManager>();
await navigationManager.NavigateAsync<TConsoleMenu>();
}
}
32 changes: 0 additions & 32 deletions src/BitzArt.Console/Menus/ConsoleMenuBase.cs

This file was deleted.

8 changes: 0 additions & 8 deletions src/BitzArt.Console/Menus/ConsolePanelMenuBase.cs

This file was deleted.

81 changes: 81 additions & 0 deletions src/BitzArt.Console/Menus/ConsoleSelectionMenu.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using Spectre.Console;
using static System.Collections.Specialized.BitVector32;

namespace BitzArt.Console;

public abstract class ConsoleSelectionMenu : ConsoleMenu
{
/// <summary>
/// Identifies whether this is a main menu of the app or not.
/// </summary>
protected virtual bool IsMainMenu => false;

public override string Title => "Selection Menu";

protected virtual List<ConsoleSelectionMenuItem> SelectionItems { get; set; } = [];

public void AddSubmenu<TMenu>(string? selectionName = null)
where TMenu : IConsoleMenu
{
selectionName ??= typeof(TMenu).Name;
AddSelection(selectionName, async () => await RunAsync<TMenu>());
}

public void AddSelection(string name, Action? action = null, bool pauseOnComplete = false)
{
SelectionItems.Add(new ConsoleSelectionMenuItem(name, action, pauseOnComplete));
}

public override void Render()
{
AnsiConsole.Clear();

var selectionPrompt = new SelectionPrompt<ConsoleSelectionMenuItem>().Title($"[green]{Title}[/]");

selectionPrompt.AddChoices(SelectionItems);

var backSelectionOption = IsMainMenu ? ConsoleSelectionMenuItem.ExitItem : ConsoleSelectionMenuItem.BackItem;
selectionPrompt.AddChoice(backSelectionOption);

var selected = AnsiConsole.Prompt(selectionPrompt);

if (selected.IsExit)
{
OnExit();
return;
}

OnSelection(selected);
InvokeSelection(selected);
OnSelectionInvoked(selected);
OnAfterSelectionInvoked(selected);

Render();
}

public virtual void OnSelection(ConsoleSelectionMenuItem selection)
{
}

public virtual void InvokeSelection(ConsoleSelectionMenuItem selection)
{
selection.Action?.Invoke();
}

public virtual void OnSelectionInvoked(ConsoleSelectionMenuItem selection)
{
}

public virtual void OnAfterSelectionInvoked(ConsoleSelectionMenuItem selection)
{
if (selection.PauseOnComplete)
{
AnsiConsole.WriteLine("Press any key to continue...");
System.Console.ReadKey();
}
}

public virtual void OnExit()
{
}
}
12 changes: 0 additions & 12 deletions src/BitzArt.Console/Menus/ConsoleSelectionMenuBase.cs

This file was deleted.

Loading

0 comments on commit 0de3e45

Please sign in to comment.