editor.go 30 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130
  1. // SPDX-License-Identifier: Unlicense OR MIT
  2. package widget
  3. import (
  4. "bufio"
  5. "image"
  6. "io"
  7. "math"
  8. "strings"
  9. "time"
  10. "unicode"
  11. "unicode/utf8"
  12. "gioui.org/f32"
  13. "gioui.org/font"
  14. "gioui.org/gesture"
  15. "gioui.org/io/clipboard"
  16. "gioui.org/io/event"
  17. "gioui.org/io/key"
  18. "gioui.org/io/pointer"
  19. "gioui.org/io/semantic"
  20. "gioui.org/io/system"
  21. "gioui.org/io/transfer"
  22. "gioui.org/layout"
  23. "gioui.org/op"
  24. "gioui.org/op/clip"
  25. "gioui.org/text"
  26. "gioui.org/unit"
  27. )
  28. // Editor implements an editable and scrollable text area.
  29. type Editor struct {
  30. // text manages the text buffer and provides shaping and cursor positioning
  31. // services.
  32. text textView
  33. // Alignment controls the alignment of text within the editor.
  34. Alignment text.Alignment
  35. // LineHeight determines the gap between baselines of text. If zero, a sensible
  36. // default will be used.
  37. LineHeight unit.Sp
  38. // LineHeightScale is multiplied by LineHeight to determine the final gap
  39. // between baselines. If zero, a sensible default will be used.
  40. LineHeightScale float32
  41. // SingleLine force the text to stay on a single line.
  42. // SingleLine also sets the scrolling direction to
  43. // horizontal.
  44. SingleLine bool
  45. // ReadOnly controls whether the contents of the editor can be altered by
  46. // user interaction. If set to true, the editor will allow selecting text
  47. // and copying it interactively, but not modifying it.
  48. ReadOnly bool
  49. // Submit enabled translation of carriage return keys to SubmitEvents.
  50. // If not enabled, carriage returns are inserted as newlines in the text.
  51. Submit bool
  52. // Mask replaces the visual display of each rune in the contents with the given rune.
  53. // Newline characters are not masked. When non-zero, the unmasked contents
  54. // are accessed by Len, Text, and SetText.
  55. Mask rune
  56. // InputHint specifies the type of on-screen keyboard to be displayed.
  57. InputHint key.InputHint
  58. // MaxLen limits the editor content to a maximum length. Zero means no limit.
  59. MaxLen int
  60. // Filter is the list of characters allowed in the Editor. If Filter is empty,
  61. // all characters are allowed.
  62. Filter string
  63. // WrapPolicy configures how displayed text will be broken into lines.
  64. WrapPolicy text.WrapPolicy
  65. buffer *editBuffer
  66. // scratch is a byte buffer that is reused to efficiently read portions of text
  67. // from the textView.
  68. scratch []byte
  69. blinkStart time.Time
  70. // ime tracks the state relevant to input methods.
  71. ime struct {
  72. imeState
  73. scratch []byte
  74. }
  75. dragging bool
  76. dragger gesture.Drag
  77. scroller gesture.Scroll
  78. scrollCaret bool
  79. showCaret bool
  80. clicker gesture.Click
  81. // history contains undo history.
  82. history []modification
  83. // nextHistoryIdx is the index within the history of the next modification. This
  84. // is only not len(history) immediately after undo operations occur. It is framed as the "next" value
  85. // to make the zero value consistent.
  86. nextHistoryIdx int
  87. pending []EditorEvent
  88. }
  89. type offEntry struct {
  90. runes int
  91. bytes int
  92. }
  93. type imeState struct {
  94. selection struct {
  95. rng key.Range
  96. caret key.Caret
  97. }
  98. snippet key.Snippet
  99. start, end int
  100. }
  101. type maskReader struct {
  102. // rr is the underlying reader.
  103. rr io.RuneReader
  104. maskBuf [utf8.UTFMax]byte
  105. // mask is the utf-8 encoded mask rune.
  106. mask []byte
  107. // overflow contains excess mask bytes left over after the last Read call.
  108. overflow []byte
  109. }
  110. type selectionAction int
  111. const (
  112. selectionExtend selectionAction = iota
  113. selectionClear
  114. )
  115. func (m *maskReader) Reset(r io.Reader, mr rune) {
  116. m.rr = bufio.NewReader(r)
  117. n := utf8.EncodeRune(m.maskBuf[:], mr)
  118. m.mask = m.maskBuf[:n]
  119. }
  120. // Read reads from the underlying reader and replaces every
  121. // rune with the mask rune.
  122. func (m *maskReader) Read(b []byte) (n int, err error) {
  123. for len(b) > 0 {
  124. var replacement []byte
  125. if len(m.overflow) > 0 {
  126. replacement = m.overflow
  127. } else {
  128. var r rune
  129. r, _, err = m.rr.ReadRune()
  130. if err != nil {
  131. break
  132. }
  133. if r == '\n' {
  134. replacement = []byte{'\n'}
  135. } else {
  136. replacement = m.mask
  137. }
  138. }
  139. nn := copy(b, replacement)
  140. m.overflow = replacement[nn:]
  141. n += nn
  142. b = b[nn:]
  143. }
  144. return n, err
  145. }
  146. type EditorEvent interface {
  147. isEditorEvent()
  148. }
  149. // A ChangeEvent is generated for every user change to the text.
  150. type ChangeEvent struct{}
  151. // A SubmitEvent is generated when Submit is set
  152. // and a carriage return key is pressed.
  153. type SubmitEvent struct {
  154. Text string
  155. }
  156. // A SelectEvent is generated when the user selects some text, or changes the
  157. // selection (e.g. with a shift-click), including if they remove the
  158. // selection. The selected text is not part of the event, on the theory that
  159. // it could be a relatively expensive operation (for a large editor), most
  160. // applications won't actually care about it, and those that do can call
  161. // Editor.SelectedText() (which can be empty).
  162. type SelectEvent struct{}
  163. const (
  164. blinksPerSecond = 1
  165. maxBlinkDuration = 10 * time.Second
  166. )
  167. func (e *Editor) processEvents(gtx layout.Context) (ev EditorEvent, ok bool) {
  168. if len(e.pending) > 0 {
  169. out := e.pending[0]
  170. e.pending = e.pending[:copy(e.pending, e.pending[1:])]
  171. return out, true
  172. }
  173. selStart, selEnd := e.Selection()
  174. defer func() {
  175. afterSelStart, afterSelEnd := e.Selection()
  176. if selStart != afterSelStart || selEnd != afterSelEnd {
  177. if ok {
  178. e.pending = append(e.pending, SelectEvent{})
  179. } else {
  180. ev = SelectEvent{}
  181. ok = true
  182. }
  183. }
  184. }()
  185. ev, ok = e.processPointer(gtx)
  186. if ok {
  187. return ev, ok
  188. }
  189. ev, ok = e.processKey(gtx)
  190. if ok {
  191. return ev, ok
  192. }
  193. return nil, false
  194. }
  195. func (e *Editor) processPointer(gtx layout.Context) (EditorEvent, bool) {
  196. sbounds := e.text.ScrollBounds()
  197. var smin, smax int
  198. var axis gesture.Axis
  199. if e.SingleLine {
  200. axis = gesture.Horizontal
  201. smin, smax = sbounds.Min.X, sbounds.Max.X
  202. } else {
  203. axis = gesture.Vertical
  204. smin, smax = sbounds.Min.Y, sbounds.Max.Y
  205. }
  206. var scrollX, scrollY pointer.ScrollRange
  207. textDims := e.text.FullDimensions()
  208. visibleDims := e.text.Dimensions()
  209. if e.SingleLine {
  210. scrollOffX := e.text.ScrollOff().X
  211. scrollX.Min = min(-scrollOffX, 0)
  212. scrollX.Max = max(0, textDims.Size.X-(scrollOffX+visibleDims.Size.X))
  213. } else {
  214. scrollOffY := e.text.ScrollOff().Y
  215. scrollY.Min = -scrollOffY
  216. scrollY.Max = max(0, textDims.Size.Y-(scrollOffY+visibleDims.Size.Y))
  217. }
  218. sdist := e.scroller.Update(gtx.Metric, gtx.Source, gtx.Now, axis, scrollX, scrollY)
  219. var soff int
  220. if e.SingleLine {
  221. e.text.ScrollRel(sdist, 0)
  222. soff = e.text.ScrollOff().X
  223. } else {
  224. e.text.ScrollRel(0, sdist)
  225. soff = e.text.ScrollOff().Y
  226. }
  227. for {
  228. evt, ok := e.clicker.Update(gtx.Source)
  229. if !ok {
  230. break
  231. }
  232. ev, ok := e.processPointerEvent(gtx, evt)
  233. if ok {
  234. return ev, ok
  235. }
  236. }
  237. for {
  238. evt, ok := e.dragger.Update(gtx.Metric, gtx.Source, gesture.Both)
  239. if !ok {
  240. break
  241. }
  242. ev, ok := e.processPointerEvent(gtx, evt)
  243. if ok {
  244. return ev, ok
  245. }
  246. }
  247. if (sdist > 0 && soff >= smax) || (sdist < 0 && soff <= smin) {
  248. e.scroller.Stop()
  249. }
  250. return nil, false
  251. }
  252. func (e *Editor) processPointerEvent(gtx layout.Context, ev event.Event) (EditorEvent, bool) {
  253. switch evt := ev.(type) {
  254. case gesture.ClickEvent:
  255. switch {
  256. case evt.Kind == gesture.KindPress && evt.Source == pointer.Mouse,
  257. evt.Kind == gesture.KindClick && evt.Source != pointer.Mouse:
  258. prevCaretPos, _ := e.text.Selection()
  259. e.blinkStart = gtx.Now
  260. e.text.MoveCoord(image.Point{
  261. X: int(math.Round(float64(evt.Position.X))),
  262. Y: int(math.Round(float64(evt.Position.Y))),
  263. })
  264. gtx.Execute(key.FocusCmd{Tag: e})
  265. if !e.ReadOnly {
  266. gtx.Execute(key.SoftKeyboardCmd{Show: true})
  267. }
  268. if e.scroller.State() != gesture.StateFlinging {
  269. e.scrollCaret = true
  270. }
  271. if evt.Modifiers == key.ModShift {
  272. start, end := e.text.Selection()
  273. // If they clicked closer to the end, then change the end to
  274. // where the caret used to be (effectively swapping start & end).
  275. if abs(end-start) < abs(start-prevCaretPos) {
  276. e.text.SetCaret(start, prevCaretPos)
  277. }
  278. } else {
  279. e.text.ClearSelection()
  280. }
  281. e.dragging = true
  282. // Process multi-clicks.
  283. switch {
  284. case evt.NumClicks == 2:
  285. e.text.MoveWord(-1, selectionClear)
  286. e.text.MoveWord(1, selectionExtend)
  287. e.dragging = false
  288. case evt.NumClicks >= 3:
  289. e.text.MoveLineStart(selectionClear)
  290. e.text.MoveLineEnd(selectionExtend)
  291. e.dragging = false
  292. }
  293. }
  294. case pointer.Event:
  295. release := false
  296. switch {
  297. case evt.Kind == pointer.Release && evt.Source == pointer.Mouse:
  298. release = true
  299. fallthrough
  300. case evt.Kind == pointer.Drag && evt.Source == pointer.Mouse:
  301. if e.dragging {
  302. e.blinkStart = gtx.Now
  303. e.text.MoveCoord(image.Point{
  304. X: int(math.Round(float64(evt.Position.X))),
  305. Y: int(math.Round(float64(evt.Position.Y))),
  306. })
  307. e.scrollCaret = true
  308. if release {
  309. e.dragging = false
  310. }
  311. }
  312. }
  313. }
  314. return nil, false
  315. }
  316. func condFilter(pred bool, f key.Filter) event.Filter {
  317. if pred {
  318. return f
  319. } else {
  320. return nil
  321. }
  322. }
  323. func (e *Editor) processKey(gtx layout.Context) (EditorEvent, bool) {
  324. if e.text.Changed() {
  325. return ChangeEvent{}, true
  326. }
  327. caret, _ := e.text.Selection()
  328. atBeginning := caret == 0
  329. atEnd := caret == e.text.Len()
  330. if gtx.Locale.Direction.Progression() != system.FromOrigin {
  331. atEnd, atBeginning = atBeginning, atEnd
  332. }
  333. filters := []event.Filter{
  334. key.FocusFilter{Target: e},
  335. transfer.TargetFilter{Target: e, Type: "application/text"},
  336. key.Filter{Focus: e, Name: key.NameEnter, Optional: key.ModShift},
  337. key.Filter{Focus: e, Name: key.NameReturn, Optional: key.ModShift},
  338. key.Filter{Focus: e, Name: "Z", Required: key.ModShortcut, Optional: key.ModShift},
  339. key.Filter{Focus: e, Name: "C", Required: key.ModShortcut},
  340. key.Filter{Focus: e, Name: "V", Required: key.ModShortcut},
  341. key.Filter{Focus: e, Name: "X", Required: key.ModShortcut},
  342. key.Filter{Focus: e, Name: "A", Required: key.ModShortcut},
  343. key.Filter{Focus: e, Name: key.NameDeleteBackward, Optional: key.ModShortcutAlt | key.ModShift},
  344. key.Filter{Focus: e, Name: key.NameDeleteForward, Optional: key.ModShortcutAlt | key.ModShift},
  345. key.Filter{Focus: e, Name: key.NameHome, Optional: key.ModShortcut | key.ModShift},
  346. key.Filter{Focus: e, Name: key.NameEnd, Optional: key.ModShortcut | key.ModShift},
  347. key.Filter{Focus: e, Name: key.NamePageDown, Optional: key.ModShift},
  348. key.Filter{Focus: e, Name: key.NamePageUp, Optional: key.ModShift},
  349. condFilter(!atBeginning, key.Filter{Focus: e, Name: key.NameLeftArrow, Optional: key.ModShortcutAlt | key.ModShift}),
  350. condFilter(!atBeginning, key.Filter{Focus: e, Name: key.NameUpArrow, Optional: key.ModShortcutAlt | key.ModShift}),
  351. condFilter(!atEnd, key.Filter{Focus: e, Name: key.NameRightArrow, Optional: key.ModShortcutAlt | key.ModShift}),
  352. condFilter(!atEnd, key.Filter{Focus: e, Name: key.NameDownArrow, Optional: key.ModShortcutAlt | key.ModShift}),
  353. }
  354. // adjust keeps track of runes dropped because of MaxLen.
  355. var adjust int
  356. for {
  357. ke, ok := gtx.Event(filters...)
  358. if !ok {
  359. break
  360. }
  361. e.blinkStart = gtx.Now
  362. switch ke := ke.(type) {
  363. case key.FocusEvent:
  364. // Reset IME state.
  365. e.ime.imeState = imeState{}
  366. if ke.Focus && !e.ReadOnly {
  367. gtx.Execute(key.SoftKeyboardCmd{Show: true})
  368. }
  369. case key.Event:
  370. if !gtx.Focused(e) || ke.State != key.Press {
  371. break
  372. }
  373. if !e.ReadOnly && e.Submit && (ke.Name == key.NameReturn || ke.Name == key.NameEnter) {
  374. if !ke.Modifiers.Contain(key.ModShift) {
  375. e.scratch = e.text.Text(e.scratch)
  376. return SubmitEvent{
  377. Text: string(e.scratch),
  378. }, true
  379. }
  380. }
  381. e.scrollCaret = true
  382. e.scroller.Stop()
  383. ev, ok := e.command(gtx, ke)
  384. if ok {
  385. return ev, ok
  386. }
  387. case key.SnippetEvent:
  388. e.updateSnippet(gtx, ke.Start, ke.End)
  389. case key.EditEvent:
  390. if e.ReadOnly {
  391. break
  392. }
  393. e.scrollCaret = true
  394. e.scroller.Stop()
  395. s := ke.Text
  396. moves := 0
  397. submit := false
  398. switch {
  399. case e.Submit:
  400. if i := strings.IndexByte(s, '\n'); i != -1 {
  401. submit = true
  402. moves += len(s) - i
  403. s = s[:i]
  404. }
  405. case e.SingleLine:
  406. s = strings.ReplaceAll(s, "\n", " ")
  407. }
  408. moves += e.replace(ke.Range.Start, ke.Range.End, s, true)
  409. adjust += utf8.RuneCountInString(ke.Text) - moves
  410. // Reset caret xoff.
  411. e.text.MoveCaret(0, 0)
  412. if submit {
  413. e.scratch = e.text.Text(e.scratch)
  414. submitEvent := SubmitEvent{
  415. Text: string(e.scratch),
  416. }
  417. if e.text.Changed() {
  418. e.pending = append(e.pending, submitEvent)
  419. return ChangeEvent{}, true
  420. }
  421. return submitEvent, true
  422. }
  423. // Complete a paste event, initiated by Shortcut-V in Editor.command().
  424. case transfer.DataEvent:
  425. e.scrollCaret = true
  426. e.scroller.Stop()
  427. content, err := io.ReadAll(ke.Open())
  428. if err == nil {
  429. if e.Insert(string(content)) != 0 {
  430. return ChangeEvent{}, true
  431. }
  432. }
  433. case key.SelectionEvent:
  434. e.scrollCaret = true
  435. e.scroller.Stop()
  436. ke.Start -= adjust
  437. ke.End -= adjust
  438. adjust = 0
  439. e.text.SetCaret(ke.Start, ke.End)
  440. }
  441. }
  442. if e.text.Changed() {
  443. return ChangeEvent{}, true
  444. }
  445. return nil, false
  446. }
  447. func (e *Editor) command(gtx layout.Context, k key.Event) (EditorEvent, bool) {
  448. direction := 1
  449. if gtx.Locale.Direction.Progression() == system.TowardOrigin {
  450. direction = -1
  451. }
  452. moveByWord := k.Modifiers.Contain(key.ModShortcutAlt)
  453. selAct := selectionClear
  454. if k.Modifiers.Contain(key.ModShift) {
  455. selAct = selectionExtend
  456. }
  457. if k.Modifiers.Contain(key.ModShortcut) {
  458. switch k.Name {
  459. // Initiate a paste operation, by requesting the clipboard contents; other
  460. // half is in Editor.processKey() under clipboard.Event.
  461. case "V":
  462. if !e.ReadOnly {
  463. gtx.Execute(clipboard.ReadCmd{Tag: e})
  464. }
  465. // Copy or Cut selection -- ignored if nothing selected.
  466. case "C", "X":
  467. e.scratch = e.text.SelectedText(e.scratch)
  468. if text := string(e.scratch); text != "" {
  469. gtx.Execute(clipboard.WriteCmd{Type: "application/text", Data: io.NopCloser(strings.NewReader(text))})
  470. if k.Name == "X" && !e.ReadOnly {
  471. if e.Delete(1) != 0 {
  472. return ChangeEvent{}, true
  473. }
  474. }
  475. }
  476. // Select all
  477. case "A":
  478. e.text.SetCaret(0, e.text.Len())
  479. case "Z":
  480. if !e.ReadOnly {
  481. if k.Modifiers.Contain(key.ModShift) {
  482. if ev, ok := e.redo(); ok {
  483. return ev, ok
  484. }
  485. } else {
  486. if ev, ok := e.undo(); ok {
  487. return ev, ok
  488. }
  489. }
  490. }
  491. case key.NameHome:
  492. e.text.MoveTextStart(selAct)
  493. case key.NameEnd:
  494. e.text.MoveTextEnd(selAct)
  495. }
  496. return nil, false
  497. }
  498. switch k.Name {
  499. case key.NameReturn, key.NameEnter:
  500. if !e.ReadOnly {
  501. if e.Insert("\n") != 0 {
  502. return ChangeEvent{}, true
  503. }
  504. }
  505. case key.NameDeleteBackward:
  506. if !e.ReadOnly {
  507. if moveByWord {
  508. if e.deleteWord(-1) != 0 {
  509. return ChangeEvent{}, true
  510. }
  511. } else {
  512. if e.Delete(-1) != 0 {
  513. return ChangeEvent{}, true
  514. }
  515. }
  516. }
  517. case key.NameDeleteForward:
  518. if !e.ReadOnly {
  519. if moveByWord {
  520. if e.deleteWord(1) != 0 {
  521. return ChangeEvent{}, true
  522. }
  523. } else {
  524. if e.Delete(1) != 0 {
  525. return ChangeEvent{}, true
  526. }
  527. }
  528. }
  529. case key.NameUpArrow:
  530. e.text.MoveLines(-1, selAct)
  531. case key.NameDownArrow:
  532. e.text.MoveLines(+1, selAct)
  533. case key.NameLeftArrow:
  534. if moveByWord {
  535. e.text.MoveWord(-1*direction, selAct)
  536. } else {
  537. if selAct == selectionClear {
  538. e.text.ClearSelection()
  539. }
  540. e.text.MoveCaret(-1*direction, -1*direction*int(selAct))
  541. }
  542. case key.NameRightArrow:
  543. if moveByWord {
  544. e.text.MoveWord(1*direction, selAct)
  545. } else {
  546. if selAct == selectionClear {
  547. e.text.ClearSelection()
  548. }
  549. e.text.MoveCaret(1*direction, int(selAct)*direction)
  550. }
  551. case key.NamePageUp:
  552. e.text.MovePages(-1, selAct)
  553. case key.NamePageDown:
  554. e.text.MovePages(+1, selAct)
  555. case key.NameHome:
  556. e.text.MoveLineStart(selAct)
  557. case key.NameEnd:
  558. e.text.MoveLineEnd(selAct)
  559. }
  560. return nil, false
  561. }
  562. // initBuffer should be invoked first in every exported function that accesses
  563. // text state. It ensures that the underlying text widget is both ready to use
  564. // and has its fields synced with the editor.
  565. func (e *Editor) initBuffer() {
  566. if e.buffer == nil {
  567. e.buffer = new(editBuffer)
  568. e.text.SetSource(e.buffer)
  569. }
  570. e.text.Alignment = e.Alignment
  571. e.text.LineHeight = e.LineHeight
  572. e.text.LineHeightScale = e.LineHeightScale
  573. e.text.SingleLine = e.SingleLine
  574. e.text.Mask = e.Mask
  575. e.text.WrapPolicy = e.WrapPolicy
  576. e.text.DisableSpaceTrim = true
  577. }
  578. // Update the state of the editor in response to input events. Update consumes editor
  579. // input events until there are no remaining events or an editor event is generated.
  580. // To fully update the state of the editor, callers should call Update until it returns
  581. // false.
  582. func (e *Editor) Update(gtx layout.Context) (EditorEvent, bool) {
  583. e.initBuffer()
  584. event, ok := e.processEvents(gtx)
  585. // Notify IME of selection if it changed.
  586. newSel := e.ime.selection
  587. start, end := e.text.Selection()
  588. newSel.rng = key.Range{
  589. Start: start,
  590. End: end,
  591. }
  592. caretPos, carAsc, carDesc := e.text.CaretInfo()
  593. newSel.caret = key.Caret{
  594. Pos: layout.FPt(caretPos),
  595. Ascent: float32(carAsc),
  596. Descent: float32(carDesc),
  597. }
  598. if newSel != e.ime.selection {
  599. e.ime.selection = newSel
  600. gtx.Execute(key.SelectionCmd{Tag: e, Range: newSel.rng, Caret: newSel.caret})
  601. }
  602. e.updateSnippet(gtx, e.ime.start, e.ime.end)
  603. return event, ok
  604. }
  605. // Layout lays out the editor using the provided textMaterial as the paint material
  606. // for the text glyphs+caret and the selectMaterial as the paint material for the
  607. // selection rectangle.
  608. func (e *Editor) Layout(gtx layout.Context, lt *text.Shaper, font font.Font, size unit.Sp, textMaterial, selectMaterial op.CallOp) layout.Dimensions {
  609. for {
  610. _, ok := e.Update(gtx)
  611. if !ok {
  612. break
  613. }
  614. }
  615. e.text.Layout(gtx, lt, font, size)
  616. return e.layout(gtx, textMaterial, selectMaterial)
  617. }
  618. // updateSnippet queues a key.SnippetCmd if the snippet content or position
  619. // have changed. off and len are in runes.
  620. func (e *Editor) updateSnippet(gtx layout.Context, start, end int) {
  621. if start > end {
  622. start, end = end, start
  623. }
  624. length := e.text.Len()
  625. if start > length {
  626. start = length
  627. }
  628. if end > length {
  629. end = length
  630. }
  631. e.ime.start = start
  632. e.ime.end = end
  633. startOff := e.text.ByteOffset(start)
  634. endOff := e.text.ByteOffset(end)
  635. n := endOff - startOff
  636. if n > int64(len(e.ime.scratch)) {
  637. e.ime.scratch = make([]byte, n)
  638. }
  639. scratch := e.ime.scratch[:n]
  640. read, _ := e.text.ReadAt(scratch, startOff)
  641. if read != len(scratch) {
  642. panic("e.rr.Read truncated data")
  643. }
  644. newSnip := key.Snippet{
  645. Range: key.Range{
  646. Start: e.ime.start,
  647. End: e.ime.end,
  648. },
  649. Text: e.ime.snippet.Text,
  650. }
  651. if string(scratch) != newSnip.Text {
  652. newSnip.Text = string(scratch)
  653. }
  654. if newSnip == e.ime.snippet {
  655. return
  656. }
  657. e.ime.snippet = newSnip
  658. gtx.Execute(key.SnippetCmd{Tag: e, Snippet: newSnip})
  659. }
  660. func (e *Editor) layout(gtx layout.Context, textMaterial, selectMaterial op.CallOp) layout.Dimensions {
  661. // Adjust scrolling for new viewport and layout.
  662. e.text.ScrollRel(0, 0)
  663. if e.scrollCaret {
  664. e.scrollCaret = false
  665. e.text.ScrollToCaret()
  666. }
  667. visibleDims := e.text.Dimensions()
  668. defer clip.Rect(image.Rectangle{Max: visibleDims.Size}).Push(gtx.Ops).Pop()
  669. pointer.CursorText.Add(gtx.Ops)
  670. event.Op(gtx.Ops, e)
  671. key.InputHintOp{Tag: e, Hint: e.InputHint}.Add(gtx.Ops)
  672. e.scroller.Add(gtx.Ops)
  673. e.clicker.Add(gtx.Ops)
  674. e.dragger.Add(gtx.Ops)
  675. e.showCaret = false
  676. if gtx.Focused(e) {
  677. now := gtx.Now
  678. dt := now.Sub(e.blinkStart)
  679. blinking := dt < maxBlinkDuration
  680. const timePerBlink = time.Second / blinksPerSecond
  681. nextBlink := now.Add(timePerBlink/2 - dt%(timePerBlink/2))
  682. if blinking {
  683. gtx.Execute(op.InvalidateCmd{At: nextBlink})
  684. }
  685. e.showCaret = !blinking || dt%timePerBlink < timePerBlink/2
  686. }
  687. semantic.Editor.Add(gtx.Ops)
  688. if e.Len() > 0 {
  689. e.paintSelection(gtx, selectMaterial)
  690. e.paintText(gtx, textMaterial)
  691. }
  692. if gtx.Enabled() {
  693. e.paintCaret(gtx, textMaterial)
  694. }
  695. return visibleDims
  696. }
  697. // paintSelection paints the contrasting background for selected text using the provided
  698. // material to set the painting material for the selection.
  699. func (e *Editor) paintSelection(gtx layout.Context, material op.CallOp) {
  700. e.initBuffer()
  701. if !gtx.Focused(e) {
  702. return
  703. }
  704. e.text.PaintSelection(gtx, material)
  705. }
  706. // paintText paints the text glyphs using the provided material to set the fill of the
  707. // glyphs.
  708. func (e *Editor) paintText(gtx layout.Context, material op.CallOp) {
  709. e.initBuffer()
  710. e.text.PaintText(gtx, material)
  711. }
  712. // paintCaret paints the text glyphs using the provided material to set the fill material
  713. // of the caret rectangle.
  714. func (e *Editor) paintCaret(gtx layout.Context, material op.CallOp) {
  715. e.initBuffer()
  716. if !e.showCaret || e.ReadOnly {
  717. return
  718. }
  719. e.text.PaintCaret(gtx, material)
  720. }
  721. // Len is the length of the editor contents, in runes.
  722. func (e *Editor) Len() int {
  723. e.initBuffer()
  724. return e.text.Len()
  725. }
  726. // Text returns the contents of the editor.
  727. func (e *Editor) Text() string {
  728. e.initBuffer()
  729. e.scratch = e.text.Text(e.scratch)
  730. return string(e.scratch)
  731. }
  732. func (e *Editor) SetText(s string) {
  733. e.initBuffer()
  734. if e.SingleLine {
  735. s = strings.ReplaceAll(s, "\n", " ")
  736. }
  737. e.replace(0, e.text.Len(), s, true)
  738. // Reset xoff and move the caret to the beginning.
  739. e.SetCaret(0, 0)
  740. }
  741. // CaretPos returns the line & column numbers of the caret.
  742. func (e *Editor) CaretPos() (line, col int) {
  743. e.initBuffer()
  744. return e.text.CaretPos()
  745. }
  746. // CaretCoords returns the coordinates of the caret, relative to the
  747. // editor itself.
  748. func (e *Editor) CaretCoords() f32.Point {
  749. e.initBuffer()
  750. return e.text.CaretCoords()
  751. }
  752. // Delete runes from the caret position. The sign of the argument specifies the
  753. // direction to delete: positive is forward, negative is backward.
  754. //
  755. // If there is a selection, it is deleted and counts as a single grapheme
  756. // cluster.
  757. func (e *Editor) Delete(graphemeClusters int) (deletedRunes int) {
  758. e.initBuffer()
  759. if graphemeClusters == 0 {
  760. return 0
  761. }
  762. start, end := e.text.Selection()
  763. if start != end {
  764. graphemeClusters -= sign(graphemeClusters)
  765. }
  766. // Move caret by the target quantity of clusters.
  767. e.text.MoveCaret(0, graphemeClusters)
  768. // Get the new rune offsets of the selection.
  769. start, end = e.text.Selection()
  770. e.replace(start, end, "", true)
  771. // Reset xoff.
  772. e.text.MoveCaret(0, 0)
  773. e.ClearSelection()
  774. return end - start
  775. }
  776. func (e *Editor) Insert(s string) (insertedRunes int) {
  777. e.initBuffer()
  778. if e.SingleLine {
  779. s = strings.ReplaceAll(s, "\n", " ")
  780. }
  781. start, end := e.text.Selection()
  782. moves := e.replace(start, end, s, true)
  783. if end < start {
  784. start = end
  785. }
  786. // Reset xoff.
  787. e.text.MoveCaret(0, 0)
  788. e.SetCaret(start+moves, start+moves)
  789. e.scrollCaret = true
  790. return moves
  791. }
  792. // modification represents a change to the contents of the editor buffer.
  793. // It contains the necessary information to both apply the change and
  794. // reverse it, and is useful for implementing undo/redo.
  795. type modification struct {
  796. // StartRune is the inclusive index of the first rune
  797. // modified.
  798. StartRune int
  799. // ApplyContent is the data inserted at StartRune to
  800. // apply this operation. It overwrites len([]rune(ReverseContent)) runes.
  801. ApplyContent string
  802. // ReverseContent is the data inserted at StartRune to
  803. // apply this operation. It overwrites len([]rune(ApplyContent)) runes.
  804. ReverseContent string
  805. }
  806. // undo applies the modification at e.history[e.historyIdx] and decrements
  807. // e.historyIdx.
  808. func (e *Editor) undo() (EditorEvent, bool) {
  809. e.initBuffer()
  810. if len(e.history) < 1 || e.nextHistoryIdx == 0 {
  811. return nil, false
  812. }
  813. mod := e.history[e.nextHistoryIdx-1]
  814. replaceEnd := mod.StartRune + utf8.RuneCountInString(mod.ApplyContent)
  815. e.replace(mod.StartRune, replaceEnd, mod.ReverseContent, false)
  816. caretEnd := mod.StartRune + utf8.RuneCountInString(mod.ReverseContent)
  817. e.SetCaret(caretEnd, mod.StartRune)
  818. e.nextHistoryIdx--
  819. return ChangeEvent{}, true
  820. }
  821. // redo applies the modification at e.history[e.historyIdx] and increments
  822. // e.historyIdx.
  823. func (e *Editor) redo() (EditorEvent, bool) {
  824. e.initBuffer()
  825. if len(e.history) < 1 || e.nextHistoryIdx == len(e.history) {
  826. return nil, false
  827. }
  828. mod := e.history[e.nextHistoryIdx]
  829. end := mod.StartRune + utf8.RuneCountInString(mod.ReverseContent)
  830. e.replace(mod.StartRune, end, mod.ApplyContent, false)
  831. caretEnd := mod.StartRune + utf8.RuneCountInString(mod.ApplyContent)
  832. e.SetCaret(caretEnd, mod.StartRune)
  833. e.nextHistoryIdx++
  834. return ChangeEvent{}, true
  835. }
  836. // replace the text between start and end with s. Indices are in runes.
  837. // It returns the number of runes inserted.
  838. // addHistory controls whether this modification is recorded in the undo
  839. // history. replace can modify text in positions unrelated to the cursor
  840. // position.
  841. func (e *Editor) replace(start, end int, s string, addHistory bool) int {
  842. length := e.text.Len()
  843. if start > end {
  844. start, end = end, start
  845. }
  846. start = min(start, length)
  847. end = min(end, length)
  848. replaceSize := end - start
  849. el := e.Len()
  850. var sc int
  851. idx := 0
  852. for idx < len(s) {
  853. if e.MaxLen > 0 && el-replaceSize+sc >= e.MaxLen {
  854. s = s[:idx]
  855. break
  856. }
  857. _, n := utf8.DecodeRuneInString(s[idx:])
  858. if e.Filter != "" && !strings.Contains(e.Filter, s[idx:idx+n]) {
  859. s = s[:idx] + s[idx+n:]
  860. continue
  861. }
  862. idx += n
  863. sc++
  864. }
  865. if addHistory {
  866. deleted := make([]rune, 0, replaceSize)
  867. readPos := e.text.ByteOffset(start)
  868. for i := 0; i < replaceSize; i++ {
  869. ru, s, _ := e.text.ReadRuneAt(int64(readPos))
  870. readPos += int64(s)
  871. deleted = append(deleted, ru)
  872. }
  873. if e.nextHistoryIdx < len(e.history) {
  874. e.history = e.history[:e.nextHistoryIdx]
  875. }
  876. e.history = append(e.history, modification{
  877. StartRune: start,
  878. ApplyContent: s,
  879. ReverseContent: string(deleted),
  880. })
  881. e.nextHistoryIdx++
  882. }
  883. sc = e.text.Replace(start, end, s)
  884. newEnd := start + sc
  885. adjust := func(pos int) int {
  886. switch {
  887. case newEnd < pos && pos <= end:
  888. pos = newEnd
  889. case end < pos:
  890. diff := newEnd - end
  891. pos = pos + diff
  892. }
  893. return pos
  894. }
  895. e.ime.start = adjust(e.ime.start)
  896. e.ime.end = adjust(e.ime.end)
  897. return sc
  898. }
  899. // MoveCaret moves the caret (aka selection start) and the selection end
  900. // relative to their current positions. Positive distances moves forward,
  901. // negative distances moves backward. Distances are in grapheme clusters,
  902. // which closely match what users perceive as "characters" even when the
  903. // characters are multiple code points long.
  904. func (e *Editor) MoveCaret(startDelta, endDelta int) {
  905. e.initBuffer()
  906. e.text.MoveCaret(startDelta, endDelta)
  907. }
  908. // deleteWord deletes the next word(s) in the specified direction.
  909. // Unlike moveWord, deleteWord treats whitespace as a word itself.
  910. // Positive is forward, negative is backward.
  911. // Absolute values greater than one will delete that many words.
  912. // The selection counts as a single word.
  913. func (e *Editor) deleteWord(distance int) (deletedRunes int) {
  914. if distance == 0 {
  915. return
  916. }
  917. start, end := e.text.Selection()
  918. if start != end {
  919. deletedRunes = e.Delete(1)
  920. distance -= sign(distance)
  921. }
  922. if distance == 0 {
  923. return deletedRunes
  924. }
  925. // split the distance information into constituent parts to be
  926. // used independently.
  927. words, direction := distance, 1
  928. if distance < 0 {
  929. words, direction = distance*-1, -1
  930. }
  931. caret, _ := e.text.Selection()
  932. // atEnd if offset is at or beyond either side of the buffer.
  933. atEnd := func(runes int) bool {
  934. idx := caret + runes*direction
  935. return idx <= 0 || idx >= e.Len()
  936. }
  937. // next returns the appropriate rune given the direction and offset in runes).
  938. next := func(runes int) rune {
  939. idx := caret + runes*direction
  940. if idx < 0 {
  941. idx = 0
  942. } else if idx > e.Len() {
  943. idx = e.Len()
  944. }
  945. off := e.text.ByteOffset(idx)
  946. var r rune
  947. if direction < 0 {
  948. r, _, _ = e.text.ReadRuneBefore(int64(off))
  949. } else {
  950. r, _, _ = e.text.ReadRuneAt(int64(off))
  951. }
  952. return r
  953. }
  954. runes := 1
  955. for ii := 0; ii < words; ii++ {
  956. r := next(runes)
  957. wantSpace := unicode.IsSpace(r)
  958. for r := next(runes); unicode.IsSpace(r) == wantSpace && !atEnd(runes); r = next(runes) {
  959. runes += 1
  960. }
  961. }
  962. deletedRunes += e.Delete(runes * direction)
  963. return deletedRunes
  964. }
  965. // SelectionLen returns the length of the selection, in runes; it is
  966. // equivalent to utf8.RuneCountInString(e.SelectedText()).
  967. func (e *Editor) SelectionLen() int {
  968. e.initBuffer()
  969. return e.text.SelectionLen()
  970. }
  971. // Selection returns the start and end of the selection, as rune offsets.
  972. // start can be > end.
  973. func (e *Editor) Selection() (start, end int) {
  974. e.initBuffer()
  975. return e.text.Selection()
  976. }
  977. // SetCaret moves the caret to start, and sets the selection end to end. start
  978. // and end are in runes, and represent offsets into the editor text.
  979. func (e *Editor) SetCaret(start, end int) {
  980. e.initBuffer()
  981. e.text.SetCaret(start, end)
  982. e.scrollCaret = true
  983. e.scroller.Stop()
  984. }
  985. // SelectedText returns the currently selected text (if any) from the editor.
  986. func (e *Editor) SelectedText() string {
  987. e.initBuffer()
  988. e.scratch = e.text.SelectedText(e.scratch)
  989. return string(e.scratch)
  990. }
  991. // ClearSelection clears the selection, by setting the selection end equal to
  992. // the selection start.
  993. func (e *Editor) ClearSelection() {
  994. e.initBuffer()
  995. e.text.ClearSelection()
  996. }
  997. // WriteTo implements io.WriterTo.
  998. func (e *Editor) WriteTo(w io.Writer) (int64, error) {
  999. e.initBuffer()
  1000. return e.text.WriteTo(w)
  1001. }
  1002. // Seek implements io.Seeker.
  1003. func (e *Editor) Seek(offset int64, whence int) (int64, error) {
  1004. e.initBuffer()
  1005. return e.text.Seek(offset, whence)
  1006. }
  1007. // Read implements io.Reader.
  1008. func (e *Editor) Read(p []byte) (int, error) {
  1009. e.initBuffer()
  1010. return e.text.Read(p)
  1011. }
  1012. // Regions returns visible regions covering the rune range [start,end).
  1013. func (e *Editor) Regions(start, end int, regions []Region) []Region {
  1014. e.initBuffer()
  1015. return e.text.Regions(start, end, regions)
  1016. }
  1017. func max(a, b int) int {
  1018. if a > b {
  1019. return a
  1020. }
  1021. return b
  1022. }
  1023. func min(a, b int) int {
  1024. if a < b {
  1025. return a
  1026. }
  1027. return b
  1028. }
  1029. func abs(n int) int {
  1030. if n < 0 {
  1031. return -n
  1032. }
  1033. return n
  1034. }
  1035. func sign(n int) int {
  1036. switch {
  1037. case n < 0:
  1038. return -1
  1039. case n > 0:
  1040. return 1
  1041. default:
  1042. return 0
  1043. }
  1044. }
  1045. func (s ChangeEvent) isEditorEvent() {}
  1046. func (s SubmitEvent) isEditorEvent() {}
  1047. func (s SelectEvent) isEditorEvent() {}