# Vývěska (C)

Jednoduchý program používající WebKitGTK+ a DOM.
Shaun McCance
shaunm@gnome.org 2010
Marta Maria Casetti
mmcasetti@gmail.com 2013

# Vývěska

      V této lekci se seznámíme s následujícími věcmi:

        Jak zobrazit webovou stránku pomocí jádra WebKit.
        Jak pracovat s obsahem webové stránky pomocí funkcí DOM jádra WebKit.

      Tato lekce předpokládá, že máte znalosti programování v jazyce C a základní povědomí o GTK+, včetně toho, jak vytvořit a umístit widgety a jak připojit funkce zpětného volání k signálům. Viz  ohledně výuky základů GTK+.

# Vytvoření projektu ve studiu Anjuta

Součástí platformy GNOME je WebKitGTK+ vycházející z mocné základní konstrukce WebKit pro HTML. WebKit je používán napříč celým GNOME, ne jen pro zobrazení webových stránek z Internetu, ale také k vytváření vizuálně bohatého uživatelského rozhraní, které lze jednoduše stylovat pomocí CSS.

V této lekci vytvoříme jednoduchou vývěsku pomocí knihovny WebKit. Vývěska bude umožňovat vkládat text a přidávat jej do seznamu zpráv v HTML. Než začneme, potřebujeme vytvořit projekt v IDE Anjuta.

Ve studiu Anjuta klikněte na Soubor Nový Projekt, aby se otevřel průvodce novým projektem.

Vyberte GTK+ (jednoduchý) na kartě C a klikněte na Pokračovat.

Na stránce Základní informace vyplňte své údaje. Jako název projektu použijte message-board. Klikněte na Pokračovat.

Vypněte volbu Použít GtkBuilder k tvorbě uživatelského rozhraní, protože jej chceme v této lekci vytvořit ručně.

Potřebujete vývojářskému studiu Anjuta říct, že ve svém projektu používáte WebKitGTK+. Na stránce Volby projektu vyberte Konfigurovat externí balíčky. Klikněte na Pokračovat a na stránce Konfigurace externích balíčků zaškrtněte webkitgtk-3.0.

Po té, co projdete průvodcem nového projektu, otevřete src/main.c na kartě Projekt nebo Soubor. Anjuta jej vyplní nějakým základním kódem GTK+ ze šablony. Protože vytváříte projekt s knihovnou WebKit, potřebujete nejdříve vložit příslušné hlavičkové soubory. Za řádek, který vkládá `gtk/gtk.h` přidejte následující řádek:

#include <webkit/webkit.h>

Ověřte, že vše, co máte doposud hotovo, funguje. Klikněte na Sestavit Sestavit projekt nebo jen zmáčkněte Shift F7. Při prvním sestavení budete dotázáni na některé volby konfigurace. Stačí přijmout výchozí hodnoty a kliknout na Spustit.

Nyní byste měli být schopni program spustit. Klikněte na Spustit Spustit nebo jen zmáčkněte F3. Měli byste spatřit prázdné okno.

# Rozvržení vašeho okna a webového zobrazení

Nyní, když můžete zobrazit okno, je načase začít pracovat s knihovnou WebKit. V této lekci vytvoříte textové vstupní pole a webové zobrazení a obojí zabalíte do okna. Najděte funkci `create_window` a nahraďte ji následujícím:

static GtkWidget* create_window (void) { GtkWidget *window, *box, *scroll, *view, *entry;

window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_default_size (GTK_WINDOW (window), 400, 400); gtk_window_set_title (GTK_WINDOW (window), "Message Board"); g_signal_connect (window, "delete-event", G_CALLBACK (gtk_main_quit), NULL);

box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); gtk_container_set_border_width (GTK_CONTAINER (box), 6); gtk_container_add (GTK_CONTAINER (window), box);

entry = gtk_entry_new (); gtk_box_pack_start (GTK_BOX (box), entry, FALSE, FALSE, 0);

scroll = gtk_scrolled_window_new (NULL, NULL); g_object_set (scroll, "shadow-type", GTK_SHADOW_IN, NULL); gtk_box_pack_start (GTK_BOX (box), scroll, TRUE, TRUE, 0);

view = webkit_web_view_new (); gtk_container_add (GTK_CONTAINER (scroll), view); webkit_web_view_load_string (WEBKIT_WEB_VIEW (view), "<html><body></body></html>", "text/html", "UTF-8", NULL);

gtk_widget_show_all (GTK_WIDGET (box)); return window; }

Nejprve vytvoříte objekt `GtkWindow` a nastavíte jeho název do záhlaví a výchozí velikost. Rovněž napojíte funkci `gtk_main_quit` na signál `"delete-event"`. Tento signál je vyslán, když je okno zavřeno. Funkce `gtk_main_quit` je součástí GTK a ukončí aplikaci.

Pak vytvoříte svislý box a přidáte jej do okna. Okno může obsahovat jen jediný synovský widget, takže potřebujete box, abyste mohli přidat víc widgetů. Druhým argumentem u funkce `gtk_box_new` se nastaví velikost odsazení (v pixelech) mezi jednotlivými potomky a následující řádek vloží 6pixelový okraj okolo toho všeho.

Následně vytvoříte objekt `GtkEntry` a zabalíte jej do boxu. Třetí argument u `gtk_box_pack_start` určují, že vstupní pole by nemělo zabírat žádné místo navíc, které je v boxu dostupné. Čtvrtý argument je velikost odsazení okolo vstupního pole. V tomto případě jej nastavte na nulu, protože o odsazení se postará box.

Než přidáte webové zobrazení, musíte vytvořit okno s posuvníky, do kterého jej pak vložíte. Okno s posuvníky umístí v případě potřeby doprava a dolů posuvníky a zabrání vašemu webovému zobrazení, aby zaplnilo celou obrazovku. Tentokrát funkci `gtk_box_pack_start` předejte `TRUE` a `TRUE`, aby umožnila oknu s posuvníky (a tím webovému zobrazení) použít dodatečné místo dostupné v boxu.

Nakonec vytvoříte `WebKitWebView` a přidáte jej do okna s posuvníky. Potom načtěte úplně základní stránku v HTML do webového zobrazení zavoláním `webkit_web_view_load_string` s následujícími parametry:

## `WEBKIT_WEB_VIEW (view)`

Vlastní zobrazení. Protože `view` je typováno jako `GtkWidget*`, musíte požít `WEBKIT_WEB_VIEW`, aby se objekt bezpečně přetypoval.

## `"<html><body></body></html>"`

Nejjednodušší soubor HTML, který byste mohli napsat.

## `"text/html"`

Typ MIME obsahu, který poskytujete. V tomto případě používáte prosté HTML.

## `"UTF-8"`

Kódování znaků v obsahu, který poskytujete. I když použijete jen znaky ASCII, je dobrým pravidlem zadat UTF-8. UTF-8 je totiž používáno jako výchozí kódování napříč celou platformou GNOME.

## `NULL`

Základní adresa URI. V tomto příkladu ji nepotřebujeme, ale možná budete chtít zadat URI file:, kdybyste přidali obrázky nebo jinou funkčnost, ve které chcete použít relativní odkazy URI.

Pokaždé, když přidáte widget, musíte pro něj zavolat `gtk_widget_show`, aby byl viditelný. Když zavoláte `gtk_widget_show_all` pro kontejnerový widget, jako je `GtkBox`, zobrazí GTK+ automaticky všechny widgety uvnitř kontejneru do libovolné hloubky. Občas nemusíte chtít `gtk_widget_show_all` volat, třeba když chcete dynamicky skrývat a zobrazovat některé widgety v reakci na události.

Nakonec musíme v boxu zavolat `gtk_widget_show_all`, jinak nebude žádný vytvořený widget viditelný. (Okno je zobrazeno ve funkci `main` pomocí `gtk_widget_show`.)

Sestavte a spusťte vývěsku znovu. Měli byste vidět okno s textovým vstupním polem a webové zobrazení. Zatím to nic nedělá, protože vstupní pole a webové zobrazení o sobě navzájem nic neví.

# Zaháčkování signálů

Nyní chcete vývěsku naučit něco udělat, když zadáte text do textového vstupního pole. To se provede tak, že se napojí funkce zpětného volání na signál `"activate"` objektu `entry`. GTK+ vyšle signále `"activate"`, kdykoliv uživatel ve vstupním poli zmáčkne Enter. Následující kód přidejte do `create_window` kamkoliv za definici `entry` a `view`:

g_signal_connect (entry, "activate", G_CALLBACK (entry_activate_cb), view);

Potom můžete totiž definovat `entry_activate_cb`. Nadefinujte jej následovně kdekoliv nad `create_window`:

static void entry_activate_cb (GtkEntry *entry, WebKitWebView *view) { WebKitDOMDocument *document; WebKitDOMElement *body, *div;

document = webkit_web_view_get_dom_document (view); body = webkit_dom_document_query_selector (document, "body", NULL); div = webkit_dom_document_create_element (document, "div", NULL); webkit_dom_node_set_text_content (WEBKIT_DOM_NODE (div), gtk_entry_get_text (entry), NULL); webkit_dom_node_append_child (WEBKIT_DOM_NODE (body), WEBKIT_DOM_NODE (div), NULL); gtk_entry_set_text (entry, ""); }

První věc, kterou uděláte, je, že získáte objekt `WebKitDOMDocument`, který představuje dokument HTML zobrazený ve widgetu `view`. Třídy a metody DOM v knihovně WebKit umožňují procházet a upravovat dokument HTML a fungují velmi podobně jako API k DOM, které můžete znát z jazyka JavaScript.

Jakmile máme dokument, chceme se dostat k prvku `body`, protože do něj můžeme přidat prvky `div`. Funkce `webkit_dom_document_query_selector` umožňuje najít prvek v dokumentu pomocí selektorů CSS. Tím si ušetříte psaní jednotvárných smyček pro procházení dokumentu.

V dalším kroku vytvoříme nový prvek `div`, ve kterém bude zpráva. Každý prvek, který vytvoříte, musí být připojen do dokumentu, takže funkce, která prvky vytváří, přebírá jako první argument `WebKitDOMDocument`. Pak nastavíte textový obsah prvku na obsah z textového vstupního pole. Protože `gtk_entry_get_text` vrací `const gchar*`, nemusíte výsledek uvolňovat z paměti.

Nakonec přidáme nový prvek `div` do těla a vymažeme textové vstupní pole, takže můžete napsat něco dalšího. Sestavte a spusťte program znovu a otestujte si jej.

# Vytvoření hezčího vzhledu pomocí CSS

V tuto chvíli je program plně funkční, ale ne moc hezký. Můžeme ostylovat zobrazení zpráv pomocí CSS, podobně jako to můžete udělat s kteroukoliv stránkou v HTML. Existuje mnoho způsobů, jak připojit stylopis do stránky: Mohli byste jej přidat do počátečního dokumentu HTML. Mohli byste jej vložit pomocí atributu `style` v prvku `div`. Mohli byste jej i sestrojit pomoci API k DOM.

V této lekci připojíme CSS pomocí vlastnosti `user-stylesheet-uri` objektu `WebKitWebSetting` připojeného do vašeho webového zobrazení. Ve složitějších aplikacích byste asi chtěli soubor HTML ukládat a načítat. Udržením stylopisu mimo HTML znamená, že může měnit styl přímo ve své aplikaci, bez zásahu do uživatelských souborů. Normálně by vám stačilo nainstalovat soubor se stylopisem spolu s vaší aplikací, ale aby pro potřeby této ukázky zůstalo vše uchováno v jednom souboru, použijeme trik nazvaný datová URI. Nejprve nadefinujeme CSS jako statický řetězec poblíž začátku vašeho souboru.

static const guchar CSS[] = "body { margin: 0; padding: 0; }\n" "div { " " -webkit-border-radius: 2px;" " background: -webkit-gradient(linear, 0% 100%, 0% 0%," " from(#f1f1f1), to(white));" " border: solid 1px #c6c6c6;" " -webkit-box-shadow: 0px 0px 2px #c6c6c6;" " margin: 12px; padding: 6px;" "}";

Vše, co máme v tomto příkladu, jsou prvky `div` uvnitř prvku `body`. Kdybyste vytvořili složitější HTML, mohli byste použít cokoliv by CSS bylo potřeba. Jestli jste s CSS obeznámeni, můžete si opravdu zkusit nějaké změny, aby se vám vzhled víc líbil.

Aby se použilo CSS, nastavte `user-stylesheet-uri` ve funkci `create_window` kdekoliv za definicí `view`.

tmp = g_base64_encode (CSS, strlen((gchar *) CSS)); css = g_strconcat ("data:text/css;charset=utf-8;base64,", tmp, NULL); g_object_set (webkit_web_view_get_settings (WEBKIT_WEB_VIEW (view)), "user-stylesheet-uri", css, NULL); g_free (css); g_free (tmp);

Rovněž zajistěte přidání deklarací proměnných pro `tmp` a `css` na začátku `create_window`.

gchar *tmp, *css;

Datová URI začíná data: a některými informacemi o typu obsahu a kódování dat. Skutečná data následují za čárkou, v tomto případě zakódovaná v Base64. Narozdíl od jiných schémat URI, jako je http:, ftp: a file:, schéma URI data: nespecifikuje, kde najít soubor k načtení. Místo toho poskytuje rovnou celý obsah souboru.

Předchozí kód nejprve zakóduje vaši definici CSS v Base64 a pak ji zkombinuje s pevným řetězcem, aby vytvořil datovou URI. Funkce `g_strconcat` může přebírat libovolný počet řetězcových argumentů a spojit je dohromady, proto jí musíte jako poslední argument předat `NULL`, aby věděla kdy přestat. A až stylopis správně nastavíte, nezapomeňte uvolnit tyto dočasné řetězce z paměti.

Sestavte a spusťte program znovu. Měl by fungovat úplně stejně jako na konci předchozí části, vyjma toho, že zprávy jsou hezky ostylované pomocí okrajů a pozadí s jemným přechodem.

# Jak se naučit více

V této lekci jsme si ukázali, jak vytvořit základní aplikaci pomocí knihoven GTK+ a WebKit, včetně zobrazení dokumentu a manipulace s jeho obsahem. Pro vytvoření reálné aplikace byste nejspíše museli udělat něco navíc. Zkuste si přidat vlastní funkčnost. Zde je pár nápadů:

Pokud máte CSS rádi, zkuste si změnit styl zobrazených zpráv. S CSS se snadno začíná, ale i roste k mocnějšímu využití. Na Internetu je k dispozici nepřeberné množství výukových materiálů k CSS a téměř vše, co můžete udělat na webu, můžete udělat i v této aplikaci.

V současné stavu přijdete o všechny své zprávy, když vývěsku zavřete. Zkuste obsah v HTML ukládat po každém příspěvku a načítat jej z uloženého souboru (když existuje) při spuštění.

Až budete mít své zprávy uchované po dlouhou dobu, možná vás začne zajímat, kdy vlastně byly zveřejněny. Přidejte ke každé zprávě při jejím zveřejnění časové razítko. Možná budete potřebovat vytvořit další synovský prvek `div` s jinou třídou, abyste to mohli ostylovat pomocí CSS.

Tento program uchovává zprávy navždy. Popřemýšlejte o způsobu, jak by uživatel mohl zprávy mazat. Třeba můžete chtít, aby zprávy samy mizely, když jsou příliš staré nebo když množství zpráv před nimi dosáhne určitého počtu. Nebo můžete ke každé zprávě přidat odkaz na její smazání. Mohli byste také přepsat kontextovou nabídku, která se objeví po kliknutí pravým tlačítkem. Tyto funkce znamenají hlubší prozkoumání API k DOM knihovny WebKit.