Skip to content

Commit

Permalink
feat: custom select/confirm key for interactive printer
Browse files Browse the repository at this point in the history
  • Loading branch information
jochil committed Sep 12, 2022
1 parent bf7e9dd commit 8913607
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 18 deletions.
29 changes: 29 additions & 0 deletions _examples/interactive_multiselect/demo_custom/README.md
@@ -0,0 +1,29 @@
# interactive_multiselect/demo_custom

![Animation](animation.svg)

```go
package main

import (
"fmt"

"atomicgo.dev/keyboard/keys"
"github.com/pterm/pterm"
)

func main() {
var options []string

for i := 0; i < 5; i++ {
options = append(options, fmt.Sprintf("Option %d", i))
}

printer := pterm.DefaultInteractiveMultiselect.WithOptions(options)
printer.NoFilter = true
printer.KeyConfirm = keys.Enter
printer.KeySelect = keys.Space
selectedOptions, _ := printer.Show()
pterm.Info.Printfln("Selected options: %s", pterm.Green(selectedOptions))
}
```
10 changes: 10 additions & 0 deletions _examples/interactive_multiselect/demo_custom/animation.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 31 additions & 0 deletions _examples/interactive_multiselect/demo_custom/ci.go
@@ -0,0 +1,31 @@
package main

import (
"os"
"time"

"atomicgo.dev/keyboard"
"atomicgo.dev/keyboard/keys"
)

// ------ Automation for CI ------
// You can ignore this function, it is used to automatically run the demo and generate the example animation in our CI system.
func init() {
if os.Getenv("CI") == "true" {
go func() {
time.Sleep(time.Second)
keyboard.SimulateKeyPress(keys.Down)
time.Sleep(time.Millisecond * 100)
keyboard.SimulateKeyPress(keys.Space)

time.Sleep(time.Millisecond * 300)

keyboard.SimulateKeyPress(keys.Down)
time.Sleep(time.Millisecond * 100)
keyboard.SimulateKeyPress(keys.Space)

time.Sleep(time.Millisecond * 300)
keyboard.SimulateKeyPress(keys.Enter)
}()
}
}
23 changes: 23 additions & 0 deletions _examples/interactive_multiselect/demo_custom/main.go
@@ -0,0 +1,23 @@
package main

import (
"fmt"

"atomicgo.dev/keyboard/keys"
"github.com/pterm/pterm"
)

func main() {
var options []string

for i := 0; i < 5; i++ {
options = append(options, fmt.Sprintf("Option %d", i))
}

printer := pterm.DefaultInteractiveMultiselect.WithOptions(options)
printer.NoFilter = true
printer.KeyConfirm = keys.Enter
printer.KeySelect = keys.Space
selectedOptions, _ := printer.Show()
pterm.Info.Printfln("Selected options: %s", pterm.Green(selectedOptions))
}
59 changes: 41 additions & 18 deletions interactive_multiselect_printer.go
Expand Up @@ -23,6 +23,8 @@ var (
Selector: ">",
SelectorStyle: &ThemeDefault.SecondaryStyle,
Filter: true,
KeySelect: keys.Enter,
KeyConfirm: keys.Tab,
}
)

Expand All @@ -46,6 +48,9 @@ type InteractiveMultiselectPrinter struct {
displayedOptions []string
displayedOptionsStart int
displayedOptionsEnd int

KeySelect keys.KeyCode
KeyConfirm keys.KeyCode
}

// WithOptions sets the options.
Expand Down Expand Up @@ -78,6 +83,18 @@ func (p InteractiveMultiselectPrinter) WithFilter(filter bool) *InteractiveMulti
return &p
}

// WithKeySelect sets the confirm key
func (p InteractiveMultiselectPrinter) WithKeySelect(keySelect keys.KeyCode) *InteractiveMultiselectPrinter {
p.KeySelect = keySelect
return &p
}

// WithKeyConfirm sets the confirm key
func (p InteractiveMultiselectPrinter) WithKeyConfirm(keyConfirm keys.KeyCode) *InteractiveMultiselectPrinter {
p.KeyConfirm = keyConfirm
return &p
}

// Show shows the interactive multiselect menu and returns the selected entry.
func (p *InteractiveMultiselectPrinter) Show(text ...string) ([]string, error) {
// should be the first defer statement to make sure it is executed last
Expand Down Expand Up @@ -119,6 +136,10 @@ func (p *InteractiveMultiselectPrinter) Show(text ...string) ([]string, error) {
return nil, fmt.Errorf("could not start area: %w", err)
}

if p.Filter && (p.KeyConfirm == keys.Space || p.KeySelect == keys.Space) {
return nil, fmt.Errorf("if filter/search is active, keys.Space can not be used for KeySelect or KeyConfirm")
}

area.Update(p.renderSelectMenu())

cursor.Hide()
Expand All @@ -133,6 +154,18 @@ func (p *InteractiveMultiselectPrinter) Show(text ...string) ([]string, error) {
}

switch key {
case p.KeyConfirm:
if len(p.fuzzySearchMatches) == 0 {
return false, nil
}
area.Update(p.renderFinishedMenu())
return true, nil
case p.KeySelect:
if len(p.fuzzySearchMatches) > 0 {
// Select option if not already selected
p.selectOption(p.fuzzySearchMatches[p.selectedOption])
}
area.Update(p.renderSelectMenu())
case keys.RuneKey:
if p.Filter {
// Fuzzy search for options
Expand All @@ -144,16 +177,12 @@ func (p *InteractiveMultiselectPrinter) Show(text ...string) ([]string, error) {
p.displayedOptions = append([]string{}, p.fuzzySearchMatches[:maxHeight]...)
}
area.Update(p.renderSelectMenu())
case keys.Tab:
if len(p.fuzzySearchMatches) == 0 {
return false, nil
}
area.Update(p.renderFinishedMenu())
return true, nil
case keys.Space:
p.fuzzySearchString += " "
p.selectedOption = 0
area.Update(p.renderSelectMenu())
if p.Filter {
p.fuzzySearchString += " "
p.selectedOption = 0
area.Update(p.renderSelectMenu())
}
case keys.Backspace:
// Remove last character from fuzzy search string
if len(p.fuzzySearchString) > 0 {
Expand Down Expand Up @@ -236,12 +265,6 @@ func (p *InteractiveMultiselectPrinter) Show(text ...string) ([]string, error) {
case keys.CtrlC:
cancel()
return true, nil
case keys.Enter:
if len(p.fuzzySearchMatches) > 0 {
// Select option if not already selected
p.selectOption(p.fuzzySearchMatches[p.selectedOption])
}
area.Update(p.renderSelectMenu())
}

return false, nil
Expand Down Expand Up @@ -333,11 +356,11 @@ func (p *InteractiveMultiselectPrinter) renderSelectMenu() string {
}
}

help := fmt.Sprintf("%s: %s | %s: %s | left: %s | right: %s", p.KeySelect, Bold.Sprint("select"), p.KeyConfirm, Bold.Sprint("confirm"), Bold.Sprint("none"), Bold.Sprint("all"))
if p.Filter {
content += ThemeDefault.SecondaryStyle.Sprintfln("enter: %s | tab: %s | left: %s | right: %s | type to %s", Bold.Sprint("select"), Bold.Sprint("confirm"), Bold.Sprint("none"), Bold.Sprint("all"), Bold.Sprint("filter"))
} else {
content += ThemeDefault.SecondaryStyle.Sprintfln("enter: %s | tab: %s | left: %s | right: %s", Bold.Sprint("select"), Bold.Sprint("confirm"), Bold.Sprint("none"), Bold.Sprint("all"))
help += fmt.Sprintf("| type to %s", Bold.Sprint("filter"))
}
content += ThemeDefault.SecondaryStyle.Sprintfln(help)

return content
}
Expand Down

0 comments on commit 8913607

Please sign in to comment.