Taryn Fox
jewelfox@fursona.net 2012
Naučte se, jak rozvrhnout komponenty uživatelského rozhraní, jako jsou obrázky a popisky.

# 2. Vítejte v mřížce

        V této lekci si ukážeme, jak vytvořit základní widgety nebo části uživatelského rozhraní GNOME, jako jsou obrázky (Image) a popisky (Label). Po té se naučíte, jak je uspořádat do mřížky (Grid), která vám umožní umístit widgety právě tam, kde je chcete.
        Prošli jste již první lekcí v této sérii? Než budete pokračovat, bylo by to dobré udělat.

# Buďme přirození

V předchozí lekci jsme vytvořili něco, co bylo v podstatě okno GNOME s rámem pro webovou aplikaci. Vše, co jsme se potřebovali naučit z kódu specifického pro GNOME, se točilo okolo vložení WebView – widgetu obsahujícího naši aplikaci – do ApplicationWindow a jak mu říct, aby se zobrazil. K napsání vlastní aplikace byly použity HTML a JavaScript, podobně jako u webových stránek.

Tentokrát budeme používat jen nativní widgety GNOME. Widget je prostě věc, jako zaškrtávací políčko nebo obrázek, a GNOME jich má širokou nabídku, ze které můžete vybírat. Nazýváme je „nativní“ widgety kvůli odlišení od věcí, jako jsou tlačítka nebo nadpisy ve webové aplikaci, kterou jsme napsali. Protože místo webového kódu použijeme GTK+, půjde o 100% aplikaci GNOME.

GTK+ je zkratka pro „GIMP Toolkit“. Je to nástrojová sada widgetů, po kterých můžeme sáhnout, když stavíme naši aplikaci. Původně byly napsány pro GIMP, což je svobodný grafický editor.

# Nastavení vaší aplikace

Než začneme tahat widgety z nástrojové sady GTK+, musíme nejdříve napsat základní kód, který aplikace vyžaduje a tak se s ním setkáte často.

#!/usr/bin/gjs

imports.gi.versions.Gtk = '3.0'; const Gtk = imports.gi.Gtk;

Tato část je vždy na začátku kódu. V závislosti na tom, co přesně hodláte dělat, zde můžete deklarovat i další importy. To, co píšeme teď, je docela jednoduché, takže nám to bude stačit takto. Gtk pro widgety používající stabilní API 3.0.

Když už o tom mluvíme:

class WelcomeToTheGrid { // Vytvoří vlastní aplikaci constructor() { this.application = new Gtk.Application();

// Napojí signály "activate" a "startup" k funkcím zpětného volání this.application.connect('activate', this._onActivate.bind(this)); this.application.connect('startup', this._onStartup.bind(this)); },

// Funkce zpětného volání pro signál "activate" zobrazující okno při aktivaci _onActivate: function () { this._window.present (); },

// Funkce zpětného volání pro signál "startup" sestavující uživatelské rozhraní _onStartup: function () { this._buildUI (); },

Toto je začátek vlastní aplikace a funkce `_init` ji vytvoří. Aplikace pak řekne funkci `_buildUI`, aby vytvořila ApplicationWindow, který budeme nazývat _window a našemu oknu se říká, aby se zobrazilo, kdykoliv je potřeba.

Tato část je opět do značné míry vytvořena stylem kopírovat-a-vložit, akorát je potřeba dát aplikaci vždy jedinečný název.

// Sestaví uživatelské rozhraní aplikace _buildUI {

// Vytvoří okno aplikace this._window = new Gtk.ApplicationWindow({ application: this.application, window_position: Gtk.WindowPosition.CENTER, border_width: 10, title: "Welcome to the Grid"});

Nakonec spustíme funkci `_buildUI` vytvořením nového ApplicationWindow. Určíme naší aplikaci, že se má objevit uprostřed obrazovky a že by mělo být nejméně 10 pixelů mezi vnější hranou a widgety uvnitř. Rovněž ji přidělíme název, který se objeví v záhlaví okna.

# Po čem sáhnout do nástrojové sady GTK+

Které widgety bychom měli použít? Řekněme, že chceme napsat aplikaci, která vypadá nějak takto:

K tomu budeme potřebovat aspoň obrázek a textový popisek. Začněme obrázkem:

// Vytvoří obrázek this._image = new Gtk.Image ({ file: "gnome-image.png" });

Obrázek použitý v tomto příkladu si můžete stáhnout zde. Zajistěte, aby byl uložený ve stejné složce, jako kód, který píšete.

// Vytvoří popisek this._label = new Gtk.Label ({ label: "Welcome to GNOME, too!" });

Tento kód pod něj přidá popisek. Zde můžete vidět, jak se vytváří widgety. Všechny jsou součástí Gtk a mi jim udáváme vlastnosti, kterými si přizpůsobíme, jak se mají chovat. V tomto případě widgetu Image nastavíme vlastnost `file` na název souboru s obrázkem a popisku vlastnost `label` na větu, kterou chceme mít pod obrázkem.

Ano, to že má Label vlastnost label vypadá jako zdvojování, ale není tomu tak. Ostatní widgety, které obsahují text, mají vlastnost `label`, takže kvůli jednotnosti je tomu tak i u widgetu Label.

Tyto widgety nemůžeme do našeho okna jen tak přidat ve správném pořadí, podobně jako se to dělá s prvky HTML na webové stránce. Kvůli tomu, že ApplicationWindow může obsahovat jen jeden widget.

Co s tím? Vytvoříme kontejnerový widget, který může obsahovat více než jeden widget a umí je uspořádat. Pohleďte: mřížka.

// Vytvoří mřížku this._grid = new Gtk.Grid ();

Neudali jsme jí žádné vlastnosti. To uděláme až později, až se naučíme s mřížkou pořádně zacházet. Nejprve pojďme do námi vytvořené mřížky připojit obrázek a popisek.

// Připojení obrázku a popisku do mřížky this._grid.attach (this._image, 0, 0, 1, 1); this._grid.attach (this._label, 0, 1, 1, 1);

Tento kód vypadá strašně složitě, ale není tomu tak. Zde je popsáno, co jednotlivá čísla znamenají:

První číslo je pozice ve směru zleva doprava, na kterou se má widget umístit, počínaje od 0. Widget, který použije 0, patří úplně doleva.

Druhé číslo je pozice ve směru shora dolů, na kterou se má daný widget umístit, počínaje od 0. Popisek má být pod obrázkem, takže widget Image dostane pozici 0 a Label pozici 1.

Třetí a čtvrté číslo udává kolik sloupců a řádků má widget okupovat. Za minutku uvidíme, jak to funguje.

// Přidá mřížku do okna this._window.add (this._grid);

// Zobrazí okno a všechny jeho synovské widgety this._window.show_all(); }

};

// Spustí aplikaci let app = new WelcomeToTheGrid (); app.application.run (ARGV);

Nyní máme vytvořenou mřížku a do ní připojené všechny naše widgety, přidali jsme ji do okna a oknu, jako poslední část funkce _buildUI, řekli, ať se zobrazí. Jako vždy končíme vytvořením nové instance třídy aplikace a jejím spuštěním.

Uložte svoji aplikaci jako welcome_to_the_grid.js. Následně ji spusťte tak, že otevřete terminál, přepnete se do složky, ve které je vaše aplikace, a napíšete:

     $ gjs welcome_to_the_grid.js

Ono to funguje! Ale… Nevypadá to úplně hezky. Proč je popisek nalepený těsně na obrázek? To jednak není estetické a jedna se to špatně čte. Co s tím můžeme udělat?

# Vyladění mřížky

Jedna z věcí, kterou můžeme udělat, je udat popisku při vytváření vlastnost `margin_top`. Funguje to podobně, jako nastavení vlastnosti „margin“ u prvků HTML pomocí stylování CSS.

// Vytvoří popisek this._label = new Gtk.Label ({ label: "Welcome to GNOME, too!", margin_top: 20 });

Samozřejmě, že když popisek nahradíme něčím jiným, nebo přidáme další widget, musíme nastavení `margin_top` zopakovat i u něj. Jinak s končíme s něčím takovýmto:

Mohli bychom udat vlastnost `margin_bottom` u obrázku, ale to by nefungovalo správně, když by nový popisek byl v jiném sloupci. Takže pojďme to místo toho zkusit takto:

// Vytvoří mřížku this._grid = new Gtk.Grid ({ row_spacing: 20 });

To zajistí, že vodorovně mezi všemi řádky bude vždy prostor 20 pixelů.

Ano, obdobně můžeme nastavit vlastnost mřížky `column_spacing` nebo vlastnosti `margin_left` a `margin_right` u libovolného widgetu. Jestli chcete, vyzkoušejte si to!

# Přidání dalších widgetů

Pokud bychom chtěli přidat druhý popisek, jak bychom to vlastně měli udělat, aby to na pohled vypadalo, že tam patří? Jedním ze způsobů je obrázek nahoře umístit na střed, tak aby byl nad oběma popisky a ne jen nad jedním vlevo. To je ta situace, kdy ostatní čísla u metody pro připojení do mřížky, přijdou vhod:

// Vytvoří druhý popisek this._labelTwo = new Gtk.Label ({ label: "The cake is a pie." });

// Připojí obrázek a popisky do mřížky this._grid.attach (this._image, 0, 0, 2, 1); this._grid.attach (this._label, 0, 1, 1, 1); this._grid.attach (this._labelTwo, 1, 1, 1, 1);

Po té, co vytvoříme druhý popisek, připojíme jej do mřížky napravo od prvního popisku. Pamatujete, že první dvě čísla udávají číslo sloupce a řádku zleva doprava a shora dolů, počítáno od 0? Takže, když je první popisek v sloupci 0 a řádku 1, můžeme vložit druhý do sloupce 1 a řádku 1, aby byl napravo od prvního.

Podívejme se na číslo 2 ve výrazu pro připojení obrázku. V něm je ten trik. Toto číslo znamená, přes kolik sloupců se obrázek roztáhne, pamatujete? Takže, když to dáme vše dohromady, dostaneme něco takového:

Jsou zde dvě věci, které byste měli vzít na vědomí.

Natavení obrázku, aby zabíral dva sloupce, přímo obrázek nijak neroztáhne. Místo toho roztáhne neviditelný box obsazený widgetem Image tak, aby zaplnil oba sloupce a obrázek pak umístí do středu tohoto boxu.

I když jsme nastavili vlastnosti `row_spacing` u mřížky a `border_width` u okna aplikace, nemáme zatím nic, co by vložilo okraj mezi dva popisky. Dříve byly odděleny díky tomu, že obrázek zabíral jen jeden sloupec, ale nyní, když je roztažen přes oba sloupce, nevidí GNOME důvod k tomu, držet popisky oddělené.

Existují aspoň tři způsoby, jak vyřešit druhou záležitost. Jako první můžeme nastavit u jednoho z popisků `margin_left` nebo `margin_right`:

Zadruhé můžeme nastavit vlastnost mřížky `column_homogenous` na `true`.

// Vytvoří mřížku this._grid = new Gtk.Grid ({ column_homogeneous: true, row_spacing: 20 });

Ve výsledku to bude vypadat nějak takto:

A zatřetí můžeme nastavit vlastnost mřížky `column_spacing`, stejným způsobem jako její vlastnost `row_spacing`.

// Vytvoří mřížku this._grid = new Gtk.Grid ({ column_spacing: 20, row_spacing: 20 });

Ve výsledku to bude vypadat takto:

# Používání standardních obrázků

GNOME má připravenou spoustu standardních obrázků, které můžete použít, kdy se necítíte na vytvoření vlastních nebo chcete použít obecně známou ikonu. Zde je, jak vytvořit standardní obrázek, pro srovnání s tím, jak vytvořit ten normální:

// Vytvoří obrázek this._image = new Gtk.Image ({ file: "gnome-image.png" });

// Vytvoří druhý obrázek pomocí standardní ikony this._icon = new Gtk.Image ({ stock: 'gtk-about' });

Po té jej připojíme do mřížky nalevo od prvního popisku. (V tomto příkladu nevyužijeme druhý popisek.)

// Připojí obrázky a popisek do mřížky this._grid.attach (this._image, 0, 0, 2, 1); this._grid.attach (this._icon, 0, 1, 1, 1); this._grid.attach (this._label, 1, 1, 1, 1);

Po spuštění dostaneme něco takového:

Je to to, co vypadá jako standardní ikona „O aplikaci“ (About). Na seznam všech standardních položek, počínaje gtk-about, se můžete podívat v dokumentaci pro vývojáře GNOME. Byla napsána pro programátory v jazyce C, ale ten nepotřebujete znát k tomu, abyste ji mohli používat. Stačí se jen podívat na část v uvozovkách, jako je "gtk-about" a tuto část zkopírovat, když chcete použít ikonu vedle ní.

Okolo 'gtk-about' jsem v kódu použili jednoduché uvozovky, protože na rozdíl od jiných textových řetězců, které máme v dvojitých uvozovkách, tento nebude nikdy potřeba překládat do jiného národního jazyka. Ve skutečnosti, kdyby byl přeložen, tak by došlo k porušení ikony, protože její název musí být pořád "gtk-about", bez ohledu na jazyk, ve kterém je uživatelské rozhraní.

# A co dál?

Než přikročíme k další lekci, zkusme něco udělat trochu jinak:

// Vytvoří tlačítko this._button = new Gtk.Button ({ label: "Welcome to GNOME, too!"});

// Připojí obrázky a tlačítka do mřížky this._grid.attach (this._image, 0, 0, 2, 1); this._grid.attach (this._icon, 0, 1, 1, 1); this._grid.attach (this._button, 1, 1, 1, 1);

Je to pravda, změnili jsme popisek na tlačítko jen pouhou změnou názvu widgetu! Zjistíte však, že když aplikaci spustíte a kliknete na něj, nic nedělá. Jak jej přimět, aby něco dělalo? To je to, co se dozvíte v naší následující lekci.

Jestli se vám to líbí, zkuste věnovat nějaký čas experimentování s mřížkami, popisky a obrázky, včetně standardních obrázků.

Jedním z triků, jak vytvořit složitější rozvržení, je vnoření mřížek do sebe. To umožňuje seskupit dohromady související widgety a snadno měnit jejich uspořádání. Pokud vás zajímá, jak se to dělá, podívejte na na ukázkový kód k widgetu RadioButton.

# Úplný kód ukázky

#!/usr/bin/gjs

imports.gi.versions.Gtk = '3.0'; const Gtk = imports.gi.Gtk;

class WelcomeToTheGrid {

// Vytvoří vlastní aplikaci constructor() { this.application = new Gtk.Application();

// Napojí signály "activate" a "startup" k funkcím zpětného volání this.application.connect('activate', this._onActivate.bind(this)); this.application.connect('startup', this._onStartup.bind(this)); },

// Funkce zpětného volání pro signál "activate" zobrazujicí okno při aktivaci _onActivate() { this._window.present(); },

// Funkce zpětného volání pro signál "startup" sestavující uživatelské rozhraní _onStartup() { this._buildUI (); },

// Sestaví uživatelské rozhraní aplikace _buildUI() {

// Vytvoří okno aplikace this._window = new Gtk.ApplicationWindow({ application: this.application, window_position: Gtk.WindowPosition.CENTER, border_width: 10, title: "Welcome to the Grid"});

// Vytvoří mřížku this._grid = new Gtk.Grid ({ // column_homogeneous: true, // column_spacing: 20, row_spacing: 20 });

// Vytvoří obrázek this._image = new Gtk.Image ({ file: "gnome-image.png" });

// Vytvoří druhý obrázek pomocí standardní ikony this._icon = new Gtk.Image ({ stock: 'gtk-about' });

// Vytvoří popisek this._label = new Gtk.Label ({ label: "Welcome to GNOME, too!", /* margin_top: 20 */ });

/* Vytvoří druhý popisek this._labelTwo = new Gtk.Label ({ label: "The cake is a pie." }); */

/* Vytvoří tlačítko this._button = new Gtk.Button ({ label: "Welcome to GNOME, too!"}); */

// Připojí obrázek a tlačítka do mřížky this._grid.attach (this._image, 0, 0, 2, 1); this._grid.attach (this._icon, 0, 1, 1, 1); this._grid.attach (this._label, 1, 1, 1, 1);

// this._grid.attach (this._label, 0, 1, 1, 1); // this._grid.attach (this._labelTwo, 1, 1, 1, 1);

// this._grid.attach (this._button, 1, 1, 1, 1);

// Přidá mřížku do okna this._window.add (this._grid);

// Zobrazí okno a všechny jeho synovské widgety this._window.show_all(); }

};

// Spustí aplikaci let app = new WelcomeToTheGrid (); app.application.run (ARGV);