edit.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  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
  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
  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.fontSize = 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.cursorOver = true
  230. ed.update()
  231. ed.root.StopPropagation(Stop3D)
  232. return
  233. }
  234. if evname == OnCursorLeave {
  235. ed.cursorOver = false
  236. ed.update()
  237. ed.root.StopPropagation(Stop3D)
  238. return
  239. }
  240. }
  241. // blink blinks the caret
  242. func (ed *Edit) blink(arg interface{}) {
  243. if !ed.focus {
  244. return
  245. }
  246. if !ed.caretOn {
  247. ed.caretOn = true
  248. } else {
  249. ed.caretOn = false
  250. }
  251. ed.redraw(ed.caretOn)
  252. }
  253. // update updates the visual state
  254. func (ed *Edit) update() {
  255. if !ed.Enabled() {
  256. ed.applyStyle(&ed.styles.Disabled)
  257. return
  258. }
  259. if ed.cursorOver {
  260. ed.applyStyle(&ed.styles.Over)
  261. return
  262. }
  263. if ed.focus {
  264. ed.applyStyle(&ed.styles.Focus)
  265. return
  266. }
  267. ed.applyStyle(&ed.styles.Normal)
  268. }
  269. // applyStyle applies the specified style
  270. func (ed *Edit) applyStyle(s *EditStyle) {
  271. ed.SetBordersFrom(&s.Border)
  272. ed.SetBordersColor4(&s.BorderColor)
  273. ed.SetPaddingsFrom(&s.Paddings)
  274. ed.Label.SetColor4(&s.FgColor)
  275. ed.Label.SetBgColor4(&s.BgColor)
  276. //ed.Label.SetBgAlpha(s.BgAlpha)
  277. if !ed.focus && len(ed.text) == 0 && len(ed.placeHolder) > 0 {
  278. ed.Label.SetColor4(&s.HolderColor)
  279. ed.Label.setTextCaret(ed.placeHolder, editMarginX, ed.width, -1, ed.col)
  280. } else {
  281. ed.Label.SetColor4(&s.FgColor)
  282. ed.redraw(ed.focus)
  283. }
  284. }