gtk2/gdk/gdkpopuplayout.c
Matthias Clasen ea19f7c360 popuplayout: Add shadow width
Add shadow width to the GdkPopupLayout struct. This
information is needed by the compositor to make correct
positioning decisions about popups.
2021-02-13 21:01:46 -05:00

417 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"
/**
* SECTION:gdkpopuplayout
* @Title: GdkPopupLayout
* @Short_description: Information for presenting popups
*
* Popups are positioned relative to their parent surface.
* The GdkPopupLayout struct contains information that is
* necessary to do so.
*
* 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
* gdk_popup_get_position_x(), gdk_popup_get_position_y(),
* gdk_popup_get_rect_anchor() and gdk_popup_get_surface_anchor() after the
* popup has been presented. This can be used to adjust the rendering. For
* example, GtkPopover 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 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
* gdk_popup_layout_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
*
* Create a new #GdkPopupLayout and copy the contents of @layout into it.
*
* 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
*
* 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;
}