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

Selected value for interactiveSelectList #250

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Changelog

## Unreleased
### Added
mikaelstaldal marked this conversation as resolved.
Show resolved Hide resolved
- Possibility to specify hidden return value in `Terminal.interactiveSelectList`, `Terminal.interactiveMultiSelectList`, and `InteractiveSelectListBuilder`.
## 3.0.1
### Fixed
- Fixed terminal size detection in the `mordant-jvm-ffm` module on macOS [(#238)](https://github.com/ajalt/mordant/issues/238)
Expand Down
31 changes: 17 additions & 14 deletions mordant/api/mordant.api
Original file line number Diff line number Diff line change
Expand Up @@ -212,10 +212,14 @@ public final class com/github/ajalt/mordant/input/InteractiveSelectListBuilder {
public fun <init> (Lcom/github/ajalt/mordant/terminal/Terminal;)V
public final fun addEntry (Lcom/github/ajalt/mordant/widgets/SelectList$Entry;)Lcom/github/ajalt/mordant/input/InteractiveSelectListBuilder;
public final fun addEntry (Ljava/lang/String;)Lcom/github/ajalt/mordant/input/InteractiveSelectListBuilder;
public final fun addEntry (Ljava/lang/String;Lcom/github/ajalt/mordant/rendering/Widget;)Lcom/github/ajalt/mordant/input/InteractiveSelectListBuilder;
public final fun addEntry (Ljava/lang/String;Lcom/github/ajalt/mordant/rendering/Widget;Z)Lcom/github/ajalt/mordant/input/InteractiveSelectListBuilder;
public final fun addEntry (Ljava/lang/String;Lcom/github/ajalt/mordant/rendering/Widget;ZLjava/lang/String;)Lcom/github/ajalt/mordant/input/InteractiveSelectListBuilder;
public final fun addEntry (Ljava/lang/String;Ljava/lang/String;)Lcom/github/ajalt/mordant/input/InteractiveSelectListBuilder;
public final fun addEntry (Ljava/lang/String;Ljava/lang/String;Z)Lcom/github/ajalt/mordant/input/InteractiveSelectListBuilder;
public static synthetic fun addEntry$default (Lcom/github/ajalt/mordant/input/InteractiveSelectListBuilder;Ljava/lang/String;Lcom/github/ajalt/mordant/rendering/Widget;ZILjava/lang/Object;)Lcom/github/ajalt/mordant/input/InteractiveSelectListBuilder;
public static synthetic fun addEntry$default (Lcom/github/ajalt/mordant/input/InteractiveSelectListBuilder;Ljava/lang/String;Ljava/lang/String;ZILjava/lang/Object;)Lcom/github/ajalt/mordant/input/InteractiveSelectListBuilder;
public final fun addEntry (Ljava/lang/String;Ljava/lang/String;ZLjava/lang/String;)Lcom/github/ajalt/mordant/input/InteractiveSelectListBuilder;
public static synthetic fun addEntry$default (Lcom/github/ajalt/mordant/input/InteractiveSelectListBuilder;Ljava/lang/String;Lcom/github/ajalt/mordant/rendering/Widget;ZLjava/lang/String;ILjava/lang/Object;)Lcom/github/ajalt/mordant/input/InteractiveSelectListBuilder;
public static synthetic fun addEntry$default (Lcom/github/ajalt/mordant/input/InteractiveSelectListBuilder;Ljava/lang/String;Ljava/lang/String;ZLjava/lang/String;ILjava/lang/Object;)Lcom/github/ajalt/mordant/input/InteractiveSelectListBuilder;
public final fun clearOnExit (Z)Lcom/github/ajalt/mordant/input/InteractiveSelectListBuilder;
public final fun createMultiSelectInputAnimation ()Lcom/github/ajalt/mordant/input/InputReceiverAnimation;
public final fun createSingleSelectInputAnimation ()Lcom/github/ajalt/mordant/input/InputReceiverAnimation;
Expand Down Expand Up @@ -436,10 +440,6 @@ public final class com/github/ajalt/mordant/rendering/Line : java/util/List, kot
public synthetic fun add (Ljava/lang/Object;)Z
public fun addAll (ILjava/util/Collection;)Z
public fun addAll (Ljava/util/Collection;)Z
public fun addFirst (Lcom/github/ajalt/mordant/rendering/Span;)V
public synthetic fun addFirst (Ljava/lang/Object;)V
public fun addLast (Lcom/github/ajalt/mordant/rendering/Span;)V
public synthetic fun addLast (Ljava/lang/Object;)V
public fun clear ()V
public final fun component1 ()Ljava/util/List;
public final fun component2 ()Lcom/github/ajalt/mordant/rendering/TextStyle;
Expand Down Expand Up @@ -467,10 +467,6 @@ public final class com/github/ajalt/mordant/rendering/Line : java/util/List, kot
public synthetic fun remove (I)Ljava/lang/Object;
public fun remove (Ljava/lang/Object;)Z
public fun removeAll (Ljava/util/Collection;)Z
public fun removeFirst ()Lcom/github/ajalt/mordant/rendering/Span;
public synthetic fun removeFirst ()Ljava/lang/Object;
public fun removeLast ()Lcom/github/ajalt/mordant/rendering/Span;
public synthetic fun removeLast ()Ljava/lang/Object;
public fun replaceAll (Ljava/util/function/UnaryOperator;)V
public fun retainAll (Ljava/util/Collection;)Z
public fun set (ILcom/github/ajalt/mordant/rendering/Span;)Lcom/github/ajalt/mordant/rendering/Span;
Expand Down Expand Up @@ -1573,19 +1569,26 @@ public final class com/github/ajalt/mordant/widgets/SelectList : com/github/ajal
}

public final class com/github/ajalt/mordant/widgets/SelectList$Entry {
public fun <init> (Ljava/lang/String;)V
public fun <init> (Ljava/lang/String;Lcom/github/ajalt/mordant/rendering/Widget;)V
public fun <init> (Ljava/lang/String;Lcom/github/ajalt/mordant/rendering/Widget;Z)V
public synthetic fun <init> (Ljava/lang/String;Lcom/github/ajalt/mordant/rendering/Widget;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Lcom/github/ajalt/mordant/rendering/Widget;ZLjava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;Lcom/github/ajalt/mordant/rendering/Widget;ZLjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Z)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;ZLjava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;ZLjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()Lcom/github/ajalt/mordant/rendering/Widget;
public final fun component3 ()Z
public final fun copy (Ljava/lang/String;Lcom/github/ajalt/mordant/rendering/Widget;Z)Lcom/github/ajalt/mordant/widgets/SelectList$Entry;
public static synthetic fun copy$default (Lcom/github/ajalt/mordant/widgets/SelectList$Entry;Ljava/lang/String;Lcom/github/ajalt/mordant/rendering/Widget;ZILjava/lang/Object;)Lcom/github/ajalt/mordant/widgets/SelectList$Entry;
public final fun component4 ()Ljava/lang/String;
public final fun copy (Ljava/lang/String;Lcom/github/ajalt/mordant/rendering/Widget;ZLjava/lang/String;)Lcom/github/ajalt/mordant/widgets/SelectList$Entry;
public static synthetic fun copy$default (Lcom/github/ajalt/mordant/widgets/SelectList$Entry;Ljava/lang/String;Lcom/github/ajalt/mordant/rendering/Widget;ZLjava/lang/String;ILjava/lang/Object;)Lcom/github/ajalt/mordant/widgets/SelectList$Entry;
public fun equals (Ljava/lang/Object;)Z
public final fun getDescription ()Lcom/github/ajalt/mordant/rendering/Widget;
public final fun getSelected ()Z
public final fun getTitle ()Ljava/lang/String;
public final fun getValue ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import kotlin.jvm.JvmName
/**
* Display a list of items and allow the user to select one with the arrow keys and enter.
*
* @return the selected item title, or null if the user canceled the selection
* @return the selected item value, or title if value is not specified, or null if the user canceled the selection
*/
inline fun Terminal.interactiveSelectList(
block: InteractiveSelectListBuilder.() -> Unit,
Expand Down Expand Up @@ -41,7 +41,7 @@ fun Terminal.interactiveSelectList(
*
* @param entries The list of items to select from
* @param title The title to display above the list
* @return the selected item title, or null if the user canceled the selection
* @return the selected item value, or title if value is not specified, or null if the user canceled the selection
*/
@JvmName("interactiveSelectListEntry")
fun Terminal.interactiveSelectList(
Expand All @@ -57,7 +57,7 @@ fun Terminal.interactiveSelectList(
/**
* Display a list of items and allow the user to select zero or more with the arrow keys and enter.
*
* @return the selected item titles, or null if the user canceled the selection
* @return the selected item value, or title if value is not specified, or null if the user canceled the selection
*/
inline fun Terminal.interactiveMultiSelectList(
block: InteractiveSelectListBuilder.() -> Unit,
Expand All @@ -73,7 +73,7 @@ inline fun Terminal.interactiveMultiSelectList(
*
* @param entries The list of items to select from
* @param title The title to display above the list
* @return the selected item titles, or null if the user canceled the selection
* @return the selected item value, or title if value is not specified, or null if the user canceled the selection
*/
@JvmName("interactiveMultiSelectListEntry")
fun Terminal.interactiveMultiSelectList(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import com.github.ajalt.mordant.terminal.Terminal
import com.github.ajalt.mordant.widgets.SelectList
import com.github.ajalt.mordant.widgets.Text
import kotlin.jvm.JvmName
import kotlin.jvm.JvmOverloads

private class SelectConfig(
var entries: MutableList<SelectList.Entry> = mutableListOf(),
Expand Down Expand Up @@ -77,33 +78,32 @@ class InteractiveSelectListBuilder(private val terminal: Terminal) {
}

/** Add an item to the list of items to select from */
@JvmOverloads
fun addEntry(
mikaelstaldal marked this conversation as resolved.
Show resolved Hide resolved
title: String,
description: String,
selected: Boolean = false,
value: String? = null,
): InteractiveSelectListBuilder = apply {
config.entries += SelectList.Entry(title, description, selected)
config.entries += SelectList.Entry(title, description, selected, value)
}

/** Add an item to the list of items to select from */
@JvmOverloads
fun addEntry(
title: String,
description: Widget? = null,
selected: Boolean = false,
value: String? = null
): InteractiveSelectListBuilder = apply {
config.entries += SelectList.Entry(title, description, selected)
config.entries += SelectList.Entry(title, description, selected, value)
}

/** Add an item to the list of items to select from */
fun addEntry(entry: SelectList.Entry): InteractiveSelectListBuilder = apply {
config.entries += entry
}

/** Add an item to the list of items to select from */
fun addEntry(entry: String): InteractiveSelectListBuilder = apply {
config.entries += SelectList.Entry(entry)
}

/** Set the title to display above the list */
fun title(title: String): InteractiveSelectListBuilder = apply {
config.title = when {
Expand Down Expand Up @@ -386,10 +386,10 @@ private class SelectInputAnimation(
}

key == keySubmit -> {
if (singleSelect) copy(finished = true, result = listOf(entry.title))
if (singleSelect) copy(finished = true, result = listOf(entry.value ?: entry.title))
else copy(
finished = true,
result = items.filter { it.selected }.map { it.title })
result = items.filter { it.selected }.map { it.value ?: it.title })
}

filtering && !key.alt && !key.ctrl -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.github.ajalt.mordant.table.Borders
import com.github.ajalt.mordant.table.table
import com.github.ajalt.mordant.table.verticalLayout
import com.github.ajalt.mordant.terminal.Terminal
import kotlin.jvm.JvmOverloads

/**
* A list widget with selectable items.
Expand Down Expand Up @@ -56,19 +57,23 @@ class SelectList private constructor(
unselectedMarkerStyle = ThemeStyle.of("select.unselected-marker", unselectedMarkerStyle),
)

data class Entry(
data class Entry @JvmOverloads constructor(
/** The title of the entry. */
val title: String,
/** An optional description of the entry. */
val description: Widget? = null,
/** Whether this entry is marked as selected. */
val selected: Boolean = false,
/** Return this value instead of title if not null. */
val value: String? = null,
) {
constructor(title: String, description: String?, selected: Boolean = false)
@JvmOverloads
constructor(title: String, description: String?, selected: Boolean = false, value: String? = null)
mikaelstaldal marked this conversation as resolved.
Show resolved Hide resolved
: this(
title = title,
description = description?.let { Text(it, whitespace = Whitespace.PRE_WRAP) },
selected = selected
selected = selected,
value = value
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,44 +16,54 @@ class InteractiveSelectListTest {
private val t = Terminal(terminalInterface = rec)

@[Test JsName("single_select_strings")]
fun `single select strings`() = doSingleSelectTest {
fun `single select strings`() = doSingleSelectTest({
t.interactiveSelectList(listOf("a", "b", "c"))
}
}, "b")

@[Test JsName("single_select_entries")]
fun `single select entries`() = doSingleSelectTest {
fun `single select entries`() = doSingleSelectTest({
t.interactiveSelectList(listOf(Entry("a"), Entry("b"), Entry("c")))
}
}, "b")

@[Test JsName("single_select_entries_with_value")]
fun `single select entries with value`() = doSingleSelectTest({
t.interactiveSelectList(listOf(Entry("a", value = "AA"), Entry("b", value = "BB"), Entry("c", value = "CC")))
}, "BB")

@[Test JsName("multi_select_strings")]
fun `multi select strings`() = doMultiSelectTest {
fun `multi select strings`() = doMultiSelectTest({
t.interactiveMultiSelectList(listOf("a", "b", "c"))
}
}, listOf("b", "c"))

@[Test JsName("multi_select_entries")]
fun `multi select entries`() = doMultiSelectTest {
fun `multi select entries`() = doMultiSelectTest({
t.interactiveMultiSelectList(listOf(Entry("a"), Entry("b"), Entry("c")))
}
}, listOf("b", "c"))

@[Test JsName("multi_select_entries_with_values")]
fun `multi select entries with values`() = doMultiSelectTest({
t.interactiveMultiSelectList(listOf(Entry("a", value = "AA"), Entry("b", value = "BB"), Entry("c", value = "CC")))
}, listOf("BB", "CC"))

private fun doSingleSelectTest(runList: () -> String?) {
private fun doSingleSelectTest(runList: () -> String?, expected: String) {
rec.inputEvents = mutableListOf(KeyboardEvent("ArrowDown"), KeyboardEvent("Enter"))
runList() shouldBe "b"
runList() shouldBe expected
rec.stdout() shouldContain """
░❯ a
░ b
░ c
""".trimMargin("░")
}

private fun doMultiSelectTest(runList: () -> List<String>?) {
private fun doMultiSelectTest(runList: () -> List<String>?, expected: List<String>) {
rec.inputEvents = mutableListOf(
KeyboardEvent("ArrowDown"),
KeyboardEvent("x"),
KeyboardEvent("ArrowDown"),
KeyboardEvent("x"),
KeyboardEvent("Enter"),
)
runList() shouldBe listOf("b", "c")
runList() shouldBe expected
rec.stdout() shouldContain """
░❯ • a
░ • b
Expand Down
Loading