folder.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  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. )
  8. // Folder represents a folder GUI element.
  9. type Folder struct {
  10. Panel // Embedded panel
  11. label Label // Folder label
  12. icon Label // Folder icon
  13. contentPanel IPanel // Content panel
  14. styles *FolderStyles
  15. cursorOver bool
  16. alignRight bool
  17. }
  18. // FolderStyle contains the styling of a Folder.
  19. type FolderStyle struct {
  20. PanelStyle
  21. FgColor math32.Color4
  22. Icons [2]string
  23. }
  24. // FolderStyles contains a FolderStyle for each valid GUI state.
  25. type FolderStyles struct {
  26. Normal FolderStyle
  27. Over FolderStyle
  28. Focus FolderStyle
  29. Disabled FolderStyle
  30. }
  31. // NewFolder creates and returns a pointer to a new folder widget
  32. // with the specified text and initial width.
  33. func NewFolder(text string, width float32, contentPanel IPanel) *Folder {
  34. f := new(Folder)
  35. f.Initialize(text, width, contentPanel)
  36. return f
  37. }
  38. // Initialize initializes the Folder with the specified text and initial width
  39. // It is normally used when the folder is embedded in another object.
  40. func (f *Folder) Initialize(text string, width float32, contentPanel IPanel) {
  41. f.Panel.Initialize(f, width, 0)
  42. f.styles = &StyleDefault().Folder
  43. // Initialize label
  44. f.label.initialize(text, StyleDefault().Font)
  45. f.Panel.Add(&f.label)
  46. // Create icon
  47. f.icon.initialize("", StyleDefault().FontIcon)
  48. f.icon.SetFontSize(StyleDefault().Label.PointSize * 1.3)
  49. f.Panel.Add(&f.icon)
  50. // Setup content panel
  51. f.contentPanel = contentPanel
  52. contentPanel.GetPanel().bounded = false
  53. contentPanel.GetPanel().zLayerDelta = 1
  54. contentPanel.GetPanel().SetVisible(false)
  55. f.Panel.Add(f.contentPanel)
  56. // Set event callbacks
  57. f.Panel.Subscribe(OnMouseDown, f.onMouse)
  58. f.Panel.Subscribe(OnCursorEnter, f.onCursor)
  59. f.Panel.Subscribe(OnCursorLeave, f.onCursor)
  60. f.Subscribe(OnMouseDownOut, func(s string, i interface{}) {
  61. // Hide list when clicked out
  62. if f.contentPanel.Visible() {
  63. f.contentPanel.SetVisible(false)
  64. }
  65. })
  66. f.contentPanel.Subscribe(OnCursorEnter, func(evname string, ev interface{}) {
  67. f.Dispatch(OnCursorLeave, ev)
  68. })
  69. f.contentPanel.Subscribe(OnCursorLeave, func(evname string, ev interface{}) {
  70. f.Dispatch(OnCursorEnter, ev)
  71. })
  72. f.alignRight = true
  73. f.update()
  74. f.recalc()
  75. }
  76. // SetStyles set the folder styles overriding the default style.
  77. func (f *Folder) SetStyles(fs *FolderStyles) {
  78. f.styles = fs
  79. f.update()
  80. }
  81. // SetAlignRight sets the side of the alignment of the content panel
  82. // in relation to the folder.
  83. func (f *Folder) SetAlignRight(state bool) {
  84. f.alignRight = state
  85. f.recalc()
  86. }
  87. // Height returns this folder total height
  88. // considering the contents panel, if visible.
  89. func (f *Folder) Height() float32 {
  90. height := f.Height()
  91. if f.contentPanel.GetPanel().Visible() {
  92. height += f.contentPanel.GetPanel().Height()
  93. }
  94. return height
  95. }
  96. // onMouse receives mouse button events over the folder panel.
  97. func (f *Folder) onMouse(evname string, ev interface{}) {
  98. switch evname {
  99. case OnMouseDown:
  100. cont := f.contentPanel.GetPanel()
  101. if !cont.Visible() {
  102. cont.SetVisible(true)
  103. } else {
  104. cont.SetVisible(false)
  105. }
  106. f.update()
  107. f.recalc()
  108. default:
  109. return
  110. }
  111. }
  112. // onCursor receives cursor events over the folder panel
  113. func (f *Folder) onCursor(evname string, ev interface{}) {
  114. switch evname {
  115. case OnCursorEnter:
  116. f.cursorOver = true
  117. f.update()
  118. case OnCursorLeave:
  119. f.cursorOver = false
  120. f.update()
  121. default:
  122. return
  123. }
  124. }
  125. // update updates the folder visual state
  126. func (f *Folder) update() {
  127. if f.cursorOver {
  128. f.applyStyle(&f.styles.Over)
  129. return
  130. }
  131. f.applyStyle(&f.styles.Normal)
  132. }
  133. // applyStyle applies the specified style
  134. func (f *Folder) applyStyle(s *FolderStyle) {
  135. f.Panel.ApplyStyle(&s.PanelStyle)
  136. icode := 0
  137. if f.contentPanel.GetPanel().Visible() {
  138. icode = 1
  139. }
  140. f.icon.SetText(string(s.Icons[icode]))
  141. f.icon.SetColor4(&s.FgColor)
  142. f.label.SetBgColor4(&s.BgColor)
  143. f.label.SetColor4(&s.FgColor)
  144. }
  145. func (f *Folder) recalc() {
  146. // icon position
  147. f.icon.SetPosition(0, 0)
  148. // Label position and width
  149. f.label.SetPosition(f.icon.Width()+4, 0)
  150. f.Panel.SetContentHeight(f.label.Height())
  151. // Sets position of the base folder scroller panel
  152. cont := f.contentPanel.GetPanel()
  153. if f.alignRight {
  154. cont.SetPosition(0, f.Panel.Height())
  155. } else {
  156. dx := cont.Width() - f.Panel.Width()
  157. cont.SetPosition(-dx, f.Panel.Height())
  158. }
  159. }