From 4be7c39b59c66d1e7f486a2b45a888ad39eb06a0 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Thu, 3 Oct 2024 21:57:54 -0700
Subject: [PATCH] Refactored editbox functionality out into a separate file
This allows it to be easier to understand and reuse in other projects.
---
CMakeLists.txt | 2 +-
VisualC/showfont/showfont.vcxproj | 10 +-
examples/editbox.c | 599 +++++++++++++++++++++++++++++
examples/editbox.h | 53 +++
examples/showfont.c | 620 ++++++------------------------
5 files changed, 769 insertions(+), 515 deletions(-)
create mode 100644 examples/editbox.c
create mode 100644 examples/editbox.h
diff --git a/CMakeLists.txt b/CMakeLists.txt
index edaf13c4..46a0ca99 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -356,7 +356,7 @@ endif()
if(SDLTTF_SAMPLES)
add_executable(glfont examples/glfont.c)
- add_executable(showfont examples/showfont.c)
+ add_executable(showfont examples/showfont.c examples/editbox.c)
add_executable(testapp examples/testapp.c)
set(OpenGL_GL_PREFERENCE GLVND)
diff --git a/VisualC/showfont/showfont.vcxproj b/VisualC/showfont/showfont.vcxproj
index fbb7e186..bad7b39e 100644
--- a/VisualC/showfont/showfont.vcxproj
+++ b/VisualC/showfont/showfont.vcxproj
@@ -234,14 +234,10 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
- <ClCompile Include="..\..\examples\showfont.c">
- <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
- </ClCompile>
+ <ClCompile Include="..\..\examples\editbox.c"/>
+ <ClCompile Include="..\..\examples\showfont.c"/>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
-</Project>
\ No newline at end of file
+</Project>
diff --git a/examples/editbox.c b/examples/editbox.c
new file mode 100644
index 00000000..22fbef48
--- /dev/null
+++ b/examples/editbox.c
@@ -0,0 +1,599 @@
+/*
+ Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely.
+*/
+#include "editbox.h"
+
+#define CURSOR_BLINK_INTERVAL_MS 500
+
+
+static bool GetHighlightExtents(EditBox *edit, int *marker1, int *marker2)
+{
+ if (edit->highlight1 >= 0 && edit->highlight2 >= 0) {
+ *marker1 = SDL_min(edit->highlight1, edit->highlight2);
+ *marker2 = SDL_max(edit->highlight1, edit->highlight2) - 1;
+ if (*marker2 > *marker1) {
+ return true;
+ }
+ }
+ return false;
+}
+
+EditBox *EditBox_Create(TTF_Text *text, const SDL_FRect *rect)
+{
+ EditBox *edit = (EditBox *)SDL_calloc(1, sizeof(*edit));
+ if (!edit) {
+ return NULL;
+ }
+
+ edit->text = text;
+ edit->font = TTF_GetTextFont(text);
+ edit->rect = *rect;
+ edit->highlight1 = -1;
+ edit->highlight2 = -1;
+
+ return edit;
+}
+
+void EditBox_Destroy(EditBox *edit)
+{
+ if (!edit) {
+ return;
+ }
+
+ SDL_free(edit);
+}
+
+void EditBox_Draw(EditBox *edit, SDL_Renderer *renderer)
+{
+ if (!edit) {
+ return;
+ }
+
+ float x = edit->rect.x;
+ float y = edit->rect.y;
+
+ /* Draw any highlight */
+ int marker1, marker2;
+ if (GetHighlightExtents(edit, &marker1, &marker2)) {
+ TTF_SubString **highlights = TTF_GetTextSubStringsForRange(edit->text, marker1, marker2, NULL);
+ if (highlights) {
+ int i;
+ SDL_SetRenderDrawColor(renderer, 0xCC, 0xCC, 0x00, 0xFF);
+ for (i = 0; highlights[i]; ++i) {
+ SDL_FRect rect;
+ SDL_RectToFRect(&highlights[i]->rect, &rect);
+ rect.x += x;
+ rect.y += y;
+ SDL_RenderFillRect(renderer, &rect);
+ }
+ SDL_free(highlights);
+ }
+ }
+
+ if (edit->window_surface) {
+ /* Flush the renderer so we can draw directly to the window surface */
+ SDL_FlushRenderer(renderer);
+ TTF_DrawSurfaceText(edit->text, (int)x, (int)y, edit->window_surface);
+ } else {
+ TTF_DrawRendererText(edit->text, x, y);
+ }
+
+ /* Draw the cursor */
+ Uint64 now = SDL_GetTicks();
+ if ((now - edit->last_cursor_change) >= CURSOR_BLINK_INTERVAL_MS) {
+ edit->cursor_visible = !edit->cursor_visible;
+ edit->last_cursor_change = now;
+ }
+
+ TTF_SubString cursor;
+ if (edit->cursor_visible && TTF_GetTextSubString(edit->text, edit->cursor, &cursor)) {
+ SDL_FRect cursorRect;
+ if (TTF_GetFontDirection(edit->font) == TTF_DIRECTION_RTL) {
+ cursorRect.x = x + cursor.rect.x + cursor.rect.w;
+ } else {
+ cursorRect.x = x + cursor.rect.x;
+ }
+ cursorRect.y = y + cursor.rect.y;
+ cursorRect.w = 1.0f;
+ cursorRect.h = (float)cursor.rect.h;
+
+ SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0xFF);
+ SDL_RenderFillRect(renderer, &cursorRect);
+ }
+}
+
+static int GetCursorTextIndex(TTF_Font *font, int x, const TTF_SubString *substring)
+{
+ bool round_down;
+ if (TTF_GetFontDirection(font) == TTF_DIRECTION_RTL) {
+ round_down = (x > (substring->rect.x + substring->rect.w / 2));
+ } else {
+ round_down = (x < (substring->rect.x + substring->rect.w / 2));
+ }
+ if (round_down) {
+ /* Start the cursor before the selected text */
+ return substring->offset;
+ } else {
+ /* Place the cursor after the selected text */
+ return substring->offset + substring->length;
+ }
+}
+
+static void MoveCursorIndex(EditBox *edit, int direction)
+{
+ TTF_SubString substring;
+
+ if (direction < 0) {
+ if (TTF_GetTextSubString(edit->text, edit->cursor - 1, &substring)) {
+ edit->cursor = substring.offset;
+ }
+ } else {
+ if (TTF_GetTextSubString(edit->text, edit->cursor, &substring) &&
+ TTF_GetTextSubString(edit->text, substring.offset + substring.length, &substring)) {
+ edit->cursor = substring.offset;
+ }
+ }
+}
+
+void EditBox_MoveCursorLeft(EditBox *edit)
+{
+ if (!edit) {
+ return;
+ }
+
+ if (TTF_GetFontDirection(edit->font) == TTF_DIRECTION_RTL) {
+ MoveCursorIndex(edit, 1);
+ } else {
+ MoveCursorIndex(edit, -1);
+ }
+}
+
+void EditBox_MoveCursorRight(EditBox *edit)
+{
+ if (!edit) {
+ return;
+ }
+
+ if (TTF_GetFontDirection(edit->font) == TTF_DIRECTION_RTL) {
+ MoveCursorIndex(edit, -1);
+ } else {
+ MoveCursorIndex(edit, 1);
+ }
+}
+
+void EditBox_MoveCursorUp(EditBox *edit)
+{
+ if (!edit) {
+ return;
+ }
+
+ TTF_SubString substring;
+ if (TTF_GetTextSubString(edit->text, edit->cursor, &substring)) {
+ int fontHeight = TTF_GetFontHeight(edit->font);
+ int x, y;
+ if (TTF_GetFontDirection(edit->font) == TTF_DIRECTION_RTL) {
+ x = substring.rect.x + substring.rect.w;
+ } else {
+ x = substring.rect.x;
+ }
+ y = substring.rect.y - fontHeight;
+ if (TTF_GetTextSubStringForPoint(edit->text, x, y, &substring)) {
+ edit->cursor = GetCursorTextIndex(edit->font, x, &substring);
+ }
+ }
+}
+
+void EditBox_MoveCursorDown(EditBox *edit)
+{
+ if (!edit) {
+ return;
+ }
+
+ TTF_SubString substring;
+ if (TTF_GetTextSubString(edit->text, edit->cursor, &substring)) {
+ int fontHeight = TTF_GetFontHeight(edit->font);
+ int x, y;
+ if (TTF_GetFontDirection(edit->font) == TTF_DIRECTION_RTL) {
+ x = substring.rect.x + substring.rect.w;
+ } else {
+ x = substring.rect.x;
+ }
+ y = substring.rect.y + substring.rect.h + fontHeight;
+ if (TTF_GetTextSubStringForPoint(edit->text, x, y, &substring)) {
+ edit->cursor = GetCursorTextIndex(edit->font, x, &substring);
+ }
+ }
+}
+
+void EditBox_MoveCursorBeginningOfLine(EditBox *edit)
+{
+ if (!edit) {
+ return;
+ }
+
+ TTF_SubString substring;
+ if (TTF_GetTextSubString(edit->text, edit->cursor, &substring) &&
+ TTF_GetTextSubStringForLine(edit->text, substring.line_index, &substring)) {
+ edit->cursor = substring.offset;
+ }
+}
+
+void EditBox_MoveCursorEndOfLine(EditBox *edit)
+{
+ if (!edit) {
+ return;
+ }
+
+ TTF_SubString substring;
+ if (TTF_GetTextSubString(edit->text, edit->cursor, &substring) &&
+ TTF_GetTextSubStringForLine(edit->text, substring.line_index, &substring)) {
+ edit->cursor = substring.offset + substring.length;
+ }
+}
+
+void EditBox_MoveCursorBeginning(EditBox *edit)
+{
+ if (!edit) {
+ return;
+ }
+
+ /* Move to the beginning of the text */
+ edit->cursor = 0;
+}
+
+void EditBox_MoveCursorEnd(EditBox *edit)
+{
+ if (!edit) {
+ return;
+ }
+
+ /* Move to the end of the text */
+ if (edit->text->text) {
+ edit->cursor = (int)SDL_strlen(edit->text->text);
+ }
+}
+
+void EditBox_Backspace(EditBox *edit)
+{
+ if (!edit || !edit->text->text) {
+ return;
+ }
+
+ if (EditBox_DeleteHighlight(edit)) {
+ return;
+ }
+
+ const char *start = &edit->text->text[edit->cursor];
+ const char *current = start;
+ /* Step back over the previous UTF-8 character */
+ do {
+ if (current == edit->text->text) {
+ break;
+ }
+ --current;
+ } while ((*current & 0xC0) == 0x80);
+
+ int length = (int)(start - current);
+ TTF_DeleteTextString(edit->text, edit->cursor - length, length);
+ edit->cursor -= length;
+}
+
+void EditBox_BackspaceToBeginning(EditBox *edit)
+{
+ if (!edit) {
+ return;
+ }
+
+ /* Delete to the beginning of the string */
+ TTF_DeleteTextString(edit->text, 0, edit->cursor);
+ edit->cursor = 0;
+}
+
+void EditBox_DeleteToEnd(EditBox *edit)
+{
+ if (!edit) {
+ return;
+ }
+
+ /* Delete to the end of the string */
+ TTF_DeleteTextString(edit->text, edit->cursor, -1);
+}
+
+void EditBox_Delete(EditBox *edit)
+{
+ if (!edit || !edit->text->text) {
+ return;
+ }
+
+ if (EditBox_DeleteHighlight(edit)) {
+ return;
+ }
+
+ const char *start = &edit->text->text[edit->cursor];
+ const char *next = start;
+ size_t length = SDL_strlen(next);
+ SDL_StepUTF8(&next, &length);
+ length = (next - start);
+ TTF_DeleteTextString(edit->text, edit->cursor, (int)length);
+}
+
+static bool HandleMouseDown(EditBox *edit, float x, float y)
+{
+ SDL_FPoint pt = { x, y };
+ if (!SDL_PointInRectFloat(&pt, &edit->rect)) {
+ return false;
+ }
+
+ /* Set the cursor position */
+ TTF_SubString substring;
+ int textX = (int)SDL_roundf(x - (edit->rect.x + 4.0f));
+ int textY = (int)SDL_roundf(y - (edit->rect.y + 4.0f));
+ if (!TTF_GetTextSubStringForPoint(edit->text, textX, textY, &substring)) {
+ SDL_Log("Couldn't get cursor location: %s\n", SDL_GetError());
+ return false;
+ }
+
+ edit->cursor = GetCursorTextIndex(edit->font, textX, &substring);
+ edit->highlighting = true;
+ edit->highlight1 = edit->cursor;
+ edit->highlight2 = -1;
+
+ return true;
+}
+
+static bool HandleMouseMotion(EditBox *edit, float x, float y)
+{
+ if (!edit->highlighting) {
+ return false;
+ }
+
+ /* Set the highlight position */
+ TTF_SubString substring;
+ int textX = (int)SDL_roundf(x - (edit->rect.x + 4.0f));
+ int textY = (int)SDL_roundf(y - (edit->rect.y + 4.0f));
+ if (!TTF_GetTextSubStringForPoint(edit->text, textX, textY, &substring)) {
+ SDL_Log("Couldn't get cursor location: %s\n", SDL_GetError());
+ return false;
+ }
+
+ edit->cursor = GetCursorTextIndex(edit->font, textX, &substring);
+ edit->highlight2 = edit->cursor;
+
+ return true;
+}
+
+static bool HandleMouseUp(EditBox *edit, float x, float y)
+{
+ (void)x; (void)y;
+
+ if (!edit->highlighting) {
+ return false;
+ }
+
+ edit->highlighting = false;
+ return true;
+}
+
+void EditBox_SelectAll(EditBox *edit)
+{
+ if (!edit || !edit->text->text) {
+ return;
+ }
+
+ edit->highlight1 = 0;
+ edit->highlight2 = (int)SDL_strlen(edit->text->text);
+}
+
+bool EditBox_DeleteHighlight(EditBox *edit)
+{
+ if (!edit || !edit->text->text) {
+ return false;
+ }
+
+ int marker1, marker2;
+ if (GetHighlightExtents(edit, &marker1, &marker2)) {
+ size_t length = marker2 - marker1 + 1;
+ TTF_DeleteTextString(edit->text, marker1, (int)length);
+ edit->cursor = marker1;
+ edit->highlight1 = -1;
+ edit->highlight2 = -1;
+ return true;
+ }
+ return false;
+}
+
+void EditBox_Copy(EditBox *edit)
+{
+ if (!edit || !edit->text->text) {
+ return;
+ }
+
+ int marker1, marker2;
+ if (GetHighlightExtents(edit, &marker1, &marker2)) {
+ size_t length = marker2 - marker1 + 1;
+ char *temp = (char *)SDL_malloc(length + 1);
+ if (temp) {
+ SDL_memcpy(temp, &edit->text->text[marker1], length);
+ temp[length] = '\0';
+ SDL_SetClipboardText(temp);
+ SDL_free(temp);
+ }
+ } else {
+ SDL_SetClipboardText(edit->text->text);
+ }
+}
+
+void EditBox_Cut(EditBox *edit)
+{
+ if (!edit || !edit->text->text) {
+ return;
+ }
+
+ /* Copy to clipboard and delete text */
+ int marker1, marker2;
+ if (GetHighlightExtents(edit, &marker1, &marker2)) {
+ size_t length = marker2 - marker1 + 1;
+ char *temp = (char *)SDL_malloc(length + 1);
+ if (temp) {
+ SDL_memcpy(temp, &edit->text->text[marker1], length);
+ temp[length] = '\0';
+ SDL_SetClipboardText(edit->text->text);
+ SDL_free(temp);
+ }
+ TTF_DeleteTextString(edit->text, marker1, (int)length);
+ edit->cursor = marker1;
+ edit->highlight1 = -1;
+ edit->highlight2 = -1;
+ } else {
+ SDL_SetClipboardText(edit->text->text);
+ TTF_DeleteTextString(edit->text, 0, -1);
+ }
+}
+
+void EditBox_Paste(EditBox *edit)
+{
+ if (!edit) {
+ return;
+ }
+
+ const char *text = SDL_GetClipboardText();
+ size_t length = SDL_strlen(text);
+ TTF_InsertTextString(edit->text, edit->cursor, text, length);
+ edit->cursor = (int)(edit->cursor + length);
+}
+
+void EditBox_Insert(EditBox *edit, const char *text)
+{
+ if (!edit || !text) {
+ return;
+ }
+
+ size_t length = SDL_strlen(text);
+ TTF_InsertTextString(edit->text, edit->cursor, text, length);
+ edit->cursor = (int)(edit->cursor + length);
+}
+
+bool EditBox_HandleEvent(EditBox *edit, SDL_Event *event)
+{
+ if (!edit || !event) {
+ return false;
+ }
+
+ switch (event->type) {
+ case SDL_EVENT_MOUSE_BUTTON_DOWN:
+ return HandleMouseDown(edit, event->button.x, event->button.y);
+
+ case SDL_EVENT_MOUSE_MOTION:
+ return HandleMouseMotion(edit, event->motion.x, event->motion.y);
+
+ case SDL_EVENT_MOUSE_BUTTON_UP:
+ return HandleMouseUp(edit, event->button.x, event->button.y);
+
+ case SDL_EVENT_KEY_DOWN:
+ switch (event->key.key) {
+ case SDLK_A:
+ if (event->key.mod & SDL_KMOD_CTRL) {
+ EditBox_SelectAll(edit);
+ return true;
+ }
+ break;
+ case SDLK_C:
+ if (event->key.mod & SDL_KMOD_CTRL) {
+ EditBox_Copy(edit);
+ return true;
+ }
+ break;
+
+ case SDLK_V:
+ if (event->key.mod & SDL_KMOD_CTRL) {
+ EditBox_Paste(edit);
+ return true;
+ }
+ break;
+
+ case SDLK_X:
+ if (event->key.mod & SDL_KMOD_CTRL) {
+ EditBox_Cut(edit);
+ return true;
+ }
+ break;
+
+ case SDLK_LEFT:
+ if (event->key.mod & SDL_KMOD_CTRL) {
+ EditBox_MoveCursorBeginningOfLine(edit);
+ } else {
+ EditBox_MoveCursorLeft(edit);
+ }
+ return true;
+
+ case SDLK_RIGHT:
+ if (event->key.mod & SDL_KMOD_CTRL) {
+ EditBox_MoveCursorEndOfLine(edit);
+ } else {
+ EditBox_MoveCursorRight(edit);
+ }
+ return true;
+
+ case SDLK_UP:
+ if (event->key.mod & SDL_KMOD_CTRL) {
+ EditBox_MoveCursorBeginning(edit);
+ } else {
+ EditBox_MoveCursorUp(edit);
+ }
+ return true;
+
+ case SDLK_DOWN:
+ if (event->key.mod & SDL_KMOD_CTRL) {
+ EditBox_MoveCursorEnd(edit);
+ } else {
+ EditBox_MoveCursorDown(edit);
+ }
+ return true;
+
+ case SDLK_HOME:
+ EditBox_MoveCursorBeginning(edit);
+ return true;
+
+ case SDLK_END:
+ EditBox_MoveCursorEnd(edit);
+ return true;
+
+ case SDLK_BACKSPACE:
+ if (event->key.mod & SDL_KMOD_CTRL) {
+ EditBox_BackspaceToBeginning(edit);
+ } else {
+ EditBox_Backspace(edit);
+ }
+ return true;
+
+ case SDLK_DELETE:
+ if (event->key.mod & SDL_KMOD_CTRL) {
+ EditBox_DeleteToEnd(edit);
+ } else {
+ EditBox_Delete(edit);
+ }
+ return true;
+
+ default:
+ break;
+ }
+ break;
+
+ case SDL_EVENT_TEXT_INPUT:
+ EditBox_Insert(edit, event->text.text);
+ return true;
+
+ default:
+ break;
+ }
+ return false;
+}
+
diff --git a/examples/editbox.h b/examples/editbox.h
new file mode 100644
index 00000000..cb4fdc46
--- /dev/null
+++ b/examples/editbox.h
@@ -0,0 +1,53 @@
+/*
+ Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely.
+*/
+#include <SDL3/SDL.h>
+#include <SDL3_ttf/SDL_ttf.h>
+
+
+typedef struct EditBox {
+ TTF_Font *font;
+ TTF_Text *text;
+ SDL_FRect rect;
+ int cursor;
+ bool cursor_visible;
+ Uint64 last_cursor_change;
+ bool highlighting;
+ int highlight1, highlight2;
+
+ // Used for testing the software rendering implementation
+ SDL_Surface *window_surface;
+} EditBox;
+
+
+extern EditBox *EditBox_Create(TTF_Text *text, const SDL_FRect *rect);
+extern void EditBox_Destroy(EditBox *edit);
+extern void EditBox_Draw(EditBox *edit, SDL_Renderer *renderer);
+extern void EditBox_MoveCursorLeft(EditBox *edit);
+extern void EditBox_MoveCursorRight(EditBox *edit);
+extern void EditBox_MoveCursorUp(EditBox *edit);
+extern void EditBox_MoveCursorDown(EditBox *edit);
+extern void EditBox_MoveCursorBeginningOfLine(EditBox *edit);
+extern void EditBox_MoveCursorEndOfLine(EditBox *edit);
+extern void EditBox_MoveCursorBeginning(EditBox *edit);
+extern void EditBox_MoveCursorEnd(EditBox *edit);
+extern void EditBox_Backspace(EditBox *edit);
+extern void EditBox_BackspaceToBeginning(EditBox *edit);
+extern void EditBox_DeleteToEnd(EditBox *edit);
+extern void EditBox_Delete(EditBox *edit);
+extern void EditBox_SelectAll(EditBox *edit);
+extern bool EditBox_DeleteHighlight(EditBox *edit);
+extern void EditBox_Copy(EditBox *edit);
+extern void EditBox_Cut(EditBox *edit);
+extern void EditBox_Paste(EditBox *edit);
+extern void EditBox_Insert(EditBox *edit, const char *text);
+extern bool EditBox_HandleEvent(EditBox *edit, SDL_Event *event);
+
diff --git a/examples/showfont.c b/examples/showfont.c
index cf87685c..20e0c837 100644
--- a/examples/showfont.c
+++ b/examples/showfont.c
@@ -26,8 +26,8 @@
#include <SDL3_ttf/SDL_ttf.h>
#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
+
+#include "editbox.h"
#define DEFAULT_PTSIZE 18.0f
#define DEFAULT_TEXT "The quick brown fox jumped over the lazy dog"
@@ -37,7 +37,6 @@
//#define DEFAULT_TEXT "\xe5\xad\xa6\xe4\xb9\xa0\xe6\x9f\x90\xe8\xaf\xbe\xe7\xa8\x8b\xe5\xbf\x85\xe8\xaf\xbb\xe7\x9a\x84"
#define WIDTH 640
#define HEIGHT 480
-#define CURSOR_BLINK_INTERVAL_MS 500
#define TTF_SHOWFONT_USAGE \
@@ -69,25 +68,9 @@ typedef struct {
TTF_Text *text;
SDL_FRect textRect;
bool textFocus;
- int cursor;
- bool cursorVisible;
- Uint64 lastCursorChange;
- bool highlighting;
- int highlight1, highlight2;
+ EditBox *edit;
} Scene;
-static bool GetHighlightExtents(Scene *scene, int *marker1, int *marker2)
-{
- if (scene->highlight1 >= 0 && scene->highlight2 >= 0) {
- *marker1 = SDL_min(scene->highlight1, scene->highlight2);
- *marker2 = SDL_max(scene->highlight1, scene->highlight2) - 1;
- if (*marker2 > *marker1) {
- return true;
- }
- }
- return false;
-}
-
static void DrawScene(Scene *scene)
{
SDL_Renderer *renderer = scene->renderer;
@@ -97,9 +80,6 @@ static void DrawScene(Scene *scene)
SDL_RenderClear(renderer);
if (scene->text) {
- float x = scene->textRect.x + 4.0f;
- float y = scene->textRect.y + 4.0f;
-
/* Clear the text rect to light gray */
SDL_SetRenderDrawColor(renderer, 0xCC, 0xCC, 0xCC, 0xFF);
SDL_RenderFillRect(renderer, &scene->textRect);
@@ -114,60 +94,7 @@ static void DrawScene(Scene *scene)
SDL_RenderRect(renderer, &focusRect);
}
- int marker1, marker2;
- if (GetHighlightExtents(scene, &marker1, &marker2)) {
- TTF_SubString **highlights = TTF_GetTextSubStringsForRange(scene->text, marker1, marker2, NULL);
- if (highlights) {
- int i;
- SDL_SetRenderDrawColor(renderer, 0xCC, 0xCC, 0x00, 0xFF);
- for (i = 0; highlights[i]; ++i) {
- SDL_FRect rect;
- SDL_RectToFRect(&highlights[i]->rect, &rect);
- rect.x += x;
- rect.y += y;
- SDL_RenderFillRect(renderer, &rect);
- }
- SDL_free(highlights);
- }
- }
-
- switch (scene->textEngine) {
- case TextEngineSurface:
- /* Flush the renderer so we can draw directly to the window surface */
- SDL_FlushRenderer(renderer);
- TTF_DrawSurfaceText(scene->text, (int)x, (int)y, scene->window_surface);
- break;
- case TextEngineRenderer:
- TTF_DrawRendererText(scene->text, x, y);
- break;
- default:
- break;
- }
-
- if (scene->textFocus) {
- /* Draw the cursor */
- Uint64 now = SDL_GetTicks();
- if ((now - scene->lastCursorChange) >= CURSOR_BLINK_INTERVAL_MS) {
- scene->cursorVisible = !scene->cursorVisible;
- scene->lastCursorChange = now;
- }
-
- TTF_SubString cursor;
- if (scene->cursorVisible && TTF_GetTextSubString(scene->text, scene->cursor, &cursor)) {
- SDL_FRect cursorRect;
- if (TTF_GetFontDirection(scene->font) == TTF_DIRECTION_RTL) {
- cursorRect.x = x + cursor.rect.x + cursor.rect.w;
- } else {
- cursorRect.x = x + cursor.rect.x;
- }
- cursorRect.y = y + cursor.rect.y;
- cursorRect.w = 1.0f;
- cursorRect.h = (float)cursor.rect.h;
-
- SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0xFF);
- SDL_RenderFillRect(renderer, &cursorRect);
- }
- }
+ EditBox_Draw(scene->edit, renderer);
}
SDL_RenderTexture(renderer, scene->caption, NULL, &scene->captionRect);
@@ -179,95 +106,6 @@ static void DrawScene(Scene *scene)
}
}
-static int GetCursorTextIndex(TTF_Font *font, int x, const TTF_SubString *substring)
-{
- bool round_down;
- if (TTF_GetFontDirection(font) == TTF_DIRECTION_RTL) {
- round_down = (x > (substring->rect.x + substring->rect.w / 2));
- } else {
- round_down = (x < (substring->rect.x + substring->rect.w / 2));
- }
- if (round_down) {
- /* Start the cursor before the selected text */
- return substring->offset;
- } else {
- /* Place the cursor after the selected text */
- return substring->offset + substring->length;
- }
-}
-
-static void MoveCursorIndex(Scene *scene, int direction)
-{
- TTF_SubString substring;
-
- if (direction < 0) {
- if (TTF_GetTextSubString(scene->text, scene->cursor - 1, &substring)) {
- scene->cursor = substring.offset;
- }
- } else {
- if (TTF_GetTextSubString(scene->text, scene->cursor, &substring) &&
- TTF_GetTextSubString(scene->text, substring.offset + substring.length, &substring)) {
- scene->cursor = substring.offset;
- }
- }
-}
-
-static void MoveCursorLeft(Scene *scene)
-{
- if (TTF_GetFontDirection(scene->font) == TTF_DIRECTION_RTL) {
- MoveCursorIndex(scene, 1);
- } else {
- MoveCursorIndex(scene, -1);
- }
-}
-
-static void MoveCursorRight(Scene *scene)
-{
- if (TTF_GetFontDirection(scene->font) == TTF_DIRECTION_RTL) {
- MoveCursorIndex(scene, -1);
- } else {
- MoveCursorIndex(scene, 1);
- }
-}
-
-static void MoveCursorUp(Scene *scene)
-{
- TTF_SubString substring;
-
- if (TTF_GetTextSubString(scene->text, scene->cursor, &substring)) {
- int fontHeight = TTF_GetFontHeight(scene->font);
- int x, y;
- if (TTF_GetFontDirection(scene->font) == TTF_DIRECTION_RTL) {
- x = substring.rect.x + substring.rect.w;
- } else {
- x = substring.rect.x;
- }
- y = substring.rect.y - fontHeight;
- if (TTF_GetTextSubStringForPoint(scene->text, x, y, &substring)) {
- scene->cursor = GetCursorTextIndex(scene->font, x, &substring);
- }
- }
-}
-
-static void MoveCursorDown(Scene *scene)
-{
- TTF_SubString substring;
-
- if (TTF_GetTextSubString(scene->text, scene->cursor, &substring)) {
- int fontHeight = TTF_GetFontHeight(scene->font);
- int x, y;
- if (TTF_GetFontDirection(scene->font) == TTF_DIRECTION_RTL) {
- x = substring.rect.x + substring.rect.w;
- } else {
- x = substring.rect.x;
- }
- y = substring.rect.y + substring.rect.h + fontHeight;
- if (TTF_GetTextSubStringForPoint(scene->text, x, y, &substring)) {
- scene->cursor = GetCursorTextIndex(scene->font, x, &substring);
- }
- }
-}
-
static void SetTextFocus(Scene *scene, bool focused)
{
if (!scene->text) {
@@ -281,60 +119,100 @@ static void SetTextFocus(Scene *scene, bool focused)
} else {
SDL_StopTextInput(scene->window);
}
-
- /* Reset the highlight */
- scene->highlighting = false;
- scene->highlight1 = -1;
- scene->highlight2 = -1;
}
-static void HandleTextMouseDown(Scene *scene, float x, float y)
+static void HandleKeyDown(Scene *scene, SDL_Event *event)
{
- if (!scene->textFocus) {
- SetTextFocus(scene, true);
- return;
- }
-
- /* Set the cursor position */
- TTF_SubString substring;
- int textX = (int)SDL_roundf(x - (scene->textRect.x + 4.0f));
- int textY = (int)SDL_roundf(y - (scene->textRect.y + 4.0f));
- if (!TTF_GetTextSubStringForPoint(scene->text, textX, textY, &substring)) {
- SDL_Log("Couldn't get cursor location: %s\n", SDL_GetError());
- return;
- }
+ int style, outline;
+
+ switch (event->key.key) {
+ case SDLK_A:
+ /* Cycle alignment */
+ switch (TTF_GetFontWrapAlignment(scene->font)) {
+ case TTF_HORIZONTAL_ALIGN_LEFT:
+ TTF_SetFontWrapAlignment(scene->font, TTF_HORIZONTAL_ALIGN_CENTER);
+ break;
+ case TTF_HORIZONTAL_ALIGN_CENTER:
+ TTF_SetFontWrapAlignment(scene->font, TTF_HORIZONTAL_ALIGN_RIGHT);
+ break;
+ case TTF_HORIZONTAL_ALIGN_RIGHT:
+ TTF_SetFontWrapAlignment(scene->font, TTF_HORIZONTAL_ALIGN_LEFT);
+ break;
+ default:
+ SDL_Log("Unknown wrap alignment: %d\n", TTF_GetFontWrapAlignment(scene->font));
+ break;
+ }
+ break;
- scene->cursor = GetCursorTextIndex(scene->font, textX, &substring);
- scene->highlighting = true;
- scene->highlight1 = scene->cursor;
- scene->highlight2 = -1;
-}
+ case SDLK_B:
+ /* Toggle bold style */
+ style = TTF_GetFontStyle(scene->font);
+ if (style & TTF_STYLE_BOLD) {
+ style &= ~TTF_STYLE_BOLD;
+ } else {
+ style |= TTF_STYLE_BOLD;
+ }
+ TTF_SetFontStyle(scene->font, style);
+ break;
-static void HandleTextMouseMotion(Scene *scene, float x, float y)
-{
- if (!scene->highlighting) {
- return;
- }
+ case SDLK_I:
+ /* Toggle italic style */
+ style = TTF_GetFontStyle(scene->font);
+ if (style & TTF_STYLE_ITALIC) {
+ style &= ~TTF_STYLE_ITALIC;
+ } else {
+ style |= TTF_STYLE_ITALIC;
+ }
+ TTF_SetFontStyle(scene->font, style);
+ break;
- /* Set the highlight position */
- TTF_SubString substring;
- int textX = (int)SDL_roundf(x - (scene->textRect.x + 4.0f));
- int textY = (int)SDL_roundf(y - (scene->textRect.y + 4.0f));
- if (!TTF_GetTextSubStringForPoint(scene->text, textX, textY, &substring)) {
- SDL_Log("Couldn't get cursor location: %s\n", SDL_GetError());
- return;
- }
+ case SDLK_O:
+ /* Toggle scene->font outline */
+ outline = TTF_GetFontOutline(scene->font);
+ if (outline) {
+ outline = 0;
+ } else {
+ outline = 1;
+ }
+ TTF_SetFontOutline(scene->font, outline);
+ break;
- scene->cursor = GetCursorTextIndex(scene->font, textX, &substring);
- scene->highlight2 = scene->cursor;
-}
+ case SDLK_R:
+ /* Toggle layout direction */
+ if (TTF_GetFontDirection(scene->font) == TTF_DIRECTION_LTR) {
+ TTF_SetFontDirection(scene->font, TTF_DIRECTION_RTL);
+ } else if (TTF_GetFontDirection(scene->font) == TTF_DIRECTION_RTL) {
+ TTF_SetFontDirection(scene->font, TTF_DIRECTION_LTR);
+ } else if (TTF_GetFontDirection(scene->font) == TTF_DIRECTION_TT
(Patch may be truncated, please check the link at the top of this post.)