...

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  // SetForeground 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  // SetLast sets the text of the last line of the field.
   165  func (t *Text) SetLast(text string) {
   166  	t.Lock()
   167  	defer t.Unlock()
   168  
   169  	t.field.SetLast(text)
   170  	t.resizeFont()
   171  }
   172  
   173  func (t *Text) resizeFont() {
   174  	if !t.textResize {
   175  		if t.textAutoSize == t.textSize {
   176  			return
   177  		}
   178  		t.textAutoSize = t.textSize
   179  		ff := FontFace(t.textFont, t.textSize)
   180  		t.field.SetFont(ff, fontMutex)
   181  		return
   182  	}
   183  
   184  	w, h := t.rect.Dx()-t.field.Padding()*2, t.rect.Dy()-t.field.Padding()*2
   185  	if w == 0 || h == 0 {
   186  		if t.textAutoSize == t.textSize {
   187  			return
   188  		}
   189  		t.textAutoSize = t.textSize
   190  		ff := FontFace(t.textFont, t.textSize)
   191  		t.field.SetFont(ff, fontMutex)
   192  		return
   193  	}
   194  
   195  	var autoSize int
   196  	var ff font.Face
   197  	for autoSize = t.textSize; autoSize > 0; autoSize-- {
   198  		ff = FontFace(t.textFont, autoSize)
   199  		bounds := BoundString(ff, t.field.Text())
   200  		if bounds.Dx() <= w && bounds.Dy() <= h {
   201  			break
   202  		}
   203  	}
   204  	if t.textAutoSize == autoSize {
   205  		return
   206  	}
   207  
   208  	t.field.SetFont(ff, fontMutex)
   209  	t.textAutoSize = autoSize
   210  }
   211  
   212  func (t *Text) scrollBarVisible() bool {
   213  	if t.textResize {
   214  		return false
   215  	}
   216  	return t.scrollVisible
   217  }
   218  
   219  // SetScrollBarVisible sets whether the scroll bar is visible on the screen.
   220  func (t *Text) SetScrollBarVisible(scrollVisible bool) {
   221  	t.Lock()
   222  	defer t.Unlock()
   223  
   224  	t.scrollVisible = scrollVisible
   225  	t.field.SetScrollBarVisible(t.scrollBarVisible())
   226  }
   227  
   228  // SetAutoHideScrollBar sets whether the scroll bar is automatically hidden
   229  // when the entire text buffer is visible.
   230  func (t *Text) SetAutoHideScrollBar(autoHide bool) {
   231  	t.Lock()
   232  	defer t.Unlock()
   233  
   234  	t.field.SetAutoHideScrollBar(autoHide)
   235  }
   236  
   237  // FontSize returns the font size of the field.
   238  func (t *Text) FontSize() int {
   239  	t.Lock()
   240  	defer t.Unlock()
   241  
   242  	return t.textAutoSize
   243  }
   244  
   245  // SetFont sets the font and text size of the field. Scaling is not applied.
   246  func (t *Text) SetFont(fnt *sfnt.Font, size int) {
   247  	t.Lock()
   248  	defer t.Unlock()
   249  
   250  	t.textFont, t.textSize = fnt, size
   251  	t.resizeFont()
   252  }
   253  
   254  // SetAutoResize sets whether the font is automatically scaled down when it is
   255  // too large to fit the entire text buffer on one line.
   256  func (t *Text) SetAutoResize(resize bool) {
   257  	t.Lock()
   258  	defer t.Unlock()
   259  
   260  	t.textResize = resize
   261  	t.resizeFont()
   262  	t.field.SetScrollBarVisible(t.scrollBarVisible())
   263  }
   264  
   265  // Padding returns the amount of padding around the text within the field.
   266  func (t *Text) Padding() int {
   267  	t.Lock()
   268  	defer t.Unlock()
   269  
   270  	return t.field.Padding()
   271  }
   272  
   273  // SetPadding sets the amount of padding around the text within the field.
   274  func (t *Text) SetPadding(padding int) {
   275  	t.Lock()
   276  	defer t.Unlock()
   277  
   278  	t.field.SetPadding(padding)
   279  }
   280  
   281  // SetFollow sets whether the field should automatically scroll to the end when
   282  // content is added to the buffer.
   283  func (t *Text) SetFollow(follow bool) {
   284  	t.Lock()
   285  	defer t.Unlock()
   286  
   287  	t.field.SetFollow(follow)
   288  }
   289  
   290  // SetSingleLine sets whether the field displays all text on a single line.
   291  // When enabled, the field scrolls horizontally. Otherwise, it scrolls vertically.
   292  func (t *Text) SetSingleLine(single bool) {
   293  	t.Lock()
   294  	defer t.Unlock()
   295  
   296  	t.field.SetSingleLine(single)
   297  }
   298  
   299  // SetMask sets the rune used to mask the text buffer contents. Set to 0 to disable.
   300  func (t *Text) SetMask(r rune) {
   301  	t.Lock()
   302  	defer t.Unlock()
   303  
   304  	t.field.SetMask(r)
   305  }
   306  
   307  // HandleKeyboard is called when a keyboard event occurs.
   308  func (t *Text) HandleKeyboard(key ebiten.Key, r rune) (handled bool, err error) {
   309  	return t.field.HandleKeyboardEvent(key, r)
   310  }
   311  
   312  // HandleMouse is called when a mouse event occurs.
   313  func (t *Text) HandleMouse(cursor image.Point, pressed bool, clicked bool) (handled bool, err error) {
   314  	return t.field.HandleMouseEvent(cursor, pressed, clicked)
   315  }
   316  
   317  // Draw draws the widget on the screen.
   318  func (t *Text) Draw(screen *ebiten.Image) error {
   319  	t.field.Draw(screen)
   320  	return nil
   321  }
   322  
   323  // Children returns the children of the widget.
   324  func (t *Text) Children() []Widget {
   325  	t.Lock()
   326  	defer t.Unlock()
   327  
   328  	return t.children
   329  }
   330  
   331  // AddChild adds a child to the widget.
   332  func (t *Text) AddChild(w ...Widget) {
   333  	t.Lock()
   334  	defer t.Unlock()
   335  
   336  	t.children = append(t.children, w...)
   337  }
   338  
   339  var _ Widget = &Text{}
   340  

View as plain text