[gtk+/wip/attachment-parameters-3: 3/27] gdkattachmentparameters: add GdkAttachmentParameters



commit a9331e950c7ce216be42352b2c100bb7c10e4c56
Author: William Hua <william hua canonical com>
Date:   Fri Sep 18 13:40:36 2015 -0400

    gdkattachmentparameters: add GdkAttachmentParameters

 docs/reference/gdk/gdk-docs.sgml     |    1 +
 docs/reference/gdk/gdk3-sections.txt |   29 +
 gdk/Makefile.am                      |    3 +
 gdk/gdk.h                            |    1 +
 gdk/gdkattachinfo.c                  |  961 ++++++++++++++++++++++++++++++++++
 gdk/gdkattachinfo.h                  |  166 ++++++
 gdk/gdkattachinfoprivate.h           |   81 +++
 7 files changed, 1242 insertions(+), 0 deletions(-)
---
diff --git a/docs/reference/gdk/gdk-docs.sgml b/docs/reference/gdk/gdk-docs.sgml
index bdd7a7a..f55e956 100644
--- a/docs/reference/gdk/gdk-docs.sgml
+++ b/docs/reference/gdk/gdk-docs.sgml
@@ -46,6 +46,7 @@
     <xi:include href="xml/wayland_interaction.xml" />
     <xi:include href="xml/gdkapplaunchcontext.xml" />
     <xi:include href="xml/gdktestutils.xml" />
+    <xi:include href="xml/gdkattachmentparameters.xml" />
   </reference>
 
   <reference>
diff --git a/docs/reference/gdk/gdk3-sections.txt b/docs/reference/gdk/gdk3-sections.txt
index 100aacf..ae46c3f 100644
--- a/docs/reference/gdk/gdk3-sections.txt
+++ b/docs/reference/gdk/gdk3-sections.txt
@@ -546,6 +546,35 @@ gdk_fullscreen_mode_get_type
 </SECTION>
 
 <SECTION>
+<TITLE>Attachment Parameters</TITLE>
+<FILE>gdkattachmentparameters</FILE>
+GdkAttachmentParameters
+GdkAttachRule
+GdkAttachmentPositionCallback
+gdk_attachment_parameters_new
+gdk_attachment_parameters_copy
+gdk_attachment_parameters_free
+gdk_attachment_parameters_set_attachment_origin
+gdk_attachment_parameters_set_attachment_rectangle
+gdk_attachment_parameters_set_attachment_margin
+gdk_attachment_parameters_set_window_margin
+gdk_attachment_parameters_set_window_padding
+gdk_attachment_parameters_set_window_offset
+gdk_attachment_parameters_get_window_type_hint
+gdk_attachment_parameters_set_window_type_hint
+gdk_attachment_parameters_set_right_to_left
+gdk_attachment_parameters_add_primary_options
+gdk_attachment_parameters_add_secondary_options
+gdk_attachment_parameters_set_position_callback
+
+<SUBSECTION Private>
+_GdkAttachmentParameters
+gdk_attachment_parameters_choose_position
+gdk_attachment_parameters_choose_position_for_window
+gdk_window_move_using_attachment_parameters
+</SECTION>
+
+<SECTION>
 <TITLE>Selections</TITLE>
 <FILE>selections</FILE>
 GDK_SELECTION_PRIMARY
diff --git a/gdk/Makefile.am b/gdk/Makefile.am
index dea10d1..c32404b 100644
--- a/gdk/Makefile.am
+++ b/gdk/Makefile.am
@@ -63,6 +63,7 @@ gdk_public_h_sources =                                \
        gdk.h                                   \
        gdk-autocleanup.h                       \
        gdkapplaunchcontext.h                   \
+       gdkattachmentparameters.h               \
        gdkcairo.h                              \
        gdkcursor.h                             \
        gdkdevice.h                             \
@@ -102,6 +103,7 @@ gdk_h_sources =                                     \
 gdk_private_headers =                          \
        gdk-private.h                           \
        gdkapplaunchcontextprivate.h            \
+       gdkattachmentparametersprivate.h        \
        gdkcursorprivate.h                      \
        gdkdevicemanagerprivate.h               \
        gdkdeviceprivate.h                      \
@@ -126,6 +128,7 @@ gdk_c_sources =                             \
        gdk-private.c                           \
        gdk.c                                   \
        gdkapplaunchcontext.c                   \
+       gdkattachmentparameters.c               \
        gdkcairo.c                              \
        gdkcursor.c                             \
        gdkdeprecated.c                         \
diff --git a/gdk/gdk.h b/gdk/gdk.h
index 65246d7..d88bfb9 100644
--- a/gdk/gdk.h
+++ b/gdk/gdk.h
@@ -30,6 +30,7 @@
 #include <gdk/gdkconfig.h>
 #include <gdk/gdkversionmacros.h>
 #include <gdk/gdkapplaunchcontext.h>
+#include <gdk/gdkattachmentparameters.h>
 #include <gdk/gdkcairo.h>
 #include <gdk/gdkcursor.h>
 #include <gdk/gdkdevice.h>
diff --git a/gdk/gdkattachinfo.c b/gdk/gdkattachinfo.c
new file mode 100644
index 0000000..8d764e7
--- /dev/null
+++ b/gdk/gdkattachinfo.c
@@ -0,0 +1,961 @@
+#include "config.h"
+
+#include "gdkattachmentparameters.h"
+#include "gdkattachmentparametersprivate.h"
+#include "gdkscreen.h"
+#include "gdkwindow.h"
+
+/**
+ * SECTION: gdkattachmentparameters
+ * @section_id: gdkattachmentparameters
+ * @title: Attachment Parameters
+ * @short_description: Describing relative window position
+ * @stability: Unstable
+ * @include: gdk/gdkattachmentparameters.h
+ *
+ * A full description of how a window should be positioned relative to an
+ * attachment rectangle.
+ *
+ * Since: 3.20
+ */
+
+/**
+ * gdk_attachment_parameters_new:
+ *
+ * Creates a new #GdkAttachmentParameters for describing the position of a
+ * #GdkWindow relative to an attachment #GdkRectangle.
+ *
+ * Returns: (transfer full): a new #GdkAttachmentParameters, to be freed with
+ *          gdk_attachment_parameters_free()
+ *
+ * Since: 3.20
+ */
+GdkAttachmentParameters *
+gdk_attachment_parameters_new (void)
+{
+  GdkAttachmentParameters *parameters = g_new0 (GdkAttachmentParameters, 1);
+
+  parameters->primary_options = g_array_new (TRUE, TRUE, sizeof (GdkAttachRule));
+  parameters->secondary_options = g_array_new (TRUE, TRUE, sizeof (GdkAttachRule));
+
+  return parameters;
+}
+
+/**
+ * gdk_attachment_parameters_copy:
+ * @src: the #GdkAttachmentParameters to copy
+ * @data: (nullable): unused
+ *
+ * Creates a deep copy of @src.
+ *
+ * Returns: (transfer full) (nullable): a deep copy of @src, to be freed with
+ *          gdk_attachment_parameters_free()
+ *
+ * Since: 3.20
+ */
+gpointer
+gdk_attachment_parameters_copy (gconstpointer src,
+                                gpointer      data)
+{
+  GdkAttachmentParameters *copy;
+  const GdkAttachmentParameters *parameters;
+
+  if (!src)
+    return NULL;
+
+  parameters = src;
+
+  copy = g_memdup (parameters, sizeof (*parameters));
+
+  copy->primary_options = g_array_sized_new (TRUE, TRUE, sizeof (GdkAttachRule),
+                                             parameters->primary_options->len);
+
+  g_array_append_vals (copy->primary_options,
+                       parameters->primary_options->data,
+                       parameters->primary_options->len);
+
+  copy->secondary_options = g_array_sized_new (TRUE, TRUE, sizeof (GdkAttachRule),
+                                               parameters->secondary_options->len);
+
+  g_array_append_vals (copy->secondary_options,
+                       parameters->secondary_options->data,
+                       parameters->secondary_options->len);
+
+  return copy;
+}
+
+/**
+ * gdk_attachment_parameters_free:
+ * @data: the #GdkAttachmentParameters to free
+ *
+ * Releases @data.
+ *
+ * Since: 3.20
+ */
+void
+gdk_attachment_parameters_free (gpointer data)
+{
+  GdkAttachmentParameters *parameters;
+
+  g_return_if_fail (data);
+
+  parameters = data;
+
+  if (parameters->position_callback_user_data && parameters->position_callback_destroy_notify)
+    parameters->position_callback_destroy_notify (parameters->position_callback_user_data);
+
+  g_array_unref (parameters->secondary_options);
+  g_array_unref (parameters->primary_options);
+
+  g_free (parameters);
+}
+
+/**
+ * gdk_attachment_parameters_set_attachment_origin:
+ * @parameters: a #GdkAttachmentParameters
+ * @origin: (nullable): the attachment rectangle's origin
+ *
+ * Sets the origin of the attachment rectangle's coordinate system in root
+ * coordinates.
+ *
+ * Since: 3.20
+ */
+void
+gdk_attachment_parameters_set_attachment_origin (GdkAttachmentParameters *parameters,
+                                                 const GdkPoint          *origin)
+{
+  GdkPoint zero = { 0 };
+
+  g_return_if_fail (parameters);
+
+  parameters->attachment_origin = origin ? *origin : zero;
+}
+
+/**
+ * gdk_attachment_parameters_get_attachment_rectangle:
+ * @parameters: a #GdkAttachmentParameters
+ *
+ * Gets the attachment rectangle the window needs to be aligned relative to.
+ *
+ * Returns: (nullable): the attachment rectangle the window needs to be aligned
+ *          relative to
+ *
+ * Since: 3.20
+ */
+const GdkRectangle *
+gdk_attachment_parameters_get_attachment_rectangle (const GdkAttachmentParameters *parameters)
+{
+  g_return_val_if_fail (parameters, NULL);
+
+  return parameters->has_attachment_rectangle ? &parameters->attachment_rectangle : NULL;
+}
+
+/**
+ * gdk_attachment_parameters_set_attachment_rectangle:
+ * @parameters: a #GdkAttachmentParameters
+ * @rectangle: (nullable): the attachment rectangle
+ *
+ * Sets the attachment rectangle the window needs to be aligned relative to.
+ *
+ * Since: 3.20
+ */
+void
+gdk_attachment_parameters_set_attachment_rectangle (GdkAttachmentParameters *parameters,
+                                                    const GdkRectangle      *rectangle)
+{
+  g_return_if_fail (parameters);
+
+  if (rectangle)
+    {
+      parameters->has_attachment_rectangle = TRUE;
+      parameters->attachment_rectangle = *rectangle;
+    }
+  else
+    parameters->has_attachment_rectangle = FALSE;
+}
+
+/**
+ * gdk_attachment_parameters_set_attachment_margin:
+ * @parameters: a #GdkAttachmentParameters
+ * @margin: (nullable): the space around the attachment rectangle
+ *
+ * Sets the amount of space to leave around the attachment rectangle.
+ *
+ * Since: 3.20
+ */
+void
+gdk_attachment_parameters_set_attachment_margin (GdkAttachmentParameters *parameters,
+                                                 const GdkBorder         *margin)
+{
+  GdkBorder zero = { 0 };
+
+  g_return_if_fail (parameters);
+
+  parameters->attachment_margin = margin ? *margin : zero;
+}
+
+/**
+ * gdk_attachment_parameters_set_window_margin:
+ * @parameters: a #GdkAttachmentParameters
+ * @margin: (nullable): the space around the window
+ *
+ * Sets the amount of space to leave around the window.
+ *
+ * Since: 3.20
+ */
+void
+gdk_attachment_parameters_set_window_margin (GdkAttachmentParameters *parameters,
+                                             const GdkBorder         *margin)
+{
+  GdkBorder zero = { 0 };
+
+  g_return_if_fail (parameters);
+
+  parameters->window_margin = margin ? *margin : zero;
+}
+
+/**
+ * gdk_attachment_parameters_set_window_padding:
+ * @parameters: a #GdkAttachmentParameters
+ * @padding: (nullable): the space between the window and its
+ *           contents.
+ *
+ * Sets the amount of space between the window and its contents.
+ *
+ * Since: 3.20
+ */
+void
+gdk_attachment_parameters_set_window_padding (GdkAttachmentParameters *parameters,
+                                              const GdkBorder         *padding)
+{
+  GdkBorder zero = { 0 };
+
+  g_return_if_fail (parameters);
+
+  parameters->window_padding = padding ? *padding : zero;
+}
+
+/**
+ * gdk_attachment_parameters_set_window_offset:
+ * @parameters: a #GdkAttachmentParameters
+ * @offset: (nullable): the window displacement
+ *
+ * Sets the offset to displace the window by.
+ *
+ * Since: 3.20
+ */
+void
+gdk_attachment_parameters_set_window_offset (GdkAttachmentParameters *parameters,
+                                             const GdkPoint          *offset)
+{
+  GdkPoint zero = { 0 };
+
+  g_return_if_fail (parameters);
+
+  parameters->window_offset = offset ? *offset : zero;
+}
+
+/**
+ * gdk_attachment_parameters_get_window_type_hint:
+ * @parameters: a #GdkAttachmentParameters
+ *
+ * Gets the window type hint set on @parameters.
+ *
+ * Returns: the window type hint set on @parameters
+ *
+ * Since: 3.20
+ */
+GdkWindowTypeHint
+gdk_attachment_parameters_get_window_type_hint (const GdkAttachmentParameters *parameters)
+{
+  g_return_val_if_fail (parameters, GDK_WINDOW_TYPE_HINT_NORMAL);
+
+  return parameters->window_type_hint;
+}
+
+/**
+ * gdk_attachment_parameters_set_window_type_hint:
+ * @parameters: a #GdkAttachmentParameters
+ * @window_type_hint: the window type hint
+ *
+ * Sets the window type hint.
+ *
+ * Since: 3.20
+ */
+void
+gdk_attachment_parameters_set_window_type_hint (GdkAttachmentParameters *parameters,
+                                                GdkWindowTypeHint        window_type_hint)
+{
+  g_return_if_fail (parameters);
+
+  parameters->window_type_hint = window_type_hint;
+}
+
+/**
+ * gdk_attachment_parameters_set_right_to_left:
+ * @parameters: a #GdkAttachmentParameters
+ * @is_right_to_left: %TRUE if the text direction is right to left
+ *
+ * Sets the window text direction.
+ *
+ * Since: 3.20
+ */
+void
+gdk_attachment_parameters_set_right_to_left (GdkAttachmentParameters *parameters,
+                                             gboolean                 is_right_to_left)
+{
+  g_return_if_fail (parameters);
+
+  parameters->is_right_to_left = is_right_to_left;
+}
+
+static void
+add_options (GArray        *array,
+             GdkAttachRule  first_option,
+             va_list        args)
+{
+  GdkAttachRule option;
+
+  for (option = first_option; option; option = va_arg (args, GdkAttachRule))
+    g_array_append_val (array, option);
+}
+
+/**
+ * gdk_attachment_parameters_add_primary_options_valist:
+ * @parameters: a #GdkAttachmentParameters
+ * @first_option: first primary option
+ * @args: a #va_list of the remaining primary options
+ *
+ * Non-variadic version of gdk_attachment_parameters_add_primary_options().
+ *
+ * Since: 3.20
+ */
+void
+gdk_attachment_parameters_add_primary_options_valist (GdkAttachmentParameters *parameters,
+                                                      GdkAttachRule            first_option,
+                                                      va_list                  args)
+{
+  add_options (parameters->primary_options, first_option, args);
+}
+
+/**
+ * gdk_attachment_parameters_add_secondary_options_valist:
+ * @parameters: a #GdkAttachmentParameters
+ * @first_option: first secondary option
+ * @args: a #va_list of the remaining secondary options
+ *
+ * Non-variadic version of gdk_attachment_parameters_add_secondary_options().
+ *
+ * Since: 3.20
+ */
+void
+gdk_attachment_parameters_add_secondary_options_valist (GdkAttachmentParameters *parameters,
+                                                        GdkAttachRule            first_option,
+                                                        va_list                  args)
+{
+  add_options (parameters->secondary_options, first_option, args);
+}
+
+/**
+ * gdk_attachment_parameters_add_primary_options:
+ * @parameters: a #GdkAttachmentParameters
+ * @first_option: first primary option
+ * @...: a %NULL-terminated list of options
+ *
+ * Appends to the list of primary positioning options to try.
+ *
+ * A typical backend will try each primary option in the order they're added.
+ * If an option can be satisfied, it will then try each secondary option until
+ * it finds a satisfiable secondary option that doesn't conflict with the
+ * primary option. If it finds a pair of satisfiable non-conflicting options,
+ * then it will place the window there. If it cannot find a pair, it proceeds
+ * to the next primary option and tries again.
+ *
+ * Since: 3.20
+ */
+void
+gdk_attachment_parameters_add_primary_options (GdkAttachmentParameters *parameters,
+                                               GdkAttachRule            first_option,
+                                               ...)
+{
+  va_list args;
+
+  g_return_if_fail (parameters);
+
+  va_start (args, first_option);
+
+  gdk_attachment_parameters_add_primary_options_valist (parameters, first_option, args);
+
+  va_end (args);
+}
+
+/**
+ * gdk_attachment_parameters_add_secondary_options:
+ * @parameters: a #GdkAttachmentParameters
+ * @first_option: first secondary option
+ * @...: a %NULL-terminated list of options
+ *
+ * Appends to the list of secondary positioning options to try.
+ *
+ * A typical backend will try each primary option in the order they're added.
+ * If an option can be satisfied, it will then try each secondary option until
+ * it finds a satisfiable secondary option that doesn't conflict with the
+ * primary option. If it finds a pair of satisfiable non-conflicting options,
+ * then it will place the window there. If it cannot find a pair, it proceeds
+ * to the next primary option and tries again.
+ *
+ * Since: 3.20
+ */
+void
+gdk_attachment_parameters_add_secondary_options (GdkAttachmentParameters *parameters,
+                                                 GdkAttachRule            first_option,
+                                                 ...)
+{
+  va_list args;
+
+  g_return_if_fail (parameters);
+
+  va_start (args, first_option);
+
+  gdk_attachment_parameters_add_secondary_options_valist (parameters, first_option, args);
+
+  va_end (args);
+}
+
+/**
+ * gdk_attachment_parameters_set_position_callback:
+ * @parameters: a #GdkAttachmentParameters
+ * @callback: (nullable): a function to be called when the final position of
+ *            the window is known
+ * @user_data: (transfer full) (nullable): additional data to pass to @callback
+ * @destroy_notify: (nullable): a function to release @user_data
+ *
+ * Sets the function to be called when the final position of the window is
+ * known. Since the position might be determined asynchronously, don't assume
+ * it will be called directly from gdk_window_set_attachment_parameters().
+ *
+ * Since: 3.20
+ */
+void
+gdk_attachment_parameters_set_position_callback (GdkAttachmentParameters       *parameters,
+                                                 GdkAttachmentPositionCallback  callback,
+                                                 gpointer                       user_data,
+                                                 GDestroyNotify                 destroy_notify)
+{
+  g_return_if_fail (parameters);
+
+  parameters->position_callback = callback;
+
+  if (user_data != parameters->position_callback_user_data)
+    {
+      if (parameters->position_callback_user_data && parameters->position_callback_destroy_notify)
+        parameters->position_callback_destroy_notify (parameters->position_callback_user_data);
+
+      parameters->position_callback_user_data = user_data;
+      parameters->position_callback_destroy_notify = destroy_notify;
+    }
+  else if (user_data)
+    g_warning ("%s (): parameters already owns user data", G_STRFUNC);
+}
+
+static gboolean
+is_satisfiable (GdkAttachRule                  option,
+                const GdkAttachmentParameters *parameters,
+                gint                           width,
+                gint                           height,
+                const GdkRectangle            *bounds,
+                gint                          *value)
+{
+  gboolean use_rect_margin;
+  gboolean use_window_margin;
+  gboolean use_window_padding;
+
+  if (parameters->is_right_to_left && (option & GDK_ATTACH_FLIP_IF_RTL))
+    {
+      switch (option & GDK_ATTACH_RECT_MASK)
+        {
+        case GDK_ATTACH_RECT_MIN:
+          option = (option & ~(GDK_ATTACH_RECT_MASK | GDK_ATTACH_FLIP_IF_RTL)) | GDK_ATTACH_RECT_MAX;
+          break;
+
+        case GDK_ATTACH_RECT_MAX:
+          option = (option & ~(GDK_ATTACH_RECT_MASK | GDK_ATTACH_FLIP_IF_RTL)) | GDK_ATTACH_RECT_MIN;
+          break;
+        }
+
+      switch (option & GDK_ATTACH_WINDOW_MASK)
+        {
+        case GDK_ATTACH_WINDOW_MIN:
+          option = (option & ~(GDK_ATTACH_WINDOW_MASK | GDK_ATTACH_FLIP_IF_RTL)) | GDK_ATTACH_WINDOW_MAX;
+          break;
+
+        case GDK_ATTACH_WINDOW_MAX:
+          option = (option & ~(GDK_ATTACH_WINDOW_MASK | GDK_ATTACH_FLIP_IF_RTL)) | GDK_ATTACH_WINDOW_MIN;
+          break;
+        }
+    }
+
+  use_rect_margin = (((option & GDK_ATTACH_RECT_MASK)   == GDK_ATTACH_RECT_MIN &&
+                      (option & GDK_ATTACH_WINDOW_MASK) == GDK_ATTACH_WINDOW_MAX) ||
+                     ((option & GDK_ATTACH_RECT_MASK)   == GDK_ATTACH_RECT_MAX &&
+                      (option & GDK_ATTACH_WINDOW_MASK) == GDK_ATTACH_WINDOW_MIN));
+
+  use_window_margin = use_rect_margin;
+
+  use_window_padding = TRUE;
+
+  switch (option & GDK_ATTACH_AXIS_MASK)
+    {
+    case GDK_ATTACH_AXIS_X:
+      *value = parameters->attachment_origin.x;
+
+      switch (option & GDK_ATTACH_RECT_MASK)
+        {
+        case GDK_ATTACH_RECT_MIN:
+          *value += parameters->attachment_rectangle.x;
+
+          if (use_rect_margin)
+            *value -= parameters->attachment_margin.left;
+
+          break;
+
+        case GDK_ATTACH_RECT_MID:
+          *value += parameters->attachment_rectangle.x + parameters->attachment_rectangle.width / 2;
+          break;
+
+        case GDK_ATTACH_RECT_MAX:
+          *value += parameters->attachment_rectangle.x + parameters->attachment_rectangle.width;
+
+          if (use_rect_margin)
+            *value += parameters->attachment_margin.right;
+
+          break;
+        }
+
+      switch (option & GDK_ATTACH_WINDOW_MASK)
+        {
+        case GDK_ATTACH_WINDOW_MIN:
+          if (use_window_margin)
+            *value += parameters->window_margin.left;
+
+          if (use_window_padding)
+            *value -= parameters->window_margin.left;
+
+          break;
+
+        case GDK_ATTACH_WINDOW_MID:
+          *value -= width / 2;
+          break;
+
+        case GDK_ATTACH_WINDOW_MAX:
+          *value -= width;
+
+          if (use_window_margin)
+            *value -= parameters->window_margin.right;
+
+          if (use_window_padding)
+            *value += parameters->window_margin.right;
+
+          break;
+        }
+
+      *value += parameters->window_offset.x;
+
+      return !bounds || (bounds->x <= *value && *value + width <= bounds->x + bounds->width);
+
+    case GDK_ATTACH_AXIS_Y:
+      *value = parameters->attachment_origin.y;
+
+      switch (option & GDK_ATTACH_RECT_MASK)
+        {
+        case GDK_ATTACH_RECT_MIN:
+          *value += parameters->attachment_rectangle.y;
+
+          if (use_rect_margin)
+            *value -= parameters->attachment_margin.top;
+
+          break;
+
+        case GDK_ATTACH_RECT_MID:
+          *value += parameters->attachment_rectangle.y + parameters->attachment_rectangle.height / 2;
+          break;
+
+        case GDK_ATTACH_RECT_MAX:
+          *value += parameters->attachment_rectangle.y + parameters->attachment_rectangle.height;
+
+          if (use_rect_margin)
+            *value += parameters->attachment_margin.bottom;
+
+          break;
+        }
+
+      switch (option & GDK_ATTACH_WINDOW_MASK)
+        {
+        case GDK_ATTACH_WINDOW_MIN:
+          if (use_window_margin)
+            *value += parameters->window_margin.top;
+
+          if (use_window_padding)
+            *value -= parameters->window_margin.top;
+
+          break;
+
+        case GDK_ATTACH_WINDOW_MID:
+          *value -= height / 2;
+          break;
+
+        case GDK_ATTACH_WINDOW_MAX:
+          *value -= height;
+
+          if (use_window_margin)
+            *value -= parameters->window_margin.bottom;
+
+          if (use_window_padding)
+            *value += parameters->window_margin.bottom;
+
+          break;
+        }
+
+      *value += parameters->window_offset.y;
+
+      return !bounds || (bounds->y <= *value && *value + height <= bounds->y + bounds->height);
+    }
+
+  return FALSE;
+}
+
+#define BEST      0
+#define GOOD      1
+#define PRIMARY   0
+#define SECONDARY 1
+#define X         0
+#define Y         1
+
+/**
+ * gdk_attachment_parameters_choose_position:
+ * @parameters: a #GdkAttachmentParameters
+ * @width: window width
+ * @height: window height
+ * @bounds: (nullable): monitor geometry
+ * @position: (out) (optional): the best position for the window
+ * @offset: (out) (optional): the displacement needed to push the window
+ *          on-screen
+ * @primary_option: (out) (optional): the best primary option
+ * @secondary_option: (out) (optional): the best secondary option
+ *
+ * Finds the best position for a window of size @width and @height on a screen
+ * with @bounds using the given @parameters.
+ *
+ * Returns: %TRUE if there is a pair of satisfiable primary and secondary
+ *          options that do not conflict with each other
+ *
+ * Since: 3.20
+ */
+gboolean
+gdk_attachment_parameters_choose_position (const GdkAttachmentParameters *parameters,
+                                           gint                           width,
+                                           gint                           height,
+                                           const GdkRectangle            *bounds,
+                                           GdkPoint                      *position,
+                                           GdkPoint                      *offset,
+                                           GdkAttachRule                 *primary_option,
+                                           GdkAttachRule                 *secondary_option)
+{
+  GdkPoint p;
+  GdkPoint o;
+  GdkAttachRule pf;
+  GdkAttachRule sf;
+  GdkAttachRule options[2][2][2] = { 0 };
+  gint axes[2][2] = { 0 };
+  gint values[2][2][2] = { 0 };
+  GArray *arrays[2];
+  gint i;
+  gint j;
+  gint k;
+  GdkAttachRule option;
+  gint axis;
+  gint value;
+  gboolean satisfiable;
+  gboolean success;
+  GdkAttachRule x_option;
+  GdkAttachRule y_option;
+
+  g_return_val_if_fail (parameters, FALSE);
+  g_return_val_if_fail (parameters->has_attachment_rectangle, FALSE);
+
+  if (!position)
+    position = &p;
+
+  if (!offset)
+    offset = &o;
+
+  if (!primary_option)
+    primary_option = &pf;
+
+  if (!secondary_option)
+    secondary_option = &sf;
+
+  arrays[PRIMARY] = parameters->primary_options;
+  arrays[SECONDARY] = parameters->secondary_options;
+
+  for (i = PRIMARY; i <= SECONDARY; i++)
+    {
+      for (j = 0; j < arrays[i]->len; j++)
+        {
+          option = g_array_index (arrays[i], GdkAttachRule, j);
+
+          switch (option & GDK_ATTACH_AXIS_MASK)
+            {
+            case GDK_ATTACH_AXIS_X:
+              axis = X;
+              break;
+
+            case GDK_ATTACH_AXIS_Y:
+              axis = Y;
+              break;
+
+            default:
+              axis = -1;
+              break;
+            }
+
+          if (axis < 0)
+            {
+              g_warning ("%s (): invalid constraint axis: 0x%X", G_STRFUNC, option);
+              continue;
+            }
+
+          satisfiable = is_satisfiable (option, parameters, width, height, bounds, &value);
+
+          if (satisfiable && !options[i][BEST][axis])
+            {
+              options[i][BEST][axis] = option;
+              values[i][BEST][axis] = value;
+
+              if (options[i][BEST][!axis])
+                break;
+              else
+                axes[i][BEST] = axis;
+            }
+          else if (!options[i][GOOD][axis])
+            {
+              options[i][GOOD][axis] = option;
+              values[i][GOOD][axis] = value;
+
+              if (!options[i][GOOD][!axis])
+                axes[i][GOOD] = axis;
+            }
+        }
+    }
+
+  success = FALSE;
+
+  for (i = BEST; i <= GOOD; i++)
+    {
+      for (j = BEST; j <= GOOD; j++)
+        {
+          for (k = Y; k >= X; k--)
+            {
+              if (options[PRIMARY][i][axes[PRIMARY][i] == k] && options[SECONDARY][j][axes[PRIMARY][i] != k])
+                {
+                  *primary_option = options[PRIMARY][i][axes[PRIMARY][i] == k];
+                  *secondary_option = options[SECONDARY][j][axes[PRIMARY][i] != k];
+
+                  if ((axes[PRIMARY][i] == k) == X)
+                    {
+                      position->x = values[PRIMARY][i][X];
+                      position->y = values[SECONDARY][j][Y];
+                    }
+                  else
+                    {
+                      position->x = values[SECONDARY][j][X];
+                      position->y = values[PRIMARY][i][Y];
+                    }
+
+                  offset->x = 0;
+                  offset->y = 0;
+                  success = TRUE;
+                  break;
+                }
+            }
+
+          if (success)
+            break;
+        }
+
+      if (success)
+        break;
+    }
+
+  if (success && bounds)
+    {
+      if ((*primary_option & GDK_ATTACH_AXIS_MASK) == GDK_ATTACH_AXIS_X)
+        {
+          x_option = *primary_option;
+          y_option = *secondary_option;
+        }
+      else
+        {
+          x_option = *secondary_option;
+          y_option = *primary_option;
+        }
+
+      if (parameters->is_right_to_left && (x_option & GDK_ATTACH_FLIP_IF_RTL))
+        {
+          if (position->x < bounds->x)
+            {
+              offset->x += bounds->x - position->x;
+              position->x = bounds->x;
+            }
+
+          if (position->x + width > bounds->x + bounds->width)
+            {
+              offset->x += bounds->x + bounds->width - width - position->x;
+              position->x = bounds->x + bounds->width - width;
+            }
+        }
+      else
+        {
+          if (position->x + width > bounds->x + bounds->width)
+            {
+              offset->x += bounds->x + bounds->width - width - position->x;
+              position->x = bounds->x + bounds->width - width;
+            }
+
+          if (position->x < bounds->x)
+            {
+              offset->x += bounds->x - position->x;
+              position->x = bounds->x;
+            }
+        }
+
+      if (parameters->is_right_to_left && (y_option & GDK_ATTACH_FLIP_IF_RTL))
+        {
+          if (position->y < bounds->y)
+            {
+              offset->y += bounds->y - position->y;
+              position->y = bounds->y;
+            }
+
+          if (position->y + height > bounds->y + bounds->height)
+            {
+              offset->y += bounds->y + bounds->height - height - position->y;
+              position->y = bounds->y + bounds->height - height;
+            }
+        }
+      else
+        {
+          if (position->y + height > bounds->y + bounds->height)
+            {
+              offset->y += bounds->y + bounds->height - height - position->y;
+              position->y = bounds->y + bounds->height - height;
+            }
+
+          if (position->y < bounds->y)
+            {
+              offset->y += bounds->y - position->y;
+              position->y = bounds->y;
+            }
+        }
+    }
+
+  return success;
+}
+
+/**
+ * gdk_attachment_parameters_choose_position_for_window:
+ * @parameters: a #GdkAttachmentParameters
+ * @window: (transfer none) (not nullable): the #GdkWindow to find the best
+ *          position for
+ * @position: (out) (optional): the best position for the window
+ * @offset: (out) (optional): the displacement needed to push the window
+ *          on-screen
+ * @primary_option: (out) (optional): the best primary option
+ * @secondary_option: (out) (optional): the best secondary option
+ *
+ * Finds the best position for @window according to @parameters.
+ *
+ * Returns: %TRUE if there's a best position
+ *
+ * Since: 3.20
+ */
+gboolean
+gdk_attachment_parameters_choose_position_for_window (const GdkAttachmentParameters *parameters,
+                                                      GdkWindow                     *window,
+                                                      GdkPoint                      *position,
+                                                      GdkPoint                      *offset,
+                                                      GdkAttachRule                 *primary_option,
+                                                      GdkAttachRule                 *secondary_option)
+{
+  GdkScreen *screen;
+  gint x;
+  gint y;
+  gint monitor;
+  GdkRectangle bounds;
+  gint width;
+  gint height;
+
+  g_return_val_if_fail (parameters, FALSE);
+  g_return_val_if_fail (parameters->has_attachment_rectangle, FALSE);
+  g_return_val_if_fail (window, FALSE);
+
+  screen = gdk_window_get_screen (window);
+  x = parameters->attachment_origin.x + parameters->attachment_rectangle.x + 
parameters->attachment_rectangle.width / 2;
+  y = parameters->attachment_origin.y + parameters->attachment_rectangle.y + 
parameters->attachment_rectangle.height / 2;
+  monitor = gdk_screen_get_monitor_at_point (screen, x, y);
+  gdk_screen_get_monitor_workarea (screen, monitor, &bounds);
+  width = gdk_window_get_width (window);
+  height = gdk_window_get_height (window);
+
+  return gdk_attachment_parameters_choose_position (parameters,
+                                                    width,
+                                                    height,
+                                                    &bounds,
+                                                    position,
+                                                    offset,
+                                                    primary_option,
+                                                    secondary_option);
+}
+
+/**
+ * gdk_window_move_using_attachment_parameters:
+ * @window: the #GdkWindow to position
+ * @parameters: (nullable): a description of how to position @window
+ *
+ * Moves @window to the best position according to @parameters.
+ *
+ * Since: 3.20
+ */
+void
+gdk_window_move_using_attachment_parameters (GdkWindow                     *window,
+                                             const GdkAttachmentParameters *parameters)
+{
+  GdkPoint position;
+  GdkPoint offset;
+  GdkAttachRule primary_option;
+  GdkAttachRule secondary_option;
+
+  g_return_if_fail (GDK_IS_WINDOW (window));
+
+  if (!parameters || !parameters->has_attachment_rectangle)
+    return;
+
+  if (gdk_attachment_parameters_choose_position_for_window (parameters,
+                                                            window,
+                                                            &position,
+                                                            &offset,
+                                                            &primary_option,
+                                                            &secondary_option))
+    {
+      gdk_window_move (window, position.x, position.y);
+
+      if (parameters->position_callback)
+        parameters->position_callback (window,
+                                       parameters,
+                                       &position,
+                                       &offset,
+                                       primary_option,
+                                       secondary_option,
+                                       parameters->position_callback_user_data);
+    }
+}
diff --git a/gdk/gdkattachinfo.h b/gdk/gdkattachinfo.h
new file mode 100644
index 0000000..12132fa
--- /dev/null
+++ b/gdk/gdkattachinfo.h
@@ -0,0 +1,166 @@
+#ifndef __GDK_ATTACHMENT_PARAMETERS_H__
+#define __GDK_ATTACHMENT_PARAMETERS_H__
+
+#if !defined (__GDK_H_INSIDE__) && !defined (GDK_COMPILATION)
+#error "Only <gdk/gdk.h> can be included directly."
+#endif
+
+#include <gdk/gdktypes.h>
+#include <gdk/gdkversionmacros.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GdkAttachRule:
+ * @GDK_ATTACH_UNKNOWN: not a constraint
+ * @GDK_ATTACH_AXIS_X: constrain horizontally
+ * @GDK_ATTACH_AXIS_Y: constrain vertically
+ * @GDK_ATTACH_AXIS_MASK: mask for constraint axis
+ * @GDK_ATTACH_RECT_MIN: left/top edge of rectangle
+ * @GDK_ATTACH_RECT_MID: center of rectangle
+ * @GDK_ATTACH_RECT_MAX: right/bottom edge of rectangle
+ * @GDK_ATTACH_RECT_MASK: mask for rectangle anchor
+ * @GDK_ATTACH_WINDOW_MIN: left/top edge of window
+ * @GDK_ATTACH_WINDOW_MID: center of window
+ * @GDK_ATTACH_WINDOW_MAX: right/bottom edge of window
+ * @GDK_ATTACH_WINDOW_MASK: mask for window anchor
+ * @GDK_ATTACH_FLIP_IF_RTL: swap min and max if text is right-to-left
+ *
+ * Constraints on the position of the window relative to its attachment
+ * rectangle.
+ *
+ * Since: 3.20
+ */
+typedef enum _GdkAttachRule
+{
+  GDK_ATTACH_UNKNOWN     = 0x00,
+  GDK_ATTACH_AXIS_X      = 0x01,
+  GDK_ATTACH_AXIS_Y      = 0x02,
+  GDK_ATTACH_AXIS_MASK   = 0x03,
+  GDK_ATTACH_RECT_MIN    = 0x04,
+  GDK_ATTACH_RECT_MID    = 0x08,
+  GDK_ATTACH_RECT_MAX    = 0x0C,
+  GDK_ATTACH_RECT_MASK   = 0x0C,
+  GDK_ATTACH_WINDOW_MIN  = 0x10,
+  GDK_ATTACH_WINDOW_MID  = 0x20,
+  GDK_ATTACH_WINDOW_MAX  = 0x30,
+  GDK_ATTACH_WINDOW_MASK = 0x30,
+  GDK_ATTACH_FLIP_IF_RTL = 0x40
+} GdkAttachRule;
+
+/**
+ * GdkAttachmentParameters:
+ *
+ * Opaque type containing the information needed to position a window relative
+ * to an attachment rectangle.
+ *
+ * Since: 3.20
+ */
+typedef struct _GdkAttachmentParameters GdkAttachmentParameters;
+
+/**
+ * GdkAttachmentPositionCallback:
+ * @window: the #GdkWindow that was moved
+ * @parameters: (transfer none) (nullable): the #GdkAttachmentParameters that was used
+ * @position: (transfer none) (nullable): the final position of @window
+ * @offset: (transfer none) (nullable): the displacement applied to keep
+ *          @window on-screen
+ * @primary_option: the primary option that was used for positioning. If
+ *                  unknown, this will be %GDK_ATTACH_UNKNOWN
+ * @secondary_option: the secondary option that was used for positioning. If
+ *                    unknown, this will be %GDK_ATTACH_UNKNOWN
+ * @user_data: (transfer none) (nullable): the user data that was set on
+ *             @parameters
+ *
+ * A function that can be used to receive information about the final position
+ * of a window after gdk_window_set_attachment_parameters() is called. Since
+ * the position might be determined asynchronously, don't assume it will be
+ * called directly from gdk_window_set_attachment_parameters().
+ *
+ * Since: 3.20
+ */
+typedef void (*GdkAttachmentPositionCallback) (GdkWindow                     *window,
+                                               const GdkAttachmentParameters *parameters,
+                                               const GdkPoint                *position,
+                                               const GdkPoint                *offset,
+                                               GdkAttachRule                  primary_option,
+                                               GdkAttachRule                  secondary_option,
+                                               gpointer                       user_data);
+
+GDK_AVAILABLE_IN_3_20
+GdkAttachmentParameters * gdk_attachment_parameters_new                          (void);
+
+GDK_AVAILABLE_IN_3_20
+gpointer                  gdk_attachment_parameters_copy                         (gconstpointer              
    src,
+                                                                                  gpointer                   
    data);
+
+GDK_AVAILABLE_IN_3_20
+void                      gdk_attachment_parameters_free                         (gpointer                   
    data);
+
+GDK_AVAILABLE_IN_3_20
+void                      gdk_attachment_parameters_set_attachment_origin        (GdkAttachmentParameters    
   *parameters,
+                                                                                  const GdkPoint             
   *origin);
+
+GDK_AVAILABLE_IN_3_20
+const GdkRectangle *      gdk_attachment_parameters_get_attachment_rectangle     (const 
GdkAttachmentParameters *parameters);
+
+GDK_AVAILABLE_IN_3_20
+void                      gdk_attachment_parameters_set_attachment_rectangle     (GdkAttachmentParameters    
   *parameters,
+                                                                                  const GdkRectangle         
   *rectangle);
+
+GDK_AVAILABLE_IN_3_20
+void                      gdk_attachment_parameters_set_attachment_margin        (GdkAttachmentParameters    
   *parameters,
+                                                                                  const GdkBorder            
   *margin);
+
+GDK_AVAILABLE_IN_3_20
+void                      gdk_attachment_parameters_set_window_margin            (GdkAttachmentParameters    
   *parameters,
+                                                                                  const GdkBorder            
   *margin);
+
+GDK_AVAILABLE_IN_3_20
+void                      gdk_attachment_parameters_set_window_padding           (GdkAttachmentParameters    
   *parameters,
+                                                                                  const GdkBorder            
   *padding);
+
+GDK_AVAILABLE_IN_3_20
+void                      gdk_attachment_parameters_set_window_offset            (GdkAttachmentParameters    
   *parameters,
+                                                                                  const GdkPoint             
   *offset);
+
+GDK_AVAILABLE_IN_3_20
+GdkWindowTypeHint         gdk_attachment_parameters_get_window_type_hint         (const 
GdkAttachmentParameters *parameters);
+
+GDK_AVAILABLE_IN_3_20
+void                      gdk_attachment_parameters_set_window_type_hint         (GdkAttachmentParameters    
   *parameters,
+                                                                                  GdkWindowTypeHint          
    window_type_hint);
+
+GDK_AVAILABLE_IN_3_20
+void                      gdk_attachment_parameters_set_right_to_left            (GdkAttachmentParameters    
   *parameters,
+                                                                                  gboolean                   
    is_right_to_left);
+
+GDK_AVAILABLE_IN_3_20
+void                      gdk_attachment_parameters_add_primary_options_valist   (GdkAttachmentParameters    
   *parameters,
+                                                                                  GdkAttachRule              
    first_option,
+                                                                                  va_list                    
    args);
+
+GDK_AVAILABLE_IN_3_20
+void                      gdk_attachment_parameters_add_secondary_options_valist (GdkAttachmentParameters    
   *parameters,
+                                                                                  GdkAttachRule              
    first_option,
+                                                                                  va_list                    
    args);
+
+GDK_AVAILABLE_IN_3_20
+void                      gdk_attachment_parameters_add_primary_options          (GdkAttachmentParameters    
   *parameters,
+                                                                                  GdkAttachRule              
    first_option,
+                                                                                  ...) 
G_GNUC_NULL_TERMINATED;
+
+GDK_AVAILABLE_IN_3_20
+void                      gdk_attachment_parameters_add_secondary_options        (GdkAttachmentParameters    
   *parameters,
+                                                                                  GdkAttachRule              
    first_option,
+                                                                                  ...) 
G_GNUC_NULL_TERMINATED;
+
+GDK_AVAILABLE_IN_3_20
+void                      gdk_attachment_parameters_set_position_callback        (GdkAttachmentParameters    
   *parameters,
+                                                                                  
GdkAttachmentPositionCallback  callback,
+                                                                                  gpointer                   
    user_data,
+                                                                                  GDestroyNotify             
    destroy_notify);
+
+G_END_DECLS
+
+#endif /* __GDK_ATTACHMENT_PARAMETERS_H__ */
diff --git a/gdk/gdkattachinfoprivate.h b/gdk/gdkattachinfoprivate.h
new file mode 100644
index 0000000..423f96f
--- /dev/null
+++ b/gdk/gdkattachinfoprivate.h
@@ -0,0 +1,81 @@
+#ifndef __GDK_ATTACHMENT_PARAMETERS_PRIVATE_H__
+#define __GDK_ATTACHMENT_PARAMETERS_PRIVATE_H__
+
+#include "gdkattachmentparameters.h"
+
+G_BEGIN_DECLS
+
+/*
+ * GdkAttachmentParameters:
+ * @attachment_origin: root origin of @attachment_rectangle coordinate system
+ * @has_attachment_rectangle: %TRUE if @attachment_rectangle is valid
+ * @attachment_rectangle: the attachment rectangle to attach the window to
+ * @attachment_margin: the space to leave around @attachment_rectangle
+ * @window_margin: the space to leave around the window
+ * @window_padding: the space between the window and its contents
+ * @window_offset: the offset to displace the window by
+ * @window_type_hint: the window type hint
+ * @is_right_to_left: %TRUE if the text direction is right to left
+ * @primary_options: an array of primary #GdkAttachRule
+ * @secondary_options: an array of secondary #GdkAttachRule
+ * @position_callback: a function to call when the final position is known
+ * @position_callback_user_data: additional data to pass to @position_callback
+ * @position_callback_destroy_notify: a function to free
+ *                                    @position_callback_user_data
+ *
+ * Opaque type containing the information needed to position a window relative
+ * to an attachment rectangle.
+ *
+ * Since: 3.20
+ */
+struct _GdkAttachmentParameters
+{
+  /*< private >*/
+  GdkPoint attachment_origin;
+
+  gboolean has_attachment_rectangle;
+  GdkRectangle attachment_rectangle;
+
+  GdkBorder attachment_margin;
+  GdkBorder window_margin;
+  GdkBorder window_padding;
+
+  GdkPoint window_offset;
+
+  GdkWindowTypeHint window_type_hint;
+
+  gboolean is_right_to_left;
+
+  GArray *primary_options;
+  GArray *secondary_options;
+
+  GdkAttachmentPositionCallback position_callback;
+  gpointer position_callback_user_data;
+  GDestroyNotify position_callback_destroy_notify;
+};
+
+G_GNUC_INTERNAL
+gboolean gdk_attachment_parameters_choose_position            (const GdkAttachmentParameters *parameters,
+                                                               gint                           width,
+                                                               gint                           height,
+                                                               const GdkRectangle            *bounds,
+                                                               GdkPoint                      *position,
+                                                               GdkPoint                      *offset,
+                                                               GdkAttachRule                 *primary_option,
+                                                               GdkAttachRule                 
*secondary_option);
+
+G_GNUC_INTERNAL
+gboolean gdk_attachment_parameters_choose_position_for_window (const GdkAttachmentParameters *parameters,
+                                                               GdkWindow                     *window,
+                                                               GdkPoint                      *position,
+                                                               GdkPoint                      *offset,
+                                                               GdkAttachRule                 *primary_option,
+                                                               GdkAttachRule                 
*secondary_option);
+
+G_GNUC_INTERNAL
+void     gdk_window_move_using_attachment_parameters          (GdkWindow                     *window,
+                                                               const GdkAttachmentParameters *parameters);
+
+G_END_DECLS
+
+#endif /* __GDK_ATTACHMENT_PARAMETERS_PRIVATE_H__ */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]