comparison third_party/raylib/custom.c @ 163:058de208e640

[Config] Adding os ignore files.
author June Park <parkjune1995@gmail.com>
date Mon, 19 Jan 2026 04:52:02 -0800
parents 87d8d3eb3491
children
comparison
equal deleted inserted replaced
162:8ceb5d3c6bdd 163:058de208e640
1 #include "third_party/raylib/include/raylib.h" 1 #include "third_party/raylib/include/raylib.h"
2 #define RAYGUI_IMPLEMENTATION 2 #define RAYGUI_IMPLEMENTATION
3 #include "third_party/raylib/include/raygui.h" 3 #include "third_party/raylib/include/raygui.h"
4 #include "third_party/raylib/custom.h" 4 #include "third_party/raylib/custom.h"
5 5 #include <string.h>
6 // -- forward declarations --// 6
7 void DefaultBehaviours() 7 // ============================================================================
8 { 8 // COLOR SCHEME IMPLEMENTATION
9 // Font sizes 9 // ============================================================================
10 IncreaseFontSize(); 10
11 DecreaseFontSize(); 11 // Global color scheme instance
12 } 12 ColorScheme g_colors = {0};
13 13
14 void IncreaseFontSize() 14 ColorScheme PostDog_DefaultColorScheme(void)
15 { 15 {
16 if ((IsKeyDown(KEY_LEFT_SUPER) || IsKeyDown(KEY_LEFT_CONTROL)) && IsKeyDown(KEY_EQUAL)) 16 return (ColorScheme){
17 GuiSetStyle(DEFAULT, TEXT_SIZE, GuiGetStyle(DEFAULT, TEXT_SIZE) + 1); 17 .primary = (Color){255, 140, 0, 255}, // Orange
18 } 18 .secondary = (Color){255, 248, 231, 255}, // Beige/Egg white
19 19 .background = (Color){255, 255, 255, 255}, // White
20 void DecreaseFontSize() 20 .text = (Color){30, 30, 30, 255}, // Near black
21 { 21 .text_light = (Color){255, 255, 255, 255}, // White
22 if ((IsKeyDown(KEY_LEFT_SUPER) || IsKeyDown(KEY_LEFT_CONTROL)) && IsKeyDown(KEY_MINUS)) 22 .border = (Color){200, 200, 200, 255}, // Light gray
23 GuiSetStyle(DEFAULT, TEXT_SIZE, GuiGetStyle(DEFAULT, TEXT_SIZE) - 1); 23 .highlight = (Color){255, 180, 100, 255}, // Light orange
24 .success = (Color){76, 175, 80, 255}, // Green
25 .error = (Color){244, 67, 54, 255}, // Red
26 .muted = (Color){158, 158, 158, 255}, // Gray
27 };
28 }
29
30 ColorScheme PostDog_DarkColorScheme(void)
31 {
32 return (ColorScheme){
33 .primary = (Color){255, 140, 0, 255}, // Orange
34 .secondary = (Color){45, 45, 48, 255}, // Dark gray
35 .background = (Color){30, 30, 30, 255}, // Near black
36 .text = (Color){240, 240, 240, 255}, // Near white
37 .text_light = (Color){255, 255, 255, 255}, // White
38 .border = (Color){70, 70, 70, 255}, // Dark gray
39 .highlight = (Color){255, 180, 100, 255}, // Light orange
40 .success = (Color){76, 175, 80, 255}, // Green
41 .error = (Color){244, 67, 54, 255}, // Red
42 .muted = (Color){100, 100, 100, 255}, // Gray
43 };
44 }
45
46 void PostDog_InitColorScheme(void)
47 {
48 g_colors = PostDog_DefaultColorScheme();
49 }
50
51 void PostDog_SetColorScheme(ColorScheme scheme)
52 {
53 g_colors = scheme;
54 }
55
56 // ============================================================================
57 // FUNCTIONALITY HELPERS
58 // ============================================================================
59
60 void DefaultBehaviours(void)
61 {
62 IncreaseFontSize();
63 DecreaseFontSize();
64 }
65
66 void IncreaseFontSize(void)
67 {
68 if ((IsKeyDown(KEY_LEFT_SUPER) || IsKeyDown(KEY_LEFT_CONTROL)) && IsKeyDown(KEY_EQUAL))
69 GuiSetStyle(DEFAULT, TEXT_SIZE, GuiGetStyle(DEFAULT, TEXT_SIZE) + 1);
70 }
71
72 void DecreaseFontSize(void)
73 {
74 if ((IsKeyDown(KEY_LEFT_SUPER) || IsKeyDown(KEY_LEFT_CONTROL)) && IsKeyDown(KEY_MINUS))
75 GuiSetStyle(DEFAULT, TEXT_SIZE, GuiGetStyle(DEFAULT, TEXT_SIZE) - 1);
24 } 76 }
25 77
26 // --- Layout helper --- // 78 // --- Layout helper --- //
27 Rectangle RightOf(Rectangle ref, float padding) 79 Rectangle RightOf(Rectangle ref, float padding)
28 { 80 {
72 .width = container.width * ratio, 124 .width = container.width * ratio,
73 .height = container.height 125 .height = container.height
74 }; 126 };
75 } 127 }
76 128
129
77 void DrawRectangleSelectiveRounded(Rectangle rec, float radius, int segments, Color color, 130 void DrawRectangleSelectiveRounded(Rectangle rec, float radius, int segments, Color color,
78 boolean roundTL, boolean roundTR, boolean roundBR, boolean roundBL) { 131 boolean roundTL, boolean roundTR, boolean roundBR, boolean roundBL) {
79 DrawRectangle(rec.x + radius, rec.y + radius, rec.width - 2*radius, rec.height - 2*radius, color); 132 // Clamp radius to valid range
80 // Top edge (excluding rounded corners) 133 float maxR = fminf(rec.width, rec.height) * 0.5f;
81 DrawRectangle(rec.x + (roundTL ? radius : 0), rec.y, rec.width - (roundTL ? radius : 0) - (roundTR ? radius : 0), radius, color); 134 radius = fminf(fmaxf(radius, 0.0f), maxR);
82 // Bottom edge 135
83 DrawRectangle(rec.x + (roundBL ? radius : 0), rec.y + rec.height - radius, rec.width - (roundBL ? radius : 0) - (roundBR ? radius : 0), radius, color); 136 // Fill center
84 // Left edge 137 DrawRectangleRec((Rectangle){
85 DrawRectangle(rec.x, rec.y + (roundTL ? radius : 0), radius, rec.height - (roundTL ? radius : 0) - (roundBL ? radius : 0), color); 138 rec.x + radius, rec.y + radius,
86 // Right edge 139 rec.width - 2.0f * radius, rec.height - 2.0f * radius
87 DrawRectangle(rec.x + rec.width - radius, rec.y + (roundTR ? radius : 0), radius, rec.height - (roundTR ? radius : 0) - (roundBR ? radius : 0), color); 140 }, color);
88 141
89 if (roundTL) 142 //
90 DrawCircleSector((Vector2){rec.x + radius, rec.y + radius}, radius, 180, 270, segments, color); 143 // |--------|
91 144 // | |
92 if (roundTR) 145 // | |
93 DrawCircleSector((Vector2){rec.x + rec.width - radius, rec.y + radius}, radius, 270, 360, segments, color); 146 // |--------|
94 147
95 if (roundBR) 148 // Top edge
96 DrawCircleSector((Vector2){rec.x + rec.width - radius, rec.y + rec.height - radius}, radius, 0, 90, segments, color); 149 float topStart = rec.x + (roundTL ? radius : 0.0f);
97 150 float topWidth = rec.width - (roundTL ? radius : 0.0f) - (roundTR ? radius : 0.0f);
98 if (roundBL) 151 if (topWidth > 0.0f)
99 DrawCircleSector((Vector2){rec.x + radius, rec.y + rec.height - radius}, radius, 90, 180, segments, color); 152 DrawRectangleRec((Rectangle){ topStart, rec.y, topWidth, radius }, color);
153
154 // Bottom edge
155 float botStart = rec.x + (roundBL ? radius : 0.0f);
156 float botWidth = rec.width - (roundBL ? radius : 0.0f) - (roundBR ? radius : 0.0f);
157 if (botWidth > 0.0f)
158 DrawRectangleRec((Rectangle){ botStart, rec.y + rec.height - radius, botWidth, radius }, color);
159
160 // Left edge
161 float leftStart = rec.y + (roundTL ? radius : 0.0f);
162 float leftHeight = rec.height - (roundTL ? radius : 0.0f) - (roundBL ? radius : 0.0f);
163 if (leftHeight > 0.0f)
164 DrawRectangleRec((Rectangle){ rec.x, leftStart, radius, leftHeight - radius}, color);
165
166 // Right edge
167 float rightStart = rec.y + (roundTR ? radius : 0.0f);
168 float rightHeight = rec.height - (roundTR ? radius : 0.0f) - (roundBR ? radius : 0.0f);
169 if (rightHeight > 0.0f)
170 DrawRectangleRec((Rectangle){ rec.x + rec.width - radius, rightStart, radius, rightHeight - radius }, color);
171
172 // Corners (arcs)
173 if (roundTL) {
174 DrawCircleSector((Vector2){ rec.x + radius, rec.y + radius }, radius, 180.0f, 270.0f, segments, color);
175 }
176 if (roundTR) {
177 DrawCircleSector((Vector2){ rec.x + rec.width - radius, rec.y + radius }, radius, 270.0f, 360.0f, segments, color);
178 }
179 if (roundBR) {
180 DrawCircleSector((Vector2){ rec.x + rec.width - radius, rec.y + rec.height - radius }, radius, 0.0f, 90.0f, segments, color);
181 }
182 if (roundBL) {
183 DrawCircleSector((Vector2){ rec.x + radius, rec.y + rec.height - radius }, radius, 90.0f, 180.0f, segments, color);
184 }
100 } 185 }
101 186
102 Rectangle AddPadding(Rectangle rect, float padding) 187 Rectangle AddPadding(Rectangle rect, float padding)
103 { 188 {
104 return (Rectangle){ 189 return (Rectangle){
139 rect.width, 224 rect.width,
140 rect.height - (2 * padding) 225 rect.height - (2 * padding)
141 }; 226 };
142 } 227 }
143 228
144 // // --- Components ---/ 229 Rectangle VerticalSplit(Rectangle container, float ratio)
145 // #define TEXT_SIZE_DEFAULT 10 230 {
146 // #define TEXT_AREA_LINE_HEIGHT 20 231 return (Rectangle){
147 // #define TEXT_AREA_PADDING 8 232 .x = container.x,
148 // #define TEXT_AREA_CURSOR_WIDTH 2 233 .y = container.y,
149 // #define TEXT_AREA_MAX_UNDO_STATES 64 234 .width = container.width,
150 // #define TEXT_AREA_MAX_INSTANCES 8 235 .height = container.height * ratio
151 // 236 };
152 // typedef struct { 237 }
153 // char *text; 238
154 // int cursor_pos; 239 // ============================================================================
155 // int selection_start; 240 // DRAWING HELPERS - Additional
156 // int selection_end; 241 // ============================================================================
157 // } TextAreaUndoEntry; 242
158 // 243 void DrawRectangleSelectiveRoundedLines(Rectangle rec, float radius, int segments, Color color,
159 // typedef struct { 244 boolean roundTL, boolean roundTR, boolean roundBR, boolean roundBL)
160 // int id; // Unique ID for this text area 245 {
161 // int cursor_pos; // Current cursor position in text 246 // Top edge
162 // int selection_start; // Selection start (-1 if no selection) 247 DrawLine(rec.x + (roundTL ? radius : 0), rec.y,
163 // int selection_end; // Selection end (-1 if no selection) 248 rec.x + rec.width - (roundTR ? radius : 0), rec.y, color);
164 // float scroll_offset_y; // Vertical scroll offset 249 // Bottom edge
165 // float scroll_offset_x; // Horizontal scroll offset (for non-wrap mode) 250 DrawLine(rec.x + (roundBL ? radius : 0), rec.y + rec.height,
166 // boolean is_selecting; // Currently dragging to select 251 rec.x + rec.width - (roundBR ? radius : 0), rec.y + rec.height, color);
167 // boolean is_initialized; // State has been initialized 252 // Left edge
168 // 253 DrawLine(rec.x, rec.y + (roundTL ? radius : 0),
169 // // Undo history 254 rec.x, rec.y + rec.height - (roundBL ? radius : 0), color);
170 // TextAreaUndoEntry *undo_stack; // Dowa array of undo entries 255 // Right edge
171 // int undo_index; // Current position in undo stack 256 DrawLine(rec.x + rec.width, rec.y + (roundTR ? radius : 0),
172 // 257 rec.x + rec.width, rec.y + rec.height - (roundBR ? radius : 0), color);
173 // // Internal tracking 258
174 // double last_blink_time; 259 // Corner arcs
175 // boolean cursor_visible; 260 if (roundTL)
176 // } TextAreaState; 261 DrawCircleSectorLines((Vector2){rec.x + radius, rec.y + radius}, radius, 180, 270, segments, color);
177 // 262 if (roundTR)
178 // static TextAreaState g_text_area_states[TEXT_AREA_MAX_INSTANCES] = {0}; 263 DrawCircleSectorLines((Vector2){rec.x + rec.width - radius, rec.y + radius}, radius, 270, 360, segments, color);
179 // static int g_text_area_state_count = 0; 264 if (roundBR)
180 // static char *g_clipboard_text = NULL; 265 DrawCircleSectorLines((Vector2){rec.x + rec.width - radius, rec.y + rec.height - radius}, radius, 0, 90, segments, color);
181 // static Dowa_Arena *g_text_area_arena = NULL; 266 if (roundBL)
182 // 267 DrawCircleSectorLines((Vector2){rec.x + radius, rec.y + rec.height - radius}, radius, 90, 180, segments, color);
183 // // Helper functions 268 }
184 // static int TA_Min_Int(int a, int b) { return a < b ? a : b; } 269
185 // static int TA_Max_Int(int a, int b) { return a > b ? a : b; } 270 // ============================================================================
186 // static float TA_Min_Float(float a, float b) { return a < b ? a : b; } 271 // CUSTOM TAB COMPONENT IMPLEMENTATION
187 // static float TA_Max_Float(float a, float b) { return a > b ? a : b; } 272 // ============================================================================
188 // 273
189 // static TextAreaState* GetTextAreaState(int id) { 274 boolean PostDog_TabBar(Rectangle bounds, TabConfig config)
190 // for (int i = 0; i < g_text_area_state_count; i++) { 275 {
191 // if (g_text_area_states[i].id == id && g_text_area_states[i].is_initialized) { 276 if (config.count <= 0 || config.labels == NULL || config.active_tab == NULL)
192 // return &g_text_area_states[i]; 277 return FALSE;
193 // } 278
194 // } 279 boolean changed = FALSE;
195 // return NULL; 280 float tab_width = (bounds.width - config.padding * (config.count - 1)) / config.count;
196 // } 281 Vector2 mouse = GetMousePosition();
197 // 282
198 // static TextAreaState* CreateTextAreaState(int id) { 283 for (int i = 0; i < config.count; i++)
199 // if (g_text_area_state_count >= TEXT_AREA_MAX_INSTANCES) 284 {
200 // return &g_text_area_states[0]; // Reuse first slot 285 Rectangle tab_rect = {
201 // 286 .x = bounds.x + i * (tab_width + config.padding),
202 // TextAreaState *state = &g_text_area_states[g_text_area_state_count++]; 287 .y = bounds.y,
203 // memset(state, 0, sizeof(TextAreaState)); 288 .width = tab_width,
204 // state->id = id; 289 .height = bounds.height
205 // state->selection_start = -1; 290 };
206 // state->selection_end = -1; 291
207 // state->undo_index = -1; 292 boolean is_active = (i == *config.active_tab);
208 // state->cursor_visible = TRUE; 293 boolean is_hovered = CheckCollisionPointRec(mouse, tab_rect);
209 // state->is_initialized = TRUE; 294 Color bg_color = is_active ? config.active_color : config.inactive_color;
210 // return state; 295 Color txt_color = is_active ? config.active_text_color : config.text_color;
211 // } 296
212 // 297 // Hover effect
213 // static void GetLineAndColumn(const char *text, int pos, int *out_line, int *out_column) { 298 if (is_hovered && !is_active)
214 // int line = 0; 299 bg_color = Fade(config.active_color, 0.5f);
215 // int column = 0; 300
216 // for (int i = 0; i < pos && text[i] != '\0'; i++) 301 // Draw tab background with rounded top corners
217 // { 302 DrawRectangleSelectiveRounded(tab_rect, config.corner_radius, 8, bg_color,
218 // if (text[i] == '\n') 303 TRUE, TRUE, FALSE, FALSE);
219 // { 304
220 // line++; 305 // Draw tab label
221 // column = 0; 306 int text_width = MeasureText(config.labels[i], GuiGetStyle(DEFAULT, TEXT_SIZE));
222 // } 307 Vector2 text_pos = {
223 // else 308 tab_rect.x + (tab_rect.width - text_width) / 2,
224 // column++; 309 tab_rect.y + (tab_rect.height - GuiGetStyle(DEFAULT, TEXT_SIZE)) / 2
225 // } 310 };
226 // *out_line = line; 311 DrawText(config.labels[i], text_pos.x, text_pos.y, GuiGetStyle(DEFAULT, TEXT_SIZE), txt_color);
227 // *out_column = column; 312
228 // } 313 // Handle click
229 // 314 if (is_hovered && IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && !is_active)
230 // static int GetPosFromLineColumn(const char *text, int line, int column) { 315 {
231 // int current_line = 0; 316 *config.active_tab = i;
232 // int current_col = 0; 317 changed = TRUE;
233 // int i = 0; 318 }
234 // 319 }
235 // while (text[i] != '\0') 320
236 // { 321 return changed;
237 // if (current_line == line && current_col == column) 322 }
238 // return i; 323
239 // 324 boolean PostDog_TabBarSimple(Rectangle bounds, const char **labels, int count, int *active_tab)
240 // if (text[i] == '\n') 325 {
241 // { 326 TabConfig config = {
242 // if (current_line == line) 327 .labels = labels,
243 // return i; 328 .count = count,
244 // current_line++; 329 .active_tab = active_tab,
245 // current_col = 0; 330 .padding = 2,
246 // } 331 .corner_radius = 8,
247 // else 332 .active_color = g_colors.primary,
248 // current_col++; 333 .inactive_color = g_colors.secondary,
249 // i++; 334 .text_color = g_colors.text,
250 // } 335 .active_text_color = g_colors.text_light,
251 // return i; 336 };
252 // } 337 return PostDog_TabBar(bounds, config);
253 // 338 }
254 // static int GetLineStart(const char *text, int pos) { 339
255 // int i = pos; 340 // ============================================================================
256 // while (i > 0 && text[i - 1] != '\n') 341 // SPLIT PANEL IMPLEMENTATION
257 // i--; 342 // ============================================================================
258 // return i; 343
259 // } 344 void PostDog_SplitPanel(Rectangle bounds, SplitPanelConfig config,
260 // 345 Rectangle *out_left, Rectangle *out_right)
261 // static int GetLineEnd(const char *text, int pos) { 346 {
262 // int i = pos; 347 float left_width = bounds.width * config.split_ratio - config.gap / 2;
263 // int len = strlen(text); 348 float right_width = bounds.width * (1.0f - config.split_ratio) - config.gap / 2;
264 // while (i < len && text[i] != '\n') 349
265 // i++; 350 if (out_left)
266 // return i; 351 {
267 // } 352 *out_left = (Rectangle){
268 // 353 .x = bounds.x,
269 // static int CountLines(const char *text) { 354 .y = bounds.y,
270 // int count = 1; 355 .width = left_width,
271 // for (int i = 0; text[i] != '\0'; i++) 356 .height = bounds.height
272 // if (text[i] == '\n') count++; 357 };
273 // return count; 358 }
274 // } 359
275 // 360 if (out_right)
276 // static int MeasureTextRange(const char *text, int start, int end, int font_size) { 361 {
277 // if (start >= end) return 0; 362 *out_right = (Rectangle){
278 // 363 .x = bounds.x + left_width + config.gap,
279 // char temp[1024]; 364 .y = bounds.y,
280 // int len = TA_Min_Int(end - start, 1023); 365 .width = right_width,
281 // strncpy(temp, text + start, len); 366 .height = bounds.height
282 // temp[len] = '\0'; 367 };
283 // 368 }
284 // return MeasureTextEx(GuiGetFont(), temp, font_size, TEXT_SIZE_DEFAULT/GuiGetStyle(DEFAULT, TEXT_SIZE)).x; 369 }
285 // } 370
286 // 371 void PostDog_SplitPanelDraw(Rectangle bounds, SplitPanelConfig config)
287 // static int GetCharIndexFromPos(const char *text, Rectangle bounds, Vector2 pos, 372 {
288 // boolean wrap, float scroll_y, int font_size, int line_height) { 373 Rectangle left_rect, right_rect;
289 // if (!text || strlen(text) == 0) return 0; 374 PostDog_SplitPanel(bounds, config, &left_rect, &right_rect);
290 // 375
291 // float content_x = bounds.x + TEXT_AREA_PADDING; 376 // Draw left panel with rounded left corners
292 // float content_y = bounds.y + TEXT_AREA_PADDING - scroll_y; 377 DrawRectangleSelectiveRounded(left_rect, config.corner_radius, 8, config.left_color,
293 // float content_width = bounds.width - TEXT_AREA_PADDING * 2; 378 TRUE, FALSE, FALSE, TRUE);
294 // 379
295 // int text_len = strlen(text); 380 // Draw right panel with rounded right corners
296 // 381 DrawRectangleSelectiveRounded(right_rect, config.corner_radius, 8, config.right_color,
297 // float click_line_y = (pos.y - content_y) / line_height; 382 FALSE, TRUE, TRUE, FALSE);
298 // int target_visual_line = (int)click_line_y; 383 }
299 // if (target_visual_line < 0) target_visual_line = 0; 384
300 // 385 // ============================================================================
301 // int current_visual_line = 0; 386 // COMBINED INPUT COMPONENTS IMPLEMENTATION
302 // int i = 0; 387 // ============================================================================
303 // int line_char_start = 0; 388
304 // 389 // Helper: Parse semicolon-separated items and return item at index
305 // while (i <= text_len) { 390 static const char* GetDropdownItem(const char *items, int index, char *buffer, int buffer_size)
306 // boolean is_newline = (i < text_len && text[i] == '\n'); 391 {
307 // 392 int current_index = 0;
308 // if (wrap && i > line_char_start) { 393 int start = 0;
309 // int line_width = MeasureTextRange(text, line_char_start, i, font_size); 394 int len = strlen(items);
310 // if (line_width > content_width && i > line_char_start + 1) { 395
311 // int wrap_pos = i - 1; 396 for (int i = 0; i <= len; i++)
312 // for (int j = i - 1; j > line_char_start; j--) { 397 {
313 // if (text[j] == ' ') { 398 if (items[i] == ';' || items[i] == '\0')
314 // wrap_pos = j; 399 {
315 // break; 400 if (current_index == index)
316 // } 401 {
317 // } 402 int item_len = i - start;
318 // 403 if (item_len >= buffer_size) item_len = buffer_size - 1;
319 // if (current_visual_line == target_visual_line) { 404 strncpy(buffer, items + start, item_len);
320 // float click_x = pos.x - content_x; 405 buffer[item_len] = '\0';
321 // int best_pos = line_char_start; 406 return buffer;
322 // float best_dist = 99999; 407 }
323 // 408 current_index++;
324 // for (int k = line_char_start; k <= wrap_pos; k++) { 409 start = i + 1;
325 // int char_x = MeasureTextRange(text, line_char_start, k, font_size); 410 }
326 // float dist = (float)(click_x - char_x); 411 }
327 // if (dist < 0) dist = -dist; 412 buffer[0] = '\0';
328 // if (dist < best_dist) { 413 return buffer;
329 // best_dist = dist; 414 }
330 // best_pos = k; 415
331 // } 416 // Helper: Count items in semicolon-separated string
332 // } 417 static int CountDropdownItems(const char *items)
333 // return best_pos; 418 {
334 // } 419 if (!items || items[0] == '\0') return 0;
335 // 420 int count = 1;
336 // current_visual_line++; 421 for (int i = 0; items[i]; i++)
337 // line_char_start = (text[wrap_pos] == ' ') ? wrap_pos + 1 : wrap_pos; 422 if (items[i] == ';') count++;
338 // i = line_char_start; 423 return count;
339 // continue; 424 }
340 // } 425
341 // } 426 boolean PostDog_DropdownTextBox(Rectangle bounds, DropdownTextBoxConfig config)
342 // 427 {
343 // if (is_newline || i == text_len) { 428 boolean result = FALSE;
344 // if (current_visual_line == target_visual_line || i == text_len) { 429
345 // float click_x = pos.x - content_x; 430 // Calculate dropdown width
346 // int line_end = i; 431 float dropdown_w = config.dropdown_width;
347 // int best_pos = line_char_start; 432 if (dropdown_w <= 1.0f)
348 // float best_dist = 99999; 433 dropdown_w = bounds.width * dropdown_w;
349 // 434
350 // for (int k = line_char_start; k <= line_end; k++) { 435 // Calculate rectangles
351 // int char_x = MeasureTextRange(text, line_char_start, k, font_size); 436 Rectangle dropdown_rect = {
352 // float dist = (float)(click_x - char_x); 437 .x = bounds.x,
353 // if (dist < 0) dist = -dist; 438 .y = bounds.y,
354 // if (dist < best_dist) { 439 .width = dropdown_w,
355 // best_dist = dist; 440 .height = bounds.height
356 // best_pos = k; 441 };
357 // } 442
358 // } 443 Rectangle textbox_rect = {
359 // return TA_Min_Int(best_pos, text_len); 444 .x = bounds.x + dropdown_w,
360 // } 445 .y = bounds.y,
361 // 446 .width = bounds.width - dropdown_w,
362 // current_visual_line++; 447 .height = bounds.height
363 // line_char_start = i + 1; 448 };
364 // } 449
365 // 450 // Draw combined background
366 // i++; 451 DrawRectangleSelectiveRounded(dropdown_rect, config.corner_radius, 8, config.background_color,
367 // } 452 TRUE, FALSE, FALSE, TRUE);
368 // 453 DrawRectangleSelectiveRounded(textbox_rect, config.corner_radius, 8, config.background_color,
369 // return text_len; 454 FALSE, TRUE, TRUE, FALSE);
370 // } 455
371 // 456 // Draw separator line
372 // static Vector2 GetCursorScreenPos(const char *text, int cursor_pos, Rectangle bounds, 457 DrawLine(dropdown_rect.x + dropdown_rect.width, dropdown_rect.y + 4,
373 // boolean wrap, float scroll_y, int font_size, int line_height) { 458 dropdown_rect.x + dropdown_rect.width, dropdown_rect.y + dropdown_rect.height - 4,
374 // float content_x = bounds.x + TEXT_AREA_PADDING; 459 config.border_color);
375 // float content_y = bounds.y + TEXT_AREA_PADDING - scroll_y; 460
376 // float content_width = bounds.width - TEXT_AREA_PADDING * 2; 461 // Draw border
377 // 462 DrawRectangleSelectiveRoundedLines(bounds, config.corner_radius, 8, config.border_color,
378 // if (!text || cursor_pos == 0) { 463 TRUE, TRUE, TRUE, TRUE);
379 // return (Vector2){content_x, content_y}; 464
380 // } 465 // Custom dropdown with per-item colors
381 // 466 Vector2 mouse_pos = GetMousePosition();
382 // int text_len = strlen(text); 467 int item_count = config.item_count > 0 ? config.item_count : CountDropdownItems(config.dropdown_items);
383 // cursor_pos = TA_Min_Int(cursor_pos, text_len); 468 int font_size = GuiGetStyle(DEFAULT, TEXT_SIZE);
384 // 469 char item_buffer[64];
385 // int visual_line = 0; 470
386 // int line_char_start = 0; 471 // Get current selected item
387 // 472 GetDropdownItem(config.dropdown_items, *config.dropdown_active, item_buffer, sizeof(item_buffer));
388 // for (int i = 0; i <= cursor_pos; i++) { 473 Color current_color = (config.item_colors && *config.dropdown_active < item_count)
389 // if (i == cursor_pos) { 474 ? config.item_colors[*config.dropdown_active]
390 // float x = content_x + MeasureTextRange(text, line_char_start, cursor_pos, font_size); 475 : config.text_color;
391 // float y = content_y + visual_line * line_height; 476
392 // return (Vector2){x, y}; 477 // Draw selected item in dropdown area
393 // } 478 Rectangle dropdown_inner = AddPadding(dropdown_rect, 6);
394 // 479 boolean dropdown_hovered = CheckCollisionPointRec(mouse_pos, dropdown_rect);
395 // if (text[i] == '\n') { 480
396 // visual_line++; 481 // Highlight on hover
397 // line_char_start = i + 1; 482 if (dropdown_hovered && !(*config.dropdown_edit_mode))
398 // } else if (wrap) { 483 {
399 // int line_width = MeasureTextRange(text, line_char_start, i + 1, font_size); 484 DrawRectangleSelectiveRounded(dropdown_rect, config.corner_radius, 8,
400 // if (line_width > content_width && i > line_char_start) { 485 Fade(current_color, 0.5f), TRUE, FALSE, FALSE, TRUE);
401 // int wrap_pos = i; 486 }
402 // for (int j = i; j > line_char_start; j--) { 487
403 // if (text[j] == ' ') { 488 // Draw selected text with color
404 // wrap_pos = j; 489 int text_width = MeasureText(item_buffer, font_size);
405 // break; 490 DrawText(item_buffer,
406 // } 491 dropdown_inner.x + (dropdown_inner.width - text_width) / 2,
407 // } 492 dropdown_inner.y + (dropdown_inner.height - font_size) / 2,
408 // 493 font_size, current_color);
409 // if (cursor_pos <= wrap_pos) { 494 DrawRectangleSelectiveRounded(dropdown_rect, config.corner_radius, 8,
410 // float x = content_x + MeasureTextRange(text, line_char_start, cursor_pos, font_size); 495 Fade(current_color, 0.3f), TRUE, FALSE, FALSE, TRUE);
411 // float y = content_y + visual_line * line_height; 496
412 // return (Vector2){x, y}; 497 // Draw dropdown arrow
413 // } 498 float arrow_size = 6;
414 // 499 float arrow_x = dropdown_rect.x + dropdown_rect.width - 14;
415 // visual_line++; 500 float arrow_y = dropdown_rect.y + dropdown_rect.height / 2;
416 // line_char_start = (text[wrap_pos] == ' ') ? wrap_pos + 1 : wrap_pos; 501 DrawTriangle(
417 // } 502 (Vector2){arrow_x, arrow_y - arrow_size/2},
418 // } 503 (Vector2){arrow_x + arrow_size, arrow_y - arrow_size/2},
419 // } 504 (Vector2){arrow_x + arrow_size/2, arrow_y + arrow_size/2},
420 // 505 config.text_color);
421 // float x = content_x + MeasureTextRange(text, line_char_start, cursor_pos, font_size); 506
422 // float y = content_y + visual_line * line_height; 507 // Handle dropdown click
423 // return (Vector2){x, y}; 508 if (dropdown_hovered && IsMouseButtonPressed(MOUSE_BUTTON_LEFT))
424 // } 509 {
425 // 510 *config.dropdown_edit_mode = !(*config.dropdown_edit_mode);
426 // static float GetContentHeight(const char *text, Rectangle bounds, boolean wrap, 511 }
427 // int font_size, int line_height) { 512
428 // if (!text || strlen(text) == 0) return line_height; 513 // Draw dropdown list when open
429 // 514 if (*config.dropdown_edit_mode)
430 // float content_width = bounds.width - TEXT_AREA_PADDING * 2; 515 {
431 // int visual_lines = 1; 516 float item_height = bounds.height;
432 // int line_char_start = 0; 517 Rectangle list_rect = {
433 // int text_len = strlen(text); 518 .x = dropdown_rect.x,
434 // 519 .y = dropdown_rect.y + dropdown_rect.height + 2,
435 // for (int i = 0; i <= text_len; i++) { 520 .width = dropdown_rect.width,
436 // if (i == text_len || text[i] == '\n') { 521 .height = item_height * item_count
437 // visual_lines++; 522 };
438 // line_char_start = i + 1; 523
439 // } else if (wrap) { 524 // Background
440 // int line_width = MeasureTextRange(text, line_char_start, i + 1, font_size); 525 DrawRectangleRounded(list_rect, 0.1f, 8, config.background_color);
441 // if (line_width > content_width && i > line_char_start) { 526 DrawRectangleRoundedLines(list_rect, 0.1f, 8, config.border_color);
442 // int wrap_pos = i; 527
443 // for (int j = i; j > line_char_start; j--) { 528 // Draw each item
444 // if (text[j] == ' ') { 529 for (int i = 0; i < item_count; i++)
445 // wrap_pos = j; 530 {
446 // break; 531 Rectangle item_rect = {
447 // } 532 .x = list_rect.x,
448 // } 533 .y = list_rect.y + i * item_height,
449 // visual_lines++; 534 .width = list_rect.width,
450 // line_char_start = (text[wrap_pos] == ' ') ? wrap_pos + 1 : wrap_pos; 535 .height = item_height
451 // } 536 };
452 // } 537
453 // } 538 GetDropdownItem(config.dropdown_items, i, item_buffer, sizeof(item_buffer));
454 // 539 Color item_color = (config.item_colors && i < item_count)
455 // return visual_lines * line_height; 540 ? config.item_colors[i]
456 // } 541 : config.text_color;
457 // 542
458 // static void PushUndoState(TextAreaState *state, const char *text, Dowa_Arena *arena) { 543 boolean item_hovered = CheckCollisionPointRec(mouse_pos, item_rect);
459 // TextAreaUndoEntry entry; 544
460 // entry.text = Dowa_String_Copy_Arena((char*)text, arena); 545 // Highlight hovered item
461 // entry.cursor_pos = state->cursor_pos; 546 if (item_hovered)
462 // entry.selection_start = state->selection_start; 547 DrawRectangleRec(item_rect, Fade(item_color, 0.15f));
463 // entry.selection_end = state->selection_end; 548
464 // 549 // Highlight selected item
465 // if (state->undo_index < (int)Dowa_Array_Length(state->undo_stack) - 1) { 550 DrawRectangleRec(item_rect, Fade(item_color, 0.2f));
466 // dowa__header(state->undo_stack)->length = state->undo_index + 1; 551
467 // } 552 // Draw item text
468 // 553 text_width = MeasureText(item_buffer, font_size);
469 // Dowa_Array_Push_Arena(state->undo_stack, entry, arena); 554 DrawText(item_buffer,
470 // state->undo_index = Dowa_Array_Length(state->undo_stack) - 1; 555 item_rect.x + (item_rect.width - text_width) / 2,
471 // 556 item_rect.y + (item_rect.height - font_size) / 2,
472 // if (Dowa_Array_Length(state->undo_stack) > TEXT_AREA_MAX_UNDO_STATES) { 557 font_size, item_color);
473 // for (int i = 0; i < (int)Dowa_Array_Length(state->undo_stack) - 1; i++) { 558
474 // state->undo_stack[i] = state->undo_stack[i + 1]; 559 // Handle item click
475 // } 560 if (item_hovered && IsMouseButtonPressed(MOUSE_BUTTON_LEFT))
476 // dowa__header(state->undo_stack)->length--; 561 {
477 // state->undo_index--; 562 *config.dropdown_active = i;
478 // } 563 *config.dropdown_edit_mode = FALSE;
479 // } 564 }
480 // 565 }
481 // static boolean PerformUndo(TextAreaState *state, char *text, int text_size) { 566
482 // if (state->undo_index <= 0) return FALSE; 567 // Close dropdown if clicked outside
483 // 568 if (!CheckCollisionPointRec(mouse_pos, list_rect) &&
484 // state->undo_index--; 569 !CheckCollisionPointRec(mouse_pos, dropdown_rect) &&
485 // TextAreaUndoEntry *entry = &state->undo_stack[state->undo_index]; 570 IsMouseButtonPressed(MOUSE_BUTTON_LEFT))
486 // 571 {
487 // strncpy(text, entry->text, text_size - 1); 572 *config.dropdown_edit_mode = FALSE;
488 // text[text_size - 1] = '\0'; 573 }
489 // state->cursor_pos = entry->cursor_pos; 574 }
490 // state->selection_start = entry->selection_start; 575
491 // state->selection_end = entry->selection_end; 576 // Draw text box (using raygui)
492 // 577 Rectangle textbox_inner = AddPadding(textbox_rect, 4);
493 // return TRUE; 578 boolean ctrl_pressed = IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL) || IsKeyDown(KEY_LEFT_SUPER) || IsKeyDown(KEY_RIGHT_SUPER);
494 // } 579 if (*config.text_edit_mode && ctrl_pressed && IsKeyPressed(KEY_V))
495 // 580 {
496 // static boolean PerformRedo(TextAreaState *state, char *text, int text_size) { 581 const char *clipboard = GetClipboardText();
497 // if (state->undo_index >= (int)Dowa_Array_Length(state->undo_stack) - 1) return FALSE; 582 snprintf(config.text_buffer, config.text_buffer_size, "%s", clipboard);
498 // 583 }
499 // state->undo_index++; 584 if (GuiTextBox(textbox_inner, config.text_buffer, config.text_buffer_size, *config.text_edit_mode))
500 // TextAreaUndoEntry *entry = &state->undo_stack[state->undo_index]; 585 {
501 // 586 *config.text_edit_mode = !(*config.text_edit_mode);
502 // strncpy(text, entry->text, text_size - 1); 587 result = TRUE;
503 // text[text_size - 1] = '\0'; 588 }
504 // state->cursor_pos = entry->cursor_pos; 589
505 // state->selection_start = entry->selection_start; 590 return result;
506 // state->selection_end = entry->selection_end; 591 }
507 //
508 // return TRUE;
509 // }
510 //
511 // static void InsertTextAtCursor(char *text, int text_size, int cursor_pos,
512 // const char *insert_text, int *new_cursor_pos) {
513 // int text_len = strlen(text);
514 // int insert_len = strlen(insert_text);
515 //
516 // if (text_len + insert_len >= text_size - 1) {
517 // insert_len = text_size - 1 - text_len;
518 // if (insert_len <= 0) return;
519 // }
520 //
521 // memmove(text + cursor_pos + insert_len,
522 // text + cursor_pos,
523 // text_len - cursor_pos + 1);
524 //
525 // memcpy(text + cursor_pos, insert_text, insert_len);
526 //
527 // *new_cursor_pos = cursor_pos + insert_len;
528 // }
529 //
530 // static void DeleteTextRange(char *text, int start, int end) {
531 // if (start >= end) return;
532 // int text_len = strlen(text);
533 // if (start < 0) start = 0;
534 // if (end > text_len) end = text_len;
535 //
536 // memmove(text + start, text + end, text_len - end + 1);
537 // }
538 //
539 // static char* GetSelectedText(const char *text, int sel_start, int sel_end, Dowa_Arena *arena) {
540 // if (sel_start < 0 || sel_end < 0 || sel_start >= sel_end) return NULL;
541 //
542 // int len = sel_end - sel_start;
543 // char *result = Dowa_Arena_Allocate(arena, len + 1);
544 // strncpy(result, text + sel_start, len);
545 // result[len] = '\0';
546 // return result;
547 // }
548 //
549 // boolean GuiTextArea(int id, Rectangle bounds, char *text, int text_size, boolean is_edit_mode,
550 // boolean should_text_wrap, Dowa_Arena *arena) {
551 // boolean should_toggle_edit_mode = FALSE;
552 //
553 // // Get or create state for this text area
554 // TextAreaState *state = GetTextAreaState(id);
555 // if (!state)
556 // {
557 // state = CreateTextAreaState(id);
558 // state->cursor_pos = strlen(text);
559 // state->last_blink_time = GetTime();
560 // PushUndoState(state, text, arena);
561 // }
562 //
563 // int text_len = strlen(text);
564 // Vector2 mouse_pos = GetMousePosition();
565 // boolean mouse_in_bounds = CheckCollisionPointRec(mouse_pos, bounds);
566 //
567 // // Handle click to enter/exit edit mode
568 // if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) {
569 // if (mouse_in_bounds && !is_edit_mode) {
570 // should_toggle_edit_mode = TRUE;
571 // } else if (!mouse_in_bounds && is_edit_mode) {
572 // should_toggle_edit_mode = TRUE;
573 // }
574 // }
575 //
576 // // Content area
577 // float content_height = GetContentHeight(text, bounds, should_text_wrap,
578 // GuiGetStyle(DEFAULT, TEXT_SIZE), TEXT_AREA_LINE_HEIGHT);
579 // float visible_height = bounds.height - TEXT_AREA_PADDING * 2;
580 // float max_scroll = TA_Max_Float(0, content_height - visible_height);
581 //
582 // // Handle scrolling
583 // float wheel = GetMouseWheelMove();
584 // if (mouse_in_bounds && wheel != 0) {
585 // state->scroll_offset_y -= wheel * TEXT_AREA_LINE_HEIGHT * 3;
586 // state->scroll_offset_y = TA_Max_Float(0, TA_Min_Float(state->scroll_offset_y, max_scroll));
587 // }
588 //
589 // if (is_edit_mode) {
590 // boolean ctrl_pressed = IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL) ||
591 // IsKeyDown(KEY_LEFT_SUPER) || IsKeyDown(KEY_RIGHT_SUPER);
592 // boolean shift_pressed = IsKeyDown(KEY_LEFT_SHIFT) || IsKeyDown(KEY_RIGHT_SHIFT);
593 // boolean text_changed = FALSE;
594 //
595 // double current_time = GetTime();
596 // if (current_time - state->last_blink_time > 0.5) {
597 // state->cursor_visible = !state->cursor_visible;
598 // state->last_blink_time = current_time;
599 // }
600 //
601 // // Mouse Selection
602 // if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && mouse_in_bounds) {
603 // int click_pos = GetCharIndexFromPos(text, bounds, mouse_pos, should_text_wrap,
604 // state->scroll_offset_y, GuiGetStyle(DEFAULT, TEXT_SIZE),
605 // TEXT_AREA_LINE_HEIGHT);
606 // state->cursor_pos = click_pos;
607 // state->selection_start = -1;
608 // state->selection_end = -1;
609 // state->is_selecting = TRUE;
610 // state->cursor_visible = TRUE;
611 // state->last_blink_time = current_time;
612 // }
613 //
614 // if (state->is_selecting && IsMouseButtonDown(MOUSE_BUTTON_LEFT)) {
615 // int drag_pos = GetCharIndexFromPos(text, bounds, mouse_pos, should_text_wrap,
616 // state->scroll_offset_y, GuiGetStyle(DEFAULT, TEXT_SIZE),
617 // TEXT_AREA_LINE_HEIGHT);
618 // if (drag_pos != state->cursor_pos) {
619 // if (state->selection_start < 0) {
620 // state->selection_start = state->cursor_pos;
621 // }
622 // state->selection_end = drag_pos;
623 // }
624 // }
625 //
626 // if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT)) {
627 // state->is_selecting = FALSE;
628 // if (state->selection_start >= 0 && state->selection_end >= 0) {
629 // if (state->selection_start > state->selection_end) {
630 // int temp = state->selection_start;
631 // state->selection_start = state->selection_end;
632 // state->selection_end = temp;
633 // }
634 // if (state->selection_start == state->selection_end) {
635 // state->selection_start = -1;
636 // state->selection_end = -1;
637 // }
638 // }
639 // }
640 //
641 // // Ctrl+A: Select All
642 // if (ctrl_pressed && IsKeyPressed(KEY_A)) {
643 // state->selection_start = 0;
644 // state->selection_end = text_len;
645 // state->cursor_pos = text_len;
646 // }
647 //
648 // // Ctrl+C: Copy
649 // if (ctrl_pressed && IsKeyPressed(KEY_C)) {
650 // if (state->selection_start >= 0 && state->selection_end >= 0 &&
651 // state->selection_start != state->selection_end) {
652 // int sel_min = TA_Min_Int(state->selection_start, state->selection_end);
653 // int sel_max = TA_Max_Int(state->selection_start, state->selection_end);
654 // char *selected = GetSelectedText(text, sel_min, sel_max, arena);
655 // if (selected) {
656 // if (g_clipboard_text) free(g_clipboard_text);
657 // g_clipboard_text = strdup(selected);
658 // SetClipboardText(g_clipboard_text);
659 // }
660 // }
661 // }
662 //
663 // // Ctrl+X: Cut
664 // if (ctrl_pressed && IsKeyPressed(KEY_X)) {
665 // if (state->selection_start >= 0 && state->selection_end >= 0 &&
666 // state->selection_start != state->selection_end) {
667 // int sel_min = TA_Min_Int(state->selection_start, state->selection_end);
668 // int sel_max = TA_Max_Int(state->selection_start, state->selection_end);
669 // char *selected = GetSelectedText(text, sel_min, sel_max, arena);
670 // if (selected) {
671 // if (g_clipboard_text) free(g_clipboard_text);
672 // g_clipboard_text = strdup(selected);
673 // SetClipboardText(g_clipboard_text);
674 // }
675 //
676 // PushUndoState(state, text, arena);
677 // DeleteTextRange(text, sel_min, sel_max);
678 // state->cursor_pos = sel_min;
679 // state->selection_start = -1;
680 // state->selection_end = -1;
681 // text_changed = TRUE;
682 // }
683 // }
684 //
685 // // Ctrl+V: Paste
686 // if (ctrl_pressed && IsKeyPressed(KEY_V)) {
687 // const char *clipboard = GetClipboardText();
688 // if (clipboard && strlen(clipboard) > 0) {
689 // if (state->selection_start >= 0 && state->selection_end >= 0 &&
690 // state->selection_start != state->selection_end) {
691 // int sel_min = TA_Min_Int(state->selection_start, state->selection_end);
692 // int sel_max = TA_Max_Int(state->selection_start, state->selection_end);
693 // PushUndoState(state, text, arena);
694 // DeleteTextRange(text, sel_min, sel_max);
695 // state->cursor_pos = sel_min;
696 // state->selection_start = -1;
697 // state->selection_end = -1;
698 // } else {
699 // PushUndoState(state, text, arena);
700 // }
701 //
702 // int new_cursor;
703 // InsertTextAtCursor(text, text_size, state->cursor_pos, clipboard, &new_cursor);
704 // state->cursor_pos = new_cursor;
705 // text_changed = TRUE;
706 // }
707 // }
708 //
709 // // Ctrl+Z: Undo / Ctrl+Shift+Z: Redo
710 // if (ctrl_pressed && IsKeyPressed(KEY_Z)) {
711 // if (shift_pressed) {
712 // PerformRedo(state, text, text_size);
713 // } else {
714 // PerformUndo(state, text, text_size);
715 // }
716 // }
717 //
718 // // Arrow Keys Navigation
719 // if (IsKeyPressed(KEY_LEFT) || IsKeyPressedRepeat(KEY_LEFT)) {
720 // if (state->selection_start >= 0 && !shift_pressed) {
721 // state->cursor_pos = TA_Min_Int(state->selection_start, state->selection_end);
722 // state->selection_start = -1;
723 // state->selection_end = -1;
724 // } else if (state->cursor_pos > 0) {
725 // if (shift_pressed) {
726 // if (state->selection_start < 0) {
727 // state->selection_start = state->cursor_pos;
728 // state->selection_end = state->cursor_pos;
729 // }
730 // state->cursor_pos--;
731 // state->selection_end = state->cursor_pos;
732 // } else {
733 // state->cursor_pos--;
734 // }
735 // }
736 // state->cursor_visible = TRUE;
737 // state->last_blink_time = current_time;
738 // }
739 //
740 // if (IsKeyPressed(KEY_RIGHT) || IsKeyPressedRepeat(KEY_RIGHT)) {
741 // if (state->selection_start >= 0 && !shift_pressed) {
742 // state->cursor_pos = TA_Max_Int(state->selection_start, state->selection_end);
743 // state->selection_start = -1;
744 // state->selection_end = -1;
745 // } else if (state->cursor_pos < text_len) {
746 // if (shift_pressed) {
747 // if (state->selection_start < 0) {
748 // state->selection_start = state->cursor_pos;
749 // state->selection_end = state->cursor_pos;
750 // }
751 // state->cursor_pos++;
752 // state->selection_end = state->cursor_pos;
753 // } else {
754 // state->cursor_pos++;
755 // }
756 // }
757 // state->cursor_visible = TRUE;
758 // state->last_blink_time = current_time;
759 // }
760 //
761 // if (IsKeyPressed(KEY_UP) || IsKeyPressedRepeat(KEY_UP)) {
762 // int line, col;
763 // GetLineAndColumn(text, state->cursor_pos, &line, &col);
764 // if (line > 0) {
765 // int new_pos = GetPosFromLineColumn(text, line - 1, col);
766 // if (shift_pressed) {
767 // if (state->selection_start < 0) {
768 // state->selection_start = state->cursor_pos;
769 // state->selection_end = state->cursor_pos;
770 // }
771 // state->selection_end = new_pos;
772 // } else {
773 // state->selection_start = -1;
774 // state->selection_end = -1;
775 // }
776 // state->cursor_pos = new_pos;
777 // }
778 // state->cursor_visible = TRUE;
779 // state->last_blink_time = current_time;
780 // }
781 //
782 // if (IsKeyPressed(KEY_DOWN) || IsKeyPressedRepeat(KEY_DOWN))
783 // {
784 // int line, col;
785 // GetLineAndColumn(text, state->cursor_pos, &line, &col);
786 // int total_lines = CountLines(text);
787 // if (line < total_lines - 1) {
788 // int new_pos = GetPosFromLineColumn(text, line + 1, col);
789 // if (shift_pressed) {
790 // if (state->selection_start < 0) {
791 // state->selection_start = state->cursor_pos;
792 // state->selection_end = state->cursor_pos;
793 // }
794 // state->selection_end = new_pos;
795 // } else {
796 // state->selection_start = -1;
797 // state->selection_end = -1;
798 // }
799 // state->cursor_pos = new_pos;
800 // }
801 // state->cursor_visible = TRUE;
802 // state->last_blink_time = current_time;
803 // }
804 //
805 // // Home/End keys
806 // if (IsKeyPressed(KEY_HOME)) {
807 // int line_start = GetLineStart(text, state->cursor_pos);
808 // if (shift_pressed) {
809 // if (state->selection_start < 0) {
810 // state->selection_start = state->cursor_pos;
811 // state->selection_end = state->cursor_pos;
812 // }
813 // state->selection_end = line_start;
814 // } else {
815 // state->selection_start = -1;
816 // state->selection_end = -1;
817 // }
818 // state->cursor_pos = line_start;
819 // }
820 //
821 // if (IsKeyPressed(KEY_END)) {
822 // int line_end = GetLineEnd(text, state->cursor_pos);
823 // if (shift_pressed) {
824 // if (state->selection_start < 0) {
825 // state->selection_start = state->cursor_pos;
826 // state->selection_end = state->cursor_pos;
827 // }
828 // state->selection_end = line_end;
829 // } else {
830 // state->selection_start = -1;
831 // state->selection_end = -1;
832 // }
833 // state->cursor_pos = line_end;
834 // }
835 //
836 // // Text Input
837 // if (!ctrl_pressed) {
838 // int key = GetCharPressed();
839 // while (key > 0) {
840 // if (key >= 32 && key <= 126) {
841 // if (state->selection_start >= 0 && state->selection_end >= 0 &&
842 // state->selection_start != state->selection_end) {
843 // int sel_min = TA_Min_Int(state->selection_start, state->selection_end);
844 // int sel_max = TA_Max_Int(state->selection_start, state->selection_end);
845 // PushUndoState(state, text, arena);
846 // DeleteTextRange(text, sel_min, sel_max);
847 // state->cursor_pos = sel_min;
848 // state->selection_start = -1;
849 // state->selection_end = -1;
850 // text_changed = TRUE;
851 // } else if (!text_changed) {
852 // PushUndoState(state, text, arena);
853 // }
854 //
855 // char insert_str[2] = {(char)key, '\0'};
856 // int new_cursor;
857 // InsertTextAtCursor(text, text_size, state->cursor_pos, insert_str, &new_cursor);
858 // state->cursor_pos = new_cursor;
859 // text_changed = TRUE;
860 // }
861 // key = GetCharPressed();
862 // }
863 // }
864 //
865 // // Enter key
866 // if (IsKeyPressed(KEY_ENTER) || IsKeyPressed(KEY_KP_ENTER)) {
867 // if (state->selection_start >= 0 && state->selection_end >= 0 &&
868 // state->selection_start != state->selection_end) {
869 // int sel_min = TA_Min_Int(state->selection_start, state->selection_end);
870 // int sel_max = TA_Max_Int(state->selection_start, state->selection_end);
871 // PushUndoState(state, text, arena);
872 // DeleteTextRange(text, sel_min, sel_max);
873 // state->cursor_pos = sel_min;
874 // state->selection_start = -1;
875 // state->selection_end = -1;
876 // } else {
877 // PushUndoState(state, text, arena);
878 // }
879 //
880 // int new_cursor;
881 // InsertTextAtCursor(text, text_size, state->cursor_pos, "\n", &new_cursor);
882 // state->cursor_pos = new_cursor;
883 // text_changed = TRUE;
884 // }
885 //
886 // // Backspace
887 // if (IsKeyPressed(KEY_BACKSPACE) || IsKeyPressedRepeat(KEY_BACKSPACE)) {
888 // if (state->selection_start >= 0 && state->selection_end >= 0 &&
889 // state->selection_start != state->selection_end) {
890 // int sel_min = TA_Min_Int(state->selection_start, state->selection_end);
891 // int sel_max = TA_Max_Int(state->selection_start, state->selection_end);
892 // PushUndoState(state, text, arena);
893 // DeleteTextRange(text, sel_min, sel_max);
894 // state->cursor_pos = sel_min;
895 // state->selection_start = -1;
896 // state->selection_end = -1;
897 // text_changed = TRUE;
898 // } else if (state->cursor_pos > 0) {
899 // PushUndoState(state, text, arena);
900 // DeleteTextRange(text, state->cursor_pos - 1, state->cursor_pos);
901 // state->cursor_pos--;
902 // text_changed = TRUE;
903 // }
904 // }
905 //
906 // // Delete key
907 // if (IsKeyPressed(KEY_DELETE) || IsKeyPressedRepeat(KEY_DELETE)) {
908 // text_len = strlen(text);
909 // if (state->selection_start >= 0 && state->selection_end >= 0 &&
910 // state->selection_start != state->selection_end) {
911 // int sel_min = TA_Min_Int(state->selection_start, state->selection_end);
912 // int sel_max = TA_Max_Int(state->selection_start, state->selection_end);
913 // PushUndoState(state, text, arena);
914 // DeleteTextRange(text, sel_min, sel_max);
915 // state->cursor_pos = sel_min;
916 // state->selection_start = -1;
917 // state->selection_end = -1;
918 // text_changed = TRUE;
919 // } else if (state->cursor_pos < text_len) {
920 // PushUndoState(state, text, arena);
921 // DeleteTextRange(text, state->cursor_pos, state->cursor_pos + 1);
922 // text_changed = TRUE;
923 // }
924 // }
925 //
926 // // Tab key
927 // if (IsKeyPressed(KEY_TAB)) {
928 // if (state->selection_start >= 0 && state->selection_end >= 0 &&
929 // state->selection_start != state->selection_end) {
930 // int sel_min = TA_Min_Int(state->selection_start, state->selection_end);
931 // int sel_max = TA_Max_Int(state->selection_start, state->selection_end);
932 // PushUndoState(state, text, arena);
933 // DeleteTextRange(text, sel_min, sel_max);
934 // state->cursor_pos = sel_min;
935 // state->selection_start = -1;
936 // state->selection_end = -1;
937 // } else {
938 // PushUndoState(state, text, arena);
939 // }
940 //
941 // int new_cursor;
942 // InsertTextAtCursor(text, text_size, state->cursor_pos, " ", &new_cursor);
943 // state->cursor_pos = new_cursor;
944 // text_changed = TRUE;
945 // }
946 //
947 // // Auto-scroll to keep cursor visible
948 // Vector2 cursor_screen = GetCursorScreenPos(text, state->cursor_pos, bounds,
949 // should_text_wrap, state->scroll_offset_y,
950 // GuiGetStyle(DEFAULT, TEXT_SIZE), TEXT_AREA_LINE_HEIGHT);
951 //
952 // float visible_top = bounds.y + TEXT_AREA_PADDING;
953 // float visible_bottom = bounds.y + bounds.height - TEXT_AREA_PADDING - TEXT_AREA_LINE_HEIGHT;
954 //
955 // if (cursor_screen.y < visible_top) {
956 // state->scroll_offset_y -= visible_top - cursor_screen.y;
957 // } else if (cursor_screen.y > visible_bottom) {
958 // state->scroll_offset_y += cursor_screen.y - visible_bottom;
959 // }
960 //
961 // state->scroll_offset_y = TA_Max_Float(0, TA_Min_Float(state->scroll_offset_y, max_scroll));
962 // }
963 //
964 // // Drawing
965 // DrawRectangleRec(bounds, is_edit_mode ? DARKGRAY : (Color){40, 40, 40, 255});
966 // DrawRectangleLinesEx(bounds, 1, is_edit_mode ? WHITE : GRAY);
967 //
968 // BeginScissorMode((int)bounds.x, (int)bounds.y, (int)bounds.width, (int)bounds.height);
969 //
970 // float content_x = bounds.x + TEXT_AREA_PADDING;
971 // float content_y = bounds.y + TEXT_AREA_PADDING - state->scroll_offset_y;
972 // float content_width = bounds.width - TEXT_AREA_PADDING * 2;
973 //
974 // text_len = strlen(text);
975 //
976 // // Draw selection highlight
977 // if (state->selection_start >= 0 && state->selection_end >= 0 &&
978 // state->selection_start != state->selection_end) {
979 // int sel_min = TA_Min_Int(state->selection_start, state->selection_end);
980 // int sel_max = TA_Max_Int(state->selection_start, state->selection_end);
981 //
982 // int line_char_start = 0;
983 // int visual_line = 0;
984 //
985 // for (int i = 0; i <= text_len; i++) {
986 // boolean is_end = (i == text_len);
987 // boolean is_newline = (!is_end && text[i] == '\n');
988 // boolean should_draw_line = is_end || is_newline;
989 //
990 // if (should_text_wrap && !is_end && !is_newline) {
991 // int line_width = MeasureTextRange(text, line_char_start, i + 1, GuiGetStyle(DEFAULT, TEXT_SIZE));
992 // if (line_width > content_width && i > line_char_start) {
993 // should_draw_line = TRUE;
994 // }
995 // }
996 //
997 // if (should_draw_line) {
998 // int line_end = i;
999 //
1000 // if (sel_min < line_end && sel_max > line_char_start) {
1001 // int highlight_start = TA_Max_Int(sel_min, line_char_start);
1002 // int highlight_end = TA_Min_Int(sel_max, line_end);
1003 //
1004 // float x1 = content_x + MeasureTextRange(text, line_char_start, highlight_start, GuiGetStyle(DEFAULT, TEXT_SIZE));
1005 // float x2 = content_x + MeasureTextRange(text, line_char_start, highlight_end, GuiGetStyle(DEFAULT, TEXT_SIZE));
1006 // float y = content_y + visual_line * TEXT_AREA_LINE_HEIGHT;
1007 //
1008 // DrawRectangle((int)x1, (int)y, (int)(x2 - x1), TEXT_AREA_LINE_HEIGHT,
1009 // Fade(SKYBLUE, 0.5f));
1010 // }
1011 //
1012 // visual_line++;
1013 // line_char_start = i + 1;
1014 // }
1015 // }
1016 // }
1017 //
1018 // // Draw text with wrapping support
1019 // if (should_text_wrap) {
1020 // int line_char_start = 0;
1021 // int visual_line = 0;
1022 //
1023 // for (int i = 0; i <= text_len; i++) {
1024 // boolean is_end = (i == text_len);
1025 // boolean is_newline = (!is_end && text[i] == '\n');
1026 // boolean should_draw_line = is_end || is_newline;
1027 //
1028 // if (!is_end && !is_newline) {
1029 // int line_width = MeasureTextRange(text, line_char_start, i + 1, GuiGetStyle(DEFAULT, TEXT_SIZE));
1030 // if (line_width > content_width && i > line_char_start) {
1031 // should_draw_line = TRUE;
1032 // }
1033 // }
1034 //
1035 // if (should_draw_line && i > line_char_start) {
1036 // char line_buffer[1024];
1037 // int line_len = TA_Min_Int(i - line_char_start, 1023);
1038 // strncpy(line_buffer, text + line_char_start, line_len);
1039 // line_buffer[line_len] = '\0';
1040 //
1041 // Vector2 draw_text_vector = {
1042 // .x = content_x,
1043 // .y = content_y + visual_line * TEXT_AREA_LINE_HEIGHT
1044 // };
1045 // DrawTextEx(GuiGetFont(), line_buffer, draw_text_vector,
1046 // GuiGetStyle(DEFAULT, TEXT_SIZE), TEXT_SIZE_DEFAULT/GuiGetStyle(DEFAULT, TEXT_SIZE), WHITE);
1047 //
1048 // visual_line++;
1049 // line_char_start = is_newline ? i + 1 : i;
1050 // } else if (is_newline) {
1051 // visual_line++;
1052 // line_char_start = i + 1;
1053 // }
1054 // }
1055 // } else {
1056 // int line_start = 0;
1057 // int visual_line = 0;
1058 //
1059 // for (int i = 0; i <= text_len; i++) {
1060 // if (i == text_len || text[i] == '\n') {
1061 // if (i > line_start) {
1062 // char line_buffer[1024];
1063 // int line_len = TA_Min_Int(i - line_start, 1023);
1064 // strncpy(line_buffer, text + line_start, line_len);
1065 // line_buffer[line_len] = '\0';
1066 //
1067 // Vector2 draw_text_vector = {
1068 // .x = content_x,
1069 // .y = content_y + visual_line * TEXT_AREA_LINE_HEIGHT
1070 // };
1071 // DrawTextEx(GuiGetFont(), line_buffer, draw_text_vector ,
1072 // GuiGetStyle(DEFAULT, TEXT_SIZE), TEXT_SIZE_DEFAULT/GuiGetStyle(DEFAULT, TEXT_SIZE), WHITE);
1073 // }
1074 // visual_line++;
1075 // line_start = i + 1;
1076 // }
1077 // }
1078 // }
1079 //
1080 // // Draw cursor
1081 // if (is_edit_mode && state->cursor_visible) {
1082 // Vector2 cursor_pos = GetCursorScreenPos(text, state->cursor_pos, bounds,
1083 // should_text_wrap, state->scroll_offset_y,
1084 // GuiGetStyle(DEFAULT, TEXT_SIZE), TEXT_AREA_LINE_HEIGHT);
1085 //
1086 // DrawRectangle((int)cursor_pos.x, (int)cursor_pos.y,
1087 // TEXT_AREA_CURSOR_WIDTH, TEXT_AREA_LINE_HEIGHT, WHITE);
1088 // }
1089 //
1090 // EndScissorMode();
1091 //
1092 // // Draw scrollbar if needed
1093 // if (max_scroll > 0) {
1094 // float scrollbar_height = (visible_height / content_height) * visible_height;
1095 // float scrollbar_y = bounds.y + TEXT_AREA_PADDING +
1096 // (state->scroll_offset_y / max_scroll) * (visible_height - scrollbar_height);
1097 //
1098 // DrawRectangle((int)(bounds.x + bounds.width - 8), (int)scrollbar_y,
1099 // 6, (int)scrollbar_height, Fade(WHITE, 0.3f));
1100 // }
1101 //
1102 // return should_toggle_edit_mode;
1103 // }
1104 //
1105 // void GuiTextAreaResetState(int id) {
1106 // TextAreaState *state = GetTextAreaState(id);
1107 // if (state) {
1108 // if (state->undo_stack) {
1109 // Dowa_Array_Free(state->undo_stack);
1110 // state->undo_stack = NULL;
1111 // }
1112 // state->is_initialized = FALSE;
1113 // }
1114 // }
1115 //
1116 // void GuiTextAreaResetAllStates(void) {
1117 // for (int i = 0; i < g_text_area_state_count; i++) {
1118 // if (g_text_area_states[i].undo_stack) {
1119 // Dowa_Array_Free(g_text_area_states[i].undo_stack);
1120 // g_text_area_states[i].undo_stack = NULL;
1121 // }
1122 // g_text_area_states[i].is_initialized = FALSE;
1123 // }
1124 // g_text_area_state_count = 0;
1125 // }