Mercurial
comparison third_party/raylib/custom.c @ 173:827c6ac504cd hg-web
Merged in default here.
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Mon, 19 Jan 2026 18:59:10 -0800 |
| parents | 058de208e640 |
| children |
comparison
equal
deleted
inserted
replaced
| 151:c033667da5f9 | 173:827c6ac504cd |
|---|---|
| 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 | 4 #include "third_party/raylib/custom.h" |
| 5 // -- forward declarations --// | 5 #include <string.h> |
| 6 | 6 |
| 7 // --- Default Behaviour that should be part of all raylib gui ---/ | 7 // ============================================================================ |
| 8 void DefaultBehaviours(); | 8 // COLOR SCHEME IMPLEMENTATION |
| 9 // --- Increase Font Sizes on key press --- // | 9 // ============================================================================ |
| 10 void IncreaseFontSize(); | 10 |
| 11 // --- Decrease Font Sizes on key press --- // | 11 // Global color scheme instance |
| 12 void DecreaseFontSize(); | 12 ColorScheme g_colors = {0}; |
| 13 | 13 |
| 14 | 14 ColorScheme PostDog_DefaultColorScheme(void) |
| 15 void DefaultBehaviours() | 15 { |
| 16 { | 16 return (ColorScheme){ |
| 17 // Font sizes | 17 .primary = (Color){255, 140, 0, 255}, // Orange |
| 18 IncreaseFontSize(); | 18 .secondary = (Color){255, 248, 231, 255}, // Beige/Egg white |
| 19 DecreaseFontSize(); | 19 .background = (Color){255, 255, 255, 255}, // White |
| 20 } | 20 .text = (Color){30, 30, 30, 255}, // Near black |
| 21 | 21 .text_light = (Color){255, 255, 255, 255}, // White |
| 22 void IncreaseFontSize() | 22 .border = (Color){200, 200, 200, 255}, // Light gray |
| 23 { | 23 .highlight = (Color){255, 180, 100, 255}, // Light orange |
| 24 if ((IsKeyDown(KEY_LEFT_SUPER) || IsKeyDown(KEY_LEFT_CONTROL)) && IsKeyDown(KEY_EQUAL)) | 24 .success = (Color){76, 175, 80, 255}, // Green |
| 25 GuiSetStyle(DEFAULT, TEXT_SIZE, GuiGetStyle(DEFAULT, TEXT_SIZE) + 1); | 25 .error = (Color){244, 67, 54, 255}, // Red |
| 26 } | 26 .muted = (Color){158, 158, 158, 255}, // Gray |
| 27 | 27 }; |
| 28 void DecreaseFontSize() | 28 } |
| 29 { | 29 |
| 30 if ((IsKeyDown(KEY_LEFT_SUPER) || IsKeyDown(KEY_LEFT_CONTROL)) && IsKeyDown(KEY_MINUS)) | 30 ColorScheme PostDog_DarkColorScheme(void) |
| 31 GuiSetStyle(DEFAULT, TEXT_SIZE, GuiGetStyle(DEFAULT, TEXT_SIZE) - 1); | 31 { |
| 32 } | 32 return (ColorScheme){ |
| 33 | 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); | |
| 76 } | |
| 34 | 77 |
| 35 // --- Layout helper --- // | 78 // --- Layout helper --- // |
| 36 Rectangle RightOf(Rectangle ref, float padding) | 79 Rectangle RightOf(Rectangle ref, float padding) |
| 37 { | 80 { |
| 38 return (Rectangle){ | 81 return (Rectangle){ |
| 82 .height = container.height | 125 .height = container.height |
| 83 }; | 126 }; |
| 84 } | 127 } |
| 85 | 128 |
| 86 | 129 |
| 130 void DrawRectangleSelectiveRounded(Rectangle rec, float radius, int segments, Color color, | |
| 131 boolean roundTL, boolean roundTR, boolean roundBR, boolean roundBL) { | |
| 132 // Clamp radius to valid range | |
| 133 float maxR = fminf(rec.width, rec.height) * 0.5f; | |
| 134 radius = fminf(fmaxf(radius, 0.0f), maxR); | |
| 135 | |
| 136 // Fill center | |
| 137 DrawRectangleRec((Rectangle){ | |
| 138 rec.x + radius, rec.y + radius, | |
| 139 rec.width - 2.0f * radius, rec.height - 2.0f * radius | |
| 140 }, color); | |
| 141 | |
| 142 // | |
| 143 // |--------| | |
| 144 // | | | |
| 145 // | | | |
| 146 // |--------| | |
| 147 | |
| 148 // Top edge | |
| 149 float topStart = rec.x + (roundTL ? radius : 0.0f); | |
| 150 float topWidth = rec.width - (roundTL ? radius : 0.0f) - (roundTR ? radius : 0.0f); | |
| 151 if (topWidth > 0.0f) | |
| 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 } | |
| 185 } | |
| 186 | |
| 187 Rectangle AddPadding(Rectangle rect, float padding) | |
| 188 { | |
| 189 return (Rectangle){ | |
| 190 rect.x + padding, | |
| 191 rect.y + padding, | |
| 192 rect.width - (2 * padding), | |
| 193 rect.height - (2 * padding) | |
| 194 }; | |
| 195 } | |
| 196 | |
| 197 Rectangle AddPaddingAll(Rectangle rect, float top, float right,float down, float left) | |
| 198 { | |
| 199 return (Rectangle){ | |
| 200 rect.x + left, | |
| 201 rect.y + top, | |
| 202 rect.width - (right + left), | |
| 203 rect.height - (top + down), | |
| 204 }; | |
| 205 } | |
| 206 | |
| 207 | |
| 208 | |
| 209 Rectangle AddPaddingHorizontal(Rectangle rect, float padding) | |
| 210 { | |
| 211 return (Rectangle){ | |
| 212 rect.x + padding, | |
| 213 rect.y, | |
| 214 rect.width - (2 * padding), | |
| 215 rect.height | |
| 216 }; | |
| 217 } | |
| 218 | |
| 219 Rectangle AddPaddingVertical(Rectangle rect, float padding) | |
| 220 { | |
| 221 return (Rectangle){ | |
| 222 rect.x, | |
| 223 rect.y + padding, | |
| 224 rect.width, | |
| 225 rect.height - (2 * padding) | |
| 226 }; | |
| 227 } | |
| 228 | |
| 229 Rectangle VerticalSplit(Rectangle container, float ratio) | |
| 230 { | |
| 231 return (Rectangle){ | |
| 232 .x = container.x, | |
| 233 .y = container.y, | |
| 234 .width = container.width, | |
| 235 .height = container.height * ratio | |
| 236 }; | |
| 237 } | |
| 238 | |
| 239 // ============================================================================ | |
| 240 // DRAWING HELPERS - Additional | |
| 241 // ============================================================================ | |
| 242 | |
| 243 void DrawRectangleSelectiveRoundedLines(Rectangle rec, float radius, int segments, Color color, | |
| 244 boolean roundTL, boolean roundTR, boolean roundBR, boolean roundBL) | |
| 245 { | |
| 246 // Top edge | |
| 247 DrawLine(rec.x + (roundTL ? radius : 0), rec.y, | |
| 248 rec.x + rec.width - (roundTR ? radius : 0), rec.y, color); | |
| 249 // Bottom edge | |
| 250 DrawLine(rec.x + (roundBL ? radius : 0), rec.y + rec.height, | |
| 251 rec.x + rec.width - (roundBR ? radius : 0), rec.y + rec.height, color); | |
| 252 // Left edge | |
| 253 DrawLine(rec.x, rec.y + (roundTL ? radius : 0), | |
| 254 rec.x, rec.y + rec.height - (roundBL ? radius : 0), color); | |
| 255 // Right edge | |
| 256 DrawLine(rec.x + rec.width, rec.y + (roundTR ? radius : 0), | |
| 257 rec.x + rec.width, rec.y + rec.height - (roundBR ? radius : 0), color); | |
| 258 | |
| 259 // Corner arcs | |
| 260 if (roundTL) | |
| 261 DrawCircleSectorLines((Vector2){rec.x + radius, rec.y + radius}, radius, 180, 270, segments, color); | |
| 262 if (roundTR) | |
| 263 DrawCircleSectorLines((Vector2){rec.x + rec.width - radius, rec.y + radius}, radius, 270, 360, segments, color); | |
| 264 if (roundBR) | |
| 265 DrawCircleSectorLines((Vector2){rec.x + rec.width - radius, rec.y + rec.height - radius}, radius, 0, 90, segments, color); | |
| 266 if (roundBL) | |
| 267 DrawCircleSectorLines((Vector2){rec.x + radius, rec.y + rec.height - radius}, radius, 90, 180, segments, color); | |
| 268 } | |
| 269 | |
| 270 // ============================================================================ | |
| 271 // CUSTOM TAB COMPONENT IMPLEMENTATION | |
| 272 // ============================================================================ | |
| 273 | |
| 274 boolean PostDog_TabBar(Rectangle bounds, TabConfig config) | |
| 275 { | |
| 276 if (config.count <= 0 || config.labels == NULL || config.active_tab == NULL) | |
| 277 return FALSE; | |
| 278 | |
| 279 boolean changed = FALSE; | |
| 280 float tab_width = (bounds.width - config.padding * (config.count - 1)) / config.count; | |
| 281 Vector2 mouse = GetMousePosition(); | |
| 282 | |
| 283 for (int i = 0; i < config.count; i++) | |
| 284 { | |
| 285 Rectangle tab_rect = { | |
| 286 .x = bounds.x + i * (tab_width + config.padding), | |
| 287 .y = bounds.y, | |
| 288 .width = tab_width, | |
| 289 .height = bounds.height | |
| 290 }; | |
| 291 | |
| 292 boolean is_active = (i == *config.active_tab); | |
| 293 boolean is_hovered = CheckCollisionPointRec(mouse, tab_rect); | |
| 294 Color bg_color = is_active ? config.active_color : config.inactive_color; | |
| 295 Color txt_color = is_active ? config.active_text_color : config.text_color; | |
| 296 | |
| 297 // Hover effect | |
| 298 if (is_hovered && !is_active) | |
| 299 bg_color = Fade(config.active_color, 0.5f); | |
| 300 | |
| 301 // Draw tab background with rounded top corners | |
| 302 DrawRectangleSelectiveRounded(tab_rect, config.corner_radius, 8, bg_color, | |
| 303 TRUE, TRUE, FALSE, FALSE); | |
| 304 | |
| 305 // Draw tab label | |
| 306 int text_width = MeasureText(config.labels[i], GuiGetStyle(DEFAULT, TEXT_SIZE)); | |
| 307 Vector2 text_pos = { | |
| 308 tab_rect.x + (tab_rect.width - text_width) / 2, | |
| 309 tab_rect.y + (tab_rect.height - GuiGetStyle(DEFAULT, TEXT_SIZE)) / 2 | |
| 310 }; | |
| 311 DrawText(config.labels[i], text_pos.x, text_pos.y, GuiGetStyle(DEFAULT, TEXT_SIZE), txt_color); | |
| 312 | |
| 313 // Handle click | |
| 314 if (is_hovered && IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && !is_active) | |
| 315 { | |
| 316 *config.active_tab = i; | |
| 317 changed = TRUE; | |
| 318 } | |
| 319 } | |
| 320 | |
| 321 return changed; | |
| 322 } | |
| 323 | |
| 324 boolean PostDog_TabBarSimple(Rectangle bounds, const char **labels, int count, int *active_tab) | |
| 325 { | |
| 326 TabConfig config = { | |
| 327 .labels = labels, | |
| 328 .count = count, | |
| 329 .active_tab = active_tab, | |
| 330 .padding = 2, | |
| 331 .corner_radius = 8, | |
| 332 .active_color = g_colors.primary, | |
| 333 .inactive_color = g_colors.secondary, | |
| 334 .text_color = g_colors.text, | |
| 335 .active_text_color = g_colors.text_light, | |
| 336 }; | |
| 337 return PostDog_TabBar(bounds, config); | |
| 338 } | |
| 339 | |
| 340 // ============================================================================ | |
| 341 // SPLIT PANEL IMPLEMENTATION | |
| 342 // ============================================================================ | |
| 343 | |
| 344 void PostDog_SplitPanel(Rectangle bounds, SplitPanelConfig config, | |
| 345 Rectangle *out_left, Rectangle *out_right) | |
| 346 { | |
| 347 float left_width = bounds.width * config.split_ratio - config.gap / 2; | |
| 348 float right_width = bounds.width * (1.0f - config.split_ratio) - config.gap / 2; | |
| 349 | |
| 350 if (out_left) | |
| 351 { | |
| 352 *out_left = (Rectangle){ | |
| 353 .x = bounds.x, | |
| 354 .y = bounds.y, | |
| 355 .width = left_width, | |
| 356 .height = bounds.height | |
| 357 }; | |
| 358 } | |
| 359 | |
| 360 if (out_right) | |
| 361 { | |
| 362 *out_right = (Rectangle){ | |
| 363 .x = bounds.x + left_width + config.gap, | |
| 364 .y = bounds.y, | |
| 365 .width = right_width, | |
| 366 .height = bounds.height | |
| 367 }; | |
| 368 } | |
| 369 } | |
| 370 | |
| 371 void PostDog_SplitPanelDraw(Rectangle bounds, SplitPanelConfig config) | |
| 372 { | |
| 373 Rectangle left_rect, right_rect; | |
| 374 PostDog_SplitPanel(bounds, config, &left_rect, &right_rect); | |
| 375 | |
| 376 // Draw left panel with rounded left corners | |
| 377 DrawRectangleSelectiveRounded(left_rect, config.corner_radius, 8, config.left_color, | |
| 378 TRUE, FALSE, FALSE, TRUE); | |
| 379 | |
| 380 // Draw right panel with rounded right corners | |
| 381 DrawRectangleSelectiveRounded(right_rect, config.corner_radius, 8, config.right_color, | |
| 382 FALSE, TRUE, TRUE, FALSE); | |
| 383 } | |
| 384 | |
| 385 // ============================================================================ | |
| 386 // COMBINED INPUT COMPONENTS IMPLEMENTATION | |
| 387 // ============================================================================ | |
| 388 | |
| 389 // Helper: Parse semicolon-separated items and return item at index | |
| 390 static const char* GetDropdownItem(const char *items, int index, char *buffer, int buffer_size) | |
| 391 { | |
| 392 int current_index = 0; | |
| 393 int start = 0; | |
| 394 int len = strlen(items); | |
| 395 | |
| 396 for (int i = 0; i <= len; i++) | |
| 397 { | |
| 398 if (items[i] == ';' || items[i] == '\0') | |
| 399 { | |
| 400 if (current_index == index) | |
| 401 { | |
| 402 int item_len = i - start; | |
| 403 if (item_len >= buffer_size) item_len = buffer_size - 1; | |
| 404 strncpy(buffer, items + start, item_len); | |
| 405 buffer[item_len] = '\0'; | |
| 406 return buffer; | |
| 407 } | |
| 408 current_index++; | |
| 409 start = i + 1; | |
| 410 } | |
| 411 } | |
| 412 buffer[0] = '\0'; | |
| 413 return buffer; | |
| 414 } | |
| 415 | |
| 416 // Helper: Count items in semicolon-separated string | |
| 417 static int CountDropdownItems(const char *items) | |
| 418 { | |
| 419 if (!items || items[0] == '\0') return 0; | |
| 420 int count = 1; | |
| 421 for (int i = 0; items[i]; i++) | |
| 422 if (items[i] == ';') count++; | |
| 423 return count; | |
| 424 } | |
| 425 | |
| 426 boolean PostDog_DropdownTextBox(Rectangle bounds, DropdownTextBoxConfig config) | |
| 427 { | |
| 428 boolean result = FALSE; | |
| 429 | |
| 430 // Calculate dropdown width | |
| 431 float dropdown_w = config.dropdown_width; | |
| 432 if (dropdown_w <= 1.0f) | |
| 433 dropdown_w = bounds.width * dropdown_w; | |
| 434 | |
| 435 // Calculate rectangles | |
| 436 Rectangle dropdown_rect = { | |
| 437 .x = bounds.x, | |
| 438 .y = bounds.y, | |
| 439 .width = dropdown_w, | |
| 440 .height = bounds.height | |
| 441 }; | |
| 442 | |
| 443 Rectangle textbox_rect = { | |
| 444 .x = bounds.x + dropdown_w, | |
| 445 .y = bounds.y, | |
| 446 .width = bounds.width - dropdown_w, | |
| 447 .height = bounds.height | |
| 448 }; | |
| 449 | |
| 450 // Draw combined background | |
| 451 DrawRectangleSelectiveRounded(dropdown_rect, config.corner_radius, 8, config.background_color, | |
| 452 TRUE, FALSE, FALSE, TRUE); | |
| 453 DrawRectangleSelectiveRounded(textbox_rect, config.corner_radius, 8, config.background_color, | |
| 454 FALSE, TRUE, TRUE, FALSE); | |
| 455 | |
| 456 // Draw separator line | |
| 457 DrawLine(dropdown_rect.x + dropdown_rect.width, dropdown_rect.y + 4, | |
| 458 dropdown_rect.x + dropdown_rect.width, dropdown_rect.y + dropdown_rect.height - 4, | |
| 459 config.border_color); | |
| 460 | |
| 461 // Draw border | |
| 462 DrawRectangleSelectiveRoundedLines(bounds, config.corner_radius, 8, config.border_color, | |
| 463 TRUE, TRUE, TRUE, TRUE); | |
| 464 | |
| 465 // Custom dropdown with per-item colors | |
| 466 Vector2 mouse_pos = GetMousePosition(); | |
| 467 int item_count = config.item_count > 0 ? config.item_count : CountDropdownItems(config.dropdown_items); | |
| 468 int font_size = GuiGetStyle(DEFAULT, TEXT_SIZE); | |
| 469 char item_buffer[64]; | |
| 470 | |
| 471 // Get current selected item | |
| 472 GetDropdownItem(config.dropdown_items, *config.dropdown_active, item_buffer, sizeof(item_buffer)); | |
| 473 Color current_color = (config.item_colors && *config.dropdown_active < item_count) | |
| 474 ? config.item_colors[*config.dropdown_active] | |
| 475 : config.text_color; | |
| 476 | |
| 477 // Draw selected item in dropdown area | |
| 478 Rectangle dropdown_inner = AddPadding(dropdown_rect, 6); | |
| 479 boolean dropdown_hovered = CheckCollisionPointRec(mouse_pos, dropdown_rect); | |
| 480 | |
| 481 // Highlight on hover | |
| 482 if (dropdown_hovered && !(*config.dropdown_edit_mode)) | |
| 483 { | |
| 484 DrawRectangleSelectiveRounded(dropdown_rect, config.corner_radius, 8, | |
| 485 Fade(current_color, 0.5f), TRUE, FALSE, FALSE, TRUE); | |
| 486 } | |
| 487 | |
| 488 // Draw selected text with color | |
| 489 int text_width = MeasureText(item_buffer, font_size); | |
| 490 DrawText(item_buffer, | |
| 491 dropdown_inner.x + (dropdown_inner.width - text_width) / 2, | |
| 492 dropdown_inner.y + (dropdown_inner.height - font_size) / 2, | |
| 493 font_size, current_color); | |
| 494 DrawRectangleSelectiveRounded(dropdown_rect, config.corner_radius, 8, | |
| 495 Fade(current_color, 0.3f), TRUE, FALSE, FALSE, TRUE); | |
| 496 | |
| 497 // Draw dropdown arrow | |
| 498 float arrow_size = 6; | |
| 499 float arrow_x = dropdown_rect.x + dropdown_rect.width - 14; | |
| 500 float arrow_y = dropdown_rect.y + dropdown_rect.height / 2; | |
| 501 DrawTriangle( | |
| 502 (Vector2){arrow_x, arrow_y - arrow_size/2}, | |
| 503 (Vector2){arrow_x + arrow_size, arrow_y - arrow_size/2}, | |
| 504 (Vector2){arrow_x + arrow_size/2, arrow_y + arrow_size/2}, | |
| 505 config.text_color); | |
| 506 | |
| 507 // Handle dropdown click | |
| 508 if (dropdown_hovered && IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) | |
| 509 { | |
| 510 *config.dropdown_edit_mode = !(*config.dropdown_edit_mode); | |
| 511 } | |
| 512 | |
| 513 // Draw dropdown list when open | |
| 514 if (*config.dropdown_edit_mode) | |
| 515 { | |
| 516 float item_height = bounds.height; | |
| 517 Rectangle list_rect = { | |
| 518 .x = dropdown_rect.x, | |
| 519 .y = dropdown_rect.y + dropdown_rect.height + 2, | |
| 520 .width = dropdown_rect.width, | |
| 521 .height = item_height * item_count | |
| 522 }; | |
| 523 | |
| 524 // Background | |
| 525 DrawRectangleRounded(list_rect, 0.1f, 8, config.background_color); | |
| 526 DrawRectangleRoundedLines(list_rect, 0.1f, 8, config.border_color); | |
| 527 | |
| 528 // Draw each item | |
| 529 for (int i = 0; i < item_count; i++) | |
| 530 { | |
| 531 Rectangle item_rect = { | |
| 532 .x = list_rect.x, | |
| 533 .y = list_rect.y + i * item_height, | |
| 534 .width = list_rect.width, | |
| 535 .height = item_height | |
| 536 }; | |
| 537 | |
| 538 GetDropdownItem(config.dropdown_items, i, item_buffer, sizeof(item_buffer)); | |
| 539 Color item_color = (config.item_colors && i < item_count) | |
| 540 ? config.item_colors[i] | |
| 541 : config.text_color; | |
| 542 | |
| 543 boolean item_hovered = CheckCollisionPointRec(mouse_pos, item_rect); | |
| 544 | |
| 545 // Highlight hovered item | |
| 546 if (item_hovered) | |
| 547 DrawRectangleRec(item_rect, Fade(item_color, 0.15f)); | |
| 548 | |
| 549 // Highlight selected item | |
| 550 DrawRectangleRec(item_rect, Fade(item_color, 0.2f)); | |
| 551 | |
| 552 // Draw item text | |
| 553 text_width = MeasureText(item_buffer, font_size); | |
| 554 DrawText(item_buffer, | |
| 555 item_rect.x + (item_rect.width - text_width) / 2, | |
| 556 item_rect.y + (item_rect.height - font_size) / 2, | |
| 557 font_size, item_color); | |
| 558 | |
| 559 // Handle item click | |
| 560 if (item_hovered && IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) | |
| 561 { | |
| 562 *config.dropdown_active = i; | |
| 563 *config.dropdown_edit_mode = FALSE; | |
| 564 } | |
| 565 } | |
| 566 | |
| 567 // Close dropdown if clicked outside | |
| 568 if (!CheckCollisionPointRec(mouse_pos, list_rect) && | |
| 569 !CheckCollisionPointRec(mouse_pos, dropdown_rect) && | |
| 570 IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) | |
| 571 { | |
| 572 *config.dropdown_edit_mode = FALSE; | |
| 573 } | |
| 574 } | |
| 575 | |
| 576 // Draw text box (using raygui) | |
| 577 Rectangle textbox_inner = AddPadding(textbox_rect, 4); | |
| 578 boolean ctrl_pressed = IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL) || IsKeyDown(KEY_LEFT_SUPER) || IsKeyDown(KEY_RIGHT_SUPER); | |
| 579 if (*config.text_edit_mode && ctrl_pressed && IsKeyPressed(KEY_V)) | |
| 580 { | |
| 581 const char *clipboard = GetClipboardText(); | |
| 582 snprintf(config.text_buffer, config.text_buffer_size, "%s", clipboard); | |
| 583 } | |
| 584 if (GuiTextBox(textbox_inner, config.text_buffer, config.text_buffer_size, *config.text_edit_mode)) | |
| 585 { | |
| 586 *config.text_edit_mode = !(*config.text_edit_mode); | |
| 587 result = TRUE; | |
| 588 } | |
| 589 | |
| 590 return result; | |
| 591 } |