root.go 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  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/core"
  7. "github.com/g3n/engine/gls"
  8. "github.com/g3n/engine/window"
  9. "sort"
  10. )
  11. type Root struct {
  12. Panel // embedded panel
  13. core.TimerManager // embedded TimerManager
  14. gs *gls.GLS // OpenGL state
  15. win window.IWindow // Window
  16. stopPropagation int // stop event propagation bitmask
  17. keyFocus IPanel // current child panel with key focus
  18. mouseFocus IPanel // current child panel with mouse focus
  19. scrollFocus IPanel // current child panel with scroll focus
  20. targets listPanelZ // preallocated list of target panels
  21. }
  22. const (
  23. StopGUI = 0x01 // Stop event propagation to GUI
  24. Stop3D = 0x02 // Stop event propagation to 3D
  25. StopAll = StopGUI | Stop3D // Stop event propagation
  26. )
  27. // NewRoot creates and returns a pointer to a gui root panel for the specified window
  28. func NewRoot(gs *gls.GLS, win window.IWindow) *Root {
  29. r := new(Root)
  30. r.gs = gs
  31. r.win = win
  32. r.root = r
  33. r.Panel.Initialize(0, 0)
  34. r.TimerManager.Initialize()
  35. // for optimization, sets this root panel as not renderable as in most cases
  36. // it is used only as a container
  37. r.SetRenderable(false)
  38. // Subscribe to window events
  39. r.SubscribeWin()
  40. r.targets = []IPanel{}
  41. return r
  42. }
  43. // SubscribeWin subscribes this root panel to window events
  44. func (r *Root) SubscribeWin() {
  45. r.win.Subscribe(window.OnKeyUp, r.onKey)
  46. r.win.Subscribe(window.OnKeyDown, r.onKey)
  47. r.win.Subscribe(window.OnKeyRepeat, r.onKey)
  48. r.win.Subscribe(window.OnChar, r.onChar)
  49. r.win.Subscribe(window.OnMouseUp, r.onMouse)
  50. r.win.Subscribe(window.OnMouseDown, r.onMouse)
  51. r.win.Subscribe(window.OnCursor, r.onCursor)
  52. r.win.Subscribe(window.OnScroll, r.onScroll)
  53. r.win.Subscribe(window.OnWindowSize, r.onWindowSize)
  54. r.win.Subscribe(window.OnFrame, r.onFrame)
  55. }
  56. // Add adds the specified panel to the root container list of children
  57. // Overrides the Panel version because it needs to set the root panel field
  58. func (r *Root) Add(ipan IPanel) {
  59. // Sets the root panel field of the child to be added
  60. ipan.GetPanel().root = r
  61. // Add this panel to the root panel children.
  62. // This will also set the root panel for all the child children
  63. // and the z coordinates of all the panel tree graph.
  64. r.Panel.Add(ipan)
  65. }
  66. // SetKeyFocus sets the panel which will receive all keyboard events
  67. // Passing nil will remove the focus (if any)
  68. func (r *Root) SetKeyFocus(ipan IPanel) {
  69. if r.keyFocus != nil {
  70. // If this panel is already in focus, nothing to do
  71. if ipan != nil {
  72. if r.keyFocus.GetPanel() == ipan.GetPanel() {
  73. return
  74. }
  75. }
  76. r.keyFocus.LostKeyFocus()
  77. }
  78. r.keyFocus = ipan
  79. }
  80. // ClearKeyFocus clears the key focus panel (if any) without
  81. // calling LostKeyFocus() for previous focused panel
  82. func (r *Root) ClearKeyFocus() {
  83. r.keyFocus = nil
  84. }
  85. // SetMouseFocus sets the panel which will receive all mouse events
  86. // Passing nil will restore the default event processing
  87. func (r *Root) SetMouseFocus(ipan IPanel) {
  88. r.mouseFocus = ipan
  89. }
  90. // SetScrollFocus sets the panel which will receive all scroll events
  91. // Passing nil will restore the default event processing
  92. func (r *Root) SetScrollFocus(ipan IPanel) {
  93. r.scrollFocus = ipan
  94. }
  95. // HasKeyFocus checks if the specified panel has the key focus
  96. func (r *Root) HasKeyFocus(ipan IPanel) bool {
  97. if r.keyFocus == nil {
  98. return false
  99. }
  100. if r.keyFocus.GetPanel() == ipan.GetPanel() {
  101. return true
  102. }
  103. return false
  104. }
  105. // HasMouseFocus checks if the specified panel has the mouse focus
  106. func (r *Root) HasMouseFocus(ipan IPanel) bool {
  107. if r.mouseFocus == nil {
  108. return false
  109. }
  110. if r.mouseFocus.GetPanel() == ipan.GetPanel() {
  111. return true
  112. }
  113. return false
  114. }
  115. // StopPropagation stops the propagation of the current event
  116. // to outside the root panel (for example the 3D camera)
  117. func (r *Root) StopPropagation(events int) {
  118. r.stopPropagation |= events
  119. }
  120. // SetCursorNormal sets the cursor of the associated window to
  121. // standard type
  122. func (r *Root) SetCursorNormal() {
  123. r.win.SetStandardCursor(window.ArrowCursor)
  124. }
  125. // SetCursorDrag sets the cursor of the associated window to
  126. // drag type
  127. func (r *Root) SetCursorDrag() {
  128. r.win.SetStandardCursor(window.HandCursor)
  129. }
  130. // SetCursorHResize sets the cursor of the associated window to
  131. // horizontal resize type
  132. func (r *Root) SetCursorHResize() {
  133. r.win.SetStandardCursor(window.HResizeCursor)
  134. }
  135. // SetCursorVResize sets the cursor of the associated window to
  136. // vertical resize type
  137. func (r *Root) SetCursorVResize() {
  138. r.win.SetStandardCursor(window.VResizeCursor)
  139. }
  140. // onKey is called when key events are received
  141. func (r *Root) onKey(evname string, ev interface{}) {
  142. // If no panel has the key focus, nothing to do
  143. if r.keyFocus == nil {
  144. return
  145. }
  146. // Dispatch window.KeyEvent to focused panel subscribers
  147. r.stopPropagation = 0
  148. r.keyFocus.GetPanel().Dispatch(evname, ev)
  149. // If requested, stop propagation of event outside the root gui
  150. if (r.stopPropagation & Stop3D) != 0 {
  151. r.win.CancelDispatch()
  152. }
  153. }
  154. // onChar is called when char events are received
  155. func (r *Root) onChar(evname string, ev interface{}) {
  156. // If no panel has the key focus, nothing to do
  157. if r.keyFocus == nil {
  158. return
  159. }
  160. // Dispatch window.CharEvent to focused panel subscribers
  161. r.stopPropagation = 0
  162. r.keyFocus.GetPanel().Dispatch(evname, ev)
  163. // If requested, stopj propagation of event outside the root gui
  164. if (r.stopPropagation & Stop3D) != 0 {
  165. r.win.CancelDispatch()
  166. }
  167. }
  168. // onMouse is called when mouse button events are received
  169. func (r *Root) onMouse(evname string, ev interface{}) {
  170. mev := ev.(*window.MouseEvent)
  171. r.sendPanels(mev.Xpos, mev.Ypos, evname, ev)
  172. }
  173. // onCursor is called when (mouse) cursor events are received
  174. func (r *Root) onCursor(evname string, ev interface{}) {
  175. cev := ev.(*window.CursorEvent)
  176. r.sendPanels(cev.Xpos, cev.Ypos, evname, ev)
  177. }
  178. // sendPanel sends mouse or cursor event to focused panel or panels
  179. // which contains the specified screen position
  180. func (r *Root) sendPanels(x, y float32, evname string, ev interface{}) {
  181. // If there is panel with MouseFocus send only to this panel
  182. if r.mouseFocus != nil {
  183. r.mouseFocus.GetPanel().Dispatch(evname, ev)
  184. if (r.stopPropagation & Stop3D) != 0 {
  185. r.win.CancelDispatch()
  186. }
  187. return
  188. }
  189. // Clear list of panels which contains the mouse position
  190. r.targets = r.targets[0:0]
  191. // checkPanel checks recursively if the specified panel and
  192. // any of its child contains the mouse position
  193. var checkPanel func(ipan IPanel)
  194. checkPanel = func(ipan IPanel) {
  195. pan := ipan.GetPanel()
  196. // If panel not visible or not enabled, ignore
  197. if !pan.Visible() || !pan.Enabled() {
  198. return
  199. }
  200. // Checks if this panel contains the mouse position
  201. found := pan.InsideBorders(x, y)
  202. if found {
  203. r.targets = append(r.targets, ipan)
  204. } else {
  205. // If OnCursorEnter previously sent, sends OnCursorLeave with a nil event
  206. if pan.cursorEnter {
  207. pan.Dispatch(OnCursorLeave, nil)
  208. pan.cursorEnter = false
  209. }
  210. // If mouse button was pressed, sends event informing mouse down outside of the panel
  211. if evname == OnMouseDown {
  212. pan.Dispatch(OnMouseOut, ev)
  213. }
  214. }
  215. // Checks if any of its children also contains the position
  216. for _, child := range pan.Children() {
  217. ipan, ok := child.(IPanel)
  218. if ok {
  219. checkPanel(ipan)
  220. }
  221. }
  222. }
  223. // Checks all children of this root node
  224. for _, iobj := range r.Node.Children() {
  225. ipan, ok := iobj.(IPanel)
  226. if !ok {
  227. continue
  228. }
  229. checkPanel(ipan)
  230. }
  231. // No panels found
  232. if len(r.targets) == 0 {
  233. // If event is mouse click, removes the keyboard focus
  234. if evname == OnMouseDown {
  235. r.SetKeyFocus(nil)
  236. }
  237. return
  238. }
  239. // Sorts panels by absolute Z with the most foreground panels first
  240. // and sends event to all panels or until a stop is requested
  241. sort.Sort(r.targets)
  242. r.stopPropagation = 0
  243. // Send events to panels
  244. for _, ipan := range r.targets {
  245. pan := ipan.GetPanel()
  246. // Cursor position event
  247. if evname == OnCursor {
  248. pan.Dispatch(evname, ev)
  249. if !pan.cursorEnter {
  250. pan.Dispatch(OnCursorEnter, ev)
  251. pan.cursorEnter = true
  252. }
  253. // Mouse button event
  254. } else {
  255. pan.Dispatch(evname, ev)
  256. }
  257. if (r.stopPropagation & StopGUI) != 0 {
  258. break
  259. }
  260. }
  261. // Stops propagation of event outside the root gui
  262. if (r.stopPropagation & Stop3D) != 0 {
  263. r.win.CancelDispatch()
  264. }
  265. }
  266. // onScroll is called when scroll events are received and
  267. // is responsible to dispatch them to child panels.
  268. func (r *Root) onScroll(evname string, ev interface{}) {
  269. // If no panel with the scroll focus, nothing to do
  270. if r.scrollFocus == nil {
  271. return
  272. }
  273. // Dispatch event to panel with scroll focus
  274. r.scrollFocus.GetPanel().Dispatch(evname, ev)
  275. // Stops propagation of event outside the root gui
  276. if (r.stopPropagation & Stop3D) != 0 {
  277. r.win.CancelDispatch()
  278. }
  279. }
  280. // onSize is called when window size events are received
  281. func (r *Root) onWindowSize(evname string, ev interface{}) {
  282. // Sends event only to immediate children
  283. for _, ipan := range r.Children() {
  284. ipan.(IPanel).GetPanel().Dispatch(evname, ev)
  285. }
  286. }
  287. // onFrame is called when window finished swapping frame buffers
  288. func (r *Root) onFrame(evname string, ev interface{}) {
  289. r.TimerManager.ProcessTimers()
  290. }
  291. // For sorting panels by Z coordinate
  292. type listPanelZ []IPanel
  293. func (p listPanelZ) Len() int { return len(p) }
  294. func (p listPanelZ) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
  295. func (p listPanelZ) Less(i, j int) bool {
  296. iz := p[i].GetPanel().Position().Z
  297. jz := p[j].GetPanel().Position().Z
  298. return iz < jz
  299. }