gtk2/gdk/gdkpopuplayout.c
2021-03-11 16:37:31 +00:00

419 lines
12 KiB
C

/* GDK - The GIMP Drawing Kit
* Copyright (C) 2020 Red Hat
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#include "gdkpopuplayout.h"
#include "gdksurface.h"
/**
* GdkPopupLayout:
*
* The `GdkPopupLayout` struct contains information that is
* necessary position a [interface@Gdk.Popup] relative to its parent.
*
* The positioning requires a negotiation with the windowing system,
* since it depends on external constraints, such as the position of
* the parent surface, and the screen dimensions.
*
* The basic ingredients are a rectangle on the parent surface,
* and the anchor on both that rectangle and the popup. The anchors
* specify a side or corner to place next to each other.
*
* ![Popup anchors](popup-anchors.png)
*
* For cases where placing the anchors next to each other would make
* the popup extend offscreen, the layout includes some hints for how
* to resolve this problem. The hints may suggest to flip the anchor
* position to the other side, or to 'slide' the popup along a side,
* or to resize it.
*
* ![Flipping popups](popup-flip.png)
*
* ![Sliding popups](popup-slide.png)
*
* These hints may be combined.
*
* Ultimatively, it is up to the windowing system to determine the position
* and size of the popup. You can learn about the result by calling
* [method@Gdk.Popup.get_position_x], [method@Gdk.Popup.get_position_y],
* [method@Gdk.Popup.get_rect_anchor] and [method@Gdk.Popup.get_surface_anchor]
* after the popup has been presented. This can be used to adjust the rendering.
* For example, [class@Gtk.Popover] changes its arrow position accordingly.
* But you have to be careful avoid changing the size of the popover, or it
* has to be presented again.
*/
struct _GdkPopupLayout
{
/* < private >*/
grefcount ref_count;
GdkRectangle anchor_rect;
GdkGravity rect_anchor;
GdkGravity surface_anchor;
GdkAnchorHints anchor_hints;
int dx;
int dy;
int shadow_left;
int shadow_right;
int shadow_top;
int shadow_bottom;
};
G_DEFINE_BOXED_TYPE (GdkPopupLayout, gdk_popup_layout,
gdk_popup_layout_ref,
gdk_popup_layout_unref)
/**
* gdk_popup_layout_new: (constructor)
* @anchor_rect: (not nullable): the anchor `GdkRectangle` to align @surface with
* @rect_anchor: the point on @anchor_rect to align with @surface's anchor point
* @surface_anchor: the point on @surface to align with @rect's anchor point
*
* Create a popup layout description.
*
* Used together with [method@Gdk.Popup.present] to describe how a popup
* surface should be placed and behave on-screen.
*
* @anchor_rect is relative to the top-left corner of the surface's parent.
* @rect_anchor and @surface_anchor determine anchor points on @anchor_rect
* and surface to pin together.
*
* The position of @anchor_rect's anchor point can optionally be offset using
* [method@Gdk.PopupLayout.set_offset], which is equivalent to offsetting the
* position of surface.
*
* Returns: (transfer full): newly created instance of `GdkPopupLayout`
*/
GdkPopupLayout *
gdk_popup_layout_new (const GdkRectangle *anchor_rect,
GdkGravity rect_anchor,
GdkGravity surface_anchor)
{
GdkPopupLayout *layout;
layout = g_new0 (GdkPopupLayout, 1);
g_ref_count_init (&layout->ref_count);
layout->anchor_rect = *anchor_rect;
layout->rect_anchor = rect_anchor;
layout->surface_anchor = surface_anchor;
return layout;
}
/**
* gdk_popup_layout_ref:
* @layout: a `GdkPopupLayout`
*
* Increases the reference count of @value.
*
* Returns: the same @layout
*/
GdkPopupLayout *
gdk_popup_layout_ref (GdkPopupLayout *layout)
{
g_ref_count_inc (&layout->ref_count);
return layout;
}
/**
* gdk_popup_layout_unref:
* @layout: a `GdkPopupLayout`
*
* Decreases the reference count of @value.
*/
void
gdk_popup_layout_unref (GdkPopupLayout *layout)
{
if (g_ref_count_dec (&layout->ref_count))
g_free (layout);
}
/**
* gdk_popup_layout_copy:
* @layout: a `GdkPopupLayout`
*
* Makes a copy of @layout.
*
* Returns: (transfer full): a copy of @layout.
*/
GdkPopupLayout *
gdk_popup_layout_copy (GdkPopupLayout *layout)
{
GdkPopupLayout *new_layout;
new_layout = g_new0 (GdkPopupLayout, 1);
g_ref_count_init (&new_layout->ref_count);
new_layout->anchor_rect = layout->anchor_rect;
new_layout->rect_anchor = layout->rect_anchor;
new_layout->surface_anchor = layout->surface_anchor;
new_layout->anchor_hints = layout->anchor_hints;
new_layout->dx = layout->dx;
new_layout->dy = layout->dy;
new_layout->shadow_left = layout->shadow_left;
new_layout->shadow_right = layout->shadow_right;
new_layout->shadow_top = layout->shadow_top;
new_layout->shadow_bottom = layout->shadow_bottom;
return new_layout;
}
/**
* gdk_popup_layout_equal:
* @layout: a `GdkPopupLayout`
* @other: another `GdkPopupLayout`
*
* Check whether @layout and @other has identical layout properties.
*
* Returns: %TRUE if @layout and @other have identical layout properties,
* otherwise %FALSE.
*/
gboolean
gdk_popup_layout_equal (GdkPopupLayout *layout,
GdkPopupLayout *other)
{
g_return_val_if_fail (layout, FALSE);
g_return_val_if_fail (other, FALSE);
return (gdk_rectangle_equal (&layout->anchor_rect, &other->anchor_rect) &&
layout->rect_anchor == other->rect_anchor &&
layout->surface_anchor == other->surface_anchor &&
layout->anchor_hints == other->anchor_hints &&
layout->dx == other->dx &&
layout->dy == other->dy &&
layout->shadow_left == other->shadow_left &&
layout->shadow_right == other->shadow_right &&
layout->shadow_top == other->shadow_top &&
layout->shadow_bottom == other->shadow_bottom);
}
/**
* gdk_popup_layout_set_anchor_rect:
* @layout: a `GdkPopupLayout`
* @anchor_rect: the new anchor rectangle
*
* Set the anchor rectangle.
*/
void
gdk_popup_layout_set_anchor_rect (GdkPopupLayout *layout,
const GdkRectangle *anchor_rect)
{
layout->anchor_rect = *anchor_rect;
}
/**
* gdk_popup_layout_get_anchor_rect:
* @layout: a `GdkPopupLayout`
*
* Get the anchor rectangle.
*
* Returns: The anchor rectangle
*/
const GdkRectangle *
gdk_popup_layout_get_anchor_rect (GdkPopupLayout *layout)
{
return &layout->anchor_rect;
}
/**
* gdk_popup_layout_set_rect_anchor:
* @layout: a `GdkPopupLayout`
* @anchor: the new rect anchor
*
* Set the anchor on the anchor rectangle.
*/
void
gdk_popup_layout_set_rect_anchor (GdkPopupLayout *layout,
GdkGravity anchor)
{
layout->rect_anchor = anchor;
}
/**
* gdk_popup_layout_get_rect_anchor:
* @layout: a `GdkPopupLayout`
*
* Returns the anchor position on the anchor rectangle.
*
* Returns: the anchor on the anchor rectangle.
*/
GdkGravity
gdk_popup_layout_get_rect_anchor (GdkPopupLayout *layout)
{
return layout->rect_anchor;
}
/**
* gdk_popup_layout_set_surface_anchor:
* @layout: a `GdkPopupLayout`
* @anchor: the new popup surface anchor
*
* Set the anchor on the popup surface.
*/
void
gdk_popup_layout_set_surface_anchor (GdkPopupLayout *layout,
GdkGravity anchor)
{
layout->surface_anchor = anchor;
}
/**
* gdk_popup_layout_get_surface_anchor:
* @layout: a `GdkPopupLayout`
*
* Returns the anchor position on the popup surface.
*
* Returns: the anchor on the popup surface.
*/
GdkGravity
gdk_popup_layout_get_surface_anchor (GdkPopupLayout *layout)
{
return layout->surface_anchor;
}
/**
* gdk_popup_layout_set_anchor_hints:
* @layout: a `GdkPopupLayout`
* @anchor_hints: the new `GdkAnchorHints`
*
* Set new anchor hints.
*
* The set @anchor_hints determines how @surface will be moved
* if the anchor points cause it to move off-screen. For example,
* %GDK_ANCHOR_FLIP_X will replace %GDK_GRAVITY_NORTH_WEST with
* %GDK_GRAVITY_NORTH_EAST and vice versa if @surface extends
* beyond the left or right edges of the monitor.
*/
void
gdk_popup_layout_set_anchor_hints (GdkPopupLayout *layout,
GdkAnchorHints anchor_hints)
{
layout->anchor_hints = anchor_hints;
}
/**
* gdk_popup_layout_get_anchor_hints:
* @layout: a `GdkPopupLayout`
*
* Get the `GdkAnchorHints`.
*
* Returns: the `GdkAnchorHints`
*/
GdkAnchorHints
gdk_popup_layout_get_anchor_hints (GdkPopupLayout *layout)
{
return layout->anchor_hints;
}
/**
* gdk_popup_layout_set_offset:
* @layout: a `GdkPopupLayout`
* @dx: x delta to offset the anchor rectangle with
* @dy: y delta to offset the anchor rectangle with
*
* Offset the position of the anchor rectangle with the given delta.
*/
void
gdk_popup_layout_set_offset (GdkPopupLayout *layout,
int dx,
int dy)
{
layout->dx = dx;
layout->dy = dy;
}
/**
* gdk_popup_layout_get_offset:
* @layout: a `GdkPopupLayout`
* @dx: (out): return location for the delta X coordinate
* @dy: (out): return location for the delta Y coordinate
*
* Retrieves the offset for the anchor rectangle.
*/
void
gdk_popup_layout_get_offset (GdkPopupLayout *layout,
int *dx,
int *dy)
{
if (dx)
*dx = layout->dx;
if (dy)
*dy = layout->dy;
}
/**
* gdk_popup_layout_set_shadow_width:
* @layout: a `GdkPopupLayout`
* @left: width of the left part of the shadow
* @right: width of the right part of the shadow
* @top: height of the top part of the shadow
* @bottom: height of the bottom part of the shadow
*
* Sets the shadow width of the popup.
*
* The shadow width corresponds to the part of the computed
* surface size that would consist of the shadow margin
* surrounding the window, would there be any.
*
* Since: 4.2
*/
void
gdk_popup_layout_set_shadow_width (GdkPopupLayout *layout,
int left,
int right,
int top,
int bottom)
{
layout->shadow_left = left;
layout->shadow_right = right;
layout->shadow_top = top;
layout->shadow_bottom = bottom;
}
/**
* gdk_popup_layout_get_shadow_width:
* @layout: a `GdkPopupLayout`
* @left: (out): return location for the left shadow width
* @right: (out): return location for the right shadow width
* @top: (out): return location for the top shadow width
* @bottom: (out): return location for the bottom shadow width
*
* Obtains the shadow widths of this layout.
*
* Since: 4.2
*/
void
gdk_popup_layout_get_shadow_width (GdkPopupLayout *layout,
int *left,
int *right,
int *top,
int *bottom)
{
if (left)
*left = layout->shadow_left;
if (right)
*right = layout->shadow_right;
if (top)
*top = layout->shadow_top;
if (bottom)
*bottom = layout->shadow_bottom;
}