This is the absolute bomb semi-graphical text user interface library, I had to mirror it.
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.

pages.go 6.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. package tview
  2. import (
  3. "git.parallelcoin.io/dev/tcell"
  4. )
  5. // page represents one page of a Pages object.
  6. type page struct {
  7. Name string // The page's name.
  8. Item Primitive // The page's primitive.
  9. Resize bool // Whether or not to resize the page when it is drawn.
  10. Visible bool // Whether or not this page is visible.
  11. }
  12. // Pages is a container for other primitives often used as the application's
  13. // root primitive. It allows to easily switch the visibility of the contained
  14. // primitives.
  15. //
  16. // See https://git.parallelcoin.io/dev/tview/wiki/Pages for an example.
  17. type Pages struct {
  18. *Box
  19. // The contained pages.
  20. pages []*page
  21. // We keep a reference to the function which allows us to set the focus to
  22. // a newly visible page.
  23. setFocus func(p Primitive)
  24. // An optional handler which is called whenever the visibility or the order of
  25. // pages changes.
  26. changed func()
  27. }
  28. // NewPages returns a new Pages object.
  29. func NewPages() *Pages {
  30. p := &Pages{
  31. Box: NewBox(),
  32. }
  33. p.focus = p
  34. return p
  35. }
  36. // SetChangedFunc sets a handler which is called whenever the visibility or the
  37. // order of any visible pages changes. This can be used to redraw the pages.
  38. func (p *Pages) SetChangedFunc(handler func()) *Pages {
  39. p.changed = handler
  40. return p
  41. }
  42. // AddPage adds a new page with the given name and primitive. If there was
  43. // previously a page with the same name, it is overwritten. Leaving the name
  44. // empty may cause conflicts in other functions so always specify a non-empty
  45. // name.
  46. //
  47. // Visible pages will be drawn in the order they were added (unless that order
  48. // was changed in one of the other functions). If "resize" is set to true, the
  49. // primitive will be set to the size available to the Pages primitive whenever
  50. // the pages are drawn.
  51. func (p *Pages) AddPage(name string, item Primitive, resize, visible bool) *Pages {
  52. hasFocus := p.HasFocus()
  53. for index, pg := range p.pages {
  54. if pg.Name == name {
  55. p.pages = append(p.pages[:index], p.pages[index+1:]...)
  56. break
  57. }
  58. }
  59. p.pages = append(p.pages, &page{Item: item, Name: name, Resize: resize, Visible: visible})
  60. if p.changed != nil {
  61. p.changed()
  62. }
  63. if hasFocus {
  64. p.Focus(p.setFocus)
  65. }
  66. return p
  67. }
  68. // AddAndSwitchToPage calls AddPage(), then SwitchToPage() on that newly added
  69. // page.
  70. func (p *Pages) AddAndSwitchToPage(name string, item Primitive, resize bool) *Pages {
  71. p.AddPage(name, item, resize, true)
  72. p.SwitchToPage(name)
  73. return p
  74. }
  75. // RemovePage removes the page with the given name. If that page was the only
  76. // visible page, visibility is assigned to the last page.
  77. func (p *Pages) RemovePage(name string) *Pages {
  78. var isVisible bool
  79. hasFocus := p.HasFocus()
  80. for index, page := range p.pages {
  81. if page.Name == name {
  82. isVisible = page.Visible
  83. p.pages = append(p.pages[:index], p.pages[index+1:]...)
  84. if page.Visible && p.changed != nil {
  85. p.changed()
  86. }
  87. break
  88. }
  89. }
  90. if isVisible {
  91. for index, page := range p.pages {
  92. if index < len(p.pages)-1 {
  93. if page.Visible {
  94. break // There is a remaining visible page.
  95. }
  96. } else {
  97. page.Visible = true // We need at least one visible page.
  98. }
  99. }
  100. }
  101. if hasFocus {
  102. p.Focus(p.setFocus)
  103. }
  104. return p
  105. }
  106. // HasPage returns true if a page with the given name exists in this object.
  107. func (p *Pages) HasPage(name string) bool {
  108. for _, page := range p.pages {
  109. if page.Name == name {
  110. return true
  111. }
  112. }
  113. return false
  114. }
  115. // ShowPage sets a page's visibility to "true" (in addition to any other pages
  116. // which are already visible).
  117. func (p *Pages) ShowPage(name string) *Pages {
  118. for _, page := range p.pages {
  119. if page.Name == name {
  120. page.Visible = true
  121. if p.changed != nil {
  122. p.changed()
  123. }
  124. break
  125. }
  126. }
  127. if p.HasFocus() {
  128. p.Focus(p.setFocus)
  129. }
  130. return p
  131. }
  132. // HidePage sets a page's visibility to "false".
  133. func (p *Pages) HidePage(name string) *Pages {
  134. for _, page := range p.pages {
  135. if page.Name == name {
  136. page.Visible = false
  137. if p.changed != nil {
  138. p.changed()
  139. }
  140. break
  141. }
  142. }
  143. if p.HasFocus() {
  144. p.Focus(p.setFocus)
  145. }
  146. return p
  147. }
  148. // SwitchToPage sets a page's visibility to "true" and all other pages'
  149. // visibility to "false".
  150. func (p *Pages) SwitchToPage(name string) *Pages {
  151. for _, page := range p.pages {
  152. if page.Name == name {
  153. page.Visible = true
  154. } else {
  155. page.Visible = false
  156. }
  157. }
  158. if p.changed != nil {
  159. p.changed()
  160. }
  161. if p.HasFocus() {
  162. p.Focus(p.setFocus)
  163. }
  164. return p
  165. }
  166. // SendToFront changes the order of the pages such that the page with the given
  167. // name comes last, causing it to be drawn last with the next update (if
  168. // visible).
  169. func (p *Pages) SendToFront(name string) *Pages {
  170. for index, page := range p.pages {
  171. if page.Name == name {
  172. if index < len(p.pages)-1 {
  173. p.pages = append(append(p.pages[:index], p.pages[index+1:]...), page)
  174. }
  175. if page.Visible && p.changed != nil {
  176. p.changed()
  177. }
  178. break
  179. }
  180. }
  181. if p.HasFocus() {
  182. p.Focus(p.setFocus)
  183. }
  184. return p
  185. }
  186. // SendToBack changes the order of the pages such that the page with the given
  187. // name comes first, causing it to be drawn first with the next update (if
  188. // visible).
  189. func (p *Pages) SendToBack(name string) *Pages {
  190. for index, pg := range p.pages {
  191. if pg.Name == name {
  192. if index > 0 {
  193. p.pages = append(append([]*page{pg}, p.pages[:index]...), p.pages[index+1:]...)
  194. }
  195. if pg.Visible && p.changed != nil {
  196. p.changed()
  197. }
  198. break
  199. }
  200. }
  201. if p.HasFocus() {
  202. p.Focus(p.setFocus)
  203. }
  204. return p
  205. }
  206. // HasFocus returns whether or not this primitive has focus.
  207. func (p *Pages) HasFocus() bool {
  208. for _, page := range p.pages {
  209. if page.Item.GetFocusable().HasFocus() {
  210. return true
  211. }
  212. }
  213. return false
  214. }
  215. // Focus is called by the application when the primitive receives focus.
  216. func (p *Pages) Focus(delegate func(p Primitive)) {
  217. if delegate == nil {
  218. return // We cannot delegate so we cannot focus.
  219. }
  220. p.setFocus = delegate
  221. var topItem Primitive
  222. for _, page := range p.pages {
  223. if page.Visible {
  224. topItem = page.Item
  225. }
  226. }
  227. if topItem != nil {
  228. delegate(topItem)
  229. }
  230. }
  231. // Draw draws this primitive onto the screen.
  232. func (p *Pages) Draw(screen tcell.Screen) {
  233. p.Box.Draw(screen)
  234. for _, page := range p.pages {
  235. if !page.Visible {
  236. continue
  237. }
  238. if page.Resize {
  239. x, y, width, height := p.GetInnerRect()
  240. page.Item.SetRect(x, y, width, height)
  241. }
  242. page.Item.Draw(screen)
  243. }
  244. }