From b9b9bf133dadd6677926ecda2d31b5ace838559c Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sat, 19 Oct 2024 12:55:27 -0700
Subject: [PATCH] Added TTF_SetTextPosition() and TTF_GetTextPosition()
This allows you to lay out multiple text objects within a single text area.
Also switched text objects to always wrap on newlines.
---
examples/showfont.c | 42 +++++-
include/SDL3_ttf/SDL_textengine.h | 2 +
include/SDL3_ttf/SDL_ttf.h | 145 ++++++++++++++++---
src/SDL_ttf.c | 227 ++++++++++++------------------
src/SDL_ttf.sym | 2 +
5 files changed, 262 insertions(+), 156 deletions(-)
diff --git a/examples/showfont.c b/examples/showfont.c
index fe804f49..fda33db4 100644
--- a/examples/showfont.c
+++ b/examples/showfont.c
@@ -105,6 +105,16 @@ static void DrawScene(Scene *scene)
}
}
+static void AdjustTextOffset(TTF_Text *text, int xoffset, int yoffset)
+{
+ int x, y;
+
+ TTF_GetTextPosition(text, &x, &y);
+ x += xoffset;
+ y += yoffset;
+ TTF_SetTextPosition(text, x, y);
+}
+
static void HandleKeyDown(Scene *scene, SDL_Event *event)
{
int style, outline;
@@ -197,16 +207,36 @@ static void HandleKeyDown(Scene *scene, SDL_Event *event)
TTF_SetFontStyle(scene->font, style);
break;
+ case SDLK_LEFT:
+ if (event->key.mod & SDL_KMOD_CTRL) {
+ AdjustTextOffset(scene->edit->text, -1, 0);
+ }
+ break;
+
+ case SDLK_RIGHT:
+ if (event->key.mod & SDL_KMOD_CTRL) {
+ AdjustTextOffset(scene->edit->text, 1, 0);
+ }
+ break;
+
case SDLK_UP:
- /* Increase font size */
- ptsize = TTF_GetFontSize(scene->font);
- TTF_SetFontSize(scene->font, ptsize + 1.0f);
+ if (event->key.mod & SDL_KMOD_CTRL) {
+ AdjustTextOffset(scene->edit->text, 0, -1);
+ } else {
+ /* Increase font size */
+ ptsize = TTF_GetFontSize(scene->font);
+ TTF_SetFontSize(scene->font, ptsize + 1.0f);
+ }
break;
case SDLK_DOWN:
- /* Decrease font size */
- ptsize = TTF_GetFontSize(scene->font);
- TTF_SetFontSize(scene->font, ptsize - 1.0f);
+ if (event->key.mod & SDL_KMOD_CTRL) {
+ AdjustTextOffset(scene->edit->text, 0, 1);
+ } else {
+ /* Decrease font size */
+ ptsize = TTF_GetFontSize(scene->font);
+ TTF_SetFontSize(scene->font, ptsize - 1.0f);
+ }
break;
case SDLK_ESCAPE:
diff --git a/include/SDL3_ttf/SDL_textengine.h b/include/SDL3_ttf/SDL_textengine.h
index 0a50c704..99222e95 100644
--- a/include/SDL3_ttf/SDL_textengine.h
+++ b/include/SDL3_ttf/SDL_textengine.h
@@ -109,6 +109,8 @@ struct TTF_TextData
bool needs_layout_update; /**< True if the layout needs to be updated */
TTF_TextLayout *layout; /**< Cached layout information, read-only. */
+ int x; /**< The x offset of the upper left corner of this text, in pixels, read-only. */
+ int y; /**< The y offset of the upper left corner of this text, in pixels, read-only. */
int w; /**< The width of this text, in pixels, read-only. */
int h; /**< The height of this text, in pixels, read-only. */
int num_ops; /**< The number of drawing operations to render this text, read-only. */
diff --git a/include/SDL3_ttf/SDL_ttf.h b/include/SDL3_ttf/SDL_ttf.h
index e4dd12c2..19d73539 100644
--- a/include/SDL3_ttf/SDL_ttf.h
+++ b/include/SDL3_ttf/SDL_ttf.h
@@ -1472,12 +1472,7 @@ extern SDL_DECLSPEC void SDLCALL TTF_DestroyRendererTextEngine(TTF_TextEngine *e
/**
* Create a text object from UTF-8 text and a text engine.
*
- * This will not word-wrap the string; you'll get a surface with a single line
- * of text, as long as the string requires. You can use
- * TTF_CreateText_Wrapped() instead if you need to wrap the output to multiple
- * lines.
- *
- * This will not wrap on newline characters.
+ * This function is equivalent to `TTF_CreateText_Wrapped(engine, font, text, 0)` and will wrap on newline characters.
*
* \param engine the text engine to use when creating the text object, may be
* NULL.
@@ -1534,7 +1529,10 @@ extern SDL_DECLSPEC TTF_Text * SDLCALL TTF_CreateText_Wrapped(TTF_TextEngine *en
* \returns a valid property ID on success or 0 on failure; call
* SDL_GetError() for more information.
*
- * \since This function is available since SDL 3.0.0.
+ * \threadsafety This function should be called on the thread that created the
+ * text.
+ *
+ * \since This function is available since SDL_ttf 3.0.0.
*/
extern SDL_DECLSPEC SDL_PropertiesID SDLCALL TTF_GetTextProperties(TTF_Text *text);
@@ -1545,6 +1543,11 @@ extern SDL_DECLSPEC SDL_PropertiesID SDLCALL TTF_GetTextProperties(TTF_Text *tex
* \param engine the text engine to use for drawing.
* \returns true on success or false on failure; call SDL_GetError() for more
* information.
+ *
+ * \threadsafety This function should be called on the thread that created the
+ * text.
+ *
+ * \since This function is available since SDL_ttf 3.0.0.
*/
extern SDL_DECLSPEC bool SDLCALL TTF_SetTextEngine(TTF_Text *text, TTF_TextEngine *engine);
@@ -1554,6 +1557,11 @@ extern SDL_DECLSPEC bool SDLCALL TTF_SetTextEngine(TTF_Text *text, TTF_TextEngin
* \param text the TTF_Text to query.
* \returns the TTF_TextEngine used by the text on success or NULL on failure;
* call SDL_GetError() for more information.
+ *
+ * \threadsafety This function should be called on the thread that created the
+ * text.
+ *
+ * \since This function is available since SDL_ttf 3.0.0.
*/
extern SDL_DECLSPEC TTF_TextEngine * SDLCALL TTF_GetTextEngine(TTF_Text *text);
@@ -1566,6 +1574,11 @@ extern SDL_DECLSPEC TTF_TextEngine * SDLCALL TTF_GetTextEngine(TTF_Text *text);
*
* \param text the TTF_Text to modify.
* \param font the font to use, may be NULL.
+ *
+ * \threadsafety This function should be called on the thread that created the
+ * text.
+ *
+ * \since This function is available since SDL_ttf 3.0.0.
*/
extern SDL_DECLSPEC bool SDLCALL TTF_SetTextFont(TTF_Text *text, TTF_Font *font);
@@ -1575,9 +1588,44 @@ extern SDL_DECLSPEC bool SDLCALL TTF_SetTextFont(TTF_Text *text, TTF_Font *font)
* \param text the TTF_Text to query.
* \returns the TTF_Font used by the text on success or NULL on failure; call
* SDL_GetError() for more information.
+ *
+ * \threadsafety This function should be called on the thread that created the
+ * text.
+ *
+ * \since This function is available since SDL_ttf 3.0.0.
*/
extern SDL_DECLSPEC TTF_Font * SDLCALL TTF_GetTextFont(TTF_Text *text);
+/**
+ * Set the position of a text object.
+ *
+ * This can be used to position multiple text objects within a single wrapping text area.
+ *
+ * \param text the TTF_Text to modify.
+ * \param x the x offset of the upper left corner of this text in pixels.
+ * \param y the y offset of the upper left corner of this text in pixels.
+ *
+ * \threadsafety This function should be called on the thread that created the
+ * text.
+ *
+ * \since This function is available since SDL_ttf 3.0.0.
+ */
+extern SDL_DECLSPEC bool SDLCALL TTF_SetTextPosition(TTF_Text *text, int x, int y);
+
+/**
+ * Get the position of a text object.
+ *
+ * \param text the TTF_Text to query.
+ * \param x a pointer filled in with the x offset of the upper left corner of this text in pixels, may be NULL.
+ * \param y a pointer filled in with the y offset of the upper left corner of this text in pixels, may be NULL.
+ *
+ * \threadsafety This function should be called on the thread that created the
+ * text.
+ *
+ * \since This function is available since SDL_ttf 3.0.0.
+ */
+extern SDL_DECLSPEC bool SDLCALL TTF_GetTextPosition(TTF_Text *text, int *x, int *y);
+
/**
* Set the UTF-8 text used by a text object.
*
@@ -1587,6 +1635,11 @@ extern SDL_DECLSPEC TTF_Font * SDLCALL TTF_GetTextFont(TTF_Text *text);
* text.
* \returns true on success or false on failure; call SDL_GetError() for more
* information.
+ *
+ * \threadsafety This function should be called on the thread that created the
+ * text.
+ *
+ * \since This function is available since SDL_ttf 3.0.0.
*/
extern SDL_DECLSPEC bool SDLCALL TTF_SetTextString(TTF_Text *text, const char *string, size_t length);
@@ -1603,6 +1656,11 @@ extern SDL_DECLSPEC bool SDLCALL TTF_SetTextString(TTF_Text *text, const char *s
* text.
* \returns true on success or false on failure; call SDL_GetError() for more
* information.
+ *
+ * \threadsafety This function should be called on the thread that created the
+ * text.
+ *
+ * \since This function is available since SDL_ttf 3.0.0.
*/
extern SDL_DECLSPEC bool SDLCALL TTF_InsertTextString(TTF_Text *text, int offset, const char *string, size_t length);
@@ -1615,6 +1673,11 @@ extern SDL_DECLSPEC bool SDLCALL TTF_InsertTextString(TTF_Text *text, int offset
* text.
* \returns true on success or false on failure; call SDL_GetError() for more
* information.
+ *
+ * \threadsafety This function should be called on the thread that created the
+ * text.
+ *
+ * \since This function is available since SDL_ttf 3.0.0.
*/
extern SDL_DECLSPEC bool SDLCALL TTF_AppendTextString(TTF_Text *text, const char *string, size_t length);
@@ -1630,6 +1693,11 @@ extern SDL_DECLSPEC bool SDLCALL TTF_AppendTextString(TTF_Text *text, const char
* remainder of the string.
* \returns true on success or false on failure; call SDL_GetError() for more
* information.
+ *
+ * \threadsafety This function should be called on the thread that created the
+ * text.
+ *
+ * \since This function is available since SDL_ttf 3.0.0.
*/
extern SDL_DECLSPEC bool SDLCALL TTF_DeleteTextString(TTF_Text *text, int offset, int length);
@@ -1637,28 +1705,33 @@ extern SDL_DECLSPEC bool SDLCALL TTF_DeleteTextString(TTF_Text *text, int offset
* Set whether wrapping is enabled on a text object.
*
* \param text the TTF_Text to modify.
- * \param wrap true if wrapping should be enabled, false if it should be
- * disabled.
* \param wrapLength the maximum width in pixels, 0 to wrap on newline
- * characters, or -1 to leave wrapLength unchanged.
+ * characters.
* \returns true on success or false on failure; call SDL_GetError() for more
* information.
+ *
+ * \threadsafety This function should be called on the thread that created the
+ * text.
+ *
+ * \since This function is available since SDL_ttf 3.0.0.
*/
-extern SDL_DECLSPEC bool SDLCALL TTF_SetTextWrapping(TTF_Text *text, bool wrap, int wrapLength);
+extern SDL_DECLSPEC bool SDLCALL TTF_SetTextWrapping(TTF_Text *text, int wrapLength);
/**
* Get whether wrapping is enabled on a text object.
*
* \param text the TTF_Text to query.
- * \param wrap a pointer filled in with true if wrapping is enabled, false if
- * it is disabled, may be NULL.
* \param wrapLength a pointer filled in with the maximum width in pixels or 0
- * if the text is being wrapped on newline characters, may
- * be NULL.
+ * if the text is being wrapped on newline characters.
* \returns true on success or false on failure; call SDL_GetError() for more
* information.
+ *
+ * \threadsafety This function should be called on the thread that created the
+ * text.
+ *
+ * \since This function is available since SDL_ttf 3.0.0.
*/
-extern SDL_DECLSPEC bool SDLCALL TTF_GetTextWrapping(TTF_Text *text, bool *wrap, int *wrapLength);
+extern SDL_DECLSPEC bool SDLCALL TTF_GetTextWrapping(TTF_Text *text, int *wrapLength);
/**
* Get the size of a text object.
@@ -1673,6 +1746,11 @@ extern SDL_DECLSPEC bool SDLCALL TTF_GetTextWrapping(TTF_Text *text, bool *wrap,
* NULL.
* \returns true on success or false on failure; call SDL_GetError() for more
* information.
+ *
+ * \threadsafety This function should be called on the thread that created the
+ * text.
+ *
+ * \since This function is available since SDL_ttf 3.0.0.
*/
extern SDL_DECLSPEC bool SDLCALL TTF_GetTextSize(TTF_Text *text, int *w, int *h);
@@ -1727,6 +1805,11 @@ typedef struct TTF_SubString
* offset.
* \returns true on success or false on failure; call SDL_GetError() for more
* information.
+ *
+ * \threadsafety This function should be called on the thread that created the
+ * text.
+ *
+ * \since This function is available since SDL_ttf 3.0.0.
*/
extern SDL_DECLSPEC bool SDLCALL TTF_GetTextSubString(TTF_Text *text, int offset, TTF_SubString *substring);
@@ -1745,6 +1828,11 @@ extern SDL_DECLSPEC bool SDLCALL TTF_GetTextSubString(TTF_Text *text, int offset
* offset.
* \returns true on success or false on failure; call SDL_GetError() for more
* information.
+ *
+ * \threadsafety This function should be called on the thread that created the
+ * text.
+ *
+ * \since This function is available since SDL_ttf 3.0.0.
*/
extern SDL_DECLSPEC bool SDLCALL TTF_GetTextSubStringForLine(TTF_Text *text, int line, TTF_SubString *substring);
@@ -1761,6 +1849,11 @@ extern SDL_DECLSPEC bool SDLCALL TTF_GetTextSubStringForLine(TTF_Text *text, int
* call SDL_GetError() for more information. This is a single
* allocation that should be freed with SDL_free() when it is no
* longer needed.
+ *
+ * \threadsafety This function should be called on the thread that created the
+ * text.
+ *
+ * \since This function is available since SDL_ttf 3.0.0.
*/
extern SDL_DECLSPEC TTF_SubString ** SDLCALL TTF_GetTextSubStringsForRange(TTF_Text *text, int offset, int length, int *count);
@@ -1778,6 +1871,11 @@ extern SDL_DECLSPEC TTF_SubString ** SDLCALL TTF_GetTextSubStringsForRange(TTF_T
* the given point.
* \returns true on success or false on failure; call SDL_GetError() for more
* information.
+ *
+ * \threadsafety This function should be called on the thread that created the
+ * text.
+ *
+ * \since This function is available since SDL_ttf 3.0.0.
*/
extern SDL_DECLSPEC bool SDLCALL TTF_GetTextSubStringForPoint(TTF_Text *text, int x, int y, TTF_SubString *substring);
@@ -1791,6 +1889,11 @@ extern SDL_DECLSPEC bool SDLCALL TTF_GetTextSubStringForPoint(TTF_Text *text, in
* \param substring the TTF_SubString to query.
* \returns true on success or false on failure; call SDL_GetError() for more
* information.
+ *
+ * \threadsafety This function should be called on the thread that created the
+ * text.
+ *
+ * \since This function is available since SDL_ttf 3.0.0.
*/
extern SDL_DECLSPEC bool SDLCALL TTF_GetPreviousTextSubString(TTF_Text *text, const TTF_SubString *substring, TTF_SubString *previous);
@@ -1805,6 +1908,11 @@ extern SDL_DECLSPEC bool SDLCALL TTF_GetPreviousTextSubString(TTF_Text *text, co
* \param next a pointer filled in with the next substring.
* \returns true on success or false on failure; call SDL_GetError() for more
* information.
+ *
+ * \threadsafety This function should be called on the thread that created the
+ * text.
+ *
+ * \since This function is available since SDL_ttf 3.0.0.
*/
extern SDL_DECLSPEC bool SDLCALL TTF_GetNextTextSubString(TTF_Text *text, const TTF_SubString *substring, TTF_SubString *next);
@@ -1818,6 +1926,11 @@ extern SDL_DECLSPEC bool SDLCALL TTF_GetNextTextSubString(TTF_Text *text, const
* \param text the TTF_Text to update.
* \returns true on success or false on failure; call SDL_GetError() for more
* information.
+ *
+ * \threadsafety This function should be called on the thread that created the
+ * text.
+ *
+ * \since This function is available since SDL_ttf 3.0.0.
*/
extern SDL_DECLSPEC bool SDLCALL TTF_UpdateText(TTF_Text *text);
diff --git a/src/SDL_ttf.c b/src/SDL_ttf.c
index 9e6ef9c1..8ffe7f55 100644
--- a/src/SDL_ttf.c
+++ b/src/SDL_ttf.c
@@ -3121,6 +3121,10 @@ static bool TTF_Size_Internal(TTF_Font *font, const char *text, size_t length, i
char_count += 1;
}
if (cw >= measure_width) {
+ if (cw > measure_width) {
+ // The last character didn't fit
+ font->pos_len -= 1;
+ }
break;
}
}
@@ -3342,7 +3346,7 @@ static bool CharacterIsNewLine(Uint32 c)
return false;
}
-static bool GetWrappedLines(TTF_Font *font, const char *text, size_t length, int wrapLength, TTF_Line **lines, int *num_lines, int *w, int *h)
+static bool GetWrappedLines(TTF_Font *font, const char *text, size_t length, int xoffset, int wrapLength, TTF_Line **lines, int *num_lines, int *w, int *h)
{
int width, height;
int i, numLines = 0, rowHeight;
@@ -3403,51 +3407,61 @@ static bool GetWrappedLines(TTF_Font *font, const char *text, size_t length, int
strLines[numLines].length = left;
++numLines;
- if (!TTF_MeasureString(font, spot, left, wrapLength, &extent, &max_count)) {
+ int measure_width = wrapLength;
+ if (measure_width > 0) {
+ measure_width = SDL_max(measure_width - xoffset, 1);
+ }
+ if (!TTF_MeasureString(font, spot, left, measure_width, &extent, &max_count)) {
SDL_SetError("Error measure text");
goto done;
}
if (wrapLength != 0) {
- if (max_count == 0) {
+ if (max_count == 0 && numLines > 1) {
max_count = 1;
}
}
- while (left > 0) {
- int is_delim;
- Uint32 c = SDL_StepUTF8((const char **)&spot, &left);
+ if (max_count > 0) {
+ while (left > 0) {
+ int is_delim;
+ Uint32 c = SDL_StepUTF8((const char **)&spot, &left);
- if (c == UNICODE_BOM_NATIVE || c == UNICODE_BOM_SWAPPED) {
- continue;
- }
+ if (c == UNICODE_BOM_NATIVE || c == UNICODE_BOM_SWAPPED) {
+ continue;
+ }
- char_count += 1;
+ char_count += 1;
+
+ // With wrapLength == 0, normal text rendering but newline aware
+ is_delim = (wrapLength > 0) ? CharacterIsDelimiter(c) : CharacterIsNewLine(c);
- // With wrapLength == 0, normal text rendering but newline aware
- is_delim = (wrapLength > 0) ? CharacterIsDelimiter(c) : CharacterIsNewLine(c);
+ // Record last delimiter position
+ if (is_delim) {
+ save_text = spot;
+ save_length = left;
+ // Break, if new line
+ if (c == '\n' || (c == '\r' && *spot != '\n')) {
+ break;
+ }
+ }
- // Record last delimiter position
- if (is_delim) {
- save_text = spot;
- save_length = left;
- // Break, if new line
- if (c == '\n' || (c == '\r' && *spot != '\n')) {
+ // Break, if reach the limit
+ if (char_count == max_count) {
break;
}
}
- // Break, if reach the limit
- if (char_count == max_count) {
- break;
+ // Cut at last delimiter/new lines, otherwise in the middle of the word
+ if (save_text && left > 0) {
+ spot = save_text;
+ left = save_length;
}
}
- // Cut at last delimiter/new lines, otherwise in the middle of the word
- if (save_text && left > 0) {
- spot = save_text;
- left = save_length;
- }
+ // First line is complete, start the next at offset 0
+ xoffset = 0;
+
} while (left > 0);
// Trim whitespace from the wrapped lines and newlines from unwrapped lines
@@ -3522,7 +3536,7 @@ static bool GetWrappedLines(TTF_Font *font, const char *text, size_t length, int
bool TTF_GetStringSizeWrapped(TTF_Font *font, const char *text, size_t length, int wrapLength, int *w, int *h)
{
- return GetWrappedLines(font, text, length, wrapLength, NULL, NULL, w, h);
+ return GetWrappedLines(font, text, length, 0, wrapLength, NULL, NULL, w, h);
}
static SDL_Surface* TTF_Render_Wrapped_Internal(TTF_Font *font, const char *text, size_t length, SDL_Color fg, SDL_Color bg, int wrapLength, const render_mode_t render_mode)
@@ -3533,7 +3547,7 @@ static SDL_Surface* TTF_Render_Wrapped_Internal(TTF_Font *font, const char *text
int i, numLines = 0;
TTF_Line *strLines = NULL;
- if (!GetWrappedLines(font, text, length, wrapLength, &strLines, &numLines, &width, &height)) {
+ if (!GetWrappedLines(font, text, length, 0, wrapLength, &strLines, &numLines, &width, &height)) {
return NULL;
}
@@ -3637,7 +3651,6 @@ SDL_Surface* TTF_RenderText_LCD_Wrapped(TTF_Font *font, const char *text, size_t
struct TTF_TextLayout
{
- bool wrap;
int wrap_length;
int *lines;
};
@@ -3649,7 +3662,7 @@ typedef struct TTF_InternalText
TTF_TextLayout layout;
} TTF_InternalText;
-static TTF_Text *CreateText(TTF_TextEngine *engine, TTF_Font *font, const char *text, size_t length, bool wrap, int wrapLength)
+static TTF_Text *CreateText(TTF_TextEngine *engine, TTF_Font *font, const char *text, size_t length, int wrapLength)
{
if (engine && engine->version < sizeof(*engine)) {
// Update this to handle older versions of this interface
@@ -3672,7 +3685,6 @@ static TTF_Text *CreateText(TTF_TextEngine *engine, TTF_Font *font, const char *
result->color.a = 1.0f;
result->internal->needs_layout_update = true;
result->internal->engine = engine;
- result->internal->layout->wrap = wrap;
result->internal->layout->wrap_length = wrapLength;
if (text && *text) {
if (length == 0) {
@@ -3696,12 +3708,12 @@ static TTF_Text *CreateText(TTF_TextEngine *engine, TTF_Font *font, const char *
TTF_Text *TTF_CreateText(TTF_TextEngine *engine, TTF_Font *font, const char *text, size_t length)
{
- return CreateText(engine, font, text, length, false, -1);
+ return CreateText(engine, font, text, length, 0);
}
TTF_Text *TTF_CreateText_Wrapped(TTF_TextEngine *engine, TTF_Font *font, const char *text, size_t length, int wrapLength)
{
- return CreateText(engine, font, text, length, true, wrapLength);
+ return CreateText(engine, font, text, length, wrapLength);
}
static int SDLCALL SortClusters(const void *a, const void *b)
@@ -3796,81 +3808,6 @@ static int CalculateClusterLengths(TTF_Text *text, TTF_SubString *clusters, int
}
static bool LayoutText(TTF_Text *text)
-{
- TTF_Font *font = text->internal->font;
- size_t length = SDL_strlen(text->text);
- TTF_DrawOperation *ops = NULL;
- int num_ops = 0, max_ops;
- TTF_SubString *clusters = NULL, *cluster;
- int num_clusters = 0;
- int xstart, ystart, width = 0, height = 0;
- bool result = false;
-
- if (!TTF_Size_Internal(font, text->text, length, &width, &height, &xstart, &ystart, NO_MEASUREMENT) || !width) {
- return true;
- }
-
- max_ops = font->pos_len;
- if (TTF_HANDLE_STYLE_UNDERLINE(font)) {
- ++max_ops;
- }
- if (TTF_HANDLE_STYLE_STRIKETHROUGH(font)) {
- ++max_ops;
- }
-
- ops = (TTF_DrawOperation *)SDL_calloc(max_ops, sizeof(*ops));
- if (!ops) {
- goto done;
- }
-
- clusters = (TTF_SubString *)SDL_calloc(font->num_clusters + 2, sizeof(*clusters));
- if (!clusters) {
- goto done;
- }
-
- // Create the text drawing operations
- if (!Render_Line_TextEngine(font, xstart, ystart, width, height, ops, &num_ops, clusters, &num_clusters, 0, 0)) {
- goto done;
- }
-
- cluster = &clusters[num_clusters++];
- cluster->flags = TTF_SUBSTRING_LINE_END;
- cluster->offset = (int)SDL_strlen(text->text);
-
- cluster = &clusters[num_clusters++];
- cluster->flags = TTF_SUBSTRING_TEXT_END;
- cluster->offset = (int)SDL_strlen(text->text);
-
- // Apply underline or strikethrough style, if needed
- if (TTF_HANDLE_STYLE_UNDERLINE(font)) {
- Draw_Line_TextEngine(font, width, height, 0, ystart + font->underline_top_row, width, font->line_thickness, ops, &num_ops);
- }
-
- if (TTF_HANDLE_STYLE_STRIKETHROUGH(font)) {
- Draw_Line_TextEngine(font, width, height, 0, ystart + font->strikethrough_top_row, width, font->line_thickness, ops, &num_ops);
- }
-
- num_clusters = CalculateClusterLengths(text, clusters, num_clusters, length, NULL);
-
- result = true;
-
-done:
- if (result) {
- text->num_lines = 1;
- text->internal->w = width;
- text->internal->h = height;
- text->internal->num_ops = num_ops;
- text->internal->ops = ops;
- text->internal->num_clusters = num_clusters;
- text->internal->clusters = clusters;
- } else {
- SDL_free(ops);
- SDL_free(clusters);
- }
- return result;
-}
-
-static bool LayoutTextWrapped(TTF_Text *text)
{
TTF_Font *font = text->internal->font;
int wrapLength = text->internal->layout->wrap_length;
@@ -3884,9 +3821,10 @@ static bool LayoutTextWrapped(TTF_Text *text)
int *lines = NULL;
bool result = false;
- if (!GetWrappedLines(font, text->text, length, wrapLength, &strLines, &numLines, &width, &height)) {
+ if (!GetWrappedLines(font, text->text, length, text->internal->x, wrapLength, &strLines, &numLines, &width, &height)) {
return true;
}
+ height += text->internal->y;
if (TTF_HANDLE_STYLE_UNDERLINE(font)) {
++extra_ops;
@@ -3931,6 +3869,8 @@ static bool LayoutTextWrapped(TTF_Text *text)
// Move to i-th line
ystart += i * font->lineskip;
+ ystart += text->internal->y;
+
// Control left/right/center align of each bit of text
if (font->horizontal_align == TTF_HORIZONTAL_ALIGN_RIGHT) {
xoffset = (width - line_width);
@@ -3941,6 +3881,10 @@ static bool LayoutTextWrapped(TTF_Text *text)
}
xoffset = SDL_max(0, xoffset);
+ if (i == 0) {
+ xoffset += text->internal->x;
+ }
+
// Allocate space for the operations on this line
additional_ops = (font->pos_len + extra_ops);
new_ops = (TTF_DrawOperation *)SDL_realloc(ops, (max_ops + additional_ops) * sizeof(*new_ops));
@@ -4096,6 +4040,38 @@ TTF_Font *TTF_GetTextFont(TTF_Text *text)
return text->internal->font;
}
+bool TTF_SetTextPosition(TTF_Text *text, int x, int y)
+{
+ TTF_CHECK_POINTER("text", text, false);
+
+ if (x != text->internal->x || y != text->internal->y) {
+ text->internal->x = x;
+ text->internal->y = y;
+ text->internal->needs_layout_update = true;
+ }
+ return true;
+}
+
+bool TTF_GetTextPosition(TTF_Text *text, int *x, int *y)
+{
+ if (x) {
+ *x = 0;
+ }
+ if (y) {
+ *y = 0;
+ }
+
+ TTF_CHECK_POINTER("text", text, false);
+
+ if (x) {
+ *x = text->internal->x;
+ }
+ if (y) {
+ *y = text->internal->y;
+ }
+ return true;
+}
+
bool TTF_SetTextString(TTF_Text *text, const char *string, size_t length)
{
TTF_CHECK_POINTER("text", text, false);
@@ -4218,38 +4194,27 @@ bool TTF_DeleteTextString(TTF_Text *text, int offset, int length)
return true;
}
-bool TTF_SetTextWrapping(TTF_Text *text, bool wrap, int wrapLength)
+bool TTF_SetTextWrapping(TTF_Text *text, int wrapLength)
{
TTF_CHECK_POINTER("text", text, false);
- if (wrap == text->internal->layout->wrap &&
- (wrapLength < 0 || wrapLength == text->internal->layout->wrap_length)) {
+ if (wrapLength == text->internal->layout->wrap_length) {
return true;
}
- text->internal->layout->wrap = wrap;
- if (wrapLength >= 0) {
- text->internal->layout->wrap_length = wrapLength;
- }
-
+ text->internal->layout->wrap_length = SDL_max(wrapLength, 0);
text->internal->needs_layout_update = true;
return true;
}
-bool TTF_GetTextWrapping(TTF_Text *text, bool *wrap, int *wrapLength)
+bool TTF_GetTextWrapping(TTF_Text *text, int *wrapLength)
{
- if (wrap) {
- *wrap = false;
- }
if (wrapLength) {
- *wrapLength = -1;
+ *wrapLength = 0;
}
TTF_CHECK_POINTER("text", text, false);
- if (wrap) {
- *wrap = text->internal->layout->wrap;
- }
if (wrapLength) {
*wrapLength = text->internal->layout->wrap_length;
}
@@ -4545,7 +4510,7 @@ bool TTF_GetTextSubStringForPoint(TTF_Text *text, int x, int y, TTF_SubString *s
#endif
const TTF_SubString *closest = NULL;
int closest_dist = INT_MAX;
- int wrap_cost = (text->internal->layout->wrap ? 100 : 1);
+ int wrap_cost = 100;
SDL_Point point = { x, y };
for (int i = 0; i < text->internal->num_clusters; ++i) {
const TTF_SubString *cluster = &text->internal->clusters[i];
@@ -4673,14 +4638,8 @@ bool TTF_UpdateText(TTF_Text *text)
text->internal->h = 0;
if (text->internal->font && text->text) {
- if (text->internal->layout->wrap) {
- if (!LayoutTextWrapped(text)) {
- return false;
- }
- } else {
- if (!LayoutText(text)) {
- return false;
- }
+ if (!LayoutText(text)) {
+ return false;
}
}
diff --git a/src/SDL_ttf.sym b/src/SDL_ttf.sym
index dffea6e6..351142b8 100644
--- a/src/SDL_ttf.sym
+++ b/src/SDL_ttf.sym
@@ -45,6 +45,7 @@ SDL3_ttf_0.0.0 {
TTF_GetStringSizeWrapped;
TTF_GetTextEngine;
TTF_GetTextFont;
+ TTF_GetTextPosition;
TTF_GetTextSize;
TTF_GetTextSubString;
TTF_GetTextSubStringForLine;
@@ -81,6 +82,7 @@ SDL3_ttf_0.0.0 {
TTF_SetFontWrapAlignment;
TTF_SetTextEngine;
TTF_SetTextFont;
+ TTF_SetTextPosition;
TTF_SetTextString;
TTF_SetTextWrapping;
TTF_UpdateText;
(Patch may be truncated, please check the link at the top of this post.)