window.go 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  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/math32"
  7. "github.com/g3n/engine/window"
  8. )
  9. /*********************************************
  10. Window panel
  11. +-----------------------------------------+
  12. | Title panel |
  13. +-----------------------------------------+
  14. | Content panel |
  15. | +-----------------------------------+ |
  16. | | | |
  17. | | | |
  18. | | | |
  19. | | | |
  20. | | | |
  21. | | | |
  22. | +-----------------------------------+ |
  23. | |
  24. +-----------------------------------------+
  25. *********************************************/
  26. type Window struct {
  27. Panel // Embedded Panel
  28. styles *WindowStyles
  29. title *WindowTitle // internal optional title panel
  30. client Panel // internal client panel
  31. resizable Resizable
  32. overBorder string
  33. drag bool
  34. mouseX float32
  35. mouseY float32
  36. }
  37. type WindowStyle struct {
  38. Border BorderSizes
  39. Paddings BorderSizes
  40. BorderColor math32.Color4
  41. TitleBorders BorderSizes
  42. TitleBorderColor math32.Color4
  43. TitleBgColor math32.Color4
  44. TitleFgColor math32.Color4
  45. }
  46. // All Window styles
  47. type WindowStyles struct {
  48. Normal WindowStyle
  49. Over WindowStyle
  50. Focus WindowStyle
  51. Disabled WindowStyle
  52. }
  53. type Resizable int
  54. const (
  55. ResizeTop = Resizable(1 << (iota + 1))
  56. ResizeRight
  57. ResizeBottom
  58. ResizeLeft
  59. ResizeAll = ResizeTop | ResizeRight | ResizeBottom | ResizeLeft
  60. )
  61. // NewWindow creates and returns a pointer to a new window with the
  62. // specified dimensions
  63. func NewWindow(width, height float32) *Window {
  64. w := new(Window)
  65. w.styles = &StyleDefault().Window
  66. w.Panel.Initialize(width, height)
  67. w.Panel.Subscribe(OnMouseDown, w.onMouse)
  68. w.Panel.Subscribe(OnMouseUp, w.onMouse)
  69. w.Panel.Subscribe(OnCursor, w.onCursor)
  70. w.Panel.Subscribe(OnCursorEnter, w.onCursor)
  71. w.Panel.Subscribe(OnCursorLeave, w.onCursor)
  72. w.Panel.Subscribe(OnResize, func(evname string, ev interface{}) { w.recalc() })
  73. w.client.Initialize(0, 0)
  74. w.Panel.Add(&w.client)
  75. w.recalc()
  76. w.update()
  77. return w
  78. }
  79. // SetResizable set the borders which are resizable
  80. func (w *Window) SetResizable(res Resizable) {
  81. w.resizable = res
  82. }
  83. // SetTitle sets the title of this window
  84. func (w *Window) SetTitle(text string) {
  85. if w.title == nil {
  86. w.title = newWindowTitle(w, text)
  87. w.Panel.Add(w.title)
  88. } else {
  89. w.title.label.SetText(text)
  90. }
  91. w.update()
  92. w.recalc()
  93. }
  94. // Add adds a child panel to the client area of this window
  95. func (w *Window) Add(ichild IPanel) *Window {
  96. w.client.Add(ichild)
  97. return w
  98. }
  99. // SetLayout set the layout of this window content area
  100. func (w *Window) SetLayout(layout ILayout) {
  101. w.client.SetLayout(layout)
  102. }
  103. // onMouse process subscribed mouse events over the window
  104. func (w *Window) onMouse(evname string, ev interface{}) {
  105. mev := ev.(*window.MouseEvent)
  106. switch evname {
  107. case OnMouseDown:
  108. par := w.Parent().(IPanel).GetPanel()
  109. par.SetTopChild(w)
  110. if w.overBorder != "" {
  111. w.drag = true
  112. w.mouseX = mev.Xpos
  113. w.mouseY = mev.Ypos
  114. w.root.SetMouseFocus(w)
  115. }
  116. case OnMouseUp:
  117. w.drag = false
  118. w.root.SetCursorNormal()
  119. w.root.SetMouseFocus(nil)
  120. default:
  121. return
  122. }
  123. w.root.StopPropagation(StopAll)
  124. }
  125. // onCursor process subscribed cursor events over the window
  126. func (w *Window) onCursor(evname string, ev interface{}) {
  127. if evname == OnCursor {
  128. cev := ev.(*window.CursorEvent)
  129. if !w.drag {
  130. cx := cev.Xpos - w.pospix.X
  131. cy := cev.Ypos - w.pospix.Y
  132. if cy <= w.borderSizes.Top {
  133. if w.resizable&ResizeTop != 0 {
  134. w.overBorder = "top"
  135. w.root.SetCursorVResize()
  136. }
  137. } else if cy >= w.height-w.borderSizes.Bottom {
  138. if w.resizable&ResizeBottom != 0 {
  139. w.overBorder = "bottom"
  140. w.root.SetCursorVResize()
  141. }
  142. } else if cx <= w.borderSizes.Left {
  143. if w.resizable&ResizeLeft != 0 {
  144. w.overBorder = "left"
  145. w.root.SetCursorHResize()
  146. }
  147. } else if cx >= w.width-w.borderSizes.Right {
  148. if w.resizable&ResizeRight != 0 {
  149. w.overBorder = "right"
  150. w.root.SetCursorHResize()
  151. }
  152. } else {
  153. if w.overBorder != "" {
  154. w.root.SetCursorNormal()
  155. w.overBorder = ""
  156. }
  157. }
  158. } else {
  159. switch w.overBorder {
  160. case "top":
  161. delta := cev.Ypos - w.mouseY
  162. w.mouseY = cev.Ypos
  163. newHeight := w.Height() - delta
  164. if newHeight < w.MinHeight() {
  165. return
  166. }
  167. w.SetPositionY(w.Position().Y + delta)
  168. w.SetHeight(newHeight)
  169. case "right":
  170. delta := cev.Xpos - w.mouseX
  171. w.mouseX = cev.Xpos
  172. newWidth := w.Width() + delta
  173. w.SetWidth(newWidth)
  174. case "bottom":
  175. delta := cev.Ypos - w.mouseY
  176. w.mouseY = cev.Ypos
  177. newHeight := w.Height() + delta
  178. w.SetHeight(newHeight)
  179. case "left":
  180. delta := cev.Xpos - w.mouseX
  181. w.mouseX = cev.Xpos
  182. newWidth := w.Width() - delta
  183. if newWidth < w.MinWidth() {
  184. return
  185. }
  186. w.SetPositionX(w.Position().X + delta)
  187. w.SetWidth(newWidth)
  188. }
  189. }
  190. } else if evname == OnCursorLeave {
  191. if !w.drag {
  192. w.root.SetCursorNormal()
  193. }
  194. }
  195. w.root.StopPropagation(StopAll)
  196. }
  197. // update updates the button visual state
  198. func (w *Window) update() {
  199. if !w.Enabled() {
  200. w.applyStyle(&w.styles.Disabled)
  201. return
  202. }
  203. w.applyStyle(&w.styles.Normal)
  204. }
  205. func (w *Window) applyStyle(s *WindowStyle) {
  206. w.SetBordersColor4(&s.BorderColor)
  207. w.SetBordersFrom(&s.Border)
  208. w.SetPaddingsFrom(&s.Paddings)
  209. if w.title != nil {
  210. w.title.applyStyle(s)
  211. }
  212. }
  213. // recalc recalculates the sizes and positions of the internal panels
  214. // from the outside to the inside.
  215. func (w *Window) recalc() {
  216. // Window title
  217. height := w.content.Height
  218. width := w.content.Width
  219. cx := float32(0)
  220. cy := float32(0)
  221. if w.title != nil {
  222. w.title.SetWidth(w.content.Width)
  223. w.title.recalc()
  224. height -= w.title.height
  225. cy = w.title.height
  226. }
  227. // Content area
  228. w.client.SetPosition(cx, cy)
  229. w.client.SetSize(width, height)
  230. }
  231. type WindowTitle struct {
  232. Panel // Embedded panel
  233. win *Window
  234. label Label
  235. pressed bool
  236. drag bool
  237. mouseX float32
  238. mouseY float32
  239. }
  240. // newWindowTitle creates and returns a pointer to a window title panel
  241. func newWindowTitle(win *Window, text string) *WindowTitle {
  242. wt := new(WindowTitle)
  243. wt.win = win
  244. wt.Panel.Initialize(0, 0)
  245. wt.label.initialize(text, StyleDefault().Font)
  246. wt.Panel.Add(&wt.label)
  247. wt.Subscribe(OnMouseDown, wt.onMouse)
  248. wt.Subscribe(OnMouseUp, wt.onMouse)
  249. wt.Subscribe(OnCursor, wt.onCursor)
  250. wt.Subscribe(OnCursorEnter, wt.onCursor)
  251. wt.Subscribe(OnCursorLeave, wt.onCursor)
  252. wt.recalc()
  253. return wt
  254. }
  255. // onMouse process subscribed mouse button events over the window title
  256. func (wt *WindowTitle) onMouse(evname string, ev interface{}) {
  257. mev := ev.(*window.MouseEvent)
  258. switch evname {
  259. case OnMouseDown:
  260. wt.pressed = true
  261. wt.mouseX = mev.Xpos
  262. wt.mouseY = mev.Ypos
  263. wt.win.root.SetMouseFocus(wt)
  264. case OnMouseUp:
  265. wt.pressed = false
  266. wt.win.root.SetMouseFocus(nil)
  267. default:
  268. return
  269. }
  270. wt.win.root.StopPropagation(Stop3D)
  271. }
  272. // onCursor process subscribed cursor events over the window title
  273. func (wt *WindowTitle) onCursor(evname string, ev interface{}) {
  274. if evname == OnCursorEnter {
  275. wt.win.root.SetCursorDrag()
  276. } else if evname == OnCursorLeave {
  277. wt.win.root.SetCursorNormal()
  278. } else if evname == OnCursor {
  279. if !wt.pressed {
  280. wt.win.root.StopPropagation(Stop3D)
  281. return
  282. }
  283. cev := ev.(*window.CursorEvent)
  284. dy := wt.mouseY - cev.Ypos
  285. dx := wt.mouseX - cev.Xpos
  286. wt.mouseX = cev.Xpos
  287. wt.mouseY = cev.Ypos
  288. posX := wt.win.Position().X - dx
  289. posY := wt.win.Position().Y - dy
  290. wt.win.SetPosition(posX, posY)
  291. }
  292. wt.win.root.StopPropagation(Stop3D)
  293. }
  294. // applyStyles sets the specified window title style
  295. func (wt *WindowTitle) applyStyle(s *WindowStyle) {
  296. wt.SetBordersFrom(&s.TitleBorders)
  297. wt.SetBordersColor4(&s.TitleBorderColor)
  298. wt.SetColor4(&s.TitleBgColor)
  299. wt.label.SetColor4(&s.TitleFgColor)
  300. }
  301. // recalc recalculates the height and position of the label in the title bar.
  302. func (wt *WindowTitle) recalc() {
  303. xpos := (wt.width - wt.label.width) / 2
  304. wt.label.SetPositionX(xpos)
  305. wt.SetContentHeight(wt.label.Height())
  306. }