src.elv.sh@v0.21.0-dev.0.20240515223629-06979efb9a2a/pkg/cli/tk/combobox.go (about)

     1  package tk
     2  
     3  import (
     4  	"src.elv.sh/pkg/cli/term"
     5  )
     6  
     7  // ComboBox is a Widget that combines a ListBox and a CodeArea.
     8  type ComboBox interface {
     9  	Widget
    10  	// Returns the embedded codearea widget.
    11  	CodeArea() CodeArea
    12  	// Returns the embedded listbox widget.
    13  	ListBox() ListBox
    14  	// Forces the filtering to rerun.
    15  	Refilter()
    16  }
    17  
    18  // ComboBoxSpec specifies the configuration and initial state for ComboBox.
    19  type ComboBoxSpec struct {
    20  	CodeArea CodeAreaSpec
    21  	ListBox  ListBoxSpec
    22  	OnFilter func(ComboBox, string)
    23  }
    24  
    25  type comboBox struct {
    26  	codeArea CodeArea
    27  	listBox  ListBox
    28  	OnFilter func(ComboBox, string)
    29  
    30  	// Last filter value.
    31  	lastFilter string
    32  }
    33  
    34  // NewComboBox creates a new ComboBox from the given spec.
    35  func NewComboBox(spec ComboBoxSpec) ComboBox {
    36  	if spec.OnFilter == nil {
    37  		spec.OnFilter = func(ComboBox, string) {}
    38  	}
    39  	w := &comboBox{
    40  		codeArea: NewCodeArea(spec.CodeArea),
    41  		listBox:  NewListBox(spec.ListBox),
    42  		OnFilter: spec.OnFilter,
    43  	}
    44  	w.OnFilter(w, "")
    45  	return w
    46  }
    47  
    48  // Render renders the codearea and the listbox below it.
    49  func (w *comboBox) Render(width, height int) *term.Buffer {
    50  	buf := w.codeArea.Render(width, height)
    51  	bufListBox := w.listBox.Render(width, height-len(buf.Lines))
    52  	buf.Extend(bufListBox, false)
    53  	return buf
    54  }
    55  
    56  func (w *comboBox) MaxHeight(width, height int) int {
    57  	return w.codeArea.MaxHeight(width, height) + w.listBox.MaxHeight(width, height)
    58  }
    59  
    60  // Handle first lets the listbox handle the event, and if it is unhandled, lets
    61  // the codearea handle it. If the codearea has handled the event and the code
    62  // content has changed, it calls OnFilter with the new content.
    63  func (w *comboBox) Handle(event term.Event) bool {
    64  	if w.listBox.Handle(event) {
    65  		return true
    66  	}
    67  	if w.codeArea.Handle(event) {
    68  		filter := w.codeArea.CopyState().Buffer.Content
    69  		if filter != w.lastFilter {
    70  			w.OnFilter(w, filter)
    71  			w.lastFilter = filter
    72  		}
    73  		return true
    74  	}
    75  	return false
    76  }
    77  
    78  func (w *comboBox) Refilter() {
    79  	w.OnFilter(w, w.codeArea.CopyState().Buffer.Content)
    80  }
    81  
    82  func (w *comboBox) CodeArea() CodeArea { return w.codeArea }
    83  func (w *comboBox) ListBox() ListBox   { return w.listBox }