Procházet zdrojové kódy

Refactor: make textbox generic

Evgeniy Parfenyuk před 2 týdny
rodič
revize
7ac802fc79
4 změnil soubory, kde provedl 164 přidání a 74 odebrání
  1. 33 8
      src/main.c
  2. 57 4
      src/text_utils.h
  3. 59 50
      src/ui/text_box.c
  4. 15 12
      src/ui/text_box.h

+ 33 - 8
src/main.c

@@ -24,29 +24,54 @@ along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 int main(void)
 {
-    const int screenWidth = 1280;
-    const int screenHeight = 720;
-
+    int screenWidth = 1280;
+    int screenHeight = 720;
+    
+    SetConfigFlags(FLAG_WINDOW_RESIZABLE);
     InitWindow(screenWidth, screenHeight, "Vinora Engine");
+    SetWindowMinSize(800, 600);
     SetTargetFPS(60);
 
+    Font gameFont = LoadFontEx("assets/fonts/NotoSans-Regular.ttf", 32, 
+                                NULL, 1240);
+    SetTextureFilter(gameFont.texture, TEXTURE_FILTER_BILINEAR);
+
+    Rectangle box_rect = { 40, (float)screenHeight - 180, 
+                               (float)screenWidth  - 80, 160 };
     TextBox dialogueBox;
-    TextBoxInit(&dialogueBox, screenWidth, screenHeight);
+    TextBoxInit(&dialogueBox, box_rect, gameFont);
 
-    TextBoxSetText(&dialogueBox, "",
-                   "Hello from Vinora Engine textbox!\n\n"
-                   "D-d-do you like it or something? :3");
+    TextBoxSetText(&dialogueBox,
+    "Lorem ipsum dolor sit amet, consectetur adipiscing elit,"
+    "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
+    "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris "
+    "nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in"
+    "reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla "
+    "pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa "
+    "qui officia deserunt mollit anim id est laborum.");
 
     while (!WindowShouldClose())
     {
-        dialogueBox.timer += GetFrameTime();
+        float dt = GetFrameTime();
+        TextBoxUpdate(&dialogueBox, dt);
+        
+        if (IsWindowResized()) {
+            screenWidth  = GetScreenWidth();
+            screenHeight = GetScreenHeight();
+            dialogueBox.rect = (Rectangle){40, (float)screenHeight - 180, 
+                                               (float)screenWidth  - 80, 160};
+            TextBoxReflow(&dialogueBox);
+        }
+
         BeginDrawing();
             ClearBackground(BLACK);
             TextBoxDraw(&dialogueBox);
         EndDrawing();
+
     }
 
     TextBoxCleanup(&dialogueBox);
+    UnloadFont(gameFont);
     CloseWindow();
     return 0;
 }

+ 57 - 4
src/text_utils.h

@@ -5,15 +5,68 @@
 
 static char *my_strdup(const char *s)
 {
-    if (!s)
-        return NULL;
+    if (!s) return NULL;
     size_t n = strlen(s) + 1;
     char *p = malloc(n);
-    if (!p)
-        return NULL;
+    if (!p) return NULL;
     memcpy(p, s, n);
     return p;
 }
 
+static char *WrapText(Font font, const char *text, float fontSize,
+                                 float spacing,    float maxWidth)
+{
+    if (!text || text[0] == '\0') return my_strdup("");
+
+    char *result = my_strdup(text);
+    if (!result) return NULL;
+
+    int length = strlen(result);
+    int lastSpaceIdx = -1;
+    int currentLineStartIdx = 0;
+
+    for (int i = 0; i < length; i++)
+    {
+        if (result[i] == ' ') {
+            lastSpaceIdx = i;
+        }
+        else if (result[i] == '\n') {
+            currentLineStartIdx = i + 1;
+            lastSpaceIdx = -1;
+            continue;
+        }
+
+        char savedChar = result[i + 1];
+        result[i + 1] = '\0';
+
+        Vector2 size = MeasureTextEx(font, &result[currentLineStartIdx], fontSize, spacing);
+        
+        result[i + 1] = savedChar;
+
+        if (size.x > maxWidth)
+        {
+            if (lastSpaceIdx != -1 && lastSpaceIdx >= currentLineStartIdx)
+            {
+                result[lastSpaceIdx] = '\n';
+                currentLineStartIdx = lastSpaceIdx + 1;
+                lastSpaceIdx = -1;
+                i = currentLineStartIdx - 1; 
+            }
+            else
+            {
+                if (i > currentLineStartIdx) 
+                {
+                    result[i] = '\n';
+                    currentLineStartIdx = i;
+                    lastSpaceIdx = -1;
+                }
+            }
+        }
+    }
+
+    return result;
+}
+
+
 #endif
 

+ 59 - 50
src/ui/text_box.c

@@ -3,81 +3,90 @@
 #include <stdlib.h>
 #include <string.h>
 
-void TextBoxInit(TextBox *tb, int screenWidth, int screenHeight)
+void TextBoxInit(TextBox *tb, Rectangle rect, Font gameFont)
 {
-    tb->rect = (Rectangle){
-        .x = 40,
-        .y = screenHeight - 180,
-        .width = screenWidth - 80,
-        .height = 160};
+    memset(tb, 0, sizeof(TextBox));
+    tb->rect = rect;
+    tb->padding = 20.0f;
 
     tb->bg_color = (Color){23, 28, 37, 220};
     tb->border_color = (Color){25, 31, 40, 180};
     tb->border_thickness = 3;
-    // TODO: remake to use charset instead of 0-2048 range
-    tb->font = LoadFontEx("assets/fonts/NotoSans-Regular.ttf", 32, NULL, 2048);
-    SetTextureFilter(tb->font.texture, TEXTURE_FILTER_BILINEAR);
+    
+    tb->font = gameFont;
     tb->font_size = 24;
+    tb->font_spacing = 1.0f;
     tb->text_color = WHITE;
-    tb->name_color = RED;
 
-    tb->typing_speed = 25.0f;
+    tb->typing_speed = 45.0f;
     tb->timer = 0.0f;
+    tb->text = NULL;
+}
 
-    memset(tb->name, 0, sizeof(tb->name));
+void TextBoxCleanup(TextBox *tb)
+{
+    if (tb->text) free(tb->text);
+    if (tb->wrapped_text) free(tb->wrapped_text);
     tb->text = NULL;
-    tb->max_text_length = 1024;
+    tb->wrapped_text = NULL;
+}
+
+void TextBoxReflow(TextBox *tb)
+{
+    if (tb->wrapped_text) {
+        free(tb->wrapped_text);
+        tb->wrapped_text = NULL;
+    }
+
+    if (tb->text) {
+        float max_text_width = tb->rect.width - (tb->padding * 2);
+        tb->wrapped_text = WrapText(tb->font, tb->text, tb->font_size,
+                                    tb->font_spacing, max_text_width);
+    }
 }
 
-void TextBoxSetText(TextBox *tb, const char *name, const char *text)
+void TextBoxUpdate(TextBox *tb, float dt)
 {
-    if (name)
-        strncpy(tb->name, name, 63);
-    else
-        tb->name[0] = '\0';
+    tb->timer += dt;
+}
 
+
+void TextBoxSetText(TextBox *tb, const char *text)
+{
     if (tb->text)
         free(tb->text);
-    tb->text = my_strdup(text ? text : "");
+    if (text) {
+        size_t len = strlen(text) + 1;
+        tb->text = malloc(len);
+        if (tb->text) {
+            memcpy(tb->text, text, len);
+        }
+    } else
+        tb->text = NULL;
+    tb->timer = 0.0f;
+    TextBoxReflow(tb);
 }
 
-void TextBoxDraw(TextBox *tb)
+void TextBoxDraw(const TextBox *tb)
 {
     DrawRectangleRec(tb->rect, tb->bg_color);
-    DrawRectangleLinesEx(tb->rect, tb->border_thickness, tb->border_color);
-
-    if (tb->name[0] != '\0')
+    if (tb->border_thickness > 0)
+        DrawRectangleLinesEx(tb->rect, tb->border_thickness, tb->border_color);
+    if (tb->wrapped_text && tb->wrapped_text[0] != '\0')
     {
-        Vector2 nameSize = MeasureTextEx(tb->font, tb->name,
-                                         tb->font_size + 2, 1);
-        Rectangle nameRect = {
-            tb->rect.x + 20,
-            tb->rect.y - 30,
-            nameSize.x + 24,
-            32};
+        int max_chars = (tb->typing_speed > 0) 
+            ? (int)(tb->timer * tb->typing_speed)
+            : (int)strlen(tb->wrapped_text);
+        const char *visible_text = TextSubtext(tb->wrapped_text, 0, max_chars);
 
-        DrawRectangleRec(nameRect, tb->bg_color);
-        DrawRectangleLinesEx(nameRect, 3, tb->border_color);
+        Vector2 text_pos = {
+            tb->rect.x + tb->padding,
+            tb->rect.y + tb->padding
+        };
 
-        DrawTextEx(tb->font, tb->name,
-                   (Vector2){nameRect.x + 12, nameRect.y + 3},
-                   tb->font_size + 2, 1, tb->name_color);
-    }
+        DrawTextEx(tb->font, visible_text, text_pos, 
+                   tb->font_size, tb->font_spacing, tb->text_color);
 
-    if (tb->text)
-    {
-        DrawTextEx(tb->font, TextSubtext(tb->text, 0, tb->timer * tb->typing_speed),
-                   (Vector2){tb->rect.x + 25, tb->rect.y + 25},
-                   tb->font_size, 1.2f, tb->text_color);
     }
 }
 
-void TextBoxCleanup(TextBox *tb)
-{
-    UnloadFont(tb->font);
-    if (tb->text)
-    {
-        free(tb->text);
-        tb->text = NULL;
-    }
-}

+ 15 - 12
src/ui/text_box.h

@@ -6,26 +6,29 @@
 typedef struct
 {
     Rectangle rect;
+    float padding;
+
     Color bg_color;
     Color border_color;
-    int border_thickness;
-
-    Font font;
-    int font_size;
+    int   border_thickness;
+    
+    Font  font;
+    float font_size;
+    float font_spacing;
     Color text_color;
-
-    char name[256];
-    Color name_color;
-
+    
     char *text;
+    char *wrapped_text;
     float typing_speed; // Characters per second
     float timer;        // How much seconds passed
-    int max_text_length;
 } TextBox;
 
-void TextBoxInit(TextBox *tb, int screenWidth, int screenHeight);
-void TextBoxSetText(TextBox *tb, const char *name, const char *text);
-void TextBoxDraw(TextBox *tb);
+void TextBoxReflow(TextBox *tb);
+
+void TextBoxInit(TextBox *tb, Rectangle rect, Font gameFont);
 void TextBoxCleanup(TextBox *tb);
 
+void TextBoxUpdate(TextBox *tb, float dt);
+void TextBoxSetText(TextBox *tb, const char *text);
+void TextBoxDraw(const TextBox *tb);
 #endif