[GH-ISSUE #1004] Feature request: add GetSelectedFunc method for TreeView component #726

Closed
opened 2026-03-04 01:07:17 +03:00 by kerem · 1 comment
Owner

Originally created by @alekseysavchuk on GitHub (Jul 11, 2024).
Original GitHub issue: https://github.com/rivo/tview/issues/1004

I've created wrappers for some library components those change wrapped component's default behavior on input. This follows a decorator pattern. My logic handles some inputs while relying on the component's default behavior for others. I understand I could create my own components to achieve this but I'd like to keep doing it that way.

Most library components have a comprehensive interface. All setters have corresponding getters, for example, SetDrawFunc-GetDrawFunc, SetInputCapture-GetInputCapture, etc. However, some components do not follow this pattern. For instance, TreeView has SetSelectedFunc but lacks GetSelectedFunc.

Here's a minimal example of the wrapper I've created

type SubtreeDeleteWrapper struct {
	deletedNodes map[*tview.TreeNode]struct{}
}

func (w *SubtreeDeleteWrapper) Wrap(t *tview.TreeView) *tview.TreeView {
	// get a wrapped tree view's default input capture handler
	defaultCapture := t.GetInputCapture()

	// add some custom input capture for wrapped tree view
	t.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
		switch event.Rune() {
		case 'D': // delete a selected node and its subtree
			selectedNode := t.GetCurrentNode()
			if _, exists := w.deletedNodes[selectedNode]; exists {
				break
			}
			// some business logic goes here e.g. delete an entry in a database
 			// or only register that deletion so it could be canceled later
			
			// just collapse the selected node's subtree and mark it as deleted
			// but it could be something different, maybe more complicated
			selectedNode.SetExpanded(false)
			selectedNode.SetColor(tcell.ColorRed)
			w.deletedNodes[selectedNode] = struct{}{}
		case 'Z': // undo deletion
			selectedNode := t.GetCurrentNode()
			if _, exists := w.deletedNodes[selectedNode]; !exists {
				break
			}
			// ...
			selectedNode.SetColor(tcell.ColorGreen)
			delete(w.deletedNodes, selectedNode)
		default:
			// otherwise, call the default input capture handler
			if defaultCapture != nil {
				return defaultCapture(event)
			}
			return event
		}
		return nil
	})

	// here I want to get a tree view's default `SelectedFunc` but I can't
	var defaultSelectedFunc func(*tview.TreeNode) = nil // ???

	t.SetSelectedFunc(func(node *tview.TreeNode) {
		// if the selected node is deleted, do nothing on that selection
		if _, exists := w.deletedNodes[node]; exists {
			return
		}
		// otherwise call the default selection handler
		if defaultSelectedFunc != nil {
			defaultSelectedFunc(node)
		}
	})

	return t
}

and I use it that way

func main() {
	tree := tview.NewTreeView()
	// add tree nodes, etc
	// ...

	tree = util.NewSubtreeDeleteWrapper().Wrap(tree)
	// initialize and run an app
}
Originally created by @alekseysavchuk on GitHub (Jul 11, 2024). Original GitHub issue: https://github.com/rivo/tview/issues/1004 I've created wrappers for some library components those change wrapped component's default behavior on input. This follows a decorator pattern. My logic handles some inputs while relying on the component's default behavior for others. I understand I could create my own components to achieve this but I'd like to keep doing it that way. Most library components have a comprehensive interface. All setters have corresponding getters, for example, `SetDrawFunc`-`GetDrawFunc`, `SetInputCapture`-`GetInputCapture`, etc. However, some components do **not** follow this pattern. For instance, `TreeView` has `SetSelectedFunc` but lacks `GetSelectedFunc`. Here's a minimal example of the wrapper I've created ```go type SubtreeDeleteWrapper struct { deletedNodes map[*tview.TreeNode]struct{} } func (w *SubtreeDeleteWrapper) Wrap(t *tview.TreeView) *tview.TreeView { // get a wrapped tree view's default input capture handler defaultCapture := t.GetInputCapture() // add some custom input capture for wrapped tree view t.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { switch event.Rune() { case 'D': // delete a selected node and its subtree selectedNode := t.GetCurrentNode() if _, exists := w.deletedNodes[selectedNode]; exists { break } // some business logic goes here e.g. delete an entry in a database // or only register that deletion so it could be canceled later // just collapse the selected node's subtree and mark it as deleted // but it could be something different, maybe more complicated selectedNode.SetExpanded(false) selectedNode.SetColor(tcell.ColorRed) w.deletedNodes[selectedNode] = struct{}{} case 'Z': // undo deletion selectedNode := t.GetCurrentNode() if _, exists := w.deletedNodes[selectedNode]; !exists { break } // ... selectedNode.SetColor(tcell.ColorGreen) delete(w.deletedNodes, selectedNode) default: // otherwise, call the default input capture handler if defaultCapture != nil { return defaultCapture(event) } return event } return nil }) // here I want to get a tree view's default `SelectedFunc` but I can't var defaultSelectedFunc func(*tview.TreeNode) = nil // ??? t.SetSelectedFunc(func(node *tview.TreeNode) { // if the selected node is deleted, do nothing on that selection if _, exists := w.deletedNodes[node]; exists { return } // otherwise call the default selection handler if defaultSelectedFunc != nil { defaultSelectedFunc(node) } }) return t } ``` and I use it that way ```go func main() { tree := tview.NewTreeView() // add tree nodes, etc // ... tree = util.NewSubtreeDeleteWrapper().Wrap(tree) // initialize and run an app } ```
kerem closed this issue 2026-03-04 01:07:18 +03:00
Author
Owner

@rivo commented on GitHub (Aug 4, 2024):

This is fine. I left a minor request for your PR.

<!-- gh-comment-id:2267504910 --> @rivo commented on GitHub (Aug 4, 2024): This is fine. I left a minor request for your PR.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/tview#726
No description provided.