ここまで、cursesに熟達してきたが、そろそろもっと大きなことをやってみたくなったのではないだろうか?
よりかっこい見栄えのウィンドウを沢山重ねたものを作ることもできるだろうが、不運にも、このウィンドウの管理は大変になって来ているだろう。
refreshとupdateの組は、悪夢の様な状況にしてしまう。正しい順番でのウィンドウのrefreshを忘れるたびに、ウィンドウの重なりはボロボロになってしまう。
しかし、落胆しないように!パネルライブラリが素敵なソリューションを用意してくれているから。
作成したUIのウィンドウが重なりの一番下に沈めたり、一番上に引き出すような作りになっている時、これを正しく実装するのは「退屈」で「面倒」だ。
だから、「パネルライブラリ」があるんだ。
もし沢山のウィンドウが重なるなら、パネルライブラリはもってこいだ。
パネルライブラリはnoutrefres()とdoupdate()の整合の必要性を撤去し、一番下のウィンドウから処理する重荷を取り除いてくれる。
パネルライブラリはウィンドウの順序、重なりを維持し、画面を適切に更新してくれる。だからじっとしている理由はない。パネルライブラリを見てみよう!
パネルオブジェクトは、全ての他のパネルオブジェクトを含む束の一部として暗黙のうちに扱われる。
このパネルの束は、完全に見える一番上のパネルや、位置次第では覆いかぶさったりしているかもしれない他のパネルのスタックを処理する。
その基本的な仕組みは、「重なりあうパネルのスタックを作成し、パネルライブラリを使ってそれらを正しく表示する」ようになっている。
これはrefresh()関数を読んだ時のように、正しい順序でパネルを表示するようになっている。
また、パネルを表示したり隠したり、移動したり、サイズを変えたりするような関数が用意されている。
パネルライブラリが提供するこれらの関数を使っている間はずっと、パネルの重なりの問題はパネルライブラリによって管理される。
パネルを使うプログラムの一般的な流れは以下の様になる。
いくつかのプログラムで、このコンセプトを明確にしていこう。
以下に3つの重なりあうパネルを生成し、画面に表示する簡単なプログラムを示す。
パネルライブラリの関数を使うためには、panel.hをインクルートし、リンクのオプションとして -lpanel を -lncurses よりも先に指定する必要がある。
#include <panel.h> . . . コンパイルとリンク: gcc <program file> -lpanel -lncurses
/* Example 14. Panel basics */ #include <panel.h> int main() { WINDOW *my_wins[3]; PANEL *my_panels[3]; int lines = 10, cols = 40, y = 2, x = 4, i; initscr(); cbreak(); noecho(); /* Create windows for the panels */ my_wins[0] = newwin(lines, cols, y, x); my_wins[1] = newwin(lines, cols, y + 1, x + 5); my_wins[2] = newwin(lines, cols, y + 2, x + 10); /* * Create borders around the windows so that you can see the effect * of panels */ for(i = 0; i < 3; ++i) box(my_wins[i], 0, 0); /* Attach a panel to each window */ /* Order is bottom up */ my_panels[0] = new_panel(my_wins[0]); /* Push 0, order: stdscr-0 */ my_panels[1] = new_panel(my_wins[1]); /* Push 1, order: stdscr-0-1 */ my_panels[2] = new_panel(my_wins[2]); /* Push 2, order: stdscr-0-1-2 */ /* Update the stacking order. 2nd panel will be on top */ update_panels(); /* Show it on the screen */ doupdate(); getch(); endwin(); }
説明した使い方の簡単な流れを上に示したプログラムから理解することができるだろう。
ウィンドウは newwin()関数で生成され、そして new_panel()関数でパネルと結びつけられる。
そして、パネルを他のパネルと結びつけていくことで、パネルのスタックをupdateすることができる。
update_panels()関数と doupdate()関数を呼び出すことで、画面上にパネルを配置している。
以下にもう少し複雑な例を示す。
このプログラムはTABキーを用いて一番上のパネルを順番に変えることができる3つのパネルを表示しているものである。
コードを見てみよう。
Example 15. Panel Window Browsing Example #include <panel.h> #define NLINES 10 #define NCOLS 40 void init_wins(WINDOW **wins, int n); void win_show(WINDOW *win, char *label, int label_color); void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color); int main() { WINDOW *my_wins[3]; PANEL *my_panels[3]; 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); init_wins(my_wins, 3); /* Attach a panel to each window */ /* Order is bottom up */ my_panels[0] = new_panel(my_wins[0]); /* Push 0, order: stdscr-0 */ my_panels[1] = new_panel(my_wins[1]); /* Push 1, order: stdscr-0-1 */ my_panels[2] = new_panel(my_wins[2]); /* Push 2, order: stdscr-0-1-2 */ /* Set up the user pointers to the next panel */ set_panel_userptr(my_panels[0], my_panels[1]); set_panel_userptr(my_panels[1], my_panels[2]); set_panel_userptr(my_panels[2], my_panels[0]); /* Update the stacking order. 2nd panel will be on top */ update_panels(); /* Show it on the screen */ attron(COLOR_PAIR(4)); mvprintw(LINES - 2, 0, "Use tab to browse through the windows (F1 to Exit)"); attroff(COLOR_PAIR(4)); doupdate(); top = my_panels[2]; while((ch = getch()) != KEY_F(1)) { switch(ch) { case 9: top = (PANEL *)panel_userptr(top); top_panel(top); break; } update_panels(); doupdate(); } endwin(); return 0; } /* Put all the windows */ void init_wins(WINDOW **wins, int n) { int x, y, i; char label[80]; y = 2; x = 10; for(i = 0; i < n; ++i) { wins[i] = newwin(NLINES, NCOLS, y, x); sprintf(label, "Window Number %d", i + 1); win_show(wins[i], label, i + 1); y += 3; x += 7; } } /* Show the window with a border and a label */ void win_show(WINDOW *win, char *label, int label_color) { int startx, starty, height, width; getbegyx(win, starty, startx); getmaxyx(win, height, width); box(win, 0, 0); mvwaddch(win, 2, 0, ACS_LTEE); mvwhline(win, 2, 1, ACS_HLINE, width - 2); mvwaddch(win, 2, width - 1, ACS_RTEE); print_in_middle(win, 1, 0, width, label, COLOR_PAIR(label_color)); } void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color) { int length, x, y; float temp; if(win == NULL) win = stdscr; getyx(win, y, x); if(startx != 0) x = startx; if(starty != 0) y = starty; if(width == 0) width = 80; length = strlen(string); temp = (width - length)/ 2; x = startx + (int)temp; wattron(win, color); mvwprintw(win, y, x, "%s", string); wattroff(win, color); refresh(); }
上の例で、次のパネルを一番上にするためにユーザポインタを利用している。
プログラマが保存しておきたい情報へのポインタをユーザポインタに格納しておくことで、パネルとこの情報を結びつけることができる。
このケースでは、次に一番上にするパネルのポインタをユーザポインタに持たせている。
パネルのユーザポインタは、set_panel_userptr()関数で設定することができる。
そして、panel_userptr()関数を使うことで、予め設定したユーザポインタを戻り値として取得することができる。
上の例では、次に一番上に表示するパネルをユーザポインタから取得した後、top_panel()関数で一番上に持って来ている。
この関数は、引数として与えられたパネルをパネルスタックの一番上に持って来る。
move_panel()関数は、パネルを希望する位置へ移動することができる。
(パネルスタック内でのパネルの位置は変えられない。)
なお、パネルと結びつけたウィンドウの場合 mvwin()関数の変わりに move_panel()関数を使わなければならないことに注意すること。
パネルのリサイズは少し複雑だ。
パネルを結びつけたウィンドウのリサイズを行うための関数はない。
パネルのリサイズは、希望するサイズのウィンドウを新たに作成し、replace_panel()関数を用いてパネルを結びつけるウィンドウを変えることで実現する。
古いウィンドウの削除を忘れてはならない。
パネルを結びつけたウィンドウは panel_window()関数で取得することができる。
以下のプログラムは3つの要点を示す、単純なものだ。
TABキーを持ちいてウィンドウの表示順を廻すことができる。
rキーでリサイズ、mキーでパネルの移動ができる。
なお、矢印キーで好きなサイズにリサイズ、もしくは移動でき、リサイズや移動を終える場合はエンターキーを押すようになっている。
この例では操作をするために必要なデータをユーザポインタとして使っている。
/* Example 16. Panel Moving and Resizing example */ #include <panel.h> typedef struct _PANEL_DATA { int x, y, w, h; char label[80]; int label_color; PANEL *next; }PANEL_DATA; #define NLINES 10 #define NCOLS 40 void init_wins(WINDOW **wins, int n); void win_show(WINDOW *win, char *label, int label_color); void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color); void set_user_ptrs(PANEL **panels, int n); int main() { WINDOW *my_wins[3]; PANEL *my_panels[3]; PANEL_DATA *top; PANEL *stack_top; WINDOW *temp_win, *old_win; int ch; int newx, newy, neww, newh; int size = FALSE, move = FALSE; /* 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); init_wins(my_wins, 3); /* Attach a panel to each window */ /* Order is bottom up */ my_panels[0] = new_panel(my_wins[0]); /* Push 0, order: stdscr-0 */ my_panels[1] = new_panel(my_wins[1]); /* Push 1, order: stdscr-0-1 */ my_panels[2] = new_panel(my_wins[2]); /* Push 2, order: stdscr-0-1-2 */ set_user_ptrs(my_panels, 3); /* Update the stacking order. 2nd panel will be on top */ update_panels(); /* Show it on the screen */ attron(COLOR_PAIR(4)); mvprintw(LINES - 3, 0, "Use 'm' for moving, 'r' for resizing"); mvprintw(LINES - 2, 0, "Use tab to browse through the windows (F1 to Exit)"); attroff(COLOR_PAIR(4)); doupdate(); stack_top = my_panels[2]; top = (PANEL_DATA *)panel_userptr(stack_top); newx = top->x; newy = top->y; neww = top->w; newh = top->h; while((ch = getch()) != KEY_F(1)) { switch(ch) { case 9: /* Tab */ top = (PANEL_DATA *)panel_userptr(stack_top); top_panel(top->next); stack_top = top->next; top = (PANEL_DATA *)panel_userptr(stack_top); newx = top->x; newy = top->y; neww = top->w; newh = top->h; break; case 'r': /* Re-Size*/ size = TRUE; attron(COLOR_PAIR(4)); mvprintw(LINES - 4, 0, "Entered Resizing :Use Arrow Keys to resize and press <ENTER> to end resizing"); refresh(); attroff(COLOR_PAIR(4)); break; case 'm': /* Move */ attron(COLOR_PAIR(4)); mvprintw(LINES - 4, 0, "Entered Moving: Use Arrow Keys to Move and press <ENTER> to end moving"); refresh(); attroff(COLOR_PAIR(4)); move = TRUE; break; case KEY_LEFT: if(size == TRUE) { --newx; ++neww; } if(move == TRUE) --newx; break; case KEY_RIGHT: if(size == TRUE) { ++newx; --neww; } if(move == TRUE) ++newx; break; case KEY_UP: if(size == TRUE) { --newy; ++newh; } if(move == TRUE) --newy; break; case KEY_DOWN: if(size == TRUE) { ++newy; --newh; } if(move == TRUE) ++newy; break; case 10: /* Enter */ move(LINES - 4, 0); clrtoeol(); refresh(); if(size == TRUE) { old_win = panel_window(stack_top); temp_win = newwin(newh, neww, newy, newx); replace_panel(stack_top, temp_win); win_show(temp_win, top->label, top->label_color); delwin(old_win); size = FALSE; } if(move == TRUE) { move_panel(stack_top, newy, newx); move = FALSE; } break; } attron(COLOR_PAIR(4)); mvprintw(LINES - 3, 0, "Use 'm' for moving, 'r' for resizing"); mvprintw(LINES - 2, 0, "Use tab to browse through the windows (F1 to Exit)"); attroff(COLOR_PAIR(4)); refresh(); update_panels(); doupdate(); } endwin(); return 0; } /* Put all the windows */ void init_wins(WINDOW **wins, int n) { int x, y, i; char label[80]; y = 2; x = 10; for(i = 0; i < n; ++i) { wins[i] = newwin(NLINES, NCOLS, y, x); sprintf(label, "Window Number %d", i + 1); win_show(wins[i], label, i + 1); y += 3; x += 7; } } /* Set the PANEL_DATA structures for individual panels */ void set_user_ptrs(PANEL **panels, int n) { PANEL_DATA *ptrs; WINDOW *win; int x, y, w, h, i; char temp[80]; ptrs = (PANEL_DATA *)calloc(n, sizeof(PANEL_DATA)); for(i = 0;i < n; ++i) { win = panel_window(panels[i]); getbegyx(win, y, x); getmaxyx(win, h, w); ptrs[i].x = x; ptrs[i].y = y; ptrs[i].w = w; ptrs[i].h = h; sprintf(temp, "Window Number %d", i + 1); strcpy(ptrs[i].label, temp); ptrs[i].label_color = i + 1; if(i + 1 == n) ptrs[i].next = panels[0]; else ptrs[i].next = panels[i + 1]; set_panel_userptr(panels[i], &ptrs[i]); } } /* Show the window with a border and a label */ void win_show(WINDOW *win, char *label, int label_color) { int startx, starty, height, width; getbegyx(win, starty, startx); getmaxyx(win, height, width); box(win, 0, 0); mvwaddch(win, 2, 0, ACS_LTEE); mvwhline(win, 2, 1, ACS_HLINE, width - 2); mvwaddch(win, 2, width - 1, ACS_RTEE); print_in_middle(win, 1, 0, width, label, COLOR_PAIR(label_color)); } void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color) { int length, x, y; float temp; if(win == NULL) win = stdscr; getyx(win, y, x); if(startx != 0) x = startx; if(starty != 0) y = starty; if(width == 0) width = 80; length = strlen(string); temp = (width - length)/ 2; x = startx + (int)temp; wattron(win, color); mvwprintw(win, y, x, "%s", string); wattroff(win, color); refresh(); }
main関数のwhileループを集中して見てみよう。
一度キーが押されたことを検知したら、アクションを開始する。
もしrキーが押されていたらリサイズモードを開始する。
その後、ユーザが矢印キーを押したら、新しいサイズに更新する。
ユーザがENTERキーを押したら、サイズの選択を終え、パネルは上記で説明した方法を用いてリサイズする。
リサイズモード中、プログラムはリサイズされたウィンドウを表示していない。リサイズされた新しい位置は、枠線からユーザが想像することにしている。
mキーを押した時は、移動モードが開始される。これはリサイズと比較してものすごく単純だ。
矢印キーで新しい位置を決め、ENTERキーを押した時、move_panel()関数を呼び出して移動している。
このプログラムでは、PANEL_DATAを表現するために使っているユーザポインタは、パネルの情報を見付けるという重要な役割を担っている。
コメントに有る通り、PANEL_DATAにはパネルのサイズ、ラベル、ラベルの色、次に一番上に持って来るパネルへのポインタを持っている。
パネルは hide_panel()関数を用いて非表示にすることができる。
この関数はパネルのスタックからパネルを取り除くにすぎないが、このあと update_panels()関数と doupdate()関数を使うだけで画面上からパネルを隠すことができる。
なお、パネルを隠すことは、パネル構造体を破壊しない。再度表示したい場合は、show_panel()関数を使うことでできる。
以下のプログラムにパネルを隠す例を示す。
a,b,cキーを押すと、対応するウィンドウの表示/非表示を行う。
ユーザポインタh、ウィンドウが隠されているかどうかのためだけに用いている。
何らかの理由から、パネルが隠されているかどうかを取得することができるpanel_hidden()関数は機能していない。
このバグレポートはMichael Andresさんまで提供願いたい。
Example 17. Panel Hiding and Showing example #include <panel.h> typedef struct _PANEL_DATA { int hide; /* TRUE if panel is hidden */ }PANEL_DATA; #define NLINES 10 #define NCOLS 40 void init_wins(WINDOW **wins, int n); void win_show(WINDOW *win, char *label, int label_color); void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color); int main() { WINDOW *my_wins[3]; PANEL *my_panels[3]; PANEL_DATA panel_datas[3]; PANEL_DATA *temp; 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); init_wins(my_wins, 3); /* Attach a panel to each window */ /* Order is bottom up */ my_panels[0] = new_panel(my_wins[0]); /* Push 0, order: stdscr-0 */ my_panels[1] = new_panel(my_wins[1]); /* Push 1, order: stdscr-0-1 */ my_panels[2] = new_panel(my_wins[2]); /* Push 2, order: stdscr-0-1-2 */ /* Initialize panel datas saying that nothing is hidden */ panel_datas[0].hide = FALSE; panel_datas[1].hide = FALSE; panel_datas[2].hide = FALSE; set_panel_userptr(my_panels[0], &panel_datas[0]); set_panel_userptr(my_panels[1], &panel_datas[1]); set_panel_userptr(my_panels[2], &panel_datas[2]); /* Update the stacking order. 2nd panel will be on top */ update_panels(); /* Show it on the screen */ attron(COLOR_PAIR(4)); mvprintw(LINES - 3, 0, "Show or Hide a window with 'a'(first window) 'b'(Second Window) 'c'(Third Window)"); mvprintw(LINES - 2, 0, "F1 to Exit"); attroff(COLOR_PAIR(4)); doupdate(); while((ch = getch()) != KEY_F(1)) { switch(ch) { case 'a': temp = (PANEL_DATA *)panel_userptr(my_panels[0]); if(temp->hide == FALSE) { hide_panel(my_panels[0]); temp->hide = TRUE; } else { show_panel(my_panels[0]); temp->hide = FALSE; } break; case 'b': temp = (PANEL_DATA *)panel_userptr(my_panels[1]); if(temp->hide == FALSE) { hide_panel(my_panels[1]); temp->hide = TRUE; } else { show_panel(my_panels[1]); temp->hide = FALSE; } break; case 'c': temp = (PANEL_DATA *)panel_userptr(my_panels[2]); if(temp->hide == FALSE) { hide_panel(my_panels[2]); temp->hide = TRUE; } else { show_panel(my_panels[2]); temp->hide = FALSE; } break; } update_panels(); doupdate(); } endwin(); return 0; } /* Put all the windows */ void init_wins(WINDOW **wins, int n) { int x, y, i; char label[80]; y = 2; x = 10; for(i = 0; i < n; ++i) { wins[i] = newwin(NLINES, NCOLS, y, x); sprintf(label, "Window Number %d", i + 1); win_show(wins[i], label, i + 1); y += 3; x += 7; } } /* Show the window with a border and a label */ void win_show(WINDOW *win, char *label, int label_color) { int startx, starty, height, width; getbegyx(win, starty, startx); getmaxyx(win, height, width); box(win, 0, 0); mvwaddch(win, 2, 0, ACS_LTEE); mvwhline(win, 2, 1, ACS_HLINE, width - 2); mvwaddch(win, 2, width - 1, ACS_RTEE); print_in_middle(win, 1, 0, width, label, COLOR_PAIR(label_color)); } void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color) { int length, x, y; float temp; if(win == NULL) win = stdscr; getyx(win, y, x); if(startx != 0) x = startx; if(starty != 0) y = starty; if(width == 0) width = 80; length = strlen(string); temp = (width - length)/ 2; x = startx + (int)temp; wattron(win, color); mvwprintw(win, y, x, "%s", string); wattroff(win, color); refresh(); }
panel_above()関数とpanel_below()関数は、パネルの上下を見付けるのに使うことができる。 もしこれらの関数の引数がNULLならば、これらの関数は一番上、一番下のパネルを返す。
[PageInfo]
LastUpdate: 2013-09-29 22:38:01, ModifiedBy: koinec
[License]
FreeBSD Documentation License
[Permissions]
view:all, edit:members, delete/config:members