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
Prompt options support #42
Conversation
* Moved options to its own file * Added object support * Added radio button support * Added generic static member to prompt
* Added help text * Added question text * Added is selection required
@natemcmaster Could I use the Options attribute to also mark properties that should be displayed as options? Or should I create a separate one? |
A separate attribute would be better. Btw, I'm headed out on vacation so I won't be able to take a closer look until next week. |
@natemcmaster Alright, have fun! |
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.
@redbaty looks good so far. First, a trivial thing. My code style preference is to always use brackets for if
, foreach
, else
etc. I called out a few specific places where I think they're vital.
I tried using this on macOS and the rendering doesn't appear correct. After pressing up/down a few times, this is what I see:
Final question, what do you think about this API surface? seems like most users would end up filtering Dictionary<string, bool> anyways.
// implies "radio". Only one response allowed
string GetOption(string prompt, string[] options)
// implies "checkbox". Zero or many responses allowed
string[] GetOptions(string prompt, string[] options)
/// <value> | ||
/// The checked character. | ||
/// </value> | ||
public string CheckedChar { get; set; } = "🔘"; |
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.
UTF8 output doesn't work well for all shells. Ascii may be a better default. I haven't tested on how well Console.OutputEncoding works, but my experience in the past is that UTF8 usually appears � instead of the actual character.
We could easily add an option to enable Unicode output, and in that case, use the prettier output characaters by default.
if (!Equals(Console.OutputEncoding, Encoding.UTF8)) | ||
Console.OutputEncoding = Encoding.UTF8; | ||
|
||
if (boxes != null) |
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.
Would rather we throw an ArgumentNullException. I prefer we use uses Enumerable.Empty instead of null for empty collections.
/// <param name="boxes">The boxes.</param> | ||
public CheckboxManager(CheckboxManagerOptions options, IEnumerable<string> boxes) | ||
{ | ||
if (!Equals(Console.OutputEncoding, Encoding.UTF8)) |
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.
This check doesn't really seem necessary. Re-setting output encoding is fine.
/// </summary> | ||
/// <param name="possibleSelections">The possible selections.</param> | ||
/// <returns></returns> | ||
public static Dictionary<string, bool> GetOption(params string[] possibleSelections) => |
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.
For consistency with the other Prompt APIs, can you make this GetOption(string prompt, params string[] options)
?
/// <param name="options">The options.</param> | ||
/// <param name="possibleSelections">The possible selections.</param> | ||
/// <returns></returns> | ||
public static Dictionary<string, bool> GetOption(CheckboxManagerOptions options = null, |
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.
Let's make options a non-optional parameter. It helps disambiguate which overload is used.
if (End()) break; | ||
} | ||
|
||
switch (key.Key) |
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.
Any plans to handle modifiers like shift/alt/ctrl?
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.
We could, whats your idea tho?
case ConsoleKey.UpArrow: | ||
GoUp(); | ||
break; | ||
case ConsoleKey.Spacebar: |
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.
Could we also treat X as a selection key?
private void BoxesOnCollectionChanged(object sender, | ||
NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs) | ||
{ | ||
foreach (var item in notifyCollectionChangedEventArgs.NewItems) |
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.
Code-style: nit. Can you use brackets in this method for the foreach and if?
{ | ||
get | ||
{ | ||
_boxes.CollectionChanged += BoxesOnCollectionChanged; |
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.
Move this to the constructor so its only added once. In current form, each get on .Boxes
adds a duplicate event subscription, for instance, on each call to Boxes.Add
and each redraw.
/// <value> | ||
/// <c>true</c> if this instance is radio; otherwise, <c>false</c>. | ||
/// </value> | ||
public bool IsRadio { get; 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.
Could we vary the default characters based on whether output is radio or not?
IsRadio | Unicode | Ascii |
---|---|---|
true | 🔘 / 〇 | (o) / ( ) |
false | ☑ / ▢ | [x] / [ ] |
Alright, just got home will make those review happen |
* Made keys customizable * Made help text customizable * Added non-unicode support * Added overloads
As for the mac bug I can't test it since I don't own one, I'll search arround tho, probably something with the way unix treats the console.clear thing? |
Do you have Bash for Windows? it might reproduce there. Thanks for making the some of the changes so far. |
/// <value> | ||
/// The select keys. | ||
/// </value> | ||
public ConsoleKey[] Select { get; set; } = { ConsoleKey.Spacebar, ConsoleKey.X }; |
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.
👍
while (true) | ||
{ | ||
var key = Console.ReadKey(); | ||
if (Options.Keys.Finalize.Contains(key.Key)) |
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 don't think we need this right now, but it might be nice to support using ctrl or alt to jump to the bottom or top of a list. For now, ignoring input when modifiers are used seems simpler.
@natemcmaster I've tried it on my Deepin ISO, and couldn't reproduce the MacOS bug, can you try it again? Cause maybe it could be fixed after the redraw() thingy I forced some commits ago |
|
||
while (true) | ||
{ | ||
var key = Console.ReadKey(); |
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.
This should be Console.ReadKey(intercept: true)
. This will prevent other characters from being displayed when the key is pressed.
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.
Totally forgot about that one 👍
Latest commit looks a lot better now. Still seeing issues if I press other keys |
Weird. It almost working before setting intercept to true, but now it's broken again. Something must be weird in the way Console.SetCursorPosition works on macOS when intercept true is enabled. |
Hmm let me try to reproduce it |
Can you check if for some reason this line is being hit on mac? or try to comment this If any of those dont work it'll probably be something here: Ps: I couldnt repro this on linux, so probably something mac specific |
So, I played a little with the code, and here is what I found. Console.SetCursorPosition isn't reliable. Here's an experiment. It first draws two lines, then tries to move the cursor to the end of the first line. It works until the buffer in Terminal.app reaches the bottom. static void Main(string[] args)
{
var (x, y) = (Console.CursorLeft, Console.CursorTop);
const string line = "x <-- start here. continue --";
Console.WriteLine(line);
Console.BackgroundColor = ConsoleColor.Red;
Console.WriteLine(" Bottom bar ");
Console.ResetColor();
Console.SetCursorPosition(x + line.Length, y);
Console.Write(">");
Console.CursorVisible = true;
Console.ReadLine();
} |
I think what we need to make this work is that we need to think of the output area as a rectangle of pixels, each pixel is a character. SetCursorPosition is working as expected, but the value we should be passing to it aren't absolute x/y positions. The positions need to be relative to the top-left point of the box, which moves up when you hit the bottom of the screen buffer. So, maybe a better approach is to first compute the width/height of output text needed, and use that to compute the cursor positions that should be used. |
Hmm this is really tricky, since I can't really redraw cause we dont have a fixed point. This only occurs on unix, linux included. The only solution I've thought of is to clear the console area, and show only the options then when that is done we re-write the things that were already drawn. |
I took a peek at what some other libraries do. It appears they use special ANSI escape codes for moving the cursor up/down/left/right. They use these moves to redraw the output. As a far as I can tell, this will be the only way to ensure this feature works cross-platform....which I'm going to require before I'd be okay shipping this feature. I realized you've done quite a bit of work already. If you want to take care of this in a separate PR, let me know. I can merge this to a feature branch instead of master to give this work more time to mature before it gets released. Feature branches are still available in nightly builds on my myget feed. |
@natemcmaster I'll take a look at what other libraries do then, I had not thought about that 😜 |
Any progress? I'm inclined to take this PR as-is into a feature branch so we can work on it more. I want to keep the foundation you're laid out. I think it's a great start |
@natemcmaster Yeah sorry, haven't given any updates 😢 I haven't found a solution yet, the only one I think would work is to clear the console do the prompt and then rewrite everything that was inputted. Anyway if you think a feature branch would work for you then all right I'm in aswell |
Ok, let's but this into the feature/checkbox branch. We can use #41 to track finishing up this feature. |
Added checkbox support with the following features:
Missing features (yet):