InputText.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. package imgui
  2. // #include "InputTextCallbackDataWrapper.h"
  3. import "C"
  4. import (
  5. "sync"
  6. "unsafe"
  7. )
  8. const (
  9. // InputTextFlagsNone sets everything default.
  10. InputTextFlagsNone = 0
  11. // InputTextFlagsCharsDecimal allows 0123456789.+-
  12. InputTextFlagsCharsDecimal = 1 << 0
  13. // InputTextFlagsCharsHexadecimal allow 0123456789ABCDEFabcdef
  14. InputTextFlagsCharsHexadecimal = 1 << 1
  15. // InputTextFlagsCharsUppercase turns a..z into A..Z.
  16. InputTextFlagsCharsUppercase = 1 << 2
  17. // InputTextFlagsCharsNoBlank filters out spaces, tabs.
  18. InputTextFlagsCharsNoBlank = 1 << 3
  19. // InputTextFlagsAutoSelectAll selects entire text when first taking mouse focus.
  20. InputTextFlagsAutoSelectAll = 1 << 4
  21. // InputTextFlagsEnterReturnsTrue returns 'true' when Enter is pressed (as opposed to when the value was modified).
  22. InputTextFlagsEnterReturnsTrue = 1 << 5
  23. // InputTextFlagsCallbackCompletion for callback on pressing TAB (for completion handling).
  24. InputTextFlagsCallbackCompletion = 1 << 6
  25. // InputTextFlagsCallbackHistory for callback on pressing Up/Down arrows (for history handling).
  26. InputTextFlagsCallbackHistory = 1 << 7
  27. // InputTextFlagsCallbackAlways for callback on each iteration. User code may query cursor position, modify text buffer.
  28. InputTextFlagsCallbackAlways = 1 << 8
  29. // InputTextFlagsCallbackCharFilter for callback on character inputs to replace or discard them.
  30. // Modify 'EventChar' to replace or discard, or return 1 in callback to discard.
  31. InputTextFlagsCallbackCharFilter = 1 << 9
  32. // InputTextFlagsAllowTabInput when pressing TAB to input a '\t' character into the text field.
  33. InputTextFlagsAllowTabInput = 1 << 10
  34. // InputTextFlagsCtrlEnterForNewLine in multi-line mode, unfocus with Enter, add new line with Ctrl+Enter
  35. // (default is opposite: unfocus with Ctrl+Enter, add line with Enter).
  36. InputTextFlagsCtrlEnterForNewLine = 1 << 11
  37. // InputTextFlagsNoHorizontalScroll disables following the cursor horizontally.
  38. InputTextFlagsNoHorizontalScroll = 1 << 12
  39. // InputTextFlagsAlwaysInsertMode sets insert mode.
  40. InputTextFlagsAlwaysInsertMode = 1 << 13
  41. // InputTextFlagsReadOnly sets read-only mode.
  42. InputTextFlagsReadOnly = 1 << 14
  43. // InputTextFlagsPassword sets password mode, display all characters as '*'.
  44. InputTextFlagsPassword = 1 << 15
  45. // InputTextFlagsNoUndoRedo disables undo/redo. Note that input text owns the text data while active,
  46. // if you want to provide your own undo/redo stack you need e.g. to call ClearActiveID().
  47. InputTextFlagsNoUndoRedo = 1 << 16
  48. // InputTextFlagsCharsScientific allows 0123456789.+-*/eE (Scientific notation input).
  49. InputTextFlagsCharsScientific = 1 << 17
  50. // inputTextFlagsCallbackResize for callback on buffer capacity change requests.
  51. inputTextFlagsCallbackResize = 1 << 18
  52. )
  53. // InputTextCallback is called for sharing state of an input field.
  54. // By default, the callback should return 0.
  55. type InputTextCallback func(InputTextCallbackData) int32
  56. type inputTextState struct {
  57. buf *stringBuffer
  58. key C.int
  59. callback InputTextCallback
  60. }
  61. var inputTextStates = make(map[C.int]*inputTextState)
  62. var inputTextStatesMutex sync.Mutex
  63. func newInputTextState(text string, cb InputTextCallback) *inputTextState {
  64. state := &inputTextState{}
  65. state.buf = newStringBuffer(text)
  66. state.callback = cb
  67. state.register()
  68. return state
  69. }
  70. func (state *inputTextState) register() {
  71. inputTextStatesMutex.Lock()
  72. defer inputTextStatesMutex.Unlock()
  73. key := C.int(len(inputTextStates) + 1)
  74. for _, existing := inputTextStates[key]; existing; _, existing = inputTextStates[key] {
  75. key++
  76. }
  77. state.key = key
  78. inputTextStates[key] = state
  79. }
  80. func (state *inputTextState) release() {
  81. state.buf.free()
  82. if state.key != 0 {
  83. inputTextStatesMutex.Lock()
  84. defer inputTextStatesMutex.Unlock()
  85. delete(inputTextStates, state.key)
  86. }
  87. }
  88. func (state *inputTextState) onCallback(handle C.IggInputTextCallbackData) C.int {
  89. data := InputTextCallbackData{state: state, handle: handle}
  90. if data.EventFlag() == inputTextFlagsCallbackResize {
  91. state.buf.resizeTo(data.bufSize())
  92. data.setBuf(state.buf.ptr, state.buf.size, data.bufTextLen())
  93. return 0
  94. }
  95. if state.callback == nil {
  96. return 0
  97. }
  98. return C.int(state.callback(data))
  99. }
  100. //export iggInputTextCallback
  101. func iggInputTextCallback(handle C.IggInputTextCallbackData, key C.int) C.int {
  102. state := iggInputTextStateFor(key)
  103. return state.onCallback(handle)
  104. }
  105. func iggInputTextStateFor(key C.int) *inputTextState {
  106. inputTextStatesMutex.Lock()
  107. defer inputTextStatesMutex.Unlock()
  108. return inputTextStates[key]
  109. }
  110. // InputTextCallbackData represents the shared state of InputText(), passed as an argument to your callback.
  111. type InputTextCallbackData struct {
  112. state *inputTextState
  113. handle C.IggInputTextCallbackData
  114. }
  115. // EventFlag returns one of the InputTextFlagsCallback* constants to indicate the nature of the callback.
  116. func (data InputTextCallbackData) EventFlag() int {
  117. return int(C.iggInputTextCallbackDataGetEventFlag(data.handle))
  118. }
  119. // Flags returns the set of flags that the user originally passed to InputText.
  120. func (data InputTextCallbackData) Flags() int {
  121. return int(C.iggInputTextCallbackDataGetFlags(data.handle)) & ^inputTextFlagsCallbackResize
  122. }
  123. // EventChar returns the current character input. Only valid during CharFilter callback.
  124. func (data InputTextCallbackData) EventChar() rune {
  125. return rune(C.iggInputTextCallbackDataGetEventChar(data.handle))
  126. }
  127. // SetEventChar overrides what the user entered. Set to zero do drop the current input.
  128. // Returning 1 from the callback also drops the current input.
  129. // Only valid during CharFilter callback.
  130. //
  131. // Note: The internal representation of characters is based on uint16, so less than rune would provide.
  132. func (data InputTextCallbackData) SetEventChar(value rune) {
  133. C.iggInputTextCallbackDataSetEventChar(data.handle, C.ushort(value))
  134. }
  135. // EventKey returns the currently pressed key. Valid for completion and history callbacks.
  136. func (data InputTextCallbackData) EventKey() int {
  137. return int(C.iggInputTextCallbackDataGetEventKey(data.handle))
  138. }
  139. // Buffer returns a view into the current UTF-8 buffer.
  140. // Only during the callbacks of [Completion,History,Always] the current buffer is returned.
  141. // The returned slice is a temporary view into the underlying raw buffer. Do not keep it!
  142. // The underlying memory allocation may even change through a call to InsertBytes().
  143. //
  144. // You may change the buffer through the following ways:
  145. // If the new text has a different (encoded) length, use the functions InsertBytes() and/or DeleteBytes().
  146. // Otherwise you may keep the buffer as is and modify the bytes. If you change the buffer this way directly, mark the buffer
  147. // as modified with MarkBufferModified().
  148. func (data InputTextCallbackData) Buffer() []byte {
  149. ptr := C.iggInputTextCallbackDataGetBuf(data.handle)
  150. if ptr == nil {
  151. return nil
  152. }
  153. textLen := data.bufTextLen()
  154. return ((*[1 << 30]byte)(unsafe.Pointer(ptr)))[:textLen]
  155. }
  156. // MarkBufferModified indicates that the content of the buffer was modified during a callback.
  157. // Only considered during [Completion,History,Always] callbacks.
  158. func (data InputTextCallbackData) MarkBufferModified() {
  159. C.iggInputTextCallbackDataMarkBufferModified(data.handle)
  160. }
  161. func (data InputTextCallbackData) setBuf(buf unsafe.Pointer, size, textLen int) {
  162. C.iggInputTextCallbackDataSetBuf(data.handle, (*C.char)(buf), C.int(size), C.int(textLen))
  163. }
  164. func (data InputTextCallbackData) bufSize() int {
  165. return int(C.iggInputTextCallbackDataGetBufSize(data.handle))
  166. }
  167. func (data InputTextCallbackData) bufTextLen() int {
  168. return int(C.iggInputTextCallbackDataGetBufTextLen(data.handle))
  169. }
  170. // DeleteBytes removes the given count of bytes starting at the specified byte offset within the buffer.
  171. // This function can be called during the [Completion,History,Always] callbacks.
  172. // Clears the current selection.
  173. //
  174. // This function ignores the deletion beyond the current buffer length.
  175. // Calling with negative offset or count arguments will panic.
  176. func (data InputTextCallbackData) DeleteBytes(offset, count int) {
  177. if offset < 0 {
  178. panic("invalid offset")
  179. }
  180. if count < 0 {
  181. panic("invalid count")
  182. }
  183. textLen := data.bufTextLen()
  184. if offset >= textLen {
  185. return
  186. }
  187. toRemove := count
  188. available := textLen - offset
  189. if toRemove > available {
  190. toRemove = available
  191. }
  192. C.iggInputTextCallbackDataDeleteBytes(data.handle, C.int(offset), C.int(toRemove))
  193. }
  194. // InsertBytes inserts the given bytes at given byte offset into the buffer.
  195. // Calling this function may change the underlying buffer allocation.
  196. //
  197. // This function can be called during the [Completion,History,Always] callbacks.
  198. // Clears the current selection.
  199. //
  200. // Calling with an offset outside of the range of the buffer will panic.
  201. func (data InputTextCallbackData) InsertBytes(offset int, bytes []byte) {
  202. if (offset < 0) || (offset > data.bufTextLen()) {
  203. panic("invalid offset")
  204. }
  205. var bytesPtr *C.char
  206. byteCount := len(bytes)
  207. if byteCount > 0 {
  208. bytesPtr = (*C.char)(unsafe.Pointer(&bytes[0]))
  209. C.iggInputTextCallbackDataInsertBytes(data.handle, C.int(offset), bytesPtr, C.int(byteCount))
  210. }
  211. }
  212. // CursorPos returns the byte-offset of the cursor within the buffer.
  213. // Only valid during [Completion,History,Always] callbacks.
  214. func (data InputTextCallbackData) CursorPos() int {
  215. return int(C.iggInputTextCallbackDataGetCursorPos(data.handle))
  216. }
  217. // SetCursorPos changes the current byte-offset of the cursor within the buffer.
  218. // Only valid during [Completion,History,Always] callbacks.
  219. func (data InputTextCallbackData) SetCursorPos(value int) {
  220. C.iggInputTextCallbackDataSetCursorPos(data.handle, C.int(value))
  221. }
  222. // SelectionStart returns the byte-offset of the selection start within the buffer.
  223. // Only valid during [Completion,History,Always] callbacks.
  224. func (data InputTextCallbackData) SelectionStart() int {
  225. return int(C.iggInputTextCallbackDataGetSelectionStart(data.handle))
  226. }
  227. // SetSelectionStart changes the current byte-offset of the selection start within the buffer.
  228. // Only valid during [Completion,History,Always] callbacks.
  229. func (data InputTextCallbackData) SetSelectionStart(value int) {
  230. C.iggInputTextCallbackDataSetSelectionStart(data.handle, C.int(value))
  231. }
  232. // SelectionEnd returns the byte-offset of the selection end within the buffer.
  233. // Only valid during [Completion,History,Always] callbacks.
  234. func (data InputTextCallbackData) SelectionEnd() int {
  235. return int(C.iggInputTextCallbackDataGetSelectionEnd(data.handle))
  236. }
  237. // SetSelectionEnd changes the current byte-offset of the selection end within the buffer.
  238. // Only valid during [Completion,History,Always] callbacks.
  239. func (data InputTextCallbackData) SetSelectionEnd(value int) {
  240. C.iggInputTextCallbackDataSetSelectionEnd(data.handle, C.int(value))
  241. }