User interface toolkit for Go with support for SDL2 and HTML Canvas render targets.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

191 lines
4.5 KiB

  1. package ui
  2. import (
  3. "errors"
  4. "fmt"
  5. "git.kirsle.net/go/render"
  6. )
  7. /*
  8. window_manager.go holds data types and Supervisor methods related to the
  9. management of ui.Window widgets.
  10. */
  11. // Window button options. OR these together in a call to Window.SetButtons().
  12. const (
  13. CloseButton = 0x01
  14. // NOTICE: MaximizeButton behavior is currently buggy, window doesn't
  15. // redraw itself at the new size properly.
  16. MaximizeButton = 0x02
  17. // Minimize button has no default behavior attached; you can bind it with
  18. // window.Handle(MinimizeWindow) to set your own event handler.
  19. MinimizeButton = 0x04
  20. )
  21. // FocusedWindow is a doubly-linked list of recently focused Windows, with
  22. // the current and most-recently focused on top. TODO make not exported.
  23. type FocusedWindow struct {
  24. window *Window
  25. prev *FocusedWindow
  26. next *FocusedWindow
  27. }
  28. // String of the FocusedWindow returns the underlying Window's String().
  29. func (fw FocusedWindow) String() string {
  30. return fw.window.String()
  31. }
  32. // Print the structure of the linked list from top to bottom.
  33. func (fw *FocusedWindow) Print() {
  34. var (
  35. node = fw
  36. i = 0
  37. )
  38. for node != nil {
  39. fmt.Printf("[%d] window=%s prev=%s next=%s\n",
  40. i, node.window, node.prev, node.next,
  41. )
  42. node = node.next
  43. i++
  44. }
  45. }
  46. // addWindow installs a Window into the supervisor to be managed. It is called
  47. // by ui.Window.Supervise() and the newly added window becomes the focused
  48. // one by default at the top of the linked list.
  49. func (s *Supervisor) addWindow(win *Window) {
  50. // Record in the window that it is managed by Supervisor, useful to control
  51. // event propagation to non-focused windows.
  52. win.managed = true
  53. if s.winFocus == nil {
  54. // First window added.
  55. s.winFocus = &FocusedWindow{
  56. window: win,
  57. }
  58. s.winTop = s.winFocus
  59. s.winBottom = s.winFocus
  60. win.SetFocus(true)
  61. } else {
  62. // New window, make it the top one.
  63. oldTop := s.winFocus
  64. s.winFocus = &FocusedWindow{
  65. window: win,
  66. next: oldTop,
  67. }
  68. oldTop.prev = s.winFocus
  69. oldTop.window.SetFocus(false)
  70. win.SetFocus(true)
  71. }
  72. }
  73. // FocusWindow brings the given window to the top of the supervisor's focus.
  74. //
  75. // The window must have previously been added to the supervisor's Window Manager
  76. // by calling the Supervise() method of the window.
  77. func (s *Supervisor) FocusWindow(win *Window) error {
  78. if s.winFocus == nil {
  79. return errors.New("no windows managed by supervisor")
  80. }
  81. // If the top window is already the target, return.
  82. if s.winFocus.window == win {
  83. return nil
  84. }
  85. // Find the window in the linked list.
  86. var (
  87. item = s.winFocus // item as we iterate the list
  88. oldTop = s.winFocus // original first item in the list
  89. target *FocusedWindow // identified target window to raise
  90. newBottom *FocusedWindow // if the target was the bottom, this is new bottom
  91. i = 0
  92. )
  93. for item != nil {
  94. if item.window == win {
  95. // Found it!
  96. target = item
  97. // Is it the last window in the list? Record the new bottom node.
  98. if item.next == nil && item.prev != nil {
  99. newBottom = item.prev
  100. }
  101. // Remove it from its position in the linked list. Join its
  102. // previous and next nodes to bridge the gap.
  103. if item.next != nil {
  104. item.next.prev = item.prev
  105. }
  106. if item.prev != nil {
  107. item.prev.next = item.next
  108. }
  109. break
  110. }
  111. item = item.next
  112. i++
  113. }
  114. // Found it?
  115. if target != nil {
  116. // Put the target at the top of the list, pointing to the old top.
  117. target.next = oldTop
  118. target.prev = nil
  119. oldTop.prev = target
  120. s.winFocus = target
  121. // Fix the top and bottom pointers.
  122. s.winTop = s.winFocus
  123. if newBottom != nil {
  124. s.winBottom = newBottom
  125. }
  126. // Toggle the focus states.
  127. oldTop.window.SetFocus(false)
  128. target.window.SetFocus(true)
  129. }
  130. return nil
  131. }
  132. // IsPointInWindow returns whether the given Point overlaps with a window managed
  133. // by the Supervisor.
  134. func (s *Supervisor) IsPointInWindow(point render.Point) bool {
  135. node := s.winFocus
  136. for node != nil {
  137. if point.Inside(AbsoluteRect(node.window)) && !node.window.hidden {
  138. return true
  139. }
  140. node = node.next
  141. }
  142. return false
  143. }
  144. // CloseAllWindows closes all open windows being managed by supervisor.
  145. // Returns the number of windows closed.
  146. func (s *Supervisor) CloseAllWindows() int {
  147. var (
  148. node = s.winFocus
  149. i = 0
  150. )
  151. for node != nil {
  152. i++
  153. node.window.Hide()
  154. node = node.next
  155. }
  156. return i
  157. }
  158. // presentWindows draws the windows from bottom to top.
  159. func (s *Supervisor) presentWindows(e render.Engine) {
  160. item := s.winBottom
  161. for item != nil {
  162. item.window.Compute(e)
  163. item.window.Present(e, item.window.Point())
  164. item = item.prev
  165. }
  166. }