edit.go 7.4 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. "strings"
  7. "time"
  8. "github.com/g3n/engine/math32"
  9. "github.com/g3n/engine/text"
  10. "github.com/g3n/engine/window"
  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.Subscribe(OnFocusLost, ed.OnFocusLost)
  66. ed.update()
  67. return ed
  68. }
  69. // SetText sets this edit text
  70. func (ed *Edit) SetText(text string) *Edit {
  71. // Remove new lines from text
  72. ed.text = strings.Replace(text, "\n", "", -1)
  73. ed.update()
  74. return ed
  75. }
  76. // Text returns the current edited text
  77. func (ed *Edit) Text() string {
  78. return ed.text
  79. }
  80. // SetFontSize sets label font size (overrides Label.SetFontSize)
  81. func (ed *Edit) SetFontSize(size float64) *Edit {
  82. ed.Label.SetFontSize(size)
  83. ed.redraw(ed.focus)
  84. return ed
  85. }
  86. // SetStyles set the button styles overriding the default style
  87. func (ed *Edit) SetStyles(es *EditStyles) {
  88. ed.styles = es
  89. ed.update()
  90. }
  91. // LostKeyFocus satisfies the IPanel interface and is called by gui root
  92. // container when the panel loses the key focus
  93. func (ed *Edit) OnFocusLost(evname string, ev interface{}) {
  94. ed.focus = false
  95. ed.update()
  96. Manager().ClearTimeout(ed.blinkID)
  97. }
  98. // CursorPos sets the position of the cursor at the
  99. // specified column if possible
  100. func (ed *Edit) CursorPos(col int) {
  101. if col <= text.StrCount(ed.text) {
  102. ed.col = col
  103. ed.redraw(ed.focus)
  104. }
  105. }
  106. // CursorLeft moves the edit cursor one character left if possible
  107. func (ed *Edit) CursorLeft() {
  108. if ed.col > 0 {
  109. ed.col--
  110. ed.redraw(ed.focus)
  111. }
  112. }
  113. // CursorRight moves the edit cursor one character right if possible
  114. func (ed *Edit) CursorRight() {
  115. if ed.col < text.StrCount(ed.text) {
  116. ed.col++
  117. ed.redraw(ed.focus)
  118. }
  119. }
  120. // CursorBack deletes the character at left of the cursor if possible
  121. func (ed *Edit) CursorBack() {
  122. if ed.col > 0 {
  123. ed.col--
  124. ed.text = text.StrRemove(ed.text, ed.col)
  125. ed.redraw(ed.focus)
  126. ed.Dispatch(OnChange, nil)
  127. }
  128. }
  129. // CursorHome moves the edit cursor to the beginning of the text
  130. func (ed *Edit) CursorHome() {
  131. ed.col = 0
  132. ed.redraw(ed.focus)
  133. }
  134. // CursorEnd moves the edit cursor to the end of the text
  135. func (ed *Edit) CursorEnd() {
  136. ed.col = text.StrCount(ed.text)
  137. ed.redraw(ed.focus)
  138. }
  139. // CursorDelete deletes the character at the right of the cursor if possible
  140. func (ed *Edit) CursorDelete() {
  141. if ed.col < text.StrCount(ed.text) {
  142. ed.text = text.StrRemove(ed.text, ed.col)
  143. ed.redraw(ed.focus)
  144. ed.Dispatch(OnChange, nil)
  145. }
  146. }
  147. // CursorInput inserts the specified string at the current cursor position
  148. func (ed *Edit) CursorInput(s string) {
  149. if text.StrCount(ed.text) >= ed.MaxLength {
  150. return
  151. }
  152. // Set new text with included input
  153. var newText string
  154. if ed.col < text.StrCount(ed.text) {
  155. newText = text.StrInsert(ed.text, s, ed.col)
  156. } else {
  157. newText = ed.text + s
  158. }
  159. // Checks if new text exceeds edit width
  160. width, _ := ed.Label.font.MeasureText(newText)
  161. if float32(width)+editMarginX+float32(1) >= ed.Label.ContentWidth() {
  162. return
  163. }
  164. ed.text = newText
  165. ed.col++
  166. ed.Dispatch(OnChange, nil)
  167. ed.redraw(ed.focus)
  168. }
  169. // redraw redraws the text showing the caret if specified
  170. func (ed *Edit) redraw(caret bool) {
  171. line := 0
  172. if !caret {
  173. line = -1
  174. }
  175. ed.Label.setTextCaret(ed.text, editMarginX, ed.width, line, ed.col)
  176. }
  177. // onKey receives subscribed key events
  178. func (ed *Edit) onKey(evname string, ev interface{}) {
  179. kev := ev.(*window.KeyEvent)
  180. switch kev.Key {
  181. case window.KeyLeft:
  182. ed.CursorLeft()
  183. case window.KeyRight:
  184. ed.CursorRight()
  185. case window.KeyHome:
  186. ed.CursorHome()
  187. case window.KeyEnd:
  188. ed.CursorEnd()
  189. case window.KeyBackspace:
  190. ed.CursorBack()
  191. case window.KeyDelete:
  192. ed.CursorDelete()
  193. default:
  194. return
  195. }
  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. Manager().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 = Manager().SetInterval(750*time.Millisecond, nil, ed.blink)
  222. }
  223. ed.CursorPos(nchars - 1)
  224. }
  225. // onCursor receives subscribed cursor events
  226. func (ed *Edit) onCursor(evname string, ev interface{}) {
  227. if evname == OnCursorEnter {
  228. window.Get().SetCursor(window.IBeamCursor)
  229. ed.cursorOver = true
  230. ed.update()
  231. return
  232. }
  233. if evname == OnCursorLeave {
  234. window.Get().SetCursor(window.ArrowCursor)
  235. ed.cursorOver = false
  236. ed.update()
  237. return
  238. }
  239. }
  240. // blink blinks the caret
  241. func (ed *Edit) blink(arg interface{}) {
  242. if !ed.focus {
  243. return
  244. }
  245. if !ed.caretOn {
  246. ed.caretOn = true
  247. } else {
  248. ed.caretOn = false
  249. }
  250. ed.redraw(ed.caretOn)
  251. }
  252. // update updates the visual state
  253. func (ed *Edit) update() {
  254. if !ed.Enabled() {
  255. ed.applyStyle(&ed.styles.Disabled)
  256. return
  257. }
  258. if ed.cursorOver {
  259. ed.applyStyle(&ed.styles.Over)
  260. return
  261. }
  262. if ed.focus {
  263. ed.applyStyle(&ed.styles.Focus)
  264. return
  265. }
  266. ed.applyStyle(&ed.styles.Normal)
  267. }
  268. // applyStyle applies the specified style
  269. func (ed *Edit) applyStyle(s *EditStyle) {
  270. ed.SetBordersFrom(&s.Border)
  271. ed.SetBordersColor4(&s.BorderColor)
  272. ed.SetPaddingsFrom(&s.Paddings)
  273. ed.Label.SetColor4(&s.FgColor)
  274. ed.Label.SetBgColor4(&s.BgColor)
  275. //ed.Label.SetBgAlpha(s.BgAlpha)
  276. if !ed.focus && len(ed.text) == 0 && len(ed.placeHolder) > 0 {
  277. ed.Label.SetColor4(&s.HolderColor)
  278. ed.Label.setTextCaret(ed.placeHolder, editMarginX, ed.width, -1, ed.col)
  279. } else {
  280. ed.Label.SetColor4(&s.FgColor)
  281. ed.redraw(ed.focus)
  282. }
  283. }