tree.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  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. litem *ListItem // Reference to ListItem
  40. }
  41. // NewTree creates and returns a pointer to a new tree widget.
  42. func NewTree(width, height float32) *Tree {
  43. t := new(Tree)
  44. t.Initialize(width, height)
  45. return t
  46. }
  47. // Initialize initializes the tree with the specified initial width and height
  48. // It is normally used when the folder is embedded in another object.
  49. func (t *Tree) Initialize(width, height float32) {
  50. t.List.initialize(true, width, height)
  51. t.SetStyles(&StyleDefault().Tree)
  52. t.List.Subscribe(OnKeyDown, t.onKey)
  53. t.List.Subscribe(OnKeyUp, t.onKey)
  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. n.litem = 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. n.litem = 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. // onKey receives key down events for the embedded list
  138. func (t *Tree) onKey(evname string, ev interface{}) {
  139. // Get selected item
  140. item := t.Selected()
  141. if item == nil {
  142. return
  143. }
  144. // If item is not a tree node, dispatch event to item
  145. node, ok := item.(*TreeNode)
  146. if !ok {
  147. item.GetPanel().Dispatch(evname, ev)
  148. return
  149. }
  150. // If not enter key pressed, ignore
  151. kev := ev.(*window.KeyEvent)
  152. if evname != OnKeyDown || kev.Key != window.KeyEnter {
  153. return
  154. }
  155. // Toggles the expansion state of the node
  156. node.expanded = !node.expanded
  157. node.update()
  158. node.updateItems()
  159. }
  160. //
  161. // TreeNode methods
  162. //
  163. // newTreeNode creates and returns a pointer to a new TreeNode with
  164. // the specified text, tree and parent node
  165. func newTreeNode(text string, tree *Tree, parNode *TreeNode) *TreeNode {
  166. n := new(TreeNode)
  167. n.Panel.Initialize(n, 0, 0)
  168. // Initialize node label
  169. n.label.initialize(text, StyleDefault().Font)
  170. n.Panel.Add(&n.label)
  171. // Create node icon
  172. n.icon.initialize("", StyleDefault().FontIcon)
  173. n.icon.SetFontSize(StyleDefault().Label.PointSize * 1.3)
  174. n.Panel.Add(&n.icon)
  175. // Subscribe to events
  176. n.Panel.Subscribe(OnMouseDown, n.onMouse)
  177. n.Panel.Subscribe(OnListItemResize, func(evname string, ev interface{}) {
  178. n.recalc()
  179. })
  180. n.tree = tree
  181. n.parNode = parNode
  182. n.update()
  183. n.recalc()
  184. return n
  185. }
  186. // Len returns the number of immediate children of this node
  187. func (n *TreeNode) Len() int {
  188. return len(n.items)
  189. }
  190. // SetExpanded sets the expanded state of this node
  191. func (n *TreeNode) SetExpanded(state bool) {
  192. n.expanded = state
  193. n.update()
  194. n.updateItems()
  195. }
  196. // FindChild searches for the specified child in this node and
  197. // all its children. If found, returns the parent node and
  198. // its position relative to the parent.
  199. // If not found returns nil and -1
  200. func (n *TreeNode) FindChild(child IPanel) (*TreeNode, int) {
  201. for pos, curr := range n.items {
  202. if curr == child {
  203. return n, pos
  204. }
  205. node, ok := curr.(*TreeNode)
  206. if ok {
  207. par, pos := node.FindChild(child)
  208. if par != nil {
  209. return par, pos
  210. }
  211. }
  212. }
  213. return nil, -1
  214. }
  215. // InsertAt inserts a child panel at the specified position in this node
  216. // If the position is invalid, the function panics
  217. func (n *TreeNode) InsertAt(pos int, child IPanel) {
  218. if pos < 0 || pos > len(n.items) {
  219. panic("TreeNode.InsertAt(): Invalid position")
  220. }
  221. // Insert item in the items array
  222. n.items = append(n.items, nil)
  223. copy(n.items[pos+1:], n.items[pos:])
  224. n.items[pos] = child
  225. if n.expanded {
  226. n.updateItems()
  227. }
  228. }
  229. // Add adds a child panel to this node
  230. func (n *TreeNode) Add(child IPanel) {
  231. n.InsertAt(n.Len(), child)
  232. }
  233. // InsertNodeAt inserts a new node at the specified position in this node
  234. // If the position is invalid, the function panics
  235. func (n *TreeNode) InsertNodeAt(pos int, text string) *TreeNode {
  236. if pos < 0 || pos > len(n.items) {
  237. panic("TreeNode.InsertNodeAt(): Invalid position")
  238. }
  239. childNode := newTreeNode(text, n.tree, n)
  240. // Insert item in the items array
  241. n.items = append(n.items, nil)
  242. copy(n.items[pos+1:], n.items[pos:])
  243. n.items[pos] = childNode
  244. if n.expanded {
  245. n.updateItems()
  246. }
  247. return childNode
  248. }
  249. // AddNode adds a new node to this one and return its pointer
  250. func (n *TreeNode) AddNode(text string) *TreeNode {
  251. return n.InsertNodeAt(n.Len(), text)
  252. }
  253. // Remove removes the specified child from this node or any
  254. // of its children nodes
  255. func (n *TreeNode) Remove(child IPanel) {
  256. for pos, curr := range n.items {
  257. if curr == child {
  258. copy(n.items[pos:], n.items[pos+1:])
  259. n.items[len(n.items)-1] = nil
  260. n.items = n.items[:len(n.items)-1]
  261. node, ok := curr.(*TreeNode)
  262. if ok {
  263. node.remove()
  264. } else {
  265. n.tree.List.Remove(curr)
  266. }
  267. n.updateItems()
  268. return
  269. }
  270. node, ok := curr.(*TreeNode)
  271. if ok {
  272. node.Remove(child)
  273. }
  274. }
  275. }
  276. // onMouse receives mouse button events over the tree node panel
  277. func (n *TreeNode) onMouse(evname string, ev interface{}) {
  278. switch evname {
  279. case OnMouseDown:
  280. n.expanded = !n.expanded
  281. n.update()
  282. n.recalc()
  283. n.updateItems()
  284. }
  285. n.litem.onMouse(evname, ev)
  286. }
  287. // level returns the level of this node from the start of the tree
  288. func (n *TreeNode) level() int {
  289. level := 0
  290. parNode := n.parNode
  291. for parNode != nil {
  292. parNode = parNode.parNode
  293. level++
  294. }
  295. return level
  296. }
  297. // applyStyles applies the specified style to this tree node
  298. func (n *TreeNode) applyStyle(s *TreeNodeStyle) {
  299. n.Panel.ApplyStyle(&s.PanelStyle)
  300. icode := 0
  301. if n.expanded {
  302. icode = 1
  303. }
  304. n.icon.SetText(string(s.Icons[icode]))
  305. n.icon.SetColor4(&s.FgColor)
  306. n.label.SetColor4(&s.FgColor)
  307. }
  308. // update updates this tree node style
  309. func (n *TreeNode) update() {
  310. n.applyStyle(&n.tree.styles.Node.Normal)
  311. }
  312. // recalc recalculates the positions of the internal node panels
  313. func (n *TreeNode) recalc() {
  314. // icon position
  315. n.icon.SetPosition(0, 0)
  316. // Label position and width
  317. n.label.SetPosition(n.icon.Width()+4, 0)
  318. n.Panel.SetContentHeight(n.label.Height())
  319. n.Panel.SetWidth(n.tree.ContentWidth())
  320. }
  321. // remove removes this node and all children from the tree list
  322. func (n *TreeNode) remove() {
  323. n.tree.List.Remove(n)
  324. n.removeItems()
  325. }
  326. // removeItems removes this node children from the tree list
  327. func (n *TreeNode) removeItems() {
  328. for _, ipanel := range n.items {
  329. // Remove item from scroller
  330. n.tree.List.Remove(ipanel)
  331. // If item is a node, remove all children
  332. node, ok := ipanel.(*TreeNode)
  333. if ok {
  334. node.removeItems()
  335. continue
  336. }
  337. }
  338. }
  339. // insert inserts this node and its expanded children in the tree list
  340. // at the specified position
  341. func (n *TreeNode) insert(pos int) int {
  342. n.update()
  343. n.tree.List.InsertAt(pos, n)
  344. var padLeft float32 = n.tree.styles.Padlevel * float32(n.level())
  345. n.tree.List.SetItemPadLeftAt(pos, padLeft)
  346. pos++
  347. return n.insertItems(pos)
  348. }
  349. // insertItems inserts this node items in the tree list
  350. // at the specified position
  351. func (n *TreeNode) insertItems(pos int) int {
  352. if !n.expanded {
  353. return pos
  354. }
  355. level := n.level() + 1
  356. var padLeft float32 = n.tree.styles.Padlevel * float32(level)
  357. for _, ipanel := range n.items {
  358. // Insert node and its children
  359. node, ok := ipanel.(*TreeNode)
  360. if ok {
  361. node.update()
  362. node.litem = n.tree.List.InsertAt(pos, ipanel)
  363. n.tree.List.SetItemPadLeftAt(pos, padLeft)
  364. pos++
  365. pos = node.insertItems(pos)
  366. continue
  367. }
  368. // Insert item
  369. n.tree.List.InsertAt(pos, ipanel)
  370. n.tree.List.SetItemPadLeftAt(pos, padLeft)
  371. pos++
  372. }
  373. return pos
  374. }
  375. // updateItems updates this node items, removing or inserting them into the tree scroller
  376. func (n *TreeNode) updateItems() {
  377. pos := n.tree.ItemPosition(n)
  378. if pos < 0 {
  379. return
  380. }
  381. n.removeItems()
  382. n.insertItems(pos + 1)
  383. }