Mercurial
comparison postdog/main.c @ 116:7bd795bac997
[Postdog] Added scrollable area to inputs and history files, buttons to delete and view.
| author | June Park <parkjune1995@gmail.com> |
|---|---|
| date | Wed, 07 Jan 2026 04:52:17 -0800 |
| parents | 96db6c3f38d6 |
| children | b91f2dd6f84d |
comparison
equal
deleted
inserted
replaced
| 115:96db6c3f38d6 | 116:7bd795bac997 |
|---|---|
| 14 #define POSTDOG_PATHS "/Users/mrjunejune/zenbu/postdog/history" | 14 #define POSTDOG_PATHS "/Users/mrjunejune/zenbu/postdog/history" |
| 15 #endif | 15 #endif |
| 16 | 16 |
| 17 #define SCREEN_WIDTH 1280 | 17 #define SCREEN_WIDTH 1280 |
| 18 #define SCREEN_HEIGHT 780 | 18 #define SCREEN_HEIGHT 780 |
| 19 #define TEXT_SIZE 10 | 19 #define MAX_SCROLL_HEIGHT 10000 |
| 20 | |
| 21 #define JSON_INPUT_BUFFER_LEN 8192 | |
| 22 #define HEADER_INPUT_BUFFER_LEN 4096 | |
| 23 #define PARAM_INPUT_BUFFER_LEN 4096 | |
| 24 #define MAX_HISTORY_ITEMS 100 | |
| 25 | 20 |
| 26 #define HEADER_BUFFER_LENGTH 1024 * 4 | 21 #define HEADER_BUFFER_LENGTH 1024 * 4 |
| 27 #define DEFAULT_TEXT_BUFFER_LENGTH 1024 * 4 | 22 #define DEFAULT_TEXT_BUFFER_LENGTH 1024 * 4 |
| 28 #define URL_TEXT_BUFFER 1024 * 10 | 23 #define URL_TEXT_BUFFER_LENGTH 1024 * 10 |
| 29 #define BODY_BUFFER_LENGTH 1024 * 1025 * 5 | 24 #define BODY_BUFFER_LENGTH 1024 * 1024 * 5 |
| 30 #define RESULT_BUFFER_LENGTH 1024 * 1025 * 5 | 25 #define RESULT_BUFFER_LENGTH 1024 * 1024 * 5 |
| 31 #define AREANA_BUFFER_LENGTH 1024 * 1025 * 15 | |
| 32 | 26 |
| 33 | 27 |
| 34 #ifdef _WIN32 | 28 #ifdef _WIN32 |
| 35 #include <direct.h> | 29 #include <direct.h> |
| 36 #include <io.h> | 30 #include <io.h> |
| 167 case 1: return "POST"; | 161 case 1: return "POST"; |
| 168 case 2: return "PUT"; | 162 case 2: return "PUT"; |
| 169 case 3: return "DELETE"; | 163 case 3: return "DELETE"; |
| 170 } | 164 } |
| 171 return 0; | 165 return 0; |
| 166 } | |
| 167 | |
| 168 char *PostDog_Construct_URL(char *filename) | |
| 169 { | |
| 170 char full_file_path[512] = {0}; | |
| 171 snprintf(full_file_path, 512, "%s/%s", POSTDOG_PATHS, filename); | |
| 172 return &full_file_path; | |
| 172 } | 173 } |
| 173 | 174 |
| 174 void PostDog_History_CreateFile(char *filename, char* values) | 175 void PostDog_History_CreateFile(char *filename, char* values) |
| 175 { | 176 { |
| 176 char full_file_path[512] = {0}; | 177 char full_file_path[512] = {0}; |
| 261 int PostDog_Http_Request( | 262 int PostDog_Http_Request( |
| 262 const char *url, | 263 const char *url, |
| 263 const char *method, | 264 const char *method, |
| 264 const char *headers, | 265 const char *headers, |
| 265 const char *body, | 266 const char *body, |
| 266 char *response, size_t responseSize) | 267 char *response, size_t response_size) |
| 267 { | 268 { |
| 268 CURL *curl; | 269 CURL *curl; |
| 269 CURLcode res; | 270 CURLcode res; |
| 270 ResponseBuffer buffer = { .data = malloc(1), .size = 0 }; | 271 ResponseBuffer buffer = { .data = malloc(1), .size = 0 }; |
| 271 | 272 |
| 272 response[0] = '\n'; | 273 response[0] = '\n'; |
| 273 | 274 |
| 274 if (buffer.data == NULL) | 275 if (buffer.data == NULL) |
| 275 { | 276 { |
| 276 snprintf(response, responseSize, "Error: Failed to allocate memory"); | 277 snprintf(response, response_size, "Error: Failed to allocate memory"); |
| 277 return -1; | 278 return -1; |
| 278 } | 279 } |
| 279 buffer.data[0] = '\0'; | 280 buffer.data[0] = '\0'; |
| 280 | 281 |
| 281 curl_global_init(CURL_GLOBAL_DEFAULT); | 282 curl_global_init(CURL_GLOBAL_DEFAULT); |
| 333 curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L); | 334 curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L); |
| 334 // Perform request | 335 // Perform request |
| 335 res = curl_easy_perform(curl); | 336 res = curl_easy_perform(curl); |
| 336 | 337 |
| 337 if (res != CURLE_OK) | 338 if (res != CURLE_OK) |
| 338 snprintf(response, responseSize, "Error: %s\n", curl_easy_strerror(res)); | 339 snprintf(response, response_size, "Error: %s\n", curl_easy_strerror(res)); |
| 339 else | 340 else |
| 340 { | 341 { |
| 341 long response_code; | 342 long response_code; |
| 342 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code); | 343 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code); |
| 343 | 344 |
| 344 snprintf(response, responseSize, "HTTP Status: %ld\n\n%s", | 345 if (buffer.size > response_size) |
| 346 printf("TODO: Realloc\n"); | |
| 347 | |
| 348 snprintf(response, response_size, "HTTP Status: %ld\n\n%s", | |
| 345 response_code, buffer.data ? buffer.data : ""); | 349 response_code, buffer.data ? buffer.data : ""); |
| 346 } | 350 } |
| 347 | 351 |
| 348 // Cleanup | 352 // Cleanup |
| 349 if (headerList) curl_slist_free_all(headerList); | 353 if (headerList) curl_slist_free_all(headerList); |
| 350 curl_easy_cleanup(curl); | 354 curl_easy_cleanup(curl); |
| 351 } else { | 355 } |
| 352 snprintf(response, responseSize, "Error: Failed to initialize curl"); | 356 else |
| 353 } | 357 snprintf(response, response_size, "Error: Failed to initialize curl"); |
| 354 | 358 |
| 355 free(buffer.data); | 359 free(buffer.data); |
| 356 curl_global_cleanup(); | 360 curl_global_cleanup(); |
| 357 | 361 |
| 358 PostDog_Request_SaveFile( | 362 PostDog_Request_SaveFile( |
| 459 | 463 |
| 460 Dowa_Arena *init_arena = Dowa_Arena_Create(file_size + 2); | 464 Dowa_Arena *init_arena = Dowa_Arena_Create(file_size + 2); |
| 461 Dowa_Arena *split_arena = Dowa_Arena_Create(file_size * 2); | 465 Dowa_Arena *split_arena = Dowa_Arena_Create(file_size * 2); |
| 462 char *file_buffer = Dowa_Arena_Allocate(init_arena, file_size+1); | 466 char *file_buffer = Dowa_Arena_Allocate(init_arena, file_size+1); |
| 463 fread(file_buffer, 1, file_size, file); | 467 fread(file_buffer, 1, file_size, file); |
| 468 fclose(file); | |
| 469 | |
| 464 char **values = Dowa_String_Split(file_buffer, "---\n", file_size, 4, split_arena); | 470 char **values = Dowa_String_Split(file_buffer, "---\n", file_size, 4, split_arena); |
| 465 Dowa_Arena_Free(init_arena); | 471 |
| 466 for (int i = 0; i < Dowa_Array_Length(values); i++) | 472 for (int i = 0; i < Dowa_Array_Length(values); i++) |
| 467 { | 473 { |
| 468 if (i == 0) | 474 if (i == 0) |
| 469 { | 475 { |
| 470 snprintf(url_input_text, strlen(values[i]) + 1, "%s", values[i]); | 476 snprintf(url_input_text, strlen(values[i]) + 1, "%s", values[i]); |
| 485 } | 491 } |
| 486 } | 492 } |
| 487 else | 493 else |
| 488 snprintf(url_result_text, strlen(values[i]) + 1, "%s", values[i]); | 494 snprintf(url_result_text, strlen(values[i]) + 1, "%s", values[i]); |
| 489 } | 495 } |
| 496 | |
| 497 Dowa_Arena_Free(init_arena); | |
| 498 Dowa_Arena_Free(split_arena); | |
| 490 } | 499 } |
| 491 | 500 |
| 492 Rectangle AddPadding(Rectangle rect, float padding) | 501 Rectangle AddPadding(Rectangle rect, float padding) |
| 493 { | 502 { |
| 494 return (Rectangle){ | 503 return (Rectangle){ |
| 497 rect.width - (2 * padding), | 506 rect.width - (2 * padding), |
| 498 rect.height - (2 * padding) | 507 rect.height - (2 * padding) |
| 499 }; | 508 }; |
| 500 } | 509 } |
| 501 | 510 |
| 511 Rectangle AddPaddingHorizontal(Rectangle rect, float padding) | |
| 512 { | |
| 513 return (Rectangle){ | |
| 514 rect.x + padding, | |
| 515 rect.y, | |
| 516 rect.width - (2 * padding), | |
| 517 rect.height | |
| 518 }; | |
| 519 } | |
| 520 | |
| 521 Rectangle AddPaddingVertical(Rectangle rect, float padding) | |
| 522 { | |
| 523 return (Rectangle){ | |
| 524 rect.x, | |
| 525 rect.y + padding, | |
| 526 rect.width, | |
| 527 rect.height - (2 * padding) | |
| 528 }; | |
| 529 } | |
| 530 | |
| 502 // Layout helper functions | 531 // Layout helper functions |
| 503 Rectangle RightOf(Rectangle ref, float padding) | 532 Rectangle RightOf(Rectangle ref, float padding) |
| 504 { | 533 { |
| 505 return (Rectangle){ | 534 return (Rectangle){ |
| 506 .x = ref.x + ref.width + padding, | 535 .x = ref.x + ref.width + padding, |
| 555 // -- initizlied --// | 584 // -- initizlied --// |
| 556 InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "PostDog"); | 585 InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "PostDog"); |
| 557 SetWindowState(FLAG_WINDOW_RESIZABLE); | 586 SetWindowState(FLAG_WINDOW_RESIZABLE); |
| 558 SetTargetFPS(60); | 587 SetTargetFPS(60); |
| 559 | 588 |
| 560 Dowa_Arena *arena = Dowa_Arena_Create(AREANA_BUFFER_LENGTH); | |
| 561 | |
| 562 Font customFont = LoadFontEx("postdog/Roboto-Regular.ttf", 20, 0, 0); | 589 Font customFont = LoadFontEx("postdog/Roboto-Regular.ttf", 20, 0, 0); |
| 563 GuiSetFont(customFont); | 590 GuiSetFont(customFont); |
| 564 GuiSetStyle(DEFAULT, TEXT_SIZE, 20); | 591 GuiSetStyle(DEFAULT, TEXT_SIZE, 10); |
| 592 Image logo_original = LoadImage("postdog/epi_all_colors.png"); | |
| 593 ImageResize(&logo_original, 60, 60); | |
| 594 Texture2D logo_texture = LoadTextureFromImage(logo_original); | |
| 595 UnloadImage(logo_original); | |
| 565 | 596 |
| 566 // -- Starting pos ---// | 597 // -- Starting pos ---// |
| 567 // Everyhting is relative to sidebar at this point lol | 598 Rectangle history_sidebar = { 0 }; |
| 568 float side_bar_x = 10; | |
| 569 float side_bar_y = 10; | |
| 570 Rectangle history_sidebar = { .x = side_bar_x, .y = side_bar_y, .width = 0, .height = 0 }; | |
| 571 Dowa_Array_Reserve(history_items, 10); | 599 Dowa_Array_Reserve(history_items, 10); |
| 572 Dowa_Array_Reserve(new_history_items, 10); | 600 Dowa_Array_Reserve(new_history_items, 10); |
| 573 PostDog_History_Load(&history_items); | 601 PostDog_History_Load(&history_items); |
| 602 int32 *history_deleted_items = NULL; | |
| 574 | 603 |
| 575 Rectangle url_area = { 0 }; | 604 Rectangle url_area = { 0 }; |
| 576 Rectangle textBounds = { 0 }; | 605 Rectangle textBounds = { 0 }; |
| 577 Rectangle url_input_bounds = { 0 }; | 606 Rectangle url_input_bounds = { 0 }; |
| 578 bool url_input_edit = false; | 607 bool url_input_edit = false; |
| 579 Rectangle url_text_bounds = { 0 }; | 608 Rectangle url_text_bounds = { 0 }; |
| 580 Rectangle url_enter_button = { 0 }; | 609 Rectangle url_enter_button = { 0 }; |
| 581 Rectangle method_dropdown = { 0 }; | 610 Rectangle method_dropdown = { 0 }; |
| 582 | 611 |
| 583 | 612 |
| 584 char *url_input_text = (char *)Dowa_Arena_Allocate(arena, URL_TEXT_BUFFER); | 613 char *url_input_text = (char *)malloc(sizeof(char) * URL_TEXT_BUFFER_LENGTH); |
| 585 snprintf(url_input_text, URL_TEXT_BUFFER, "https://httpbin.org/get"); | 614 snprintf(url_input_text, URL_TEXT_BUFFER_LENGTH, "https://httpbin.org/get"); |
| 586 | 615 |
| 587 char **url_body_map = NULL; | 616 char **url_body_map = NULL; |
| 588 Dowa_Array_Push_Arena(url_body_map, (char *)Dowa_Arena_Allocate(arena, HEADER_BUFFER_LENGTH), arena); | 617 Dowa_Array_Push(url_body_map, (char *)malloc(sizeof(char) * HEADER_BUFFER_LENGTH)); |
| 589 Dowa_Array_Push_Arena(url_body_map, (char *)Dowa_Arena_Allocate(arena, BODY_BUFFER_LENGTH), arena); | 618 Dowa_Array_Push(url_body_map, (char *)malloc(sizeof(char) * BODY_BUFFER_LENGTH)); |
| 590 Dowa_Array_Push_Arena(url_body_map, (char *)Dowa_Arena_Allocate(arena, DEFAULT_TEXT_BUFFER_LENGTH), arena); | 619 Dowa_Array_Push(url_body_map, (char *)malloc(sizeof(char) * DEFAULT_TEXT_BUFFER_LENGTH)); |
| 591 Dowa_Array_Push_Arena(url_body_map, (char *)Dowa_Arena_Allocate(arena, DEFAULT_TEXT_BUFFER_LENGTH), arena); | 620 Dowa_Array_Push(url_body_map, (char *)malloc(sizeof(char) * DEFAULT_TEXT_BUFFER_LENGTH)); |
| 592 | 621 |
| 593 snprintf(url_body_map[TAB_HEADER], HEADER_BUFFER_LENGTH, "Content-Type: application/json"); | 622 snprintf(url_body_map[TAB_HEADER], HEADER_BUFFER_LENGTH, "Content-Type: application/json"); |
| 594 snprintf(url_body_map[TAB_BODY], HEADER_BUFFER_LENGTH, ""); | 623 snprintf(url_body_map[TAB_BODY], HEADER_BUFFER_LENGTH, ""); |
| 595 | 624 |
| 596 char *url_result_text = (char *)Dowa_Arena_Allocate(arena, RESULT_BUFFER_LENGTH); | 625 char *url_result_text = (char *)malloc(sizeof(char) * RESULT_BUFFER_LENGTH); |
| 597 | 626 |
| 598 int active_method_dropdown = 0; | 627 int active_method_dropdown = 0; |
| 599 bool method_edit = false; | 628 bool method_edit = false; |
| 600 | 629 |
| 601 int sendRequest; | 630 int sendRequest; |
| 612 Rectangle result_body = { 0 }; | 641 Rectangle result_body = { 0 }; |
| 613 | 642 |
| 614 // General styling. | 643 // General styling. |
| 615 float padding = 10; // TODO make it % based? | 644 float padding = 10; // TODO make it % based? |
| 616 int active_input_tab = 0; | 645 int active_input_tab = 0; |
| 646 int prev_input_tab = 0; | |
| 647 | |
| 648 // Scroll offsets | |
| 649 float history_scroll_offset = 0; | |
| 650 float input_body_scroll_offset = 0; | |
| 651 float result_body_scroll_offset = 0; | |
| 617 | 652 |
| 618 while (!WindowShouldClose()) | 653 while (!WindowShouldClose()) |
| 619 { | 654 { |
| 620 int screen_width = GetScreenWidth(); | 655 int screen_width = GetScreenWidth(); |
| 621 int screen_height = GetScreenHeight(); | 656 int screen_height = GetScreenHeight(); |
| 622 | 657 |
| 623 // Define main screen container | 658 if ((IsKeyDown(KEY_LEFT_SUPER) || IsKeyDown(KEY_LEFT_CONTROL)) && IsKeyDown(KEY_EQUAL)) |
| 659 GuiSetStyle(DEFAULT, TEXT_SIZE, GuiGetStyle(DEFAULT, TEXT_SIZE) + 1); | |
| 660 | |
| 661 if ((IsKeyDown(KEY_LEFT_SUPER) || IsKeyDown(KEY_LEFT_CONTROL)) && IsKeyDown(KEY_MINUS)) | |
| 662 GuiSetStyle(DEFAULT, TEXT_SIZE, GuiGetStyle(DEFAULT, TEXT_SIZE) - 1); | |
| 663 | |
| 624 Rectangle screen = { 0, 0, screen_width, screen_height }; | 664 Rectangle screen = { 0, 0, screen_width, screen_height }; |
| 625 | 665 |
| 626 // Layout: Sidebar (left 15%) and Content Area (right 85%) | 666 // -- Side bar --// |
| 627 history_sidebar = LeftColumn(screen, 0.15, padding); | 667 history_sidebar = LeftColumn(screen, 0.15, padding); |
| 628 Rectangle content_area = RightColumn(screen, history_sidebar, padding); | 668 Rectangle content_area = RightColumn(screen, history_sidebar, padding); |
| 629 | 669 Rectangle logo_area = (Rectangle){ |
| 630 // History items inside sidebar | 670 .x = history_sidebar.x, |
| 671 .y = history_sidebar.y, | |
| 672 .width = history_sidebar.width, | |
| 673 .height = 80 | |
| 674 }; | |
| 675 | |
| 676 Rectangle history_list_area = Below(logo_area, padding); | |
| 677 history_list_area.width = history_sidebar.width; | |
| 678 history_list_area.height = history_sidebar.height - logo_area.height - padding; | |
| 679 | |
| 631 int32 new_history_items_length = Dowa_Array_Length(new_history_items); | 680 int32 new_history_items_length = Dowa_Array_Length(new_history_items); |
| 632 int32 history_item_length = Dowa_Array_Length(history_items); | 681 int32 history_item_length = Dowa_Array_Length(history_items); |
| 633 int32 total = new_history_items_length + history_item_length; | 682 int32 total = new_history_items_length + history_item_length; |
| 634 float item_height = history_sidebar.height * 0.05; | 683 float item_height = history_list_area.height * 0.10; |
| 635 | 684 |
| 685 int32 number_of_skipped_items = 0; | |
| 636 for (int i = 0; i < total; i++) | 686 for (int i = 0; i < total; i++) |
| 637 { | 687 { |
| 688 boolean skip = FALSE; | |
| 689 for (int32 j = 0; j < Dowa_Array_Length(history_deleted_items); j++) | |
| 690 { | |
| 691 if (i == j) | |
| 692 { | |
| 693 skip = TRUE; | |
| 694 number_of_skipped_items++; | |
| 695 break; | |
| 696 } | |
| 697 } | |
| 698 if (skip) | |
| 699 continue; | |
| 700 | |
| 638 HistoryItem *curr_history_items = i < new_history_items_length ? | 701 HistoryItem *curr_history_items = i < new_history_items_length ? |
| 639 &new_history_items[i] : &history_items[i - new_history_items_length]; | 702 &new_history_items[i] : &history_items[i - new_history_items_length]; |
| 640 | 703 |
| 641 curr_history_items->rect = (Rectangle){ | 704 curr_history_items->rect = (Rectangle){ |
| 642 .x = history_sidebar.x, | 705 .x = history_list_area.x, |
| 643 .y = history_sidebar.y + (padding + item_height) * i, | 706 .y = history_list_area.y + (padding + item_height) * (i - number_of_skipped_items) + history_scroll_offset, |
| 644 .width = history_sidebar.width, | 707 .width = history_list_area.width, |
| 645 .height = item_height | 708 .height = item_height |
| 646 }; | 709 }; |
| 647 } | 710 } |
| 648 | 711 |
| 649 // Content area: split into URL bar (top 10%) and body (bottom 90%) | 712 // --- URL --- // |
| 650 url_area = (Rectangle){ | 713 url_area = (Rectangle){ |
| 651 .x = content_area.x, | 714 .x = content_area.x, |
| 652 .y = content_area.y, | 715 .y = content_area.y, |
| 653 .width = content_area.width, | 716 .width = content_area.width, |
| 654 .height = content_area.height * 0.1 | 717 .height = content_area.height * 0.1 |
| 655 }; | 718 }; |
| 656 | 719 |
| 657 // URL bar elements laid out horizontally | |
| 658 float url_control_y = url_area.y + (url_area.height - TEXT_SIZE * 2) / 2; | 720 float url_control_y = url_area.y + (url_area.height - TEXT_SIZE * 2) / 2; |
| 659 | 721 |
| 660 url_text_bounds = (Rectangle){ | 722 url_text_bounds = (Rectangle){ |
| 661 .x = url_area.x + padding, | 723 .x = url_area.x + padding, |
| 662 .y = url_control_y, | 724 .y = url_control_y, |
| 674 | 736 |
| 675 method_dropdown = RightOf(url_enter_button, padding); | 737 method_dropdown = RightOf(url_enter_button, padding); |
| 676 method_dropdown.width = url_area.width * 0.1; | 738 method_dropdown.width = url_area.width * 0.1; |
| 677 method_dropdown.height = TEXT_SIZE * 2; | 739 method_dropdown.height = TEXT_SIZE * 2; |
| 678 | 740 |
| 679 // Body area: split into input (left 50%) and result (right 50%) | 741 // -- Body -- // |
| 680 Rectangle body_area = Below(url_area, 0); | 742 Rectangle body_area = Below(url_area, 0); |
| 681 body_area.height = content_area.height - url_area.height; | 743 body_area.height = content_area.height - url_area.height; |
| 682 | 744 |
| 683 input_area = HorizontalSplit(body_area, 0.5); | 745 input_area = HorizontalSplit(body_area, 0.5); |
| 684 result_area = RightOf(input_area, 0); | 746 result_area = RightOf(input_area, 0); |
| 685 result_area.width = body_area.width - input_area.width; | 747 result_area.width = body_area.width - input_area.width; |
| 686 | 748 |
| 687 // Input area: tabs at top (10%) and text box below (90%) | |
| 688 input_tab = (Rectangle){ | 749 input_tab = (Rectangle){ |
| 689 .x = input_area.x + padding, | 750 .x = input_area.x + padding, |
| 690 .y = input_area.y + padding, | 751 .y = input_area.y + padding, |
| 691 .width = input_area.width - (2 * padding), | 752 .width = input_area.width - (2 * padding), |
| 692 .height = input_area.height * 0.1 | 753 .height = input_area.height * 0.1 |
| 697 | 758 |
| 698 input_body = Below(input_tab, 0); | 759 input_body = Below(input_tab, 0); |
| 699 input_body.width = input_tab.width; | 760 input_body.width = input_tab.width; |
| 700 input_body.height = input_area.height - input_tab.height - (2 * padding); | 761 input_body.height = input_area.height - input_tab.height - (2 * padding); |
| 701 | 762 |
| 702 // Result area: aligned with input tabs | 763 // -- Result -- / |
| 703 result_body = (Rectangle){ | 764 result_body = (Rectangle){ |
| 704 .x = result_area.x + padding, | 765 .x = result_area.x + padding, |
| 705 .y = input_body.y, | 766 .y = input_body.y, |
| 706 .width = result_area.width - (2 * padding), | 767 .width = result_area.width - (2 * padding), |
| 707 .height = input_body.height | 768 .height = input_body.height |
| 708 }; | 769 }; |
| 709 | 770 |
| 710 Vector2 mouse_position = GetMousePosition(); | 771 Vector2 mouse_position = GetMousePosition(); |
| 772 float mouse_wheel = GetMouseWheelMove(); | |
| 773 | |
| 774 // Reset input body scroll when tab changes | |
| 775 if (prev_input_tab != active_input_tab) { | |
| 776 input_body_scroll_offset = 0; | |
| 777 prev_input_tab = active_input_tab; | |
| 778 } | |
| 779 | |
| 780 // Handle scroll wheel for history | |
| 781 if (InArea(mouse_position, history_list_area) && mouse_wheel != 0) { | |
| 782 history_scroll_offset += mouse_wheel * 30; // 30 pixels per wheel tick | |
| 783 // Clamp scroll offset | |
| 784 float max_scroll = (total * (item_height + padding)) - history_list_area.height; | |
| 785 if (history_scroll_offset > 0) history_scroll_offset = 0; | |
| 786 if (history_scroll_offset < -max_scroll && max_scroll > 0) history_scroll_offset = -max_scroll; | |
| 787 } | |
| 788 | |
| 789 // Handle scroll wheel for input body | |
| 790 if (InArea(mouse_position, input_body) && mouse_wheel != 0) { | |
| 791 input_body_scroll_offset += mouse_wheel * 30; | |
| 792 if (input_body_scroll_offset > 0) input_body_scroll_offset = 0; | |
| 793 } | |
| 794 | |
| 795 // Handle scroll wheel for result body | |
| 796 if (InArea(mouse_position, result_body) && mouse_wheel != 0) { | |
| 797 result_body_scroll_offset += mouse_wheel * 30; | |
| 798 if (result_body_scroll_offset > 0) result_body_scroll_offset = 0; | |
| 799 } | |
| 711 | 800 |
| 712 BeginDrawing(); | 801 BeginDrawing(); |
| 713 ClearBackground(GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR))); | 802 ClearBackground(GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR))); |
| 714 | 803 |
| 715 DrawRectangleRec(history_sidebar, Fade(GRAY, 0.1f)); | 804 DrawRectangleRec(history_sidebar, Fade(GRAY, 0.1f)); |
| 716 for (int i = 0; i < total; i++) | 805 |
| 806 // DrawRectangleRec(logo_area, Fade(BLUE, 0.2f)); | |
| 807 Rectangle logo_image_rect = AddPadding(logo_area, padding); | |
| 808 // Fit logo to area while maintaining aspect ratio | |
| 809 float logo_size = logo_image_rect.height < logo_image_rect.width ? logo_image_rect.height : logo_image_rect.width; | |
| 810 Rectangle dest = { | |
| 811 .x = logo_image_rect.x + (logo_image_rect.width - logo_size) / 2, // Center horizontally | |
| 812 .y = logo_image_rect.y, | |
| 813 .width = logo_size, | |
| 814 .height = logo_size | |
| 815 }; | |
| 816 | |
| 817 Rectangle source = { 0, 0, logo_texture.width, logo_texture.height }; | |
| 818 DrawTexturePro(logo_texture, source, dest, (Vector2){0, 0}, 0.0f, WHITE); | |
| 819 | |
| 820 // Draw history items with scissor mode for clipping | |
| 821 BeginScissorMode(history_list_area.x, history_list_area.y, history_list_area.width, history_list_area.height); | |
| 822 for (int i = 0; i < total; i++) | |
| 823 { | |
| 824 boolean skip = FALSE; | |
| 825 for (int32 j = 0; j < Dowa_Array_Length(history_deleted_items); j++) | |
| 826 { | |
| 827 if (i == j) | |
| 828 { | |
| 829 skip = TRUE; | |
| 830 break; | |
| 831 } | |
| 832 } | |
| 833 if (skip) | |
| 834 continue; | |
| 835 HistoryItem *curr_history_items = i < new_history_items_length ? &new_history_items[i] : &history_items[i - new_history_items_length]; | |
| 836 | |
| 837 float diff = curr_history_items->rect.height*0.3; | |
| 838 // DrawRectangleRec(curr_history_items->rect, Fade(RED, 0.1f)); | |
| 839 Rectangle filename_area = curr_history_items->rect; | |
| 840 | |
| 841 filename_area.height -= diff; | |
| 842 Rectangle icon_area = Below(filename_area, 0); | |
| 843 icon_area.height = diff; | |
| 844 | |
| 845 DrawRectangleRec(filename_area, Fade(BLUE, 0.1f)); | |
| 846 DrawRectangleRec(icon_area, Fade(YELLOW, 0.1f)); | |
| 847 | |
| 848 Rectangle icon_area_left_column = LeftColumn(icon_area, 0.5, 0); | |
| 849 Rectangle icon_area_right_column = RightColumn(icon_area, icon_area_left_column, 0); | |
| 850 | |
| 851 GuiDrawText(curr_history_items->filename, AddPadding(filename_area, padding), TEXT_ALIGN_CENTER, RED); | |
| 852 if (GuiButton(AddPaddingHorizontal(icon_area_left_column, padding), "view")) | |
| 853 PostDog_Load_File( | |
| 854 curr_history_items->filename, | |
| 855 &url_input_text, | |
| 856 &active_method_dropdown, | |
| 857 url_body_map, | |
| 858 &url_result_text | |
| 859 ); | |
| 860 if (GuiButton(AddPaddingHorizontal(icon_area_right_column,padding), "delete")) | |
| 861 { | |
| 862 if (!remove(PostDog_Construct_URL(curr_history_items->filename))) | |
| 863 Dowa_Array_Push(history_deleted_items, i); | |
| 864 else | |
| 865 { | |
| 866 fprintf(stderr, "Wasn't able to delete file: %s \n", curr_history_items->filename); | |
| 867 } | |
| 868 } | |
| 869 } | |
| 870 EndScissorMode(); | |
| 871 | |
| 872 // Draw scroll indicator for history | |
| 873 if (total > 0) | |
| 717 { | 874 { |
| 718 HistoryItem *curr_history_items = i < new_history_items_length ? &new_history_items[i] : &history_items[i - new_history_items_length]; | 875 float content_height = total * (item_height + padding); |
| 719 DrawRectangleRec(curr_history_items->rect, Fade(RED, 0.1f)); | 876 if (content_height > history_list_area.height) |
| 720 GuiDrawText(curr_history_items->filename, AddPadding(curr_history_items->rect, padding), TEXT_ALIGN_CENTER, RED); | 877 { |
| 878 float scrollbar_height = (history_list_area.height / content_height) * history_list_area.height; | |
| 879 float scrollbar_y = history_list_area.y - (history_scroll_offset / content_height) * history_list_area.height; | |
| 880 Rectangle scrollbar = { | |
| 881 history_list_area.x + history_list_area.width - 5, | |
| 882 scrollbar_y, | |
| 883 5, | |
| 884 scrollbar_height | |
| 885 }; | |
| 886 DrawRectangleRec(scrollbar, Fade(WHITE, 0.5f)); | |
| 887 } | |
| 721 } | 888 } |
| 722 | 889 |
| 723 // URL area Rect | 890 // URL area Rect |
| 724 GuiDrawText("URL: ", url_text_bounds, TEXT_ALIGN_CENTER, RED); | 891 GuiDrawText("URL: ", url_text_bounds, TEXT_ALIGN_CENTER, RED); |
| 725 DrawRectangleRec(url_area, Fade(RED, 0.1f)); | 892 DrawRectangleRec(url_area, Fade(RED, 0.1f)); |
| 741 | 908 |
| 742 // Input Tabs Rect | 909 // Input Tabs Rect |
| 743 DrawRectangleRec(input_area, Fade(BLUE, 0.1f)); | 910 DrawRectangleRec(input_area, Fade(BLUE, 0.1f)); |
| 744 DrawRectangleRec(input_tab, Fade(DARKBLUE, 0.1f)); | 911 DrawRectangleRec(input_tab, Fade(DARKBLUE, 0.1f)); |
| 745 GuiSetStyle(TOGGLE, GROUP_PADDING, 0); | 912 GuiSetStyle(TOGGLE, GROUP_PADDING, 0); |
| 746 if (JUNE_GuiTextBox(input_body, url_body_map[active_input_tab], DEFAULT_TEXT_BUFFER_LENGTH, input_body_bool)) | 913 GuiDrawRectangle(input_body, 1, GetColor(GuiGetStyle(TEXTBOX, BORDER)), GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED))); |
| 747 input_body_bool = !input_body_bool; | 914 |
| 915 // Create scrollable input body with offset | |
| 916 BeginScissorMode(input_body.x, input_body.y, input_body.width, input_body.height); | |
| 917 Rectangle scrolled_input = AddPadding(input_body, padding * 2); | |
| 918 scrolled_input.y += input_body_scroll_offset; | |
| 919 scrolled_input.height = MAX_SCROLL_HEIGHT; | |
| 920 if (JUNE_GuiTextBox(scrolled_input, url_body_map[active_input_tab], DEFAULT_TEXT_BUFFER_LENGTH, input_body_bool)) | |
| 921 input_body_bool = !input_body_bool; | |
| 922 EndScissorMode(); | |
| 923 | |
| 924 // Draw scroll indicator for input body | |
| 925 if (input_body_scroll_offset < 0) { | |
| 926 float scrollbar_height = 50; // Fixed size indicator | |
| 927 float max_scroll = 1000; // Estimated max scroll | |
| 928 float scrollbar_y = input_body.y - (input_body_scroll_offset / max_scroll) * (input_body.height - scrollbar_height); | |
| 929 Rectangle scrollbar = { | |
| 930 input_body.x + input_body.width - 5, | |
| 931 scrollbar_y, | |
| 932 5, | |
| 933 scrollbar_height | |
| 934 }; | |
| 935 DrawRectangleRec(scrollbar, Fade(BLUE, 0.5f)); | |
| 936 } | |
| 937 | |
| 748 GuiToggleGroup(input_tab_item, "Header;Body;Get Param;Bar", &active_input_tab); | 938 GuiToggleGroup(input_tab_item, "Header;Body;Get Param;Bar", &active_input_tab); |
| 749 | 939 |
| 750 PostDog_Update_URL(&url_input_text, url_body_map[TAB_GET_PARAMS]); | 940 PostDog_Update_URL(&url_input_text, url_body_map[TAB_GET_PARAMS]); |
| 751 | 941 |
| 752 // Result Rect | 942 // Result Rect |
| 753 DrawRectangleRec(result_area, Fade(GREEN, 0.1f)); | 943 DrawRectangleRec(result_area, Fade(GREEN, 0.1f)); |
| 754 DrawRectangleRec(result_body, Fade(DARKGREEN, 0.1f)); | 944 DrawRectangleRec(result_body, Fade(DARKGREEN, 0.1f)); |
| 755 GuiTextBoxMulti(result_body, url_result_text, RESULT_BUFFER_LENGTH, false); | 945 |
| 946 // Create scrollable result body with offset | |
| 947 BeginScissorMode(result_body.x, result_body.y, result_body.width, result_body.height); | |
| 948 Rectangle scrolled_result = result_body; | |
| 949 scrolled_result.y += result_body_scroll_offset; | |
| 950 scrolled_result.height = MAX_SCROLL_HEIGHT; | |
| 951 GuiTextBoxMulti(scrolled_result, url_result_text, RESULT_BUFFER_LENGTH, false); | |
| 952 EndScissorMode(); | |
| 953 | |
| 954 // Draw scroll indicator for result body | |
| 955 if (result_body_scroll_offset < 0) { | |
| 956 float scrollbar_height = 50; // Fixed size indicator | |
| 957 float max_scroll = 1000; // Estimated max scroll | |
| 958 float scrollbar_y = result_body.y - (result_body_scroll_offset / max_scroll) * (result_body.height - scrollbar_height); | |
| 959 Rectangle scrollbar = { | |
| 960 result_body.x + result_body.width - 5, | |
| 961 scrollbar_y, | |
| 962 5, | |
| 963 scrollbar_height | |
| 964 }; | |
| 965 DrawRectangleRec(scrollbar, Fade(GREEN, 0.5f)); | |
| 966 } | |
| 756 | 967 |
| 757 if (url_input_edit && (IsKeyDown(KEY_LEFT_SUPER) || IsKeyDown(KEY_LEFT_CONTROL)) && IsKeyPressed(KEY_C)) | 968 if (url_input_edit && (IsKeyDown(KEY_LEFT_SUPER) || IsKeyDown(KEY_LEFT_CONTROL)) && IsKeyPressed(KEY_C)) |
| 758 { | 969 { |
| 759 SetClipboardText(url_input_text); | 970 SetClipboardText(url_input_text); |
| 760 } | 971 } |
| 766 { | 977 { |
| 767 DrawRectangleRec(result_body, Fade(GREEN, 0.3f)); | 978 DrawRectangleRec(result_body, Fade(GREEN, 0.3f)); |
| 768 if ((IsKeyDown(KEY_LEFT_SUPER) || IsKeyDown(KEY_LEFT_CONTROL)) && IsKeyPressed(KEY_C)) | 979 if ((IsKeyDown(KEY_LEFT_SUPER) || IsKeyDown(KEY_LEFT_CONTROL)) && IsKeyPressed(KEY_C)) |
| 769 SetClipboardText(url_result_text); | 980 SetClipboardText(url_result_text); |
| 770 } | 981 } |
| 771 | |
| 772 for (int i = 0; i < Dowa_Array_Length(history_items); i++) | |
| 773 { | |
| 774 if (Clicked(mouse_position, history_items[i].rect)) | |
| 775 PostDog_Load_File( | |
| 776 history_items[i].filename, | |
| 777 &url_input_text, | |
| 778 &active_method_dropdown, | |
| 779 url_body_map, | |
| 780 &url_result_text | |
| 781 ); | |
| 782 } | |
| 783 EndDrawing(); | 982 EndDrawing(); |
| 784 } | 983 } |
| 785 CloseWindow(); | 984 CloseWindow(); |
| 786 return 0; | 985 return 0; |
| 787 } | 986 } |