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