Closed mokun closed 5 years ago
A generic solution that works on all terminals is not possible with the current API. I suppose that you use exclusively the SwingTextTerminal, so I added a few methods to it in release 3.3.0.
This example application shows how to register handlers for the up and down arrow keys and how to manipulate the input. To keep the things simple, the application doesn't implement autocompletion. It just lets you scroll through a list of choices, but I think this is enough to get you started.
@siordache, thanks much for the update and the new demo.
I just tried it out and works well.
However, in my implementation, I use the ContactInfo.java demo as an example and implement the use of Ctrl-U to go back to the previous question.
What I found out is that I canNOT register both the Ctrl-U handler and the UP and DOWN key handler at the same time.
addString(textIO, "First name", () -> contact.firstName, s -> contact.firstName = s);
addString(textIO, "Last name", () -> contact.lastName, s -> contact.lastName = s);
It will throw the followings :
org.beryx.textio.ReadAbortedException
at org.beryx.textio.InputReader.readWithPrompt(InputReader.java:452)
at org.beryx.textio.InputReader.lambda$read$3(InputReader.java:382)
at org.beryx.textio.TextTerminal.applyWithPropertiesConfigurator(TextTerminal.java:243)
at org.beryx.textio.InputReader.executeWithTerminal(InputReader.java:461)
at org.beryx.textio.InputReader.read(InputReader.java:380)
at org.beryx.textio.InputReader.read(InputReader.java:368)
at org.mars_sim.msp.core.terminal.CommanderProfile.addString(CommanderProfile.java:206)
The incompatibility is caused by the fact that I need to hide the s
string within valueSetter.accept()
if I want to make Ctrl-U handler works.
But I need to take s
outside of valueSetter.accept()
if I want to make UP/DOWN handler works.
And logically I cannot get both working at the same time !
See my addString() as follows :
private void addString(TextIO textIO, String prompt, Supplier<String> defaultValueSupplier, Consumer<String> valueSetter) {
setChoices("alice", "ava", "joyce");
String s = textIO.newStringInputReader()
.withDefaultValue(defaultValueSupplier.get())
.read(prompt);
operations.add(() -> valueSetter.accept(s));
// setChoices("alice", "ava", "joyce");
// operations.add(() -> valueSetter.accept(textIO.newStringInputReader()
// .withDefaultValue(defaultValueSupplier.get())
// .read(prompt)));
}
So when I hit Ctrl-U, it will end in that exception at the line
String s = textIO.newStringInputReader()
.withDefaultValue(defaultValueSupplier.get())
.read(prompt);
What can I do ?
@mokun I changed the example application to resemble the ContactInfo demo.
Now you can both scroll through choices and go back to the previous field.
The trick was to include the call to setChoices
into the Runnable added to operations:
operations.add(() -> {
setChoices(choices);
valueSetter.accept(textIO.newStringInputReader()
.withDefaultValue(defaultValueSupplier.get())
.read(prompt));
});
oic !
it seems that I can't replace the string array with int array even after I refactor the setChoices() method to accept an int array and process integers.
It complains that the API's replace method accepts string only and is unable to take in integer yet.
Also, how would you make it detect the partial input even before hitting the enter key ?
e.g. if I type in just "ha" and start to use the UP and DOWN key to scroll through the names that have the first 2 letters matching "ha", then "harold", "harry", "hamon", etc will show up.
I suppose I'll use stream to filter off the original list.
Is there a way to implement a listener to sense the letters that have been input at the prompt in real-time (without using the enter key) ?
@mokun The SwingTextTerminal provides (since 3.3.0) the getPartialInput()
method.
I made some changes to the example application. Now it supports int and double arrays and implements a simple form of autocomplete. I splitted the original file in two in order to separate the handler class from the code that uses it. Note that primitive and boxed types behave a bit different: for primitive types, a default value is always displayed.
Wow that's very cool and now I can implement autocomplete with text-io !
but I do have an issue with in Eclipse.
Please see screen shot below in SwingHandler :
In all those class such as StringTask, IntTask, etc.
It said,
Cannot refer to 'this' nor 'super' while explicitly invoking a constructor
What do you make of it ?
btw, I'm using Java 10 on windows OS and set to compile to Java 9 SE.
This seems to be a known issue with Eclipse. I refactored the code to get rid of this error.
I also added two methods:
constrainInputToChoices
- only values in the choice list will be accepted.withInputReaderConfigurator
- allows you to tweak the InputReaderSee Demo.java for usage examples.
I love text-io !
Now that I can create an autocomplete list.
One minor issue I had is that at the very end of when the line Press enter to terminate...
shows up, if I ever press the up/down arrow key, the previous choices will pop up unexpectedly.
I was going to use setChoices() to null the previous choices but now I can't and I wonder why.
In the new version of SwingHandler, I can see the following :
private void setChoices(List<String> choices) {
this.originalInput = "";
this.choiceIndex = -1;
this.choices = choices;
}
Now I have to add back the original setChoices() in which the parameter list is String... choices
public void setChoices(String... choices) {
this.originalInput = "";
this.choiceIndex = -1;
this.choices = Arrays.asList(choices);
}
Also, I have to use Arrays.asList(choices) at the end to convert the string array back to the list.
This is just my workaround.
Can you tweak it so that we don't have to call setChoices() at the end of each prompt to avoid this artifact ? Thx !
@mokun I fixed it by resetting the choice list before exiting Task.run().
Thx a lot !
In my implementation, my typical prompt is just ">". But I also want a customized
prompt. I want the prompt to be changed to "Connected with ABC >" after I type in "ABC" in the previous attempt .
However, I run into the issue of not being able to have this customized prompt showing up properly.
So what I did was that I add a boolean parameter called showPrevious
in Task inside SwingHandler
so that I can insert extra words as well as the previous choice in the input prompt.
I'll just show my code below as a reference :
public Task(String prompt, boolean showPrevious, Supplier<R> inputReaderSupplier, Supplier<T> defaultValueSupplier, Consumer<T> valueSetter) {
this.prompt = prompt;
this.showPrevious = showPrevious;
this.inputReaderSupplier = inputReaderSupplier;
this.defaultValueSupplier = defaultValueSupplier;
this.valueSetter = valueSetter;
}
@Override
public void run() {
setChoices(choices.stream().map(Object::toString).collect(Collectors.toList()));
try {
R inputReader = inputReaderSupplier.get();
if (showPrevious)
inputReader.withDefaultValue(defaultValueSupplier.get());
if(inputReaderConfigurator != null) {
inputReaderConfigurator.accept(inputReader);
}
if(constrainedInput) {
inputReader.withValueChecker((val,name) -> choices.contains(val) ? null
: Arrays.asList("'" + val + "' is not in the choice list."));
}
valueSetter.accept(inputReader.read(prompt));
} finally {
setChoices(Collections.emptyList());
}
}
Of course, this will change the StringTask, IntTask, etc.
Also, if I deal with multiple questions, your Demo shows the use of calling handler.execute() at the end.
In my implementation, I don't know how many of the same or different question will be asked in run-time.
So in my case, I don't want to add them to the bookmark and I come up with a more flexible method called executeOneTask() as follows for your reference :
public void executeOneTask() {
// Remove the last task
if (tasks.size() > 1)
tasks.remove(0);
terminal.setBookmark("bookmark_" + 0);
try {
tasks.get(0).run();
} catch (ReadAbortedException e) {
terminal.resetToBookmark("bookmark_" + 0);
}
}
I'm sure you can figure out a better way to implement it but this is what I have now.
Again appreciate much for your text-io library !
It looks good to me.
Thanks! I'm closing this thread now.
how do I even begin to implement some sort of autocomplete at the input cursor by using up and down arrow keys ?
I suppose this will be a nice feature for text-io that others are interested in.
Say I have
List<String> names = Arrays.asList("albert", "alice", "ava", "betty", "cathy");
So when asked about inputting a name, at the input prompt, if I type in the first letter as "a", (but before typing in the second letter), and I hit the up arrow key, I would like the word "albert" to pop up at the input prompt.
I suppose I'll need to write my own methods to implement the nitty gritty.
Can someone show me first on how to capture the input before hitting enter ?
How do I make use of the up and down arrow keys. Is it by registering handlers and how ?
Thanks!