manager.go 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  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/window"
  8. )
  9. // manager singleton
  10. var gm *manager
  11. // manager routes GUI events to the appropriate panels.
  12. type manager struct {
  13. core.Dispatcher // Embedded Dispatcher
  14. core.TimerManager // Embedded TimerManager
  15. win window.IWindow // The current IWindow
  16. scene core.INode // INode containing IPanels to dispatch events to (can contain non-IPanels as well)
  17. modal IPanel // Panel which along its descendants will exclusively receive all events
  18. target IPanel // Panel immediately under the cursor
  19. keyFocus core.IDispatcher // IDispatcher which will exclusively receive all key and char events
  20. cursorFocus core.IDispatcher // IDispatcher which will exclusively receive all OnCursor events
  21. cev *window.CursorEvent // IDispatcher which will exclusively receive all OnCursor events
  22. }
  23. // Manager returns the GUI manager singleton (creating it the first time)
  24. func Manager() *manager {
  25. // Return singleton if already created
  26. if gm != nil {
  27. return gm
  28. }
  29. gm = new(manager)
  30. gm.Dispatcher.Initialize()
  31. gm.TimerManager.Initialize()
  32. // Subscribe to window events
  33. gm.win = window.Get()
  34. gm.win.Subscribe(window.OnKeyUp, gm.onKeyboard)
  35. gm.win.Subscribe(window.OnKeyDown, gm.onKeyboard)
  36. gm.win.Subscribe(window.OnKeyRepeat, gm.onKeyboard)
  37. gm.win.Subscribe(window.OnChar, gm.onKeyboard)
  38. gm.win.Subscribe(window.OnCursor, gm.onCursor)
  39. gm.win.Subscribe(window.OnMouseUp, gm.onMouse)
  40. gm.win.Subscribe(window.OnMouseDown, gm.onMouse)
  41. gm.win.Subscribe(window.OnScroll, gm.onScroll)
  42. return gm
  43. }
  44. // Set sets the INode to watch for events.
  45. // It's usually a scene containing a hierarchy of INodes.
  46. // The manager only cares about IPanels inside that hierarchy.
  47. func (gm *manager) Set(scene core.INode) {
  48. gm.scene = scene
  49. }
  50. // SetModal sets the specified panel and its descendants to be the exclusive receivers of events.
  51. func (gm *manager) SetModal(ipan IPanel) {
  52. gm.modal = ipan
  53. gm.SetKeyFocus(nil)
  54. gm.SetCursorFocus(nil)
  55. }
  56. // SetKeyFocus sets the key-focused IDispatcher, which will exclusively receive key and char events.
  57. func (gm *manager) SetKeyFocus(disp core.IDispatcher) {
  58. if gm.keyFocus == disp {
  59. return
  60. }
  61. if gm.keyFocus != nil {
  62. gm.keyFocus.Dispatch(OnFocusLost, nil)
  63. }
  64. gm.keyFocus = disp
  65. if gm.keyFocus != nil {
  66. gm.keyFocus.Dispatch(OnFocus, nil)
  67. }
  68. }
  69. // SetCursorFocus sets the cursor-focused IDispatcher, which will exclusively receive OnCursor events.
  70. func (gm *manager) SetCursorFocus(disp core.IDispatcher) {
  71. if gm.cursorFocus == disp {
  72. return
  73. }
  74. gm.cursorFocus = disp
  75. if gm.cursorFocus == nil {
  76. gm.onCursor(OnCursor, gm.cev)
  77. }
  78. }
  79. // onKeyboard is called when char or key events are received.
  80. // The events are dispatched to the focused IDispatcher or to non-GUI.
  81. func (gm *manager) onKeyboard(evname string, ev interface{}) {
  82. if gm.keyFocus != nil {
  83. if gm.modal == nil {
  84. gm.keyFocus.Dispatch(evname, ev)
  85. } else if ipan, ok := gm.keyFocus.(IPanel); ok && gm.modal.IsAncestorOf(ipan) {
  86. gm.keyFocus.Dispatch(evname, ev)
  87. }
  88. } else {
  89. gm.Dispatch(evname, ev)
  90. }
  91. }
  92. // onMouse is called when mouse events are received.
  93. // OnMouseDown/OnMouseUp are dispatched to gm.target or to non-GUI, while
  94. // OnMouseDownOut/OnMouseUpOut are dispatched to all non-target panels.
  95. func (gm *manager) onMouse(evname string, ev interface{}) {
  96. // Check if gm.scene is nil and if so then there are no IPanels to send events to
  97. if gm.scene == nil {
  98. gm.Dispatch(evname, ev) // Dispatch event to non-GUI since event was not filtered by any GUI component
  99. return
  100. }
  101. // Dispatch OnMouseDownOut/OnMouseUpOut to all panels except ancestors of target
  102. gm.forEachIPanel(func(ipan IPanel) {
  103. if gm.target == nil || !ipan.IsAncestorOf(gm.target) {
  104. switch evname {
  105. case OnMouseDown:
  106. ipan.Dispatch(OnMouseDownOut, ev)
  107. case OnMouseUp:
  108. ipan.Dispatch(OnMouseUpOut, ev)
  109. }
  110. }
  111. })
  112. // Appropriately dispatch the event to target panel's lowest subscribed ancestor or to non-GUI or not at all
  113. if gm.target != nil {
  114. if gm.modal == nil || gm.modal.IsAncestorOf(gm.target) {
  115. sendAncestry(gm.target, false, nil, gm.modal, evname, ev)
  116. }
  117. } else if gm.modal == nil {
  118. gm.Dispatch(evname, ev)
  119. }
  120. }
  121. // onScroll is called when scroll events are received.
  122. // The events are dispatched to the target panel or to non-GUI.
  123. func (gm *manager) onScroll(evname string, ev interface{}) {
  124. // Check if gm.scene is nil and if so then there are no IPanels to send events to
  125. if gm.scene == nil {
  126. gm.Dispatch(evname, ev) // Dispatch event to non-GUI since event was not filtered by any GUI component
  127. return
  128. }
  129. // Appropriately dispatch the event to target panel's lowest subscribed ancestor or to non-GUI or not at all
  130. if gm.target != nil {
  131. if gm.modal == nil || gm.modal.IsAncestorOf(gm.target) {
  132. sendAncestry(gm.target, false, nil, gm.modal, evname, ev)
  133. }
  134. } else if gm.modal == nil {
  135. gm.Dispatch(evname, ev)
  136. }
  137. }
  138. // onCursor is called when (mouse) cursor events are received.
  139. // Updates the target/click panels and dispatches OnCursor, OnCursorEnter, OnCursorLeave events.
  140. func (gm *manager) onCursor(evname string, ev interface{}) {
  141. // If an IDispatcher is capturing cursor events dispatch to it and return
  142. if gm.cursorFocus != nil {
  143. gm.cursorFocus.Dispatch(evname, ev)
  144. return
  145. }
  146. // If gm.scene is nil then there are no IPanels to send events to
  147. if gm.scene == nil {
  148. gm.Dispatch(evname, ev) // Dispatch event to non-GUI since event was not filtered by any GUI component
  149. return
  150. }
  151. // Get and store CursorEvent
  152. gm.cev = ev.(*window.CursorEvent)
  153. // Temporarily store last target and clear current one
  154. oldTarget := gm.target
  155. gm.target = nil
  156. // Find IPanel immediately under the cursor and store it in gm.target
  157. gm.forEachIPanel(func(ipan IPanel) {
  158. if ipan.InsideBorders(gm.cev.Xpos, gm.cev.Ypos) && (gm.target == nil || ipan.Position().Z < gm.target.GetPanel().Position().Z) {
  159. gm.target = ipan
  160. }
  161. })
  162. // If the cursor is now over a different panel, dispatch OnCursorLeave/OnCursorEnter
  163. if gm.target != oldTarget {
  164. // We are only interested in sending events up to the lowest common ancestor of target and oldTarget
  165. var commonAnc IPanel
  166. if gm.target != nil && oldTarget != nil {
  167. commonAnc, _ = gm.target.LowestCommonAncestor(oldTarget).(IPanel)
  168. }
  169. // If just left a panel and the new panel is not a descendant of the old panel
  170. if oldTarget != nil && !oldTarget.IsAncestorOf(gm.target) && (gm.modal == nil || gm.modal.IsAncestorOf(oldTarget)) {
  171. sendAncestry(oldTarget, true, commonAnc, gm.modal, OnCursorLeave, ev)
  172. }
  173. // If just entered a panel and it's not an ancestor of the old panel
  174. if gm.target != nil && !gm.target.IsAncestorOf(oldTarget) && (gm.modal == nil || gm.modal.IsAncestorOf(gm.target)) {
  175. sendAncestry(gm.target, true, commonAnc, gm.modal, OnCursorEnter, ev)
  176. }
  177. }
  178. // Appropriately dispatch the event to target panel's lowest subscribed ancestor or to non-GUI or not at all
  179. if gm.target != nil {
  180. if gm.modal == nil || gm.modal.IsAncestorOf(gm.target) {
  181. sendAncestry(gm.target, false, nil, gm.modal, evname, ev)
  182. }
  183. } else if gm.modal == nil {
  184. gm.Dispatch(evname, ev)
  185. }
  186. }
  187. // sendAncestry sends the specified event (evname/ev) to the specified target panel and its ancestors.
  188. // If all is false, then the event is only sent to the lowest subscribed ancestor.
  189. // If uptoEx (i.e. excluding) is not nil then the event will not be dispatched to that ancestor nor any higher ancestors.
  190. // If uptoIn (i.e. including) is not nil then the event will be dispatched to that ancestor but not to any higher ancestors.
  191. // uptoEx and uptoIn can both be defined.
  192. func sendAncestry(ipan IPanel, all bool, uptoEx IPanel, uptoIn IPanel, evname string, ev interface{}) {
  193. var ok bool
  194. for ipan != nil {
  195. if uptoEx != nil && ipan == uptoEx {
  196. break
  197. }
  198. count := ipan.Dispatch(evname, ev)
  199. if (uptoIn != nil && ipan == uptoIn) || (!all && count > 0) {
  200. break
  201. }
  202. ipan, ok = ipan.Parent().(IPanel)
  203. if !ok {
  204. break
  205. }
  206. }
  207. }
  208. // traverseIPanel traverses the descendants of the provided IPanel,
  209. // executing the specified function for each IPanel.
  210. func traverseIPanel(ipan IPanel, f func(ipan IPanel)) {
  211. // If panel not visible, ignore entire hierarchy below this point
  212. if !ipan.Visible() {
  213. return
  214. }
  215. if ipan.Enabled() {
  216. f(ipan) // Call specified function
  217. }
  218. // Check descendants (can assume they are IPanels)
  219. for _, child := range ipan.Children() {
  220. traverseIPanel(child.(IPanel), f)
  221. }
  222. }
  223. // traverseINode traverses the descendants of the specified INode,
  224. // executing the specified function for each IPanel.
  225. func traverseINode(inode core.INode, f func(ipan IPanel)) {
  226. if ipan, ok := inode.(IPanel); ok {
  227. traverseIPanel(ipan, f)
  228. } else {
  229. for _, child := range inode.Children() {
  230. traverseINode(child, f)
  231. }
  232. }
  233. }
  234. // forEachIPanel executes the specified function for each enabled and visible IPanel in gm.scene.
  235. func (gm *manager) forEachIPanel(f func(ipan IPanel)) {
  236. traverseINode(gm.scene, f)
  237. }