다음 이전 차례

9. 컨테이너 widget

9.1 노트북

노트북 widget은 서로 오버랩되며 다른 정보를 가지고 있는 '페이지들'의 모임이다. 이런 widget은 최근 GUI 프로그래밍에서 더 일반화되었고, 화면에서 비슷하지만 구별되어야 할 정보 블럭을 보이는 데 좋은 방법이다.

우리가 알아야 할 첫번째 함수는 예상한대로 새로운 노트북 widget을 만드는 것이다.

GtkWidget* gtk_notebook_new (void);

일단 노트북이 만들어지면, 그 노트북 widget을 다룰 12개의 함수가 있다. 그들 하나하나를 살펴보자.

우리가 먼저 할 일은 페이지 표시자의 위치를 어떻게 잡는지 알아보는 것이다. 이런 페이지 표시자 혹은 'tab'은, 네 가지 방법으로 잡아줄 수 있다. 즉 top, bottom, left, 또는 right이다.

void gtk_notebook_set_tab_pos (GtkNotebook *notebook, GtkPositionType pos);

GtkPositionType은 다음 중 하나이며, 상당히 직관적이다.

GTK_POS_TOP이 디폴트다.

다음으로 노트북에 어떻게 페이지를 넣는지 알아보자. 이렇게 하는 것에는 세가지 방법이 있다. 우선 서로 유사한 두가지를 먼저 살펴보자.

void gtk_notebook_append_page (GtkNotebook *notebook, GtkWidget *child, GtkWidget *tab_label);

void gtk_notebook_prepend_page (GtkNotebook *notebook, GtkWidget *child, GtkWidget *tab_label);

이런 함수들은 노트북의 뒤에 삽입되던지(append), 또는 노트북의 앞에서 삽입되던지(prepend) 해서 페이지를 더해준다. *child는 노트북 페이지에 위치하는 widget이고, *tab_label은 더해질 페이지를 위한 라벨이다.

페이지를 더해주는 마지막 함수는 앞의 두가지가 가진 특성을 모두 가지고 있다. 하지만 이것은 우리가 노트북의 어느 위치에 페이지를 설정할 지를 정하도록 해준다.

void gtk_notebook_insert_page (GtkNotebook *notebook, GtkWidget *child, GtkWidget *tab_label, gint position);

position이라는 또 하나의 인자를 포함한다는 것만 빼고는, gtk_notebook_append_page 그리고 gtk_notebook_prepend_page와 인자가 같다. position 인자는 이 페이지가 어느 위치에 삽입될 것인지 지정해 준다.

이제 어떻게 페이지를 더하는지 알았으므로, 노트북에서 페이지를 어떻게 제거하는지를 알아보도록 하자.

void gtk_notebook_remove_page (GtkNotebook *notebook, gint page_num);

이 함수는 page_num 인자로 주어진 페이지를 취해서, 그것을 *notebook이라는 widget에서 제거해준다.

현재의 페이지를 알아내기 위해서는 이 함수를 이용한다.

gint gtk_notebook_current_page (GtkNotebook *notebook);

여기 소개하는 두 함수는 노트북의 페이지를 앞뒤로 이동할 때 쓰는 간단한 함수들이다. 우리가 다룰 노트북 widget을 다루기 위해서 저마다의 함수를 주기만 하면 된다. 주의할 것은, 마지막 페이지에서 gtk_notebook_next_page가 호출되면 노트북이 첫번째 페이지로 돌아가 버린다는 사실이다. 마찬가지로 첫번째 페이지에서 gtk_notebook_prev_page가 호출되면 노트북의 마지막 페이지 로 가버린다.

void gtk_notebook_next_page (GtkNoteBook *notebook);
void gtk_notebook_prev_page (GtkNoteBook *notebook);

이번 함수는 'active', 즉 활성화된 페이지를 세팅한다. 예를들어 처음부터 5번 페이지로 노트북을 열려면 이 함수를 이용하면 될 것이다. 이것을 쓰지 않으면, 디폴트로 노트북은 첫번째 페이지에서 열리게 된다.

void gtk_notebook_set_page (GtkNotebook *notebook, gint page_num);

이 두 함수는 각각 노트북의 페이지 tab과 경계를 더하거나 제거해준다.

void gtk_notebook_set_show_tabs (GtkNotebook *notebook, gint show_tabs);
void gtk_notebook_set_show_border (GtkNotebook *notebook, gint show_border);

인자 show_tabs와 show_border는 TRUE/FALSE 중 하나가 될 수 있다.

이제 예제를 하나 보자. 이것은 GTK 배포본과 함께 있는 testgtk.c 프로그램 으로, 13개의 함수를 모두 보여준다. 이 작은 프로그램은 노트북과 여섯 개의 버튼을 가지고 있는 윈도를 만든다. 노트북은 세가지 다른 방법, 즉 append/ insert/prepend를 통해 더해진 11개 페이지를 가지고 있다. 버튼들은 tab의 위치를 순환시킬 수 있고, tab과 border를 더해주거나 지울수 있으며, 한 페이지 를 지울 수도 있다. 그리고 앞뒤 양방향으로 페이지를 바꿔주며, 프로그램을 끝낼 수도 있다.

/* notebook.c */

#include <gtk/gtk.h>

/* 이 함수는 tab의 위치를 로테이션 시킨다. */
void rotate_book (GtkButton *button, GtkNotebook *notebook)
{
                gtk_notebook_set_tab_pos (notebook, (notebook->tab_pos +1) %4);
}

/* page tab과 border를 더하거나 삭제한다. */
void tabsborder_book (GtkButton *button, GtkNotebook *notebook)
{
                gint tval = FALSE;
                gint bval = FALSE;
                if (notebook->show_tabs == 0)
                                                tval = TRUE;
                if (notebook->show_border == 0)
                                                bval = TRUE;

                gtk_notebook_set_show_tabs (notebook, tval);
                gtk_notebook_set_show_border (notebook, bval);
}

/* 노트북에서 한 페이지를 제거한다. */
void remove_book (GtkButton *button, GtkNotebook *notebook)
{
                gint page;

                page = gtk_notebook_current_page(notebook);
                gtk_notebook_remove_page (notebook, page);
                /* Need to refresh the widget -
                 This forces the widget to redraw itself. */
                gtk_widget_draw(GTK_WIDGET(notebook), NULL);
}

void delete (GtkWidget *widget, GdkEvent *event, gpointer data)
{
                gtk_main_quit ();
}

int main (int argc, char *argv[])
{
                GtkWidget *window;
                GtkWidget *button;
                GtkWidget *table;
                GtkWidget *notebook;
                GtkWidget *frame;
                GtkWidget *label;
                GtkWidget *checkbutton;
                int i;
                char bufferf[32];
                char bufferl[32];

                gtk_init (&argc, &argv);

                window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

                gtk_signal_connect (GTK_OBJECT (window), "delete_event",
                                                        GTK_SIGNAL_FUNC (delete), NULL);

                gtk_container_border_width (GTK_CONTAINER (window), 10);

                table = gtk_table_new(2,6,TRUE);
                gtk_container_add (GTK_CONTAINER (window), table);

                /* 새 노트북을 만들고, tab들의 위치를 설정한다. */
                notebook = gtk_notebook_new ();
                gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP);
                gtk_table_attach_defaults(GTK_TABLE(table), notebook, 0,6,0,1);
                gtk_widget_show(notebook);

                /* 노트북에 페이지들의 묶음을 추가한다. */
                for (i=0; i < 5; i++) {
                                sprintf(bufferf, "Append Frame %d", i+1);
                                sprintf(bufferl, "Page %d", i+1);

                                frame = gtk_frame_new (bufferf);
                                gtk_container_border_width (GTK_CONTAINER (frame), 10);
                                gtk_widget_set_usize (frame, 100, 75);
                                gtk_widget_show (frame);

                                label = gtk_label_new (bufferf);
                                gtk_container_add (GTK_CONTAINER (frame), label);
                                gtk_widget_show (label);

                                label = gtk_label_new (bufferl);
                                gtk_notebook_append_page (GTK_NOTEBOOK (notebook), frame, label);
                }

                /* 지정한 곳으로 한 페이지를 더한다. */
                checkbutton = gtk_check_button_new_with_label ("Check me please!");
                gtk_widget_set_usize(checkbutton, 100, 75);
                gtk_widget_show (checkbutton);

                label = gtk_label_new ("Add spot");
                gtk_container_add (GTK_CONTAINER (checkbutton), label);
                gtk_widget_show (label);
                label = gtk_label_new ("Add page");
                gtk_notebook_insert_page (GTK_NOTEBOOK (notebook), checkbutton, label, 2);

                /* 이제 노트북의 선두에 페이지를 prepend한다. */
                for (i=0; i < 5; i++) {
                                sprintf(bufferf, "Prepend Frame %d", i+1);
                                sprintf(bufferl, "PPage %d", i+1);

                                frame = gtk_frame_new (bufferf);
                                gtk_container_border_width (GTK_CONTAINER (frame), 10);
                                gtk_widget_set_usize (frame, 100, 75);
                                gtk_widget_show (frame);

                                label = gtk_label_new (bufferf);
                                gtk_container_add (GTK_CONTAINER (frame), label);
                                gtk_widget_show (label);

                                label = gtk_label_new (bufferl);
                                gtk_notebook_prepend_page (GTK_NOTEBOOK(notebook), frame, label);
                }

                /* 시작할 페이지를 세팅한다(여기서는 page 4). */
                gtk_notebook_set_page (GTK_NOTEBOOK(notebook), 3);

                /* 버튼의 다발(bunch)을 하나 만든다. */
                button = gtk_button_new_with_label ("close");
                gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                                   GTK_SIGNAL_FUNC (destroy), NULL);
                gtk_table_attach_defaults(GTK_TABLE(table), button, 0,1,1,2);
                gtk_widget_show(button);

                button = gtk_button_new_with_label ("next page");
                gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                                   (GtkSignalFunc) gtk_notebook_next_page,
                                   GTK_OBJECT (notebook));
                gtk_table_attach_defaults(GTK_TABLE(table), button, 1,2,1,2);
                gtk_widget_show(button);

                button = gtk_button_new_with_label ("prev page");
                gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                                   (GtkSignalFunc) gtk_notebook_prev_page,
                                   GTK_OBJECT (notebook));
                gtk_table_attach_defaults(GTK_TABLE(table), button, 2,3,1,2);
                gtk_widget_show(button);

                button = gtk_button_new_with_label ("tab position");
                gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                                   (GtkSignalFunc) rotate_book, GTK_OBJECT(notebook));
                gtk_table_attach_defaults(GTK_TABLE(table), button, 3,4,1,2);
                gtk_widget_show(button);

                button = gtk_button_new_with_label ("tabs/border on/off");
                gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                                   (GtkSignalFunc) tabsborder_book,
                                   GTK_OBJECT (notebook));
                gtk_table_attach_defaults(GTK_TABLE(table), button, 4,5,1,2);
                gtk_widget_show(button);

                button = gtk_button_new_with_label ("remove page");
                gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                                   (GtkSignalFunc) remove_book,
                                   GTK_OBJECT(notebook));
                gtk_table_attach_defaults(GTK_TABLE(table), button, 5,6,1,2);
                gtk_widget_show(button);

                gtk_widget_show(table);
                gtk_widget_show(window);

                gtk_main ();

                return 0;
}

이것으로 여러분의 GTK 어플을 만들 때 노트북을 다루는 방법에 대한 도움이 되기를 바란다.

9.2 스크롤된 윈도

이것은 실제 윈도 내부에서 스크롤된 영역을 만들 때 쓰이는 것이다. 우리는 이런 스크롤된 영역에 어떤 종류의 widget이라도 삽입할 수 있고, 스크롤바를 이용하여 크기에 관계없이 접근할 수 있을 것이다.

이 함수는 새로운 스크롤 윈도를 만드는 것이다.

GtkWidget* gtk_scrolled_window_new (GtkAdjustment *hadjustment,
                                    GtkAdjustment *vadjustment);

첫번째 인자는 수평방향을, 그리고 두번째 인자는 수직방향을 조절해 준다. 이것들은 대개 NULL로 세팅된다.

void gtk_scrolled_window_set_policy (GtkScrolledWindow *scrolled_window,
                                     GtkPolicyType hscrollbar_policy,
                                     GtkPolicyType vscrollbar_policy);

이것은 스크롤바들을 쓰는 방식을 설정한다. 첫번째 인자는 우리가 변화 시키고자 하는 스크롤 윈도다. 두번째와 세번째 인자들은 각각 수평 스크롤바와 수직 스크롤바의 방식을 세팅한다.

이 방식이란 것은 GTK_POLICY_AUTOMATIC 혹은 GTK_POLICY_ALWAYS 가 된다. GTK_POLICY_AUTOMATIC은 스크롤바가 필요할 때를 자동적으로 결정하고, 반면 GTK_POLICY_ALWAYS는 언제나 스크롤바를 만들어 둔다.

이 간단한 예제는 스크롤된 윈도에 100개의 토글 버튼을 패킹한다. 여기서의 주석은 여러분에게 생소할 부분에만 붙여질 것이다.

/* scrolledwin.c */

#include <gtk/gtk.h>

void destroy(GtkWidget *widget, gpointer data)
{
                gtk_main_quit();
}

int main (int argc, char *argv[])
{
                static GtkWidget *window;
                GtkWidget *scrolled_window;
                GtkWidget *table;
                GtkWidget *button;
                char buffer[32];
                int i, j;

                gtk_init (&argc, &argv);

                /* 스크롤된 윈도가 패킹되어 들어갈 dialog 윈도를 만든다. 
                 * Dialog 윈도란 vbox와 그것에 패킹되어 들어갈 수평 seperator를 가지고
                 * 있다는 것만 빼고는 보통의 윈도와 동일한 것이다.  이것은 dialog를
                 * 만들게 되는 일종의 shortcut이다. */
                window = gtk_dialog_new ();
                gtk_signal_connect (GTK_OBJECT (window), "destroy",
                                                        (GtkSignalFunc) destroy, NULL);
                gtk_window_set_title (GTK_WINDOW (window), "dialog");
                gtk_container_border_width (GTK_CONTAINER (window), 0);

                /* 스크롤된(scrolled) 윈도를 하나 만든다. */
                scrolled_window = gtk_scrolled_window_new (NULL, NULL);

                gtk_container_border_width (GTK_CONTAINER (scrolled_window), 10);

                /* 여기서의 방식은 GTK_POLICY_AUTOMATIC 또는 GTK_POLICY_ALWAYS 중
                 * 하나이다.  GTK_POLICY_AUTOMATIC은 스크롤바가 필요한지를 자동적으로
                 * 결정하고, 반면 GTK_POLICY_ALWAYS는 언제나 스크롤바를 가지게 한다.
                 * 첫번째는 수평방향의 스크롤바, 그리고 두번째는 수직방향의 것이다. */

                gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
                                       GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
                /* vbox가 패킹되어 들어간 dialog 윈도를 하나 만든다. */
                gtk_box_pack_start (GTK_BOX (GTK_DIALOG(window)->vbox), scrolled_window,
                                                                                                TRUE, TRUE, 0);
                gtk_widget_show (scrolled_window);

                /* 10행 10열의 테이블을 만든다. */
                table = gtk_table_new (10, 10, FALSE);

                /* x와 y에 대해 10 만큼의 spacing을 세팅한다. */
                gtk_table_set_row_spacings (GTK_TABLE (table), 10);
                gtk_table_set_col_spacings (GTK_TABLE (table), 10);

                /* 스크롤된 윈도에 테이블을 패킹한다. */
                gtk_container_add (GTK_CONTAINER (scrolled_window), table);
                gtk_widget_show (table);

                /* 이것은 단순히 스크롤된 윈도를 보여주기 위해 테이블 위에
                 * 토글버튼들의 격자(grid)를 하나 만든다. */
                for (i = 0; i < 10; i++)
                                                for (j = 0; j < 10; j++) {
                                                                sprintf (buffer, "button (%d,%d)\n", i, j);
                                                                button = gtk_toggle_button_new_with_label (buffer);
                                                                gtk_table_attach_defaults (GTK_TABLE (table), button,
                                                                                                         i, i+1, j, j+1);
                                                                gtk_widget_show (button);
                                                }
                /* Dialog의 맨 아래쪽에 "close" 버튼을 추가한다. */
                button = gtk_button_new_with_label ("close");
                gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                                                                 (GtkSignalFunc) gtk_widget_destroy,
                                                                 GTK_OBJECT (window));

                /* 이것을 디폴트 버튼으로 한다. */

                GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
                gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->action_area), button, TRUE, TRUE, 0);

                /* 이 버튼이 디폴트 버튼으로 되도록 잡아챈다.  간단히 "Enter"를 치면
                 * 이 버튼을 활성화 시킬 것이다. */
                gtk_widget_grab_default (button);
                gtk_widget_show (button);

                gtk_widget_show (window);

                gtk_main();

                return(0);
}

윈도의 크기를 변하게 해보라. 스크롤바가 어떻게 반응하는지 알 수 있을 것이다. 우리는 또한 윈도나 다른 widget의 디폴트 크기를 세팅하기 위해 gtk_widget_set_usize() 함수를 이용할 수도 있을 것이다.

9.3 Paned 윈도 widget

이 paned 윈도 widget은 한 영역을 사용자에 의해서 그 상대적인 크기를 마음대로 조절할 수 있는 두 영역으로 나누어 쓰고 싶을 때 사용한다. 두 영역 사이에는 handle이 달린 홈이 있고 이를 마우스로 드래그해서 사용자는 원하는 대로 두 영역의 비율을 바꿀 수 있다. 이러한 분할은 수평(HPaned)적이거나 수직(VPaned)적이 된다.

새 paned 윈도를 만들려면 다음중 하나를 부른다.

GtkWidget* gtk_hpaned_new (void)
GtkWidget* gtk_vpaned_new (void)

Paned 윈도 widget을 만든 다음에는 나누어진 양쪽에 자식 widget을 주어야 한다. 이는 다음 함수들을 이용해서 이루어진다.

void gtk_paned_add1 (GtkPaned *paned, GtkWidget *child)
void gtk_paned_add2 (GtkPaned *paned, GtkWidget *child)

gtk_paned_add1()는 paned 윈도 widget의 왼쪽 혹은 윗쪽에 자식 widget을 더한다. gtk_paned_add2()는 반대로 오른쪽 혹은 아랫쪽에 더한다.

한 에로 가상적인 email 프로그램의 사용자 인터페이스 일부를 만들어 보기로 한다. 윈도는 email 메세지들의 리스트를 표시하는 윗 부분과 메세지 자체를 표시하는 아랫 부분으로 나누어진다. 프로그래의 대부분은 무척 간단하다. 두가지 주의해야 할 점이 있다. 텍스트는 텍스트 widget에 이것이 realize되기 전에는 더해질 수 없다. 이는 gtk_widget_realize()를 호출해서 이루어지지만 또 다른 방법으로 text를 더하기 위해 "realize" 시그널을 연결할 수도 있다. 또한, GTK_SHRINK 옵션을 text 윈도와 스크롤바를 포함하고 있는 테이블 내용 일부에 더해주는 것이 필요하다. 그래야만 아랫 부분을 작게 만들때 윈도의 바닥이 밀려나는 대신 원하던 부분이 줄어들게 된다.

/* paned.c */

#include <gtk/gtk.h>
   
/* "messages"의 리스트를 만든다 */
GtkWidget *
create_list (void)
{

    GtkWidget *scrolled_window;
    GtkWidget *list;
    GtkWidget *list_item;
   
    int i;
    char buffer[16];
   
    /* 스크롤바(필요할 때만)가 딸린 스크롤된 윈도를 만든다. */
    scrolled_window = gtk_scrolled_window_new (NULL, NULL);
    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
                                    GTK_POLICY_AUTOMATIC, 
                                    GTK_POLICY_AUTOMATIC);
   
    /* 새로운 리스트를 만들어 이를 스크롤된 윈도에 집어넣는다. */
    list = gtk_list_new ();
    gtk_container_add (GTK_CONTAINER(scrolled_window), list);
    gtk_widget_show (list);
   
    /* 윈도에 메시지 몇개를 더한다 */
    for (i=0; i<10; i++) {

        sprintf(buffer,"Message #%d",i);
        list_item = gtk_list_item_new_with_label (buffer);
        gtk_container_add (GTK_CONTAINER(list), list_item);
        gtk_widget_show (list_item);

    }
   
    return scrolled_window;
}
   
/* 텍스트 몇개를 텍스트 widget에 더한다. - 아래 함수는 우리의 윈도가 realize 될
때 불리는 callback이다. gtk_widget_realize로 realize되도록 강제할 수도 있지만
그건 먼저 계층구조의 한 부분이 되어야 할 것이다.
*/
void
realize_text (GtkWidget *text, gpointer data)
{
    gtk_text_freeze (GTK_TEXT (text));
    gtk_text_insert (GTK_TEXT (text), NULL, &text->style->black, NULL,
    "From: pathfinder@nasa.gov\n"
    "To: mom@nasa.gov\n"
    "Subject: Made it!\n"
    "\n"
    "We just got in this morning. The weather has been\n"
    "great - clear but cold, and there are lots of fun sights.\n"
    "Sojourner says hi. See you soon.\n"
    " -Path\n", -1);
   
    gtk_text_thaw (GTK_TEXT (text));
}
   
/* "message"를 보여주는 스크롤된 텍스트 영역을 만든다. */
GtkWidget *
create_text (void)
{
    GtkWidget *table;
    GtkWidget *text;
    GtkWidget *hscrollbar;
    GtkWidget *vscrollbar;
   
    /* 텍스트 위젯과 스크롤바를 갖는 테이블을 만든다 */
    table = gtk_table_new (2, 2, FALSE);
   
    /* 텍스트 위젯을 왼쪽 위에 놓는다. Y 축 방향으로 GTK_SHRINK가 쓰인 것을
     * 주목할 것. */
    text = gtk_text_new (NULL, NULL);
    gtk_table_attach (GTK_TABLE (table), text, 0, 1, 0, 1,
                      GTK_FILL | GTK_EXPAND,
                      GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0);
    gtk_widget_show (text);
   
    /* HScrollbar를 왼쪽 아래에 놓는다. */
    hscrollbar = gtk_hscrollbar_new (GTK_TEXT (text)->hadj);
    gtk_table_attach (GTK_TABLE (table), hscrollbar, 0, 1, 1, 2,
                      GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
    gtk_widget_show (hscrollbar);
   
    /* VScrollbar를 오른쪽 위에 놓는다. */
    vscrollbar = gtk_vscrollbar_new (GTK_TEXT (text)->vadj);
    gtk_table_attach (GTK_TABLE (table), vscrollbar, 1, 2, 0, 1,
                      GTK_FILL, GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 0);
    gtk_widget_show (vscrollbar);
   
    /* 텍스트 widget이 realize되었을 때 그 widget이 갖고 있는 메시지를
     * 출력하여주는 시그널 핸들러를 더한다. */
    gtk_signal_connect (GTK_OBJECT (text), "realize",
                        GTK_SIGNAL_FUNC (realize_text), NULL);
   
    return table;
}
   
int
main (int argc, char *argv[])
{
    GtkWidget *window;
    GtkWidget *vpaned;
    GtkWidget *list;
    GtkWidget *text;

    gtk_init (&argc, &argv);
   
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (window), "Paned Windows");
    gtk_signal_connect (GTK_OBJECT (window), "destroy",
                        GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
    gtk_container_border_width (GTK_CONTAINER (window), 10);
   
    /* vpaned widget을 만들어서 toplevel 윈도에 더한다. */
   
    vpaned = gtk_vpaned_new ();
    gtk_container_add (GTK_CONTAINER(window), vpaned);
    gtk_widget_show (vpaned);
   
    /* 이제 윈도 두 부분의 내용을 만든다. */
   
    list = create_list ();
    gtk_paned_add1 (GTK_PANED(vpaned), list);
    gtk_widget_show (list);
   
    text = create_text ();
    gtk_paned_add2 (GTK_PANED(vpaned), text);
    gtk_widget_show (text);
    gtk_widget_show (window);
    gtk_main ();
    return 0;
}

9.4 Aspect 프레임

Aspect 프레임은 몇가지 특징을 빼면 프레임 widget과 비슷하다. 이 widget은 자식 widget의 aspect 비율(가로와 세로 비율)이 반드시 어떤 특정한 값이 되도록 때에 따라 여분의 공간을 더해서라도 강제한다. 이것은 예를 들면 상당히 큰 이미지의 미리보기를 보는 때 같은 경우 유용하다. 이 미리보기의 크기는 사용자가 윈도의 크기를 바꿀 때마다 달라지지만 가로세로 비율은 언제나 원래 이미지와 같아야만 한다.

새로운 aspect 프레임을 만드려면,

GtkWidget* gtk_aspect_frame_new  (const gchar *label,
                                        gfloat xalign,
                                        gfloat yalign,
                                        gfloat ratio,
                                        gint obey_child)

xalignyalign은 Alignment widget에서처럼 widget의 alignment 값을 정한다. 만일 obey_child 값이 true면, 자식 widget의 가로세로 비율은 원래 만들어졌을 때의 이상적인 비율과 같게 된다. 반대로 false이면 그 비율은 ratio로 직접 주어진다.

이미 존재하고 있는 aspect 프레임의 옵션을 바꾸려면 다음 함수를 이용한다.

void gtk_aspect_frame_set (GtkAspectFrame *aspect_frame,
                           gfloat xalign,
                           gfloat yalign,
                           gfloat ratio,
                           gint obey_child)

예제인 다음 프로그램은 사용자가 toplevel 윈도의 크기를 어떻게 바꾸든 언제나 가로세로 비율이 2:1인 drawing area를 표현하는데 AspectFrame을 쓰고 있다.

/* aspectframe.c */

#include <gtk/gtk.h>
   
int
main (int argc, char *argv[])
{
    GtkWidget *window;
    GtkWidget *aspect_frame;
    GtkWidget *drawing_area;
    gtk_init (&argc, &argv);
   
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (window), "Aspect Frame");
    gtk_signal_connect (GTK_OBJECT (window), "destroy",
    GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
    gtk_container_border_width (GTK_CONTAINER (window), 10);
   
    /* aspect_frame을 만들어 toplevel 윈도에 더한다 */
   
    aspect_frame = gtk_aspect_frame_new ("2x1", /* 레이블 */
                                         0.5, /* center x */
                                         0.5, /* center y */
                                         2, /* xsize/ysize = 2 */
                                         FALSE /* 자식의 가로세로 비율 무시*/);
   
    gtk_container_add (GTK_CONTAINER(window), aspect_frame);
    gtk_widget_show (aspect_frame);
   
    /* aspect frame에 더할 자식 widget을 만든다*/
   
    drawing_area = gtk_drawing_area_new ();
   
    /* 200x200 윈도를 요청했지만 AspectFrame은 2:1 비율을 강제하기 때문에
     * 200x100 윈도를 준다. */
    gtk_widget_set_usize (drawing_area, 200, 200);
    gtk_container_add (GTK_CONTAINER(aspect_frame), drawing_area);
    gtk_widget_show (drawing_area);
   
    gtk_widget_show (window);
    gtk_main ();
    return 0;
}  


다음 이전 차례