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  }