DEV Community

Cover image for Create dropdown menu (tui) in c
Samyabrata Maji
Samyabrata Maji

Posted on

Create dropdown menu (tui) in c

In this article, I’ll show how to create a simple dropdown menu using C, with the ability to navigate through options using the arrow keys and select one using the Enter key. Here’s how it would look.

dropdown menu tui

Disable terminal modes

First, we need to disable canonical mode and terminal echo.

In canonical mode, input is processed when line by line (when enter key is pressed). For our application, we want to read key press when it happens.

The echo mode, prints out everything that is typed. Again, not really useful - we don’t want to log every key press.

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>

void init_terminal() {
  struct termios term;
  tcgetattr(STDIN_FILENO, &term);
  term.c_lflag &= ~(ICANON | ECHO); // Disable canonical mode and echo
  tcsetattr(STDIN_FILENO, TCSANOW, &term);
}

void reset_terminal() {
  struct termios term;
  tcgetattr(STDIN_FILENO, &term);
  term.c_lflag |= (ICANON | ECHO);
  tcsetattr(STDIN_FILENO, TCSANOW, &term);
}
Enter fullscreen mode Exit fullscreen mode

The init_terminal function disables canonical mode and terminal echo. reset_terminal re-enables it.

Here, c_lflag is an unsigned int, where each bit indicates some property. ICANON and ECHO are unsigned int as well. If our, c_lflag is 1011, it indicates that canonical mode is turned on, since the bit that indicates canonical mode (2nd last bit) is 1. ICANON is 0010. When we do a bit-wise and operation, we flip only the bit indicating canonical mode and keep all the others as it is.

1011 & ~00101011 & 11011001

In our case, we are flipping bits for canonical mode and echo mode.

ANSI and Key Codes

We define some ANSI escape codes and some key codes.

Here, HIGHLIGHT escape code will flip the colors of the background and text, useful of highlighting an item.

#define CLEAR_SCREEN "\033[2J\033[H"
#define CURSOR_UP "\033[A"
#define CURSOR_DOWN "\033[B"
#define CLEAR_LINE "\033[K"
#define HIGHLIGHT "\033[7m" // Reverse video (highlight)
#define RESET "\033[0m"     // Reset attributes
Enter fullscreen mode Exit fullscreen mode

Struct definition

Let us also create a struct to store all the data in the dropdown menu. The create_dropdown method can be used to initialize the struct.

typedef struct {
    char **options;
    int num_options;
    int selected;
    char *title;
} Dropdown;

Dropdown *create_dropdown(char *title, char **options, int num_options) {
    Dropdown *dropdown = (Dropdown *) malloc(sizeof(Dropdown));
    if (!dropdown) {
        return NULL;
    }

    dropdown->options = options;
    dropdown->num_options = num_options;
    dropdown->selected = 0;
    dropdown->title = title;
    return dropdown;
}

void free_dropdown(Dropdown *dropdown) {
    free(dropdown);
}
Enter fullscreen mode Exit fullscreen mode

Printing dropdown

The code is pretty straightforward. We run over the options and print them one by one. We highlight the selected option.

void display_dropdown(Dropdown *dropdown) {
    printf(CLEAR_SCREEN);
    printf("%s\n\n", dropdown -> title);

    for (int i = 0; i < dropdown -> num_options; i++) {
        if (i == dropdown -> selected) {
            printf("%s> %s%s\n", HIGHLIGHT, dropdown -> options[i], RESET);
        } else {
            printf("  %s\n", dropdown -> options[i]);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Input handling

The read_key function defined below reads a single character and returns it.

int read_key() {
    char c;
    if (read(STDIN_FILENO, &c, 1) == 1) {
        return c;
    }
    return 0;
}
Enter fullscreen mode Exit fullscreen mode

When we press arrow keys, three characters are pushed to the input buffer:

  • '\033' (start of escape sequence)

  • '['

  • 'A', 'B', 'C' or 'D' (up, down, right, left)

So if we get \033[A, it indicates up arrow key press.

Let us define these character codes as macros:


#define KEY_UP 65
#define KEY_DOWN 66
#define KEY_ENTER 10
#define KEY_ESC 27
#define KEY_Q 113
Enter fullscreen mode Exit fullscreen mode

The run_dropdown method will read characters and re-renders the dropdown menu accordingly.

void handle_arrow_key(Dropdown *dropdown) {
    switch (read_key()) {
    case KEY_UP: // ascii code for A
        if (dropdown->selected > 0) {
            dropdown->selected--;
            display_dropdown(dropdown);
        }
        break;

    case KEY_DOWN: // ascii code for B
        if (dropdown->selected < dropdown->num_options - 1) {
            dropdown->selected++;
            display_dropdown(dropdown);
        }
        break;
    }
}

int run_dropdown(Dropdown *dropdown) {
    init_terminal();
    bool is_running = true;
    display_dropdown(dropdown);

    while (is_running) {
        int key = read_key();

        switch (key) {
        case KEY_ESC: // Check if this is an escape sequence for arrow keys
            if (read_key() == '[') {
                handle_arrow_key(dropdown);
            } else {
                // Plain ESC key was pressed - cancel selection
                dropdown->selected = -1;
                is_running = false;
            }
            break;

        case KEY_ENTER:
            // Confirm selection
            is_running = false;
            break;

        case KEY_Q:
            // Cancel selection
            dropdown->selected = -1;
            is_running = false;
            break;
        }
    }

    reset_terminal();
    return dropdown->selected;
}
Enter fullscreen mode Exit fullscreen mode

That’s it.

Now, you can use it to create a dropdown.

int main() {
    char title[] = "Select an option:";
    char *options[] = {
        "Option 1",
        "Option 2",
        "Option 3",
        "Option 4",
        "Option 5"
    };
    int num_options = sizeof(options) / sizeof(options[0]);
    Dropdown *dropdown = create_dropdown(title, options, num_options);

    int selected = run_dropdown(dropdown);

    // Clear screen and show result
    printf(CLEAR_SCREEN);
    if (selected >= 0) printf("You selected: %s\n", options[selected]);
    else printf("Selection cancelled\n");

    free_dropdown(dropdown);
    return 0;
}
Enter fullscreen mode Exit fullscreen mode

Here’s link to the full source code.

Now that you've implemented a basic dropdown, you can build more complex tui elements. You could handle signals (like SIGINT or SIGTERM), add mouse support, focus groups, etc.

Top comments (2)

Collapse
 
ddebajyati profile image
Debajyati Dey

Next level 🔥🔥🔥

Collapse
 
sammaji profile image
Samyabrata Maji

@ddebajyati Thanks 👍