-
-
Notifications
You must be signed in to change notification settings - Fork 532
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Abort SelectionPrompt
and MultiSelectionPrompt
with Escape
key
#711
Conversation
I haven't been able to get on my PC for the past few days, but I was thinking about the syntax and one thought I had was perhaps this syntax would be better over setting some properties public static bool TryPrompt<T>(IPrompt<T> prompt, out T result) |
|
@phil-scott-78 Sorry, you have right. If we add Aborted property to IPrompt then we can add AnsiConsole.TryPrompt and this should not break existing logic. |
namespace Spectre.Console;
/// <summary>
/// A console capable of writing ANSI escape sequences.
/// </summary>
public static partial class AnsiConsole
{
/// <summary>
/// Displays abortable prompt to the user.
/// </summary>
/// <typeparam name="T">The prompt result type.</typeparam>
/// <param name="prompt">The prompt to display.</param>
/// <param name="result">The prompt input result.</param>
/// <returns>True if prompt processed normally; false if prompt aborted.</returns>
public static bool TryPrompt<T>(SelectionPrompt<T> prompt, out T result) where T : notnull
{
prompt.AllowAbort = true;
result = prompt.Show(Console);
return !prompt.Aborted;
}
/// <summary>
/// Displays abortable prompt to the user.
/// </summary>
/// <typeparam name="T">The prompt result type.</typeparam>
/// <param name="prompt">The prompt to display.</param>
/// <param name="result">The prompt input result.</param>
/// <returns>True if prompt processed normally; false if prompt aborted.</returns>
public static bool TryPrompt<T>(MultiSelectionPrompt<T> prompt, out List<T> result) where T : notnull
{
prompt.AllowAbort = true;
result = prompt.Show(Console);
return !prompt.Aborted;
}
} However in this case I currently see three options on how to proceed with this idea:
What do others think? |
/// <summary> | ||
/// Gets a value indicating whether or not the prompt was aborted. | ||
/// </summary> | ||
public bool Aborted { get; private set; } = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not a fan of mutating the Prompt item.
I'm thinking that maybe it would be a good idea to have a method called ShowAbortable
or similar, and set the AllowAbort
as part of the context instead?
@phil-scott-78 @nils-a What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we use ShowSortable then it should contain ShowSortableAsync too. I more like solution with AllowAbort method.
var item = await new SelectionPrompt<string>()
.AllowAbort(true)
.AddChoices( chocies )
.ShowAsync( AnsiConsole.Console, CancellationToken.None );
Also, It will be interesting if this feature could be a part of IPrompt. I think that option to abort TextPrompt could be usefull. But I know that is bigger change. Adding it to selection without interface is still one step forward.
I want to be sure I did not miss any important feedback (English is not my native language) so let's sum up this merge request from my point of view:
I'm still here. I still want to continue with this PR. I'm just not sure how. |
Nobody asked for my opinion, but since I hit the same issue in another project of mine, I'll chip in my €0.02 anyway. :-) Let me start with a philosophical statement: I think supporting escape is a worthy goal, and I might even go as far as to state that practically every prompt actually should be cancelable, as non-cancelable prompts just lead to the user hitting Ctrl-C if they panic. It would be better for the application to deal with cancellation according to its best possible understanding of the situation - this is much similar to GUIs having close buttons in dialog boxes even if a choice is logically mandatory. This was true when I wrote my first console UX library for BBSes in 1992, and I think it still mostly is. That may still obviously not be the scope under consideration here. I agree with @patriksvensson that adding a result property into the Prompt object confuses things - that's the way e.g. Winforms did things with Form.DialogResult, and I don't think it was a good choice. But I'm not super-excited by a separate ShowAbortable method either: Sometimes the "abortability" (with whatever definition it has, given what I said above) is just another property of the prompt, and having a separate method for it makes things more complex at the callsite, especially if you have a structure where a generic UI framework (say, a wizard wrapper) is just passed a bunch of prompts. With separate ShowAbortable method, you'd end up passing What I'm saying is leading into the direction of a breaking change; perhaps having Show<T>() methods return a PromptResult<T> instead of a T, but perhaps that could have an implicit conversion to T. The value T would be one field of the PromptResult, the "dialog state" (i.e. aborted/OK) would be another. That's not trivial, but it could have potential for future extensibility as well. From past experience, I'd guess some likely extensibility points to come up are supporting tab/shift-tab to navigate between prompts (i.e. encapsulating several prompts into a form), which might be implementable as a "AllowTabNavigation" property and a couple of new "dialog states" (MovePrev/MoveNext), which a form controller might handle - but no, I don't think something like ShowNavigableAbortableAsync is going to fly. Another one is a combobox control, which may be used to pick from a list of customers (objects), but also enter a new customer name, in which scenario the result will never fit into a single field (as it may be either an object or a string). None of these issues is fully solved by complicating the result type of a Prompt.Show call, but I do think composability of complex UI (whether console or GUI) benefits from having result objects that can return not just the value, but also some information about the user's intent. That should be the part that makes complex things possible. Simple things should then be kept simple by having the Abortability be false by default and having the PromptResult be implicitly convertible to the result type. It will still break |
I'm really digging the feedback from @jouniheikniemi. I'd actually prefer to break things than introduce properties to force it into working with the current model. Sorry to keep hemming and hawing over this, @jariq. I think this is a really good feature and one that is needed for Spectre to reach a 1.0 release so it means a lot that we land it with something we all feel good about support long term. |
Just a quick reminder I'm still waiting for the input from maintainers willing to merge this feature. |
Stumbled across this PR and wanted to pitch in. I'm looking for similar functionality, but my use case is a tad different. I'm using this project as a rudimentary UI for a dialog API. Basically it's a form which can be filled in on per question basis. So we render a given question, the user answers it and we move on to the next. The question types range from text input to numeric, date, choice, boolean, etc. What I wanted to acheive is the ability to navigate back and forth between questions. The API behind this allows the user to navigate forward as long as the questions have already been answered. The user can also go back in order to revisit their answer to a given question. I was looking to use the arrow keys for navigating back and forth, so my first comment would be: do not tie cancelling a prompt to specific keys, but allow for the ability to specify which keystrokes should cause the prompt to cancel. If there's a need to implement this as a non-breaking change, you could consider a solution like this: var item = await new SelectionPrompt<string?>()
.AllowAbort(true, Keys.Left, Keys.Right)
.AddChoices( choices )
.ShowAsync( AnsiConsole.Console, CancellationToken.None ); Setting Even better imho would be to not return the typed response directly, but have a typed |
SelectionPrompt.ShowAsync must return instance of T. I created INullablePrompt in #1084. var item = await new SelectionPrompt<string?>()
.AllowAbort(true) //or .AllowAbort(true, Keys.Left, Keys.Right)
.AddChoices( choices )
.ShowNullableAsync( AnsiConsole.Console, CancellationToken.None ); I'm not sure if we need wrapper around result (like |
From PR spectreconsole#711, adapted a bit.
I'm closing this PR as I'm no longer interested in working on this feature. |
Code in this PR adds possibility to abort
SelectionPrompt
andMultiSelectionPrompt
withEscape
key. WhenEscape
key is pressed, prompt is aborted and itsAborted
property is set totrue
. I believe this PR does not change the current default behavior in any way, becauseEscape
key still does nothing unlessAllowAbort
property is explicitly set totrue
.I tried to follow existing coding style as closely as possible. I did add some tests and made sure all existing tests are passing before opening this PR. As suggested by contribution guidelines I started the discussion first in #700 but then had to move on with the implementation before I received any reply. I'm really sorry for that but I couldn't continue with a soon-to-be-releasedTM Pkcs11AdminConsole project without being able to abort these prompts.
If implementation in this PR is not suitable for inclusion I am willing to work on any suggested alternative.