...

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  	"github.com/hajimehoshi/ebiten/v2/text/v2"
     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(fontSource *text.GoTextFaceSource, fontSize int, fontMutex *sync.Mutex) *InputField {
    44  	f := &InputField{
    45  		TextField: NewTextField(fontSource, fontSize, fontMutex),
    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  		ok := f.handleRunes([]rune{r})
    89  		if ok {
    90  			f.resizeFont()
    91  		}
    92  		return true, nil
    93  	}
    94  
    95  	// Handle key event.
    96  	ok := f.handleKeys([]ebiten.Key{key})
    97  	if ok {
    98  		f.resizeFont()
    99  	}
   100  	return true, nil
   101  }
   102  
   103  func (f *InputField) handleRunes(runes []rune) bool {
   104  	var redraw bool
   105  	for _, r := range runes {
   106  		if f.changedFunc != nil {
   107  			f.Unlock()
   108  			accept := f.changedFunc(r)
   109  			f.Lock()
   110  
   111  			if !accept {
   112  				continue
   113  			}
   114  		}
   115  
   116  		f.TextField._write([]byte(string(r)))
   117  		redraw = true
   118  	}
   119  
   120  	return redraw
   121  }
   122  
   123  func (f *InputField) handleKeys(keys []ebiten.Key) bool {
   124  	var redraw bool
   125  	for _, key := range keys {
   126  		switch key {
   127  		case ebiten.KeyBackspace:
   128  			l := len(f.buffer)
   129  			if l > 0 {
   130  				var rewrap bool
   131  				if len(f.incoming) != 0 {
   132  					line := string(f.incoming)
   133  					f.incoming = append(f.incoming, []byte(line[:len(line)-1])...)
   134  				} else if len(f.buffer[l-1]) == 0 {
   135  					f.buffer = f.buffer[:l-1]
   136  					rewrap = true
   137  				} else {
   138  					line := string(f.buffer[l-1])
   139  					f.buffer[l-1] = []byte(line[:len(line)-1])
   140  					rewrap = true
   141  				}
   142  				if rewrap && (f.needWrap == -1 || f.needWrap > l-1) {
   143  					f.needWrap = l - 1
   144  				}
   145  				redraw = true
   146  				f.modified = true
   147  				f.redraw = true
   148  			}
   149  		case ebiten.KeyEnter, ebiten.KeyKPEnter:
   150  			if f.selectedFunc != nil {
   151  				f.Unlock()
   152  				accept := f.selectedFunc()
   153  				f.Lock()
   154  
   155  				// Clear input buffer.
   156  				if accept {
   157  					f.incoming = f.incoming[:0]
   158  					f.buffer = f.buffer[:0]
   159  					f.bufferWrapped = f.bufferWrapped[:0]
   160  					f.lineWidths = f.lineWidths[:0]
   161  					f.needWrap = 0
   162  					f.wrapStart = 0
   163  					f.modified = true
   164  					f.redraw = true
   165  					redraw = true
   166  				}
   167  			} else if !f.singleLine {
   168  				// Append newline.
   169  				f.incoming = append(f.incoming, '\n')
   170  				f.modified = true
   171  				f.redraw = true
   172  				redraw = true
   173  			}
   174  		}
   175  	}
   176  	return redraw
   177  }
   178  
   179  // Update updates the input field. This function should be called when
   180  // Game.Update is called.
   181  func (f *InputField) Update() error {
   182  	f.Lock()
   183  	defer f.Unlock()
   184  
   185  	if !f.visible || rectIsZero(f.r) {
   186  		return nil
   187  	}
   188  
   189  	if !f.handleKeyboard {
   190  		return f.TextField.Update()
   191  	}
   192  
   193  	var redraw bool
   194  
   195  	// Handler rune input.
   196  	f.readBuffer = ebiten.AppendInputChars(f.readBuffer[:0])
   197  	if f.handleRunes(f.readBuffer) {
   198  		redraw = true
   199  	}
   200  	if f.handleRunes(f.rawRuneBuffer) {
   201  		redraw = true
   202  	}
   203  	f.rawRuneBuffer = f.rawRuneBuffer[:0]
   204  
   205  	// Handle key input.
   206  	f.keyBuffer = inpututil.AppendJustPressedKeys(f.keyBuffer[:0])
   207  	if f.handleKeys(f.keyBuffer) {
   208  		redraw = true
   209  	}
   210  	if f.handleKeys(f.rawKeyBuffer) {
   211  		redraw = true
   212  	}
   213  	f.rawKeyBuffer = f.rawKeyBuffer[:0]
   214  
   215  	if redraw {
   216  		f.resizeFont()
   217  		f.bufferModified()
   218  	}
   219  
   220  	return f.TextField.Update()
   221  }
   222  

View as plain text