Por Jean Hertel, 31/07/2020, na categoria Dicas
Recentemente fiz um grande refactoring do adriconf e uma das coisas que finalmente decidi corrigir foi o gerenciamento de combo box. Para configurar qualquer opção no hardware, temos que perguntar quais opções são suportadas e portanto desenhamos dinamicamente os campos de configuração. Por causa disso é necessário localizar dinamicamente qual campo estamos alterando e então fazer o update.
No HTML os Combo Box suportam dois valores: um texto de exibição e o valor verdadeiro do campo. Eu queria o mesmo tipo de funcionalidade usando GTKmm, porem o processo é um pouco doloroso.
Os ComboBox no GTKmm são bem flexiveis, mas isso vem com uma complexidade. Se você quiser apenas texto pode usar o ComboBoxText:
Gtk::ComboBoxText myCombo;
myCombo.append("Option 1");
myCombo.append("Ninjas!");
myCombo.append("Samurais");
myCombo.set_active(1);
// Ver opção selecionada
Glib::ustring text = myCombo.get_active_text();
Este exemplo parece bem simples, porem para adicionar campos extras, precisamos fazer um processo bem mais complicado. A documentação oficial já cobre bem isto, portanto se quiser ver em detalhes como desenhar um campo com mais opções ou outras coisas apenas veja a documentação aqui.
No meu caso eu queria ter um ponteiro para o objeto que estou alterando mas sem ter que lidar com toda a maluquice de models e renderers. Por causa disso, escrevi as classes abaixo:
#include "gtkmm.h"
template<class T>
class ExtraDataTreeModel : public Gtk::TreeModel::ColumnRecord {
public:
Gtk::TreeModelColumn<Glib::ustring> displayName;
Gtk::TreeModelColumn<T> extraData;
ExtraDataTreeModel() {
add(displayName);
add(extraData);
}
};
template<class T>
class ComboBoxExtra : public Gtk::ComboBox {
private:
ExtraDataTreeModel<T> treeModel;
public:
// Permite chamadas por Glade Builder
ComboBoxExtra(BaseObjectType *cobject, const Glib::RefPtr<Gtk::Builder> &refGlade) : Gtk::ComboBox(cobject) {
auto listStore = Gtk::ListStore::create(treeModel);
this->set_model(listStore);
this->pack_start(treeModel.displayName);
}
ComboBoxExtra() : Gtk::ComboBox() {
auto listStore = Gtk::ListStore::create(treeModel);
this->set_model(listStore);
this->pack_start(treeModel.displayName);
}
void append(const Glib::ustring &displayName, T extraData) {
auto iter = static_cast<Gtk::ListStore *>(this->get_model().get())->append();
auto row = *iter;
row[treeModel.displayName] = displayName;
row[treeModel.extraData] = extraData;
}
T get_active_extra_data() {
auto iter = this->get_active();
auto row = *iter;
return row[treeModel.extraData];
}
void removeAllChildren() {
auto listStore = dynamic_cast<Gtk::ListStore *>(this->get_model().get());
listStore->clear();
}
};
Com estas duas classes podemos criar combo box com dados extras de maneira muito simples:
MyCustomData *someCustomClassA, *someCustomClassB;
ComboBoxExtra<MyCustomData*> myCombo;
myCombo.append("Nome para exibir", someCustomClassA);
myCombo.append("Outro nome", someCustomClassB);
// Durante a chamada de signal_changed()
MyCustomData* extraData = myCombo.get_active_extra_data();
Agora podemos pegar os ponteiros das classes que estamos alterando e atualizar diretamente, sem loops e sem maluquices.