Last active
October 25, 2020 17:18
-
-
Save etzl/e2c35306474219f66b9cacf75d3dc9ac to your computer and use it in GitHub Desktop.
ncurses with panels a customizable window inside terminal - but not very functional :)
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 <curses.h> | |
#include <panel.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#define NLINES 10 | |
#define NCOLS 40 | |
#define COUNT 3 | |
#define TEELINE 2 | |
typedef struct __PANEL_DATA { | |
int x,y, height, width; | |
int label_color; | |
char label[80]; | |
PANEL* next; | |
} PANEL_DATA; | |
void init(WINDOW **wins, PANEL **all_panels); | |
void win_show(WINDOW *win, char *label, int label_color); | |
void print_in_middle(WINDOW* win, int maxx, int teeline, char *string, | |
chtype color); | |
void operate_on_panel(PANEL* top); | |
void move_mode(PANEL* top); | |
void resize_mode(PANEL* top); | |
int main() | |
{ | |
WINDOW *my_wins[COUNT]; | |
PANEL *my_panels[COUNT]; | |
PANEL *top; | |
int ch; | |
/* Initialize curses */ | |
initscr(); | |
start_color(); | |
cbreak(); | |
noecho(); | |
keypad(stdscr, TRUE); | |
/* Initialize all the colors */ | |
init_pair(1, COLOR_RED, COLOR_BLACK); | |
init_pair(2, COLOR_GREEN, COLOR_BLACK); | |
init_pair(3, COLOR_BLUE, COLOR_BLACK); | |
init_pair(4, COLOR_CYAN, COLOR_BLACK); | |
chtype notice_color = COLOR_PAIR(4); | |
init(my_wins, my_panels); | |
/* Update the stacking order. 3rd panel will be on top */ | |
update_panels(); | |
/* Show it on the screen */ | |
attron(notice_color); | |
mvprintw(LINES - 2, 0, "Use <tab> to browse, <m> to move, <r> to resize, <Enter> to write (F1 to Exit)"); | |
attroff(notice_color); | |
refresh(); | |
top = my_panels[2]; | |
PANEL_DATA* current_data = (PANEL_DATA*)panel_userptr(top); | |
while((ch = getch()) != KEY_F(1)) | |
{ switch(ch) | |
{ | |
case 'm': | |
attron(notice_color); | |
mvprintw(LINES-3, 0, "You can move panels with arrow keys (use <Enter> to exit!)"); | |
attroff(notice_color); | |
refresh(); | |
move_mode(top); | |
break; | |
case 'r': | |
attron(notice_color); | |
mvprintw(LINES-3, 0, "Use arrow keys to resize window and press <Enter> when you're done!"); | |
attroff(notice_color); | |
refresh(); | |
resize_mode(top); | |
break; | |
case '\t': | |
top_panel(current_data->next); | |
top = current_data->next; | |
current_data = (PANEL_DATA*)panel_userptr(top); | |
update_panels(); | |
doupdate(); | |
break; | |
case '\n': // allow user to insert text in the current panel | |
attron(notice_color); | |
mvprintw(LINES - 3, 0, "Entered writing mode use <Esc> to exit"); | |
attroff(notice_color); | |
refresh(); | |
operate_on_panel(top); | |
break; | |
} | |
} | |
endwin(); | |
return 0; | |
} | |
void move_mode(PANEL* top) | |
{ | |
PANEL_DATA* data = (PANEL_DATA*)panel_userptr(top); | |
int c; | |
while ((c = getch()) != 10) { | |
switch (c) { | |
case KEY_UP: | |
--data->y; | |
break; | |
case KEY_DOWN: | |
++data->y; | |
break; | |
case KEY_LEFT: | |
--data->x; | |
break; | |
case KEY_RIGHT: | |
++data->x; | |
break; | |
} | |
move_panel(top, data->y, data->x); | |
update_panels(); | |
doupdate(); | |
} | |
move(LINES-3, 0); | |
clrtoeol(); | |
refresh(); | |
update_panels(); | |
doupdate(); | |
} | |
void resize_mode(PANEL* top) | |
{ | |
PANEL_DATA* data = (PANEL_DATA*)panel_userptr(top); | |
int ch; | |
while ((ch = getch()) != 10) { | |
switch (ch) { | |
case KEY_UP: | |
--(data->y); | |
++(data->height); | |
break; | |
case KEY_DOWN: | |
++(data->y); | |
--(data->height); | |
break; | |
case KEY_LEFT: | |
--(data->x); | |
++(data->width); | |
break; | |
case KEY_RIGHT: | |
++(data->x); | |
--(data->width); | |
break; | |
} | |
WINDOW* tmp = newwin(data->height, data->width, data->y, data->x); | |
WINDOW* old_win = panel_window(top); | |
replace_panel(top, tmp); | |
delwin(old_win); | |
// replacing panel doesn't automatically show the new window | |
win_show(tmp, data->label, data->label_color); | |
update_panels(); | |
doupdate(); | |
} | |
move(LINES-3, 0); | |
clrtoeol(); | |
refresh(); | |
update_panels(); | |
doupdate(); | |
} | |
/* Put all the windows */ | |
void init(WINDOW **wins, PANEL **panels_arr) | |
{ int x, y, i; | |
char label[80]; | |
PANEL_DATA* panels = (PANEL_DATA*)calloc(COUNT, sizeof(PANEL_DATA)); | |
y = 2; | |
x = 10; | |
for(i = 0; i < COUNT; ++i) | |
{ wins[i] = newwin(NLINES, NCOLS, y, x); | |
sprintf(label, "Window Number %d", i + 1); | |
win_show(wins[i], label, i + 1); | |
// attach panels | |
panels_arr[i] = new_panel(wins[i]); | |
// create data | |
strcpy(panels[i].label, label); | |
panels[i].label_color = i+1; | |
panels[i].x = x; | |
panels[i].y = y; | |
panels[i].height = NLINES; | |
panels[i].width = NCOLS; | |
set_panel_userptr(panels_arr[i], &panels[i]); | |
// do something :) | |
y += 3; | |
x += 7; | |
} | |
panels[0].next = panels_arr[1]; | |
panels[1].next = panels_arr[2]; | |
panels[2].next = panels_arr[0]; | |
} | |
/* Show the window with a border and a label */ | |
void win_show(WINDOW *win, char *label, int label_color) | |
{ int startx, starty, maxy, maxx; | |
getbegyx(win, starty, startx); | |
getmaxyx(win, maxy, maxx); | |
box(win, 0, 0); | |
mvwaddch(win, TEELINE, 0, ACS_LTEE); | |
mvwhline(win, TEELINE, 1, ACS_HLINE, maxx - 2); | |
mvwaddch(win, TEELINE, maxx - 1, ACS_RTEE); | |
print_in_middle(win, maxx, TEELINE, label, COLOR_PAIR(label_color)); | |
} | |
void print_in_middle(WINDOW* win, int maxx, int teeline, char *string, | |
chtype color) | |
{ | |
int length = strlen(string); | |
int middle = (maxx - length) / 2; | |
wattron(win, color); | |
mvwprintw(win, teeline/2, middle, "%s", string); | |
wattroff(win, color); | |
wrefresh(win); | |
} | |
void operate_on_panel(PANEL* p) | |
{ | |
WINDOW *top_win = panel_window(p); | |
int curs_x, curs_y; | |
getyx(top_win, curs_y, curs_x); | |
int cols = getmaxx(top_win); | |
wmove(top_win, TEELINE+1, 1); /* set cursor ready to write */ | |
int c = 0; | |
while ((c = wgetch(top_win)) != 27) { | |
int x,y; | |
getyx(top_win, y, x); | |
if (x == cols-1) | |
wmove(top_win, y+1, 1); | |
waddch(top_win, c); // print what user has typed | |
wrefresh(top_win); | |
} | |
/* clear notice */ | |
move(LINES-3,0); | |
clrtoeol(); | |
refresh(); | |
// return cursor back to initial position | |
wmove(top_win, curs_y, curs_x); | |
wrefresh(top_win); | |
} |
Also if you wanted to solve a problem :) take a look at line 209-211. I couldn't put these lines inside the loop which somehow didn't initialized at runtime. I've tried if-else to set last parameter to first one and also used this line panels[i].next = panels_arr[(i+1) % COUNT]
but didn't have any luck.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Note that I took the base code from here (which I highly recommend for anyone learning ncurses) and added some functionality and changed some of the code.