...

Source file src/code.rocket9labs.com/tslocum/etk/messeji/inputfield.go

Documentation: code.rocket9labs.com/tslocum/etk/messeji

     1  package messeji
     2  
     3  import (
     4  	"sync"
     5  
     6  	"github.com/hajimehoshi/ebiten/v2"
     7  	"github.com/hajimehoshi/ebiten/v2/inpututil"
     8  	"golang.org/x/image/font"
     9  )
    10  
    11  // InputField is a text input field. Call Update and Draw when your Game's
    12  // Update and Draw methods are called.
    13  //
    14  // Note: A position and size must be set via SetRect before the field will appear.
    15  // Keyboard events are not handled by default, and may be enabled via SetHandleKeyboard.
    16  type InputField struct {
    17  	*TextField
    18  
    19  	// changedFunc is a function which is called when the text buffer is changed.
    20  	// The function may return false to skip adding the rune to the text buffer.
    21  	changedFunc func(r rune) (accept bool)
    22  
    23  	// selectedFunc is a function which is called when the enter key is pressed. The
    24  	// function may return true to clear the text buffer.
    25  	selectedFunc func() (accept bool)
    26  
    27  	// readBuffer is where incoming runes are stored before being added to the input buffer.
    28  	readBuffer []rune
    29  
    30  	// keyBuffer is where incoming keys are stored before being added to the input buffer.
    31  	keyBuffer []ebiten.Key
    32  
    33  	// rawRuneBuffer is where incoming raw runes are stored before being added to the input buffer.
    34  	rawRuneBuffer []rune
    35  
    36  	// rawKeyBuffer is where incoming raw keys are stored before being added to the input buffer.
    37  	rawKeyBuffer []ebiten.Key
    38  
    39  	sync.Mutex
    40  }
    41  
    42  // NewInputField returns a new InputField. See type documentation for more info.
    43  func NewInputField(face font.Face, faceMutex *sync.Mutex) *InputField {
    44  	f := &InputField{
    45  		TextField: NewTextField(face, faceMutex),
    46  	}
    47  	f.TextField.suffix = "_"
    48  	return f
    49  }
    50  
    51  // SetHandleKeyboard sets a flag controlling whether keyboard input should be handled
    52  // by the field. This can be used to facilitate focus changes between multiple inputs.
    53  func (f *InputField) SetHandleKeyboard(handle bool) {
    54  	f.Lock()
    55  	defer f.Unlock()
    56  
    57  	f.handleKeyboard = handle
    58  }
    59  
    60  // SetChangedFunc sets a handler which is called when the text buffer is changed.
    61  // The handler may return true to add the rune to the text buffer.
    62  func (f *InputField) SetChangedFunc(changedFunc func(r rune) (accept bool)) {
    63  	f.changedFunc = changedFunc
    64  }
    65  
    66  // SetSelectedFunc sets a handler which is called when the enter key is pressed.
    67  // Providing a nil function value will remove the existing handler (if set).
    68  // The handler may return true to clear the text buffer.
    69  func (f *InputField) SetSelectedFunc(selectedFunc func() (accept bool)) {
    70  	f.selectedFunc = selectedFunc
    71  }
    72  
    73  // HandleKeyboardEvent passes the provided key or rune to the Inputfield.
    74  func (f *InputField) HandleKeyboardEvent(key ebiten.Key, r rune) (handled bool, err error) {
    75  	f.Lock()
    76  	defer f.Unlock()
    77  
    78  	if !f.visible || rectIsZero(f.r) {
    79  		return
    80  	}
    81  
    82  	if !f.handleKeyboard {
    83  		return
    84  	}
    85  
    86  	// Handle rune event.
    87  	if r > 0 {
    88  		f.handleRunes([]rune{r})
    89  		return true, nil
    90  	}
    91  
    92  	// Handle key event.
    93  	f.handleKeys([]ebiten.Key{key})
    94  	return true, nil
    95  }
    96  
    97  func (f *InputField) handleRunes(runes []rune) bool {
    98  	var redraw bool
    99  	for _, r := range runes {
   100  		if f.changedFunc != nil {
   101  			f.Unlock()
   102  			accept := f.changedFunc(r)
   103  			f.Lock()
   104  
   105  			if !accept {
   106  				continue
   107  			}
   108  		}
   109  
   110  		f.TextField._write([]byte(string(r)))
   111  		redraw = true
   112  	}
   113  
   114  	return redraw
   115  }
   116  
   117  func (f *InputField) handleKeys(keys []ebiten.Key) bool {
   118  	var redraw bool
   119  	for _, key := range keys {
   120  		switch key {
   121  		case ebiten.KeyBackspace:
   122  			l := len(f.buffer)
   123  			if l > 0 {
   124  				var rewrap bool
   125  				if len(f.incoming) != 0 {
   126  					line := string(f.incoming)
   127  					f.incoming = append(f.incoming, []byte(line[:len(line)-1])...)
   128  				} else if len(f.buffer[l-1]) == 0 {
   129  					f.buffer = f.buffer[:l-1]
   130  					rewrap = true
   131  				} else {
   132  					line := string(f.buffer[l-1])
   133  					f.buffer[l-1] = []byte(line[:len(line)-1])
   134  					rewrap = true
   135  				}
   136  				if rewrap && (f.needWrap == -1 || f.needWrap > l-1) {
   137  					f.needWrap = l - 1
   138  				}
   139  				redraw = true
   140  				f.modified = true
   141  				f.redraw = true
   142  			}
   143  		case ebiten.KeyEnter, ebiten.KeyKPEnter:
   144  			if f.selectedFunc != nil {
   145  				f.Unlock()
   146  				accept := f.selectedFunc()
   147  				f.Lock()
   148  
   149  				// Clear input buffer.
   150  				if accept {
   151  					f.incoming = f.incoming[:0]
   152  					f.buffer = f.buffer[:0]
   153  					f.bufferWrapped = f.bufferWrapped[:0]
   154  					f.lineWidths = f.lineWidths[:0]
   155  					f.needWrap = 0
   156  					f.wrapStart = 0
   157  					f.modified = true
   158  					f.redraw = true
   159  					redraw = true
   160  				}
   161  			} else if !f.singleLine {
   162  				// Append newline.
   163  				f.incoming = append(f.incoming, '\n')
   164  				f.modified = true
   165  				f.redraw = true
   166  				redraw = true
   167  			}
   168  		}
   169  	}
   170  	return redraw
   171  }
   172  
   173  // Update updates the input field. This function should be called when
   174  // Game.Update is called.
   175  func (f *InputField) Update() error {
   176  	f.Lock()
   177  	defer f.Unlock()
   178  
   179  	if !f.visible || rectIsZero(f.r) {
   180  		return nil
   181  	}
   182  
   183  	if !f.handleKeyboard {
   184  		return f.TextField.Update()
   185  	}
   186  
   187  	var redraw bool
   188  
   189  	// Handler rune input.
   190  	f.readBuffer = ebiten.AppendInputChars(f.readBuffer[:0])
   191  	if f.handleRunes(f.readBuffer) {
   192  		redraw = true
   193  	}
   194  	if f.handleRunes(f.rawRuneBuffer) {
   195  		redraw = true
   196  	}
   197  	f.rawRuneBuffer = f.rawRuneBuffer[:0]
   198  
   199  	// Handle key input.
   200  	f.keyBuffer = inpututil.AppendJustPressedKeys(f.keyBuffer[:0])
   201  	if f.handleKeys(f.keyBuffer) {
   202  		redraw = true
   203  	}
   204  	if f.handleKeys(f.rawKeyBuffer) {
   205  		redraw = true
   206  	}
   207  	f.rawKeyBuffer = f.rawKeyBuffer[:0]
   208  
   209  	if redraw {
   210  		f.bufferModified()
   211  	}
   212  
   213  	return f.TextField.Update()
   214  }
   215  

View as plain text