StTooltip: Add the ability to set a hook to constrain the tooltip
If, for example, the stage is divided into multiple monitors, we might want to constrain tooltips so they don't cross monitor boundaries. Add a function to set a per-stage callback to constrain tooltips. https://bugzilla.gnome.org/show_bug.cgi?id=645547
This commit is contained in:
parent
02078255ea
commit
d19cdc206b
@ -31,6 +31,7 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
@ -59,9 +60,6 @@ struct _StTooltipPrivate
|
|||||||
{
|
{
|
||||||
StLabel *label;
|
StLabel *label;
|
||||||
|
|
||||||
gfloat arrow_offset;
|
|
||||||
gboolean actor_below;
|
|
||||||
|
|
||||||
ClutterGeometry *tip_area;
|
ClutterGeometry *tip_area;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -73,6 +71,10 @@ static void st_tooltip_show (ClutterActor *self);
|
|||||||
static void st_tooltip_show_all (ClutterActor *self);
|
static void st_tooltip_show_all (ClutterActor *self);
|
||||||
static void st_tooltip_hide_all (ClutterActor *self);
|
static void st_tooltip_hide_all (ClutterActor *self);
|
||||||
|
|
||||||
|
static void st_tooltip_constrain (StTooltip *tooltip,
|
||||||
|
const ClutterGeometry *geometry,
|
||||||
|
ClutterGeometry *adjusted_geometry);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
st_tooltip_set_property (GObject *gobject,
|
st_tooltip_set_property (GObject *gobject,
|
||||||
guint prop_id,
|
guint prop_id,
|
||||||
@ -279,9 +281,9 @@ st_tooltip_update_position (StTooltip *tooltip)
|
|||||||
{
|
{
|
||||||
StTooltipPrivate *priv = tooltip->priv;
|
StTooltipPrivate *priv = tooltip->priv;
|
||||||
ClutterGeometry *tip_area = tooltip->priv->tip_area;
|
ClutterGeometry *tip_area = tooltip->priv->tip_area;
|
||||||
|
ClutterGeometry geometry;
|
||||||
|
ClutterGeometry adjusted_geometry;
|
||||||
gfloat tooltip_w, tooltip_h, tooltip_x, tooltip_y;
|
gfloat tooltip_w, tooltip_h, tooltip_x, tooltip_y;
|
||||||
gfloat parent_w, parent_h;
|
|
||||||
ClutterActor *parent;
|
|
||||||
|
|
||||||
/* if no area set, just position ourselves top left */
|
/* if no area set, just position ourselves top left */
|
||||||
if (!priv->tip_area)
|
if (!priv->tip_area)
|
||||||
@ -301,37 +303,15 @@ st_tooltip_update_position (StTooltip *tooltip)
|
|||||||
tooltip_x = (int)(tip_area->x + (tip_area->width / 2) - (tooltip_w / 2));
|
tooltip_x = (int)(tip_area->x + (tip_area->width / 2) - (tooltip_w / 2));
|
||||||
tooltip_y = (int)(tip_area->y + tip_area->height);
|
tooltip_y = (int)(tip_area->y + tip_area->height);
|
||||||
|
|
||||||
parent = clutter_actor_get_parent ((ClutterActor *) tooltip);
|
geometry.x = tooltip_x;
|
||||||
if (!parent)
|
geometry.y = tooltip_y;
|
||||||
{
|
geometry.width = ceil (tooltip_w);
|
||||||
g_critical ("StTooltip is not parented");
|
geometry.height = ceil (tooltip_h);
|
||||||
return;
|
|
||||||
}
|
|
||||||
clutter_actor_get_size (parent, &parent_w, &parent_h);
|
|
||||||
|
|
||||||
/* make sure the tooltip is not off screen vertically */
|
st_tooltip_constrain (tooltip, &geometry, &adjusted_geometry);
|
||||||
if (tooltip_x < 0)
|
|
||||||
{
|
|
||||||
tooltip_x = 0;
|
|
||||||
}
|
|
||||||
else if (tooltip_x + tooltip_w > parent_w)
|
|
||||||
{
|
|
||||||
tooltip_x = (int)(parent_w) - tooltip_w;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* make sure the tooltip is not off screen horizontally */
|
tooltip_x = adjusted_geometry.x;
|
||||||
if (tooltip_y + tooltip_h > parent_h)
|
tooltip_y = adjusted_geometry.y;
|
||||||
{
|
|
||||||
priv->actor_below = TRUE;
|
|
||||||
tooltip_y = tip_area->y - tooltip_h;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
priv->actor_below = FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* calculate the arrow offset */
|
|
||||||
priv->arrow_offset = tip_area->x + tip_area->width / 2 - tooltip_x;
|
|
||||||
|
|
||||||
/* Since we are updating the position out of st_widget_allocate(), we can't
|
/* Since we are updating the position out of st_widget_allocate(), we can't
|
||||||
* call clutter_actor_set_position(), since that would trigger another
|
* call clutter_actor_set_position(), since that would trigger another
|
||||||
@ -443,3 +423,102 @@ st_tooltip_get_tip_area (StTooltip *tooltip)
|
|||||||
|
|
||||||
return tooltip->priv->tip_area;
|
return tooltip->priv->tip_area;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
StTooltipConstrainFunc func;
|
||||||
|
gpointer data;
|
||||||
|
GDestroyNotify notify;
|
||||||
|
} ConstrainFuncClosure;
|
||||||
|
|
||||||
|
static void
|
||||||
|
constrain_func_closure_free (gpointer data)
|
||||||
|
{
|
||||||
|
ConstrainFuncClosure *closure = data;
|
||||||
|
if (closure->notify)
|
||||||
|
closure->notify (closure->data);
|
||||||
|
g_slice_free (ConstrainFuncClosure, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GQuark
|
||||||
|
st_tooltip_constrain_func_quark (void)
|
||||||
|
{
|
||||||
|
static GQuark value = 0;
|
||||||
|
if (G_UNLIKELY (value == 0))
|
||||||
|
value = g_quark_from_static_string ("st-tooltip-constrain-func");
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* st_tooltip_set_constrain_func:
|
||||||
|
* @stage: a #ClutterStage
|
||||||
|
* @func: (allow-none): function to be called to constrain tooltip position
|
||||||
|
* @data: (allow-none): user data to pass to @func
|
||||||
|
* @notify: (allow-none): function to be called when @data is no longer needed
|
||||||
|
*
|
||||||
|
* Sets a callback function that will be used to constrain the position
|
||||||
|
* of tooltips within @stage. This can be used, for example, if the stage
|
||||||
|
* spans multiple monitors and tooltips should be positioned not to cross
|
||||||
|
* monitors.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
st_tooltip_set_constrain_func (ClutterStage *stage,
|
||||||
|
StTooltipConstrainFunc func,
|
||||||
|
gpointer data,
|
||||||
|
GDestroyNotify notify)
|
||||||
|
{
|
||||||
|
ConstrainFuncClosure *closure;
|
||||||
|
|
||||||
|
g_return_if_fail (CLUTTER_IS_STAGE (stage));
|
||||||
|
|
||||||
|
if (func)
|
||||||
|
{
|
||||||
|
closure = g_slice_new (ConstrainFuncClosure);
|
||||||
|
closure->func = func;
|
||||||
|
closure->data = data;
|
||||||
|
closure->notify = notify;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
closure = NULL;
|
||||||
|
|
||||||
|
g_object_set_qdata_full (G_OBJECT (stage), st_tooltip_constrain_func_quark (),
|
||||||
|
closure, constrain_func_closure_free);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
st_tooltip_constrain (StTooltip *tooltip,
|
||||||
|
const ClutterGeometry *geometry,
|
||||||
|
ClutterGeometry *adjusted_geometry)
|
||||||
|
{
|
||||||
|
ConstrainFuncClosure *closure;
|
||||||
|
|
||||||
|
ClutterActor *stage = clutter_actor_get_stage (CLUTTER_ACTOR (tooltip));
|
||||||
|
|
||||||
|
*adjusted_geometry = *geometry;
|
||||||
|
|
||||||
|
if (stage == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
closure = g_object_get_qdata (G_OBJECT (stage), st_tooltip_constrain_func_quark ());
|
||||||
|
if (closure)
|
||||||
|
{
|
||||||
|
closure->func (tooltip, geometry, adjusted_geometry, closure->data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ClutterActor *parent;
|
||||||
|
gfloat parent_w, parent_h;
|
||||||
|
|
||||||
|
parent = clutter_actor_get_parent ((ClutterActor *) tooltip);
|
||||||
|
clutter_actor_get_size (parent, &parent_w, &parent_h);
|
||||||
|
|
||||||
|
/* make sure the tooltip is not off parent horizontally */
|
||||||
|
if (adjusted_geometry->x < 0)
|
||||||
|
adjusted_geometry->x = 0;
|
||||||
|
else if (adjusted_geometry->x + adjusted_geometry->width > parent_w)
|
||||||
|
adjusted_geometry->x = (int)(parent_w) - adjusted_geometry->width;
|
||||||
|
|
||||||
|
/* make sure the tooltip is not off parent vertically */
|
||||||
|
if (adjusted_geometry->y + adjusted_geometry->height > parent_h)
|
||||||
|
adjusted_geometry->y = parent_h - adjusted_geometry->height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -68,6 +68,24 @@ void st_tooltip_set_tip_area (StTooltip *tooltip,
|
|||||||
const ClutterGeometry *area);
|
const ClutterGeometry *area);
|
||||||
const ClutterGeometry* st_tooltip_get_tip_area (StTooltip *tooltip);
|
const ClutterGeometry* st_tooltip_get_tip_area (StTooltip *tooltip);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* StTooltipConstrainFunc:
|
||||||
|
* @tooltip: the #StTooltip that is being positioned
|
||||||
|
* @geometry: size and position of the tooltip without any constraints
|
||||||
|
* @adjusted_geometry: (out): new position of the tooltip.
|
||||||
|
* The width and height fields will be ignored.
|
||||||
|
* @data: (closure): user data passed to st_tooltip_set_constrain_func()
|
||||||
|
*/
|
||||||
|
typedef void (*StTooltipConstrainFunc) (StTooltip *tooltip,
|
||||||
|
const ClutterGeometry *geometry,
|
||||||
|
ClutterGeometry *adjusted_geometry,
|
||||||
|
gpointer data);
|
||||||
|
|
||||||
|
void st_tooltip_set_constrain_func (ClutterStage *stage,
|
||||||
|
StTooltipConstrainFunc func,
|
||||||
|
gpointer data,
|
||||||
|
GDestroyNotify notify);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
#endif /* __ST_TOOLTIP_H__ */
|
#endif /* __ST_TOOLTIP_H__ */
|
||||||
|
Loading…
Reference in New Issue
Block a user