Skip to content
This repository has been archived by the owner on Dec 31, 2022. It is now read-only.

Commit

Permalink
Merge pull request #23 from inkyblackness/InputTextSupport
Browse files Browse the repository at this point in the history
Input text support
  • Loading branch information
dertseha authored Feb 27, 2019
2 parents 1c14fb3 + 368f62e commit 48b83d9
Show file tree
Hide file tree
Showing 10 changed files with 636 additions and 21 deletions.
273 changes: 273 additions & 0 deletions InputText.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,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))
}
113 changes: 113 additions & 0 deletions InputTextCallbackDataWrapper.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#include "imguiWrappedHeader.h"
#include "InputTextCallbackDataWrapper.h"

int iggInputTextCallbackDataGetEventFlag(IggInputTextCallbackData handle)
{
ImGuiInputTextCallbackData *data = reinterpret_cast<ImGuiInputTextCallbackData *>(handle);
return data->EventFlag;
}

int iggInputTextCallbackDataGetFlags(IggInputTextCallbackData handle)
{
ImGuiInputTextCallbackData *data = reinterpret_cast<ImGuiInputTextCallbackData *>(handle);
return data->Flags;
}

unsigned short iggInputTextCallbackDataGetEventChar(IggInputTextCallbackData handle)
{
ImGuiInputTextCallbackData *data = reinterpret_cast<ImGuiInputTextCallbackData *>(handle);
return data->EventChar;
}

void iggInputTextCallbackDataSetEventChar(IggInputTextCallbackData handle, unsigned short value)
{
ImGuiInputTextCallbackData *data = reinterpret_cast<ImGuiInputTextCallbackData *>(handle);
data->EventChar = value;
}

int iggInputTextCallbackDataGetEventKey(IggInputTextCallbackData handle)
{
ImGuiInputTextCallbackData *data = reinterpret_cast<ImGuiInputTextCallbackData *>(handle);
return data->EventKey;
}

char *iggInputTextCallbackDataGetBuf(IggInputTextCallbackData handle)
{
ImGuiInputTextCallbackData *data = reinterpret_cast<ImGuiInputTextCallbackData *>(handle);
return data->Buf;
}

void iggInputTextCallbackDataSetBuf(IggInputTextCallbackData handle, char *buf, int size, int textLen)
{
ImGuiInputTextCallbackData *data = reinterpret_cast<ImGuiInputTextCallbackData *>(handle);
data->Buf = buf;
data->BufSize = size;
data->BufTextLen = textLen;
data->BufDirty = true;
}

void iggInputTextCallbackDataMarkBufferModified(IggInputTextCallbackData handle)
{
ImGuiInputTextCallbackData *data = reinterpret_cast<ImGuiInputTextCallbackData *>(handle);
data->BufDirty = true;
}

int iggInputTextCallbackDataGetBufSize(IggInputTextCallbackData handle)
{
ImGuiInputTextCallbackData *data = reinterpret_cast<ImGuiInputTextCallbackData *>(handle);
return data->BufSize;
}

int iggInputTextCallbackDataGetBufTextLen(IggInputTextCallbackData handle)
{
ImGuiInputTextCallbackData *data = reinterpret_cast<ImGuiInputTextCallbackData *>(handle);
return data->BufTextLen;
}

void iggInputTextCallbackDataDeleteBytes(IggInputTextCallbackData handle, int offset, int count)
{
ImGuiInputTextCallbackData *data = reinterpret_cast<ImGuiInputTextCallbackData *>(handle);
data->DeleteChars(offset, count);
}

void iggInputTextCallbackDataInsertBytes(IggInputTextCallbackData handle, int offset, char *bytes, int count)
{
ImGuiInputTextCallbackData *data = reinterpret_cast<ImGuiInputTextCallbackData *>(handle);
data->InsertChars(offset, bytes, bytes+count);
}

int iggInputTextCallbackDataGetCursorPos(IggInputTextCallbackData handle)
{
ImGuiInputTextCallbackData *data = reinterpret_cast<ImGuiInputTextCallbackData *>(handle);
return data->CursorPos;
}

void iggInputTextCallbackDataSetCursorPos(IggInputTextCallbackData handle, int value)
{
ImGuiInputTextCallbackData *data = reinterpret_cast<ImGuiInputTextCallbackData *>(handle);
data->CursorPos = value;
}

int iggInputTextCallbackDataGetSelectionStart(IggInputTextCallbackData handle)
{
ImGuiInputTextCallbackData *data = reinterpret_cast<ImGuiInputTextCallbackData *>(handle);
return data->SelectionStart;
}

void iggInputTextCallbackDataSetSelectionStart(IggInputTextCallbackData handle, int value)
{
ImGuiInputTextCallbackData *data = reinterpret_cast<ImGuiInputTextCallbackData *>(handle);
data->SelectionStart = value;
}

int iggInputTextCallbackDataGetSelectionEnd(IggInputTextCallbackData handle)
{
ImGuiInputTextCallbackData *data = reinterpret_cast<ImGuiInputTextCallbackData *>(handle);
return data->SelectionEnd;
}

void iggInputTextCallbackDataSetSelectionEnd(IggInputTextCallbackData handle, int value)
{
ImGuiInputTextCallbackData *data = reinterpret_cast<ImGuiInputTextCallbackData *>(handle);
data->SelectionEnd = value;
}
Loading

0 comments on commit 48b83d9

Please sign in to comment.