Skip to content

Commit

Permalink
fixed jline#20 and added documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
awegmann committed Aug 18, 2020
1 parent acbad79 commit 144a0d1
Show file tree
Hide file tree
Showing 26 changed files with 811 additions and 91 deletions.
30 changes: 21 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<img src="./doc/ConsoleUI-Logo.png" width="200" align="right">
<img src="./doc/ConsoleUI-Logo.png" width="200" align="right" alt="ConsoleUI logo">

[![Build Status](https://travis-ci.org/awegmann/consoleui.svg?branch=master)](https://travis-ci.org/awegmann/consoleui)

# Console UI
# ConsoleUI

[![Join the chat at https://gitter.im/awegmann/consoleui](https://badges.gitter.im/awegmann/consoleui.svg)](https://gitter.im/awegmann/consoleui?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)

Expand All @@ -14,15 +14,15 @@ Tiny java library that provides simple UI elements on ANSI console based termina
I was impressed by JavaScript based Yeoman which leads the user through the process of creating new projects
by querying with a simple user interface on the console. An investigation how this is done, brought
me to Inquirer.js which implements a very simple and intuitive set of controls for checkbox, list and text input.

Because I didn't found anything comparable to this in the Java eco system, I decided to write `Console UI`
as a library with the same easy 'look and feel'. Some parts of the API are also comparable, but Console UI is not
a Java clone of Inquirer.js.

# Features

Console UI currently supports:

- Text input with completion and GNU ReadLine compatible editing
- Checkboxes
- Lists
Expand All @@ -35,23 +35,28 @@ A screen recording of the basic elements demo can be fund on Youtube [console UI

Console UI uses jansi and jline for the dirty console things.

# Maven artefact

ConsoleUI releases are available at Maven Central [de.codeshelf.consoleui » consoleui](https://mvnrepository.com/artifact/de.codeshelf.consoleui/consoleui)

# Test Run

You can get an idea how the project works by looking at `de.codeshelf.consoleui.Basic`. You can run this by executing the following from the project root:
You can get an idea how the project works by looking at `de.codeshelf.consoleui.Basic`.
You can run this by executing the following from the project root:

gradlew fatJar
java -jar build/libs/consoleui-all-0.0.10.jar # <- replace with the latest version

# Usage

Before you can use Console UI the AnsiConsole library has to be initialized.
Before you can use ConsoleUI the AnsiConsole library has to be initialized.

AnsiConsole.systemInstall();

Entry point to the builder classes is to create a new object of type `ConsolePrompt`.

ConsolePrompt prompt = new ConsolePrompt();

From the prompt object, use the `getPromptBuilder()` method to create the builder for all subsequent UI elements
you want to use.

Expand All @@ -73,9 +78,16 @@ From with this `PromptBuilder` you can access UI builder with the following meth
- createListPrompt()
* creates a list prompt. This prompt lets the user choose one item from a given list.

*See the [how to](doc/howto.md) to get a more detailed documentation how to use ConsoleUI.*

# Changes

### Version 0.0.12

- Fixed Bug #20: Lists higher than the terminal height were not handled correctly.
ConsoleUI now supports scrolling for checkbox promt and list prompt.
To configure the height of the view port, either an absolute number of lines or a fraction (percentage) of the
screen height can be defined.



6 changes: 1 addition & 5 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ plugins {
}

project.group = "de.codeshelf.consoleui"
project.version = "0.0.11"
project.version = "0.0.12"

java {
withJavadocJar()
Expand Down Expand Up @@ -106,10 +106,6 @@ publishing {
}
}

/*
signing {
sign publishing.publications.consoleUI
}
*/
294 changes: 294 additions & 0 deletions doc/howto.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,294 @@
# ConsoleUI programming guide

This document contains a brief introduction in using ConsoleUI.

## Introduction

ConsoleUI is a library for prompting the user for different types of input. It provides simple UI elements on ANSI console based terminals. ConsoleUI is inspired by [Inquirer.js](https://github.com/SBoudrias/Inquirer.js) which is written in JavaScript.

## Features

Console UI currently supports:

- Text input with completion and GNU ReadLine compatible editing
- Checkboxes
- Lists
- Expandable Choices (multiple key based answers for a question with help and optional list navigation)
- Yes/No-Questions

## A small example

The following code presents a simple, but complete code example to use CosoleUI for a selecting an item from a list.

```java
package de.codeshelf.consoleui;

import de.codeshelf.consoleui.prompt.ConsolePrompt;
import de.codeshelf.consoleui.prompt.PromtResultItemIF;
import de.codeshelf.consoleui.prompt.builder.PromptBuilder;
import jline.TerminalFactory;
import org.fusesource.jansi.AnsiConsole;

import java.io.IOException;
import java.util.HashMap;

import static org.fusesource.jansi.Ansi.ansi;

/**
* User: Andreas Wegmann
* Date: 12.08.2020
*/
public class SimpleExample {

public static void main(String[] args) throws InterruptedException {
AnsiConsole.systemInstall(); // #1
System.out.println(ansi().eraseScreen().render("Simple list example:"));

try {
ConsolePrompt prompt = new ConsolePrompt(); // #2
PromptBuilder promptBuilder = prompt.getPromptBuilder(); // #3

promptBuilder.createListPrompt() // #4
.name("pizzatype")
.message("Which pizza do you want?")
.newItem().text("Margherita").add() // without name (name defaults to text)
.newItem("veneziana").text("Veneziana").add()
.newItem("hawai").text("Hawai").add()
.newItem("quattro").text("Quattro Stagioni").add()
.addPrompt();

HashMap<String, ? extends PromtResultItemIF> result = prompt.prompt(promptBuilder.build()); // #5
System.out.println("result = " + result);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
TerminalFactory.get().restore();
} catch (Exception e) {
e.printStackTrace();
}
}
}}

```

Basic steps:

1. ConsoleUI uses [jansi](https://github.com/fusesource/jansi), so you have to initialize it first
2. Create a new `ConsolePrompt` object.
3. Create a `PromptBuilder`. All user interaction can be create with the prompt builder object.
4. In this example we create a `ListPrompt`
1. give it a name ('pizzatype')
2. assign a prompt message ("Which pizza do you want?")
3. create items with and name (optinal) and a text and add them to the ListPrompt
4. to finish call `addPrompt()` to create the ListPrompt and add it to the prompt builder object.
5. calling `prompt.prompt(promptBuilder.build())` builds all the promts (in this example only one) and enters the user interaction. After all prompts are processes, the `prompt()` method returns an object with the user input results.

# Prompting for user input

### Input

The InputPrompt is a classic entry line like a shell. Because of the underlying readline implementation it offers you to provide completers (like file name completer or string completer). In addition to his, you can define a mask character which is printed on the screen instead of the typed keys like used for hidden password entry.

```java
promptBuilder.createInputPrompt() // #1
.name("name") // #2
.message("Please enter your name") // #3
.defaultValue("John Doe") // #4
//.mask('*') // #5
.addCompleter(new StringsCompleter("Jim", "Jack", "John")) // #6
.addPrompt();
```

Description:

1. With the prompt builder call `createInputPrompt()` to create a new input prompt builder
2. Set a name for the prompt. Setting a name is neccessary to pick the right user input from the result object after the user interaction is finished. The resulting object is `HashMap` where each key is the name of a prompt you have created before.
3. Add a message which is printed in front of the user input.
4. (optional) By adding a default value, you offer the user just to press enter to accept the default value. The default value is printed in parentheses after the prompt.
5. (optional) If you add a masking character, each key pressed by the user echoed as this mask character on the console. That's useful for password entries.
6. (optional) You can add completers (like simple string competers, file completers or more complex completers) to simplify the user input. The user can use the TAB button to complete partitial input. See [jline/jline2](https://github.com/jline/jline2/tree/master/src/main/java/jline/console/completer) for details and examples.

#### Console

<img src="./screenshots/input_prompt.png" alt="input prompt" style="zoom: 67%;" />

#### User Input

The user can use readline compatible navigation (mainly Emacs control codes) inside the user input area. This includes CTRL-a to go to the beginning of input, CTRL-e to go to the end, and so on.

After the input, the entered value is printed in different color right after the prompt.

#### Result

The result of this prompt is of type `InputResult` and has a method `getInput()` to the get user input.

### List

The ListPrompt lets the user choose one item from a given list. If the list is bigger than the terminal height, the list partially shown and scrolled if needed.

```java
promptBuilder.createListPrompt() // #1
.name("pizzatype") // #2
.message("Which pizza do you want?") // #3
.newItem().text("Margherita").add() // without name (name defaults to text) #4
.newItem("veneziana").text("Veneziana").add() // #5
.newItem("hawai").text("Hawai").add()
.newItem("quattro").text("Quattro Stagioni").add()
// .pageSize(10) #6
// .relativePageSize(66) #7
.addPrompt();
```

Description:

1. With the prompt builder call `createListPrompt()` to create a new list prompt builder.
2. Set a name for the prompt.
3. Add a message which is printed on top of list.
4. Add items to the list. If you call `newItem()` without a name for the item, the text of the item is used in the result.
5. Add items with `newItem(<item_name>)` to give the item a name which make it possible to use a technical key or an other value instead of the printed text as result.
6. (optional) For long lists, you can use `pageSize()` to set an absolute number of items to display (default is 10). Even if you choose a higher value than the terminal, the list will never exceed the terminal height.
7. (optional) Further you can use `relativePageSize()` to set a percentual size of the terminal as height. In this example 66 is 66/100 or 2/3 of the terminal used for the list items. At least one line is display even when you select a very low value.

#### Console

<img src="screenshots/list_prompt.png" alt="list_prompt" style="zoom:67%;" />

#### User input

The user can use up and down keys or VI like the keys `'j'` for down and `'k'` for moving the selector in front of the items to the right position. Pressing the enter button selects the item.

After the input, the list is erased from the screen and the selected value is printed in different color right after the prompt.

#### Result

The result of this prompt is of type `ListResult` and has a method `getSelectedId()` to the get user input.

### Checkbox

The checkox input lets the user choose any number of items of a given list. It's possible to add separators between items. Further it's possible to disable items and display a message text, why they are disabled. Sometimes this may be useful to present a consistent user interface with all usually displayed options and an explanation why some items are not usable here.

```java
promptBuilder.createCheckboxPrompt() // #1
.name("topping") // #2
.message("Please select additional toppings:") // #3
.newSeparator("standard toppings").add() // #4
.newItem().name("cheese").text("Cheese").add() // #5
.newItem("bacon").text("Bacon").add() // #6
.newItem("onions").text("Onions").disabledText("Sorry. Out of stock.").add() // #7
.newSeparator().text("special toppings").add()
.newItem("salami").text("Very hot salami").check().add() // #8
.newItem("salmon").text("Smoked Salmon").add()
.newSeparator("and our speciality...").add()
.newItem("special").text("Anchovies, and olives").checked(true).add() // #9
// .pageSize(10) #10
// .relativePageSize(66) #11
.addPrompt();
```

Description:

1. With the prompt builder call `createCheckboxPrompt()` to create a new checkbox prompt builder.
2. Set a name for the prompt.
3. Add a message which is printed on top of list.
4. Use `newSeparator()` to add a separation element with a describing text.
5. Use `newItem()` like in the list promt to add selectable elements to the checkbox prompt.
6. The name can be set directly with `newItem(<name>)` instead of using the `name()` method.
7. With `disabledText()` the item is not selectable, the text is displayed after the item. *Note:* even when the item is disabled it can be checked by default.
8. Use the `check()` method to pre check the corresponding item.
9. For more flexibility you can use `checked()` with a boolean value to select if the item is checked by default.
10. (optional) For long lists, you can use `pageSize()` to set an absolute number of items to display (default is 10). Even if you choose a higher value than the terminal, the list will never exceed the terminal height.
11. (optional) Further you can use `relativePageSize()` to set a percentual size of the terminal as height. In this example 66 is 66/100 or 2/3 of the terminal used for the list items. At least one line is display even when you select a very low value.

#### Console

<img src="screenshots/checkbox_prompt.png" alt="checkbox_prompt" style="zoom:67%;" />

#### User input

The user can use up and down keys or VI like the keys `'j'` for down and `'k'` for moving the selector in front of the items to the right position. Toggeling selection on an item is done with the space bar. Pressing the enter button finished the input.

After the input, the list is erased from the screen and the names of the selected values are printed in different color right after the prompt.

#### Result

The result of this prompt is of type `CheckboxResult` and has a method `getSelectedIds()` to the get user input which is of type`HashSet<String>` containing the names of the selected items.

### Expandable choice

The choice prompt lets the user choose one from a given number of possible answers. It is used for requesting input what would be done in a graphical user interface by a message box. It's usable for choices like "yes/no/cancel". Each entry is assigned with a single key stroke to provide a quick input by pressing that key.

By default only the message and the possible keys are displayed which may make it difficult for new users to know what keystroke is associated with what answer. To get around this, a help mode is integrated. If the user pressed the 'h' button (for help), all possible answers with explanation are shown as a list where the user can choose from.

```java
promptBuilder.createChoicePrompt() // #1
.name("payment") // #2
.message("How do you want to pay?") // #3
.newItem().name("cash").message("Cash").key('c').asDefault().add() // #4
.newItem("visa").message("Visa Card").key('v').add() // #5
.newItem("master").message("Master Card").key('m').add()
.newSeparator("online payment").add() // #6
.newItem("paypal").message("Paypal").key('p').add()
.addPrompt();
```

Description:

1. With the prompt builder call `createChoicePrompt()` to create a new choice prompt builder.
2. Set a name for the prompt.
3. Add a message which is printed on the screen.
4. Create new items with name, message and associated key. One of the items can be a default.
5. Create other non default items.
6. Use `newSeparator()` to add a separation element with a describing text.

#### Console and user input

By default, the message is displayed and a list of all short keys for selection. The defaults value key is printed in upper case to indicate the default value. The letter 'h' is added to the list by default to activate the more detailed helpful list view.

<img src="screenshots/expandable_choice_prompt_1.png" alt="expandable_choice_prompt_1" style="zoom:67%;" />

If the user presses one of the choice keys, the corresponding message is displayed and can be confirmed with the enter key.

<img src="screenshots/expandable_choice_prompt_2.png" alt="expandable_choice_prompt_2" style="zoom:67%;" />

If the user presses the 'h' key, the long list is displayed.

<img src="screenshots/expandable_choice_prompt_3.png" alt="expandable_choice_prompt_3" style="zoom:67%;" />

The detailed navigation is usable like the list prompt by pressing up and down arrow keys and selecting a value with the enter key.

#### Result

The result of this prompt is of type `ExpandableChoiceResult` and has a method `getSelectedId()` to the get user input.

### Confirmation

The confirmation prompt lets the user answer with 'yes' or 'no' to a given question. This is the minimalistic version of the choice prompt.

```java
promptBuilder.createConfirmPromp() // #1
.name("delivery") // #2
.message("Is this pizza for delivery?") // #3
.defaultValue(ConfirmChoice.ConfirmationValue.YES) // #4
.addPrompt();
```

Description:

1. With the prompt builder call `createChonfirmPrompt()` to create a new confirmation prompt builder.
2. Set a name for the prompt.
3. Add a message which is printed as prompt.
4. With `defaultValue()` you can set either 'yes' or 'no' as a default.

#### Console

<img src="screenshots/confirmation_prompt.png" alt="confirmation_prompt" style="zoom:67%;" />

#### User input

By pressing 'y' or 'n' (or 'j/n' in the german localization) the user can select beween 'yes' and 'no' as answer. By pressing the enter key the user confirms the input.

#### Result

The result of this prompt is of type `ConfirmResult` and has a method `getConfirmed()` to the get user input of type `ConfirmChoice.ConfirmationValue` which is an enum of either 'YES' or 'NO'.

Binary file added doc/screenshots/checkbox_prompt.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/screenshots/confirmation_prompt.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/screenshots/expandable_choice_prompt_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/screenshots/expandable_choice_prompt_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/screenshots/expandable_choice_prompt_3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/screenshots/input_prompt.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/screenshots/list_prompt.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 144a0d1

Please sign in to comment.