root.go 11 KB

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