...

Source file src/code.rocket9labs.com/tslocum/etk/text.go

Documentation: code.rocket9labs.com/tslocum/etk

     1  package etk
     2  
     3  import (
     4  	"image"
     5  	"image/color"
     6  
     7  	"code.rocket9labs.com/tslocum/etk/messeji"
     8  	"github.com/hajimehoshi/ebiten/v2"
     9  	"golang.org/x/image/font"
    10  	"golang.org/x/image/font/sfnt"
    11  )
    12  
    13  // Text is a text display widget.
    14  type Text struct {
    15  	*Box
    16  	field         *messeji.TextField
    17  	textFont      *sfnt.Font
    18  	textSize      int
    19  	textResize    bool
    20  	textAutoSize  int
    21  	scrollVisible bool
    22  	children      []Widget
    23  }
    24  
    25  // NewText returns a new Text widget.
    26  func NewText(text string) *Text {
    27  	f := newText()
    28  	f.SetText(text)
    29  	f.SetForegroundColor(Style.TextColorLight)
    30  	f.SetHandleKeyboard(true)
    31  
    32  	t := &Text{
    33  		Box:           NewBox(),
    34  		field:         f,
    35  		textFont:      Style.TextFont,
    36  		textSize:      Scale(Style.TextSize),
    37  		scrollVisible: true,
    38  	}
    39  	t.resizeFont()
    40  	return t
    41  }
    42  
    43  // SetRect sets the position and size of the widget.
    44  func (t *Text) SetRect(r image.Rectangle) {
    45  	t.Lock()
    46  	defer t.Unlock()
    47  
    48  	t.rect = r
    49  	t.field.SetRect(r)
    50  	t.resizeFont()
    51  }
    52  
    53  // Foreground return the color of the text within the field.
    54  func (t *Text) Foreground() color.RGBA {
    55  	t.Lock()
    56  	defer t.Unlock()
    57  
    58  	return t.field.ForegroundColor()
    59  }
    60  
    61  // SetForegroundColor sets the color of the text within the field.
    62  func (t *Text) SetForeground(c color.RGBA) {
    63  	t.Lock()
    64  	defer t.Unlock()
    65  
    66  	t.field.SetForegroundColor(c)
    67  }
    68  
    69  // Focus returns the focus state of the widget.
    70  func (t *Text) Focus() bool {
    71  	return false
    72  }
    73  
    74  // SetFocus sets the focus state of the widget.
    75  func (t *Text) SetFocus(focus bool) bool {
    76  	return false
    77  }
    78  
    79  // SetScrollBarWidth sets the width of the scroll bar.
    80  func (t *Text) SetScrollBarWidth(width int) {
    81  	t.Lock()
    82  	defer t.Unlock()
    83  
    84  	t.field.SetScrollBarWidth(width)
    85  }
    86  
    87  // SetScrollBarColors sets the color of the scroll bar area and handle.
    88  func (t *Text) SetScrollBarColors(area color.RGBA, handle color.RGBA) {
    89  	t.Lock()
    90  	defer t.Unlock()
    91  
    92  	t.field.SetScrollBarColors(Style.ScrollAreaColor, Style.ScrollHandleColor)
    93  }
    94  
    95  // SetScrollBorderColor sets the color of the top, right, bottom and left border
    96  // of the scroll bar handle.
    97  func (t *Text) SetScrollBorderColors(top color.RGBA, right color.RGBA, bottom color.RGBA, left color.RGBA) {
    98  	t.Lock()
    99  	defer t.Unlock()
   100  
   101  	t.field.SetScrollBorderColors(top, right, bottom, left)
   102  }
   103  
   104  // SetWordWrap sets a flag which, when enabled, causes text to wrap without breaking words.
   105  func (t *Text) SetWordWrap(wrap bool) {
   106  	t.Lock()
   107  	defer t.Unlock()
   108  
   109  	t.field.SetWordWrap(wrap)
   110  }
   111  
   112  // SetHorizontal sets the horizontal alignment of the text within the field.
   113  func (t *Text) SetHorizontal(h Alignment) {
   114  	t.Lock()
   115  	defer t.Unlock()
   116  
   117  	t.field.SetHorizontal(messeji.Alignment(h))
   118  }
   119  
   120  // SetVertical sets the vertical alignment of the text within the field.
   121  func (t *Text) SetVertical(v Alignment) {
   122  	t.Lock()
   123  	defer t.Unlock()
   124  
   125  	t.field.SetVertical(messeji.Alignment(v))
   126  }
   127  
   128  // Cursor returns the cursor shape shown when a mouse cursor hovers over the
   129  // widget, or -1 to let widgets beneath determine the cursor shape.
   130  func (t *Text) Cursor() ebiten.CursorShapeType {
   131  	return ebiten.CursorShapeDefault
   132  }
   133  
   134  // Write writes to the text buffer.
   135  func (t *Text) Write(p []byte) (n int, err error) {
   136  	t.Lock()
   137  	defer t.Unlock()
   138  
   139  	n, err = t.field.Write(p)
   140  	if err != nil {
   141  		return n, err
   142  	}
   143  	t.resizeFont()
   144  	return n, err
   145  }
   146  
   147  // Text returns the content of the text buffer.
   148  func (t *Text) Text() string {
   149  	t.Lock()
   150  	defer t.Unlock()
   151  
   152  	return t.field.Text()
   153  }
   154  
   155  // SetText sets the text in the field.
   156  func (t *Text) SetText(text string) {
   157  	t.Lock()
   158  	defer t.Unlock()
   159  
   160  	t.field.SetText(text)
   161  	t.resizeFont()
   162  }
   163  
   164  func (t *Text) resizeFont() {
   165  	if !t.textResize {
   166  		if t.textAutoSize == t.textSize {
   167  			return
   168  		}
   169  		t.textAutoSize = t.textSize
   170  		ff := FontFace(t.textFont, t.textSize)
   171  		t.field.SetFont(ff, fontMutex)
   172  		return
   173  	}
   174  
   175  	w, h := t.rect.Dx()-t.field.Padding()*2, t.rect.Dy()-t.field.Padding()*2
   176  	if w == 0 || h == 0 {
   177  		if t.textAutoSize == t.textSize {
   178  			return
   179  		}
   180  		t.textAutoSize = t.textSize
   181  		ff := FontFace(t.textFont, t.textSize)
   182  		t.field.SetFont(ff, fontMutex)
   183  		return
   184  	}
   185  
   186  	var autoSize int
   187  	var ff font.Face
   188  	for autoSize = t.textSize; autoSize > 0; autoSize-- {
   189  		ff = FontFace(t.textFont, autoSize)
   190  		bounds := BoundString(ff, t.field.Text())
   191  		if bounds.Dx() <= w && bounds.Dy() <= h {
   192  			break
   193  		}
   194  	}
   195  	if t.textAutoSize == autoSize {
   196  		return
   197  	}
   198  
   199  	t.field.SetFont(ff, fontMutex)
   200  	t.textAutoSize = autoSize
   201  }
   202  
   203  func (t *Text) scrollBarVisible() bool {
   204  	if t.textResize {
   205  		return false
   206  	}
   207  	return t.scrollVisible
   208  }
   209  
   210  // SetScrollBarVisible sets whether the scroll bar is visible on the screen.
   211  func (t *Text) SetScrollBarVisible(scrollVisible bool) {
   212  	t.Lock()
   213  	defer t.Unlock()
   214  
   215  	t.scrollVisible = scrollVisible
   216  	t.field.SetScrollBarVisible(t.scrollBarVisible())
   217  }
   218  
   219  // SetAutoHideScrollBar sets whether the scroll bar is automatically hidden
   220  // when the entire text buffer is visible.
   221  func (t *Text) SetAutoHideScrollBar(autoHide bool) {
   222  	t.Lock()
   223  	defer t.Unlock()
   224  
   225  	t.field.SetAutoHideScrollBar(autoHide)
   226  }
   227  
   228  // SetFont sets the font and text size of the field. Scaling is not applied.
   229  func (t *Text) SetFont(fnt *sfnt.Font, size int) {
   230  	t.Lock()
   231  	defer t.Unlock()
   232  
   233  	t.textFont, t.textSize = fnt, size
   234  	t.resizeFont()
   235  }
   236  
   237  // SetAutoResize sets whether the font is automatically scaled down when it is
   238  // too large to fit the entire text buffer on one line.
   239  func (t *Text) SetAutoResize(resize bool) {
   240  	t.Lock()
   241  	defer t.Unlock()
   242  
   243  	t.textResize = resize
   244  	t.resizeFont()
   245  	t.field.SetScrollBarVisible(t.scrollBarVisible())
   246  }
   247  
   248  // Padding returns the amount of padding around the text within the field.
   249  func (t *Text) Padding() int {
   250  	t.Lock()
   251  	defer t.Unlock()
   252  
   253  	return t.field.Padding()
   254  }
   255  
   256  // SetPadding sets the amount of padding around the text within the field.
   257  func (t *Text) SetPadding(padding int) {
   258  	t.Lock()
   259  	defer t.Unlock()
   260  
   261  	t.field.SetPadding(padding)
   262  }
   263  
   264  // SetFollow sets whether the field should automatically scroll to the end when
   265  // content is added to the buffer.
   266  func (t *Text) SetFollow(follow bool) {
   267  	t.Lock()
   268  	defer t.Unlock()
   269  
   270  	t.field.SetFollow(follow)
   271  }
   272  
   273  // SetSingleLine sets whether the field displays all text on a single line.
   274  // When enabled, the field scrolls horizontally. Otherwise, it scrolls vertically.
   275  func (t *Text) SetSingleLine(single bool) {
   276  	t.Lock()
   277  	defer t.Unlock()
   278  
   279  	t.field.SetSingleLine(single)
   280  }
   281  
   282  // SetMask sets the rune used to mask the text buffer contents. Set to 0 to disable.
   283  func (t *Text) SetMask(r rune) {
   284  	t.Lock()
   285  	defer t.Unlock()
   286  
   287  	t.field.SetMask(r)
   288  }
   289  
   290  // HandleKeyboard is called when a keyboard event occurs.
   291  func (t *Text) HandleKeyboard(key ebiten.Key, r rune) (handled bool, err error) {
   292  	return t.field.HandleKeyboardEvent(key, r)
   293  }
   294  
   295  // HandleMouse is called when a mouse event occurs.
   296  func (t *Text) HandleMouse(cursor image.Point, pressed bool, clicked bool) (handled bool, err error) {
   297  	return t.field.HandleMouseEvent(cursor, pressed, clicked)
   298  }
   299  
   300  // Draw draws the widget on the screen.
   301  func (t *Text) Draw(screen *ebiten.Image) error {
   302  	t.field.Draw(screen)
   303  	return nil
   304  }
   305  
   306  // Children returns the children of the widget.
   307  func (t *Text) Children() []Widget {
   308  	t.Lock()
   309  	defer t.Unlock()
   310  
   311  	return t.children
   312  }
   313  
   314  // AddChild adds a child to the widget.
   315  func (t *Text) AddChild(w ...Widget) {
   316  	t.Lock()
   317  	defer t.Unlock()
   318  
   319  	t.children = append(t.children, w...)
   320  }
   321  
   322  var _ Widget = &Text{}
   323  

View as plain text