fynetree

package module
v0.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 3, 2020 License: MIT Imports: 10 Imported by: 0

README

fynetree

This library provides a tree widget implementation that can be used in lieu of one being provided by the fyne framework itself.

I really enjoy using the framework and its accompanying tooling, but not being able to show data in a hierarchical view was a real blocker for me, so I decided to create a widget for this. Which really helped me understand what's going on under the covers more too. :)

Task list

  • Create base tree node widget with custom layout and dynamic event handling
  • Create tree node container
  • Provide InsertSorted method
  • Handle custom secondary tap menu and logic
  • Provide icon and text tap event hooks
  • Won't do for now Try out some selection model ideas
  • Possibly create factory methods to create leaf/branch nodes instead of setting leaf explicitly after creation

How to get it

It's a pure Go library, so using plain go get github.com/drognisep/fynetree will get you started.

How it's organized

The library is meant to follow (more or less) an MVVM structure, borrowing a lot from the base framework.

Model

The model is provided by the library consumer, and currently has very few requirements. More will be added as more interesting functionality comes online.

// TreeNodeModel is the interface to user defined data.
type TreeNodeModel interface {
	// GetIconResource should return the user defined icon resource to show in the view, or nil if no icon is needed.
	GetIconResource() fyne.Resource

	// GetText should return the user defined text to display for this node in the view, or "" if no text is needed.
	GetText() string
}

This optionally returns a fyne.Resource which is populated in the icon slot. If nil is passed then no icon will be rendered. The GetText method just returns the string that should be shown for the tree entry.

To make simple things simple, a factory function is provided which creates a static model for situations where the icon/text will not be changing. nil values are accepted for either argument.

// NewStaticModel creates a TreeNodeModel with fixed values that never change.
func NewStaticModel(resource fyne.Resource, text string) TreeNodeModel {
	return &StaticNodeModel{
		Resource: resource,
		Text:     text,
	}
}

Of course, the static model type is also exposed in case there's a need to create or extend it directly.

type StaticNodeModel struct {
	Resource fyne.Resource
	Text     string
}
ViewModel and View

The fyne framework seems to want the Widget implementation to be the view model, while the WidgetRenderer defines the actual view logic. I follow this pattern by exposing a TreeNode type which keeps track of expanded/condensed and leaf/branch state, interfaces with the model, exposes event-based integration points, and manages the set of child nodes.

Nodes can be dynamically added/inserted/removed from any other node, nodes can be programmatically expanded or condensed, event receivers can be registered, and the icon/text data can be changed by the model with a node refresh.

// NodeEventHandler is a handler function for node events triggered by the view.
type NodeEventHandler func()

// TapEventHandler is a handler function for tap events triggered by the view.
type TapEventHandler func(pe *fyne.PointEvent)

// TreeNode holds a TreeNodeModel's position within the view.
type TreeNode struct {
	widget.BaseWidget
	*nodeList
	model             TreeNodeModel
	expanded          bool
	leaf              bool
	OnBeforeExpand    NodeEventHandler
	OnAfterCondense   NodeEventHandler
	OnTappedSecondary TapEventHandler
	OnIconTapped      TapEventHandler
	OnLabelTapped     TapEventHandler

	mux      sync.Mutex
	parent   *TreeNode
}

The view is completely defined by the renderer. This keeps view behavior and complexity neatly hidden behind the widget state facade.

How to use it

Root tree nodes can be added to a TreeContainer to keep them together to be added to a view once. This type extends the base fyne scroll container to ensure that the tree is flexible enough to add to most any consumer view without compromising layout expectations.

// This is an excerpt from the example app. Clone the project to try it out.

/* ... */
// Create a ready-made container
treeContainer := fynetree.NewTreeContainer()
// Used to make a node and model at the same time
rootModel := fynetree.NewStaticBoundModel(theme.FolderOpenIcon(), "Tasks")
// Or created separately with a provided model
notesNode := fynetree.NewTreeNode(fynetree.NewStaticModel(theme.FolderIcon(), "Notes"))
createPopupFunc := func(msg string) func() {
    return func() {
        dialog.ShowInformation("Hello", msg, win)
    }
}
// Task defined elsewhere
exampleTask := &example.Task{
    Summary:     "Hello!",
    Description: "This is an example Task",
    Menu:        fyne.NewMenu("", fyne.NewMenuItem("Say Hello", createPopupFunc("Hello from a popup menu!"))),
}
// Factory methods for creating leaf/branch nodes, can be easily changed later
exampleNode := fynetree.NewLeafTreeNode(exampleTask)
// General event handler
exampleNode.OnTappedSecondary = func(pe *fyne.PointEvent) {
    canvas := fyne.CurrentApp().Driver().CanvasForObject(exampleNode)
    widget.ShowPopUpMenuAtPosition(exampleTask.Menu, canvas, pe.AbsolutePosition)
}
exampleNode.OnDoubleTapped = func(pe *fyne.PointEvent) { createPopupFunc("Hello from node double-tapped")() }
// Icon tap event handler
exampleNode.OnIconTapped = func(pe *fyne.PointEvent) { createPopupFunc("Hello from icon tapped!")() }
// This would block the node double-tapped event
// exampleNode.OnLabelTapped = func(pe *fyne.PointEvent) { createPopupFunc("Hello from label tapped!")() }
_ = rootModel.Node.Append(exampleNode)
_ = treeContainer.Append(rootModel.Node)
_ = treeContainer.Append(notesNode)
/* ... */

And that's about it! More features are planned, so check back often. If you see an opportunity for improvement, please create an issue detailing what you want to see.

Documentation

Index

Constants

View Source
const (
	HierarchyPadding = 24
)

Variables

This section is empty.

Functions

func InitTreeNode

func InitTreeNode(newNode *TreeNode, model TreeNodeModel)

InitTreeNode initializes the given tree node with the given model. If newNode is nil, then a new one will be created.

func NewExpandHandle

func NewExpandHandle(node *TreeNode) *expandHandle

Types

type NodeEventHandler

type NodeEventHandler func()

NodeEventHandler is a handler function for node events triggered by the view.

type StaticNodeModel

type StaticNodeModel struct {
	Resource fyne.Resource
	Text     string
	Node     *TreeNode
}

func NewStaticBoundModel

func NewStaticBoundModel(resource fyne.Resource, text string) *StaticNodeModel

NewStaticBoundModel creates a TreeNode and StaticNodeModel at the same time, and returns the bound model.

func NewStaticModel

func NewStaticModel(resource fyne.Resource, text string) *StaticNodeModel

NewStaticModel creates a TreeNodeModel with fixed values that never change.

func (*StaticNodeModel) GetIconResource

func (s *StaticNodeModel) GetIconResource() fyne.Resource

func (*StaticNodeModel) GetText

func (s *StaticNodeModel) GetText() string

func (*StaticNodeModel) SetTreeNode

func (s *StaticNodeModel) SetTreeNode(node *TreeNode)

type TapEventHandler

type TapEventHandler func(pe *fyne.PointEvent)

TapEventHandler is a handler function for tap events triggered by the view.

type TreeContainer

type TreeContainer struct {
	widget.BaseWidget

	Background color.Color
	// contains filtered or unexported fields
}

TreeContainer widget simplifies display of several root tree nodes.

func NewTreeContainer

func NewTreeContainer() *TreeContainer

func (TreeContainer) Append

func (n TreeContainer) Append(node *TreeNode) error

Append adds a node to the end of the Objects.

func (*TreeContainer) CreateRenderer added in v0.0.2

func (t *TreeContainer) CreateRenderer() fyne.WidgetRenderer

func (TreeContainer) IndexOf

func (n TreeContainer) IndexOf(node *TreeNode) int

IndexOf returns the index of the given node in the list if it's present, -1 otherwise.

func (TreeContainer) InsertAt

func (n TreeContainer) InsertAt(position int, node *TreeNode) error

InsertAt a new TreeNode at the given position as a child of this Objects.

func (TreeContainer) InsertSorted

func (n TreeContainer) InsertSorted(node *TreeNode) error

func (TreeContainer) Len

func (n TreeContainer) Len() int

func (*TreeContainer) NumRoots

func (t *TreeContainer) NumRoots() int

func (*TreeContainer) Refresh

func (t *TreeContainer) Refresh()

func (TreeContainer) Remove

func (n TreeContainer) Remove(node *TreeNode) (removedNode fyne.CanvasObject, err error)

Remove searches for the given node to remove and return it if it exists, returns nil and an error otherwise.

func (TreeContainer) RemoveAt

func (n TreeContainer) RemoveAt(position int) (removedNode fyne.CanvasObject, err error)

Remove the child node at the given position and return it. An error is returned if the index is invalid or the node is not found.

type TreeNode

type TreeNode struct {
	widget.BaseWidget

	OnBeforeExpand    NodeEventHandler
	OnAfterCondense   NodeEventHandler
	OnTappedSecondary TapEventHandler
	OnIconTapped      TapEventHandler
	OnLabelTapped     TapEventHandler
	OnTapped          TapEventHandler
	OnDoubleTapped    TapEventHandler
	// contains filtered or unexported fields
}

TreeNode holds a TreeNodeModel's position within the view.

func NewBranchTreeNode

func NewBranchTreeNode(model TreeNodeModel) *TreeNode

func NewLeafTreeNode

func NewLeafTreeNode(model TreeNodeModel) *TreeNode

func NewTreeNode

func NewTreeNode(model TreeNodeModel) *TreeNode

NewTreeNode constructs a tree node with the given model.

func (TreeNode) Append

func (n TreeNode) Append(node *TreeNode) error

Append adds a node to the end of the Objects.

func (*TreeNode) Condense

func (n *TreeNode) Condense()

Condense condenses the node and triggers the AfterCondense hook in the model if it's a branch and not already condensed.

func (*TreeNode) CreateRenderer

func (n *TreeNode) CreateRenderer() fyne.WidgetRenderer

func (*TreeNode) DoubleTapped

func (n *TreeNode) DoubleTapped(pe *fyne.PointEvent)

func (*TreeNode) Expand

func (n *TreeNode) Expand()

Expand expands the node and triggers the OnBeforeExpand hook in the model if it's a branch and not already expanded.

func (*TreeNode) GetModelIconResource

func (n *TreeNode) GetModelIconResource() fyne.Resource

GetModelIconResource gets the icon for this node.

func (*TreeNode) GetModelText

func (n *TreeNode) GetModelText() string

GetModelText gets the text for this node.

func (*TreeNode) GetParent

func (n *TreeNode) GetParent() *TreeNode

GetParent gets the parent node, or nil if this is a root node.

func (TreeNode) IndexOf

func (n TreeNode) IndexOf(node *TreeNode) int

IndexOf returns the index of the given node in the list if it's present, -1 otherwise.

func (TreeNode) InsertAt

func (n TreeNode) InsertAt(position int, node *TreeNode) error

InsertAt a new TreeNode at the given position as a child of this Objects.

func (TreeNode) InsertSorted

func (n TreeNode) InsertSorted(node *TreeNode) error

func (*TreeNode) IsBranch

func (n *TreeNode) IsBranch() bool

IsBranch returns whether this is a branch node.

func (*TreeNode) IsCondensed

func (n *TreeNode) IsCondensed() bool

IsCondensed returns whether this node is condensed down and child nodes are not shown.

func (*TreeNode) IsExpanded

func (n *TreeNode) IsExpanded() bool

IsExpanded returns whether this node is expanded.

func (*TreeNode) IsLeaf

func (n *TreeNode) IsLeaf() bool

IsLeaf returns whether this is a leaf node.

func (TreeNode) Len

func (n TreeNode) Len() int

func (*TreeNode) NumChildren

func (n *TreeNode) NumChildren() int

NumChildren returns how many child nodes this node has.

func (TreeNode) Remove

func (n TreeNode) Remove(node *TreeNode) (removedNode fyne.CanvasObject, err error)

Remove searches for the given node to remove and return it if it exists, returns nil and an error otherwise.

func (TreeNode) RemoveAt

func (n TreeNode) RemoveAt(position int) (removedNode fyne.CanvasObject, err error)

Remove the child node at the given position and return it. An error is returned if the index is invalid or the node is not found.

func (*TreeNode) SetBranch

func (n *TreeNode) SetBranch()

SetBranch sets this node to a branch node.

func (*TreeNode) SetLeaf

func (n *TreeNode) SetLeaf()

SetLeaf sets this node to a leaf node.

func (*TreeNode) TappedSecondary

func (n *TreeNode) TappedSecondary(pe *fyne.PointEvent)

func (*TreeNode) ToggleExpand

func (n *TreeNode) ToggleExpand()

ToggleExpand toggles the expand state of the node.

type TreeNodeModel

type TreeNodeModel interface {
	// GetIconResource should return the user defined icon Resource to show in the view, or nil if no icon is needed.
	GetIconResource() fyne.Resource

	// GetText should return the user defined Text to display for this node in the view, or "" if no Text is needed.
	GetText() string

	// SetTreeNode is called by node initialization when the model is bound to the node.
	SetTreeNode(node *TreeNode)
}

TreeNodeModel is the interface to user defined data.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL