Construa seu navegador - parte III
Nesta edição vamos aprender a manter as aparências de nosso navegador
O design de uma aplicação é importantíssimo. Pode parecer óbvio, mas certamente não é. Muitos aplicativos sofrem (ou fazem seus usuário sofrerem) de sérias falhas de design. Ao planejar um programa, o bom desenvolvedor tem de pensar em como o usuário vai usar o programa, como ele vai sentir-se ao utilizá-lo. Já o mau desenvolvedor escreve um programa com falhas graves que fazem os seus usuários penarem para usar o programa.
E eis que o nosso navegador possui uma pequena (nem tanto) falha de design. Se você já usou nosso navegador, deve ter odiado o fato de que temos de usar a opção `Abrir' para ir para um site. Bem, vamos mudar isso agora.
Embora já tenhamos utilizado isso anteriormente, nada foi dito sobre a classe QLayout e suas derivadas. Estas classes são muito importantes. Aliás, estão entre as classes mais importantes da Qt, pois não deve haver programa Qt ou KDE que não as utilize. Em alguns ambientes, é comum que os diálogos não sejam redimensionáveis e, por isso, a maioria dos widgets nestas plataformas é inserida na janela com dimensões e posições definidas pelo programador. Em ambientes XWindow, é mais comum dar ao usuário a escolha do melhor tamanho das janelas. Assim, não podemos simplesmente dizer que o botão X terá 64x30 de tamanho e ficará na posição 100x200, pois se o usuário redimensionasse a janela, o botão pareceria estar no local errado. A Qt resolve este tipo de problema com o uso de layouts. Os layouts definem qual a posição correta para um objeto, independentemente do tamanho da janela. Para quem está começando, isto pode parecer um pouco complexo, mas logo tudo começa a fazer sentido e percebe-se que é muito mais simples montar uma tela bem desenhada com layouts do que com posições fixas.
A Figura 1 mostra como funcionam os layouts. A primeira coisa que o desenvolvedor faz é criar um layout para o diálogo. No caso da figura trata-se de um QHBoxLayout. O QHBoxLayout divide a tela em pedaços horizontais da mesma maneira como QVBoxLayout o faz de maneira vertical. No nosso exemplo, adicionamos dois "objetos" ao QHBoxLayout inicial: uma lista (QListBox) e um outro layout (QVBoxLayout).
O segundo layout contém três objetos, mas apenas
dois destes são visíveis: um label (QLabel) e um botão
(QPushButton). O objeto intermediário (QSpacerItem) é
a maneira que a Qt achou de satisfazer a necessidade que
o usuário tem de incluir "espaços" em um diálogo ou
objeto. A Figura 2 mostra um diálogo usando exatamente
o esquema de layout da Figura 1, enquanto que o código
abaixo mostra como este esquema de layout é feito no
programa.
Quando se visualiza o código de layout, tem-se a impressão de que é muito difícil de se imaginar a janela. Isso é muito facilitado se o desenvolvedor desenhar a tela em um pedaço de papel antes de começar a programar, mais ou menos como na Figura 1. Além disso, os programadores que preferirem podem usar o QT Designer, programa que acompanha a distribuição da Qt.
QHBoxLayout *Form1Layout = new QHBoxLayout( this, 11, 6, "Form1Layout");
QListBox *ListBox1 = new QListBox( this, "ListBox1" );
ListBox1gt;insertItem( "QListBox" );
Form1Layout->addWidget( ListBox1 );
QVBoxLayout *Layout1 = new QVBoxLayout( 0, 0, 6, "Layout1");
QLabel *TextLabel1 = new QLabel( this, "TextLabel1" );
TextLabel1->setSizePolicy( QSizePolicy( (QSizePolicy::SizeType)5,
(QSizePolicy::SizeType)4, 0, 0, TextLabel1->sizePolicy().
hasHeightForWidth() ) );
TextLabel1->setText( "QLabel" );
Layout1->addWidget( TextLabel1 );
QSpacerItem* spacer = new QSpacerItem( 20, 20,
QSizePolicy::Expanding, QSizePolicy::Minimum );
Layout1->addItem( spacer );
QPushButton *PushButton1 = new QPushButton( this, "PushButton1" );
PushButton1->setText( "QButton" );
Layout1->addWidget( PushButton1 );
Form1Layout->addLayout( Layout1 );
Finalizando a parte teórica dos layouts, pode-se ver a Figura 3 mostrando as vantagens do uso dos mesmos. Estão sobrepostos três tamanhos da mesma janela, observe como os objetos se posicionam automaticamente para manter as aparências.
Aonde você quer ir agora?
Conhecendo os layouts, vai ser extremamente simples adicionarmos mais um elemento ao nosso navegador: uma barra de localização, ou seja, um campo onde poderemos digitar o nome do site para onde desejamos ir.
Para editar um texto simples (de uma linha), podemos usar o KLineEdit. Vamos então incluir um KLineEdit ao browser.
No arquivo krdlgatorview.cpp, vamos editar o construtor para adicionar o novo objeto:
m_text = new KLineEdit(this, "text");
Agora precisamos conectar o sinal returnPressed() que é ativado sempre que o usuário pressionar a tecla Enter:
connect (m_text, SIGNAL(returnPressed(const QString&)),this,SLOT(slotOpenURL(const QString&)));
E o slot slotOpenURL simplesmente contém uma chamada para o método OpenURL(). A Figura 4 mostra como ficou a nova interface do navegador.
Personalizando
A grande maioria dos programas precisa permitir que seus usuários definam opções pessoais. Na realidade, um programa pouco personalizável é geralmente um programa bastante desagradável de usar e tende a ser deixado de lado pelos usuários em favor de programas mais amigáveis. Assim, é imprescindível que sua aplicação tenha um diálogo de personalização bem desenhado. Felizmente, o KDE possui algumas classes bastante úteis para isso. Já conhecemos a classe KConfig , que será responsável por efetivamente guardar e recuperar dados, mas o que nos interessa agora é a parte gráfica. A primeira classe que nos interessa é a classe KDialogBase.
KDialogBase é uma classe derivada de KDialog, que é usada para criar diálogos com layouts pré-definidos como, por exemplo, um diálogo de preferências de usuário. Esta classe é base de vários tipos de diálogos usados no KDE como, por exemplo:
- KAboutDialog: diálogo que é apresentado em todas as aplicações quando o usuário escolhe o menu `Sobre';
- KPasswordDialog: diálogo para digitação de senhas;
- KFontDialog: diálogo de seleção de fontes usado em várias aplicações;
- KEdReplace: janela para procura e substituição de strings em uma caixa de textos.
Além disso, podemos usar KDialogBase para criar nosso diálogo de preferências. A inicialização de um KDialogBase é feita de mais de uma maneira, mas para o nosso caso, vamos usar o seguinte construtor:
KDialogBase ( int dialogFace, const QString &caption, int buttonMask,
ButtonCode defaultButton, QWidget *parent=0, const char *name=0,
bool modal=true, bool separator=false,
const KGuiItem &user1=KGuiItem(),
const KGuiItem &user2=KGuiItem(),
const KGuiItem &user3=KGuiItem() )
Não se desespere com estes parâmetros, pois vamos usar apenas os primeiros quatro. O primeiro argumento, dialogFace, pode ser um dos seguintes valores:
- TreeList: mostra uma lista em formato de árvore;
- Tabbed: o diálogo é dividido em pastas;
- Plain: um diálogo normal, sem características especiais;
- IconList: mostra uma lista de ícones, cada um associado a uma visão.
No nosso caso, vamos usar IconList. O segundo parâmetro é simplesmente o título do diálogo. O terceiro parâmetro é uma combinação de valores para indicar quais botões queremos que apareçam no diálogo. Em nosso caso vamos definir Help Default Ok Aplly Cancel para termos os botões padrão de ajuda, de voltar aos valores padrão, ok, aplicar e cancelar. O argumento final é simplesmente o botão padrão, ou seja, o botão que deve ser acionado quando o usuário pressionar Enter.
Sabendo isso, podemos começar a usar KDialogBase em nosso navegador. No arquivo krdlgatorpref.h, vamos definir uma classe chamada KrdlgatorPreferences derivada de KDialogBase:
class KrdlgatorPreferences : public KDialogBase
O construtor da classe em krdlgatorpref.cpp deverá ficar na seguinte forma:
KrdlgatorPreferences::KrdlgatorPreferences()
: KDialogBase(IconList, "Krdlgator Preferences",
Help|Default|Ok|Apply|Cancel, Ok)
Isso inicializa KDialogBase como um diálogo de ícones. Agora vem ao caso entender como funcionará este diálogo.
Um diálogo com IconView (assim como o TreeView e Tabbed ) funciona em um esquema de páginas. Cada página deve conter apenas um QWidget ou derivado. O mais comum é criarmos uma classe derivada de QWidget ou QFrame separada para cada página, isso permite uma maior organização no código. De todo o modo, para incluir um widget em uma página, pode-se fazer algo assim:
QFrame *frame;
frame = addPage( i18n( "First Page" ), i18n(
"Page One Options" ),
loadPixmap( "general" ) );
m_pageOne = new KrdlgatorPrefPageOne( frame );
Note que krdlgatorPrefPageOne é derivada de QFrame e dentro deste QFrame você pode colocar o que quiser, desde um simples QLabel até uma complexa KHTMLPart.
A função interessante acima é addPage(). Os primeiros dois parâmetros são bastante diretos e simples: título do ícone e título da janela. O terceiro parâmetro é opcional e especifica um QPixmap, ou seja, uma imagem para o ícone. No caso vamos usar uma pequena função útil para carregar o ícone:
static inline QPixmap loadIcon( const char * name ) {
return KGlobal::instance()->iconLoader()
->loadIcon( QString::fromLatin1(name),
KIcon::NoGroup,KIcon::SizeMedium );
}
Esta função simplifica bastante a tarefa de carregar um ícone. Só precisamos passar o identificador do ícone, que o KDE faz o resto. Os ícones do KDE são sempre arquivos PNG com um nome de arquivo especial. Este nome contém o tamanho do ícone, sua resolução de cores e seu grupo. Por exemplo, o ícone hi48-app-kmail.png indica que se trata de um ícone em hicolour (atualmente não se usa mais o tipo low colour, antigamente usado para máquina em 256 cores), de 48x48 pixels e que é de um ícone de aplicação. Seu identificador seria, então, "kmail". Mas o programador não precisa conhecer estes detalhes para usar um ícone, pois o IconLoader::loadIcon() só precisa receber o identificador do ícone, seu grupo de procura (pode-se especificar NoGroup, para que a procura não se restrinja a um grupo específico) e o tamanho para montar o nome do arquivo correto e carregá-lo. Para simplificar, a nossa função loadIcon() recebe apenas o identificador de ícone. A Figura 4 mostra o navegador com o novo diálogo de opções.
Roberto Teixeira - maragato@kde.org
Para saber mais