dropdown.go 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  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. "github.com/g3n/engine/window"
  8. )
  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. clickOut bool
  20. }
  21. // DropDown list style
  22. type DropDownStyle BasicStyle
  23. // DropDown list styles
  24. type DropDownStyles struct {
  25. Normal DropDownStyle
  26. Over DropDownStyle
  27. Focus DropDownStyle
  28. Disabled DropDownStyle
  29. }
  30. // NewDropDown creates and returns a pointer to a new drop down widget with the specified width.
  31. func NewDropDown(width float32, item *ImageLabel) *DropDown {
  32. dd := new(DropDown)
  33. dd.styles = &StyleDefault().DropDown
  34. dd.litem = item
  35. dd.Panel.Initialize(width, 0)
  36. dd.Panel.Subscribe(OnKeyDown, dd.onKeyEvent)
  37. dd.Panel.Subscribe(OnMouseDown, dd.onMouse)
  38. dd.Panel.Subscribe(OnCursorEnter, dd.onCursor)
  39. dd.Panel.Subscribe(OnCursorLeave, dd.onCursor)
  40. dd.Panel.Subscribe(OnResize, func(name string, ev interface{}) { dd.recalc() })
  41. // ListItem
  42. dd.Panel.Add(dd.litem)
  43. // Create icon
  44. dd.icon = NewLabel(" ", true)
  45. dd.icon.SetFontSize(StyleDefault().Font.Size() * 1.3)
  46. dd.icon.SetText(string(icon.ArrowDropDown))
  47. dd.Panel.Add(dd.icon)
  48. /// Create list
  49. dd.list = NewVList(0, 0)
  50. dd.list.bounded = false
  51. dd.list.dropdown = true
  52. dd.list.SetVisible(false)
  53. dd.list.Subscribe(OnMouseDown, dd.onListMouse)
  54. dd.list.Subscribe(OnMouseOut, dd.onListMouse)
  55. dd.list.Subscribe(OnChange, dd.onListChangeEvent)
  56. dd.Panel.Add(dd.list)
  57. dd.update()
  58. // This will trigger recalc()
  59. dd.Panel.SetContentHeight(item.Height())
  60. return dd
  61. }
  62. // Add add a list item at the end of the list
  63. func (dd *DropDown) Add(item *ImageLabel) {
  64. dd.list.Add(item)
  65. }
  66. // InsertAt inserts a list item at the specified position
  67. // Returs true if the item was successfuly inserted
  68. func (dd *DropDown) InsertAt(pos int, item *ImageLabel) {
  69. dd.list.InsertAt(pos, item)
  70. }
  71. // RemoveAt removes the list item from the specified position
  72. // Returs true if the item was successfuly removed
  73. func (dd *DropDown) RemoveAt(pos int) {
  74. dd.list.RemoveAt(pos)
  75. }
  76. // ItemAt returns the list item at the specified position
  77. func (dd *DropDown) ItemAt(pos int) *ImageLabel {
  78. return dd.list.ItemAt(pos).(*ImageLabel)
  79. }
  80. // Len returns the number of items in the dropdown's list.
  81. func (dd *DropDown) Len() int {
  82. return dd.list.Len()
  83. }
  84. // Returns the currently selected item or nil if not item
  85. // was selected
  86. func (dd *DropDown) Selected() *ImageLabel {
  87. return dd.selItem
  88. }
  89. // SetSelected sets the selected item
  90. func (dd *DropDown) SetSelected(item *ImageLabel) {
  91. dd.list.SetSelected(item, true)
  92. }
  93. // SelectPos selects the item at the specified position
  94. func (dd *DropDown) SelectPos(pos int) {
  95. dd.list.SelectPos(pos, true)
  96. }
  97. // onKeyEvent is called when key event is received when this dropdown has the key focus.
  98. func (dd *DropDown) onKeyEvent(evname string, ev interface{}) {
  99. kev := ev.(*window.KeyEvent)
  100. switch kev.Keycode {
  101. case window.KeyF1:
  102. if dd.list.Visible() {
  103. dd.list.SetVisible(false)
  104. }
  105. default:
  106. return
  107. }
  108. }
  109. // onMouse receives subscribed mouse events over the dropdown
  110. func (dd *DropDown) onMouse(evname string, ev interface{}) {
  111. if evname == OnMouseDown {
  112. // If clickOut list already closed
  113. if dd.clickOut {
  114. dd.clickOut = false
  115. return
  116. }
  117. dd.list.SetVisible(true)
  118. dd.root.SetKeyFocus(dd.list)
  119. return
  120. }
  121. }
  122. // onCursor receives subscribed cursor events over the dropdown
  123. func (dd *DropDown) onCursor(evname string, ev interface{}) {
  124. if evname == OnCursorEnter {
  125. dd.overDropdown = true
  126. dd.update()
  127. return
  128. }
  129. if evname == OnCursorLeave {
  130. dd.overDropdown = false
  131. dd.update()
  132. return
  133. }
  134. }
  135. // onListMouseEvent receives mouse events over the list
  136. func (dd *DropDown) onListMouse(evname string, ev interface{}) {
  137. mev := ev.(*window.MouseEvent)
  138. // List was clicked
  139. if evname == OnMouseDown {
  140. // If click occurred inside the list scrollbar ignore it
  141. if dd.list.vscroll != nil {
  142. if dd.list.vscroll.InsideBorders(mev.Xpos, mev.Ypos) {
  143. return
  144. }
  145. }
  146. // Otherwise, closes the list
  147. dd.list.SetVisible(false)
  148. //dd.copySelected()
  149. dd.overList = false
  150. dd.update()
  151. return
  152. }
  153. // Hide list when clicked out
  154. if evname == OnMouseOut {
  155. if dd.list.Visible() {
  156. dd.list.SetVisible(false)
  157. }
  158. // If list clickout occurred inside the dropdown, set 'clickOut' to
  159. // indicate that the list was already closed
  160. if dd.Panel.InsideBorders(mev.Xpos, mev.Ypos) {
  161. dd.clickOut = true
  162. }
  163. }
  164. }
  165. // onListCursor receives subscribed events over the list
  166. func (dd *DropDown) onListCursor(evname string, ev interface{}) {
  167. if evname == OnCursorEnter {
  168. dd.overList = true
  169. dd.update()
  170. return
  171. }
  172. if evname == OnCursorLeave {
  173. dd.overList = false
  174. dd.update()
  175. return
  176. }
  177. }
  178. // copySelected copy to the dropdown panel the selected item
  179. // from the list.
  180. func (dd *DropDown) copySelected() {
  181. dd.selItem = dd.list.Selected()[0].(*ImageLabel)
  182. dd.litem.CopyFields(dd.selItem)
  183. dd.litem.SetWidth(dd.selItem.Width())
  184. dd.recalc()
  185. dd.Dispatch(OnChange, nil)
  186. }
  187. // onListChangeEvent is called when an item in the list is selected
  188. func (dd *DropDown) onListChangeEvent(evname string, ev interface{}) {
  189. dd.copySelected()
  190. }
  191. // recalc recalculates the dimensions and positions of the dropdown
  192. // panel, children and list
  193. func (dd *DropDown) recalc() {
  194. // Dropdown icon position
  195. posx := dd.Panel.ContentWidth() - dd.icon.Width()
  196. dd.icon.SetPosition(posx, 0)
  197. // List item position and width
  198. ipan := dd.litem.GetPanel()
  199. ipan.SetPosition(0, 0)
  200. height := ipan.Height()
  201. // List position
  202. dd.list.SetWidth(dd.Panel.Width())
  203. dd.list.SetHeight(6*height + 1)
  204. dd.list.SetPositionX(0)
  205. dd.list.SetPositionY(dd.Panel.Height())
  206. }
  207. // update updates the visual state
  208. func (dd *DropDown) update() {
  209. if dd.overDropdown || dd.overList {
  210. dd.applyStyle(&dd.styles.Over)
  211. dd.list.ApplyStyle(StyleOver)
  212. return
  213. }
  214. if dd.focus {
  215. dd.applyStyle(&dd.styles.Focus)
  216. dd.list.ApplyStyle(StyleFocus)
  217. return
  218. }
  219. dd.applyStyle(&dd.styles.Normal)
  220. dd.list.ApplyStyle(StyleNormal)
  221. }
  222. // applyStyle applies the specified style
  223. func (dd *DropDown) applyStyle(s *DropDownStyle) {
  224. dd.Panel.ApplyStyle(&s.PanelStyle)
  225. }