From 81f8e6aba6b3b73e44767a61f37e8f72b18ce3e4 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Fri, 2 Aug 2024 06:56:51 -0700 Subject: [PATCH] Added SDL_StartTextInputWithProperties() This allows you to customize the text input so you can have numeric text entry, hidden passwords, etc. Fixes https://github.com/libsdl-org/SDL/issues/7101 Fixes https://github.com/libsdl-org/SDL/issues/7965 Fixes https://github.com/libsdl-org/SDL/issues/9439 --- .../SDLTest/SDLTest.xcodeproj/project.pbxproj | 2 + .../main/java/org/libsdl/app/SDLActivity.java | 9 +- .../java/org/libsdl/app/SDLDummyEdit.java | 8 +- include/SDL3/SDL_hints.h | 2 + include/SDL3/SDL_keyboard.h | 82 ++++++++++ src/core/android/SDL_android.c | 5 +- src/core/android/SDL_android.h | 2 +- src/dynapi/SDL_dynapi.sym | 1 + src/dynapi/SDL_dynapi_overrides.h | 1 + src/dynapi/SDL_dynapi_procs.h | 1 + src/events/SDL_keyboard.c | 2 +- src/video/SDL_sysvideo.h | 10 +- src/video/SDL_video.c | 76 ++++++++- src/video/android/SDL_androidkeyboard.c | 104 +++++++++++- src/video/android/SDL_androidkeyboard.h | 2 +- src/video/cocoa/SDL_cocoakeyboard.h | 2 +- src/video/cocoa/SDL_cocoakeyboard.m | 2 +- src/video/gdk/SDL_gdktextinput.cpp | 39 ++++- src/video/gdk/SDL_gdktextinput.h | 4 +- src/video/n3ds/SDL_n3dsswkb.c | 2 +- src/video/n3ds/SDL_n3dsswkb.h | 2 +- src/video/psp/SDL_pspvideo.c | 33 +++- src/video/psp/SDL_pspvideo.h | 2 +- src/video/uikit/SDL_uikitviewcontroller.h | 2 +- src/video/uikit/SDL_uikitviewcontroller.m | 109 +++++++++++-- src/video/vita/SDL_vitavideo.c | 44 ++++- src/video/vita/SDL_vitavideo.h | 2 +- src/video/wayland/SDL_waylandkeyboard.c | 66 +++++++- src/video/wayland/SDL_waylandkeyboard.h | 2 +- src/video/windows/SDL_windowskeyboard.c | 2 +- src/video/windows/SDL_windowskeyboard.h | 2 +- src/video/winrt/SDL_winrtevents_c.h | 2 +- src/video/winrt/SDL_winrtkeyboard.cpp | 2 +- src/video/x11/SDL_x11keyboard.c | 29 +++- src/video/x11/SDL_x11keyboard.h | 4 +- test/testime.c | 154 ++++++++++++++++-- 36 files changed, 737 insertions(+), 76 deletions(-) diff --git a/Xcode/SDLTest/SDLTest.xcodeproj/project.pbxproj b/Xcode/SDLTest/SDLTest.xcodeproj/project.pbxproj index 0d28405ff..a3b389edc 100644 --- a/Xcode/SDLTest/SDLTest.xcodeproj/project.pbxproj +++ b/Xcode/SDLTest/SDLTest.xcodeproj/project.pbxproj @@ -185,6 +185,7 @@ F3C17D3928E424B800E1A26D /* sample.wav in Resources */ = {isa = PBXBuildFile; fileRef = 00794E6209D20839003FC8A1 /* sample.wav */; }; F3C17D3B28E4252900E1A26D /* icon.bmp in Resources */ = {isa = PBXBuildFile; fileRef = 00794E5D09D20839003FC8A1 /* icon.bmp */; }; F3C2CAC62C5C8BD6004D7998 /* unifont-15.1.05.hex in Resources */ = {isa = PBXBuildFile; fileRef = F3C2CAC52C5C8BD6004D7998 /* unifont-15.1.05.hex */; }; + F3C2CB072C5D3FB2004D7998 /* icon.bmp in Resources */ = {isa = PBXBuildFile; fileRef = 00794E5D09D20839003FC8A1 /* icon.bmp */; }; F3CB56892A7895F800766177 /* SDL3.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 003FA643093FFD41000C53B3 /* SDL3.framework */; }; F3CB568A2A7895F800766177 /* SDL3.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 003FA643093FFD41000C53B3 /* SDL3.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; F3CB568C2A7896BF00766177 /* SDL3.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 003FA643093FFD41000C53B3 /* SDL3.framework */; }; @@ -3026,6 +3027,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + F3C2CB072C5D3FB2004D7998 /* icon.bmp in Resources */, F3C2CAC62C5C8BD6004D7998 /* unifont-15.1.05.hex in Resources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java index 6cfbc904f..134a3dfdc 100644 --- a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java +++ b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java @@ -1375,9 +1375,11 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh */ 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; @@ -1405,6 +1407,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh } else { mTextEdit.setLayoutParams(params); } + mTextEdit.setInputType(input_type); mTextEdit.setVisibility(View.VISIBLE); mTextEdit.requestFocus(); @@ -1419,9 +1422,9 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh /** * 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) { diff --git a/android-project/app/src/main/java/org/libsdl/app/SDLDummyEdit.java b/android-project/app/src/main/java/org/libsdl/app/SDLDummyEdit.java index dca28145e..40e556ff4 100644 --- a/android-project/app/src/main/java/org/libsdl/app/SDLDummyEdit.java +++ b/android-project/app/src/main/java/org/libsdl/app/SDLDummyEdit.java @@ -12,6 +12,7 @@ import android.view.inputmethod.InputConnection; public class SDLDummyEdit extends View implements View.OnKeyListener { InputConnection ic; + int input_type; public SDLDummyEdit(Context context) { super(context); @@ -20,6 +21,10 @@ public class SDLDummyEdit extends View implements View.OnKeyListener setOnKeyListener(this); } + public void setInputType(int input_type) { + this.input_type = input_type; + } + @Override public boolean onCheckIsTextEditor() { return true; @@ -51,8 +56,7 @@ public class SDLDummyEdit extends View implements View.OnKeyListener 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 */; diff --git a/include/SDL3/SDL_hints.h b/include/SDL3/SDL_hints.h index eb14e7a2c..79aa0089e 100644 --- a/include/SDL3/SDL_hints.h +++ b/include/SDL3/SDL_hints.h @@ -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) diff --git a/include/SDL3/SDL_keyboard.h b/include/SDL3/SDL_keyboard.h index b5453a0a1..67c4f5842 100644 --- a/include/SDL3/SDL_keyboard.h +++ b/include/SDL3/SDL_keyboard.h @@ -367,11 +367,93 @@ 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); +/** + * Text input type. + * + * These are the valid values for SDL_PROP_TEXTINPUT_TYPE_NUMBER. Not every value is valid on every platform, but where a value isn't supported, a reasonable fallback will be used. + * + * \since This enum is available since SDL 3.0.0. + * + * \sa SDL_StartTextInputWithProperties + */ +typedef enum SDL_TextInputType +{ + SDL_TEXTINPUT_TYPE_TEXT, /**< The input is text */ + SDL_TEXTINPUT_TYPE_TEXT_NAME, /**< The input is a person's name */ + SDL_TEXTINPUT_TYPE_TEXT_EMAIL, /**< The input is an e-mail address */ + SDL_TEXTINPUT_TYPE_TEXT_USERNAME, /**< The input is a username */ + SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_HIDDEN, /**< The input is a secure password that is hidden */ + SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_VISIBLE, /**< The input is a secure password that is visible */ + SDL_TEXTINPUT_TYPE_NUMBER, /**< The input is a number */ + SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_HIDDEN, /**< The input is a secure PIN that is hidden */ + SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_VISIBLE /**< The input is a secure PIN that is visible */ +} SDL_TextInputType; + +/** + * Auto capitalization type. + * + * These are the valid values for SDL_PROP_TEXTINPUT_AUTOCAPITALIZATION_NUMBER. Not every value is valid on every platform, but where a value isn't supported, a reasonable fallback will be used. + * + * \since This enum is available since SDL 3.0.0. + * + * \sa SDL_StartTextInputWithProperties + */ +typedef enum SDL_Capitalization +{ + SDL_CAPITALIZE_NONE, /**< No auto-capitalization will be done */ + SDL_CAPITALIZE_SENTENCES, /**< The first letter of sentences will be capitalized */ + SDL_CAPITALIZE_WORDS, /**< The first letter of words will be capitalized */ + SDL_CAPITALIZE_LETTERS /**< All letters will be capitalized */ +} SDL_Capitalization; + +/** + * 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_TYPE_NUMBER` - an SDL_TextInputType value that describes text being input, defaults to SDL_TEXTINPUT_TYPE_TEXT. + * - `SDL_PROP_TEXTINPUT_CAPITALIZATION_NUMBER` - an SDL_Capitalization value that describes how text should be capitalized, defaults to SDL_CAPITALIZE_NONE. + * - `SDL_PROP_TEXTINPUT_AUTOCORRECT_BOOLEAN` - true to enable auto completion and auto correction. + * - `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". + * + * 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_TYPE_NUMBER "SDL.textinput.type" +#define SDL_PROP_TEXTINPUT_CAPITALIZATION_NUMBER "SDL.textinput.capitalization" +#define SDL_PROP_TEXTINPUT_AUTOCORRECT_BOOLEAN "SDL.textinput.autocorrect" +#define SDL_PROP_TEXTINPUT_MULTILINE_BOOLEAN "SDL.textinput.multiline" +#define SDL_PROP_TEXTINPUT_ANDROID_INPUTTYPE_NUMBER "SDL.textinput.android.inputtype" + /** * Check whether or not Unicode text input events are enabled for a window. * diff --git a/src/core/android/SDL_android.c b/src/core/android/SDL_android.c index c0c73bcf6..bf0f30b98 100644 --- a/src/core/android/SDL_android.c +++ b/src/core/android/SDL_android.c @@ -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"); @@ -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, diff --git a/src/core/android/SDL_android.h b/src/core/android/SDL_android.h index ef0df18e3..6950ef2c7 100644 --- a/src/core/android/SDL_android.h +++ b/src/core/android/SDL_android.h @@ -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); diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index bf7a2276b..38aad316a 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -816,6 +816,7 @@ SDL3_0.0.0 { SDL_SignalCondition; SDL_SignalSemaphore; SDL_StartTextInput; + SDL_StartTextInputWithProperties; SDL_StepUTF8; SDL_StopHapticEffect; SDL_StopHapticEffects; diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index 96d40eaa3..004eee31c 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -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 diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 749af3ce2..6cdf7ff09 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -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) diff --git a/src/events/SDL_keyboard.c b/src/events/SDL_keyboard.c index 63c503d14..78abd8613 100644 --- a/src/events/SDL_keyboard.c +++ b/src/events/SDL_keyboard.c @@ -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); } } } diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h index 2be73c9b5..655419122 100644 --- a/src/video/SDL_sysvideo.h +++ b/src/video/SDL_sysvideo.h @@ -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; @@ -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); @@ -565,4 +566,9 @@ extern SDL_bool SDL_ShouldAllowTopmost(void); extern void SDL_ToggleDragAndDropSupport(void); +extern SDL_TextInputType SDL_GetTextInputType(SDL_PropertiesID props); +extern SDL_Capitalization SDL_GetTextInputCapitalization(SDL_PropertiesID props); +extern SDL_bool SDL_GetTextInputAutocorrect(SDL_PropertiesID props); +extern SDL_bool SDL_GetTextInputMultiline(SDL_PropertiesID props); + #endif /* SDL_sysvideo_h_ */ diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index 484f84f13..fa6707f99 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -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 @@ -5129,23 +5130,80 @@ void SDL_WM_SetIcon(SDL_Surface *icon, Uint8 *mask) } #endif -int SDL_StartTextInput(SDL_Window *window) +SDL_TextInputType SDL_GetTextInputType(SDL_PropertiesID props) { - CHECK_WINDOW_MAGIC(window, -1); + return (SDL_TextInputType)SDL_GetNumberProperty(props, SDL_PROP_TEXTINPUT_TYPE_NUMBER, SDL_TEXTINPUT_TYPE_TEXT); +} - /* Show the on-screen keyboard, if desired */ +SDL_Capitalization SDL_GetTextInputCapitalization(SDL_PropertiesID props) +{ + return (SDL_Capitalization)SDL_GetNumberProperty(props, SDL_PROP_TEXTINPUT_CAPITALIZATION_NUMBER, SDL_CAPITALIZE_NONE); +} + +SDL_bool SDL_GetTextInputAutocorrect(SDL_PropertiesID props) +{ + return SDL_GetBooleanProperty(props, SDL_PROP_TEXTINPUT_AUTOCORRECT_BOOLEAN, SDL_FALSE); +} + +SDL_bool SDL_GetTextInputMultiline(SDL_PropertiesID props) +{ + if (SDL_HasProperty(props, SDL_PROP_TEXTINPUT_MULTILINE_BOOLEAN)) { + return SDL_GetBooleanProperty(props, SDL_PROP_TEXTINPUT_MULTILINE_BOOLEAN, SDL_FALSE); + } + + if (SDL_GetHintBoolean(SDL_HINT_RETURN_KEY_HIDES_IME, SDL_FALSE)) { + return SDL_FALSE; + } else { + return SDL_TRUE; + } +} + +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 */ + 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; } } @@ -5174,9 +5232,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); } @@ -5239,7 +5295,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; diff --git a/src/video/android/SDL_androidkeyboard.c b/src/video/android/SDL_androidkeyboard.c index 85a124725..84da48166 100644 --- a/src/video/android/SDL_androidkeyboard.c +++ b/src/video/android/SDL_androidkeyboard.c @@ -30,6 +30,46 @@ #include "../../core/android/SDL_android.h" +#define TYPE_CLASS_TEXT 0x00000001 +#define TYPE_CLASS_NUMBER 0x00000002 +#define TYPE_CLASS_PHONE 0x00000003 +#define TYPE_CLASS_DATETIME 0x00000004 + +#define TYPE_DATETIME_VARIATION_NORMAL 0x00000000 +#define TYPE_DATETIME_VARIATION_DATE 0x00000010 +#define TYPE_DATETIME_VARIATION_TIME 0x00000020 + +#define TYPE_NUMBER_VARIATION_NORMAL 0x00000000 +#define TYPE_NUMBER_VARIATION_PASSWORD 0x00000010 +#define TYPE_NUMBER_FLAG_SIGNED 0x00001000 +#define TYPE_NUMBER_FLAG_DECIMAL 0x00002000 + +#define TYPE_TEXT_FLAG_CAP_CHARACTERS 0x00001000 +#define TYPE_TEXT_FLAG_CAP_WORDS 0x00002000 +#define TYPE_TEXT_FLAG_CAP_SENTENCES 0x00004000 +#define TYPE_TEXT_FLAG_AUTO_CORRECT 0x00008000 +#define TYPE_TEXT_FLAG_AUTO_COMPLETE 0x00010000 +#define TYPE_TEXT_FLAG_MULTI_LINE 0x00020000 +#define TYPE_TEXT_FLAG_IME_MULTI_LINE 0x00040000 +#define TYPE_TEXT_FLAG_NO_SUGGESTIONS 0x00080000 + +#define TYPE_TEXT_VARIATION_NORMAL 0x00000000 +#define TYPE_TEXT_VARIATION_URI 0x00000010 +#define TYPE_TEXT_VARIATION_EMAIL_ADDRESS 0x00000020 +#define TYPE_TEXT_VARIATION_EMAIL_SUBJECT 0x00000030 +#define TYPE_TEXT_VARIATION_SHORT_MESSAGE 0x00000040 +#define TYPE_TEXT_VARIATION_LONG_MESSAGE 0x00000050 +#define TYPE_TEXT_VARIATION_PERSON_NAME 0x00000060 +#define TYPE_TEXT_VARIATION_POSTAL_ADDRESS 0x00000070 +#define TYPE_TEXT_VARIATION_PASSWORD 0x00000080 +#define TYPE_TEXT_VARIATION_VISIBLE_PASSWORD 0x00000090 +#define TYPE_TEXT_VARIATION_WEB_EDIT_TEXT 0x000000a0 +#define TYPE_TEXT_VARIATION_FILTER 0x000000b0 +#define TYPE_TEXT_VARIATION_PHONETIC 0x000000c0 +#define TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS 0x000000d0 +#define TYPE_TEXT_VARIATION_WEB_PASSWORD 0x000000e0 + + static SDL_Scancode Android_Keycodes[] = { SDL_SCANCODE_UNKNOWN, /* AKEYCODE_UNKNOWN */ SDL_SCANCODE_SOFTLEFT, /* AKEYCODE_SOFT_LEFT */ @@ -343,9 +383,67 @@ SDL_bool Android_HasScreenKeyboardSupport(SDL_VideoDevice *_this) return SDL_TRUE; } -void Android_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window) +void Android_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props) { - Android_JNI_ShowScreenKeyboard(&window->text_input_rect); + int input_type = 0; + if (SDL_HasProperty(props, SDL_PROP_TEXTINPUT_ANDROID_INPUTTYPE_NUMBER)) { + input_type = (int)SDL_GetNumberProperty(props, SDL_PROP_TEXTINPUT_ANDROID_INPUTTYPE_NUMBER, 0); + } else { + switch (SDL_GetTextInputType(props)) { + default: + case SDL_TEXTINPUT_TYPE_TEXT: + input_type = (TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_NORMAL); + break; + case SDL_TEXTINPUT_TYPE_TEXT_NAME: + input_type = (TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_PERSON_NAME); + break; + case SDL_TEXTINPUT_TYPE_TEXT_EMAIL: + input_type = (TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_EMAIL_ADDRESS); + break; + case SDL_TEXTINPUT_TYPE_TEXT_USERNAME: + input_type = (TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_NORMAL); + break; + case SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_HIDDEN: + input_type = (TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_PASSWORD); + break; + case SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_VISIBLE: + input_type = (TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_VISIBLE_PASSWORD); + break; + case SDL_TEXTINPUT_TYPE_NUMBER: + input_type = (TYPE_CLASS_NUMBER | TYPE_NUMBER_VARIATION_NORMAL); + break; + case SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_HIDDEN: + input_type = (TYPE_CLASS_NUMBER | TYPE_NUMBER_VARIATION_PASSWORD); + break; + case SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_VISIBLE: + input_type = (TYPE_CLASS_NUMBER | TYPE_NUMBER_VARIATION_NORMAL); + break; + } + + switch (SDL_GetTextInputCapitalization(props)) { + default: + case SDL_CAPITALIZE_NONE: + break; + case SDL_CAPITALIZE_LETTERS: + input_type |= TYPE_TEXT_FLAG_CAP_CHARACTERS; + break; + case SDL_CAPITALIZE_WORDS: + input_type |= TYPE_TEXT_FLAG_CAP_WORDS; + break; + case SDL_CAPITALIZE_SENTENCES: + input_type |= TYPE_TEXT_FLAG_CAP_SENTENCES; + break; + } + + if (SDL_GetTextInputAutocorrect(props)) { + input_type |= (TYPE_TEXT_FLAG_AUTO_CORRECT | TYPE_TEXT_FLAG_AUTO_COMPLETE); + } + + if (SDL_GetTextInputMultiline(props)) { + input_type |= TYPE_TEXT_FLAG_MULTI_LINE; + } + } + Android_JNI_ShowScreenKeyboard(input_type, &window->text_input_rect); SDL_screen_keyboard_shown = SDL_TRUE; } @@ -358,7 +456,7 @@ void Android_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window) void Android_RestoreScreenKeyboardOnResume(SDL_VideoDevice *_this, SDL_Window *window) { if (SDL_screen_keyboard_shown) { - Android_ShowScreenKeyboard(_this, window); + Android_ShowScreenKeyboard(_this, window, window->text_input_props); } } diff --git a/src/video/android/SDL_androidkeyboard.h b/src/video/android/SDL_androidkeyboard.h index bcbd948e1..c7b70090b 100644 --- a/src/video/android/SDL_androidkeyboard.h +++ b/src/video/android/SDL_androidkeyboard.h @@ -26,7 +26,7 @@ extern int Android_OnKeyDown(int keycode); extern int Android_OnKeyUp(int keycode); extern SDL_bool Android_HasScreenKeyboardSupport(SDL_VideoDevice *_this); -extern void Android_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window); +extern void Android_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props); extern void Android_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window); extern void Android_RestoreScreenKeyboardOnResume(SDL_VideoDevice *_this, SDL_Window *window); extern SDL_bool Android_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window); diff --git a/src/video/cocoa/SDL_cocoakeyboard.h b/src/video/cocoa/SDL_cocoakeyboard.h index 85a409258..0103e1a8a 100644 --- a/src/video/cocoa/SDL_cocoakeyboard.h +++ b/src/video/cocoa/SDL_cocoakeyboard.h @@ -27,7 +27,7 @@ extern void Cocoa_InitKeyboard(SDL_VideoDevice *_this); extern void Cocoa_HandleKeyEvent(SDL_VideoDevice *_this, NSEvent *event); extern void Cocoa_QuitKeyboard(SDL_VideoDevice *_this); -extern int Cocoa_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window); +extern int Cocoa_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props); extern int Cocoa_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window); extern int Cocoa_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window); diff --git a/src/video/cocoa/SDL_cocoakeyboard.m b/src/video/cocoa/SDL_cocoakeyboard.m index 685d6752d..1bc0664da 100644 --- a/src/video/cocoa/SDL_cocoakeyboard.m +++ b/src/video/cocoa/SDL_cocoakeyboard.m @@ -381,7 +381,7 @@ void Cocoa_InitKeyboard(SDL_VideoDevice *_this) SDL_ToggleModState(SDL_KMOD_CAPS, (data.modifierFlags & NSEventModifierFlagCapsLock) ? SDL_TRUE : SDL_FALSE); } -int Cocoa_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window) +int Cocoa_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props) { @autoreleasepool { NSView *parentView; diff --git a/src/video/gdk/SDL_gdktextinput.cpp b/src/video/gdk/SDL_gdktextinput.cpp index 7af1b381a..0c6b1d30c 100644 --- a/src/video/gdk/SDL_gdktextinput.cpp +++ b/src/video/gdk/SDL_gdktextinput.cpp @@ -178,7 +178,7 @@ void GDK_EnsureHints(void) } } -int GDK_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window) +int GDK_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props) { /* * Currently a stub, since all input is handled by the virtual keyboard, @@ -226,7 +226,7 @@ SDL_bool GDK_HasScreenKeyboardSupport(SDL_VideoDevice *_this) return SDL_TRUE; } -void GDK_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window) +void GDK_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props) { /* * There is XGameUiTextEntryOpen but it's only in online docs, @@ -253,6 +253,39 @@ void GDK_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window) return; } + XGameUiTextEntryInputScope scope; + switch (SDL_GetTextInputType(props)) { + default: + case SDL_TEXTINPUT_TYPE_TEXT: + scope = (XGameUiTextEntryInputScope)g_TextInputScope; + break; + case SDL_TEXTINPUT_TYPE_TEXT_NAME: + scope = XGameUiTextEntryInputScope::Default; + break; + case SDL_TEXTINPUT_TYPE_TEXT_EMAIL: + scope = XGameUiTextEntryInputScope::EmailSmtpAddress; + break; + case SDL_TEXTINPUT_TYPE_TEXT_USERNAME: + scope = XGameUiTextEntryInputScope::Default; + break; + case SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_HIDDEN: + scope = XGameUiTextEntryInputScope::Password; + break; + case SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_VISIBLE: + scope = XGameUiTextEntryInputScope::Default; + break; + case SDL_TEXTINPUT_TYPE_NUMBER: + scope = XGameUiTextEntryInputScope::Number; + break; + case SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_HIDDEN: + // FIXME: Password or number scope? + scope = XGameUiTextEntryInputScope::Number; + break; + case SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_VISIBLE: + scope = XGameUiTextEntryInputScope::Number; + break; + } + g_TextBlock->queue = g_TextTaskQueue; g_TextBlock->context = _this; g_TextBlock->callback = GDK_InternalTextEntryCallback; @@ -261,7 +294,7 @@ void GDK_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window) g_TitleText, g_DescriptionText, g_DefaultText, - (XGameUiTextEntryInputScope)g_TextInputScope, + scope, (uint32_t)g_MaxTextLength))) { SDL_free(g_TextBlock); g_TextBlock = NULL; diff --git a/src/video/gdk/SDL_gdktextinput.h b/src/video/gdk/SDL_gdktextinput.h index cbd043885..4b343da97 100644 --- a/src/video/gdk/SDL_gdktextinput.h +++ b/src/video/gdk/SDL_gdktextinput.h @@ -32,13 +32,13 @@ extern "C" { void GDK_EnsureHints(void); -int GDK_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window); +int GDK_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props); int GDK_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window); int GDK_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window); int GDK_ClearComposition(SDL_VideoDevice *_this, SDL_Window *window); SDL_bool GDK_HasScreenKeyboardSupport(SDL_VideoDevice *_this); -void GDK_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window); +void GDK_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props); void GDK_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window); SDL_bool GDK_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window); diff --git a/src/video/n3ds/SDL_n3dsswkb.c b/src/video/n3ds/SDL_n3dsswkb.c index 51c38d8d2..d15d65489 100644 --- a/src/video/n3ds/SDL_n3dsswkb.c +++ b/src/video/n3ds/SDL_n3dsswkb.c @@ -50,7 +50,7 @@ SDL_bool N3DS_HasScreenKeyboardSupport(SDL_VideoDevice *_this) return SDL_TRUE; } -int N3DS_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window) +int N3DS_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props) { char buffer[BUFFER_SIZE]; SwkbdButton button_pressed; diff --git a/src/video/n3ds/SDL_n3dsswkb.h b/src/video/n3ds/SDL_n3dsswkb.h index 0f505ebe8..199e9e8c5 100644 --- a/src/video/n3ds/SDL_n3dsswkb.h +++ b/src/video/n3ds/SDL_n3dsswkb.h @@ -30,7 +30,7 @@ void N3DS_SwkbQuit(); SDL_bool N3DS_HasScreenKeyboardSupport(SDL_VideoDevice *_this); -int N3DS_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window); +int N3DS_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props); int N3DS_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window); #endif /* SDL_n3dskeyboard_h_ */ diff --git a/src/video/psp/SDL_pspvideo.c b/src/video/psp/SDL_pspvideo.c index bec60d0d1..1897e2ea6 100644 --- a/src/video/psp/SDL_pspvideo.c +++ b/src/video/psp/SDL_pspvideo.c @@ -249,7 +249,7 @@ SDL_bool PSP_HasScreenKeyboardSupport(SDL_VideoDevice *_this) return SDL_TRUE; } -void PSP_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window) +void PSP_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props) { char list[0x20000] __attribute__((aligned(64))); // Needed for sceGuStart to work int i; @@ -266,7 +266,36 @@ void PSP_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window) data.language = PSP_UTILITY_OSK_LANGUAGE_DEFAULT; data.lines = 1; data.unk_24 = 1; - data.inputtype = PSP_UTILITY_OSK_INPUTTYPE_ALL; + switch (SDL_GetTextInputType(props)) { + default: + case SDL_TEXTINPUT_TYPE_TEXT: + data.inputtype = PSP_UTILITY_OSK_INPUTTYPE_ALL; + break; + case SDL_TEXTINPUT_TYPE_TEXT_NAME: + data.inputtype = PSP_UTILITY_OSK_INPUTTYPE_ALL; + break; + case SDL_TEXTINPUT_TYPE_TEXT_EMAIL: + data.inputtype = PSP_UTILITY_OSK_INPUTTYPE_ALL; + break; + case SDL_TEXTINPUT_TYPE_TEXT_USERNAME: + data.inputtype = PSP_UTILITY_OSK_INPUTTYPE_ALL; + break; + case SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_HIDDEN: + data.inputtype = PSP_UTILITY_OSK_INPUTTYPE_ALL; + break; + case SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_VISIBLE: + data.inputtype = PSP_UTILITY_OSK_INPUTTYPE_ALL; + break; + case SDL_TEXTINPUT_TYPE_NUMBER: + data.inputtype = PSP_UTILITY_OSK_INPUTTYPE_LATIN_DIGIT; + break; + case SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_HIDDEN: + data.inputtype = PSP_UTILITY_OSK_INPUTTYPE_LATIN_DIGIT; + break; + case SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_VISIBLE: + data.inputtype = PSP_UTILITY_OSK_INPUTTYPE_LATIN_DIGIT; + break; + } data.desc = NULL; data.intext = NULL; data.outtextlength = input_text_length; diff --git a/src/video/psp/SDL_pspvideo.h b/src/video/psp/SDL_pspvideo.h index 50de4bca8..79351ffb9 100644 --- a/src/video/psp/SDL_pspvideo.h +++ b/src/video/psp/SDL_pspvideo.h @@ -74,7 +74,7 @@ int PSP_GL_DeleteContext(SDL_VideoDevice *_this, SDL_GLContext context); /* PSP on screen keyboard */ SDL_bool PSP_HasScreenKeyboardSupport(SDL_VideoDevice *_this); -void PSP_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window); +void PSP_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props); void PSP_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window); SDL_bool PSP_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window); diff --git a/src/video/uikit/SDL_uikitviewcontroller.h b/src/video/uikit/SDL_uikitviewcontroller.h index cfd8767db..6433e7c33 100644 --- a/src/video/uikit/SDL_uikitviewcontroller.h +++ b/src/video/uikit/SDL_uikitviewcontroller.h @@ -88,7 +88,7 @@ #ifdef SDL_IPHONE_KEYBOARD SDL_bool UIKit_HasScreenKeyboardSupport(SDL_VideoDevice *_this); -void UIKit_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window); +void UIKit_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props); void UIKit_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window); SDL_bool UIKit_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window); int UIKit_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window); diff --git a/src/video/uikit/SDL_uikitviewcontroller.m b/src/video/uikit/SDL_uikitviewcontroller.m index 20a3aae0c..db85b2533 100644 --- a/src/video/uikit/SDL_uikitviewcontroller.m +++ b/src/video/uikit/SDL_uikitviewcontroller.m @@ -278,15 +278,6 @@ static void SDLCALL SDL_HideHomeIndicatorHintChanged(void *userdata, const char textField.text = obligateForBackspace; committedText = textField.text; - /* set UITextInputTrait properties, mostly to defaults */ - textField.autocapitalizationType = UITextAutocapitalizationTypeNone; - textField.autocorrectionType = UITextAutocorrectionTypeNo; - textField.enablesReturnKeyAutomatically = NO; - textField.keyboardAppearance = UIKeyboardAppearanceDefault; - textField.keyboardType = UIKeyboardTypeDefault; - textField.returnKeyType = UIReturnKeyDefault; - textField.secureTextEntry = NO; - textField.hidden = YES; keyboardVisible = NO; @@ -393,6 +384,103 @@ static void SDLCALL SDL_HideHomeIndicatorHintChanged(void *userdata, const char object:nil]; } +- (void)setKeyboardProperties:(SDL_PropertiesID) props +{ + textField.secureTextEntry = NO; + + switch (SDL_GetTextInputType(props)) { + default: + case SDL_TEXTINPUT_TYPE_TEXT: + textField.keyboardType = UIKeyboardTypeDefault; + textField.textContentType = nil; + break; + case SDL_TEXTINPUT_TYPE_TEXT_NAME: + textField.keyboardType = UIKeyboardTypeDefault; + textField.textContentType = UITextContentTypeName; + break; + case SDL_TEXTINPUT_TYPE_TEXT_EMAIL: + textField.keyboardType = UIKeyboardTypeEmailAddress; + textField.textContentType = UITextContentTypeEmailAddress; + break; + case SDL_TEXTINPUT_TYPE_TEXT_USERNAME: + textField.keyboardType = UIKeyboardTypeDefault; + if (@available(iOS 11.0, *)) { + textField.textContentType = UITextContentTypeUsername; + } else { + textField.textContentType = nil; + } + break; + case SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_HIDDEN: + textField.keyboardType = UIKeyboardTypeDefault; + if (@available(iOS 11.0, *)) { + textField.textContentType = UITextContentTypePassword; + } else { + textField.textContentType = nil; + } + textField.secureTextEntry = YES; + break; + case SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_VISIBLE: + textField.keyboardType = UIKeyboardTypeDefault; + if (@available(iOS 11.0, *)) { + textField.textContentType = UITextContentTypePassword; + } else { + textField.textContentType = nil; + } + break; + case SDL_TEXTINPUT_TYPE_NUMBER: + textField.keyboardType = UIKeyboardTypeNumberPad; + textField.textContentType = nil; + break; + case SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_HIDDEN: + textField.keyboardType = UIKeyboardTypeNumberPad; + if (@available(iOS 12.0, *)) { + textField.textContentType = UITextContentTypeOneTimeCode; + } else { + textField.textContentType = nil; + } + textField.secureTextEntry = YES; + break; + case SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_VISIBLE: + textField.keyboardType = UIKeyboardTypeNumberPad; + if (@available(iOS 12.0, *)) { + textField.textContentType = UITextContentTypeOneTimeCode; + } else { + textField.textContentType = nil; + } + break; + } + + switch (SDL_GetTextInputCapitalization(props)) { + default: + case SDL_CAPITALIZE_NONE: + textField.autocapitalizationType = UITextAutocapitalizationTypeNone; + break; + case SDL_CAPITALIZE_LETTERS: + textField.autocapitalizationType = UITextAutocapitalizationTypeAllCharacters; + break; + case SDL_CAPITALIZE_WORDS: + textField.autocapitalizationType = UITextAutocapitalizationTypeWords; + break; + case SDL_CAPITALIZE_SENTENCES: + textField.autocapitalizationType = UITextAutocapitalizationTypeSentences; + break; + } + + if (SDL_GetTextInputAutocorrect(props)) { + textField.autocorrectionType = UITextAutocorrectionTypeYes; + textField.spellCheckingType = UITextSpellCheckingTypeYes; + } else { + textField.autocorrectionType = UITextAutocorrectionTypeNo; + textField.spellCheckingType = UITextSpellCheckingTypeNo; + } + + if (SDL_GetTextInputMultiline(props)) { + textField.enablesReturnKeyAutomatically = YES; + } else { + textField.enablesReturnKeyAutomatically = NO; + } +} + /* reveal onscreen virtual keyboard */ - (void)showKeyboard { @@ -596,10 +684,11 @@ SDL_bool UIKit_HasScreenKeyboardSupport(SDL_VideoDevice *_this) return SDL_TRUE; } -void UIKit_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window) +void UIKit_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props) { @autoreleasepool { SDL_uikitviewcontroller *vc = GetWindowViewController(window); + [vc setKeyboardProperties:props]; [vc showKeyboard]; } } diff --git a/src/video/vita/SDL_vitavideo.c b/src/video/vita/SDL_vitavideo.c index a8bafc5ed..c5ced23e3 100644 --- a/src/video/vita/SDL_vitavideo.c +++ b/src/video/vita/SDL_vitavideo.c @@ -411,7 +411,7 @@ void VITA_ImeEventHandler(void *arg, const SceImeEventData *e) } #endif -void VITA_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window) +void VITA_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props) { SDL_VideoData *videodata = _this->internal; SceInt32 res; @@ -427,8 +427,46 @@ void VITA_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window) param.supportedLanguages = SCE_IME_LANGUAGE_ENGLISH_US; param.languagesForced = SCE_FALSE; - param.type = SCE_IME_TYPE_DEFAULT; - param.option = SCE_IME_OPTION_NO_ASSISTANCE; + switch (SDL_GetTextInputType(props)) { + default: + case SDL_TEXTINPUT_TYPE_TEXT: + param.type = SCE_IME_TYPE_DEFAULT; + break; + case SDL_TEXTINPUT_TYPE_TEXT_NAME: + param.type = SCE_IME_TYPE_DEFAULT; + break; + case SDL_TEXTINPUT_TYPE_TEXT_EMAIL: + param.type = SCE_IME_TYPE_MAIL; + break; + case SDL_TEXTINPUT_TYPE_TEXT_USERNAME: + param.type = SCE_IME_TYPE_DEFAULT; + break; + case SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_HIDDEN: + param.type = SCE_IME_TYPE_DEFAULT; + break; + case SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_VISIBLE: + param.type = SCE_IME_TYPE_DEFAULT; + break; + case SDL_TEXTINPUT_TYPE_NUMBER: + param.type = SCE_IME_TYPE_NUMBER; + break; + case SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_HIDDEN: + param.type = SCE_IME_TYPE_NUMBER; + break; + case SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_VISIBLE: + param.type = SCE_IME_TYPE_NUMBER; + break; + } + param.option = 0; + if (SDL_GetTextInputCapitalization(props) != SDL_CAPITALIZE_SENTENCES) { + param.option |= SCE_IME_OPTION_NO_AUTO_CAPITALIZATION; + } + if (!SDL_GetTextInputAutocorrect(props)) { + param.option |= SCE_IME_OPTION_NO_ASSISTANCE; + } + if (SDL_GetTextInputMultiline(props)) { + param.option |= SCE_IME_OPTION_MULTILINE; + } param.inputTextBuffer = libime_out; param.maxTextLength = SCE_IME_MAX_TEXT_LENGTH; param.handler = VITA_ImeEventHandler; diff --git a/src/video/vita/SDL_vitavideo.h b/src/video/vita/SDL_vitavideo.h index 610d0c0de..61aa6646f 100644 --- a/src/video/vita/SDL_vitavideo.h +++ b/src/video/vita/SDL_vitavideo.h @@ -97,7 +97,7 @@ int VITA_GLES_DeleteContext(SDL_VideoDevice *_this, SDL_GLContext context); /* VITA on screen keyboard */ SDL_bool VITA_HasScreenKeyboardSupport(SDL_VideoDevice *_this); -void VITA_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window); +void VITA_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props); void VITA_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window); SDL_bool VITA_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window); diff --git a/src/video/wayland/SDL_waylandkeyboard.c b/src/video/wayland/SDL_waylandkeyboard.c index 854197e9c..88152c95f 100644 --- a/src/video/wayland/SDL_waylandkeyboard.c +++ b/src/video/wayland/SDL_waylandkeyboard.c @@ -51,7 +51,7 @@ void Wayland_QuitKeyboard(SDL_VideoDevice *_this) #endif } -int Wayland_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window) +int Wayland_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props) { SDL_VideoData *internal = _this->internal; struct SDL_WaylandInput *input = internal->input; @@ -59,13 +59,71 @@ int Wayland_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window) if (internal->text_input_manager) { if (input && input->text_input) { const SDL_Rect *rect = &input->text_input->cursor_rect; + enum zwp_text_input_v3_content_hint hint = ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE; + enum zwp_text_input_v3_content_purpose purpose; + + switch (SDL_GetTextInputType(props)) { + default: + case SDL_TEXTINPUT_TYPE_TEXT: + purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NORMAL; + break; + case SDL_TEXTINPUT_TYPE_TEXT_NAME: + purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NAME; + break; + case SDL_TEXTINPUT_TYPE_TEXT_EMAIL: + purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_EMAIL; + break; + case SDL_TEXTINPUT_TYPE_TEXT_USERNAME: + purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NORMAL; + hint |= ZWP_TEXT_INPUT_V3_CONTENT_HINT_SENSITIVE_DATA; + break; + case SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_HIDDEN: + purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PASSWORD; + hint |= (ZWP_TEXT_INPUT_V3_CONTENT_HINT_HIDDEN_TEXT | ZWP_TEXT_INPUT_V3_CONTENT_HINT_SENSITIVE_DATA); + break; + case SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_VISIBLE: + purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PASSWORD; + hint |= ZWP_TEXT_INPUT_V3_CONTENT_HINT_SENSITIVE_DATA; + break; + case SDL_TEXTINPUT_TYPE_NUMBER: + purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NUMBER; + break; + case SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_HIDDEN: + purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PIN; + hint |= (ZWP_TEXT_INPUT_V3_CONTENT_HINT_HIDDEN_TEXT | ZWP_TEXT_INPUT_V3_CONTENT_HINT_SENSITIVE_DATA); + break; + case SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_VISIBLE: + purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PIN; + hint |= ZWP_TEXT_INPUT_V3_CONTENT_HINT_SENSITIVE_DATA; + break; + } + + switch (SDL_GetTextInputCapitalization(props)) { + default: + case SDL_CAPITALIZE_NONE: + break; + case SDL_CAPITALIZE_LETTERS: + hint |= ZWP_TEXT_INPUT_V3_CONTENT_HINT_UPPERCASE; + break; + case SDL_CAPITALIZE_WORDS: + hint |= ZWP_TEXT_INPUT_V3_CONTENT_HINT_TITLECASE; + break; + case SDL_CAPITALIZE_SENTENCES: + hint |= ZWP_TEXT_INPUT_V3_CONTENT_HINT_AUTO_CAPITALIZATION; + break; + } + + if (SDL_GetTextInputAutocorrect(props)) { + hint |= (ZWP_TEXT_INPUT_V3_CONTENT_HINT_COMPLETION | ZWP_TEXT_INPUT_V3_CONTENT_HINT_SPELLCHECK); + } + if (SDL_GetTextInputMultiline(props)) { + hint |= ZWP_TEXT_INPUT_V3_CONTENT_HINT_MULTILINE; + } zwp_text_input_v3_enable(input->text_input->text_input); /* Now that it's enabled, set the input properties */ - zwp_text_input_v3_set_content_type(input->text_input->text_input, - ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE, - ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NORMAL); + zwp_text_input_v3_set_content_type(input->text_input->text_input, hint, purpose); if (!SDL_RectEmpty(rect)) { /* This gets reset on enable so we have to cache it */ zwp_text_input_v3_set_cursor_rectangle(input->text_input->text_input, diff --git a/src/video/wayland/SDL_waylandkeyboard.h b/src/video/wayland/SDL_waylandkeyboard.h index 8550aec2b..c85bfc84b 100644 --- a/src/video/wayland/SDL_waylandkeyboard.h +++ b/src/video/wayland/SDL_waylandkeyboard.h @@ -32,7 +32,7 @@ typedef struct SDL_WaylandTextInput extern int Wayland_InitKeyboard(SDL_VideoDevice *_this); extern void Wayland_QuitKeyboard(SDL_VideoDevice *_this); -extern int Wayland_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window); +extern int Wayland_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props); extern int Wayland_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window); extern int Wayland_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window); extern SDL_bool Wayland_HasScreenKeyboardSupport(SDL_VideoDevice *_this); diff --git a/src/video/windows/SDL_windowskeyboard.c b/src/video/windows/SDL_windowskeyboard.c index fe141c576..304f15491 100644 --- a/src/video/windows/SDL_windowskeyboard.c +++ b/src/video/windows/SDL_windowskeyboard.c @@ -196,7 +196,7 @@ void WIN_ResetDeadKeys(void) } } -int WIN_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window) +int WIN_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props) { WIN_ResetDeadKeys(); diff --git a/src/video/windows/SDL_windowskeyboard.h b/src/video/windows/SDL_windowskeyboard.h index 15de35f20..efdff742d 100644 --- a/src/video/windows/SDL_windowskeyboard.h +++ b/src/video/windows/SDL_windowskeyboard.h @@ -29,7 +29,7 @@ extern void WIN_QuitKeyboard(SDL_VideoDevice *_this); extern void WIN_ResetDeadKeys(void); -extern int WIN_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window); +extern int WIN_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props); extern int WIN_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window); extern int WIN_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window); extern int WIN_ClearComposition(SDL_VideoDevice *_this, SDL_Window *window); diff --git a/src/video/winrt/SDL_winrtevents_c.h b/src/video/winrt/SDL_winrtevents_c.h index e77e37d90..55a20f921 100644 --- a/src/video/winrt/SDL_winrtevents_c.h +++ b/src/video/winrt/SDL_winrtevents_c.h @@ -69,7 +69,7 @@ extern void WINRT_ProcessCharacterReceivedEvent(SDL_Window *window, Windows::UI: #if NTDDI_VERSION >= NTDDI_WIN10 extern void WINTRT_InitialiseInputPaneEvents(SDL_VideoDevice *_this); extern SDL_bool WINRT_HasScreenKeyboardSupport(SDL_VideoDevice *_this); -extern void WINRT_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window); +extern void WINRT_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props); extern void WINRT_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window); extern SDL_bool WINRT_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window); #endif // NTDDI_VERSION >= ... diff --git a/src/video/winrt/SDL_winrtkeyboard.cpp b/src/video/winrt/SDL_winrtkeyboard.cpp index 02a68dc44..c2b5649da 100644 --- a/src/video/winrt/SDL_winrtkeyboard.cpp +++ b/src/video/winrt/SDL_winrtkeyboard.cpp @@ -148,7 +148,7 @@ SDL_bool WINRT_HasScreenKeyboardSupport(SDL_VideoDevice *_this) return SDL_TRUE; } -void WINRT_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window) +void WINRT_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props) { using namespace Windows::UI::ViewManagement; InputPane ^ inputPane = InputPane::GetForCurrentView(); diff --git a/src/video/x11/SDL_x11keyboard.c b/src/video/x11/SDL_x11keyboard.c index 753abb3c8..dcfcdf8f1 100644 --- a/src/video/x11/SDL_x11keyboard.c +++ b/src/video/x11/SDL_x11keyboard.c @@ -421,7 +421,7 @@ static void X11_ResetXIM(SDL_VideoDevice *_this, SDL_Window *window) #endif } -int X11_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window) +int X11_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props) { X11_ResetXIM(_this, window); @@ -451,7 +451,7 @@ SDL_bool X11_HasScreenKeyboardSupport(SDL_VideoDevice *_this) return videodata->is_steam_deck; } -void X11_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window) +void X11_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props) { SDL_VideoData *videodata = _this->internal; @@ -459,10 +459,33 @@ void X11_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window) /* For more documentation of the URL parameters, see: * https://partner.steamgames.com/doc/api/ISteamUtils#ShowFloatingGamepadTextInput */ + const int k_EFloatingGamepadTextInputModeModeSingleLine = 0; // Enter dismisses the keyboard + const int k_EFloatingGamepadTextInputModeModeMultipleLines = 1; // User needs to explicitly dismiss the keyboard + const int k_EFloatingGamepadTextInputModeModeEmail = 2; // Keyboard is displayed in a special mode that makes it easier to enter emails + const int k_EFloatingGamepadTextInputModeModeNumeric = 3; // Numeric keypad is shown char deeplink[128]; + int mode; + + switch (SDL_GetTextInputType(props)) { + case SDL_TEXTINPUT_TYPE_TEXT_EMAIL: + mode = k_EFloatingGamepadTextInputModeModeEmail; + break; + case SDL_TEXTINPUT_TYPE_NUMBER: + case SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_HIDDEN: + case SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_VISIBLE: + mode = k_EFloatingGamepadTextInputModeModeNumeric; + break; + default: + if (SDL_GetTextInputMultiline(props)) { + mode = k_EFloatingGamepadTextInputModeModeMultipleLines; + } else { + mode = k_EFloatingGamepadTextInputModeModeSingleLine; + } + break; + } (void)SDL_snprintf(deeplink, sizeof(deeplink), "steam://open/keyboard?XPosition=0&YPosition=0&Width=0&Height=0&Mode=%d", - SDL_GetHintBoolean(SDL_HINT_RETURN_KEY_HIDES_IME, SDL_FALSE) ? 0 : 1); + mode); SDL_OpenURL(deeplink); videodata->steam_keyboard_open = SDL_TRUE; } diff --git a/src/video/x11/SDL_x11keyboard.h b/src/video/x11/SDL_x11keyboard.h index 60e96af0b..364e1bc67 100644 --- a/src/video/x11/SDL_x11keyboard.h +++ b/src/video/x11/SDL_x11keyboard.h @@ -26,11 +26,11 @@ extern int X11_InitKeyboard(SDL_VideoDevice *_this); extern void X11_UpdateKeymap(SDL_VideoDevice *_this, SDL_bool send_event); extern void X11_QuitKeyboard(SDL_VideoDevice *_this); -extern int X11_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window); +extern int X11_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props); extern int X11_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window); extern int X11_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window); extern SDL_bool X11_HasScreenKeyboardSupport(SDL_VideoDevice *_this); -extern void X11_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window); +extern void X11_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props); extern void X11_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window); extern SDL_bool X11_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window); extern KeySym X11_KeyCodeToSym(SDL_VideoDevice *_this, KeyCode, unsigned char group, unsigned int mod_mask); diff --git a/test/testime.c b/test/testime.c index 8727c720d..e5ae2e914 100644 --- a/test/testime.c +++ b/test/testime.c @@ -15,6 +15,7 @@ #include #include +#include #ifdef HAVE_SDL_TTF #include "SDL_ttf.h" #endif @@ -40,6 +41,11 @@ #endif #define MAX_TEXT_LENGTH 256 +#define WINDOW_WIDTH 640 +#define WINDOW_HEIGHT 480 + +#define MARGIN 32.0f +#define LINE_HEIGHT (FONT_CHARACTER_SIZE + 4.0f) #define CURSOR_BLINK_INTERVAL_MS 500 typedef struct @@ -47,6 +53,10 @@ typedef struct SDL_Window *window; SDL_Renderer *renderer; int rendererID; + SDL_bool settings_visible; + SDL_Texture *settings_icon; + SDL_FRect settings_rect; + SDL_PropertiesID text_settings; SDL_FRect textRect; SDL_FRect markedRect; char text[MAX_TEXT_LENGTH]; @@ -68,6 +78,33 @@ static const SDL_Color backColor = { 255, 255, 255, 255 }; static const SDL_Color textColor = { 0, 0, 0, 255 }; static SDL_BlendMode highlight_mode; +static const struct +{ + const char *label; + const char *setting; + int value; +} settings[] = { + { "Text", SDL_PROP_TEXTINPUT_TYPE_NUMBER, SDL_TEXTINPUT_TYPE_TEXT }, + { "Name", SDL_PROP_TEXTINPUT_TYPE_NUMBER, SDL_TEXTINPUT_TYPE_TEXT_NAME }, + { "E-mail", SDL_PROP_TEXTINPUT_TYPE_NUMBER, SDL_TEXTINPUT_TYPE_TEXT_EMAIL }, + { "Username", SDL_PROP_TEXTINPUT_TYPE_NUMBER, SDL_TEXTINPUT_TYPE_TEXT_USERNAME }, + { "Password (hidden)", SDL_PROP_TEXTINPUT_TYPE_NUMBER, SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_HIDDEN }, + { "Password (visible)", SDL_PROP_TEXTINPUT_TYPE_NUMBER, SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_VISIBLE }, + { "Number", SDL_PROP_TEXTINPUT_TYPE_NUMBER, SDL_TEXTINPUT_TYPE_NUMBER }, + { "Numeric PIN (hidden)", SDL_PROP_TEXTINPUT_TYPE_NUMBER, SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_HIDDEN }, + { "Numeric PIN (visible)", SDL_PROP_TEXTINPUT_TYPE_NUMBER, SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_VISIBLE }, + { "", NULL }, + { "No capitalization", SDL_PROP_TEXTINPUT_CAPITALIZATION_NUMBER, SDL_CAPITALIZE_NONE }, + { "Capitalize sentences", SDL_PROP_TEXTINPUT_CAPITALIZATION_NUMBER, SDL_CAPITALIZE_SENTENCES }, + { "Capitalize words", SDL_PROP_TEXTINPUT_CAPITALIZATION_NUMBER, SDL_CAPITALIZE_WORDS }, + { "All caps", SDL_PROP_TEXTINPUT_CAPITALIZATION_NUMBER, SDL_CAPITALIZE_LETTERS }, + { "", NULL }, + { "Auto-correct OFF", SDL_PROP_TEXTINPUT_AUTOCORRECT_BOOLEAN, SDL_FALSE }, + { "Auto-correct ON", SDL_PROP_TEXTINPUT_AUTOCORRECT_BOOLEAN, SDL_TRUE }, + { "Multiline OFF", SDL_PROP_TEXTINPUT_MULTILINE_BOOLEAN, SDL_FALSE }, + { "Multiline ON", SDL_PROP_TEXTINPUT_MULTILINE_BOOLEAN, SDL_TRUE } +}; + #ifdef HAVE_SDL_TTF static TTF_Font *font; #else @@ -487,7 +524,9 @@ static void InitInput(WindowState *ctx) ctx->textRect.h = 50.0f; ctx->markedRect = ctx->textRect; - SDL_StartTextInput(ctx->window); + ctx->text_settings = SDL_CreateProperties(); + + SDL_StartTextInputWithProperties(ctx->window, ctx->text_settings); } @@ -692,6 +731,7 @@ static void CleanupVideo(void) SDL_StopTextInput(ctx->window); ClearCandidates(ctx); + SDL_DestroyProperties(ctx->text_settings); } #ifdef HAVE_SDL_TTF TTF_CloseFont(font); @@ -701,11 +741,85 @@ static void CleanupVideo(void) #endif } +static void DrawSettingsButton(WindowState *ctx) +{ + SDL_Renderer *renderer = ctx->renderer; + + SDL_RenderTexture(renderer, ctx->settings_icon, NULL, &ctx->settings_rect); +} + +static void ToggleSettings(WindowState *ctx) +{ + if (ctx->settings_visible) { + ctx->settings_visible = SDL_FALSE; + SDL_StartTextInputWithProperties(ctx->window, ctx->text_settings); + } else { + SDL_StopTextInput(ctx->window); + ctx->settings_visible = SDL_TRUE; + } +} + +static void DrawSettings(WindowState *ctx) +{ + SDL_Renderer *renderer = ctx->renderer; + SDL_FRect checkbox; + int i; + + checkbox.x = MARGIN; + checkbox.y = MARGIN; + checkbox.w = (float)FONT_CHARACTER_SIZE; + checkbox.h = (float)FONT_CHARACTER_SIZE; + + for (i = 0; i < SDL_arraysize(settings); ++i) { + if (settings[i].setting) { + int value = (int)SDL_GetNumberProperty(ctx->text_settings, settings[i].setting, 0); + if (value == settings[i].value) { + SDL_SetRenderDrawColor(renderer, 255, 255, 0, 255); + SDL_RenderFillRect(renderer, &checkbox); + } + SDL_SetRenderDrawColor(renderer, backColor.r, backColor.g, backColor.b, backColor.a); + SDL_RenderRect(renderer, &checkbox); + SDLTest_DrawString(renderer, checkbox.x + checkbox.w + 8.0f, checkbox.y, settings[i].label); + } + checkbox.y += LINE_HEIGHT; + } +} + +static void ClickSettings(WindowState *ctx, float x, float y) +{ + int setting = (int)SDL_floorf((y - MARGIN) / LINE_HEIGHT); + if (setting >= 0 && setting < SDL_arraysize(settings)) { + SDL_SetNumberProperty(ctx->text_settings, settings[setting].setting, settings[setting].value); + } +} + static void RedrawWindow(WindowState *ctx) { SDL_Renderer *renderer = ctx->renderer; int rendererID = ctx->rendererID; SDL_FRect drawnTextRect, cursorRect, underlineRect; + char text[MAX_TEXT_LENGTH]; + + DrawSettingsButton(ctx); + + if (ctx->settings_visible) { + DrawSettings(ctx); + return; + } + + /* Hide the text if it's a password */ + switch ((SDL_TextInputType)SDL_GetNumberProperty(ctx->text_settings, SDL_PROP_TEXTINPUT_TYPE_NUMBER, SDL_TEXTINPUT_TYPE_TEXT)) { + case SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_HIDDEN: + case SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_HIDDEN: { + size_t len = SDL_utf8strlen(ctx->text); + SDL_memset(text, '*', len); + text[len] = '\0'; + break; + } + default: + SDL_strlcpy(text, ctx->text, sizeof(text)); + break; + } SDL_SetRenderDrawColor(renderer, backColor.r, backColor.g, backColor.b, backColor.a); SDL_RenderFillRect(renderer, &ctx->textRect); @@ -716,9 +830,9 @@ static void RedrawWindow(WindowState *ctx) drawnTextRect.w = 0.0f; drawnTextRect.h = UNIFONT_GLYPH_SIZE * UNIFONT_DRAW_SCALE; - if (ctx->text[0]) { + if (text[0]) { #ifdef HAVE_SDL_TTF - SDL_Surface *textSur = TTF_RenderUTF8_Blended(font, ctx->text, textColor); + SDL_Surface *textSur = TTF_RenderUTF8_Blended(font, text, textColor); SDL_Texture *texture; /* Vertically center text */ @@ -732,7 +846,7 @@ static void RedrawWindow(WindowState *ctx) SDL_RenderTexture(renderer, texture, NULL, &drawnTextRect); SDL_DestroyTexture(texture); #else - char *utext = ctx->text; + char *utext = text; Uint32 codepoint; size_t len; SDL_FRect dstrect; @@ -756,13 +870,6 @@ static void RedrawWindow(WindowState *ctx) /* The marked text rectangle is the text area that hasn't been filled by committed text */ ctx->markedRect.x = ctx->textRect.x + drawnTextRect.w; ctx->markedRect.w = ctx->textRect.w - drawnTextRect.w; - if (ctx->markedRect.w < 0) { - /* Stop text input because we cannot hold any more characters */ - SDL_StopTextInput(ctx->window); - return; - } else { - SDL_StartTextInput(ctx->window); - } /* Update the drawn text rectangle for composition text, after the committed text */ drawnTextRect.x += drawnTextRect.w; @@ -974,10 +1081,18 @@ int main(int argc, char *argv[]) WindowState *ctx = &windowstate[i]; SDL_Window *window = state->windows[i]; SDL_Renderer *renderer = state->renderers[i]; + int icon_w = 0, icon_h = 0; + + SDL_SetRenderLogicalPresentation(renderer, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_LOGICAL_PRESENTATION_LETTERBOX, SDL_SCALEMODE_LINEAR); ctx->window = window; ctx->renderer = renderer; ctx->rendererID = i; + ctx->settings_icon = LoadTexture(renderer, "icon.bmp", SDL_TRUE, &icon_w, &icon_h); + ctx->settings_rect.x = (float)WINDOW_WIDTH - icon_w - MARGIN; + ctx->settings_rect.y = MARGIN; + ctx->settings_rect.w = (float)icon_w; + ctx->settings_rect.h = (float)icon_h; InitInput(ctx); @@ -999,6 +1114,23 @@ int main(int argc, char *argv[]) while (SDL_PollEvent(&event)) { SDLTest_CommonEvent(state, &event, &done); switch (event.type) { + case SDL_EVENT_MOUSE_BUTTON_UP: { + SDL_FPoint point; + WindowState *ctx = GetWindowStateForWindowID(event.button.windowID); + if (!ctx) { + break; + } + + SDL_ConvertEventToRenderCoordinates(ctx->renderer, &event); + point.x = event.button.x; + point.y = event.button.y; + if (SDL_PointInRectFloat(&point, &ctx->settings_rect)) { + ToggleSettings(ctx); + } else if (ctx->settings_visible) { + ClickSettings(ctx, point.x, point.y); + } + break; + } case SDL_EVENT_KEY_DOWN: { WindowState *ctx = GetWindowStateForWindowID(event.key.windowID); if (!ctx) {