cell.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. // Copyright 2022 The TCell Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use file except in compliance with the License.
  5. // You may obtain a copy of the license at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package tcell
  15. import (
  16. "os"
  17. runewidth "github.com/mattn/go-runewidth"
  18. )
  19. type cell struct {
  20. currMain rune
  21. currComb []rune
  22. currStyle Style
  23. lastMain rune
  24. lastStyle Style
  25. lastComb []rune
  26. width int
  27. }
  28. // CellBuffer represents a two dimensional array of character cells.
  29. // This is primarily intended for use by Screen implementors; it
  30. // contains much of the common code they need. To create one, just
  31. // declare a variable of its type; no explicit initialization is necessary.
  32. //
  33. // CellBuffer is not thread safe.
  34. type CellBuffer struct {
  35. w int
  36. h int
  37. cells []cell
  38. }
  39. // SetContent sets the contents (primary rune, combining runes,
  40. // and style) for a cell at a given location.
  41. func (cb *CellBuffer) SetContent(x int, y int,
  42. mainc rune, combc []rune, style Style) {
  43. if x >= 0 && y >= 0 && x < cb.w && y < cb.h {
  44. c := &cb.cells[(y*cb.w)+x]
  45. for i := 1; i < c.width; i++ {
  46. cb.SetDirty(x+i, y, true)
  47. }
  48. c.currComb = append([]rune{}, combc...)
  49. if c.currMain != mainc {
  50. c.width = runewidth.RuneWidth(mainc)
  51. }
  52. c.currMain = mainc
  53. c.currStyle = style
  54. }
  55. }
  56. // GetContent returns the contents of a character cell, including the
  57. // primary rune, any combining character runes (which will usually be
  58. // nil), the style, and the display width in cells. (The width can be
  59. // either 1, normally, or 2 for East Asian full-width characters.)
  60. func (cb *CellBuffer) GetContent(x, y int) (rune, []rune, Style, int) {
  61. var mainc rune
  62. var combc []rune
  63. var style Style
  64. var width int
  65. if x >= 0 && y >= 0 && x < cb.w && y < cb.h {
  66. c := &cb.cells[(y*cb.w)+x]
  67. mainc, combc, style = c.currMain, c.currComb, c.currStyle
  68. if width = c.width; width == 0 || mainc < ' ' {
  69. width = 1
  70. mainc = ' '
  71. }
  72. }
  73. return mainc, combc, style, width
  74. }
  75. // Size returns the (width, height) in cells of the buffer.
  76. func (cb *CellBuffer) Size() (int, int) {
  77. return cb.w, cb.h
  78. }
  79. // Invalidate marks all characters within the buffer as dirty.
  80. func (cb *CellBuffer) Invalidate() {
  81. for i := range cb.cells {
  82. cb.cells[i].lastMain = rune(0)
  83. }
  84. }
  85. // Dirty checks if a character at the given location needs an
  86. // to be refreshed on the physical display. This returns true
  87. // if the cell content is different since the last time it was
  88. // marked clean.
  89. func (cb *CellBuffer) Dirty(x, y int) bool {
  90. if x >= 0 && y >= 0 && x < cb.w && y < cb.h {
  91. c := &cb.cells[(y*cb.w)+x]
  92. if c.lastMain == rune(0) {
  93. return true
  94. }
  95. if c.lastMain != c.currMain {
  96. return true
  97. }
  98. if c.lastStyle != c.currStyle {
  99. return true
  100. }
  101. if len(c.lastComb) != len(c.currComb) {
  102. return true
  103. }
  104. for i := range c.lastComb {
  105. if c.lastComb[i] != c.currComb[i] {
  106. return true
  107. }
  108. }
  109. }
  110. return false
  111. }
  112. // SetDirty is normally used to indicate that a cell has
  113. // been displayed (in which case dirty is false), or to manually
  114. // force a cell to be marked dirty.
  115. func (cb *CellBuffer) SetDirty(x, y int, dirty bool) {
  116. if x >= 0 && y >= 0 && x < cb.w && y < cb.h {
  117. c := &cb.cells[(y*cb.w)+x]
  118. if dirty {
  119. c.lastMain = rune(0)
  120. } else {
  121. if c.currMain == rune(0) {
  122. c.currMain = ' '
  123. }
  124. c.lastMain = c.currMain
  125. c.lastComb = c.currComb
  126. c.lastStyle = c.currStyle
  127. }
  128. }
  129. }
  130. // Resize is used to resize the cells array, with different dimensions,
  131. // while preserving the original contents. The cells will be invalidated
  132. // so that they can be redrawn.
  133. func (cb *CellBuffer) Resize(w, h int) {
  134. if cb.h == h && cb.w == w {
  135. return
  136. }
  137. newc := make([]cell, w*h)
  138. for y := 0; y < h && y < cb.h; y++ {
  139. for x := 0; x < w && x < cb.w; x++ {
  140. oc := &cb.cells[(y*cb.w)+x]
  141. nc := &newc[(y*w)+x]
  142. nc.currMain = oc.currMain
  143. nc.currComb = oc.currComb
  144. nc.currStyle = oc.currStyle
  145. nc.width = oc.width
  146. nc.lastMain = rune(0)
  147. }
  148. }
  149. cb.cells = newc
  150. cb.h = h
  151. cb.w = w
  152. }
  153. // Fill fills the entire cell buffer array with the specified character
  154. // and style. Normally choose ' ' to clear the screen. This API doesn't
  155. // support combining characters, or characters with a width larger than one.
  156. func (cb *CellBuffer) Fill(r rune, style Style) {
  157. for i := range cb.cells {
  158. c := &cb.cells[i]
  159. c.currMain = r
  160. c.currComb = nil
  161. c.currStyle = style
  162. c.width = 1
  163. }
  164. }
  165. var runeConfig *runewidth.Condition
  166. func init() {
  167. // The defaults for the runewidth package are poorly chosen for terminal
  168. // applications. We however will honor the setting in the environment if
  169. // it is set.
  170. if os.Getenv("RUNEWIDTH_EASTASIAN") == "" {
  171. runewidth.DefaultCondition.EastAsianWidth = false
  172. }
  173. // For performance reasons, we create a lookup table. However some users
  174. // might be more memory conscious. If that's you, set the TCELL_MINIMIZE
  175. // environment variable.
  176. if os.Getenv("TCELL_MINIMIZE") == "" {
  177. runewidth.CreateLUT()
  178. }
  179. }