Skip to content

Instantly share code, notes, and snippets.

@b4284
Created January 17, 2021 12:43
Show Gist options
  • Save b4284/6a3f3cf61d91af26c3d4064ab6cb69ec to your computer and use it in GitHub Desktop.
Save b4284/6a3f3cf61d91af26c3d4064ab6cb69ec to your computer and use it in GitHub Desktop.
#include <ncurses.h>
#include <panel.h>
#include <stdbool.h>
#include <string.h>
#include <locale.h>
#define WIDTH 80
#define HEIGHT 20
#define MAXTAB 10
WINDOW *debugwin;
typedef struct {
WINDOW *win;
PANEL *panel;
char title[50];
int beforelen;
int afterlen;
} TAB;
typedef struct {
int height;
int width;
int y;
int x;
WINDOW *tabbarw;
PANEL *tabbarp;
int active_tab;
int tab_cnt;
TAB tabs[MAXTAB];
} TABINFO;
void init_colors() {
init_pair(1, COLOR_WHITE, COLOR_BLUE);
init_pair(2, COLOR_WHITE, COLOR_BLUE);
init_pair(3, COLOR_BLACK, COLOR_WHITE);
}
int draw_tab_title(TABINFO *tabinfo, int offset, const char *str, bool selected) {
wattron(tabinfo->tabbarw, COLOR_PAIR(1));
if (selected) {
wattron(tabinfo->tabbarw, A_BOLD);
} else {
wattroff(tabinfo->tabbarw, A_BOLD);
}
mvwprintw(tabinfo->tabbarw, 0, offset, " %s ", str);
wattroff(tabinfo->tabbarw, COLOR_PAIR(1));
wattroff(tabinfo->tabbarw, A_BOLD);
return strlen(str) + 2;
}
WINDOW *add_tab(TABINFO *tabinfo, char *str, bool deflt) {
WINDOW *newtabwin = newwin(HEIGHT - 3, WIDTH - 4, 2, 2);
PANEL *panel = new_panel(newtabwin);
TAB *nexttab = &(tabinfo->tabs[tabinfo->tab_cnt]);
TAB *nowtab = (tabinfo->tab_cnt ? &(tabinfo->tabs[tabinfo->tab_cnt - 1]) : NULL);
nexttab->win = newtabwin;
nexttab->panel = panel;
strcpy(nexttab->title, str);
nexttab->beforelen = (nowtab == NULL ? 0 : nowtab->afterlen);
nexttab->afterlen = (nowtab == NULL ? 0 : nowtab->afterlen) + draw_tab_title(tabinfo, nexttab->beforelen, str, deflt);
if (deflt) {
tabinfo->active_tab = tabinfo->tab_cnt;
} else {
hide_panel(panel);
}
tabinfo->tab_cnt += 1;
update_panels();
return newtabwin;
}
void init_tab(TABINFO *tabinfo, int h, int w, int y, int x) {
tabinfo->height = h;
tabinfo->width = w;
tabinfo->y = y;
tabinfo->x = x;
tabinfo->tabbarw = newwin(h, w, y, x);
tabinfo->tabbarp = new_panel(tabinfo->tabbarw);
tabinfo->active_tab = 0;
tabinfo->tab_cnt = 0;
wbkgd(tabinfo->tabbarw, COLOR_PAIR(3));
WINDOW *boxwin = newwin(h - 1, w, y + 1, x);
new_panel(boxwin);
box(boxwin, 0, 0);
update_panels();
}
void debug(const char *fmt, ...) {
static int debugn = 0;
va_list ap;
char buf[500];
va_start(ap, fmt);
vsnprintf(buf, sizeof buf, fmt, ap);
va_end(ap);
mvwprintw(debugwin, debugn++, 0, buf);
if (debugn > 20) {
wclear(debugwin);
debugn = 0;
}
wrefresh(debugwin);
}
void switch_tab(TABINFO *tabinfo, int tab) {
if (tabinfo->active_tab != tab) {
for (int i = 0; i < tabinfo->tab_cnt; i++) {
if (i == tabinfo->active_tab) {
draw_tab_title(tabinfo, tabinfo->tabs[i].beforelen, tabinfo->tabs[i].title, false);
} else if (i == tab) {
draw_tab_title(tabinfo, tabinfo->tabs[i].beforelen, tabinfo->tabs[i].title, true);
}
}
hide_panel(tabinfo->tabs[tabinfo->active_tab].panel);
tabinfo->active_tab = tab;
show_panel(tabinfo->tabs[tabinfo->active_tab].panel);
update_panels();
}
}
int is_tab_clicked(TABINFO *tabinfo, int y, int x) {
if (y == tabinfo->y && x >= tabinfo->x && x < (tabinfo->x + tabinfo->width)) {
for (int i = 0; i < tabinfo->tab_cnt; i++) {
if (x >= tabinfo->tabs[i].beforelen && x < tabinfo->tabs[i].afterlen) {
return i;
}
}
}
return -1;
}
bool is_click_in_tab_area(TABINFO *tabinfo, int y, int x) {
return (x >= tabinfo->x+2 && x < tabinfo->x+tabinfo->width-2 &&
y >= tabinfo->y+2 && y < tabinfo->y+tabinfo->height-1);
}
typedef struct {
WINDOW *win;
char title[50];
int y;
int x1;
int x2;
} TEXTBOX;
void draw_text_box(TEXTBOX *textbox, TABINFO *tabinfo, WINDOW *tabwin, int y, int x, const char *str) {
mvwprintw(tabwin, y, x, "%s: ", str);
int len = strlen(str) + 2;
textbox->win = derwin(tabwin, 1, tabinfo->width - 4 - len, y, x + len);
wbkgd(textbox->win, COLOR_PAIR(3));
wrefresh(textbox->win);
strcpy(textbox->title, str);
textbox->y = y;
textbox->x1 = x + len;
textbox->x2 = tabinfo->width - 4;
}
bool is_textbox_clicked(TEXTBOX *textbox, int y, int x) {
if (y == textbox->y && x >= textbox->x1 && x < textbox->x2) {
debug("textbox %s is clicked", textbox->title);
return true;
}
return false;
}
void focus_textbox(TEXTBOX *textbox) {
wmove(textbox->win, 0, 0);
wattron(textbox->win, COLOR_PAIR(3));
wrefresh(textbox->win);
}
void convert_tab_xy(TABINFO *tabinfo, int *newy, int *newx, int eventy, int eventx) {
*newy = eventy - tabinfo->y - 2;
*newx = eventx - tabinfo->x - 2;
}
void line_edit(WINDOW *win, int ch) {
int cury, curx;
getyx(win, cury, curx);
switch (ch) {
case 127:
wmove(win, cury, curx - 1);
wdelch(win);
break;
case KEY_LEFT:
wmove(win, cury, curx - 1);
debug("KEY_LEFT");
break;
case KEY_RIGHT:
wmove(win, cury, curx + 1);
debug("KEY_RIGHT");
break;
case KEY_DC:
wdelch(win);
debug("KEY_RIGHT");
break;
default:
waddch(win, ch);
break;
}
}
int main()
{
MEVENT event = {0};
int ch;
TABINFO tabinfo = {0};
setlocale(LC_ALL, "");
initscr(); /* Start curses mode */
keypad(stdscr, TRUE);
noecho();
cbreak();
mousemask(ALL_MOUSE_EVENTS, NULL);
// color for tab button
start_color();
init_colors();
// init debug window
debugwin = newwin(20, 80, 30, 0);
new_panel(debugwin);
// create tabwin
init_tab(&tabinfo, HEIGHT, WIDTH, 0, 0);
// print the buttons
WINDOW *tab1 = add_tab(&tabinfo, "Main", true);
WINDOW *tab2 = add_tab(&tabinfo, "Options", false);
TEXTBOX textbox[10] = {0};
WINDOW *focuswin = NULL;
int n = 0;
draw_text_box(&textbox[n++], &tabinfo, tab1, 0, 0, "TextBoxA");
draw_text_box(&textbox[n++], &tabinfo, tab1, 1, 0, "T_ext_Box_B");
/* draw_text_box(&textbox[n++], &tabinfo, tab2, 0, 0, "Version"); */
update_panels();
doupdate();
for (;;) {
ch = getch();
debug("ch=%d", ch);
if (ch == KEY_MOUSE) {
focuswin = NULL;
if (getmouse(&event) == OK) {
debug("evx=%d,evy=%d", event.x, event.y);
if (event.bstate & BUTTON1_CLICKED) {
// did we click on tab bar
int tab_clicked = is_tab_clicked(&tabinfo, event.y, event.x);
if (tab_clicked >= 0) {
switch_tab(&tabinfo, tab_clicked);
doupdate();
}
if (is_click_in_tab_area(&tabinfo, event.y, event.x)) {
int tabx, taby;
convert_tab_xy(&tabinfo, &taby, &tabx, event.y, event.x);
for (int i = 0; i < n; i++) {
if (is_textbox_clicked(&textbox[i], taby, tabx)) {
focus_textbox(&textbox[i]);
focuswin = textbox[i].win;
}
}
}
}
}
} else {
// line-editing mode
if (focuswin != NULL) {
line_edit(focuswin, ch);
wrefresh(focuswin);
}
if (ch == 'q') {
goto END;
}
}
}
END:
endwin(); /* End curses mode */
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment