Skip to content

Commit

Permalink
Added SDL_StartTextInputWithProperties()
Browse files Browse the repository at this point in the history
This allows you to customize the text input so you can have numeric text entry, hidden passwords, etc.

Fixes libsdl-org#7101
Fixes libsdl-org#7965
Fixes libsdl-org#9439
  • Loading branch information
slouken committed Aug 2, 2024
1 parent 78bb1f2 commit 451d54e
Show file tree
Hide file tree
Showing 34 changed files with 408 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1375,9 +1375,11 @@ static class ShowTextInputTask implements Runnable {
*/
static final int HEIGHT_PADDING = 15;

public int input_type;
public int x, y, w, h;

public ShowTextInputTask(int x, int y, int w, int h) {
public ShowTextInputTask(int input_type, int x, int y, int w, int h) {
this.input_type = input_type;
this.x = x;
this.y = y;
this.w = w;
Expand Down Expand Up @@ -1405,6 +1407,7 @@ public void run() {
} else {
mTextEdit.setLayoutParams(params);
}
mTextEdit.setInputType(input_type);

mTextEdit.setVisibility(View.VISIBLE);
mTextEdit.requestFocus();
Expand All @@ -1419,9 +1422,9 @@ public void run() {
/**
* This method is called by SDL using JNI.
*/
public static boolean showTextInput(int x, int y, int w, int h) {
public static boolean showTextInput(int input_type, int x, int y, int w, int h) {
// Transfer the task to the main thread as a Runnable
return mSingleton.commandHandler.post(new ShowTextInputTask(x, y, w, h));
return mSingleton.commandHandler.post(new ShowTextInputTask(input_type, x, y, w, h));
}

public static boolean isTextInputEvent(KeyEvent event) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
public class SDLDummyEdit extends View implements View.OnKeyListener
{
InputConnection ic;
int input_type;

public SDLDummyEdit(Context context) {
super(context);
Expand All @@ -20,6 +21,10 @@ public SDLDummyEdit(Context context) {
setOnKeyListener(this);
}

public void setInputType(int input_type) {
this.input_type = input_type;
}

@Override
public boolean onCheckIsTextEditor() {
return true;
Expand Down Expand Up @@ -51,8 +56,7 @@ public boolean onKeyPreIme (int keyCode, KeyEvent event) {
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
ic = new SDLInputConnection(this, true);

outAttrs.inputType = InputType.TYPE_CLASS_TEXT |
InputType.TYPE_TEXT_FLAG_MULTI_LINE;
outAttrs.inputType = input_type;
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI |
EditorInfo.IME_FLAG_NO_FULLSCREEN /* API 11 */;

Expand Down
2 changes: 2 additions & 0 deletions include/SDL3/SDL_hints.h
Original file line number Diff line number Diff line change
Expand Up @@ -2620,6 +2620,8 @@ extern "C" {
* A variable to control whether the return key on the soft keyboard should
* hide the soft keyboard on Android and iOS.
*
* This hint sets the default value of SDL_PROP_TEXTINPUT_MULTILINE_BOOLEAN.
*
* The variable can be set to the following values:
*
* - "0": The return key will be handled as a key event. (default)
Expand Down
61 changes: 61 additions & 0 deletions include/SDL3/SDL_keyboard.h
Original file line number Diff line number Diff line change
Expand Up @@ -367,11 +367,72 @@ extern SDL_DECLSPEC SDL_Keycode SDLCALL SDL_GetKeyFromName(const char *name);
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_SetTextInputArea
* \sa SDL_StartTextInputWithProperties
* \sa SDL_StopTextInput
* \sa SDL_TextInputActive
*/
extern SDL_DECLSPEC int SDLCALL SDL_StartTextInput(SDL_Window *window);

/**
* Start accepting Unicode text input events in a window, with properties describing the input.
*
* This function will enable text input (SDL_EVENT_TEXT_INPUT and
* SDL_EVENT_TEXT_EDITING events) in the specified window. Please use this
* function paired with SDL_StopTextInput().
*
* Text input events are not received by default.
*
* On some platforms using this function shows the screen keyboard.
*
* These are the supported properties:
*
* - `SDL_PROP_TEXTINPUT_AUTOCAPITALIZE_CHARACTERS_BOOLEAN` - true if all characters should be capitalized.
* - `SDL_PROP_TEXTINPUT_AUTOCAPITALIZE_SENTENCES_BOOLEAN` - true if sentences should be capitalized.
* - `SDL_PROP_TEXTINPUT_AUTOCAPITALIZE_WORDS_BOOLEAN` - true if words should be capitalized.
* - `SDL_PROP_TEXTINPUT_AUTOCORRECT_BOOLEAN` - true to enable automatic correction of typos.
* - `SDL_PROP_TEXTINPUT_AUTOCOMPLETE_BOOLEAN` - true to enable autocomplete suggestions.
* - `SDL_PROP_TEXTINPUT_MULTILINE_BOOLEAN` - true if multiple lines of text are allowed. This defaults to true if SDL_HINT_RETURN_KEY_HIDES_IME is "0" or is not set, and defaults to false if SDL_HINT_RETURN_KEY_HIDES_IME is "1".
* - `SDL_PROP_TEXTINPUT_SPELLCHECK_BOOLEAN` - true to enable spell checking.
* - `SDL_PROP_TEXTINPUT_TYPE_EMAIL_BOOLEAN` - true if the input is an e-mail address.
* - `SDL_PROP_TEXTINPUT_TYPE_NAME_BOOLEAN` - true if the input is a person's name.
* - `SDL_PROP_TEXTINPUT_TYPE_NUMBER_BOOLEAN` - true if the input is a number.
* - `SDL_PROP_TEXTINPUT_TYPE_PASSWORD_BOOLEAN` - true if the input is a password. This can be combined with `SDL_PROP_TEXTINPUT_TYPE_NUMBER_BOOLEAN` to indicate a secure PIN entry.
* - `SDL_PROP_TEXTINPUT_TYPE_USERNAME_BOOLEAN` - true if the input is a username.
* - `SDL_PROP_TEXTINPUT_TYPE_VISIBLE_PASSWORD_BOOLEAN` - true if the input is a password that should be visible to the user.
*
* On Android you can directly specify the input type:
*
* - `SDL_PROP_TEXTINPUT_ANDROID_INPUTTYPE_NUMBER` - the text input type to use, overriding other properties. This is documented at https://developer.android.com/reference/android/text/InputType
*
* \param window the window to enable text input.
* \param props the properties to use.
* \returns 0 on success or a negative error code on failure; call
* SDL_GetError() for more information.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_SetTextInputArea
* \sa SDL_StartTextInput
* \sa SDL_StopTextInput
* \sa SDL_TextInputActive
*/
extern SDL_DECLSPEC int SDLCALL SDL_StartTextInputWithProperties(SDL_Window *window, SDL_PropertiesID props);

#define SDL_PROP_TEXTINPUT_AUTOCAPITALIZE_CHARACTERS_BOOLEAN "SDL.textinput.autocapitalize.characters"
#define SDL_PROP_TEXTINPUT_AUTOCAPITALIZE_SENTENCES_BOOLEAN "SDL.textinput.autocapitalize.sentences"
#define SDL_PROP_TEXTINPUT_AUTOCAPITALIZE_WORDS_BOOLEAN "SDL.textinput.autocapitalize.words"
#define SDL_PROP_TEXTINPUT_AUTOCOMPLETE_BOOLEAN "SDL.textinput.autocomplete"
#define SDL_PROP_TEXTINPUT_AUTOCORRECT_BOOLEAN "SDL.textinput.autocorrect"
#define SDL_PROP_TEXTINPUT_MULTILINE_BOOLEAN "SDL.textinput.multiline"
#define SDL_PROP_TEXTINPUT_SPELLCHECK_BOOLEAN "SDL.textinput.spellcheck"
#define SDL_PROP_TEXTINPUT_TYPE_EMAIL_BOOLEAN "SDL.textinput.type.email"
#define SDL_PROP_TEXTINPUT_TYPE_NAME_BOOLEAN "SDL.textinput.type.name"
#define SDL_PROP_TEXTINPUT_TYPE_NUMBER_BOOLEAN "SDL.textinput.type.number"
#define SDL_PROP_TEXTINPUT_TYPE_PASSWORD_BOOLEAN "SDL.textinput.type.password"
#define SDL_PROP_TEXTINPUT_TYPE_USERNAME_BOOLEAN "SDL.textinput.type.username"
#define SDL_PROP_TEXTINPUT_TYPE_VISIBLE_PASSWORD_BOOLEAN "SDL.textinput.type.visible_password"
#define SDL_PROP_TEXTINPUT_ANDROID_INPUTTYPE_NUMBER "SDL.textinput.android.inputtype"

/**
* Check whether or not Unicode text input events are enabled for a window.
*
Expand Down
5 changes: 3 additions & 2 deletions src/core/android/SDL_android.c
Original file line number Diff line number Diff line change
Expand Up @@ -645,7 +645,7 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cl
midSetSystemCursor = (*env)->GetStaticMethodID(env, mActivityClass, "setSystemCursor", "(I)Z");
midSetWindowStyle = (*env)->GetStaticMethodID(env, mActivityClass, "setWindowStyle", "(Z)V");
midShouldMinimizeOnFocusLoss = (*env)->GetStaticMethodID(env, mActivityClass, "shouldMinimizeOnFocusLoss", "()Z");
midShowTextInput = (*env)->GetStaticMethodID(env, mActivityClass, "showTextInput", "(IIII)Z");
midShowTextInput = (*env)->GetStaticMethodID(env, mActivityClass, "showTextInput", "(IIIII)Z");
midSupportsRelativeMouse = (*env)->GetStaticMethodID(env, mActivityClass, "supportsRelativeMouse", "()Z");
midOpenFileDescriptor = (*env)->GetStaticMethodID(env, mActivityClass, "openFileDescriptor", "(Ljava/lang/String;Ljava/lang/String;)I");
midShowFileDialog = (*env)->GetStaticMethodID(env, mActivityClass, "showFileDialog", "([Ljava/lang/String;ZZI)Z");
Expand Down Expand Up @@ -2044,10 +2044,11 @@ int Android_JNI_SuspendScreenSaver(SDL_bool suspend)
return Android_JNI_SendMessage(COMMAND_SET_KEEP_SCREEN_ON, (suspend == SDL_FALSE) ? 0 : 1);
}

void Android_JNI_ShowScreenKeyboard(SDL_Rect *inputRect)
void Android_JNI_ShowScreenKeyboard(int input_type, SDL_Rect *inputRect)
{
JNIEnv *env = Android_JNI_GetEnv();
(*env)->CallStaticBooleanMethod(env, mActivityClass, midShowTextInput,
input_type,
inputRect->x,
inputRect->y,
inputRect->w,
Expand Down
2 changes: 1 addition & 1 deletion src/core/android/SDL_android.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ extern void Android_JNI_MinizeWindow(void);
extern SDL_bool Android_JNI_ShouldMinimizeOnFocusLoss(void);

extern SDL_bool Android_JNI_GetAccelerometerValues(float values[3]);
extern void Android_JNI_ShowScreenKeyboard(SDL_Rect *inputRect);
extern void Android_JNI_ShowScreenKeyboard(int input_type, SDL_Rect *inputRect);
extern void Android_JNI_HideScreenKeyboard(void);
extern SDL_bool Android_JNI_IsScreenKeyboardShown(void);
extern ANativeWindow *Android_JNI_GetNativeWindow(void);
Expand Down
1 change: 1 addition & 0 deletions src/dynapi/SDL_dynapi.sym
Original file line number Diff line number Diff line change
Expand Up @@ -816,6 +816,7 @@ SDL3_0.0.0 {
SDL_SignalCondition;
SDL_SignalSemaphore;
SDL_StartTextInput;
SDL_StartTextInputWithProperties;
SDL_StepUTF8;
SDL_StopHapticEffect;
SDL_StopHapticEffects;
Expand Down
1 change: 1 addition & 0 deletions src/dynapi/SDL_dynapi_overrides.h
Original file line number Diff line number Diff line change
Expand Up @@ -841,6 +841,7 @@
#define SDL_SignalCondition SDL_SignalCondition_REAL
#define SDL_SignalSemaphore SDL_SignalSemaphore_REAL
#define SDL_StartTextInput SDL_StartTextInput_REAL
#define SDL_StartTextInputWithProperties SDL_StartTextInputWithProperties_REAL
#define SDL_StepUTF8 SDL_StepUTF8_REAL
#define SDL_StopHapticEffect SDL_StopHapticEffect_REAL
#define SDL_StopHapticEffects SDL_StopHapticEffects_REAL
Expand Down
1 change: 1 addition & 0 deletions src/dynapi/SDL_dynapi_procs.h
Original file line number Diff line number Diff line change
Expand Up @@ -851,6 +851,7 @@ SDL_DYNAPI_PROC(int,SDL_ShowWindowSystemMenu,(SDL_Window *a, int b, int c),(a,b,
SDL_DYNAPI_PROC(int,SDL_SignalCondition,(SDL_Condition *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_SignalSemaphore,(SDL_Semaphore *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_StartTextInput,(SDL_Window *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_StartTextInputWithProperties,(SDL_Window *a, SDL_PropertiesID b),(a,b),return)
SDL_DYNAPI_PROC(Uint32,SDL_StepUTF8,(const char **a, size_t *b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_StopHapticEffect,(SDL_Haptic *a, int b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_StopHapticEffects,(SDL_Haptic *a),(a),return)
Expand Down
2 changes: 1 addition & 1 deletion src/events/SDL_keyboard.c
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ int SDL_SetKeyboardFocus(SDL_Window *window)

if (SDL_TextInputActive(keyboard->focus)) {
if (video && video->StartTextInput) {
video->StartTextInput(video, keyboard->focus);
video->StartTextInput(video, keyboard->focus, keyboard->focus->text_input_props);
}
}
}
Expand Down
7 changes: 5 additions & 2 deletions src/video/SDL_sysvideo.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ struct SDL_Window
int safe_inset_bottom;
SDL_Rect safe_rect;

SDL_PropertiesID text_input_props;
SDL_bool text_input_active;
SDL_Rect text_input_rect;
int text_input_cursor;
Expand Down Expand Up @@ -331,14 +332,14 @@ struct SDL_VideoDevice
int (*SuspendScreenSaver)(SDL_VideoDevice *_this);

/* Text input */
int (*StartTextInput)(SDL_VideoDevice *_this, SDL_Window *window);
int (*StartTextInput)(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props);
int (*StopTextInput)(SDL_VideoDevice *_this, SDL_Window *window);
int (*UpdateTextInputArea)(SDL_VideoDevice *_this, SDL_Window *window);
int (*ClearComposition)(SDL_VideoDevice *_this, SDL_Window *window);

/* Screen keyboard */
SDL_bool (*HasScreenKeyboardSupport)(SDL_VideoDevice *_this);
void (*ShowScreenKeyboard)(SDL_VideoDevice *_this, SDL_Window *window);
void (*ShowScreenKeyboard)(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props);
void (*HideScreenKeyboard)(SDL_VideoDevice *_this, SDL_Window *window);
SDL_bool (*IsScreenKeyboardShown)(SDL_VideoDevice *_this, SDL_Window *window);

Expand Down Expand Up @@ -565,4 +566,6 @@ extern SDL_bool SDL_ShouldAllowTopmost(void);

extern void SDL_ToggleDragAndDropSupport(void);

#define SDL_PROP_TEXTINPUT_MULTILINE_DEFAULT (!SDL_GetHintBoolean(SDL_HINT_RETURN_KEY_HIDES_IME, SDL_FALSE))

#endif /* SDL_sysvideo_h_ */
48 changes: 39 additions & 9 deletions src/video/SDL_video.c
Original file line number Diff line number Diff line change
Expand Up @@ -4011,6 +4011,7 @@ void SDL_DestroyWindow(SDL_Window *window)
SDL_HideWindow(window);
}

SDL_DestroyProperties(window->text_input_props);
SDL_DestroyProperties(window->props);

/* Clear the modal status, but don't unset the parent, as it may be
Expand Down Expand Up @@ -5129,23 +5130,52 @@ void SDL_WM_SetIcon(SDL_Surface *icon, Uint8 *mask)
}
#endif

static SDL_bool AutoShowingScreenKeyboard(void)
{
const char *hint = SDL_GetHint(SDL_HINT_ENABLE_SCREEN_KEYBOARD);
if (((!hint || SDL_strcasecmp(hint, "auto") == 0) && !SDL_HasKeyboard()) ||
SDL_GetStringBoolean(hint, SDL_FALSE)) {
return SDL_TRUE;
} else {
return SDL_FALSE;
}
}

int SDL_StartTextInput(SDL_Window *window)
{
return SDL_StartTextInputWithProperties(window, 0);
}

int SDL_StartTextInputWithProperties(SDL_Window *window, SDL_PropertiesID props)
{
CHECK_WINDOW_MAGIC(window, -1);

if (window->text_input_props) {
SDL_DestroyProperties(window->text_input_props);
window->text_input_props = 0;
}

if (props) {
window->text_input_props = SDL_CreateProperties();
if (!window->text_input_props) {
return -1;
}
if (SDL_CopyProperties(props, window->text_input_props) < 0) {
return -1;
}
}

/* Show the on-screen keyboard, if desired */
const char *hint = SDL_GetHint(SDL_HINT_ENABLE_SCREEN_KEYBOARD);
if (((!hint || SDL_strcasecmp(hint, "auto") == 0) && !SDL_HasKeyboard()) ||
SDL_GetStringBoolean(hint, SDL_FALSE)) {
if (AutoShowingScreenKeyboard() && !SDL_ScreenKeyboardShown(window)) {
if (_this->ShowScreenKeyboard) {
_this->ShowScreenKeyboard(_this, window);
_this->ShowScreenKeyboard(_this, window, props);
}
}

if (!window->text_input_active) {
/* Finally start the text input system */
if (_this->StartTextInput) {
if (_this->StartTextInput(_this, window) < 0) {
if (_this->StartTextInput(_this, window, props) < 0) {
return -1;
}
}
Expand Down Expand Up @@ -5174,9 +5204,7 @@ int SDL_StopTextInput(SDL_Window *window)
}

/* Hide the on-screen keyboard, if desired */
const char *hint = SDL_GetHint(SDL_HINT_ENABLE_SCREEN_KEYBOARD);
if (((!hint || SDL_strcasecmp(hint, "auto") == 0) && !SDL_HasKeyboard()) ||
SDL_GetStringBoolean(hint, SDL_FALSE)) {
if (AutoShowingScreenKeyboard() && SDL_ScreenKeyboardShown(window)) {
if (_this->HideScreenKeyboard) {
_this->HideScreenKeyboard(_this, window);
}
Expand Down Expand Up @@ -5239,7 +5267,9 @@ SDL_bool SDL_HasScreenKeyboardSupport(void)

SDL_bool SDL_ScreenKeyboardShown(SDL_Window *window)
{
if (window && _this && _this->IsScreenKeyboardShown) {
CHECK_WINDOW_MAGIC(window, SDL_FALSE);

if (_this->IsScreenKeyboardShown) {
return _this->IsScreenKeyboardShown(_this, window);
}
return SDL_FALSE;
Expand Down
Loading

0 comments on commit 451d54e

Please sign in to comment.