8. Menu i toolbary
8.1. Menu programu
Ten rozdział opisuje tworzenie menu programu oraz paska narzędziowego.
Menu jest wykorzystywane w wielu programach. Wykorzystuje się je, aby ułatwić dostęp do niektórych funkcji lub nie zaśmiecać głównego okna programu. W każdym razie menu jest to rzecz bardzo przydatna i warto wiedzieć, jak je utworzyć. Pasek menu tworzy się funkcją:
GtkWidget* gtk_menu_bar_new (void);
Jednak co nam z takiego paska, jak nic na nim nie ma. Menu do naszego paska można utworzyć funkcją:
GtkWidget* gtk_menu_new (void);
Wciąż jednak z wykorzystaniem tych dwóch funkcji nie stworzymy funkcjonalnego menu. Potrzebna jest jeszcze jedna z poniższych funkcji:
GtkWidget* gtk_menu_item_new_with_label (const gchar *label); GtkWidget* gtk_menu_item_new_with_mnemonic (const gchar *label);
Czym one się różnią? Obie tworzą element menu z etykietą podaną w argumencie. Druga z nich pozwala jednak dodać mnemonikę, czyli literę, która pozwala na szybki dostęp do poszczególnych elementów menu z wykorzystaniem klawiatury. Dla przykładu – otwórz jakąś aplikację wykorzystującą paski menu, np. menedżer plików (Eksplorator Windows, Nautilus, Dolphin…) i wciśnij lewy Alt. Mnemoniki w menu programu powinny być teraz podkreślone. Dla menu „Plik” zazwyczaj jest to litera P. Wciśnij teraz tę literę trzymając wciąż Alt. Menu powinno się otworzyć. I po to właśnie są mnemoniki.
Warto również zaznaczyć, że mnemoniki można wykorzystać nie tylko przy tworzeniu menu. Wiele z omówionych wcześniej kontrolek, jak np. przyciski obsługuje mnemoniki. Zazwyczaj aby utworzyć jakąś kontrolkę z etykietą i z mnemoniką, wystarczy zmienić with_label na with_mnemonic w wywołaniu funkcji tworzącej daną kontrolkę.
No tak – mamy już utworzony pasek menu, mamy utworzone menu oraz jego elementy, czego więc brakuje? No tak – połączenia tych elementów w jedną całość. Od tego bowiem jest funkcja:
void gtk_menu_shell_append (GtkMenuShell *menu_shell, GtkWidget *child);
Czym jednak w tej funkcji jest GtkMenuShell? Otóż widżety GtkMenuBar i GtkMenu dziedziczą właśnie GtkMenuShell – tak więc tej funkcji należy przekazać referencję do paska menu lub do samego menu.
Ostatnią funkcją, jaką należy wykorzystać, aby stworzyć działające menu programu jest:
void gtk_menu_item_set_submenu (GtkMenuItem *menu_item, GtkWidget *submenu);
Dodaje ona element do menu.
Warto również wiedzieć, czy użytkownik kliknął w jakiś element menu. Można użyć do tego zdarzenia, które nazywa się activate.
Aby zrozumieć, jak utworzyć menu programu, można przeanalizować poniższy kod źródłowy.
#include <gtk/gtk.h> int main( int argc, char *argv[]) { GtkWidget *okno; GtkWidget *vbox; GtkWidget *pasek; GtkWidget *menu; GtkWidget *plik; GtkWidget *wyjdz; GtkWidget *etykieta1; gtk_init(&argc, &argv); okno = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_position(GTK_WINDOW(okno), GTK_WIN_POS_CENTER); gtk_window_set_default_size(GTK_WINDOW(okno), 250, 200); gtk_window_set_title(GTK_WINDOW(okno), "Kurs GTK+"); vbox = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(okno), vbox); etykieta1 = gtk_label_new("Lorem ipsum dolor sit amet, consectetur adipiscing elit. \ Maecenas sit amet magna in mi tincidunt iaculis sit amet quis augue. Curabitur libero est, \ vehicula vel consequat a, cursus sit amet risus. Nulla id eros arcu, sit amet dictum eros. \ Cras mollis, leo et dignissim bibendum, purus sapien interdum enim, ut."); gtk_label_set_line_wrap(GTK_LABEL(etykieta1), TRUE); pasek = gtk_menu_bar_new(); menu = gtk_menu_new(); plik = gtk_menu_item_new_with_mnemonic("_Plik"); wyjdz = gtk_menu_item_new_with_mnemonic("_Wyjdź"); gtk_menu_item_set_submenu(GTK_MENU_ITEM(plik), menu); gtk_menu_shell_append(GTK_MENU_SHELL(menu), wyjdz); gtk_menu_shell_append(GTK_MENU_SHELL(pasek), plik); gtk_box_pack_start(GTK_BOX(vbox), pasek, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(vbox), etykieta1, FALSE, FALSE, 3); g_signal_connect (G_OBJECT(okno), "destroy", G_CALLBACK(gtk_main_quit), NULL); g_signal_connect (G_OBJECT(wyjdz), "activate", G_CALLBACK(gtk_main_quit), NULL); gtk_widget_show_all(okno); gtk_main(); return 0; }
Działanie powyższego kodu jest następujące:
8.2. Stock items
Stock items to zbiór predefiniowanych etykiet, często połączonych z ikonami oraz czasami akceleratorami (o akceleratorach za chwilę), które można wykorzystać, by w łatwy sposób dodać do programu ikony, nie zwiększając jednocześnie jego wielkości lub etykiety automatycznie dostosowujące się do języka systemu użytkownika. Listę stock items można znaleźć na stronie dokumentacji GTK+.
Aby utworzyć nowy element menu korzystając ze stocku, należy posłużyć się funkcją:
GtkWidget* gtk_image_menu_item_new_from_stock (const gchar *stock_id, GtkAccelGroup *accel_group);
W pierwszym argumencie należy przekazać identyfikator stock itemu, a w drugim grupę akceleratorów. Tymczasowo można w drugim argumencie przekazać NULL, gdyż o akceleratorach – jak już pisałem – będzie za chwilę, natomiast w identyfikator przekazywany w pierwszym argumencie można zdobyć korzystając z powyższego linku prowadzącego do dokumentacji GTK+.
Linijkę:
wyjdz = gtk_menu_item_new_with_mnemonic("_Wyjdź");
Możesz więc już zastąpić z:
wyjdz = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL);
GTK+ posiada własne ustawienia wspólne dla wszystkich aplikacji napisanych z jego wykorzystaniem. Może zdarzyć się tak, że w ustawieniach tych będzie wyłączone pokazywanie obrazków w menu. Wtedy należy to wymusić funkcją:
void gtk_image_menu_item_set_always_show_image (GtkImageMenuItem *image_menu_item, gboolean always_show);
W pierwszym argumencie należy przekazać referencję do widżetu typu GtkImageMenuItem (nasz element menu utworzony ze stocku się do nich zalicza), a w drugim TRUE, jeżeli chcesz, aby ikonka była zawsze pokazywana oraz FALSE w przeciwnym wypadku. Po wywołaniu funkcji gtk_image_menu_item_new_from_stock() możesz więc dopisać:
gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM(wyjdz), true);
Dzięki temu uzyskasz coś takiego:
8.3. Akceleratory
Akceleratory to skróty klawiszowe, pokazywane obok elementów menu. Aby z nich korzystać, należy utworzyć grupę akceleratorów. Robi się to funkcją:
GtkAccelGroup* gtk_accel_group_new (void);
Która nie przyjmuje żadnych argumentów, zwraca natomiast wskaźnik do obiektu typu GtkAccelGroup. Aby połączyć widżet z akceleratorem, należy wykorzystać funkcję:
void gtk_widget_add_accelerator (GtkWidget *widget, const gchar *accel_signal, GtkAccelGroup *accel_group, guint accel_key, GdkModifierType accel_mods, GtkAccelFlags accel_flags);
W pierwszym argumencie należy podać referencję do widżatu, do którego ma zostać podłączony akcelerator, w drugim nazwę zdarzenia, które ma zostać wysłane przy wywołaniu akceleratora, w następnym wskaźnik do grupy akceleratorów, w następnym wymagany klawisz, w kolejnym modyfikator (czyli dodatkowy wymagany klawisz, taki jak Ctrl, Alt, czy Shift – ich lista dostępna jest w dokumentacji GDK), a w ostatnim – flagi, których lista również jest dostępna na stronie dokumentacji GTK+. Do większości zastosowań wystarcza tylko GTK_ACCEL_VISIBLE – ustawia on widoczność akceleratora.
Aby akceleratory działały poprawnie, potrzebna jest jeszcze jedna funkcja. Jest nią:
void gtk_window_add_accel_group (GtkWindow *window, GtkAccelGroup *accel_group);
Łączy ona grupę akceleratorów z oknem.
Aby program po dodaniu obsługi akceleratorów się skompilował, należy jeszcze dołączyć plik gdk/gdkkeysyms.h.
Poniższy kod źródłowy ma za zadanie utworzyć okno, menu, oraz element z etykietą „Zakończ” pobraną ze stocku, oraz ze skrótem klawiaturowym Ctrl+Q.
#include <gtk/gtk.h> #include <gdk/gdkkeysyms.h> int main( int argc, char *argv[]) { GtkWidget *okno; GtkWidget *vbox; GtkWidget *pasek; GtkWidget *menu; GtkWidget *plik; GtkWidget *wyjdz; GtkWidget *etykieta1; GtkAccelGroup *grupa_akcel; gtk_init(&argc, &argv); okno = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_position(GTK_WINDOW(okno), GTK_WIN_POS_CENTER); gtk_window_set_default_size(GTK_WINDOW(okno), 250, 200); gtk_window_set_title(GTK_WINDOW(okno), "Kurs GTK+"); vbox = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(okno), vbox); etykieta1 = gtk_label_new("Lorem ipsum dolor sit amet, consectetur adipiscing elit. \ Maecenas sit amet magna in mi tincidunt iaculis sit amet quis augue. Curabitur libero est, \ vehicula vel consequat a, cursus sit amet risus. Nulla id eros arcu, sit amet dictum eros. \ Cras mollis, leo et dignissim bibendum, purus sapien interdum enim, ut."); gtk_label_set_line_wrap(GTK_LABEL(etykieta1), TRUE); pasek = gtk_menu_bar_new(); menu = gtk_menu_new(); grupa_akcel = gtk_accel_group_new(); gtk_window_add_accel_group(GTK_WINDOW(okno), grupa_akcel); plik = gtk_menu_item_new_with_mnemonic("_Plik"); wyjdz = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL); gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM(wyjdz), true); gtk_widget_add_accelerator(wyjdz, "activate", grupa_akcel, GDK_q, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE); gtk_menu_item_set_submenu(GTK_MENU_ITEM(plik), menu); gtk_menu_shell_append(GTK_MENU_SHELL(menu), wyjdz); gtk_menu_shell_append(GTK_MENU_SHELL(pasek), plik); gtk_box_pack_start(GTK_BOX(vbox), pasek, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(vbox), etykieta1, FALSE, FALSE, 3); g_signal_connect (G_OBJECT(okno), "destroy", G_CALLBACK(gtk_main_quit), NULL); g_signal_connect (G_OBJECT(wyjdz), "activate", G_CALLBACK(gtk_main_quit), NULL); gtk_widget_show_all(okno); gtk_main(); return 0; }
Po skompilowaniu i uruchomieniu tak powstałego programu ujrzysz:
Po wciśnięciu kombinacji Ctrl+Q program powinien zakończyć działanie.
8.4. Separatory
Separatory przydają się, aby oddzielić kilka grup elementów menu. Tworzy się je funkcją:
GtkWidget* gtk_separator_menu_item_new (void);
Przyłącza się go do menu identycznie, jak w przypadku każdego innego elementu.
Poniższy kod źródłowy tworzy menu z elementami „Otwórz” i „Zakończ” oraz separatorem pomiędzy nimi:
#include <gtk/gtk.h> #include <gdk/gdkkeysyms.h> int main( int argc, char *argv[]) { GtkWidget *okno; GtkWidget *vbox; GtkWidget *pasek; GtkWidget *menu; GtkWidget *plik; GtkWidget *otworz; GtkWidget *separator; GtkWidget *wyjdz; GtkWidget *etykieta1; GtkAccelGroup *grupa_akcel; gtk_init(&argc, &argv); okno = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_position(GTK_WINDOW(okno), GTK_WIN_POS_CENTER); gtk_window_set_default_size(GTK_WINDOW(okno), 250, 200); gtk_window_set_title(GTK_WINDOW(okno), "Kurs GTK+"); vbox = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(okno), vbox); etykieta1 = gtk_label_new("Lorem ipsum dolor sit amet, consectetur adipiscing elit. \ Maecenas sit amet magna in mi tincidunt iaculis sit amet quis augue. Curabitur libero est, \ vehicula vel consequat a, cursus sit amet risus. Nulla id eros arcu, sit amet dictum eros. \ Cras mollis, leo et dignissim bibendum, purus sapien interdum enim, ut."); gtk_label_set_line_wrap(GTK_LABEL(etykieta1), TRUE); pasek = gtk_menu_bar_new(); menu = gtk_menu_new(); grupa_akcel = gtk_accel_group_new(); gtk_window_add_accel_group(GTK_WINDOW(okno), grupa_akcel); plik = gtk_menu_item_new_with_mnemonic("_Plik"); otworz = gtk_image_menu_item_new_from_stock(GTK_STOCK_OPEN, NULL); separator = gtk_separator_menu_item_new(); wyjdz = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL); gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM(otworz), true); gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM(wyjdz), true); gtk_widget_add_accelerator(otworz, "activate", grupa_akcel, GDK_o, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE); gtk_widget_add_accelerator(wyjdz, "activate", grupa_akcel, GDK_q, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE); gtk_menu_item_set_submenu(GTK_MENU_ITEM(plik), menu); gtk_menu_shell_append(GTK_MENU_SHELL(menu), otworz); gtk_menu_shell_append(GTK_MENU_SHELL(menu), separator); gtk_menu_shell_append(GTK_MENU_SHELL(menu), wyjdz); gtk_menu_shell_append(GTK_MENU_SHELL(pasek), plik); gtk_box_pack_start(GTK_BOX(vbox), pasek, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(vbox), etykieta1, FALSE, FALSE, 3); g_signal_connect (G_OBJECT(okno), "destroy", G_CALLBACK(gtk_main_quit), NULL); g_signal_connect (G_OBJECT(wyjdz), "activate", G_CALLBACK(gtk_main_quit), NULL); gtk_widget_show_all(okno); gtk_main(); return 0; }
Jego działanie jest następujące:
8.5. Paski narzędziowe
Pasków narzędziowych się używa, aby przyspieszyć pracę z programem, dając użytkownikowi natychmiastowy dostęp do funkcji takich jak np. otwieranie, zapisywanie, drukowanie, czy chociażby cofanie oraz ponawianie. Pasek narzędziowy tworzy się funkcją:
GtkWidget* gtk_toolbar_new (void);
Warto ustawić mu styl. W tym celu należy posłużyć się funkcją:
void gtk_toolbar_set_style (GtkToolbar *toolbar, GtkToolbarStyle style);
W pierwszym argumencie przyjmuje referencję do paska narzędziowego, a w drugim styl, których listę można znaleźć w dokumentacji GTK+.
Aby więc teraz utworzyć przycisk do naszego paska, należy wywołać:
GtkToolItem* gtk_tool_button_new_from_stock (const gchar *stock_id);
Funkcja ta w argumencie przyjmuje identyfikator obiektu ze stocku.
Aby dodać przycisk do paska, należy wywołać funkcję:
void gtk_toolbar_insert (GtkToolbar *toolbar, GtkToolItem *item, gint pos);
W pierwszym argumencie przyjmuje ona referencję do paska narzędziowego, w drugim referencję do elementu, który ma zostać dodany, a w ostatnim żądaną pozycję. Aby umieścić element na końcu, można przekazać w ostatnim argumencie liczbę -1.
Poniższy kod źródłowy może pomóc zrozumieć, jak utworzyć pasek narzędziowy.
#include <gtk/gtk.h> int main( int argc, char *argv[]) { GtkWidget *okno; GtkWidget *vbox; GtkWidget *pasek; GtkToolItem *nowy; GtkToolItem *otworz; GtkToolItem *zapisz; GtkToolItem *separator; GtkToolItem *wyjdz; gtk_init(&argc, &argv); okno = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_position(GTK_WINDOW(okno), GTK_WIN_POS_CENTER); gtk_window_set_default_size(GTK_WINDOW(okno), 250, 200); gtk_window_set_title(GTK_WINDOW(okno), "Kurs GTK+"); vbox = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(okno), vbox); pasek = gtk_toolbar_new(); gtk_toolbar_set_style(GTK_TOOLBAR(pasek), GTK_TOOLBAR_ICONS); gtk_container_set_border_width(GTK_CONTAINER(pasek), 2); nowy = gtk_tool_button_new_from_stock(GTK_STOCK_NEW); gtk_toolbar_insert(GTK_TOOLBAR(pasek), nowy, -1); otworz = gtk_tool_button_new_from_stock(GTK_STOCK_OPEN); gtk_toolbar_insert(GTK_TOOLBAR(pasek), otworz, -1); zapisz = gtk_tool_button_new_from_stock(GTK_STOCK_SAVE); gtk_toolbar_insert(GTK_TOOLBAR(pasek), zapisz, -1); separator = gtk_separator_tool_item_new(); gtk_toolbar_insert(GTK_TOOLBAR(pasek), separator, -1); wyjdz = gtk_tool_button_new_from_stock(GTK_STOCK_QUIT); gtk_toolbar_insert(GTK_TOOLBAR(pasek), wyjdz, -1); gtk_box_pack_start(GTK_BOX(vbox), pasek, FALSE, FALSE, 5); g_signal_connect(G_OBJECT(wyjdz), "clicked", G_CALLBACK(gtk_main_quit), NULL); g_signal_connect(G_OBJECT(okno), "destroy", G_CALLBACK(gtk_main_quit), NULL); gtk_widget_show_all(okno); gtk_main(); return 0; }
Po skompilowaniu powyższego kodu i uruchomieniu tak powstałego programu, powinieneś zobaczyć:
To koniec tego rozdziału kursu. W następnym postaram przybliżyć nieco dialogi.