-
-
Save b4284/6a3f3cf61d91af26c3d4064ab6cb69ec to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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