rivo / tview

Terminal UI library with rich, interactive widgets — written in Golang
MIT License
11.17k stars 574 forks source link

List.RemoveItem() #898

Closed daniel-p-h closed 1 year ago

daniel-p-h commented 1 year ago

Hi, I've just started toying with go so this might be my bad code, or a side effect of language features I don't understand.

When trying to put together a dynamic list the behaviour I'm experiencing is very unusal. I'm not entirely sure what it's doing to be honest, but it's not what I expected which is for items to be removed / added as per the result of strings.Contains(item, searchQuery).

Example code:

package main

import (
    "strconv"
    "strings"

    "github.com/gdamore/tcell/v2"
    "github.com/rivo/tview"
)

var (
    app        = tview.NewApplication()
    flex       = tview.NewFlex()
    testList   = tview.NewList()
    testSearch = tview.NewInputField()
    testOutput = tview.NewTextView()

    removedItems []string
)

func main() {

    testList.
        ShowSecondaryText(false).
        SetBorder(true).
        SetTitle("items")

    testList.AddItem("a", "", 0, nil)
    testList.AddItem("ants", "", 0, nil)
    testList.AddItem("antelope", "", 0, nil)
    testList.AddItem("assess", "", 0, nil)
    testList.AddItem("bears", "", 0, nil)
    testList.AddItem("london", "", 0, nil)
    testList.AddItem("bigtown-not-chicago", "", 0, nil)
    testList.AddItem("some-other-type-of-example", "", 0, nil)

    testSearch.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
        search(event)
        return event
    })

    flex.SetDirection(tview.FlexRow)
    flex.AddItem(testSearch, 0, 1, true)
    flex.AddItem(testList, 0, 10, false)
    flex.AddItem(testOutput, 0, 1, false)

    if err := app.SetRoot(flex, true).EnableMouse(true).Run(); err != nil {
        panic(err)
    }
}

func search(key *tcell.EventKey) {
    length := len(testSearch.GetText())
    previousSearch := testSearch.GetText()
    searchVal := ""

    if key.Key() == tcell.KeyBS {
        searchVal = previousSearch[:length-1]
    } else if key.Key() == tcell.KeyRune {
        searchVal = previousSearch + string(key.Rune())
    } else {
        return
    }

    if searchVal == "" {
        return
    }

    for i := 0; i < testList.GetItemCount(); i++ {
        primary, _ := testList.GetItemText(i)
        testOutput.Write([]byte(primary + " == " + searchVal + ": " + strconv.FormatBool(strings.Contains(primary, searchVal)) + "\n"))
        if !strings.Contains(primary, searchVal) {
            removedItems = append(removedItems, primary)
            testList = testList.RemoveItem(i)
        }
    }
    j := 0
    for i := 0; i < len(removedItems); i++ {
        if len(searchVal) >= 2 && !strings.Contains(removedItems[i], searchVal) {
            testList = testList.AddItem(removedItems[i], "", 0, nil)
        } else {
            removedItems[j] = removedItems[i]
            j++
        }
    }
    removedItems = removedItems[:j]
}
daniel-p-h commented 1 year ago

Closed, my bad code! Logic for moving items back into the list is to blame

rivo commented 1 year ago

Ok. You may also want to think about using SetChangedFunc() instead of SetInputCapture(). SetInputCapture() is meant to intercept actual key stroke events while SetChangedFunc() handles all common key strokes for you and then simply informs you that the content of the InputField has changed.

daniel-p-h commented 1 year ago

Ok. You may also want to think about using SetChangedFunc() instead of SetInputCapture(). SetInputCapture() is meant to intercept actual key stroke events while SetChangedFunc() handles all common key strokes for you and then simply informs you that the content of the InputField has changed.

I did notice this after I posted and it works much better, but thanks for taking the time to read!