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