Search code examples
c++comboboxgtkgtkmmgtkmm3

Gtkmm: create a Gtk::ComboBox which lists Gtk::DrawingArea


I am trying to create a Gtk::ComboBox listing Gtk::DrawingArea widgets. I have followed this tutorial. So far, here is a minimal working example (i.e. which can be used to reproduce the issue) with Gtkmm3:

#include <gtkmm.h>

class NewPlayerRow : public Gtk::ListBoxRow
{

public:

    NewPlayerRow();

private:

    // Model for the combobox row: a disc with the appropriate player color...
    struct NewPlayerDiscColorComboRowModel : public Gtk::TreeModel::ColumnRecord
    {
        NewPlayerDiscColorComboRowModel()
        {
            add(m_discColorIcon);
        }

        Gtk::TreeModelColumn<Gtk::DrawingArea> m_discColorIcon;
    };

    NewPlayerDiscColorComboRowModel m_comboRowModel;
    Glib::RefPtr<Gtk::ListStore>    m_listStore;

    Gtk::ComboBox m_comboBox;
};

NewPlayerRow::NewPlayerRow()
{
    // First, create and register the TreeModel:
    m_listStore = Gtk::ListStore::create(m_comboRowModel);
    m_comboBox.set_model(m_listStore);

    // Then, populate the TreeModel:
    Gtk::TreeModel::Row row = *(m_listStore->append());
    row[m_comboRowModel.m_discColorIcon] = Gtk::DrawingArea();

    row = *(m_listStore->append());
    row[m_comboRowModel.m_discColorIcon] = Gtk::DrawingArea();

    // Add the model columns to the Combo:
    m_comboBox.pack_start(m_comboRowModel.m_discColorIcon);

    add(m_comboBox);
}

int main(int argc, char** argv)
{
    Glib::RefPtr<Gtk::Application> app{Gtk::Application::create(argc, argv, "com.github.bobmorane22.connectx")};

    NewPlayerRow np;
    Gtk::Window w;
    w.add(np);
    w.show_all();

    return app->run(w);
}

When I compile this, I get the following error:

In file included from /usr/include/glibmm-2.4/glibmm/value.h:196:0,
                 from /usr/include/glibmm-2.4/glibmm/propertyproxy_base.h:25,
                 from /usr/include/glibmm-2.4/glibmm/propertyproxy.h:25,
                 from /usr/include/glibmm-2.4/glibmm/objectbase.h:24,
                 from /usr/include/glibmm-2.4/glibmm/object.h:29,
                 from /usr/include/pangomm-1.4/pangomm/context.h:32,
                 from /usr/include/gtkmm-3.0/gtkmm/widget.h:32,
                 from /usr/include/gtkmm-3.0/gtkmm/actiongroup.h:29,
                 from /usr/include/gtkmm-3.0/gtkmm/application.h:32,
                 from src/main.cpp:32:
/usr/include/glibmm-2.4/glibmm/value_custom.h: In instantiation of ‘static void Glib::Value<T>::value_copy_func(const GValue*, GValue*) [with T = Gtk::DrawingArea; GValue = _GValue]’:
/usr/include/glibmm-2.4/glibmm/value_custom.h:257:9:   required from ‘static GType Glib::Value<T>::value_type() [with T = Gtk::DrawingArea; GType = long unsigned int]’
/usr/include/gtkmm-3.0/gtkmm/treemodelcolumn.h:134:64:   required from ‘Gtk::TreeModelColumn<T>::TreeModelColumn() [with T = Gtk::DrawingArea]’
src/main.cpp:50:9:   required from here
/usr/include/glibmm-2.4/glibmm/value_custom.h:283:33: error: use of deleted function ‘Gtk::DrawingArea::DrawingArea(const Gtk::DrawingArea&)’
   dest_value->data[0].v_pointer = new(std::nothrow) T(source);
                                 ^
In file included from /home/morane/Programming/cpp/ConnectX/cxgui/include/GeometricShape.h:35:0,
                 from /home/morane/Programming/cpp/ConnectX/cxgui/include/Disc.h:35,
                 from src/../include/../include/CXDisc.h:35,
                 from src/../include/../include/GBDisc.h:37,
                 from src/../include/GameBoard.h:41,
                 from src/../include/GameWindow.h:17,
                 from src/main.cpp:34:
/usr/include/gtkmm-3.0/gtkmm/drawingarea.h:64:3: note: declared here
   DrawingArea(const DrawingArea&) = delete;

which seems to indicate that the type in the combobox row model must be copyable for it to work. I have tried replacing the type Gtk::DrawingArea with std::string (which is copyable) in the above code and it builds fine and runs fine as well. I can see the combo box with its text rows.

Is there a way around this? I would really like to create a combo box which lists drawing areas.


EDIT Diving a little bit deeper in the error, I found that the issue is coming from the file value_custom.h in Glibmm. The following two functions seem to case the issue, since they try to access the copy member operation for the templated type (in my case Gtk::DrawingArea, which is not copyable, as mentionned above).

// static
template <class T>
GType Value<T>::value_type()
{
  if(!custom_type_)
  {
    custom_type_ = Glib::custom_boxed_type_register(
        typeid(CppType).name(),
        &Value<T>::value_init_func,
        &Value<T>::value_free_func,
        &Value<T>::value_copy_func);
  }
  return custom_type_;
}

// static
template <class T>
void Value<T>::value_copy_func(const GValue* src_value, GValue* dest_value)
{
  // Assume the source is not NULL.  See value_init_func().
  const T& source = *static_cast<T*>(src_value->data[0].v_pointer);
  dest_value->data[0].v_pointer = new(std::nothrow) T(source);
}

I'm starting to feel like there is no way around this issue... The documentation for Glib::Value<T> even mentions that the type T has to implement copy assignment/construction.

If you have an idea, I'm all ears.


Solution

  • After more research, I have come to the conclusion that Gtk::ComboBoxes are simply not designed to hold widgets (hence Gtk::DrawingAreas) because is uses a Gtk::TreeModelColumn<T> in its TreeModel, where T needs to be copyable.

    In other words, the types that compose your combobox model (ie. the types of what it actually lists when you click on it) must be copyable otherwise the framework won't let your code compile.

    At first, the fact that widgets could not be copied made no sense to me, but after some research, I found this article, which clearly explains some of the (tough) issues that one might face when using copyable widgets.

    In conclusion, it seems I am trying to accomplish something that would, in retrospective, be a bit weird UI-wise anyway and I will try to find some cleaner way to express my intent.