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