[StWidget] add (optional) hover tracking
If track-hover is set, update the hover property automatically, and the "hover" pseudo class to match, as StClickable used to do. (Remove the corresponding code in StClickable). Tweak the tooltip handling to use track-hover, which also makes it slightly more reliable in the presence of reactive children, etc.
This commit is contained in:
parent
909b5ec43c
commit
f9e4385e02
@ -536,6 +536,8 @@ AppWellIcon.prototype = {
|
||||
},
|
||||
|
||||
_onMenuPoppedDown: function() {
|
||||
this.actor.sync_hover();
|
||||
|
||||
if (this._didActivateWindow)
|
||||
return;
|
||||
if (!this._setWindowSelection)
|
||||
|
@ -484,10 +484,7 @@ NewWorkspaceArea.prototype = {
|
||||
},
|
||||
|
||||
setStyle: function(isHover) {
|
||||
if (isHover)
|
||||
this._child1.add_style_pseudo_class('hover');
|
||||
else
|
||||
this._child1.remove_style_pseudo_class('hover');
|
||||
this._child1.set_hover(isHover);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -16,7 +16,6 @@ G_DEFINE_TYPE (StClickable, st_clickable, ST_TYPE_BIN);
|
||||
struct _StClickablePrivate {
|
||||
gboolean active;
|
||||
gboolean held;
|
||||
gboolean hover;
|
||||
gboolean pressed;
|
||||
|
||||
guint initiating_button;
|
||||
@ -33,7 +32,6 @@ enum {
|
||||
PROP_0,
|
||||
|
||||
PROP_ACTIVE,
|
||||
PROP_HOVER,
|
||||
PROP_PRESSED,
|
||||
};
|
||||
|
||||
@ -46,11 +44,6 @@ sync_pseudo_class (StClickable *self)
|
||||
st_widget_add_style_pseudo_class (ST_WIDGET (self), "pressed");
|
||||
else
|
||||
st_widget_remove_style_pseudo_class (ST_WIDGET (self), "pressed");
|
||||
|
||||
if (self->priv->hover)
|
||||
st_widget_add_style_pseudo_class (ST_WIDGET (self), "hover");
|
||||
else
|
||||
st_widget_remove_style_pseudo_class (ST_WIDGET (self), "hover");
|
||||
}
|
||||
|
||||
static void
|
||||
@ -64,17 +57,6 @@ set_active (StClickable *self,
|
||||
g_object_notify (G_OBJECT (self), "active");
|
||||
}
|
||||
|
||||
static void
|
||||
set_hover (StClickable *self,
|
||||
gboolean hover)
|
||||
{
|
||||
if (self->priv->hover == hover)
|
||||
return;
|
||||
self->priv->hover = hover;
|
||||
sync_pseudo_class (self);
|
||||
g_object_notify (G_OBJECT (self), "hover");
|
||||
}
|
||||
|
||||
static void
|
||||
set_pressed (StClickable *self,
|
||||
gboolean pressed)
|
||||
@ -102,21 +84,18 @@ st_clickable_enter_event (ClutterActor *actor,
|
||||
ClutterCrossingEvent *event)
|
||||
{
|
||||
StClickable *self = ST_CLICKABLE (actor);
|
||||
|
||||
if (st_clickable_contains (self, event->related))
|
||||
return TRUE;
|
||||
if (!st_clickable_contains (self, event->source))
|
||||
return TRUE;
|
||||
gboolean result;
|
||||
|
||||
g_object_freeze_notify (G_OBJECT (actor));
|
||||
|
||||
if (self->priv->held)
|
||||
set_pressed (self, TRUE);
|
||||
set_hover (self, TRUE);
|
||||
result = CLUTTER_ACTOR_CLASS (st_clickable_parent_class)->enter_event (actor, event);
|
||||
|
||||
/* We can't just assume get_hover() is TRUE; see st_widget_enter(). */
|
||||
set_pressed (self, self->priv->held && st_widget_get_hover (ST_WIDGET (actor)));
|
||||
|
||||
g_object_thaw_notify (G_OBJECT (actor));
|
||||
|
||||
return TRUE;
|
||||
return result;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@ -124,14 +103,18 @@ st_clickable_leave_event (ClutterActor *actor,
|
||||
ClutterCrossingEvent *event)
|
||||
{
|
||||
StClickable *self = ST_CLICKABLE (actor);
|
||||
gboolean result;
|
||||
|
||||
if (st_clickable_contains (self, event->related))
|
||||
return TRUE;
|
||||
g_object_freeze_notify (G_OBJECT (actor));
|
||||
|
||||
set_hover (self, FALSE);
|
||||
set_pressed (self, FALSE);
|
||||
result = CLUTTER_ACTOR_CLASS (st_clickable_parent_class)->leave_event (actor, event);
|
||||
|
||||
return TRUE;
|
||||
/* As above, we can't just assume get_hover() is FALSE. */
|
||||
set_pressed (self, self->priv->held && st_widget_get_hover (ST_WIDGET (actor)));
|
||||
|
||||
g_object_thaw_notify (G_OBJECT (actor));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@ -243,9 +226,6 @@ st_clickable_get_property (GObject *object,
|
||||
case PROP_PRESSED:
|
||||
g_value_set_boolean (value, self->priv->pressed);
|
||||
break;
|
||||
case PROP_HOVER:
|
||||
g_value_set_boolean (value, self->priv->hover);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
@ -297,20 +277,6 @@ st_clickable_class_init (StClickableClass *klass)
|
||||
FALSE,
|
||||
G_PARAM_READWRITE));
|
||||
|
||||
/**
|
||||
* StClickable:hover
|
||||
*
|
||||
* This property tracks whether the mouse is over the button; note this
|
||||
* state is independent of whether the button is pressed.
|
||||
*/
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_HOVER,
|
||||
g_param_spec_boolean ("hover",
|
||||
"Hovering state",
|
||||
"Whether the mouse is over the button",
|
||||
FALSE,
|
||||
G_PARAM_READABLE));
|
||||
|
||||
/**
|
||||
* StClickable:pressed
|
||||
*
|
||||
@ -333,4 +299,5 @@ st_clickable_init (StClickable *self)
|
||||
{
|
||||
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, ST_TYPE_CLICKABLE,
|
||||
StClickablePrivate);
|
||||
st_widget_set_track_hover (ST_WIDGET (self), TRUE);
|
||||
}
|
||||
|
@ -76,6 +76,8 @@ struct _StWidgetPrivate
|
||||
gboolean is_style_dirty : 1;
|
||||
gboolean draw_bg_color : 1;
|
||||
gboolean draw_border_internal : 1;
|
||||
gboolean track_hover : 1;
|
||||
gboolean hover : 1;
|
||||
|
||||
StTooltip *tooltip;
|
||||
|
||||
@ -101,11 +103,11 @@ enum
|
||||
PROP_PSEUDO_CLASS,
|
||||
PROP_STYLE_CLASS,
|
||||
PROP_STYLE,
|
||||
|
||||
PROP_STYLABLE,
|
||||
|
||||
PROP_HAS_TOOLTIP,
|
||||
PROP_TOOLTIP_TEXT
|
||||
PROP_TOOLTIP_TEXT,
|
||||
PROP_TRACK_HOVER,
|
||||
PROP_HOVER
|
||||
};
|
||||
|
||||
enum
|
||||
@ -167,6 +169,14 @@ st_widget_set_property (GObject *gobject,
|
||||
st_widget_set_tooltip_text (actor, g_value_get_string (value));
|
||||
break;
|
||||
|
||||
case PROP_TRACK_HOVER:
|
||||
st_widget_set_track_hover (actor, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
case PROP_HOVER:
|
||||
st_widget_set_hover (actor, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
||||
break;
|
||||
@ -212,6 +222,14 @@ st_widget_get_property (GObject *gobject,
|
||||
g_value_set_string (value, st_widget_get_tooltip_text (actor));
|
||||
break;
|
||||
|
||||
case PROP_TRACK_HOVER:
|
||||
g_value_set_boolean (value, priv->track_hover);
|
||||
break;
|
||||
|
||||
case PROP_HOVER:
|
||||
g_value_set_boolean (value, priv->hover);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
||||
break;
|
||||
@ -1125,15 +1143,34 @@ st_widget_get_theme_node (StWidget *widget)
|
||||
return priv->theme_node;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
actor_contains (ClutterActor *widget,
|
||||
ClutterActor *other)
|
||||
{
|
||||
while (other != NULL && other != widget)
|
||||
other = clutter_actor_get_parent (other);
|
||||
return other != NULL;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
st_widget_enter (ClutterActor *actor,
|
||||
ClutterCrossingEvent *event)
|
||||
{
|
||||
StWidgetPrivate *priv = ST_WIDGET (actor)->priv;
|
||||
|
||||
|
||||
if (priv->has_tooltip)
|
||||
st_widget_show_tooltip ((StWidget*) actor);
|
||||
if (priv->track_hover)
|
||||
{
|
||||
if (actor_contains (actor, event->source))
|
||||
st_widget_set_hover (ST_WIDGET (actor), TRUE);
|
||||
else
|
||||
{
|
||||
/* The widget has a grab and is being told about an
|
||||
* enter-event outside its hierarchy. Hopefully we already
|
||||
* got a leave-event, but if not, handle it now.
|
||||
*/
|
||||
st_widget_set_hover (ST_WIDGET (actor), FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
if (CLUTTER_ACTOR_CLASS (st_widget_parent_class)->enter_event)
|
||||
return CLUTTER_ACTOR_CLASS (st_widget_parent_class)->enter_event (actor, event);
|
||||
@ -1147,8 +1184,11 @@ st_widget_leave (ClutterActor *actor,
|
||||
{
|
||||
StWidgetPrivate *priv = ST_WIDGET (actor)->priv;
|
||||
|
||||
if (priv->has_tooltip)
|
||||
st_tooltip_hide (priv->tooltip);
|
||||
if (priv->track_hover)
|
||||
{
|
||||
if (!actor_contains (actor, event->related))
|
||||
st_widget_set_hover (ST_WIDGET (actor), FALSE);
|
||||
}
|
||||
|
||||
if (CLUTTER_ACTOR_CLASS (st_widget_parent_class)->leave_event)
|
||||
return CLUTTER_ACTOR_CLASS (st_widget_parent_class)->leave_event (actor, event);
|
||||
@ -1267,8 +1307,9 @@ st_widget_class_init (StWidgetClass *klass)
|
||||
/**
|
||||
* StWidget:has-tooltip:
|
||||
*
|
||||
* Determines whether the widget has a tooltip. If set to TRUE, causes the
|
||||
* widget to monitor enter and leave events (i.e. sets the widget reactive).
|
||||
* Determines whether the widget has a tooltip. If set to %TRUE, causes the
|
||||
* widget to monitor hover state (i.e. sets #ClutterActor:reactive and
|
||||
* #StWidget:track-hover).
|
||||
*/
|
||||
pspec = g_param_spec_boolean ("has-tooltip",
|
||||
"Has Tooltip",
|
||||
@ -1292,6 +1333,40 @@ st_widget_class_init (StWidgetClass *klass)
|
||||
ST_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, PROP_TOOLTIP_TEXT, pspec);
|
||||
|
||||
/**
|
||||
* StWidget:track-hover:
|
||||
*
|
||||
* Determines whether the widget tracks pointer hover state. If
|
||||
* %TRUE (and the widget is visible and reactive), the
|
||||
* #StWidget:hover property and "hover" style pseudo class will be
|
||||
* adjusted automatically as the pointer moves in and out of the
|
||||
* widget.
|
||||
*/
|
||||
pspec = g_param_spec_boolean ("track-hover",
|
||||
"Track hover",
|
||||
"Determines whether the widget tracks hover state",
|
||||
FALSE,
|
||||
ST_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_TRACK_HOVER,
|
||||
pspec);
|
||||
|
||||
/**
|
||||
* StWidget:hover:
|
||||
*
|
||||
* Whether or not the pointer is currently hovering over the widget. This is
|
||||
* only tracked automatically if #StWidget:track-hover is %TRUE, but you can
|
||||
* adjust it manually in any case.
|
||||
*/
|
||||
pspec = g_param_spec_boolean ("hover",
|
||||
"Hover",
|
||||
"Whether the pointer is hovering over the widget",
|
||||
FALSE,
|
||||
ST_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_HOVER,
|
||||
pspec);
|
||||
|
||||
/**
|
||||
* StWidget::style-changed:
|
||||
*
|
||||
@ -1794,10 +1869,10 @@ st_widget_set_direction (StWidget *self, StTextDirection dir)
|
||||
*
|
||||
* Enables tooltip support on the #StWidget.
|
||||
*
|
||||
* Note that setting has-tooltip to %TRUE will cause the widget to be set
|
||||
* reactive. If you no longer need tooltip support and do not need the widget
|
||||
* to be reactive, you need to set ClutterActor::reactive to FALSE.
|
||||
*
|
||||
* Note that setting has-tooltip to %TRUE will cause
|
||||
* #ClutterActor:reactive and #StWidget:track-hover to be set %TRUE as
|
||||
* well, but you must clear these flags yourself (if appropriate) when
|
||||
* setting it %FALSE.
|
||||
*/
|
||||
void
|
||||
st_widget_set_has_tooltip (StWidget *widget,
|
||||
@ -1814,6 +1889,7 @@ st_widget_set_has_tooltip (StWidget *widget,
|
||||
if (has_tooltip)
|
||||
{
|
||||
clutter_actor_set_reactive ((ClutterActor*) widget, TRUE);
|
||||
st_widget_set_track_hover (widget, TRUE);
|
||||
|
||||
if (!priv->tooltip)
|
||||
{
|
||||
@ -1948,3 +2024,143 @@ st_widget_hide_tooltip (StWidget *widget)
|
||||
if (widget->priv->tooltip)
|
||||
st_tooltip_hide (widget->priv->tooltip);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_widget_set_track_hover:
|
||||
* @widget: A #StWidget
|
||||
* @track_hover: %TRUE if the widget should track the pointer hover state
|
||||
*
|
||||
* Enables hover tracking on the #StWidget.
|
||||
*
|
||||
* If hover tracking is enabled, and the widget is visible and
|
||||
* reactive, then @widget's #StWidget:hover property will be updated
|
||||
* automatically to reflect whether the pointer is in @widget (or one
|
||||
* of its children), and @widget's #StWidget:pseudo-class will have
|
||||
* the "hover" class added and removed from it accordingly.
|
||||
*
|
||||
* Note that currently it is not possible to correctly track the hover
|
||||
* state when another actor has a pointer grab. You can use
|
||||
* st_widget_sync_hover() to update the property manually in this
|
||||
* case.
|
||||
*/
|
||||
void
|
||||
st_widget_set_track_hover (StWidget *widget,
|
||||
gboolean track_hover)
|
||||
{
|
||||
StWidgetPrivate *priv;
|
||||
|
||||
g_return_if_fail (ST_IS_WIDGET (widget));
|
||||
|
||||
priv = widget->priv;
|
||||
|
||||
if (priv->track_hover != track_hover)
|
||||
{
|
||||
priv->track_hover = track_hover;
|
||||
g_object_notify (G_OBJECT (widget), "track-hover");
|
||||
|
||||
if (priv->track_hover)
|
||||
st_widget_sync_hover (widget);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* st_widget_get_track_hover:
|
||||
* @widget: A #StWidget
|
||||
*
|
||||
* Returns the current value of the track-hover property. See
|
||||
* st_tooltip_set_track_hover() for more information.
|
||||
*
|
||||
* Returns: current value of track-hover on @widget
|
||||
*/
|
||||
gboolean
|
||||
st_widget_get_track_hover (StWidget *widget)
|
||||
{
|
||||
g_return_val_if_fail (ST_IS_WIDGET (widget), FALSE);
|
||||
|
||||
return widget->priv->track_hover;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_widget_set_hover:
|
||||
* @widget: A #StWidget
|
||||
* @hover: whether the pointer is hovering over the widget
|
||||
*
|
||||
* Sets @widget's hover property and adds or removes "hover" from its
|
||||
* pseudo class accordingly. If #StWidget:has-tooltip is %TRUE, this
|
||||
* will also show or hide the tooltip, as appropriate.
|
||||
*
|
||||
* If you have set #StWidget:track-hover, you should not need to call
|
||||
* this directly. You can call st_widget_sync_hover() if the hover
|
||||
* state might be out of sync due to another actor's pointer grab.
|
||||
*/
|
||||
void
|
||||
st_widget_set_hover (StWidget *widget,
|
||||
gboolean hover)
|
||||
{
|
||||
StWidgetPrivate *priv;
|
||||
|
||||
g_return_if_fail (ST_IS_WIDGET (widget));
|
||||
|
||||
priv = widget->priv;
|
||||
|
||||
if (priv->hover != hover)
|
||||
{
|
||||
priv->hover = hover;
|
||||
if (priv->hover)
|
||||
{
|
||||
st_widget_add_style_pseudo_class (widget, "hover");
|
||||
if (priv->has_tooltip)
|
||||
st_widget_show_tooltip (widget);
|
||||
}
|
||||
else
|
||||
{
|
||||
st_widget_remove_style_pseudo_class (widget, "hover");
|
||||
if (priv->has_tooltip)
|
||||
st_widget_hide_tooltip (widget);
|
||||
}
|
||||
g_object_notify (G_OBJECT (widget), "hover");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* st_widget_sync_hover:
|
||||
* @widget: A #StWidget
|
||||
*
|
||||
* Sets @widget's hover state according to the current pointer
|
||||
* position. This can be used to ensure that it is correct after
|
||||
* (or during) a pointer grab.
|
||||
*/
|
||||
void
|
||||
st_widget_sync_hover (StWidget *widget)
|
||||
{
|
||||
ClutterDeviceManager *device_manager;
|
||||
ClutterInputDevice *pointer;
|
||||
ClutterActor *actor;
|
||||
|
||||
device_manager = clutter_device_manager_get_default ();
|
||||
pointer = clutter_device_manager_get_core_device (device_manager,
|
||||
CLUTTER_POINTER_DEVICE);
|
||||
actor = clutter_input_device_get_pointer_actor (pointer);
|
||||
|
||||
while (actor && actor != (ClutterActor *)widget)
|
||||
actor = clutter_actor_get_parent (actor);
|
||||
|
||||
st_widget_set_hover (widget, actor == (ClutterActor *)widget);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_widget_get_hover:
|
||||
* @widget: A #StWidget
|
||||
*
|
||||
* If #StWidget:track-hover is set, this returns whether the pointer
|
||||
* is currently over the widget.
|
||||
*
|
||||
* Returns: current value of hover on @widget
|
||||
*/
|
||||
gboolean
|
||||
st_widget_get_hover (StWidget *widget)
|
||||
{
|
||||
g_return_val_if_fail (ST_IS_WIDGET (widget), FALSE);
|
||||
|
||||
return widget->priv->hover;
|
||||
}
|
||||
|
@ -120,6 +120,14 @@ const gchar* st_widget_get_tooltip_text (StWidget *widg
|
||||
void st_widget_show_tooltip (StWidget *widget);
|
||||
void st_widget_hide_tooltip (StWidget *widget);
|
||||
|
||||
void st_widget_set_track_hover (StWidget *widget,
|
||||
gboolean track_hover);
|
||||
gboolean st_widget_get_track_hover (StWidget *widget);
|
||||
void st_widget_set_hover (StWidget *widget,
|
||||
gboolean hover);
|
||||
void st_widget_sync_hover (StWidget *widget);
|
||||
gboolean st_widget_get_hover (StWidget *widget);
|
||||
|
||||
void st_widget_ensure_style (StWidget *widget);
|
||||
|
||||
StTextDirection st_widget_get_default_direction (void);
|
||||
|
Loading…
Reference in New Issue
Block a user