[gtkmm/use-dllexport: 2/60] Gdk, Gtk: Improve the drag-and-drop code and its documentation



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]