gnome-shell/src/st/st-button.c
Carlos Garnacho 111a0a7fa1 st: Mass update to ClutterActorClass event vmethod changes
These are now forwarded as ClutterEvent in order to remain accessible
on the Javascript side. This also means all ClutterActor implementations
in ST need to update to this change.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2872>
2023-08-09 13:46:14 +02:00

1054 lines
28 KiB
C

/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-button.c: Plain button actor
*
* Copyright 2007 OpenedHand
* Copyright 2008, 2009 Intel Corporation.
* Copyright 2009, 2010 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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 program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* SECTION:st-button
* @short_description: Button widget
*
* A button widget with support for either a text label or icon, toggle mode
* and transitions effects between states.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <glib.h>
#include <clutter/clutter.h>
#include "st-button.h"
#include "st-icon.h"
#include "st-enum-types.h"
#include "st-texture-cache.h"
#include "st-private.h"
#include <st/st-widget-accessible.h>
enum
{
PROP_0,
PROP_LABEL,
PROP_ICON_NAME,
PROP_BUTTON_MASK,
PROP_TOGGLE_MODE,
PROP_CHECKED,
PROP_PRESSED,
N_PROPS
};
static GParamSpec *props[N_PROPS] = { NULL, };
enum
{
CLICKED,
LAST_SIGNAL
};
typedef struct _StButtonPrivate StButtonPrivate;
struct _StButtonPrivate
{
gchar *text;
ClutterInputDevice *device;
ClutterEventSequence *press_sequence;
ClutterGrab *grab;
guint button_mask : 3;
guint is_toggle : 1;
guint pressed : 3;
guint grabbed : 3;
guint is_checked : 1;
};
static guint button_signals[LAST_SIGNAL] = { 0, };
G_DEFINE_TYPE_WITH_PRIVATE (StButton, st_button, ST_TYPE_BIN);
static GType st_button_accessible_get_type (void) G_GNUC_CONST;
static void
st_button_update_label_style (StButton *button)
{
ClutterActor *label;
label = st_bin_get_child (ST_BIN (button));
/* check the child is really a label */
if (!CLUTTER_IS_TEXT (label))
return;
_st_set_text_from_style (CLUTTER_TEXT (label), st_widget_get_theme_node (ST_WIDGET (button)));
}
static void
st_button_style_changed (StWidget *widget)
{
StButton *button = ST_BUTTON (widget);
StButtonClass *button_class = ST_BUTTON_GET_CLASS (button);
ST_WIDGET_CLASS (st_button_parent_class)->style_changed (widget);
/* update the label styling */
st_button_update_label_style (button);
/* run a transition if applicable */
if (button_class->transition)
{
button_class->transition (button);
}
}
static void
st_button_press (StButton *button,
ClutterInputDevice *device,
StButtonMask mask,
ClutterEventSequence *sequence)
{
StButtonPrivate *priv = st_button_get_instance_private (button);
gboolean active_changed = priv->pressed == 0 || sequence;
if (active_changed)
st_widget_add_style_pseudo_class (ST_WIDGET (button), "active");
priv->pressed |= mask;
priv->press_sequence = sequence;
priv->device = device;
if (active_changed)
g_object_notify_by_pspec (G_OBJECT (button), props[PROP_PRESSED]);
}
static void
st_button_release (StButton *button,
ClutterInputDevice *device,
StButtonMask mask,
int clicked_button,
ClutterEventSequence *sequence)
{
StButtonPrivate *priv = st_button_get_instance_private (button);
if ((device && priv->device != device) ||
(sequence && priv->press_sequence != sequence))
return;
else if (!sequence)
{
priv->pressed &= ~mask;
if (priv->pressed != 0)
return;
}
priv->press_sequence = NULL;
priv->device = NULL;
st_widget_remove_style_pseudo_class (ST_WIDGET (button), "active");
g_object_notify_by_pspec (G_OBJECT (button), props[PROP_PRESSED]);
if (clicked_button || sequence)
{
if (priv->is_toggle)
st_button_set_checked (button, !priv->is_checked);
g_signal_emit (button, button_signals[CLICKED], 0, clicked_button);
}
}
static gboolean
st_button_button_press (ClutterActor *actor,
ClutterEvent *event)
{
StButton *button = ST_BUTTON (actor);
StButtonPrivate *priv = st_button_get_instance_private (button);
int button_nr = clutter_event_get_button (event);
StButtonMask mask = ST_BUTTON_MASK_FROM_BUTTON (button_nr);
ClutterInputDevice *device = clutter_event_get_device (event);
if (priv->press_sequence)
return CLUTTER_EVENT_PROPAGATE;
if (priv->button_mask & mask)
{
ClutterActor *stage;
stage = clutter_actor_get_stage (actor);
if (priv->grabbed == 0)
priv->grab = clutter_stage_grab (CLUTTER_STAGE (stage), actor);
priv->grabbed |= mask;
st_button_press (button, device, mask, NULL);
return TRUE;
}
return FALSE;
}
static gboolean
st_button_button_release (ClutterActor *actor,
ClutterEvent *event)
{
StButton *button = ST_BUTTON (actor);
StButtonPrivate *priv = st_button_get_instance_private (button);
int button_nr = clutter_event_get_button (event);
StButtonMask mask = ST_BUTTON_MASK_FROM_BUTTON (button_nr);
ClutterInputDevice *device = clutter_event_get_device (event);
if (priv->button_mask & mask)
{
ClutterStage *stage;
ClutterActor *target;
gboolean is_click;
stage = CLUTTER_STAGE (clutter_actor_get_stage (actor));
target = clutter_stage_get_event_actor (stage, event);
is_click = priv->grabbed && clutter_actor_contains (actor, target);
st_button_release (button, device, mask, is_click ? button_nr : 0, NULL);
priv->grabbed &= ~mask;
if (priv->grab && priv->grabbed == 0)
{
clutter_grab_dismiss (priv->grab);
g_clear_pointer (&priv->grab, clutter_grab_unref);
}
return TRUE;
}
return FALSE;
}
static gboolean
st_button_touch_event (ClutterActor *actor,
ClutterEvent *event)
{
StButton *button = ST_BUTTON (actor);
StButtonPrivate *priv = st_button_get_instance_private (button);
StButtonMask mask = ST_BUTTON_MASK_FROM_BUTTON (1);
ClutterEventSequence *sequence;
ClutterInputDevice *device;
ClutterEventType event_type;
if (priv->pressed != 0)
return CLUTTER_EVENT_PROPAGATE;
if ((priv->button_mask & mask) == 0)
return CLUTTER_EVENT_PROPAGATE;
device = clutter_event_get_device (event);
sequence = clutter_event_get_event_sequence (event);
event_type = clutter_event_type (event);
if (event_type == CLUTTER_TOUCH_BEGIN && !priv->grab && !priv->press_sequence)
{
st_button_press (button, device, 0, sequence);
return CLUTTER_EVENT_STOP;
}
else if (event_type == CLUTTER_TOUCH_END &&
priv->device == device &&
priv->press_sequence == sequence)
{
st_button_release (button, device, mask, 0, sequence);
return CLUTTER_EVENT_STOP;
}
else if (event_type == CLUTTER_TOUCH_CANCEL)
{
st_button_fake_release (button);
}
return CLUTTER_EVENT_PROPAGATE;
}
static gboolean
st_button_key_press (ClutterActor *actor,
ClutterEvent *event)
{
StButton *button = ST_BUTTON (actor);
StButtonPrivate *priv = st_button_get_instance_private (button);
uint32_t keyval;
if (priv->button_mask & ST_BUTTON_ONE)
{
keyval = clutter_event_get_key_symbol (event);
if (keyval == CLUTTER_KEY_space ||
keyval == CLUTTER_KEY_Return ||
keyval == CLUTTER_KEY_KP_Enter ||
keyval == CLUTTER_KEY_ISO_Enter)
{
st_button_press (button, NULL, ST_BUTTON_ONE, NULL);
return TRUE;
}
}
return CLUTTER_ACTOR_CLASS (st_button_parent_class)->key_press_event (actor, event);
}
static gboolean
st_button_key_release (ClutterActor *actor,
ClutterEvent *event)
{
StButton *button = ST_BUTTON (actor);
StButtonPrivate *priv = st_button_get_instance_private (button);
uint32_t keyval;
if (priv->button_mask & ST_BUTTON_ONE)
{
keyval = clutter_event_get_key_symbol (event);
if (keyval == CLUTTER_KEY_space ||
keyval == CLUTTER_KEY_Return ||
keyval == CLUTTER_KEY_KP_Enter ||
keyval == CLUTTER_KEY_ISO_Enter)
{
gboolean is_click;
is_click = (priv->pressed & ST_BUTTON_ONE);
st_button_release (button, NULL, ST_BUTTON_ONE, is_click ? 1 : 0, NULL);
return TRUE;
}
}
return FALSE;
}
static void
st_button_key_focus_out (ClutterActor *actor)
{
StButton *button = ST_BUTTON (actor);
StButtonPrivate *priv = st_button_get_instance_private (button);
/* If we lose focus between a key press and release, undo the press */
if ((priv->pressed & ST_BUTTON_ONE) &&
!(priv->grabbed & ST_BUTTON_ONE))
st_button_release (button, NULL, ST_BUTTON_ONE, 0, NULL);
CLUTTER_ACTOR_CLASS (st_button_parent_class)->key_focus_out (actor);
}
static gboolean
st_button_enter (ClutterActor *actor,
ClutterEvent *event)
{
StButton *button = ST_BUTTON (actor);
StButtonPrivate *priv = st_button_get_instance_private (button);
gboolean ret;
ret = CLUTTER_ACTOR_CLASS (st_button_parent_class)->enter_event (actor, event);
if (priv->grabbed)
{
if (st_widget_get_hover (ST_WIDGET (button)))
st_button_press (button, priv->device,
priv->grabbed, NULL);
else
st_button_release (button, priv->device,
priv->grabbed, 0, NULL);
}
return ret;
}
static gboolean
st_button_leave (ClutterActor *actor,
ClutterEvent *event)
{
StButton *button = ST_BUTTON (actor);
StButtonPrivate *priv = st_button_get_instance_private (button);
gboolean ret;
ret = CLUTTER_ACTOR_CLASS (st_button_parent_class)->leave_event (actor, event);
if (priv->grabbed)
{
if (st_widget_get_hover (ST_WIDGET (button)))
st_button_press (button, priv->device,
priv->grabbed, NULL);
else
st_button_release (button, priv->device,
priv->grabbed, 0, NULL);
}
return ret;
}
static void
st_button_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
StButton *button = ST_BUTTON (gobject);
switch (prop_id)
{
case PROP_LABEL:
st_button_set_label (button, g_value_get_string (value));
break;
case PROP_ICON_NAME:
st_button_set_icon_name (button, g_value_get_string (value));
break;
case PROP_BUTTON_MASK:
st_button_set_button_mask (button, g_value_get_flags (value));
break;
case PROP_TOGGLE_MODE:
st_button_set_toggle_mode (button, g_value_get_boolean (value));
break;
case PROP_CHECKED:
st_button_set_checked (button, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
st_button_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
StButtonPrivate *priv = st_button_get_instance_private (ST_BUTTON (gobject));
switch (prop_id)
{
case PROP_LABEL:
g_value_set_string (value, priv->text);
break;
case PROP_ICON_NAME:
g_value_set_string (value, st_button_get_icon_name (ST_BUTTON (gobject)));
break;
case PROP_BUTTON_MASK:
g_value_set_flags (value, priv->button_mask);
break;
case PROP_TOGGLE_MODE:
g_value_set_boolean (value, priv->is_toggle);
break;
case PROP_CHECKED:
g_value_set_boolean (value, priv->is_checked);
break;
case PROP_PRESSED:
g_value_set_boolean (value, priv->pressed != 0 || priv->press_sequence != NULL);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
st_button_finalize (GObject *gobject)
{
StButtonPrivate *priv = st_button_get_instance_private (ST_BUTTON (gobject));
g_free (priv->text);
G_OBJECT_CLASS (st_button_parent_class)->finalize (gobject);
}
static void
st_button_class_init (StButtonClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
gobject_class->set_property = st_button_set_property;
gobject_class->get_property = st_button_get_property;
gobject_class->finalize = st_button_finalize;
actor_class->button_press_event = st_button_button_press;
actor_class->button_release_event = st_button_button_release;
actor_class->key_press_event = st_button_key_press;
actor_class->key_release_event = st_button_key_release;
actor_class->key_focus_out = st_button_key_focus_out;
actor_class->enter_event = st_button_enter;
actor_class->leave_event = st_button_leave;
actor_class->touch_event = st_button_touch_event;
widget_class->style_changed = st_button_style_changed;
widget_class->get_accessible_type = st_button_accessible_get_type;
/**
* StButton:label:
*
* The label of the #StButton.
*/
props[PROP_LABEL] =
g_param_spec_string ("label",
"Label",
"Label of the button",
NULL,
ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
/**
* StButton:icon-name:
*
* The icon name of the #StButton.
*/
props[PROP_ICON_NAME] =
g_param_spec_string ("icon-name",
"Icon name",
"Icon name of the button",
NULL,
ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
/**
* StButton:button-mask:
*
* Which buttons will trigger the #StButton::clicked signal.
*/
props[PROP_BUTTON_MASK] =
g_param_spec_flags ("button-mask",
"Button mask",
"Which buttons trigger the 'clicked' signal",
ST_TYPE_BUTTON_MASK, ST_BUTTON_ONE,
ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
/**
* StButton:toggle-mode:
*
* Whether the #StButton is operating in toggle mode (on/off).
*/
props[PROP_TOGGLE_MODE] =
g_param_spec_boolean ("toggle-mode",
"Toggle Mode",
"Enable or disable toggling",
FALSE,
ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
/**
* StButton:checked:
*
* If #StButton:toggle-mode is %TRUE, indicates if the #StButton is toggled
* "on" or "off".
*
* When the value is %TRUE, the #StButton will have the `checked` CSS
* pseudo-class set.
*/
props[PROP_CHECKED] =
g_param_spec_boolean ("checked",
"Checked",
"Indicates if a toggle button is \"on\" or \"off\"",
FALSE,
ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
/**
* StButton:pressed:
*
* In contrast to #StButton:checked, this property indicates whether the
* #StButton is being actively pressed, rather than just in the "on" state.
*/
props[PROP_PRESSED] =
g_param_spec_boolean ("pressed",
"Pressed",
"Indicates if the button is pressed in",
FALSE,
ST_PARAM_READABLE);
g_object_class_install_properties (gobject_class, N_PROPS, props);
/**
* StButton::clicked:
* @button: the object that received the signal
* @clicked_button: the mouse button that was used
*
* Emitted when the user activates the button, either with a mouse press and
* release or with the keyboard.
*/
button_signals[CLICKED] =
g_signal_new ("clicked",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (StButtonClass, clicked),
NULL, NULL, NULL,
G_TYPE_NONE, 1,
G_TYPE_INT);
}
static void
st_button_init (StButton *button)
{
StButtonPrivate *priv = st_button_get_instance_private (button);
priv->button_mask = ST_BUTTON_ONE;
clutter_actor_set_reactive (CLUTTER_ACTOR (button), TRUE);
st_widget_set_track_hover (ST_WIDGET (button), TRUE);
}
/**
* st_button_new:
*
* Create a new button
*
* Returns: a new #StButton
*/
StWidget *
st_button_new (void)
{
return g_object_new (ST_TYPE_BUTTON, NULL);
}
/**
* st_button_new_with_label:
* @text: text to set the label to
*
* Create a new #StButton with the specified label
*
* Returns: a new #StButton
*/
StWidget *
st_button_new_with_label (const gchar *text)
{
return g_object_new (ST_TYPE_BUTTON, "label", text, NULL);
}
/**
* st_button_get_label:
* @button: a #StButton
*
* Get the text displayed on the button. If the label is empty, an empty string
* will be returned instead of %NULL.
*
* Returns: (transfer none): the text for the button
*/
const gchar *
st_button_get_label (StButton *button)
{
g_return_val_if_fail (ST_IS_BUTTON (button), NULL);
return ((StButtonPrivate *)st_button_get_instance_private (button))->text;
}
/**
* st_button_set_label:
* @button: a #Stbutton
* @text: (nullable): text to set the label to
*
* Sets the text displayed on the button.
*/
void
st_button_set_label (StButton *button,
const gchar *text)
{
StButtonPrivate *priv;
ClutterActor *label;
g_return_if_fail (ST_IS_BUTTON (button));
priv = st_button_get_instance_private (button);
if (g_strcmp0 (priv->text, text) == 0)
return;
g_free (priv->text);
if (text)
priv->text = g_strdup (text);
else
priv->text = g_strdup ("");
label = st_bin_get_child (ST_BIN (button));
if (label && CLUTTER_IS_TEXT (label))
{
clutter_text_set_text (CLUTTER_TEXT (label), priv->text);
}
else
{
label = g_object_new (CLUTTER_TYPE_TEXT,
"text", priv->text,
"line-alignment", PANGO_ALIGN_CENTER,
"ellipsize", PANGO_ELLIPSIZE_END,
"use-markup", TRUE,
"x-align", CLUTTER_ACTOR_ALIGN_CENTER,
"y-align", CLUTTER_ACTOR_ALIGN_CENTER,
NULL);
st_bin_set_child (ST_BIN (button), label);
}
/* Fake a style change so that we reset the style properties on the label */
st_widget_style_changed (ST_WIDGET (button));
g_object_notify_by_pspec (G_OBJECT (button), props[PROP_LABEL]);
}
/**
* st_button_get_icon_name:
* @button: a #StButton
*
* Get the icon name of the button. If the button isn't showing an icon,
* the return value will be %NULL.
*
* Returns: (transfer none) (nullable): the icon name of the button
*/
const char *
st_button_get_icon_name (StButton *button)
{
ClutterActor *icon;
g_return_val_if_fail (ST_IS_BUTTON (button), NULL);
icon = st_bin_get_child (ST_BIN (button));
if (ST_IS_ICON (icon))
return st_icon_get_icon_name (ST_ICON (icon));
return NULL;
}
/**
* st_button_set_icon_name:
* @button: a #Stbutton
* @icon_name: an icon name
*
* Adds an `StIcon` with the given icon name as a child.
*
* If @button already contains a child actor, that child will
* be removed and replaced with the icon.
*/
void
st_button_set_icon_name (StButton *button,
const char *icon_name)
{
ClutterActor *icon;
g_return_if_fail (ST_IS_BUTTON (button));
g_return_if_fail (icon_name != NULL);
icon = st_bin_get_child (ST_BIN (button));
if (ST_IS_ICON (icon))
{
if (g_strcmp0 (st_icon_get_icon_name (ST_ICON (icon)), icon_name) == 0)
return;
st_icon_set_icon_name (ST_ICON (icon), icon_name);
}
else
{
icon = g_object_new (ST_TYPE_ICON,
"icon-name", icon_name,
"x-align", CLUTTER_ACTOR_ALIGN_CENTER,
"y-align", CLUTTER_ACTOR_ALIGN_CENTER,
NULL);
st_bin_set_child (ST_BIN (button), icon);
}
g_object_notify_by_pspec (G_OBJECT (button), props[PROP_ICON_NAME]);
}
/**
* st_button_get_button_mask:
* @button: a #StButton
*
* Gets the mask of mouse buttons that @button emits the
* #StButton::clicked signal for.
*
* Returns: the mask of mouse buttons that @button emits the
* #StButton::clicked signal for.
*/
StButtonMask
st_button_get_button_mask (StButton *button)
{
g_return_val_if_fail (ST_IS_BUTTON (button), 0);
return ((StButtonPrivate *)st_button_get_instance_private (button))->button_mask;
}
/**
* st_button_set_button_mask:
* @button: a #Stbutton
* @mask: the mask of mouse buttons that @button responds to
*
* Sets which mouse buttons @button emits #StButton::clicked for.
*/
void
st_button_set_button_mask (StButton *button,
StButtonMask mask)
{
StButtonPrivate *priv;
g_return_if_fail (ST_IS_BUTTON (button));
priv = st_button_get_instance_private (button);
if (priv->button_mask == mask)
return;
priv->button_mask = mask;
g_object_notify_by_pspec (G_OBJECT (button), props[PROP_BUTTON_MASK]);
}
/**
* st_button_get_toggle_mode:
* @button: a #StButton
*
* Get the toggle mode status of the button.
*
* Returns: %TRUE if toggle mode is set, otherwise %FALSE
*/
gboolean
st_button_get_toggle_mode (StButton *button)
{
g_return_val_if_fail (ST_IS_BUTTON (button), FALSE);
return ((StButtonPrivate *)st_button_get_instance_private (button))->is_toggle;
}
/**
* st_button_set_toggle_mode:
* @button: a #Stbutton
* @toggle: %TRUE or %FALSE
*
* Enables or disables toggle mode for the button. In toggle mode, the active
* state will be "toggled" when the user clicks the button.
*/
void
st_button_set_toggle_mode (StButton *button,
gboolean toggle)
{
StButtonPrivate *priv;
g_return_if_fail (ST_IS_BUTTON (button));
priv = st_button_get_instance_private (button);
if (priv->is_toggle == toggle)
return;
priv->is_toggle = toggle;
g_object_notify_by_pspec (G_OBJECT (button), props[PROP_TOGGLE_MODE]);
}
/**
* st_button_get_checked:
* @button: a #StButton
*
* Get the #StButton:checked property of a #StButton that is in toggle mode.
*
* Returns: %TRUE if the button is checked, or %FALSE if not
*/
gboolean
st_button_get_checked (StButton *button)
{
g_return_val_if_fail (ST_IS_BUTTON (button), FALSE);
return ((StButtonPrivate *)st_button_get_instance_private (button))->is_checked;
}
/**
* st_button_set_checked:
* @button: a #Stbutton
* @checked: %TRUE or %FALSE
*
* Set the #StButton:checked property of the button. This is only really useful
* if the button has #StButton:toggle-mode property set to %TRUE.
*/
void
st_button_set_checked (StButton *button,
gboolean checked)
{
StButtonPrivate *priv;
g_return_if_fail (ST_IS_BUTTON (button));
priv = st_button_get_instance_private (button);
if (priv->is_checked == checked)
return;
priv->is_checked = checked;
if (checked)
st_widget_add_style_pseudo_class (ST_WIDGET (button), "checked");
else
st_widget_remove_style_pseudo_class (ST_WIDGET (button), "checked");
g_object_notify_by_pspec (G_OBJECT (button), props[PROP_CHECKED]);
}
/**
* st_button_fake_release:
* @button: an #StButton
*
* If this widget is holding a pointer grab, this function will
* will ungrab it, and reset the #StButton:pressed state. The effect is
* similar to if the user had released the mouse button, but without
* emitting the #StButton::clicked signal.
*
* This function is useful if for example you want to do something
* after the user is holding the mouse button for a given period of
* time, breaking the grab.
*/
void
st_button_fake_release (StButton *button)
{
StButtonPrivate *priv;
g_return_if_fail (ST_IS_BUTTON (button));
priv = st_button_get_instance_private (button);
if (priv->grab)
{
clutter_grab_dismiss (priv->grab);
g_clear_pointer (&priv->grab, clutter_grab_unref);
}
priv->grabbed = 0;
if (priv->pressed || priv->press_sequence)
st_button_release (button, priv->device,
priv->pressed, 0, NULL);
}
/******************************************************************************/
/*************************** ACCESSIBILITY SUPPORT ****************************/
/******************************************************************************/
#define ST_TYPE_BUTTON_ACCESSIBLE st_button_accessible_get_type ()
#define ST_BUTTON_ACCESSIBLE(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), \
ST_TYPE_BUTTON_ACCESSIBLE, StButtonAccessible))
#define ST_IS_BUTTON_ACCESSIBLE(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
ST_TYPE_BUTTON_ACCESSIBLE))
#define ST_BUTTON_ACCESSIBLE_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST ((klass), \
ST_TYPE_BUTTON_ACCESSIBLE, StButtonAccessibleClass))
#define ST_IS_BUTTON_ACCESSIBLE_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), \
ST_TYPE_BUTTON_ACCESSIBLE))
#define ST_BUTTON_ACCESSIBLE_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS ((obj), \
ST_TYPE_BUTTON_ACCESSIBLE, StButtonAccessibleClass))
typedef struct _StButtonAccessible StButtonAccessible;
typedef struct _StButtonAccessibleClass StButtonAccessibleClass;
struct _StButtonAccessible
{
StWidgetAccessible parent;
};
struct _StButtonAccessibleClass
{
StWidgetAccessibleClass parent_class;
};
/* AtkObject */
static void st_button_accessible_initialize (AtkObject *obj,
gpointer data);
G_DEFINE_TYPE (StButtonAccessible, st_button_accessible, ST_TYPE_WIDGET_ACCESSIBLE)
static const gchar *
st_button_accessible_get_name (AtkObject *obj)
{
StButton *button = NULL;
const gchar *name = NULL;
button = ST_BUTTON (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (obj)));
if (button == NULL)
return NULL;
name = ATK_OBJECT_CLASS (st_button_accessible_parent_class)->get_name (obj);
if (name != NULL)
return name;
return st_button_get_label (button);
}
static void
st_button_accessible_class_init (StButtonAccessibleClass *klass)
{
AtkObjectClass *atk_class = ATK_OBJECT_CLASS (klass);
atk_class->initialize = st_button_accessible_initialize;
atk_class->get_name = st_button_accessible_get_name;
}
static void
st_button_accessible_init (StButtonAccessible *self)
{
/* initialization done on AtkObject->initialize */
}
static void
st_button_accessible_notify_label_cb (StButton *button,
GParamSpec *psec,
AtkObject *accessible)
{
g_object_notify (G_OBJECT (accessible), "accessible-name");
}
static void
st_button_accessible_compute_role (AtkObject *accessible,
StButton *button)
{
atk_object_set_role (accessible, st_button_get_toggle_mode (button)
? ATK_ROLE_TOGGLE_BUTTON : ATK_ROLE_PUSH_BUTTON);
}
static void
st_button_accessible_notify_toggle_mode_cb (StButton *button,
GParamSpec *psec,
AtkObject *accessible)
{
st_button_accessible_compute_role (accessible, button);
}
static void
st_button_accessible_initialize (AtkObject *obj,
gpointer data)
{
ATK_OBJECT_CLASS (st_button_accessible_parent_class)->initialize (obj, data);
st_button_accessible_compute_role (obj, ST_BUTTON (data));
g_signal_connect (data, "notify::label",
G_CALLBACK (st_button_accessible_notify_label_cb), obj);
g_signal_connect (data, "notify::toggle-mode",
G_CALLBACK (st_button_accessible_notify_toggle_mode_cb), obj);
}