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 }