From c2821dcea0d88828149c9a1ffe50544564250aa8 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 28 Feb 2016 18:03:35 +0100 Subject: [PATCH] Allow using arbitrary windows as wxDataViewCtrl editors in wxGTK Previously the editor created by wxDataViewRenderer::CreateEditorCtrl() had to be a native GTK+ widget implementing GtkCellEditable interface which prevented using composite windows (e.g. a container for a text control and a button) as editors. Create a helper container wrapping them now and implementing GtkCellEditable now to allow this. --- docs/changes.txt | 1 + include/wx/gtk/dvrenderer.h | 4 + include/wx/gtk/dvrenderers.h | 1 + src/gtk/dataview.cpp | 187 ++++++++++++++++++++++++++++++++++- 4 files changed, 189 insertions(+), 4 deletions(-) diff --git a/docs/changes.txt b/docs/changes.txt index 9f36db6ef1..6b47596bda 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -162,6 +162,7 @@ All (GUI): wxGTK: - Support building wxGTK3 under Windows (Kolya Kosenko). +- Implement support for non-wxTextCtrl custom editors in wxDataViewCtrl. - Fix vertical cell alignment in wxDataViewCtrl. - Fix clearing of wxComboBox with wxCB_READONLY (Chuddah). - Fix setting "pressed" bitmap for wxToggleButton (Kevin B. McCarty). diff --git a/include/wx/gtk/dvrenderer.h b/include/wx/gtk/dvrenderer.h index e885aa5873..c5d3d74a4e 100644 --- a/include/wx/gtk/dvrenderer.h +++ b/include/wx/gtk/dvrenderer.h @@ -60,6 +60,10 @@ public: // text virtual GtkCellRendererText *GtkGetTextRenderer() const { return NULL; } + // return the widget actually used by the renderer for editing, this may be + // different from the editor control widget for the custom renderers + virtual GtkWidget* GtkGetEditorWidget() const; + private: // Change the mode at GTK level without touching m_mode, this is useful for // temporarily making the renderer insensitive but does mean that GetMode() diff --git a/include/wx/gtk/dvrenderers.h b/include/wx/gtk/dvrenderers.h index 895d13753d..68e4d4a980 100644 --- a/include/wx/gtk/dvrenderers.h +++ b/include/wx/gtk/dvrenderers.h @@ -137,6 +137,7 @@ public: } virtual GtkCellRendererText *GtkGetTextRenderer() const wxOVERRIDE; + virtual GtkWidget* GtkGetEditorWidget() const wxOVERRIDE; private: bool Init(wxDataViewCellMode mode, int align); diff --git a/src/gtk/dataview.cpp b/src/gtk/dataview.cpp index 4860df13e4..19d9dfaeae 100644 --- a/src/gtk/dataview.cpp +++ b/src/gtk/dataview.cpp @@ -1143,6 +1143,168 @@ static GtkCellEditable *gtk_wx_cell_renderer_text_start_editing( return NULL; } +// ---------------------------------------------------------------------------- +// GTK+ class GtkWxCellEditorBin: needed only to implement GtkCellEditable for +// our editor widgets which don't necessarily implement it on their own. +// ---------------------------------------------------------------------------- + +enum +{ + CELL_EDITOR_BIN_PROP_0, + CELL_EDITOR_BIN_PROP_EDITING_CANCELED +}; + +extern "C" { + +#define GTK_TYPE_WX_CELL_EDITOR_BIN (gtk_wx_cell_editor_bin_get_type ()) +#define GTK_WX_CELL_EDITOR_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_WX_CELL_EDITOR_BIN, GtkWxCellEditorBin)) +#define GTK_IS_WX_CELL_EDITOR_BIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_WX_CELL_EDITOR_BIN)) +#define GTK_IS_WX_CELL_EDITOR_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_WX_CELL_EDITOR_BIN)) + +GType gtk_wx_cell_editor_bin_get_type(); + +struct GtkWxCellEditorBin +{ + GtkBin parent; + + // The actual user-created editor. + wxWindow* editor; +}; + +static GtkWidget* gtk_wx_cell_editor_bin_new(wxWindow* editor); +static void gtk_wx_cell_editor_bin_init(GTypeInstance* instance, void*); +static void gtk_wx_cell_editor_bin_class_init(void* klass, void*); +static void gtk_wx_cell_editor_bin_get_property(GObject*, guint, GValue*, GParamSpec*); +static void gtk_wx_cell_editor_bin_set_property(GObject*, guint, const GValue*, GParamSpec*); + +static void gtk_wx_cell_editor_bin_cell_editable_init(void* g_iface, void*); +static void gtk_wx_cell_editor_bin_cell_editable_start_editing( + GtkCellEditable *cell_editable, + GdkEvent *event); + +} + +GType +gtk_wx_cell_editor_bin_get_type() +{ + static GType cell_editor_bin_type = 0; + + if ( !cell_editor_bin_type ) + { + const GTypeInfo cell_editor_bin_info = + { + sizeof (GtkBinClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + gtk_wx_cell_editor_bin_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GtkWxCellEditorBin), + 0, /* n_preallocs */ + gtk_wx_cell_editor_bin_init, + NULL + }; + + cell_editor_bin_type = g_type_register_static( GTK_TYPE_BIN, + "GtkWxCellEditorBin", &cell_editor_bin_info, (GTypeFlags)0 ); + + + static const GInterfaceInfo cell_editable_iface_info = + { + gtk_wx_cell_editor_bin_cell_editable_init, + NULL, + NULL + }; + + g_type_add_interface_static (cell_editor_bin_type, + GTK_TYPE_CELL_EDITABLE, + &cell_editable_iface_info); + } + + return cell_editor_bin_type; +} + +static void +gtk_wx_cell_editor_bin_init(GTypeInstance* instance, void*) +{ + GtkWxCellEditorBin* const bin = GTK_WX_CELL_EDITOR_BIN(instance); + bin->editor = NULL; +} + +static void gtk_wx_cell_editor_bin_class_init(void* klass, void*) +{ + GObjectClass* const oclass = G_OBJECT_CLASS(klass); + + oclass->set_property = gtk_wx_cell_editor_bin_set_property; + oclass->get_property = gtk_wx_cell_editor_bin_get_property; + + g_object_class_override_property(oclass, + CELL_EDITOR_BIN_PROP_EDITING_CANCELED, + "editing-canceled"); +} + +// We need to provide these virtual methods as we must support the +// editing-canceled property, but they don't seem to be actually ever called, +// so it's not clear if we must implement them and how. For now just do +// nothing. + +static void +gtk_wx_cell_editor_bin_get_property(GObject*, guint, GValue*, GParamSpec*) +{ +} + +static void +gtk_wx_cell_editor_bin_set_property(GObject*, guint, const GValue*, GParamSpec*) +{ +} + +GtkWidget* +gtk_wx_cell_editor_bin_new(wxWindow* editor) +{ + if ( !editor ) + return NULL; + + GtkWxCellEditorBin* const + bin = (GtkWxCellEditorBin*)g_object_new (GTK_TYPE_WX_CELL_EDITOR_BIN, NULL); + + bin->editor = editor; + gtk_container_add(GTK_CONTAINER(bin), editor->m_widget); + + return GTK_WIDGET(bin); +} + +// GtkCellEditable interface implementation for GtkWxCellEditorBin + +static void +gtk_wx_cell_editor_bin_cell_editable_init(void* g_iface, void*) +{ + GtkCellEditableIface* iface = static_cast(g_iface); + iface->start_editing = gtk_wx_cell_editor_bin_cell_editable_start_editing; +} + +static void +gtk_wx_cell_editor_bin_cell_editable_start_editing(GtkCellEditable *cell_editable, + GdkEvent *event) +{ + GtkWxCellEditorBin* const bin = (GtkWxCellEditorBin *)cell_editable; + + // If we have an editable widget inside the editor, forward to it. + for ( wxWindow* win = bin->editor; win; ) + { + GtkWidget* const widget = win->m_widget; + if ( GTK_IS_CELL_EDITABLE(widget) ) + { + gtk_cell_editable_start_editing(GTK_CELL_EDITABLE(widget), event); + break; + } + + if ( win == bin->editor ) + win = win->GetChildren().front(); + else + win = win->GetNextSibling(); + } +} + //----------------------------------------------------------------------------- // define new GTK+ class GtkWxCellRenderer //----------------------------------------------------------------------------- @@ -1164,6 +1326,9 @@ struct _GtkWxCellRenderer /*< private >*/ wxDataViewCustomRenderer *cell; + + // Non null only while editing. + GtkWidget* editor_bin; }; static GtkCellRenderer *gtk_wx_cell_renderer_new (void); @@ -1245,6 +1410,7 @@ gtk_wx_cell_renderer_init(GTypeInstance* instance, void*) { GtkWxCellRenderer* cell = GTK_WX_CELL_RENDERER(instance); cell->cell = NULL; + cell->editor_bin = NULL; } static void @@ -1302,10 +1468,13 @@ static GtkCellEditable *gtk_wx_cell_renderer_start_editing( wxDataViewItem item(cell->GetOwner()->GetOwner()->GTKPathToItem(wxGtkTreePath(path))); - if (cell->StartEditing(item, renderrect)) - return GTK_CELL_EDITABLE(cell->GetEditorCtrl()->m_widget); + if (!cell->StartEditing(item, renderrect)) + return NULL; - return NULL; + wxrenderer->editor_bin = gtk_wx_cell_editor_bin_new(cell->GetEditorCtrl()); + gtk_widget_show(wxrenderer->editor_bin); + + return GTK_CELL_EDITABLE(wxrenderer->editor_bin); } static void @@ -1804,7 +1973,7 @@ bool wxDataViewRenderer::FinishEditing() { // remove editor widget before editor control is deleted, // to prevent several GTK warnings - gtk_cell_editable_remove_widget(GTK_CELL_EDITABLE(editorCtrl->m_widget)); + gtk_cell_editable_remove_widget(GTK_CELL_EDITABLE(GtkGetEditorWidget())); // delete editor control now, if it is deferred multiple erroneous // focus-out events will occur, causing debug warnings delete editorCtrl; @@ -1826,6 +1995,11 @@ void wxDataViewRenderer::GtkInitHandlers() } } +GtkWidget* wxDataViewRenderer::GtkGetEditorWidget() const +{ + return GetEditorCtrl()->m_widget; +} + void wxDataViewRenderer::SetMode( wxDataViewCellMode mode ) { m_mode = mode; @@ -2419,6 +2593,11 @@ GtkCellRendererText *wxDataViewCustomRenderer::GtkGetTextRenderer() const return m_text_renderer; } +GtkWidget* wxDataViewCustomRenderer::GtkGetEditorWidget() const +{ + return GTK_WX_CELL_RENDERER(m_renderer)->editor_bin; +} + void wxDataViewCustomRenderer::RenderText( const wxString &text, int xoffset, wxRect cell,