[gtkmm/use-dllexport: 2/60] Gdk, Gtk: Improve the drag-and-drop code and its documentation
- From: Chun-wei Fan <fanchunwei src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtkmm/use-dllexport: 2/60] Gdk, Gtk: Improve the drag-and-drop code and its documentation
- Date: Mon, 8 Jun 2020 02:42:09 +0000 (UTC)
commit 95984ef1518cf156917b697bc37754a827fdfb18
Author: Kjell Ahlstedt <kjellahlstedt gmail com>
Date: Mon Jan 20 18:56:01 2020 +0100
Gdk, Gtk: Improve the drag-and-drop code and its documentation
DnD in the icon browser demo works as the corresponding gtk demo.
demos/gtk-demo/example_iconbrowser.cc | 46 ++++++++++++++++-----
gdk/src/contentformats.hg | 7 ++++
gdk/src/contentprovider.ccg | 75 +++++++++++++++++++++++++++++++++++
gdk/src/contentprovider.hg | 67 ++++++++++++++++++++++++++++++-
gtk/src/dragsource.hg | 6 ++-
gtk/src/droptarget.hg | 8 +++-
gtk/src/gtk_docs_override.xml | 36 +++++++++++++++++
tools/m4/convert_gdk.m4 | 2 +
8 files changed, 232 insertions(+), 15 deletions(-)
---
diff --git a/demos/gtk-demo/example_iconbrowser.cc b/demos/gtk-demo/example_iconbrowser.cc
index dfda8216..ff1ffb77 100644
--- a/demos/gtk-demo/example_iconbrowser.cc
+++ b/demos/gtk-demo/example_iconbrowser.cc
@@ -92,8 +92,9 @@ public:
protected:
// Signal handler:
- void on_image_drag_data_get(const Glib::RefPtr<Gdk::Drag>& drag,
- Gtk::SelectionData& selection_data, int size_index);
+ void on_image_drag_begin(const Glib::RefPtr<Gdk::Drag>& drag, int size_index);
+
+ void on_image_get_texture(Glib::ValueBase& value, int size_index);
Glib::RefPtr<const Gdk::Texture> get_icon(int size_index);
@@ -108,6 +109,8 @@ protected:
Gtk::Label m_label[n_icon_sizes];
Gtk::Label m_description;
+ Glib::RefPtr<Gtk::DragSource> m_image_drag_source[n_icon_sizes];
+
}; // end DetailDialog
// Main window.
@@ -908,11 +911,14 @@ DetailDialog::DetailDialog(Gtk::Window& parent)
m_image[i].set_valign(Gtk::Align::END);
// Enable dragging an image, and copying it to another program.
- //??m_image[i].drag_source_set(
- //?? Gdk::ContentFormats::create(), Gdk::ModifierType::BUTTON1_MASK, Gdk::DragAction::COPY);
- //??m_image[i].drag_source_add_image_targets();
- //??m_image[i].signal_drag_data_get().connect(
- //?? sigc::bind(sigc::mem_fun(*this, &DetailDialog::on_image_drag_data_get), i));
+ m_image_drag_source[i] = Gtk::DragSource::create();
+ auto content = Gdk::ContentProvider::create(
+ Glib::Value<Glib::RefPtr<Gdk::Texture>>::value_type(),
+ sigc::bind(sigc::mem_fun(*this, &DetailDialog::on_image_get_texture), i));
+ m_image_drag_source[i]->set_content(content);
+ m_image_drag_source[i]->signal_drag_begin().connect(
+ sigc::bind(sigc::mem_fun(*this, &DetailDialog::on_image_drag_begin), i));
+ m_image[i].add_controller(m_image_drag_source[i]);
m_grid.attach(m_label[i], i, 1);
m_label[i].set_margin(4);
@@ -942,7 +948,6 @@ void DetailDialog::set_image(
{
m_image[i].set_from_icon_name(icon_name);
m_image[i].set_pixel_size(m_icon_size[i]);
- //??m_image[i].drag_source_set_icon(icon_name);
}
if (description.empty())
m_description.hide();
@@ -953,10 +958,29 @@ void DetailDialog::set_image(
}
}
-void DetailDialog::on_image_drag_data_get(const Glib::RefPtr<Gdk::Drag>& /* drag */,
- Gtk::SelectionData& selection_data, int size_index)
+void DetailDialog::on_image_drag_begin(const Glib::RefPtr<Gdk::Drag>& /* drag */, int size_index)
{
- selection_data.set_texture(get_icon(size_index));
+ std::cout << "on_image_drag_begin\n";
+ auto image_texture = get_icon(size_index);
+ if (image_texture)
+ {
+ const auto hot_x = image_texture->get_intrinsic_width();
+ const auto hot_y = image_texture->get_intrinsic_height();
+ m_image_drag_source[size_index]->set_icon(image_texture, hot_x, hot_y);
+ }
+}
+
+void DetailDialog::on_image_get_texture(Glib::ValueBase& value, int size_index)
+{
+ std::cout << "on_image_get_texture\n";
+ auto image_texture = get_icon(size_index);
+ if (image_texture)
+ {
+ Glib::Value<Glib::RefPtr<const Gdk::Texture>> texture_value;
+ texture_value.init(value.gobj());
+ texture_value.set(image_texture);
+ value = texture_value;
+ }
}
Glib::RefPtr<const Gdk::Texture> DetailDialog::get_icon(int size_index)
diff --git a/gdk/src/contentformats.hg b/gdk/src/contentformats.hg
index 0fb85169..0eb1da94 100644
--- a/gdk/src/contentformats.hg
+++ b/gdk/src/contentformats.hg
@@ -86,6 +86,13 @@ public:
*/
static Glib::RefPtr<ContentFormats> create(const Glib::ustring& mime_type);
+ /** Creates a new %Gdk::ContentFormats from a GType.
+ *
+ * @param type A GType.
+ * @return The new %Gdk::ContentFormats.
+ */
+ _WRAP_METHOD(static Glib::RefPtr<ContentFormats> create(GType type), gdk_content_formats_new_for_gtype)
+
/** Prints the given %ContentFormats into a string for human consumption.
* This is meant for debugging and logging.
*
diff --git a/gdk/src/contentprovider.ccg b/gdk/src/contentprovider.ccg
index 0586bd23..f00d24f8 100644
--- a/gdk/src/contentprovider.ccg
+++ b/gdk/src/contentprovider.ccg
@@ -14,3 +14,78 @@
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <giomm/slot_async.h>
+#include <giomm/outputstream.h>
+
+namespace
+{
+
+void ContentProvider_get_value_callback(GValue* value, void* data)
+{
+ auto the_slot = static_cast<Gdk::ContentProvider::SlotGetValue*>(data);
+ Glib::ValueBase cpp_value;
+ cpp_value.init(G_VALUE_TYPE(value));
+
+ try
+ {
+ (*the_slot)(cpp_value);
+ g_value_copy(cpp_value.gobj(), value);
+ }
+ catch (...)
+ {
+ Glib::exception_handlers_invoke();
+ }
+}
+
+GBytes* ContentProvider_get_bytes_callback(const char* mime_type, void* data)
+{
+ auto the_slot = static_cast<Gdk::ContentProvider::SlotGetBytes*>(data);
+
+ try
+ {
+ auto bytes = (*the_slot)(mime_type);
+ return Glib::unwrap_copy(bytes);
+ }
+ catch (...)
+ {
+ Glib::exception_handlers_invoke();
+ }
+ return nullptr;
+}
+
+} // anonymous namespace
+
+namespace Gdk
+{
+
+Glib::RefPtr<ContentProvider> ContentProvider::create(GType type, const SlotGetValue& slot)
+{
+ // Create a copy of the slot object. A pointer to this will be passed
+ // through the callback's data parameter. It will also be stored
+ // in m_slot_get_value. It will be deleted when *this is deleted.
+ auto slot_copy = new SlotGetValue(slot);
+
+ auto content_provider = Glib::wrap(gdk_content_provider_new_with_callback(
+ type, &ContentProvider_get_value_callback, slot_copy));
+
+ content_provider->m_slot_get_value.reset(slot_copy);
+ return content_provider;
+}
+
+Glib::RefPtr<ContentProvider> ContentProvider::create(
+ const Glib::RefPtr<const ContentFormats>& formats, const SlotGetBytes& slot)
+{
+ // Create a copy of the slot object. A pointer to this will be passed
+ // through the callback's data parameter. It will also be stored
+ // in m_slot_get_bytes. It will be deleted when *this is deleted.
+ auto slot_copy = new SlotGetBytes(slot);
+
+ auto content_provider = Glib::wrap(gdk_content_provider_new_with_formats(
+ const_cast<GdkContentFormats*>(Glib::unwrap(formats)),
+ &ContentProvider_get_bytes_callback, slot_copy));
+
+ content_provider->m_slot_get_bytes.reset(slot_copy);
+ return content_provider;
+}
+
+} //namespace Gdk
diff --git a/gdk/src/contentprovider.hg b/gdk/src/contentprovider.hg
index d3213ab0..2a7e9a09 100644
--- a/gdk/src/contentprovider.hg
+++ b/gdk/src/contentprovider.hg
@@ -20,10 +20,18 @@ _PINCLUDE(glibmm/private/object_p.h)
#include <glibmm/bytes.h>
#include <glibmm/object.h>
#include <glibmm/refptr.h>
+#include <giomm/asyncresult.h>
+#include <giomm/cancellable.h>
#include <gdkmm/contentformats.h>
+#include <memory>
_CC_INCLUDE(gdk/gdk.h)
+namespace Gio
+{
+class OutputStream;
+}
+
namespace Gdk
{
@@ -47,13 +55,55 @@ class ContentProvider : public Glib::Object
_CLASS_GOBJECT(ContentProvider, GdkContentProvider, GDK_CONTENT_PROVIDER, Glib::Object, GObject)
public:
- // _WRAP_METHOD is used for the create() methods because the gdk_content_provider_new_for_*()
+ /** Callback type for providing data in a Glib::ValueBase.
+ * For instance:
+ * @code
+ * void on_get_value(Glib::ValueBase& value);
+ * @endcode
+ *
+ * @param[in,out] value Where to store the provided value. @a value has been
+ * initialized to the GType the value should be provided in.
+ * This given GType does not need to be listed in the formats returned by
+ * ref_formats(). However, if the given GType is not supported,
+ * the operation can fail.
+ */
+ using SlotGetValue = sigc::slot<void(Glib::ValueBase&)>;
+
+ /** Callback type for providing data in a Glib::Bytes.
+ * For instance:
+ * @code
+ * Glib::RefPtr<Glib::Bytes> on_get_bytes(const Glib::ustring& mime_type);
+ * @endcode
+ *
+ * @param mime_type The mime type.
+ * @return A Glib::Bytes with the data for @a mime_type.
+ */
+ using SlotGetBytes = sigc::slot<Glib::RefPtr<Glib::Bytes>(const Glib::ustring&)>;
+
+ // _WRAP_METHOD is used for the create() methods because the gdk_content_provider_new_*()
// functions do more than call g_object_new().
_WRAP_METHOD(static Glib::RefPtr<ContentProvider> create(const Glib::ustring& mime_type,
const Glib::RefPtr<const Glib::Bytes>& bytes), gdk_content_provider_new_for_bytes)
_WRAP_METHOD(static Glib::RefPtr<ContentProvider> create(const Glib::ValueBase& value),
gdk_content_provider_new_for_value)
+ /** Create a content provider that provides data that is provided via a callback.
+ *
+ * @param type The type that the callback provides.
+ * @param slot Callback to populate a Glib::Value.
+ * @return A new Gdk::ContentProvider.
+ */
+ static Glib::RefPtr<ContentProvider> create(GType type, const SlotGetValue& slot);
+
+ /** Create a content provider that provides data that is provided via a callback.
+ *
+ * @param formats Formats to advertise.
+ * @param slot Callback to return a Glib::Bytes.
+ * @return A new Gdk::ContentProvider.
+ */
+ static Glib::RefPtr<ContentProvider> create(const Glib::RefPtr<const ContentFormats>& formats, const
SlotGetBytes& slot);
+ _IGNORE(gdk_content_provider_new_with_callback, gdk_content_provider_new_with_formats)
+
// ref_formats() and ref_storable_formats() are const because they return
// newly created ContentFormats instances.
_WRAP_METHOD(Glib::RefPtr<ContentFormats> ref_formats() const, gdk_content_provider_ref_formats)
@@ -61,6 +111,15 @@ public:
_WRAP_METHOD(void content_changed(), gdk_content_provider_content_changed)
+ _WRAP_METHOD(void write_mime_type_async(const Glib::ustring& mime_type,
+ const Glib::RefPtr<Gio::OutputStream>& stream, int io_priority,
+ const Gio::SlotAsyncReady& slot{callback}, const Glib::RefPtr<Gio::Cancellable>& cancellable{.?}) const,
+ gdk_content_provider_write_mime_type_async, slot_name slot, slot_callback
Gio::SignalProxy_async_callback)
+ _WRAP_METHOD(void write_mime_type_finish(const Glib::RefPtr<Gio::AsyncResult>& result) const,
+ gdk_content_provider_write_mime_type_finish, errthrow)
+
+ _WRAP_METHOD(void get_value(Glib::ValueBase& value) const, gdk_content_provider_get_value, errthrow)
+
_WRAP_PROPERTY("formats", Glib::RefPtr<ContentFormats>)
_WRAP_PROPERTY("storable-formats", Glib::RefPtr<ContentFormats>)
@@ -70,6 +129,12 @@ public:
// there is no constructor that calls g_object_new() to create a gtkmm__GdkContentProvider
// instance. A GdkContentProvider instance never calls an overriding vfunc in
// Gdk::ContentProvider.
+
+private:
+ // Pointers to copies of the slots. The slots shall be deleted
+ // when the ContentProvider is deleted.
+ std::unique_ptr<SlotGetValue> m_slot_get_value;
+ std::unique_ptr<SlotGetBytes> m_slot_get_bytes;
};
} // namespace Gdk
diff --git a/gtk/src/dragsource.hg b/gtk/src/dragsource.hg
index 8f7a723b..0b93ab9b 100644
--- a/gtk/src/dragsource.hg
+++ b/gtk/src/dragsource.hg
@@ -74,7 +74,7 @@ public:
_WRAP_METHOD(void set_actions(Gdk::DragAction actions), gtk_drag_source_set_actions)
_WRAP_METHOD(Gdk::DragAction get_actions() const, gtk_drag_source_get_actions)
- _WRAP_METHOD(void set_icon(const Glib::RefPtr<Gdk::Paintable>& paintable, int hot_x, int hot_y),
gtk_drag_source_set_icon)
+ _WRAP_METHOD(void set_icon(const Glib::RefPtr<const Gdk::Paintable>& paintable, int hot_x, int hot_y),
gtk_drag_source_set_icon)
_WRAP_METHOD(void drag_cancel(), gtk_drag_source_drag_cancel)
_WRAP_METHOD(Glib::RefPtr<Gdk::Drag> get_drag(), gtk_drag_source_get_drag, refreturn)
_WRAP_METHOD(Glib::RefPtr<const Gdk::Drag> get_drag() const, gtk_drag_source_get_drag, refreturn,
constversion)
@@ -85,6 +85,10 @@ public:
#m4 _CONVERSION(`Glib::RefPtr<Gdk::ContentProvider>',`GdkContentProvider*',`Glib::unwrap_copy($3)')
#m4 _CONVERSION(`GdkDrag*',`const Glib::RefPtr<Gdk::Drag>&',`Glib::wrap($3, true)')
// no_default_handler because GtkDragSourceClass is private.
+ /** Only one signal handler is called. If you connect a handler, it must be
+ * called before (instead of) the default handler, otherwise it won't be called.
+ * Set the @a after parameter in connect() to <tt>false</tt>.
+ */
_WRAP_SIGNAL(Glib::RefPtr<Gdk::ContentProvider> prepare(double x, double y), "prepare", no_default_handler)
_WRAP_SIGNAL(void drag_begin(const Glib::RefPtr<Gdk::Drag>& drag), "drag-begin", no_default_handler)
_WRAP_SIGNAL(void drag_end(const Glib::RefPtr<Gdk::Drag>& drag, bool delete_data), "drag-end",
no_default_handler)
diff --git a/gtk/src/droptarget.hg b/gtk/src/droptarget.hg
index 0a366932..b77b212f 100644
--- a/gtk/src/droptarget.hg
+++ b/gtk/src/droptarget.hg
@@ -47,7 +47,7 @@ namespace Gtk
* receive a signal_drag_enter(), followed by
* signal_drag_motion() signals as the pointer moves, and
* finally either a signal_drag_leave() when the pointer
- * move off the widget, or a signal_drag_drop() when a drop happens.
+ * moves off the widget, or a signal_drag_drop() when a drop happens.
*
* The signal_drag_enter() and signal_drag_motion() handler can call Gdk::Drop::status()
* to update the status of the ongoing operation. The signal_drag_drop() handler
@@ -71,7 +71,7 @@ protected:
public:
_WRAP_CREATE(const Glib::RefPtr<Gdk::ContentFormats>& formats, Gdk::DragAction actions)
- Glib::RefPtr<DropTarget> create(Gdk::DragAction actions);
+ static Glib::RefPtr<DropTarget> create(Gdk::DragAction actions);
_WRAP_METHOD(void set_formats(const Glib::RefPtr<Gdk::ContentFormats>& formats),
gtk_drop_target_set_formats)
_WRAP_METHOD(Glib::RefPtr<Gdk::ContentFormats> get_formats(), gtk_drop_target_get_formats, refreturn)
@@ -100,6 +100,10 @@ public:
#m4 _CONVERSION(`GdkDrop*',`const Glib::RefPtr<Gdk::Drop>&',`Glib::wrap($3, true)')
// no_default_handler because GtkDropTargetClass is private.
+ /** Only one signal handler is called. If you connect a handler, it must be
+ * called before (instead of) the default handler, otherwise it won't be called.
+ * Set the @a after parameter in connect() to <tt>false</tt>.
+ */
_WRAP_SIGNAL(bool accept(const Glib::RefPtr<Gdk::Drop>& drop), "accept", no_default_handler)
_WRAP_SIGNAL(void drag_enter(const Glib::RefPtr<Gdk::Drop>& drop), "drag-enter", no_default_handler)
_WRAP_SIGNAL(void drag_motion(const Glib::RefPtr<Gdk::Drop>& drop, int x, int y), "drag-motion",
no_default_handler)
diff --git a/gtk/src/gtk_docs_override.xml b/gtk/src/gtk_docs_override.xml
index a0f0a921..97e18c75 100644
--- a/gtk/src/gtk_docs_override.xml
+++ b/gtk/src/gtk_docs_override.xml
@@ -1531,5 +1531,41 @@ that cannot be undone.
<return></return>
</function>
+<!-- TODO: Remove this signal description when gtk's description contains -->
+<!-- the correct class name (not GtkWidget). -->
+<signal name="GtkDropTarget::accept">
+<description>
+The ::accept signal is emitted on the drop site when the user
+moves the cursor over the widget during a drag. The signal handler
+must determine whether the cursor position is in a drop zone or not.
+If it is not in a drop zone, it returns %FALSE and no further processing
+is necessary. Otherwise, the handler returns %TRUE. In this case, the
+handler is responsible for providing the necessary information for
+displaying feedback to the user, by calling gdk_drag_status().
+
+The default handler for this signal decides whether to accept the drop
+based on the type of the data.
+
+If the decision whether the drop will be accepted or rejected can't be
+made based solely the data format, handler may inspect the dragged data
+by calling one of the #GdkDrop read functions and return %TRUE to
+tentatively accept the drop. When the data arrives and is found to not be
+acceptable, a call to gtk_drop_target_deny_drop() should be made to reject
+the drop.
+</description>
+<parameters>
+<parameter name="dest">
+<parameter_description> the #GtkDropTarget
+</parameter_description>
+</parameter>
+<parameter name="drop">
+<parameter_description> the #GdkDrop
+</parameter_description>
+</parameter>
+</parameters>
+<return> whether the cursor position is in a drop zone
+</return>
+</signal>
+
</root>
diff --git a/tools/m4/convert_gdk.m4 b/tools/m4/convert_gdk.m4
index b119678a..c61af901 100644
--- a/tools/m4/convert_gdk.m4
+++ b/tools/m4/convert_gdk.m4
@@ -265,6 +265,8 @@ _CONVERSION(`const Glib::RefPtr<const ContentProvider>&',`GdkContentProvider*',_
_CONVERSION(`GdkContentProvider*',`Glib::RefPtr<ContentProvider>',`Glib::wrap($3)')
_CONVERSION(`GdkContentProvider*',`Glib::RefPtr<Gdk::ContentProvider>',`Glib::wrap($3)')
+_CONVERSION(`const Glib::RefPtr<Gio::OutputStream>&',`GOutputStream*',__CONVERT_REFPTR_TO_P)
+
# FrameClock, FrameTimings
_CONVERSION(`GdkFrameClock*',`Glib::RefPtr<FrameClock>',`Glib::wrap($3)')
_CONVERSION(`GdkFrameClock*',`Glib::RefPtr<Gdk::FrameClock>',`Glib::wrap($3)')
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]