edit.go 7.2 KB

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