manager.go 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  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. // To fix #299
  97. if gm.cev == nil {
  98. mev := ev.(*window.MouseEvent)
  99. gm.cev = &window.CursorEvent{
  100. Xpos: mev.Xpos,
  101. Ypos: mev.Ypos,
  102. Mods: mev.Mods,
  103. }
  104. }
  105. // Check if gm.scene is nil and if so then there are no IPanels to send events to
  106. if gm.scene == nil {
  107. gm.Dispatch(evname, ev) // Dispatch event to non-GUI since event was not filtered by any GUI component
  108. return
  109. }
  110. // Dispatch OnMouseDownOut/OnMouseUpOut to all panels except ancestors of target
  111. gm.forEachIPanel(func(ipan IPanel) {
  112. if gm.target == nil || !ipan.IsAncestorOf(gm.target) {
  113. switch evname {
  114. case OnMouseDown:
  115. ipan.Dispatch(OnMouseDownOut, ev)
  116. case OnMouseUp:
  117. ipan.Dispatch(OnMouseUpOut, ev)
  118. }
  119. }
  120. })
  121. // Appropriately dispatch the event to target panel's lowest subscribed ancestor or to non-GUI or not at all
  122. if gm.target != nil {
  123. if gm.modal == nil || gm.modal.IsAncestorOf(gm.target) {
  124. sendAncestry(gm.target, false, nil, gm.modal, evname, ev)
  125. }
  126. } else if gm.modal == nil {
  127. gm.Dispatch(evname, ev)
  128. }
  129. }
  130. // onScroll is called when scroll events are received.
  131. // The events are dispatched to the target panel or to non-GUI.
  132. func (gm *manager) onScroll(evname string, ev interface{}) {
  133. // Check if gm.scene is nil and if so then there are no IPanels to send events to
  134. if gm.scene == nil {
  135. gm.Dispatch(evname, ev) // Dispatch event to non-GUI since event was not filtered by any GUI component
  136. return
  137. }
  138. // Appropriately dispatch the event to target panel's lowest subscribed ancestor or to non-GUI or not at all
  139. if gm.target != nil {
  140. if gm.modal == nil || gm.modal.IsAncestorOf(gm.target) {
  141. sendAncestry(gm.target, false, nil, gm.modal, evname, ev)
  142. }
  143. } else if gm.modal == nil {
  144. gm.Dispatch(evname, ev)
  145. }
  146. }
  147. // onCursor is called when (mouse) cursor events are received.
  148. // Updates the target/click panels and dispatches OnCursor, OnCursorEnter, OnCursorLeave events.
  149. func (gm *manager) onCursor(evname string, ev interface{}) {
  150. // If an IDispatcher is capturing cursor events dispatch to it and return
  151. if gm.cursorFocus != nil {
  152. gm.cursorFocus.Dispatch(evname, ev)
  153. return
  154. }
  155. // If gm.scene is nil then there are no IPanels to send events to
  156. if gm.scene == nil {
  157. gm.Dispatch(evname, ev) // Dispatch event to non-GUI since event was not filtered by any GUI component
  158. return
  159. }
  160. // Get and store CursorEvent
  161. gm.cev = ev.(*window.CursorEvent)
  162. // Temporarily store last target and clear current one
  163. oldTarget := gm.target
  164. gm.target = nil
  165. // Find IPanel immediately under the cursor and store it in gm.target
  166. gm.forEachIPanel(func(ipan IPanel) {
  167. if ipan.InsideBorders(gm.cev.Xpos, gm.cev.Ypos) && (gm.target == nil || ipan.Position().Z < gm.target.GetPanel().Position().Z) {
  168. gm.target = ipan
  169. }
  170. })
  171. // If the cursor is now over a different panel, dispatch OnCursorLeave/OnCursorEnter
  172. if gm.target != oldTarget {
  173. // We are only interested in sending events up to the lowest common ancestor of target and oldTarget
  174. var commonAnc IPanel
  175. if gm.target != nil && oldTarget != nil {
  176. commonAnc, _ = gm.target.LowestCommonAncestor(oldTarget).(IPanel)
  177. }
  178. // If just left a panel and the new panel is not a descendant of the old panel
  179. if oldTarget != nil && !oldTarget.IsAncestorOf(gm.target) && (gm.modal == nil || gm.modal.IsAncestorOf(oldTarget)) {
  180. sendAncestry(oldTarget, true, commonAnc, gm.modal, OnCursorLeave, ev)
  181. }
  182. // If just entered a panel and it's not an ancestor of the old panel
  183. if gm.target != nil && !gm.target.IsAncestorOf(oldTarget) && (gm.modal == nil || gm.modal.IsAncestorOf(gm.target)) {
  184. sendAncestry(gm.target, true, commonAnc, gm.modal, OnCursorEnter, ev)
  185. }
  186. }
  187. // Appropriately dispatch the event to target panel's lowest subscribed ancestor or to non-GUI or not at all
  188. if gm.target != nil {
  189. if gm.modal == nil || gm.modal.IsAncestorOf(gm.target) {
  190. sendAncestry(gm.target, false, nil, gm.modal, evname, ev)
  191. }
  192. } else if gm.modal == nil {
  193. gm.Dispatch(evname, ev)
  194. }
  195. }
  196. // sendAncestry sends the specified event (evname/ev) to the specified target panel and its ancestors.
  197. // If all is false, then the event is only sent to the lowest subscribed ancestor.
  198. // If uptoEx (i.e. excluding) is not nil then the event will not be dispatched to that ancestor nor any higher ancestors.
  199. // If uptoIn (i.e. including) is not nil then the event will be dispatched to that ancestor but not to any higher ancestors.
  200. // uptoEx and uptoIn can both be defined.
  201. func sendAncestry(ipan IPanel, all bool, uptoEx IPanel, uptoIn IPanel, evname string, ev interface{}) {
  202. var ok bool
  203. for ipan != nil {
  204. if uptoEx != nil && ipan == uptoEx {
  205. break
  206. }
  207. count := ipan.Dispatch(evname, ev)
  208. if (uptoIn != nil && ipan == uptoIn) || (!all && count > 0) {
  209. break
  210. }
  211. ipan, ok = ipan.Parent().(IPanel)
  212. if !ok {
  213. break
  214. }
  215. }
  216. }
  217. // traverseIPanel traverses the descendants of the provided IPanel,
  218. // executing the specified function for each IPanel.
  219. func traverseIPanel(ipan IPanel, f func(ipan IPanel)) {
  220. // If panel not visible, ignore entire hierarchy below this point
  221. if !ipan.Visible() {
  222. return
  223. }
  224. if ipan.Enabled() {
  225. f(ipan) // Call specified function
  226. }
  227. // Check descendants (can assume they are IPanels)
  228. for _, child := range ipan.Children() {
  229. traverseIPanel(child.(IPanel), f)
  230. }
  231. }
  232. // traverseINode traverses the descendants of the specified INode,
  233. // executing the specified function for each IPanel.
  234. func traverseINode(inode core.INode, f func(ipan IPanel)) {
  235. if ipan, ok := inode.(IPanel); ok {
  236. traverseIPanel(ipan, f)
  237. } else {
  238. for _, child := range inode.Children() {
  239. traverseINode(child, f)
  240. }
  241. }
  242. }
  243. // forEachIPanel executes the specified function for each enabled and visible IPanel in gm.scene.
  244. func (gm *manager) forEachIPanel(f func(ipan IPanel)) {
  245. traverseINode(gm.scene, f)
  246. }