Taryn Fox jewelfox@fursona.net 2012 Aprenda como posicionar componentes de interface de usuário, como Images e Labels. Rafael Ferreira rafael.f.f1@gmail.com 2013 2. Bem-vindo à Grid

Este tutorial vai mostrar a você como criar widgets básicos ou partes da interface de usuário do GNOME, como Images (imagens) e Labels (rótulos). Você, então, aprenderá como organizá-las em uma Grid (grade), que permite que você coloca widgets exatamente onde você os quer.

Você já viu o primeiro tutorial desta série? Você vai querer vê-lo antes de continuar.

Se tornando nativo

No último tutorial, nós criamos o que era basicamente um quadro de janela do GNOME para um aplicativo web. Todo o código específico do GNOME que nós precisamos aprender se resolver colocando o WebView -- o widget contendo nosso aplicativo -- em um ApplicationWindow, e falando para ele exibir. O aplicativo em si foi escrito em HTML e JavaScript, assim como a maioria das páginas na web.

Agora, nós vamos usar apenas widgets nativos do GNOME. Um widget é apenas uma coisa, como uma caixa de seleção ou imagem, e o GNOME tem uma ampla variedade delas para se escolher. Nós chamamos elas de widgets "nativas" para diferenciá-las de coisas como o botão e cabeçalho nos aplicativos web que nós escrevemos. Porque em vez de usarmos código web, essas vão ser 100 porcento GNOME, usando o GTK+.

GTK+ significa "GIMP Toolkit" (kit de ferramentas do GIMP). É como uma caixa de ferramentas de widgets que você pode acessar, enquanto construindo seus aplicativos. Ele foi escrito originalmente para o GIMP, que é um software livre de edição de imagens.

Configurando seu aplicativo

Antes de nos aprofundarmos na caixa de ferramentas do GTK+, nós primeiro precisamos escrever o código básico padrão que nosso aplicativo requer.

This part always goes at the start of your code. Depending on what you'll be doing with it, you may want to declare more imports here. What we're writing today is pretty basic, so these are all we need; Gtk for the widgets, using the stable '3.0' API.

Falando nisso:

Esse é o começo de um aplicativo em si e a função _init que cria-o. Ela fala para _buildUI criar um ApplicationWindow, com o qual nós vamos chamar _window, e fala para nossa janela para se apresentar quando for necessário.

Essa parte, de novo, é basicamente um copia-e-cola, mas você sempre deseja dar seu aplicativo um nome único.

Finalmente, nós começamos a função _buildUI criando uma nova ApplicationWindow. Nós especificamos que ela vai com este aplicativo, que ela deveria aparecer no centro da tela, e que deve haver pelo menos 10 pixels entre a borda exterior e quaisquer widgets dentro dela. Nós também damos a ela um título, que vai aparecer no topo da janela.

Alcançando a caixa de ferramentas do GTK+

Quais widgets nós deveríamos usar? Bem, digamos que nós queremos escrever um aplicativo que se parece com este:

Nós vamos precisar, no mínimo, de uma imagem e um rótulo de texto para seguir. Vamos começar com a imagem:

// Cria uma imagem this._image = new Gtk.Image ({ file: "gnome-image.png" });

Você pode baixar o arquivo de imagem neste exemplo arquivo. Certifique-se de colocá-lo no mesmo diretório do código que você está escrevendo.

// Cria um rótulo this._label = new Gtk.Label ({ label: "Bem-vindo ao GNOME, também!" });

Aquele código adiciona o rótulo abaixo. Você pode ver como nós criamos widgets aqui; cada um é uma parte do GTK e nós podemos dar a ele propriedades que personalizam como ele se comporta. neste caso, nós definimos a propriedade do arquivo de imagem para ser o nome do arquivo da imagem que queremos e adicionamos a propriedade de rótulo do Label para ser a sentença que desejamos embaixo da imagem.

Sim, isso soa redundante um Label ter uma propriedade de rótulo, mas não é. Outros widgets que contêm texto possuem uma propriedade de rótulo, de forma que é consistente para o widget de Label ter um também.

Nós não podemos simplesmente adicionar estes widgets a nossa janela em ordem, porém, da mesma forma que elementos HTML aparecem na ordem que você escreve-os. Isso porque em ApplicationWindow pode conter apenas um widget.

Como nós resolvemos isso? Marcando aquele widget como um widget contêiner, que pode manter mais de um widget e organizá-los dentro dele. Conheça: a Grid.

// Cria a Grid this._grid = new Gtk.Grid ();

Nós não estamos desistindo de nenhuma propriedade ainda. Aquelas virão posteriormente, na medida em que aprendemos como usar os poderes da Grid. Primeiro, vamos anexar a Image e Label que nós fizemos para nossa Grid.

// Anexa a imagem e o rótulo à grade this._grid.attach (this._image, 0, 0, 1, 1); this._grid.attach (this._label, 0, 1, 1, 1);

Este código parece muito complicado, mas não é. Aqui segue o que aqueles números significam:

O primeiro número é qual posição esquerda-para-direita deve-se coloca as coisas, começando de 0. Qualquer widget que usa um 0 aqui vai para o mais esquerda possível.

The second number is what top-to-bottom position to put a given widget in, starting from 0. The Label goes beneath the Image, so we give the Image a 0 and the Label a 1 here.

O terceiro e quarto números são quantas colunas e linhas um widget deveria levar. Nós vamos ver como estes funcionam em um minuto.

Agora que nós criamos a Grid e anexamos todos os nossos widgets a ela, nós adicionamos-a à janela e falamos para a janela se mostrar, como parte da função _buildUI. Como sempre, para finalizar nós criamos uma nova instância da classe do aplicativo e falamos para ele executar.

Salva seu aplicativo como welcome_to_the_grid.js. Então, para executar seu aplicativo basta abrir um terminal, ir até o diretório onde seu aplicativo se encontra e digitar

$ gjs welcome_to_the_grid.js

Pronto! Mas espera. Isso não parece correto. Por que o Label está misturado próximo à Image desta forma? Isso não ficou legal e fica mais difícil de ler. O que nós podemos fazer quanto a isso?

Ajustando a Grid

Uma coisa que nós podemos fazer é dar ao Label uma propriedade margin_top no momento de sua criação. Isso funciona mais ou menos como definir uma margem para um elemento HTML usando estilo CSS embutido.

// Cria um rótulo this._label = new Gtk.Label ({ label: "Bem-vindo ao GNOME, também!", margin_top: 20 });

É claro que, se nós fizermos isso, então se substituirmos a Label com alguma outra coisa -- ou adicionar em outro widget --, então nós teremos que repetir o margin_top nele também. Do contrário, nós acabaremos com alguma coisa como isso:

Nós poderíamos dar à Image uma propriedade margin_bottom, mas isso não vai funcionar quando a nova Label estiver em uma coluna separada. Então, que tal nós tentarmos isso:

// Cria a Grid this._grid = new Gtk.Grid ({ row_spacing: 20 });

Isso resolve, de forma que há sempre 20 pixels de espaço entre cada linha horizontal.

Sim, você também pode definir a propriedade column_spacing em uma Grid ou as propriedades margin_left e margin_right em qualquer widget. Tente-os, se você quiser!

Adicionando mais widgets

Se nós quiséssemos adicionar um segundo Label, como que nós vamos fazê-lo de forma que ela realmente pareça como pertencente dali? Uma forma é centralizar a Image na parte superior, de forma que esteja em cima de ambas Labels, em vez de apenas uma para a esquerda. É aí que aqueles outros números no método para anexar na Grid entram:

// Cria um segundo rótulo this._labelTwo = new Gtk.Label ({ label: "O bolo é uma torta." }); // Anexa a imagem e rótulos à grade 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);

Após termos criado um segundo Label, nós o anexamos o segundo Label, o anexamos à Grid à direita do primeiro Label. Lembre-se, os primeiros dois números contam colunas e linhas da esquerda para a direita e de cima para baixo, começando com 0. Então, se o primeiro Label está na coluna 0 e linha 1, nós podemos colocar o segundo na coluna 1 e linha 1 para colocá-lo à direita do primeiro Label.

Note que o número 2 na declaração de anexação para a Image. É o que faz o truque aqui. Aquele número é quantas colunas uma Image se estende, lembra-se? Então, quando nós colocarmos isso junto, nós obteremos algo tipo isso:

Há duas coisas das quais você deveria tomar nota, aqui.

A definição da Image para se estender a duas colunas não estica a imagem em si horizontalmente. Ao invés disso, ela estica a caixa invisível utilizadas pelo widget da Image para preencher ambas colunas e, então, colocar a imagem no centro daquela caixa.

Ainda que tenhamos definido as propriedades row_spacing da Grid e border_width da ApplicationWindow, nós ainda não definimos nada que coloque uma borda entre as duas Labels. Elas estavam separadas anteriormente, quando a Imagem estava apenas em uma coluna, mas agora que ela estende ambas, o GNOME não vê um motivo para mantê-los separados.

Há pelo menos três formas de resolvermos o último. Primeiro, nós podemos definir um margin_left ou margin_right em um dos Labels:

Second, we can set the Grid's column_homogeneous property to true.

// Cria a Grid this._grid = new Gtk.Grid ({ column_homogeneous: true, row_spacing: 20 });

Isso faz com que ela se pareça com algo como isso:

E terceiro, nós podemos definir a propriedade column_spacing da Grid, da mesma forma que nós definimos sua row_spacing.

// Cria a Grid this._grid = new Gtk.Grid ({ column_spacing: 20, row_spacing: 20 });

Isso faz com que ela se pareça com isso:

Usando imagens padrões

O GNOME já possui um monte de imagens padrões em mãos, que nós podemos usar se não quisermos criar nossa própria ou se nós quisermos um ícone reconhecido universalmente. Aqui está como nós criamos imagens padrões, comparado com como nós criamos um normal:

// Cria uma imagem this._image = new Gtk.Image ({ file: "gnome-image.png" }); // Cria uma segunda imagem usando o ícone padrão this._icon = new Gtk.Image ({ stock: 'gtk-about' });

Após isso, nós anexamos-o à Grid à esquerda do primeiro Label. (Nós não vamos usar o segundo para este exemplo.)

// Anexa as imagens e rótulos à grade 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);

Isso nos dá isso, quando nós o executamos:

É assim que o ícone padrão "Sobre" se parece. Você pode ver uma lista de todos os itens padrões começando com o gtk-about na documentação de desenvolvimento do GNOME. Foi escrita para programadores de C, mas você não precisa saber C para usá-la; basta olha na parte nos sinais de citação, como "gtk-about" e copiar aquela parte para usar o ícone ao lado dele.

Nós colocamos aspas simples em volta de 'gtk-about' aqui porque, ao contrário de strings de texto que têm aspas duplas em volta, aquela parte nunca precisará ser traduzida para outro idioma. Na verdade, se ela fosse traduzida, ela quebraria o ícone porque seu nome ainda é "gtk-about" independentemente do seu idioma.

O que vem em seguida?

Antes de irmos para o próximo tutorial, vamos tentar algo um pouco diferente:

// Cria um botão this._button = new Gtk.Button ({ label: "Bem-vindo ao GNOME, também!"}); // Anexa as imagens e botão à grade 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);

É isso mesmo, nós tornamos o Label em um Button apenas alterando o seu nome! Se você executar o aplicativo e clicar nele, porém, você descobrirá que ele faz nada. Como nós vamos fazer nosso Button fazer algo? É o que vamos descobrir, em nosso próximo tutorial.

Se você quiser, sinta-se à vontade para gastar algum tempo experimentando Grids, Labels e Images, incluindo imagens padrões.

Um truque que você pode usar para fazer layouts mais complexos é aninhar Grids dentro de Grids. Isso permite que você agrupe widgets relacionados e rearranje-os facilmente. Dê uma olhada na amostra de código do RadioButton, se você quiser ver como ele é feito.

Amostra de código completo #!/usr/bin/gjs imports.gi.versions.Gtk = '3.0'; const Gtk = imports.gi.Gtk; class WelcomeToTheGrid { // Create the application itself constructor() { this.application = new Gtk.Application(); // Connect 'activate' and 'startup' signals to the callback functions this.application.connect('activate', this._onActivate.bind(this)); this.application.connect('startup', this._onStartup.bind(this)); } // Callback function for 'activate' signal presents windows when active _onActivate() { this._window.present(); } // Callback function for 'startup' signal builds the UI _onStartup() { this._buildUI (); } // Build the application's UI _buildUI() { // Create the application window this._window = new Gtk.ApplicationWindow({ application: this.application, window_position: Gtk.WindowPosition.CENTER, border_width: 10, title: "Welcome to the Grid"}); // Create the Grid this._grid = new Gtk.Grid ({ // column_homogeneous: true, // column_spacing: 20, row_spacing: 20 }); // Create an image this._image = new Gtk.Image ({ file: "gnome-image.png" }); // Create a second image using a stock icon this._icon = new Gtk.Image ({ stock: 'gtk-about' }); // Create a label this._label = new Gtk.Label ({ label: "Welcome to GNOME, too!", /* margin_top: 20 */ }); /* Create a second label this._labelTwo = new Gtk.Label ({ label: "The cake is a pie." }); */ /* Create a button this._button = new Gtk.Button ({ label: "Welcome to GNOME, too!"}); */ // Attach the images and button to the grid 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); // Add the grid to the window this._window.add (this._grid); // Show the window and all child widgets this._window.show_all(); } }; // Run the application let app = new WelcomeToTheGrid (); app.application.run (ARGV);