Forum und email

A visão: GtkTreeView, GtkTreeViewColumn, GtkCellRenderer

O widget GtkTreeView é a visão na aproximação Modelo/Vista/Controlador. Ela cuida de mostrar os dados guardados em um modelo (GtkTreeStore ou GtkListStore) para o usuário. Você pode ter vários GtkTreeViews para um único modelo, e mudanças no modelo serão exibidos automaticamente em todos eles.

Para usar o GtkTreeView, você precisa criar o widget em sí, então criar GtkTreeViewColumns para as colunas a exibir e GtkCellRenderers para dizer para as colunas como exibir uma célula na coluna.

Exemplo 10.3. TreeView e Renderer

$view = new GtkTreeView($model);
$renderer = new GtkCellRendererText();
$column = new GtkTreeViewColumn("Folder", $renderer, "text", 1);
$view->append_column($column);

Este código cria um novo GtkTreeView a anexa-o ao modelo. Então cria um cell renderer de texto e uma coluna e adiciona estes para a visão. "Folder" é o nome da coluna, exibida ao topo da coluna.

Existem vários GtkCellRenderers na biblioteca Gtk+, e você pode escrever o seu próprio, se estes não forem o suficiente.

As seleções do usuário em um GtkTreeView (por visão) são seguidas usando o objeto GtkTreeSelection. Se o seu código necessita saber quando o usuário muda a seleção, conecte com o sinal "changed" do GtkTreeSelection. Você também pode fazer o objeto da seleção chamar uma função para cada ramo selecionado, ou mudar atráves de programação a seleção. Veja a documentação da API para detalhes.

Exemplo 10.4. O sinal "changed" da seleção

$selection = $view->get_selection();
$selection->connect("changed", "display_selected_folder");

Este código conecta o sinal "changed" a função (display_selected_folder()) que exibe o conteúdo da pasta selecionada.

Você precisa dizer ao GtkTreeView explicitamente que é permitido ao usuário rearanjar a arvore de pastas usando drag-and-drop. Felizmente, após dizer isto uma vez, o widget se encarrega de cuidar do resto.

Exemplo 10.5. Reordenando com Drag-and-Drop

$view->set_reorderable(true);

Isto é tudo o que você precisa para ativar o drag-and-drop no widget.

A mesma coisa pode ser feita manualmente se você escolher não ativar o drag-and-drop removendo o ramo filho da arvore e inserindo-o de volta como filho de outro ramo.

Exemplo 10.6. Reordenamento manual

$folder = $model->get_value($old_iter, 0);
$model->remove($old_iter);
$new_iter = $model->insert_before($new_parent, null);
$model->set($new_iter, 0, $folder);
$model->set($new_iter, 1, $folder['name']);

Este código move um ramo de old_iter para ser o último filho do ramo new_parent.

Quando este tutorial foi escrito/portado havia um bug no PHP-Gtk2 que a ordem dos parãmetros para insert_before() e insert_after() foram trocadas. O código acima não irá funcionar (nem o código do exemplo abaixo) sem trocar a ordem dos parâmetros. Na versão php-gtk-2.0.0 alpha, a ordem ainda é (sibling, parent) e deveria ser (parent, sibling). Isto já foi modificado no CVS.

Exemplo 10.7. Código fonte de exemplo

<?php
// This is an example for demonstrating use of the GtkTreeView widget.
// The code in this example is not particularly good: it is written to
// concentrate on widget usage demonstration, not for maintainability.

$view = null;
$choose_parent_view = null;
$dialog = null;

function move($old_iter = null, $new_parent, $model)
{
    if ($old_iter) {
        $folder = $model->get_value($old_iter, 0);
        $model->remove($old_iter);
        $new_iter = $model->insert_before($new_parent, null);
        $model->set($new_iter, 0, $folder);
        $model->set($new_iter, 1, $folder['name']);
    }
}

function dialog_ok($args)
{
    global $dialog, $choose_parent_view, $view;

    $dialog->hide();

    list($model, $parent_iter) = $choose_parent_view->get_selection()->get_selected();
    list($model, $old_iter) = $view->get_selection()->get_selected();

    if ($parent_iter && $old_iter) {
        move($old_iter, $parent_iter, $model);
    }
}

function dialog_cancel($args)
{
    global $dialog;

    $dialog->hide();
}

function choose_parent($args)
{
    global $dialog;

    $dialog->show();
}

function move_to_bottom($args)
{
    global $view;

    list ($model, $old_iter) = $view->get_selection()->get_selected();

    if ($old_iter) {
        move($old_iter, null, $model);
    }
}

function quit($args)
{
    Gtk::main_quit();
}

function make_view($model)
{
    $view = new GtkTreeView($model);
    $view->set_reorderable(true);
    $renderer = new GtkCellRendererText();
    $column = new GtkTreeViewColumn("Folder", $renderer, "text", 1);
    $view->append_column($column);
    $view->show();

    $scrolled = new GtkScrolledWindow();
    $scrolled->add($view);
    $scrolled->show();

    return array($view, $scrolled);
}

function make_buttons($list)
{
    $buttonbox = new GtkHBox();

    foreach ($list as $label => $func) {
        $button = new GtkButton();
        $button->set_label($label);
        $button->connect("clicked", $func);
        $button->show();
        $buttonbox->pack_start($button, false, false);
    }

    $buttonbox->show();

    return $buttonbox;
}

$model = new GtkTreeStore(Gtk::TYPE_PHP_VALUE, Gtk::TYPE_STRING);

for ($i=0; $i < 100; $i++)
{
    $folder = array('name' => 'folder ' . $i, 'files' => array('foo', 'bar'));
    $iter = $model->insert_before(null, null);
    $model->set($iter, 0, $folder);
    $model->set($iter, 1, $folder['name']);
}

list($view, $scrolled) = make_view($model);
$view->set_reorderable(true);

$buttons = array(
    "Quit"           => "quit",
    "Choose parent"  => "choose_parent",
    "Move to bottom" => "move_to_bottom"
);

$buttonbox = make_buttons($buttons);

$vbox = new GtkVBox();
$vbox->pack_start($buttonbox, false, false);
$vbox->pack_start($scrolled, true, true);
$vbox->show();

$win = new GtkWindow(Gtk::WINDOW_TOPLEVEL);
$win->connect("delete_event", "quit");
$win->add($vbox);
$win->show();
$win->resize(300, 500);

list($choose_parent_view, $scrolled) = make_view($model);

$buttons = array(
    "OK"     => "dialog_ok",
    "Cancel" => "dialog_cancel"
);

$buttonbox = make_buttons($buttons);

$vbox = new GtkVBox();
$vbox->pack_start($scrolled, true, true);
$vbox->pack_start($buttonbox, false, false);
$vbox->show();

$dialog = new GtkWindow(Gtk::WINDOW_TOPLEVEL);
$dialog->set_default_size(200, 400);
$dialog->add($vbox);

Gtk::main();
?>