From 426cd5ed1fb5f2192f4c4d8849cfa58c209eefa8 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Thu, 3 Oct 2024 08:38:40 -0700
Subject: [PATCH] Added simple text input to showfont
---
examples/showfont.c | 137 ++++++++++++++++++++++++++++++----
src/SDL_renderer_textengine.c | 12 +--
src/SDL_surface_textengine.c | 8 +-
src/SDL_ttf.c | 13 +++-
4 files changed, 144 insertions(+), 26 deletions(-)
diff --git a/examples/showfont.c b/examples/showfont.c
index 24ad8fe3..6c6339b8 100644
--- a/examples/showfont.c
+++ b/examples/showfont.c
@@ -237,12 +237,27 @@ static void MoveCursorDown(Scene *scene)
}
}
+static void SetTextFocus(Scene *scene, bool focused)
+{
+ if (!scene->text) {
+ return;
+ }
+
+ scene->textFocus = focused;
+
+ if (focused) {
+ SDL_StartTextInput(scene->window);
+ } else {
+ SDL_StopTextInput(scene->window);
+ }
+}
+
static void HandleTextClick(Scene *scene, float x, float y)
{
TTF_SubString substring;
if (!scene->textFocus) {
- scene->textFocus = true;
+ SetTextFocus(scene, true);
return;
}
@@ -574,7 +589,7 @@ int main(int argc, char *argv[])
if (SDL_PointInRectFloat(&pt, &scene.textRect)) {
HandleTextClick(&scene, pt.x, pt.y);
} else if (scene.textFocus) {
- scene.textFocus = false;
+ SetTextFocus(&scene, false);
} else {
scene.messageRect.x = (event.button.x - text->w/2);
scene.messageRect.y = (event.button.y - text->h/2);
@@ -618,9 +633,9 @@ int main(int argc, char *argv[])
}
break;
case SDLK_C:
- /* Copy to clipboard */
- if (event.key.mod & SDL_KMOD_CTRL) {
- if (scene.text) {
+ if (scene.textFocus) {
+ /* Copy to clipboard */
+ if (event.key.mod & SDL_KMOD_CTRL) {
SDL_SetClipboardText(scene.text->text);
}
}
@@ -688,26 +703,54 @@ int main(int argc, char *argv[])
}
break;
case SDLK_V:
- /* Paste from clipboard */
- if (event.key.mod & SDL_KMOD_CTRL) {
- if (scene.text) {
- TTF_SetTextString(scene.text, SDL_GetClipboardText(), 0);
+ if (scene.textFocus) {
+ if (event.key.mod & SDL_KMOD_CTRL) {
+ /* Paste from clipboard */
+ const char *text = SDL_GetClipboardText();
+ size_t length = SDL_strlen(text);
+ TTF_InsertTextString(scene.text, scene.cursor, text, length);
+ scene.cursor = (int)(scene.cursor + length);
+ }
+ }
+ break;
+ case SDLK_X:
+ if (scene.textFocus) {
+ if (event.key.mod & SDL_KMOD_CTRL) {
+ /* Copy to clipboard and delete text */
+ if (scene.text->text) {
+ SDL_SetClipboardText(scene.text->text);
+ TTF_DeleteTextString(scene.text, 0, -1);
+ }
}
}
break;
case SDLK_LEFT:
if (scene.textFocus) {
- MoveCursorLeft(&scene);
+ if (event.key.mod & SDL_KMOD_CTRL) {
+ /* Move to the beginning of the line (FIXME) */
+ scene.cursor = 0;
+ } else {
+ MoveCursorLeft(&scene);
+ }
}
break;
case SDLK_RIGHT:
if (scene.textFocus) {
- MoveCursorRight(&scene);
+ if (event.key.mod & SDL_KMOD_CTRL) {
+ /* Move to the end of the line (FIXME) */
+ } else {
+ MoveCursorRight(&scene);
+ }
}
break;
case SDLK_UP:
if (scene.textFocus) {
- MoveCursorUp(&scene);
+ if (event.key.mod & SDL_KMOD_CTRL) {
+ /* Move to the beginning of the text */
+ scene.cursor = 0;
+ } else {
+ MoveCursorUp(&scene);
+ }
} else {
/* Increase font size */
ptsize = TTF_GetFontSize(font);
@@ -716,16 +759,75 @@ int main(int argc, char *argv[])
break;
case SDLK_DOWN:
if (scene.textFocus) {
- MoveCursorDown(&scene);
+ if (event.key.mod & SDL_KMOD_CTRL) {
+ /* Move to the end of the text */
+ if (scene.text->text) {
+ scene.cursor = (int)SDL_strlen(scene.text->text);
+ }
+ } else {
+ MoveCursorDown(&scene);
+ }
} else {
/* Decrease font size */
ptsize = TTF_GetFontSize(font);
TTF_SetFontSize(font, ptsize - 1.0f);
}
break;
+ case SDLK_HOME:
+ if (scene.textFocus) {
+ /* Move to the beginning of the text */
+ scene.cursor = 0;
+ }
+ break;
+ case SDLK_END:
+ if (scene.textFocus) {
+ /* Move to the end of the text */
+ if (scene.text->text) {
+ scene.cursor = (int)SDL_strlen(scene.text->text);
+ }
+ }
+ break;
+ case SDLK_BACKSPACE:
+ if (scene.textFocus) {
+ if (event.key.mod & SDL_KMOD_CTRL) {
+ /* Delete to the beginning of the string */
+ TTF_DeleteTextString(scene.text, 0, scene.cursor);
+ scene.cursor = 0;
+ } else if (scene.text->text) {
+ const char *start = &scene.text->text[scene.cursor];
+ const char *current = start;
+ /* Step back over the previous UTF-8 character */
+ do {
+ if (current == scene.text->text) {
+ break;
+ }
+ --current;
+ } while ((*current & 0xC0) == 0x80);
+
+ int length = (int)(start - current);
+ TTF_DeleteTextString(scene.text, scene.cursor - length, length);
+ scene.cursor -= length;
+ }
+ }
+ break;
+ case SDLK_DELETE:
+ if (scene.textFocus) {
+ if (event.key.mod & SDL_KMOD_CTRL) {
+ /* Delete to the end of the string */
+ TTF_DeleteTextString(scene.text, scene.cursor, -1);
+ } else if (scene.text->text) {
+ const char *start = &scene.text->text[scene.cursor];
+ const char *next = start;
+ size_t length = SDL_strlen(next);
+ SDL_StepUTF8(&next, &length);
+ length = (next - start);
+ TTF_DeleteTextString(scene.text, scene.cursor, (int)length);
+ }
+ }
+ break;
case SDLK_ESCAPE:
if (scene.textFocus) {
- scene.textFocus = false;
+ SetTextFocus(&scene, false);
} else {
done = true;
}
@@ -735,6 +837,13 @@ int main(int argc, char *argv[])
}
break;
+ case SDL_EVENT_TEXT_INPUT:
+ if (scene.text) {
+ size_t length = SDL_strlen(event.text.text);
+ TTF_InsertTextString(scene.text, scene.cursor, event.text.text, length);
+ scene.cursor = (int)(scene.cursor + length);
+ }
+ break;
case SDL_EVENT_QUIT:
done = true;
break;
diff --git a/src/SDL_renderer_textengine.c b/src/SDL_renderer_textengine.c
index fbe08408..a7140461 100644
--- a/src/SDL_renderer_textengine.c
+++ b/src/SDL_renderer_textengine.c
@@ -850,9 +850,6 @@ TTF_TextEngine *TTF_CreateRendererTextEngine(SDL_Renderer *renderer)
bool TTF_DrawRendererText(TTF_Text *text, float x, float y)
{
- TTF_RendererTextEngineTextData *data;
- SDL_Renderer *renderer;
-
if (!text || !text->internal || text->internal->engine->CreateText != CreateText) {
return SDL_InvalidParamError("text");
}
@@ -862,8 +859,13 @@ bool TTF_DrawRendererText(TTF_Text *text, float x, float y)
return false;
}
- renderer = ((TTF_RendererTextEngineData *)text->internal->engine->userdata)->renderer;
- data = (TTF_RendererTextEngineTextData *)text->internal->engine_text;
+ TTF_RendererTextEngineTextData *data = (TTF_RendererTextEngineTextData *)text->internal->engine_text;
+ if (!data) {
+ // Empty string, nothing to do
+ return true;
+ }
+
+ SDL_Renderer *renderer = ((TTF_RendererTextEngineData *)text->internal->engine->userdata)->renderer;
AtlasDrawSequence *sequence = data->draw_sequence;
while (sequence) {
float *position = sequence->positions;
diff --git a/src/SDL_surface_textengine.c b/src/SDL_surface_textengine.c
index 2860a514..f8a70754 100644
--- a/src/SDL_surface_textengine.c
+++ b/src/SDL_surface_textengine.c
@@ -326,8 +326,6 @@ static void DrawCopy(TTF_SurfaceTextEngineTextData *data, const TTF_CopyOperatio
bool TTF_DrawSurfaceText(TTF_Text *text, int x, int y, SDL_Surface *surface)
{
- TTF_SurfaceTextEngineTextData *data;
-
if (!text || !text->internal || text->internal->engine->CreateText != CreateText) {
return SDL_InvalidParamError("text");
}
@@ -340,7 +338,11 @@ bool TTF_DrawSurfaceText(TTF_Text *text, int x, int y, SDL_Surface *surface)
return false;
}
- data = (TTF_SurfaceTextEngineTextData *)text->internal->engine_text;
+ TTF_SurfaceTextEngineTextData *data = (TTF_SurfaceTextEngineTextData *)text->internal->engine_text;
+ if (!data) {
+ // Empty string, nothing to do
+ return true;
+ }
if (text->color.r != data->fcolor.r ||
text->color.g != data->fcolor.g ||
diff --git a/src/SDL_ttf.c b/src/SDL_ttf.c
index 54294995..99ffe11d 100644
--- a/src/SDL_ttf.c
+++ b/src/SDL_ttf.c
@@ -4019,17 +4019,21 @@ bool TTF_InsertTextString(TTF_Text *text, int offset, const char *string, size_t
{
TTF_CHECK_POINTER("text", text, false);
- if (!string || !*string || !length) {
+ if (!string || !*string) {
return true;
}
+ if (!length) {
+ length = SDL_strlen(string);
+ }
+
if (!text->text) {
return TTF_SetTextString(text, string, length);
}
int old_length = (int)SDL_strlen(text->text);
- size_t new_length = old_length + length + 1;
- char *new_string = (char *)SDL_realloc(text->text, new_length);
+ size_t new_length = old_length + length;
+ char *new_string = (char *)SDL_realloc(text->text, new_length + 1);
if (!new_string) {
return false;
}
@@ -4047,7 +4051,7 @@ bool TTF_InsertTextString(TTF_Text *text, int offset, const char *string, size_t
int shift = (old_length - offset);
if (shift > 0) {
- SDL_memcpy(new_string + offset + shift, new_string + offset, shift);
+ SDL_memmove(new_string + offset + length, new_string + offset, shift);
}
SDL_memcpy(new_string + offset, string, length);
new_string[new_length] = '\0';
@@ -4177,6 +4181,7 @@ bool SDLCALL TTF_GetTextSubString(TTF_Text *text, int offset, TTF_SubString *sub
}
if (text->internal->num_clusters == 0) {
+ substring->rect.h = text->internal->font->height;
return true;
}