github.com/jmigpin/editor@v1.6.0/util/uiutil/widget/text.go (about) 1 package widget 2 3 import ( 4 "image" 5 "image/color" 6 7 "github.com/jmigpin/editor/util/drawutil" 8 "github.com/jmigpin/editor/util/drawutil/drawer4" 9 "github.com/jmigpin/editor/util/imageutil" 10 "github.com/jmigpin/editor/util/iout/iorw" 11 ) 12 13 type Text struct { 14 ENode 15 TextScroll 16 17 Drawer drawutil.Drawer 18 19 scrollable struct{ x, y bool } 20 ctx ImageContext 21 bg color.Color 22 23 rw iorw.ReadWriterAt 24 } 25 26 func NewText(ctx ImageContext) *Text { 27 t := &Text{ctx: ctx} 28 29 t.Drawer = drawer4.New() 30 31 t.TextScroll.Text = t 32 t.TextScroll.Drawer = t.Drawer 33 34 rw := iorw.NewBytesReadWriterAt(nil) 35 t.SetRW(rw) 36 37 return t 38 } 39 40 //---------- 41 42 func (t *Text) RW() iorw.ReadWriterAt { 43 return t.rw 44 } 45 46 func (t *Text) SetRW(rw iorw.ReadWriterAt) { 47 t.rw = rw 48 t.Drawer.SetReader(rw) 49 } 50 51 func (t *Text) Len() int { 52 return t.rw.Max() - t.rw.Min() 53 } 54 55 // Result might not be a copy, so changes to the slice might affect the text data. 56 func (t *Text) Bytes() ([]byte, error) { 57 return iorw.ReadFastFull(t.rw) 58 } 59 60 func (t *Text) SetBytes(b []byte) error { 61 if err := iorw.SetBytes(t.rw, b); err != nil { 62 return err 63 } 64 t.contentChanged() 65 return nil 66 } 67 68 //---------- 69 70 func (t *Text) Str() string { 71 p, err := t.Bytes() 72 if err != nil { 73 return "" 74 } 75 return string(p) 76 } 77 78 func (t *Text) SetStr(str string) error { 79 return t.SetBytes([]byte(str)) 80 } 81 82 //---------- 83 84 func (t *Text) contentChanged() { 85 t.Drawer.ContentChanged() 86 87 // content changing can influence the layout in the case of dynamic sized textareas (needs layout). Also in the case of scrollareas that need to recalc scrollbars. 88 t.MarkNeedsLayoutAndPaint() 89 } 90 91 //---------- 92 93 // implements Scrollable interface. 94 func (t *Text) SetScrollable(x, y bool) { 95 t.scrollable.x = x 96 t.scrollable.y = y 97 } 98 99 //---------- 100 101 func (t *Text) RuneOffset() int { 102 return t.Drawer.RuneOffset() 103 } 104 105 func (t *Text) SetRuneOffset(v int) { 106 if t.scrollable.y && t.Drawer.RuneOffset() != v { 107 t.Drawer.SetRuneOffset(v) 108 t.MarkNeedsLayoutAndPaint() 109 } 110 } 111 112 //---------- 113 114 func (t *Text) IndexVisible(offset int) bool { 115 return t.Drawer.RangeVisible(offset, 0) 116 } 117 func (t *Text) MakeIndexVisible(offset int) { 118 t.MakeRangeVisible(offset, 0) 119 } 120 func (t *Text) MakeRangeVisible(offset, n int) { 121 o := t.Drawer.RangeVisibleOffset(offset, n) 122 t.SetRuneOffset(o) 123 } 124 125 //---------- 126 127 func (t *Text) GetPoint(i int) image.Point { 128 return t.Drawer.LocalPointOf(i) 129 } 130 func (t *Text) GetIndex(p image.Point) int { 131 return t.Drawer.LocalIndexOf(p) 132 } 133 134 //---------- 135 136 func (t *Text) LineHeight() int { 137 return t.Drawer.LineHeight() 138 } 139 140 //---------- 141 142 func (t *Text) Measure(hint image.Point) image.Point { 143 b := t.Bounds 144 b.Max = b.Min.Add(hint) 145 t.Drawer.SetBounds(b) 146 m := t.Drawer.Measure() 147 return imageutil.MinPoint(m, hint) 148 } 149 150 //---------- 151 152 func (t *Text) Layout() { 153 if t.Bounds != t.Drawer.Bounds() { 154 t.Drawer.SetBounds(t.Bounds) 155 t.MarkNeedsPaint() 156 } 157 } 158 159 //---------- 160 161 func (t *Text) PaintBase() { 162 imageutil.FillRectangle(t.ctx.Image(), t.Bounds, t.bg) 163 } 164 func (t *Text) Paint() { 165 t.Drawer.Draw(t.ctx.Image()) 166 } 167 168 //---------- 169 170 func (t *Text) OnThemeChange() { 171 // word highlight ops (contain fg/bg colors) are cached. A contentchanged() here is the easiest way to invalidate the cache and have all the colors be updated. 172 t.Drawer.ContentChanged() 173 174 fg := t.TreeThemePaletteColor("text_fg") 175 t.Drawer.SetFg(fg) 176 177 t.bg = t.TreeThemePaletteColor("text_bg") 178 179 ff := t.TreeThemeFontFace() 180 if ff != t.Drawer.FontFace() { 181 t.Drawer.SetFontFace(ff) 182 t.MarkNeedsLayoutAndPaint() 183 } else { 184 t.MarkNeedsPaint() 185 } 186 }