...

Source file src/codeberg.org/tslocum/etk/input.go

Documentation: codeberg.org/tslocum/etk

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

View as plain text