1 package messeji
2
3 import (
4 "bytes"
5 "image"
6 "image/color"
7 "math"
8 "strings"
9 "sync"
10 "unicode"
11
12 "github.com/hajimehoshi/ebiten/v2"
13 "github.com/hajimehoshi/ebiten/v2/inpututil"
14 "github.com/hajimehoshi/ebiten/v2/text/v2"
15 )
16
17
18 type Alignment int
19
20 const (
21
22 AlignStart Alignment = 0
23
24
25 AlignCenter Alignment = 1
26
27
28 AlignEnd Alignment = 2
29 )
30
31 const (
32 initialPadding = 5
33 initialScrollWidth = 32
34 maxScroll = 3
35 )
36
37 var (
38 initialForeground = color.RGBA{0, 0, 0, 255}
39 initialBackground = color.RGBA{255, 255, 255, 255}
40 initialScrollArea = color.RGBA{200, 200, 200, 255}
41 initialScrollHandle = color.RGBA{108, 108, 108, 255}
42 )
43
44
45
46
47
48
49 type TextField struct {
50
51 r image.Rectangle
52
53
54 buffer [][]byte
55
56
57 incoming []byte
58
59
60 prefix string
61
62
63 suffix string
64
65
66 wordWrap bool
67
68
69 bufferWrapped []string
70
71
72
73 wrapStart int
74
75
76 needWrap int
77
78
79 wrapScrollBar bool
80
81
82
83 bufferSize int
84
85
86 lineWidths []int
87
88
89 singleLine bool
90
91
92 horizontal Alignment
93
94
95 vertical Alignment
96
97 autoResize bool
98
99
100 fontSource *text.GoTextFaceSource
101
102
103 fontFace *text.GoTextFace
104
105
106 fontSize int
107
108
109 overrideFontSize int
110
111
112 fontMutex *sync.Mutex
113
114
115 lineHeight int
116
117
118 overrideLineHeight int
119
120
121 lineOffset int
122
123
124 textColor color.RGBA
125
126
127 backgroundColor color.RGBA
128
129
130 padding int
131
132
133
134 follow bool
135
136
137 overflow bool
138
139
140 offset int
141
142
143 handleKeyboard bool
144
145
146
147 modified bool
148
149
150 scrollRect image.Rectangle
151
152
153 scrollWidth int
154
155
156 scrollAreaColor color.RGBA
157
158
159 scrollHandleColor color.RGBA
160
161
162 scrollBorderSize int
163
164
165 scrollBorderTop color.RGBA
166 scrollBorderRight color.RGBA
167 scrollBorderBottom color.RGBA
168 scrollBorderLeft color.RGBA
169
170
171 scrollVisible bool
172
173
174
175 scrollAutoHide bool
176
177
178 scrollDrag bool
179
180
181 scrollDragPoint image.Point
182
183
184 scrollDragOffset int
185
186
187 maskRune rune
188
189
190 img *ebiten.Image
191
192
193 visible bool
194
195
196 redraw bool
197
198
199 keyBuffer []ebiten.Key
200
201
202 runeBuffer []rune
203
204 sync.Mutex
205 }
206
207
208 func NewTextField(fontSource *text.GoTextFaceSource, fontSize int, fontMutex *sync.Mutex) *TextField {
209 if fontMutex == nil {
210 fontMutex = &sync.Mutex{}
211 }
212
213 f := &TextField{
214 fontSource: fontSource,
215 fontSize: fontSize,
216 fontMutex: fontMutex,
217 textColor: initialForeground,
218 backgroundColor: initialBackground,
219 padding: initialPadding,
220 scrollWidth: initialScrollWidth,
221 scrollAreaColor: initialScrollArea,
222 scrollHandleColor: initialScrollHandle,
223 follow: true,
224 wordWrap: true,
225 scrollVisible: true,
226 scrollAutoHide: true,
227 scrollDragPoint: image.Point{-1, -1},
228 visible: true,
229 redraw: true,
230 }
231
232 f.fontMutex.Lock()
233 defer f.fontMutex.Unlock()
234
235 f.resizeFont()
236 return f
237 }
238
239
240 func (f *TextField) Rect() image.Rectangle {
241 f.Lock()
242 defer f.Unlock()
243
244 return f.r
245 }
246
247
248 func (f *TextField) SetRect(r image.Rectangle) {
249 f.Lock()
250 defer f.Unlock()
251
252 if f.r.Eq(r) {
253 return
254 }
255
256 if f.r.Dx() != r.Dx() || f.r.Dy() != r.Dy() {
257 f.bufferWrapped = f.bufferWrapped[:0]
258 f.lineWidths = f.lineWidths[:0]
259 f.needWrap = 0
260 f.wrapStart = 0
261 f.modified = true
262 }
263
264 f.r = r
265 f.resizeFont()
266 }
267
268 func (f *TextField) text() string {
269 f.processIncoming()
270 f.resizeFont()
271 return string(bytes.Join(f.buffer, []byte("\n")))
272 }
273
274
275 func (f *TextField) Text() string {
276 f.Lock()
277 defer f.Unlock()
278
279 return f.text()
280 }
281
282
283 func (f *TextField) SetText(text string) {
284 f.Lock()
285 defer f.Unlock()
286
287 f.buffer = f.buffer[:0]
288 f.bufferWrapped = f.bufferWrapped[:0]
289 f.lineWidths = f.lineWidths[:0]
290 f.needWrap = 0
291 f.wrapStart = 0
292 f.incoming = append(f.incoming[:0], []byte(text)...)
293 f.modified = true
294 f.redraw = true
295 f.resizeFont()
296 }
297
298
299
300 func (f *TextField) SetLast(text string) {
301 f.Lock()
302 defer f.Unlock()
303
304 t := bytes.ReplaceAll([]byte(text), []byte("\n"), []byte(" "))
305
306 f.processIncoming()
307
308 bufferLen := len(f.buffer)
309 if bufferLen == 0 {
310 f.incoming = append(f.incoming[:0], t...)
311 } else {
312 f.buffer[bufferLen-1] = t
313 if f.needWrap == -1 || f.needWrap > bufferLen-1 {
314 f.needWrap = bufferLen - 1
315 }
316 }
317
318 f.modified = true
319 f.redraw = true
320 f.resizeFont()
321 }
322
323
324 func (f *TextField) SetPrefix(text string) {
325 f.Lock()
326 defer f.Unlock()
327
328 f.prefix = text
329 f.needWrap = 0
330 f.wrapStart = 0
331 f.modified = true
332 f.resizeFont()
333 }
334
335
336 func (f *TextField) SetSuffix(text string) {
337 f.Lock()
338 defer f.Unlock()
339
340 f.suffix = text
341 f.needWrap = 0
342 f.wrapStart = 0
343 f.modified = true
344 f.resizeFont()
345 }
346
347
348
349 func (f *TextField) SetFollow(follow bool) {
350 f.Lock()
351 defer f.Unlock()
352
353 f.follow = follow
354 }
355
356
357
358 func (f *TextField) SetSingleLine(single bool) {
359 f.Lock()
360 defer f.Unlock()
361
362 if f.singleLine == single {
363 return
364 }
365
366 f.singleLine = single
367 f.needWrap = 0
368 f.wrapStart = 0
369 f.modified = true
370 f.resizeFont()
371 }
372
373
374 func (f *TextField) SetHorizontal(h Alignment) {
375 f.Lock()
376 defer f.Unlock()
377
378 if f.horizontal == h {
379 return
380 }
381
382 f.horizontal = h
383 f.needWrap = 0
384 f.wrapStart = 0
385 f.modified = true
386 }
387
388
389 func (f *TextField) SetVertical(v Alignment) {
390 f.Lock()
391 defer f.Unlock()
392
393 if f.vertical == v {
394 return
395 }
396
397 f.vertical = v
398 f.needWrap = 0
399 f.wrapStart = 0
400 f.modified = true
401 }
402
403
404 func (f *TextField) LineHeight() int {
405 f.Lock()
406 defer f.Unlock()
407
408 if f.overrideLineHeight != 0 {
409 return f.overrideLineHeight
410 }
411 return f.lineHeight
412 }
413
414
415
416 func (f *TextField) SetLineHeight(height int) {
417 f.Lock()
418 defer f.Unlock()
419
420 f.overrideLineHeight = height
421 f.needWrap = 0
422 f.wrapStart = 0
423 f.modified = true
424 f.resizeFont()
425 }
426
427
428 func (f *TextField) ForegroundColor() color.RGBA {
429 f.Lock()
430 defer f.Unlock()
431
432 return f.textColor
433 }
434
435
436 func (f *TextField) SetForegroundColor(c color.RGBA) {
437 f.Lock()
438 defer f.Unlock()
439
440 f.textColor = c
441 f.modified = true
442 }
443
444
445 func (f *TextField) SetBackgroundColor(c color.RGBA) {
446 f.Lock()
447 defer f.Unlock()
448
449 f.backgroundColor = c
450 f.modified = true
451 }
452
453
454 func (f *TextField) SetFont(faceSource *text.GoTextFaceSource, size int, mutex *sync.Mutex) {
455 if mutex == nil {
456 mutex = &sync.Mutex{}
457 }
458
459 f.Lock()
460 defer f.Unlock()
461
462 mutex.Lock()
463 defer mutex.Unlock()
464
465 f.fontSource = faceSource
466 f.fontSize = size
467 f.fontMutex = mutex
468 f.overrideFontSize = 0
469
470 f.needWrap = 0
471 f.wrapStart = 0
472 f.modified = true
473 f.resizeFont()
474 }
475
476
477
478 func (f *TextField) SetAutoResize(resize bool) {
479 f.Lock()
480 defer f.Unlock()
481
482 f.autoResize = resize
483 f.resizeFont()
484 }
485
486
487 func (f *TextField) Padding() int {
488 f.Lock()
489 defer f.Unlock()
490
491 return f.padding
492 }
493
494
495 func (f *TextField) SetPadding(padding int) {
496 f.Lock()
497 defer f.Unlock()
498
499 f.padding = padding
500 f.needWrap = 0
501 f.wrapStart = 0
502 f.modified = true
503 f.resizeFont()
504 }
505
506
507 func (f *TextField) Visible() bool {
508 return f.visible
509 }
510
511
512 func (f *TextField) SetVisible(visible bool) {
513 f.Lock()
514 defer f.Unlock()
515
516 if f.visible == visible {
517 return
518 }
519
520 f.visible = visible
521 if visible {
522 f.redraw = true
523 }
524 }
525
526
527 func (f *TextField) SetScrollBarWidth(width int) {
528 f.Lock()
529 defer f.Unlock()
530
531 if f.scrollWidth == width {
532 return
533 }
534
535 f.scrollWidth = width
536 f.needWrap = 0
537 f.wrapStart = 0
538 f.modified = true
539 f.resizeFont()
540 }
541
542
543 func (f *TextField) SetScrollBarColors(area color.RGBA, handle color.RGBA) {
544 f.Lock()
545 defer f.Unlock()
546
547 f.scrollAreaColor, f.scrollHandleColor = area, handle
548 f.redraw = true
549 }
550
551
552 func (f *TextField) SetScrollBorderSize(size int) {
553 f.Lock()
554 defer f.Unlock()
555
556 f.scrollBorderSize = size
557 f.redraw = true
558 f.resizeFont()
559 }
560
561
562
563 func (f *TextField) SetScrollBorderColors(top color.RGBA, right color.RGBA, bottom color.RGBA, left color.RGBA) {
564 f.Lock()
565 defer f.Unlock()
566
567 f.scrollBorderTop = top
568 f.scrollBorderRight = right
569 f.scrollBorderBottom = bottom
570 f.scrollBorderLeft = left
571 f.redraw = true
572 }
573
574
575 func (f *TextField) SetScrollBarVisible(scrollVisible bool) {
576 f.Lock()
577 defer f.Unlock()
578
579 if f.scrollVisible == scrollVisible {
580 return
581 }
582
583 f.scrollVisible = scrollVisible
584 f.needWrap = 0
585 f.wrapStart = 0
586 f.modified = true
587 f.resizeFont()
588 }
589
590
591
592 func (f *TextField) SetAutoHideScrollBar(autoHide bool) {
593 f.Lock()
594 defer f.Unlock()
595
596 if f.scrollAutoHide == autoHide {
597 return
598 }
599
600 f.scrollAutoHide = autoHide
601 f.needWrap = 0
602 f.wrapStart = 0
603 f.modified = true
604 f.resizeFont()
605 }
606
607
608 func (f *TextField) WordWrap() bool {
609 f.Lock()
610 defer f.Unlock()
611
612 return f.wordWrap
613 }
614
615
616 func (f *TextField) SetWordWrap(wrap bool) {
617 f.Lock()
618 defer f.Unlock()
619
620 if f.wordWrap == wrap {
621 return
622 }
623
624 f.wordWrap = wrap
625 f.needWrap = 0
626 f.wrapStart = 0
627 f.modified = true
628 }
629
630 func (f *TextField) resizeFont() {
631 if !f.autoResize {
632 if f.overrideFontSize == f.fontSize {
633 return
634 }
635 f.overrideFontSize = f.fontSize
636 f.fontFace = fontFace(f.fontSource, f.overrideFontSize)
637 f.fontUpdated()
638 f.bufferModified()
639 return
640 }
641
642 w, h := f.r.Dx()-f.padding*2, f.r.Dy()-f.padding*2
643 if w <= 0 || h <= 0 {
644 if f.overrideFontSize == f.fontSize {
645 return
646 }
647 f.overrideFontSize = f.fontSize
648 f.fontFace = fontFace(f.fontSource, f.overrideFontSize)
649 f.fontUpdated()
650 f.bufferModified()
651 return
652 }
653
654 f.processIncoming()
655
656 for size := f.fontSize; size > 0; size-- {
657 f.fontFace = fontFace(f.fontSource, size)
658 f.fontUpdated()
659 if f.lineHeight > h {
660 continue
661 }
662 f.needWrap = 0
663 f.wrapStart = 0
664 f.wrap()
665 if len(f.bufferWrapped) <= 1 {
666 break
667 }
668 }
669 }
670
671
672
673 func (f *TextField) SetHandleKeyboard(handle bool) {
674 f.Lock()
675 defer f.Unlock()
676
677 f.handleKeyboard = handle
678 }
679
680
681 func (f *TextField) SetMask(r rune) {
682 f.Lock()
683 defer f.Unlock()
684
685 if f.maskRune == r {
686 return
687 }
688
689 f.maskRune = r
690 f.modified = true
691 f.resizeFont()
692 }
693
694
695 func (f *TextField) Write(p []byte) (n int, err error) {
696 f.Lock()
697 defer f.Unlock()
698
699 return f._write(p)
700 }
701
702 func (f *TextField) _write(p []byte) (n int, err error) {
703 f.incoming = append(f.incoming, p...)
704 f.modified = true
705 f.redraw = true
706 return len(p), nil
707 }
708
709
710 func (f *TextField) HandleKeyboardEvent(key ebiten.Key, r rune) (handled bool, err error) {
711 f.Lock()
712 defer f.Unlock()
713
714 if !f.visible || rectIsZero(f.r) || !f.handleKeyboard {
715 return false, nil
716 }
717
718 return f._handleKeyboardEvent(key, r)
719 }
720
721 func (f *TextField) _handleKeyboardEvent(key ebiten.Key, r rune) (handled bool, err error) {
722 if key != -1 {
723
724 offsetAmount := 0
725 switch key {
726 case ebiten.KeyPageUp:
727 offsetAmount = 100
728 case ebiten.KeyPageDown:
729 offsetAmount = -100
730 }
731 if offsetAmount != 0 {
732 f.offset += offsetAmount
733 f.clampOffset()
734 f.redraw = true
735 return true, nil
736 }
737 return true, err
738 }
739 return true, nil
740 }
741
742 func (f *TextField) HandleMouseEvent(cursor image.Point, pressed bool, clicked bool) (handled bool, err error) {
743 f.Lock()
744 defer f.Unlock()
745
746 if !f.visible || rectIsZero(f.r) {
747 return false, nil
748 }
749
750 return f._handleMouseEvent(cursor, pressed, clicked)
751 }
752
753 func (f *TextField) _handleMouseEvent(cursor image.Point, pressed bool, clicked bool) (handled bool, err error) {
754 if !f.scrollDrag && !cursor.In(f.r) {
755 return false, nil
756 }
757
758
759 _, scroll := ebiten.Wheel()
760 if scroll != 0 {
761 if scroll < -maxScroll {
762 scroll = -maxScroll
763 } else if scroll > maxScroll {
764 scroll = maxScroll
765 }
766 const offsetAmount = 25
767 f.offset += int(scroll * offsetAmount)
768 f.clampOffset()
769 f.redraw = true
770 }
771
772
773 if !f.showScrollBar() {
774 return true, nil
775 } else if pressed || f.scrollDrag {
776 p := image.Point{cursor.X - f.r.Min.X, cursor.Y - f.r.Min.Y}
777 if pressed {
778
779 if !f.scrollDrag && !p.In(f.scrollRect) && f.scrollDragPoint.X == -1 && f.scrollDragPoint.Y == -1 {
780 f.scrollDragPoint = p
781 f.scrollDragOffset = f.offset
782 }
783 if f.scrollDragPoint.X != -1 {
784 delta := f.scrollDragPoint.Y - p.Y
785 f.offset = f.scrollDragOffset - delta
786 } else {
787 dragY := cursor.Y - f.r.Min.Y - f.scrollWidth/4
788 if dragY < 0 {
789 dragY = 0
790 } else if dragY > f.scrollRect.Dy() {
791 dragY = f.scrollRect.Dy()
792 }
793
794 pct := float64(dragY) / float64(f.scrollRect.Dy()-f.scrollWidth/2)
795 if pct < 0 {
796 pct = 0
797 } else if pct > 1 {
798 pct = 1
799 }
800
801 h := f.r.Dy()
802 f.offset = -int(float64(f.bufferSize-h-f.lineOffset+f.padding*2) * pct)
803 }
804 f.clampOffset()
805
806 f.redraw = true
807 f.scrollDrag = true
808 } else if !pressed {
809 f.scrollDrag = false
810 f.scrollDragPoint = image.Point{-1, -1}
811 f.scrollDragOffset = 0
812 }
813 }
814 return true, nil
815 }
816
817
818
819 func (f *TextField) Update() error {
820 f.Lock()
821 defer f.Unlock()
822
823 if !f.visible || rectIsZero(f.r) {
824 return nil
825 }
826
827 f.keyBuffer = inpututil.AppendJustPressedKeys(f.keyBuffer[:0])
828 for _, key := range f.keyBuffer {
829 handled, err := f._handleKeyboardEvent(key, 0)
830 if err != nil {
831 return err
832 } else if handled {
833 f.redraw = true
834 }
835 }
836
837 f.runeBuffer = ebiten.AppendInputChars(f.runeBuffer[:0])
838 for _, r := range f.runeBuffer {
839 handled, err := f._handleKeyboardEvent(-1, r)
840 if err != nil {
841 return err
842 } else if handled {
843 f.redraw = true
844 }
845 }
846
847 cx, cy := ebiten.CursorPosition()
848 if cx != 0 || cy != 0 {
849 handled, err := f._handleMouseEvent(image.Point{X: cx, Y: cy}, ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft), inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft))
850 if err != nil {
851 return err
852 } else if handled {
853 f.redraw = true
854 }
855 }
856
857 return nil
858 }
859
860
861
862 func (f *TextField) Draw(screen *ebiten.Image) {
863 f.Lock()
864 defer f.Unlock()
865
866 if f.modified {
867 f.fontMutex.Lock()
868
869 f.bufferModified()
870 f.modified = false
871
872 f.fontMutex.Unlock()
873 }
874
875 if !f.visible || rectIsZero(f.r) {
876 return
877 }
878
879 if f.redraw {
880 f.fontMutex.Lock()
881
882 f.drawImage()
883 f.redraw = false
884
885 f.fontMutex.Unlock()
886 }
887
888 op := &ebiten.DrawImageOptions{}
889 op.GeoM.Translate(float64(f.r.Min.X), float64(f.r.Min.Y))
890 screen.DrawImage(f.img, op)
891 }
892
893 func (f *TextField) fontUpdated() {
894 m := f.fontFace.Metrics()
895 f.lineHeight = int(m.HAscent + m.HDescent)
896 f.lineOffset = int(m.HLineGap)
897 if f.lineOffset < 0 {
898 f.lineOffset *= -1
899 }
900 }
901
902 func (f *TextField) wrapContent(withScrollBar bool) {
903 if withScrollBar != f.wrapScrollBar {
904 f.needWrap = 0
905 f.wrapStart = 0
906 } else if f.needWrap == -1 {
907 return
908 }
909 f.wrapScrollBar = withScrollBar
910
911 if len(f.buffer) == 0 || (f.singleLine && !f.autoResize) {
912 buffer := f.prefix + string(bytes.Join(f.buffer, nil)) + f.suffix
913 w, _ := text.Measure(buffer, f.fontFace, float64(f.lineHeight))
914
915 f.bufferWrapped = []string{buffer}
916 f.wrapStart = 0
917 f.lineWidths = append(f.lineWidths[:0], int(w))
918
919 f.needWrap = -1
920 return
921 }
922
923 w := f.r.Dx()
924 if withScrollBar {
925 w -= f.scrollWidth
926 }
927 bufferLen := len(f.buffer)
928 j := f.wrapStart
929 for i := f.needWrap; i < bufferLen; i++ {
930 var line string
931 if i == 0 {
932 line = f.prefix + string(f.buffer[i])
933 } else {
934 line = string(f.buffer[i])
935 }
936 if i == bufferLen-1 {
937 line += f.suffix
938 }
939 l := len(line)
940 availableWidth := w - (f.padding * 2)
941
942 f.wrapStart = j
943
944
945 if strings.TrimSpace(line) == "" {
946 if len(f.bufferWrapped) <= j {
947 f.bufferWrapped = append(f.bufferWrapped, "")
948 } else {
949 f.bufferWrapped[j] = ""
950 }
951 if len(f.lineWidths) <= j {
952 f.lineWidths = append(f.lineWidths, 0)
953 } else {
954 f.lineWidths[j] = 0
955 }
956 j++
957 continue
958 }
959
960
961
962 var start int
963 var lastSpace int
964 var lastSpaceSize int
965 var boundsWidth int
966 var lastBoundsWidth int
967 WRAPTEXT:
968 for start < l {
969 lastSpace = -1
970 lastBoundsWidth = -1
971 var e int
972 for _, r := range line[start:] {
973 runeSize := len(string(r))
974 if e > l-start-runeSize {
975 e = l - start - runeSize
976 }
977 if unicode.IsSpace(r) {
978 lastSpace = e
979 lastSpaceSize = runeSize
980 }
981 w, _ := text.Measure(line[start:start+e+runeSize], f.fontFace, float64(f.lineHeight))
982 boundsWidth = int(w)
983 if boundsWidth > availableWidth {
984 var addSpace bool
985 if e > 0 {
986 if e > 0 {
987 e -= runeSize
988 }
989 if f.wordWrap && lastSpace != -1 {
990 e = lastSpace
991 addSpace = true
992 }
993 }
994 if lastBoundsWidth == -1 {
995 w, _ := text.Measure(line[start:start+e], f.fontFace, float64(f.lineHeight))
996 boundsWidth = int(w)
997 } else {
998 boundsWidth = lastBoundsWidth
999 }
1000
1001 if len(f.bufferWrapped) <= j {
1002 f.bufferWrapped = append(f.bufferWrapped, line[start:start+e])
1003 } else {
1004 f.bufferWrapped[j] = line[start : start+e]
1005 }
1006 if len(f.lineWidths) <= j {
1007 f.lineWidths = append(f.lineWidths, boundsWidth)
1008 } else {
1009 f.lineWidths[j] = boundsWidth
1010 }
1011 j++
1012
1013 if addSpace {
1014 e += lastSpaceSize
1015 }
1016
1017 start += e
1018 if e == 0 {
1019 start += runeSize
1020 }
1021 continue WRAPTEXT
1022 }
1023 lastBoundsWidth = boundsWidth
1024 e += runeSize
1025 }
1026
1027 if len(f.bufferWrapped) <= j {
1028 f.bufferWrapped = append(f.bufferWrapped, line[start:])
1029 } else {
1030 f.bufferWrapped[j] = line[start:]
1031 }
1032 if len(f.lineWidths) <= j {
1033 f.lineWidths = append(f.lineWidths, boundsWidth)
1034 } else {
1035 f.lineWidths[j] = boundsWidth
1036 }
1037 j++
1038 break
1039 }
1040 }
1041
1042 if len(f.bufferWrapped) >= j {
1043 f.bufferWrapped = f.bufferWrapped[:j]
1044 }
1045
1046 f.needWrap = -1
1047 }
1048
1049
1050 func (f *TextField) drawContent() (overflow bool) {
1051 if f.backgroundColor.A != 0 {
1052 f.img.Fill(f.backgroundColor)
1053 } else {
1054 f.img.Clear()
1055 }
1056 fieldWidth := f.r.Dx()
1057 fieldHeight := f.r.Dy()
1058 if f.showScrollBar() {
1059 fieldWidth -= f.scrollWidth
1060 }
1061 lines := len(f.bufferWrapped)
1062
1063 h := f.r.Dy()
1064 lineHeight := f.overrideLineHeight
1065 if lineHeight == 0 {
1066 lineHeight = f.lineHeight
1067 }
1068 var firstVisible, lastVisible int
1069 firstVisible = 0
1070 lastVisible = len(f.bufferWrapped) - 1
1071 if !f.singleLine {
1072 firstVisible = (f.offset * -1) / f.lineHeight
1073 lastVisible = firstVisible + (f.r.Dy() / f.lineHeight) + 1
1074 if lastVisible > len(f.bufferWrapped)-1 {
1075 lastVisible = len(f.bufferWrapped) - 1
1076 }
1077 }
1078 numVisible := lastVisible - firstVisible
1079
1080 if f.singleLine {
1081 w, _ := text.Measure(f.bufferWrapped[firstVisible], f.fontFace, float64(f.lineHeight))
1082 f.bufferSize = int(w)
1083 if f.bufferSize > fieldWidth-f.padding*2 {
1084 overflow = true
1085 }
1086 } else {
1087 f.bufferSize = (len(f.bufferWrapped)) * lineHeight
1088 if f.bufferSize > fieldHeight-f.padding*2 {
1089 overflow = true
1090 }
1091 }
1092 for i := firstVisible; i <= lastVisible; i++ {
1093 line := f.bufferWrapped[i]
1094 if f.maskRune != 0 {
1095 line = strings.Repeat(string(f.maskRune), len(line))
1096 if i == lastVisible && len(line) > 0 && len(line) >= len(f.suffix) {
1097 line = line[:len(line)-len(f.suffix)] + f.suffix
1098 }
1099 }
1100 lineX := f.padding
1101 lineY := 1 + f.padding + -f.lineOffset + lineHeight*i
1102
1103
1104 lineOverflows := lineY < 0 || lineY >= h-f.padding
1105 if lineOverflows {
1106 overflow = true
1107 }
1108
1109
1110 if lineY < 0 {
1111 continue
1112 }
1113
1114
1115 if f.singleLine {
1116 lineX += f.offset
1117 } else {
1118 lineY += f.offset
1119 }
1120
1121
1122 if f.horizontal == AlignCenter {
1123 lineX = (fieldWidth - f.lineWidths[i]) / 2
1124 } else if f.horizontal == AlignEnd {
1125 lineX = (fieldWidth - f.lineWidths[i]) - f.padding - 1
1126 }
1127
1128
1129 totalHeight := f.lineOffset + lineHeight*(lines)
1130 if f.vertical == AlignCenter && (f.autoResize || totalHeight <= h) {
1131 lineY = fieldHeight/2 - totalHeight/2 + f.lineOffset + (lineHeight * (i)) - 2
1132 } else if f.vertical == AlignEnd && (f.autoResize || totalHeight <= h) {
1133 lineY = fieldHeight - lineHeight*(numVisible+1-i) - f.padding
1134 }
1135
1136
1137 op := &text.DrawOptions{}
1138 op.GeoM.Translate(float64(lineX), float64(lineY))
1139 op.ColorScale.ScaleWithColor(f.textColor)
1140 text.Draw(f.img, line, f.fontFace, op)
1141 }
1142
1143 return overflow
1144 }
1145
1146 func (f *TextField) clampOffset() {
1147 fieldSize := f.r.Dy()
1148 if f.singleLine {
1149 fieldSize = f.r.Dx()
1150 }
1151 minSize := -(f.bufferSize - fieldSize + f.padding*2)
1152 if !f.singleLine {
1153 minSize += f.lineOffset
1154 }
1155 if minSize > 0 {
1156 minSize = 0
1157 }
1158 maxSize := 0
1159 if f.offset < minSize {
1160 f.offset = minSize
1161 } else if f.offset > maxSize {
1162 f.offset = maxSize
1163 }
1164 }
1165
1166 func (f *TextField) showScrollBar() bool {
1167 return !f.autoResize && !f.singleLine && f.scrollVisible && (f.overflow || !f.scrollAutoHide)
1168 }
1169
1170 func (f *TextField) wrap() {
1171 w, h := f.r.Dx(), f.r.Dy()
1172
1173 var newImage bool
1174 if f.img == nil {
1175 newImage = true
1176 } else {
1177 imgRect := f.img.Bounds()
1178 imgW, imgH := imgRect.Dx(), imgRect.Dy()
1179 newImage = imgW != w || imgH != h
1180 }
1181 if newImage {
1182 f.img = ebiten.NewImage(w, h)
1183 }
1184
1185 showScrollBar := f.showScrollBar()
1186 f.wrapContent(showScrollBar)
1187 f.overflow = f.drawContent()
1188 if f.showScrollBar() != showScrollBar {
1189 f.wrapContent(!showScrollBar)
1190 f.drawContent()
1191 }
1192 }
1193
1194
1195 func (f *TextField) drawImage() {
1196 if rectIsZero(f.r) {
1197 f.img = nil
1198 return
1199 }
1200
1201 f.wrap()
1202
1203
1204 if f.showScrollBar() {
1205 w, h := f.r.Dx(), f.r.Dy()
1206
1207 scrollAreaX, scrollAreaY := w-f.scrollWidth, 0
1208 f.scrollRect = image.Rect(scrollAreaX, scrollAreaY, scrollAreaX+f.scrollWidth, h)
1209
1210 scrollBarH := f.scrollWidth / 2
1211 if scrollBarH < 4 {
1212 scrollBarH = 4
1213 }
1214
1215 scrollX, scrollY := w-f.scrollWidth, 0
1216 pct := float64(-f.offset) / float64(f.bufferSize-h-f.lineOffset+f.padding*2)
1217 scrollY += int(float64(h-scrollBarH) * pct)
1218 scrollBarRect := image.Rect(scrollX, scrollY, scrollX+f.scrollWidth, scrollY+scrollBarH)
1219
1220
1221 f.img.SubImage(f.scrollRect).(*ebiten.Image).Fill(f.scrollAreaColor)
1222
1223
1224 f.img.SubImage(scrollBarRect).(*ebiten.Image).Fill(f.scrollHandleColor)
1225
1226
1227 if f.scrollBorderSize != 0 {
1228 r := scrollBarRect
1229 f.img.SubImage(image.Rect(r.Min.X, r.Min.Y, r.Min.X+f.scrollBorderSize, r.Max.Y)).(*ebiten.Image).Fill(f.scrollBorderLeft)
1230 f.img.SubImage(image.Rect(r.Min.X, r.Min.Y, r.Max.X, r.Min.Y+f.scrollBorderSize)).(*ebiten.Image).Fill(f.scrollBorderTop)
1231 f.img.SubImage(image.Rect(r.Max.X-f.scrollBorderSize, r.Min.Y, r.Max.X, r.Max.Y)).(*ebiten.Image).Fill(f.scrollBorderRight)
1232 f.img.SubImage(image.Rect(r.Min.X, r.Max.Y-f.scrollBorderSize, r.Max.X, r.Max.Y)).(*ebiten.Image).Fill(f.scrollBorderBottom)
1233 }
1234 }
1235 }
1236
1237 func (f *TextField) processIncoming() {
1238 if len(f.incoming) == 0 {
1239 return
1240 }
1241
1242 line := len(f.buffer) - 1
1243 if line < 0 {
1244 line = 0
1245 f.buffer = append(f.buffer, nil)
1246 }
1247 if f.needWrap == -1 {
1248 f.needWrap = line
1249 }
1250 for _, b := range f.incoming {
1251 if b == '\n' {
1252 line++
1253 f.buffer = append(f.buffer, nil)
1254 continue
1255 }
1256 f.buffer[line] = append(f.buffer[line], b)
1257 }
1258 f.incoming = f.incoming[:0]
1259 }
1260
1261 func (f *TextField) bufferModified() {
1262 f.processIncoming()
1263 f.resizeFont()
1264
1265 f.drawImage()
1266
1267 lastOffset := f.offset
1268 if f.follow {
1269 f.offset = -math.MaxInt
1270 }
1271 f.clampOffset()
1272 if f.offset != lastOffset {
1273 f.drawImage()
1274 }
1275
1276 f.redraw = false
1277 }
1278
1279 func rectIsZero(r image.Rectangle) bool {
1280 return r.Dx() == 0 || r.Dy() == 0
1281 }
1282
1283 func fontFace(source *text.GoTextFaceSource, size int) *text.GoTextFace {
1284 return &text.GoTextFace{
1285 Source: source,
1286 Size: float64(size),
1287 }
1288 }
1289
View as plain text