edit.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  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/text"
  8. "github.com/g3n/engine/window"
  9. "strings"
  10. "time"
  11. )
  12. // Edit represents a text edit box GUI element
  13. type Edit struct {
  14. Label // Embedded label
  15. MaxLength int // Maximum number of characters
  16. width int // edit width in pixels
  17. placeHolder string // place holder string
  18. text string // current edit text
  19. col int // current column
  20. focus bool // key focus flag
  21. cursorOver bool
  22. blinkID int
  23. caretOn bool
  24. styles *EditStyles
  25. }
  26. // EditStyle contains the styling of an Edit
  27. type EditStyle struct {
  28. Border RectBounds
  29. Paddings RectBounds
  30. BorderColor math32.Color4
  31. BgColor math32.Color4
  32. BgAlpha float32
  33. FgColor math32.Color4
  34. HolderColor math32.Color4
  35. }
  36. // EditStyles contains an EditStyle for each valid GUI state
  37. type EditStyles struct {
  38. Normal EditStyle
  39. Over EditStyle
  40. Focus EditStyle
  41. Disabled EditStyle
  42. }
  43. const (
  44. editMarginX = 4
  45. blinkTime = 1000
  46. )
  47. // NewEdit creates and returns a pointer to a new edit widget
  48. func NewEdit(width int, placeHolder string) *Edit {
  49. ed := new(Edit)
  50. ed.width = width
  51. ed.placeHolder = placeHolder
  52. ed.styles = &StyleDefault().Edit
  53. ed.text = ""
  54. ed.MaxLength = 80
  55. ed.col = 0
  56. ed.focus = false
  57. ed.Label.initialize("", StyleDefault().Font)
  58. ed.Label.Subscribe(OnKeyDown, ed.onKey)
  59. ed.Label.Subscribe(OnKeyRepeat, ed.onKey)
  60. ed.Label.Subscribe(OnChar, ed.onChar)
  61. ed.Label.Subscribe(OnMouseDown, ed.onMouse)
  62. ed.Label.Subscribe(OnCursorEnter, ed.onCursor)
  63. ed.Label.Subscribe(OnCursorLeave, ed.onCursor)
  64. ed.Label.Subscribe(OnEnable, func(evname string, ev interface{}) { ed.update() })
  65. ed.update()
  66. return ed
  67. }
  68. // SetText sets this edit text
  69. func (ed *Edit) SetText(text string) *Edit {
  70. // Remove new lines from text
  71. ed.text = strings.Replace(text, "\n", "", -1)
  72. ed.update()
  73. return ed
  74. }
  75. // Text returns the current edited text
  76. func (ed *Edit) Text() string {
  77. return ed.text
  78. }
  79. // SetFontSize sets label font size (overrides Label.SetFontSize)
  80. func (ed *Edit) SetFontSize(size float64) *Edit {
  81. ed.Label.SetFontSize(size)
  82. ed.redraw(ed.focus)
  83. return ed
  84. }
  85. // SetStyles set the button styles overriding the default style
  86. func (ed *Edit) SetStyles(es *EditStyles) {
  87. ed.styles = es
  88. ed.update()
  89. }
  90. // LostKeyFocus satisfies the IPanel interface and is called by gui root
  91. // container when the panel loses the key focus
  92. func (ed *Edit) LostKeyFocus() {
  93. ed.focus = false
  94. ed.update()
  95. ed.root.ClearTimeout(ed.blinkID)
  96. }
  97. // CursorPos sets the position of the cursor at the
  98. // specified column if possible
  99. func (ed *Edit) CursorPos(col int) {
  100. if col <= text.StrCount(ed.text) {
  101. ed.col = col
  102. ed.redraw(ed.focus)
  103. }
  104. }
  105. // CursorLeft moves the edit cursor one character left if possible
  106. func (ed *Edit) CursorLeft() {
  107. if ed.col > 0 {
  108. ed.col--
  109. ed.redraw(ed.focus)
  110. }
  111. }
  112. // CursorRight moves the edit cursor one character right if possible
  113. func (ed *Edit) CursorRight() {
  114. if ed.col < text.StrCount(ed.text) {
  115. ed.col++
  116. ed.redraw(ed.focus)
  117. }
  118. }
  119. // CursorBack deletes the character at left of the cursor if possible
  120. func (ed *Edit) CursorBack() {
  121. if ed.col > 0 {
  122. ed.col--
  123. ed.text = text.StrRemove(ed.text, ed.col)
  124. ed.redraw(ed.focus)
  125. ed.Dispatch(OnChange, nil)
  126. }
  127. }
  128. // CursorHome moves the edit cursor to the beginning of the text
  129. func (ed *Edit) CursorHome() {
  130. ed.col = 0
  131. ed.redraw(ed.focus)
  132. }
  133. // CursorEnd moves the edit cursor to the end of the text
  134. func (ed *Edit) CursorEnd() {
  135. ed.col = text.StrCount(ed.text)
  136. ed.redraw(ed.focus)
  137. }
  138. // CursorDelete deletes the character at the right of the cursor if possible
  139. func (ed *Edit) CursorDelete() {
  140. if ed.col < text.StrCount(ed.text) {
  141. ed.text = text.StrRemove(ed.text, ed.col)
  142. ed.redraw(ed.focus)
  143. ed.Dispatch(OnChange, nil)
  144. }
  145. }
  146. // CursorInput inserts the specified string at the current cursor position
  147. func (ed *Edit) CursorInput(s string) {
  148. if text.StrCount(ed.text) >= ed.MaxLength {
  149. return
  150. }
  151. // Set new text with included input
  152. var newText string
  153. if ed.col < text.StrCount(ed.text) {
  154. newText = text.StrInsert(ed.text, s, ed.col)
  155. } else {
  156. newText = ed.text + s
  157. }
  158. // Checks if new text exceeds edit width
  159. width, _ := ed.Label.font.MeasureText(newText)
  160. if float32(width)+editMarginX+float32(1) >= ed.Label.ContentWidth() {
  161. return
  162. }
  163. ed.text = newText
  164. ed.col++
  165. ed.Dispatch(OnChange, nil)
  166. ed.redraw(ed.focus)
  167. }
  168. // redraw redraws the text showing the caret if specified
  169. func (ed *Edit) redraw(caret bool) {
  170. line := 0
  171. if !caret {
  172. line = -1
  173. }
  174. ed.Label.setTextCaret(ed.text, editMarginX, ed.width, line, ed.col)
  175. }
  176. // onKey receives subscribed key events
  177. func (ed *Edit) onKey(evname string, ev interface{}) {
  178. kev := ev.(*window.KeyEvent)
  179. switch kev.Keycode {
  180. case window.KeyLeft:
  181. ed.CursorLeft()
  182. case window.KeyRight:
  183. ed.CursorRight()
  184. case window.KeyHome:
  185. ed.CursorHome()
  186. case window.KeyEnd:
  187. ed.CursorEnd()
  188. case window.KeyBackspace:
  189. ed.CursorBack()
  190. case window.KeyDelete:
  191. ed.CursorDelete()
  192. default:
  193. return
  194. }
  195. ed.root.StopPropagation(Stop3D)
  196. }
  197. // onChar receives subscribed char events
  198. func (ed *Edit) onChar(evname string, ev interface{}) {
  199. cev := ev.(*window.CharEvent)
  200. ed.CursorInput(string(cev.Char))
  201. }
  202. // onMouseEvent receives subscribed mouse down events
  203. func (ed *Edit) onMouse(evname string, ev interface{}) {
  204. e := ev.(*window.MouseEvent)
  205. if e.Button != window.MouseButtonLeft {
  206. return
  207. }
  208. // Set key focus to this panel
  209. ed.root.SetKeyFocus(ed)
  210. // Find clicked column
  211. var nchars int
  212. for nchars = 1; nchars <= text.StrCount(ed.text); nchars++ {
  213. width, _ := ed.Label.font.MeasureText(text.StrPrefix(ed.text, nchars))
  214. posx := e.Xpos - ed.pospix.X
  215. if posx < editMarginX+float32(width) {
  216. break
  217. }
  218. }
  219. if !ed.focus {
  220. ed.focus = true
  221. ed.blinkID = ed.root.SetInterval(750*time.Millisecond, nil, ed.blink)
  222. }
  223. ed.CursorPos(nchars - 1)
  224. ed.root.StopPropagation(Stop3D)
  225. }
  226. // onCursor receives subscribed cursor events
  227. func (ed *Edit) onCursor(evname string, ev interface{}) {
  228. if evname == OnCursorEnter {
  229. ed.root.SetCursorText()
  230. ed.cursorOver = true
  231. ed.update()
  232. ed.root.StopPropagation(Stop3D)
  233. return
  234. }
  235. if evname == OnCursorLeave {
  236. ed.root.SetCursorNormal()
  237. ed.cursorOver = false
  238. ed.update()
  239. ed.root.StopPropagation(Stop3D)
  240. return
  241. }
  242. }
  243. // blink blinks the caret
  244. func (ed *Edit) blink(arg interface{}) {
  245. if !ed.focus {
  246. return
  247. }
  248. if !ed.caretOn {
  249. ed.caretOn = true
  250. } else {
  251. ed.caretOn = false
  252. }
  253. ed.redraw(ed.caretOn)
  254. }
  255. // update updates the visual state
  256. func (ed *Edit) update() {
  257. if !ed.Enabled() {
  258. ed.applyStyle(&ed.styles.Disabled)
  259. return
  260. }
  261. if ed.cursorOver {
  262. ed.applyStyle(&ed.styles.Over)
  263. return
  264. }
  265. if ed.focus {
  266. ed.applyStyle(&ed.styles.Focus)
  267. return
  268. }
  269. ed.applyStyle(&ed.styles.Normal)
  270. }
  271. // applyStyle applies the specified style
  272. func (ed *Edit) applyStyle(s *EditStyle) {
  273. ed.SetBordersFrom(&s.Border)
  274. ed.SetBordersColor4(&s.BorderColor)
  275. ed.SetPaddingsFrom(&s.Paddings)
  276. ed.Label.SetColor4(&s.FgColor)
  277. ed.Label.SetBgColor4(&s.BgColor)
  278. //ed.Label.SetBgAlpha(s.BgAlpha)
  279. if !ed.focus && len(ed.text) == 0 && len(ed.placeHolder) > 0 {
  280. ed.Label.SetColor4(&s.HolderColor)
  281. ed.Label.setTextCaret(ed.placeHolder, editMarginX, ed.width, -1, ed.col)
  282. } else {
  283. ed.Label.SetColor4(&s.FgColor)
  284. ed.redraw(ed.focus)
  285. }
  286. }