diff --git a/src/interactive/interactive.cpp b/src/interactive/interactive.cpp index 6eb088e..944349e 100644 --- a/src/interactive/interactive.cpp +++ b/src/interactive/interactive.cpp @@ -21,11 +21,12 @@ int fullscreen_window::ncurses_streambuf::overflow(int c) { } fullscreen_window::fullscreen_window(std::string title) { + // Initialize ncurses initscr(); - cbreak(); + raw(); // Use raw mode for better control noecho(); - keypad(stdscr, TRUE); - curs_set(0); + keypad(stdscr, TRUE); // Enable keypad + curs_set(0); // Hide cursor refresh(); // Create display window (takes up all but bottom 4 lines) @@ -33,11 +34,13 @@ fullscreen_window::fullscreen_window(std::string title) { getmaxyx(stdscr, max_y, max_x); display_win = newwin(max_y - 4, max_x, 0, 0); scrollok(display_win, TRUE); + keypad(display_win, TRUE); // Enable keypad for display window wrefresh(display_win); // Create input window (bottom 4 lines) input_win = newwin(4, max_x, max_y - 4, 0); box(input_win, 0, 0); + keypad(input_win, TRUE); // Enable keypad for input window wrefresh(input_win); // Set up output redirection @@ -138,6 +141,9 @@ std::string fullscreen_window::set_input_multiple_choice(std::string prompt, std std::string filter = ""; auto last_key_time = std::chrono::steady_clock::now(); int scroll_offset = 0; + std::vector last_filtered_choices; + int last_selected = -1; + int last_scroll_offset = -1; while (true) { // Filter choices based on input @@ -153,12 +159,6 @@ std::string fullscreen_window::set_input_multiple_choice(std::string prompt, std filter = ""; } - // Calculate total width needed - int total_width = 0; - for (const auto& choice : filtered_choices) { - total_width += choice.length() + 3; // +3 for " | " separator - } - // Get window dimensions int max_y, max_x; getmaxyx(input_win, max_y, max_x); @@ -179,76 +179,104 @@ std::string fullscreen_window::set_input_multiple_choice(std::string prompt, std } } - // Display filtered choices horizontally - int x = 2 - scroll_offset; - for (size_t i = 0; i < filtered_choices.size(); i++) { - if (i == selected) { - wattron(input_win, A_REVERSE); - } + // Only redraw if something changed + if (filtered_choices != last_filtered_choices || + selected != last_selected || + scroll_offset != last_scroll_offset) { - // Only draw if visible - if (x + filtered_choices[i].length() > 0 && x < max_x - 2) { - mvwprintw(input_win, 2, x, "%s", filtered_choices[i].c_str()); - } + // Clear the input window except for the prompt + werase(input_win); + box(input_win, 0, 0); + mvwprintw(input_win, 1, 2, "%s", prompt.c_str()); - if (i == selected) { - wattroff(input_win, A_REVERSE); - } - - x += filtered_choices[i].length() + 3; // +3 for " | " separator - - // Draw separator if not last item - if (i < filtered_choices.size() - 1) { - if (x - 3 + 3 > 0 && x - 3 < max_x - 2) { - mvwprintw(input_win, 2, x - 3, " | "); + // Display filtered choices horizontally + int x = 2 - scroll_offset; + for (size_t i = 0; i < filtered_choices.size(); i++) { + if (i == selected) { + wattron(input_win, A_REVERSE); + } + + // Only draw if visible + if (x + filtered_choices[i].length() > 0 && x < max_x - 2) { + mvwprintw(input_win, 2, x, "%s", filtered_choices[i].c_str()); + } + + if (i == selected) { + wattroff(input_win, A_REVERSE); + } + + x += filtered_choices[i].length() + 3; // +3 for " | " separator + + // Draw separator if not last item + if (i < filtered_choices.size() - 1) { + if (x - 3 + 3 > 0 && x - 3 < max_x - 2) { + mvwprintw(input_win, 2, x - 3, " | "); + } } } + + wrefresh(input_win); + + // Update last state + last_filtered_choices = filtered_choices; + last_selected = selected; + last_scroll_offset = scroll_offset; } - wrefresh(input_win); - int ch = wgetch(input_win); // Handle key input - if (ch == '\n') { - if (!filtered_choices.empty()) { - return filtered_choices[selected]; - } - } else if (ch == 27) { // ESC key - if (!filter.empty()) { - // Clear the filter and reset selection - filter = ""; - selected = 0; - scroll_offset = 0; - } else { - // Only return if filter is already empty - return ""; - } - } else if (ch == KEY_LEFT || ch == KEY_UP) { - if (selected > 0) { - selected--; - } else { - selected = filtered_choices.size() - 1; // Wrap to end - } - continue; // Skip the rest of the loop - } else if (ch == KEY_RIGHT || ch == KEY_DOWN) { - if (selected < filtered_choices.size() - 1) { - selected++; - } else { - selected = 0; // Wrap to beginning - } - continue; // Skip the rest of the loop - } else if (isprint(ch)) { - auto now = std::chrono::steady_clock::now(); - auto elapsed = std::chrono::duration_cast(now - last_key_time).count(); - - if (elapsed > 500) { // Reset filter if too much time has passed - filter = ""; - } - - filter += ch; - selected = 0; - last_key_time = now; + switch (ch) { + case '\n': + if (!filtered_choices.empty()) { + return filtered_choices[selected]; + } + break; + + case 27: // ESC key + if (!filter.empty()) { + // Clear the filter and reset selection + filter = ""; + selected = 0; + scroll_offset = 0; + } else { + // Only return if filter is already empty + return ""; + } + break; + + case KEY_LEFT: + case KEY_UP: + if (selected > 0) { + selected--; + } else { + selected = filtered_choices.size() - 1; // Wrap to end + } + break; + + case KEY_RIGHT: + case KEY_DOWN: + if (selected < filtered_choices.size() - 1) { + selected++; + } else { + selected = 0; // Wrap to beginning + } + break; + + default: + if (isalnum(ch)) { + auto now = std::chrono::steady_clock::now(); + auto elapsed = std::chrono::duration_cast(now - last_key_time).count(); + + if (elapsed > 500) { // Reset filter if too much time has passed + filter = ""; + } + + filter += ch; + selected = 0; + last_key_time = now; + } + break; } } }