dropdown.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  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/gui/assets/icon"
  7. )
  8. // DropDown represents a dropdown GUI element.
  9. type DropDown struct {
  10. Panel // Embedded panel
  11. icon *Label // internal label with icon
  12. list *List // internal list
  13. styles *DropDownStyles // pointer to dropdown styles
  14. litem *ImageLabel // Item shown in drop box (copy of selected)
  15. selItem *ImageLabel // selected item from list
  16. overDropdown bool
  17. overList bool
  18. focus bool
  19. }
  20. // DropDownStyle contains the styling of a DropDown.
  21. type DropDownStyle BasicStyle
  22. // DropDownStyles contains a DropDownStyle for each valid GUI state.
  23. type DropDownStyles struct {
  24. Normal DropDownStyle
  25. Over DropDownStyle
  26. Focus DropDownStyle
  27. Disabled DropDownStyle
  28. }
  29. // NewDropDown creates and returns a pointer to a new drop down widget with the specified width.
  30. func NewDropDown(width float32, item *ImageLabel) *DropDown {
  31. dd := new(DropDown)
  32. dd.styles = &StyleDefault().DropDown
  33. dd.litem = item
  34. dd.Panel.Initialize(dd, width, 0)
  35. dd.Panel.Subscribe(OnMouseDown, dd.onMouse)
  36. dd.Panel.Subscribe(OnCursorEnter, dd.onCursor)
  37. dd.Panel.Subscribe(OnCursorLeave, dd.onCursor)
  38. dd.Panel.Subscribe(OnResize, func(name string, ev interface{}) { dd.recalc() })
  39. // ListItem
  40. dd.Panel.Add(dd.litem)
  41. // Create icon
  42. dd.icon = NewIcon(" ")
  43. dd.icon.SetFontSize(StyleDefault().Label.PointSize * 1.3)
  44. dd.icon.SetText(string(icon.ArrowDropDown))
  45. dd.Panel.Add(dd.icon)
  46. /// Create list
  47. dd.list = NewVList(0, 0)
  48. dd.list.bounded = false
  49. dd.list.zLayerDelta = 1
  50. dd.list.dropdown = true
  51. dd.list.SetVisible(false)
  52. dd.Panel.Subscribe(OnKeyDown, dd.list.onKeyEvent)
  53. dd.Subscribe(OnMouseDownOut, func(s string, i interface{}) {
  54. // Hide list when clicked out
  55. if dd.list.Visible() {
  56. dd.list.SetVisible(false)
  57. }
  58. })
  59. dd.list.Subscribe(OnCursorEnter, func(evname string, ev interface{}) {
  60. dd.Dispatch(OnCursorLeave, ev)
  61. })
  62. dd.list.Subscribe(OnCursorLeave, func(evname string, ev interface{}) {
  63. dd.Dispatch(OnCursorEnter, ev)
  64. })
  65. dd.list.Subscribe(OnChange, dd.onListChangeEvent)
  66. dd.Panel.Add(dd.list)
  67. dd.update()
  68. // This will trigger recalc()
  69. dd.Panel.SetContentHeight(item.Height())
  70. return dd
  71. }
  72. // Add adds a list item at the end of the list
  73. func (dd *DropDown) Add(item *ImageLabel) {
  74. dd.list.Add(item)
  75. }
  76. // InsertAt inserts a list item at the specified position
  77. // Returs true if the item was successfully inserted
  78. func (dd *DropDown) InsertAt(pos int, item *ImageLabel) {
  79. dd.list.InsertAt(pos, item)
  80. }
  81. // RemoveAt removes the list item from the specified position
  82. // Returs true if the item was successfully removed
  83. func (dd *DropDown) RemoveAt(pos int) {
  84. dd.list.RemoveAt(pos)
  85. }
  86. // ItemAt returns the list item at the specified position
  87. func (dd *DropDown) ItemAt(pos int) *ImageLabel {
  88. return dd.list.ItemAt(pos).(*ImageLabel)
  89. }
  90. // Len returns the number of items in the dropdown's list.
  91. func (dd *DropDown) Len() int {
  92. return dd.list.Len()
  93. }
  94. // Selected returns the currently selected item or nil if no item was selected
  95. func (dd *DropDown) Selected() *ImageLabel {
  96. return dd.selItem
  97. }
  98. // SelectedPos returns the currently selected position or -1 if no item was selected
  99. func (dd *DropDown) SelectedPos() int {
  100. return dd.list.selected()
  101. }
  102. // SetSelected sets the selected item
  103. func (dd *DropDown) SetSelected(item *ImageLabel) {
  104. dd.list.SetSelected(dd.selItem, false)
  105. dd.list.SetSelected(item, true)
  106. dd.copySelected()
  107. dd.update()
  108. }
  109. // SelectPos selects the item at the specified position
  110. func (dd *DropDown) SelectPos(pos int) {
  111. dd.list.SetSelected(dd.selItem, false)
  112. dd.list.SelectPos(pos, true)
  113. dd.Dispatch(OnChange, nil)
  114. }
  115. // SetStyles sets the drop down styles overriding the default style
  116. func (dd *DropDown) SetStyles(dds *DropDownStyles) {
  117. dd.styles = dds
  118. dd.update()
  119. }
  120. // onMouse receives subscribed mouse events over the dropdown
  121. func (dd *DropDown) onMouse(evname string, ev interface{}) {
  122. Manager().SetKeyFocus(dd.list)
  123. if evname == OnMouseDown {
  124. dd.list.SetVisible(!dd.list.Visible())
  125. return
  126. }
  127. }
  128. // onCursor receives subscribed cursor events over the dropdown
  129. func (dd *DropDown) onCursor(evname string, ev interface{}) {
  130. if evname == OnCursorEnter {
  131. dd.overDropdown = true
  132. }
  133. if evname == OnCursorLeave {
  134. dd.overDropdown = false
  135. }
  136. dd.update()
  137. }
  138. // copySelected copy to the dropdown panel the selected item
  139. // from the list.
  140. func (dd *DropDown) copySelected() {
  141. selected := dd.list.Selected()
  142. if len(selected) > 0 {
  143. dd.selItem = selected[0].(*ImageLabel)
  144. dd.litem.CopyFields(dd.selItem)
  145. dd.litem.SetWidth(dd.selItem.Width())
  146. dd.recalc()
  147. dd.Dispatch(OnChange, nil)
  148. } else {
  149. return
  150. }
  151. }
  152. // onListChangeEvent is called when an item in the list is selected
  153. func (dd *DropDown) onListChangeEvent(evname string, ev interface{}) {
  154. dd.copySelected()
  155. }
  156. // recalc recalculates the dimensions and positions of the dropdown
  157. // panel, children and list
  158. func (dd *DropDown) recalc() {
  159. // Dropdown icon position
  160. posx := dd.Panel.ContentWidth() - dd.icon.Width()
  161. dd.icon.SetPosition(posx, 0)
  162. // List item position and width
  163. ipan := dd.litem.GetPanel()
  164. ipan.SetPosition(0, 0)
  165. height := ipan.Height()
  166. // List position
  167. dd.list.SetWidth(dd.Panel.Width())
  168. dd.list.SetHeight(6*height + 1)
  169. dd.list.SetPositionX(0)
  170. dd.list.SetPositionY(dd.Panel.Height())
  171. }
  172. // update updates the visual state
  173. func (dd *DropDown) update() {
  174. if dd.overDropdown || dd.overList {
  175. dd.applyStyle(&dd.styles.Over)
  176. dd.list.ApplyStyle(StyleOver)
  177. return
  178. }
  179. if dd.focus {
  180. dd.applyStyle(&dd.styles.Focus)
  181. dd.list.ApplyStyle(StyleFocus)
  182. return
  183. }
  184. dd.applyStyle(&dd.styles.Normal)
  185. dd.list.ApplyStyle(StyleNormal)
  186. }
  187. // applyStyle applies the specified style
  188. func (dd *DropDown) applyStyle(s *DropDownStyle) {
  189. dd.Panel.ApplyStyle(&s.PanelStyle)
  190. }