...

Source file src/codeberg.org/tslocum/etk/messeji/inputfield.go

Documentation: codeberg.org/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.SetFollow(true)
    48  	f.TextField.suffix = "_"
    49  	return f
    50  }
    51  
    52  // SetHandleKeyboard sets a flag controlling whether keyboard input should be handled
    53  // by the field. This can be used to facilitate focus changes between multiple inputs.
    54  func (f *InputField) SetHandleKeyboard(handle bool) {
    55  	f.Lock()
    56  	defer f.Unlock()
    57  
    58  	f.handleKeyboard = handle
    59  }
    60  
    61  // SetChangedFunc sets a handler which is called when the text buffer is changed.
    62  // The handler may return true to add the rune to the text buffer.
    63  func (f *InputField) SetChangedFunc(changedFunc func(r rune) (accept bool)) {
    64  	f.changedFunc = changedFunc
    65  }
    66  
    67  // SetSelectedFunc sets a handler which is called when the enter key is pressed.
    68  // Providing a nil function value will remove the existing handler (if set).
    69  // The handler may return true to clear the text buffer.
    70  func (f *InputField) SetSelectedFunc(selectedFunc func() (accept bool)) {
    71  	f.selectedFunc = selectedFunc
    72  }
    73  
    74  // HandleKeyboardEvent passes the provided key or rune to the Inputfield.
    75  func (f *InputField) HandleKeyboardEvent(key ebiten.Key, r rune) (handled bool, err error) {
    76  	f.Lock()
    77  	defer f.Unlock()
    78  
    79  	if !f.visible || rectIsZero(f.r) {
    80  		return
    81  	}
    82  
    83  	if !f.handleKeyboard {
    84  		return
    85  	}
    86  
    87  	// Handle rune event.
    88  	if r > 0 {
    89  		ok := f.handleRunes([]rune{r})
    90  		if ok {
    91  			f.resizeFont()
    92  		}
    93  		return true, nil
    94  	}
    95  
    96  	// Handle key event.
    97  	ok := f.handleKeys([]ebiten.Key{key})
    98  	if ok {
    99  		f.resizeFont()
   100  	}
   101  	return true, nil
   102  }
   103  
   104  func (f *InputField) handleRunes(runes []rune) bool {
   105  	var redraw bool
   106  	for _, r := range runes {
   107  		if f.changedFunc != nil {
   108  			f.Unlock()
   109  			accept := f.changedFunc(r)
   110  			f.Lock()
   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  				if f.changedFunc != nil {
   131  					f.Unlock()
   132  					accept := f.changedFunc(0)
   133  					f.Lock()
   134  					if !accept {
   135  						continue
   136  					}
   137  				}
   138  
   139  				var rewrap bool
   140  				if len(f.incoming) != 0 {
   141  					line := string(f.incoming)
   142  					f.incoming = append(f.incoming, []byte(line[:len(line)-1])...)
   143  				} else if len(f.buffer[l-1]) == 0 {
   144  					f.buffer = f.buffer[:l-1]
   145  					rewrap = true
   146  				} else {
   147  					line := string(f.buffer[l-1])
   148  					f.buffer[l-1] = []byte(line[:len(line)-1])
   149  					rewrap = true
   150  				}
   151  				if rewrap && (f.needWrap == -1 || f.needWrap > l-1) {
   152  					f.needWrap = l - 1
   153  				}
   154  				redraw = true
   155  				f.modified = true
   156  				f.redraw = true
   157  			}
   158  		case ebiten.KeyEnter, ebiten.KeyKPEnter:
   159  			if f.selectedFunc != nil {
   160  				f.Unlock()
   161  				accept := f.selectedFunc()
   162  				f.Lock()
   163  
   164  				// Clear input buffer.
   165  				if accept {
   166  					f.incoming = f.incoming[:0]
   167  					f.buffer = f.buffer[:0]
   168  					f.bufferWrapped = f.bufferWrapped[:0]
   169  					f.lineWidths = f.lineWidths[:0]
   170  					f.needWrap = 0
   171  					f.wrapStart = 0
   172  					f.modified = true
   173  					f.redraw = true
   174  					redraw = true
   175  				}
   176  			} else if !f.singleLine {
   177  				// Append newline.
   178  				f.incoming = append(f.incoming, '\n')
   179  				f.modified = true
   180  				f.redraw = true
   181  				redraw = true
   182  			}
   183  		}
   184  	}
   185  	return redraw
   186  }
   187  
   188  // Update updates the input field. This function should be called when
   189  // Game.Update is called.
   190  func (f *InputField) Update() error {
   191  	f.Lock()
   192  	defer f.Unlock()
   193  
   194  	if !f.visible || rectIsZero(f.r) {
   195  		return nil
   196  	}
   197  
   198  	if !f.handleKeyboard {
   199  		return f.TextField.Update()
   200  	}
   201  
   202  	var redraw bool
   203  
   204  	// Handler rune input.
   205  	f.readBuffer = ebiten.AppendInputChars(f.readBuffer[:0])
   206  	if f.handleRunes(f.readBuffer) {
   207  		redraw = true
   208  	}
   209  	if f.handleRunes(f.rawRuneBuffer) {
   210  		redraw = true
   211  	}
   212  	f.rawRuneBuffer = f.rawRuneBuffer[:0]
   213  
   214  	// Handle key input.
   215  	f.keyBuffer = inpututil.AppendJustPressedKeys(f.keyBuffer[:0])
   216  	if f.handleKeys(f.keyBuffer) {
   217  		redraw = true
   218  	}
   219  	if f.handleKeys(f.rawKeyBuffer) {
   220  		redraw = true
   221  	}
   222  	f.rawKeyBuffer = f.rawKeyBuffer[:0]
   223  
   224  	if redraw {
   225  		f.resizeFont()
   226  		f.bufferModified()
   227  	}
   228  
   229  	return f.TextField.Update()
   230  }
   231  

View as plain text