Skip to content
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

Merged
merged 13 commits into from Feb 20, 2018
Merged

Conversation

redbaty
Copy link

@redbaty redbaty commented Jan 18, 2018

Added checkbox support with the following features:

  • Can be treated as radio button.
  • Can be created from a class.

Missing features (yet):

  • Nesting
  • Select what properties to show from model using attributes.

* 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
@redbaty
Copy link
Author

redbaty commented Jan 18, 2018

@natemcmaster Could I use the Options attribute to also mark properties that should be displayed as options? Or should I create a separate one?

@redbaty redbaty mentioned this pull request Jan 18, 2018
@redbaty redbaty changed the title Checkbox support Prompt options support Jan 18, 2018
@natemcmaster
Copy link
Owner

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.

@redbaty
Copy link
Author

redbaty commented Jan 19, 2018

@natemcmaster Alright, have fun!

Copy link
Owner

@natemcmaster natemcmaster left a 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:
image

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; } = "🔘";
Copy link
Owner

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)
Copy link
Owner

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))
Copy link
Owner

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) =>
Copy link
Owner

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,
Copy link
Owner

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)
Copy link
Owner

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?

Copy link
Author

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:
Copy link
Owner

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)
Copy link
Owner

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;
Copy link
Owner

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;
Copy link
Owner

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] / [ ]

@redbaty
Copy link
Author

redbaty commented Jan 27, 2018

Alright, just got home will make those review happen

* Made keys customizable
* Made help text customizable
* Added non-unicode support
* Added overloads
@redbaty
Copy link
Author

redbaty commented Jan 27, 2018

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?

@natemcmaster
Copy link
Owner

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 };
Copy link
Owner

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))
Copy link
Owner

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.

@redbaty
Copy link
Author

redbaty commented Jan 27, 2018

@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();
Copy link
Owner

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.

Copy link
Author

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 👍

@natemcmaster
Copy link
Owner

Latest commit looks a lot better now. Still seeing issues if I press other keys

@natemcmaster
Copy link
Owner

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.

@redbaty
Copy link
Author

redbaty commented Jan 27, 2018

Hmm let me try to reproduce it

@natemcmaster
Copy link
Owner

natemcmaster commented Jan 27, 2018

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();
    }

out

@natemcmaster
Copy link
Owner

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.

@redbaty
Copy link
Author

redbaty commented Jan 28, 2018

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.

@natemcmaster
Copy link
Owner

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.

@redbaty
Copy link
Author

redbaty commented Feb 1, 2018

@natemcmaster I'll take a look at what other libraries do then, I had not thought about that 😜
After that we'll decide what to do with this PR

@natemcmaster
Copy link
Owner

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

@redbaty
Copy link
Author

redbaty commented Feb 16, 2018

@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

@natemcmaster natemcmaster changed the base branch from master to feature/checkbox February 20, 2018 05:12
@natemcmaster
Copy link
Owner

Ok, let's but this into the feature/checkbox branch. We can use #41 to track finishing up this feature.

@natemcmaster natemcmaster merged commit d958cdd into natemcmaster:feature/checkbox Feb 20, 2018
This was referenced Jul 21, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants