tree.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. // Copyright 2016 The G3N Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package gui
  5. import (
  6. "github.com/g3n/engine/math32"
  7. "github.com/g3n/engine/window"
  8. )
  9. // Tree is the tree structure GUI element.
  10. type Tree struct {
  11. List // Embedded list panel
  12. styles *TreeStyles // Pointer to styles
  13. }
  14. // TreeStyles contains the styling of all tree components for each valid GUI state.
  15. type TreeStyles struct {
  16. List *ListStyles // Styles for the embedded list
  17. Node *TreeNodeStyles // Styles for the node panel
  18. Padlevel float32 // Left padding indentation
  19. }
  20. // TreeNodeStyles contains a TreeNodeStyle for each valid GUI state.
  21. type TreeNodeStyles struct {
  22. Normal TreeNodeStyle
  23. }
  24. // TreeNodeStyle contains the styling of a TreeNode.
  25. type TreeNodeStyle struct {
  26. PanelStyle
  27. FgColor math32.Color4
  28. Icons [2]string
  29. }
  30. // TreeNode is a tree node.
  31. type TreeNode struct {
  32. Panel // Embedded panel
  33. label Label // Node label
  34. icon Label // Node icon
  35. tree *Tree // Parent tree
  36. parNode *TreeNode // Parent node
  37. items []IPanel // List of node items
  38. expanded bool // Node expanded flag
  39. }
  40. // NewTree creates and returns a pointer to a new tree widget.
  41. func NewTree(width, height float32) *Tree {
  42. t := new(Tree)
  43. t.Initialize(width, height)
  44. return t
  45. }
  46. // Initialize initializes the tree with the specified initial width and height
  47. // It is normally used when the folder is embedded in another object.
  48. func (t *Tree) Initialize(width, height float32) {
  49. t.List.initialize(true, width, height)
  50. t.SetStyles(&StyleDefault().Tree)
  51. t.List.Subscribe(OnKeyDown, t.onKey)
  52. t.List.Subscribe(OnKeyUp, t.onKey)
  53. t.List.Subscribe(OnCursor, t.onCursor)
  54. }
  55. // SetStyles sets the tree styles overriding the default style.
  56. func (t *Tree) SetStyles(s *TreeStyles) {
  57. t.styles = s
  58. t.List.SetStyles(t.styles.List)
  59. t.update()
  60. }
  61. // InsertAt inserts a child panel at the specified position in the tree.
  62. func (t *Tree) InsertAt(pos int, child IPanel) {
  63. t.List.InsertAt(pos, child)
  64. }
  65. // Add child panel to the end tree.
  66. func (t *Tree) Add(ichild IPanel) {
  67. t.List.Add(ichild)
  68. }
  69. // InsertNodeAt inserts at the specified position a new tree node
  70. // with the specified text at the end of this tree
  71. // and returns pointer to the new node.
  72. func (t *Tree) InsertNodeAt(pos int, text string) *TreeNode {
  73. n := newTreeNode(text, t, nil)
  74. n.update()
  75. n.recalc()
  76. t.List.InsertAt(pos, n)
  77. return n
  78. }
  79. // AddNode adds a new tree node with the specified text
  80. // at the end of this tree and returns a pointer to the new node.
  81. func (t *Tree) AddNode(text string) *TreeNode {
  82. n := newTreeNode(text, t, nil)
  83. n.update()
  84. n.recalc()
  85. t.List.Add(n)
  86. return n
  87. }
  88. // Remove removes the specified child from the tree or any
  89. // of its children nodes.
  90. func (t *Tree) Remove(child IPanel) {
  91. for idx := 0; idx < t.List.Len(); idx++ {
  92. curr := t.List.ItemAt(idx)
  93. if curr == child {
  94. node, ok := curr.(*TreeNode)
  95. if ok {
  96. node.remove()
  97. } else {
  98. t.List.Remove(child)
  99. }
  100. return
  101. }
  102. node, ok := curr.(*TreeNode)
  103. if ok {
  104. node.Remove(child)
  105. }
  106. }
  107. }
  108. // Selected returns the currently selected element or nil
  109. func (t *Tree) Selected() IPanel {
  110. sel := t.List.Selected()
  111. if len(sel) == 0 {
  112. return nil
  113. }
  114. return sel[0]
  115. }
  116. // FindChild searches for the specified child in the tree and
  117. // all its children. If found, returns the parent node and
  118. // its position relative to the parent.
  119. // If the parent is the tree returns nil as the parent
  120. // If not found returns nil and -1
  121. func (t *Tree) FindChild(child IPanel) (*TreeNode, int) {
  122. for idx := 0; idx < t.List.Len(); idx++ {
  123. curr := t.List.ItemAt(idx)
  124. if curr == child {
  125. return nil, idx
  126. }
  127. node, ok := curr.(*TreeNode)
  128. if ok {
  129. par, pos := node.FindChild(child)
  130. if pos >= 0 {
  131. return par, pos
  132. }
  133. }
  134. }
  135. return nil, -1
  136. }
  137. // onCursor receives subscribed cursor events over the tree
  138. func (t *Tree) onCursor(evname string, ev interface{}) {
  139. // Do not propagate any cursor events
  140. t.root.StopPropagation(StopAll)
  141. }
  142. // onKey receives key down events for the embedded list
  143. func (t *Tree) onKey(evname string, ev interface{}) {
  144. // Get selected item
  145. item := t.Selected()
  146. if item == nil {
  147. return
  148. }
  149. // If item is not a tree node, dispatch event to item
  150. node, ok := item.(*TreeNode)
  151. if !ok {
  152. item.SetRoot(t.root)
  153. item.GetPanel().Dispatch(evname, ev)
  154. return
  155. }
  156. // If not enter key pressed, ignore
  157. kev := ev.(*window.KeyEvent)
  158. if evname != OnKeyDown || kev.Keycode != window.KeyEnter {
  159. return
  160. }
  161. // Toggles the expansion state of the node
  162. node.expanded = !node.expanded
  163. node.update()
  164. node.updateItems()
  165. }
  166. //
  167. // TreeNode methods
  168. //
  169. // newTreeNode creates and returns a pointer to a new TreeNode with
  170. // the specified text, tree and parent node
  171. func newTreeNode(text string, tree *Tree, parNode *TreeNode) *TreeNode {
  172. n := new(TreeNode)
  173. n.Panel.Initialize(n, 0, 0)
  174. // Initialize node label
  175. n.label.initialize(text, StyleDefault().Font)
  176. n.Panel.Add(&n.label)
  177. // Create node icon
  178. n.icon.initialize("", StyleDefault().FontIcon)
  179. n.icon.SetFontSize(StyleDefault().Label.PointSize * 1.3)
  180. n.Panel.Add(&n.icon)
  181. // Subscribe to events
  182. n.Panel.Subscribe(OnMouseDown, n.onMouse)
  183. n.Panel.Subscribe(OnListItemResize, func(evname string, ev interface{}) {
  184. n.recalc()
  185. })
  186. n.tree = tree
  187. n.parNode = parNode
  188. n.update()
  189. n.recalc()
  190. return n
  191. }
  192. // Len returns the number of immediate children of this node
  193. func (n *TreeNode) Len() int {
  194. return len(n.items)
  195. }
  196. // SetExpanded sets the expanded state of this node
  197. func (n *TreeNode) SetExpanded(state bool) {
  198. n.expanded = state
  199. n.update()
  200. n.updateItems()
  201. }
  202. // FindChild searches for the specified child in this node and
  203. // all its children. If found, returns the parent node and
  204. // its position relative to the parent.
  205. // If not found returns nil and -1
  206. func (n *TreeNode) FindChild(child IPanel) (*TreeNode, int) {
  207. for pos, curr := range n.items {
  208. if curr == child {
  209. return n, pos
  210. }
  211. node, ok := curr.(*TreeNode)
  212. if ok {
  213. par, pos := node.FindChild(child)
  214. if par != nil {
  215. return par, pos
  216. }
  217. }
  218. }
  219. return nil, -1
  220. }
  221. // InsertAt inserts a child panel at the specified position in this node
  222. // If the position is invalid, the function panics
  223. func (n *TreeNode) InsertAt(pos int, child IPanel) {
  224. if pos < 0 || pos > len(n.items) {
  225. panic("TreeNode.InsertAt(): Invalid position")
  226. }
  227. // Insert item in the items array
  228. n.items = append(n.items, nil)
  229. copy(n.items[pos+1:], n.items[pos:])
  230. n.items[pos] = child
  231. if n.expanded {
  232. n.updateItems()
  233. }
  234. }
  235. // Add adds a child panel to this node
  236. func (n *TreeNode) Add(child IPanel) {
  237. n.InsertAt(n.Len(), child)
  238. }
  239. // InsertNodeAt inserts a new node at the specified position in this node
  240. // If the position is invalid, the function panics
  241. func (n *TreeNode) InsertNodeAt(pos int, text string) *TreeNode {
  242. if pos < 0 || pos > len(n.items) {
  243. panic("TreeNode.InsertNodeAt(): Invalid position")
  244. }
  245. childNode := newTreeNode(text, n.tree, n)
  246. // Insert item in the items array
  247. n.items = append(n.items, nil)
  248. copy(n.items[pos+1:], n.items[pos:])
  249. n.items[pos] = childNode
  250. if n.expanded {
  251. n.updateItems()
  252. }
  253. return childNode
  254. }
  255. // AddNode adds a new node to this one and return its pointer
  256. func (n *TreeNode) AddNode(text string) *TreeNode {
  257. return n.InsertNodeAt(n.Len(), text)
  258. }
  259. // Remove removes the specified child from this node or any
  260. // of its children nodes
  261. func (n *TreeNode) Remove(child IPanel) {
  262. for pos, curr := range n.items {
  263. if curr == child {
  264. copy(n.items[pos:], n.items[pos+1:])
  265. n.items[len(n.items)-1] = nil
  266. n.items = n.items[:len(n.items)-1]
  267. node, ok := curr.(*TreeNode)
  268. if ok {
  269. node.remove()
  270. } else {
  271. n.tree.List.Remove(curr)
  272. }
  273. n.updateItems()
  274. return
  275. }
  276. node, ok := curr.(*TreeNode)
  277. if ok {
  278. node.Remove(child)
  279. }
  280. }
  281. }
  282. // onMouse receives mouse button events over the tree node panel
  283. func (n *TreeNode) onMouse(evname string, ev interface{}) {
  284. switch evname {
  285. case OnMouseDown:
  286. n.expanded = !n.expanded
  287. n.update()
  288. n.recalc()
  289. n.updateItems()
  290. default:
  291. return
  292. }
  293. }
  294. // level returns the level of this node from the start of the tree
  295. func (n *TreeNode) level() int {
  296. level := 0
  297. parNode := n.parNode
  298. for parNode != nil {
  299. parNode = parNode.parNode
  300. level++
  301. }
  302. return level
  303. }
  304. // applyStyles applies the specified style to this tree node
  305. func (n *TreeNode) applyStyle(s *TreeNodeStyle) {
  306. n.Panel.ApplyStyle(&s.PanelStyle)
  307. icode := 0
  308. if n.expanded {
  309. icode = 1
  310. }
  311. n.icon.SetText(string(s.Icons[icode]))
  312. n.icon.SetColor4(&s.FgColor)
  313. n.label.SetColor4(&s.FgColor)
  314. }
  315. // update updates this tree node style
  316. func (n *TreeNode) update() {
  317. n.applyStyle(&n.tree.styles.Node.Normal)
  318. }
  319. // recalc recalculates the positions of the internal node panels
  320. func (n *TreeNode) recalc() {
  321. // icon position
  322. n.icon.SetPosition(0, 0)
  323. // Label position and width
  324. n.label.SetPosition(n.icon.Width()+4, 0)
  325. n.Panel.SetContentHeight(n.label.Height())
  326. n.Panel.SetWidth(n.tree.ContentWidth())
  327. }
  328. // remove removes this node and all children from the tree list
  329. func (n *TreeNode) remove() {
  330. n.tree.List.Remove(n)
  331. n.removeItems()
  332. }
  333. // removeItems removes this node children from the tree list
  334. func (n *TreeNode) removeItems() {
  335. for _, ipanel := range n.items {
  336. // Remove item from scroller
  337. n.tree.List.Remove(ipanel)
  338. // If item is a node, remove all children
  339. node, ok := ipanel.(*TreeNode)
  340. if ok {
  341. node.removeItems()
  342. continue
  343. }
  344. }
  345. }
  346. // insert inserts this node and its expanded children in the tree list
  347. // at the specified position
  348. func (n *TreeNode) insert(pos int) int {
  349. n.update()
  350. n.tree.List.InsertAt(pos, n)
  351. var padLeft float32 = n.tree.styles.Padlevel * float32(n.level())
  352. n.tree.List.SetItemPadLeftAt(pos, padLeft)
  353. pos++
  354. return n.insertItems(pos)
  355. }
  356. // insertItems inserts this node items in the tree list
  357. // at the specified position
  358. func (n *TreeNode) insertItems(pos int) int {
  359. if !n.expanded {
  360. return pos
  361. }
  362. level := n.level() + 1
  363. var padLeft float32 = n.tree.styles.Padlevel * float32(level)
  364. for _, ipanel := range n.items {
  365. // Insert node and its children
  366. node, ok := ipanel.(*TreeNode)
  367. if ok {
  368. node.update()
  369. n.tree.List.InsertAt(pos, ipanel)
  370. n.tree.List.SetItemPadLeftAt(pos, padLeft)
  371. pos++
  372. pos = node.insertItems(pos)
  373. continue
  374. }
  375. // Insert item
  376. n.tree.List.InsertAt(pos, ipanel)
  377. n.tree.List.SetItemPadLeftAt(pos, padLeft)
  378. pos++
  379. }
  380. return pos
  381. }
  382. // updateItems updates this node items, removing or inserting them into the tree scroller
  383. func (n *TreeNode) updateItems() {
  384. pos := n.tree.ItemPosition(n)
  385. if pos < 0 {
  386. return
  387. }
  388. n.removeItems()
  389. n.insertItems(pos + 1)
  390. }