edit.go 7.1 KB

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