From c86a977564e5d2db83b0be503cb10a4c4129eeec Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Tue, 25 Jan 2011 16:22:00 -0500 Subject: [PATCH] St: drop StClickable, add some functionality to StButton For historical reasons, we had both StClickable and StButton, which were nearly identical. StButton was more widely-used, so keep that and port all StClickable users to that. https://bugzilla.gnome.org/show_bug.cgi?id=640583 --- data/theme/gnome-shell.css | 7 +- js/ui/altTab.js | 4 +- js/ui/appDisplay.js | 14 +- js/ui/dnd.js | 10 +- js/ui/endSessionDialog.js | 12 +- js/ui/panel.js | 14 +- js/ui/searchDisplay.js | 26 +-- src/Makefile-st.am | 2 - src/st/st-button.c | 219 +++++++++++++++++----- src/st/st-button.h | 44 +++-- src/st/st-clickable.c | 363 ------------------------------------- src/st/st-clickable.h | 54 ------ src/st/st-marshal.list | 1 + 13 files changed, 244 insertions(+), 526 deletions(-) delete mode 100644 src/st/st-clickable.c delete mode 100644 src/st/st-clickable.h diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index 3e7cc8112..a79b01cc2 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -217,7 +217,7 @@ StTooltip StLabel { transition-duration: 100; } -.panel-button:active, .panel-button:checked, .panel-button:pressed, .panel-button:focus { +.panel-button:active, .panel-button:checked, .panel-button:focus { background-gradient-direction: vertical; background-gradient-start: #3c3c3c; background-gradient-end: #131313; @@ -541,11 +541,6 @@ StTooltip StLabel { transition-duration: 100; } -.app-well-app:active > .overview-icon { - background-color: #1e1e1e; - border: 1px solid #5f5f5f; -} - .app-well-menu { font-size: 12px } diff --git a/js/ui/altTab.js b/js/ui/altTab.js index 03b8fdca7..46c2a09bf 100644 --- a/js/ui/altTab.js +++ b/js/ui/altTab.js @@ -593,8 +593,8 @@ SwitcherList.prototype = { }, addItem : function(item) { - let bbox = new St.Clickable({ style_class: 'item-box', - reactive: true }); + let bbox = new St.Button({ style_class: 'item-box', + reactive: true }); bbox.set_child(item); this._list.add_actor(bbox); diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js index c5fe8c6a6..4628a5a7a 100644 --- a/js/ui/appDisplay.js +++ b/js/ui/appDisplay.js @@ -319,10 +319,11 @@ function AppWellIcon(app, iconParams) { AppWellIcon.prototype = { _init : function(app, iconParams) { this.app = app; - this.actor = new St.Clickable({ style_class: 'app-well-app', - reactive: true, - x_fill: true, - y_fill: true }); + this.actor = new St.Button({ style_class: 'app-well-app', + reactive: true, + button_mask: St.ButtonMask.ONE | St.ButtonMask.TWO, + x_fill: true, + y_fill: true }); this.actor._delegate = this; this.icon = new AppIcon(app, iconParams); @@ -390,12 +391,11 @@ AppWellIcon.prototype = { return false; }, - _onClicked: function(actor, event) { + _onClicked: function(actor, button) { this._removeMenuTimeout(); - let button = event.get_button(); if (button == 1) { - this._onActivate(event); + this._onActivate(Clutter.get_current_event()); } else if (button == 2) { let newWorkspace = Main.overview.workspaces.addWorkspace(); if (newWorkspace != null) { diff --git a/js/ui/dnd.js b/js/ui/dnd.js index 71d8c04bc..7c88dc22a 100644 --- a/js/ui/dnd.js +++ b/js/ui/dnd.js @@ -112,11 +112,11 @@ _Draggable.prototype = { return false; this._buttonDown = true; - // special case St.Clickable: grabbing the pointer would mess up the + // special case St.Button: grabbing the pointer would mess up the // internal state, so we start the drag manually on hover change - if (this.actor instanceof St.Clickable) + if (this.actor instanceof St.Button) this.actor.connect('notify::hover', - Lang.bind(this, this._onClickableHoverChanged)); + Lang.bind(this, this._onButtonHoverChanged)); else this._grabActor(); @@ -127,8 +127,8 @@ _Draggable.prototype = { return false; }, - _onClickableHoverChanged: function(button) { - if (button.hover || !button.held) + _onButtonHoverChanged: function(button) { + if (button.hover || !button.pressed) return; button.fake_release(); diff --git a/js/ui/endSessionDialog.js b/js/ui/endSessionDialog.js index c25633ac2..6b85bd87f 100644 --- a/js/ui/endSessionDialog.js +++ b/js/ui/endSessionDialog.js @@ -137,12 +137,12 @@ ListItem.prototype = { let layout = new St.BoxLayout({ vertical: false}); - this.actor = new St.Clickable({ style_class: 'end-session-dialog-app-list-item', - can_focus: true, - child: layout, - reactive: true, - x_align: St.Align.START, - x_fill: true }); + this.actor = new St.Button({ style_class: 'end-session-dialog-app-list-item', + can_focus: true, + child: layout, + reactive: true, + x_align: St.Align.START, + x_fill: true }); this._icon = this._app.create_icon_texture(_ITEM_ICON_SIZE); diff --git a/js/ui/panel.js b/js/ui/panel.js index 9b7c3b36a..4d0e00145 100644 --- a/js/ui/panel.js +++ b/js/ui/panel.js @@ -612,10 +612,10 @@ Panel.prototype = { /* Button on the left side of the panel. */ /* Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview". */ let label = new St.Label({ text: _("Activities") }); - this.button = new St.Clickable({ name: 'panelActivities', - style_class: 'panel-button', - reactive: true, - can_focus: true }); + this.button = new St.Button({ name: 'panelActivities', + style_class: 'panel-button', + reactive: true, + can_focus: true }); this.button.set_child(label); this.button._delegate = this.button; this.button._xdndTimeOut = 0; @@ -712,7 +712,7 @@ Panel.prototype = { // We get into the Overview mode on button-press-event as opposed to button-release-event because eventually we'll probably // have the Overview act like a menu that allows the user to release the mouse on the activity the user wants // to switch to. - this.button.connect('clicked', Lang.bind(this, function(b, event) { + this.button.connect('clicked', Lang.bind(this, function(b) { if (!Main.overview.animationInProgress) { this._maybeToggleOverviewOnClick(); return true; @@ -724,10 +724,10 @@ Panel.prototype = { // pressing the System key, Alt+F1 or Esc. We want the button to be pressed in when the Overview is entered // and to be released when it is exited regardless of how it was triggered. Main.overview.connect('showing', Lang.bind(this, function() { - this.button.active = true; + this.button.checked = true; })); Main.overview.connect('hiding', Lang.bind(this, function() { - this.button.active = false; + this.button.checked = false; })); Main.chrome.addActor(this.actor, { visibleInOverview: true }); diff --git a/js/ui/searchDisplay.js b/js/ui/searchDisplay.js index 3ed60ad5e..44794f6a9 100644 --- a/js/ui/searchDisplay.js +++ b/js/ui/searchDisplay.js @@ -24,10 +24,10 @@ SearchResult.prototype = { _init: function(provider, metaInfo, terms) { this.provider = provider; this.metaInfo = metaInfo; - this.actor = new St.Clickable({ style_class: 'search-result', - reactive: true, - x_align: St.Align.START, - y_fill: true }); + this.actor = new St.Button({ style_class: 'search-result', + reactive: true, + x_align: St.Align.START, + y_fill: true }); this.actor._delegate = this; let content = provider.createResultActor(metaInfo, terms); @@ -69,7 +69,7 @@ SearchResult.prototype = { Main.overview.toggle(); }, - _onResultClicked: function(actor, event) { + _onResultClicked: function(actor) { this.activate(); }, @@ -221,23 +221,23 @@ SearchResults.prototype = { }, _createOpenSearchProviderButton: function(provider) { - let clickable = new St.Clickable({ style_class: 'dash-search-button', - reactive: true, - x_fill: true, - y_align: St.Align.MIDDLE }); + let button = new St.Button({ style_class: 'dash-search-button', + reactive: true, + x_fill: true, + y_align: St.Align.MIDDLE }); let bin = new St.Bin({ x_fill: false, x_align:St.Align.MIDDLE }); - clickable.connect('clicked', Lang.bind(this, function() { + button.connect('clicked', Lang.bind(this, function() { this._openSearchSystem.activateResult(provider.id); })); let title = new St.Label({ text: provider.name, style_class: 'dash-search-button-label' }); bin.set_child(title); - clickable.set_child(bin); - provider.actor = clickable; + button.set_child(bin); + provider.actor = button; - this._searchProvidersBox.add(clickable); + this._searchProvidersBox.add(button); }, createProviderMeta: function(provider) { diff --git a/src/Makefile-st.am b/src/Makefile-st.am index cb54ef4ba..ffeeb21b8 100644 --- a/src/Makefile-st.am +++ b/src/Makefile-st.am @@ -72,7 +72,6 @@ st_source_h = \ st/st-box-layout.h \ st/st-box-layout-child.h \ st/st-button.h \ - st/st-clickable.h \ st/st-clipboard.h \ st/st-container.h \ st/st-drawing-area.h \ @@ -130,7 +129,6 @@ st_source_c = \ st/st-box-layout.c \ st/st-box-layout-child.c \ st/st-button.c \ - st/st-clickable.c \ st/st-clipboard.c \ st/st-container.c \ st/st-drawing-area.c \ diff --git a/src/st/st-button.c b/src/st/st-button.c index dfee0265e..c3eca6118 100644 --- a/src/st/st-button.c +++ b/src/st/st-button.c @@ -40,6 +40,7 @@ #include "st-button.h" +#include "st-enum-types.h" #include "st-marshal.h" #include "st-texture-cache.h" #include "st-private.h" @@ -49,8 +50,10 @@ enum PROP_0, PROP_LABEL, + PROP_BUTTON_MASK, PROP_TOGGLE_MODE, - PROP_CHECKED + PROP_CHECKED, + PROP_PRESSED }; enum @@ -65,14 +68,16 @@ enum struct _StButtonPrivate { - gchar *text; + gchar *text; - guint is_pressed : 1; - guint is_checked : 1; - guint is_toggle : 1; - guint has_grab : 1; + guint button_mask : 3; + guint is_toggle : 1; - gint spacing; + guint pressed : 3; + guint grabbed : 3; + guint is_checked : 1; + + gint spacing; }; static guint button_signals[LAST_SIGNAL] = { 0, }; @@ -119,31 +124,32 @@ st_button_style_changed (StWidget *widget) } static void -st_button_press (StButton *button) +st_button_press (StButton *button, + StButtonMask mask) { - if (button->priv->is_pressed) - return; + if (button->priv->pressed == 0) + st_widget_add_style_pseudo_class (ST_WIDGET (button), "active"); - button->priv->is_pressed = TRUE; - st_widget_add_style_pseudo_class (ST_WIDGET (button), "active"); + button->priv->pressed |= mask; } static void -st_button_release (StButton *button, - gboolean clicked) +st_button_release (StButton *button, + StButtonMask mask, + int clicked_button) { - if (!button->priv->is_pressed) + button->priv->pressed &= ~mask; + if (button->priv->pressed != 0) return; - button->priv->is_pressed = FALSE; st_widget_remove_style_pseudo_class (ST_WIDGET (button), "active"); - if (clicked) + if (clicked_button) { if (button->priv->is_toggle) st_button_set_checked (button, !button->priv->is_checked); - g_signal_emit (button, button_signals[CLICKED], 0); + g_signal_emit (button, button_signals[CLICKED], 0, clicked_button); } } @@ -151,15 +157,18 @@ static gboolean st_button_button_press (ClutterActor *actor, ClutterButtonEvent *event) { + StButton *button = ST_BUTTON (actor); + StButtonMask mask = ST_BUTTON_MASK_FROM_BUTTON (event->button); + st_widget_hide_tooltip (ST_WIDGET (actor)); - if (event->button == 1) + if (button->priv->button_mask & mask) { - StButton *button = ST_BUTTON (actor); + if (button->priv->grabbed == 0) + clutter_grab_pointer (actor); - clutter_grab_pointer (actor); - button->priv->has_grab = TRUE; - st_button_press (button); + button->priv->grabbed |= mask; + st_button_press (button, mask); return TRUE; } @@ -171,19 +180,19 @@ static gboolean st_button_button_release (ClutterActor *actor, ClutterButtonEvent *event) { - if (event->button == 1) + StButton *button = ST_BUTTON (actor); + StButtonMask mask = ST_BUTTON_MASK_FROM_BUTTON (event->button); + + if (button->priv->button_mask & mask) { - StButton *button = ST_BUTTON (actor); gboolean is_click; - is_click = button->priv->has_grab && st_widget_get_hover (ST_WIDGET (button)); - st_button_release (button, is_click); + is_click = button->priv->grabbed && st_widget_get_hover (ST_WIDGET (button)); + st_button_release (button, mask, is_click ? event->button : 0); - if (button->priv->has_grab) - { - button->priv->has_grab = FALSE; - clutter_ungrab_pointer (); - } + button->priv->grabbed &= ~mask; + if (button->priv->grabbed == 0) + clutter_ungrab_pointer (); return TRUE; } @@ -195,13 +204,18 @@ static gboolean st_button_key_press (ClutterActor *actor, ClutterKeyEvent *event) { + StButton *button = ST_BUTTON (actor); + st_widget_hide_tooltip (ST_WIDGET (actor)); - if (event->keyval == CLUTTER_KEY_space || - event->keyval == CLUTTER_KEY_Return) + if (button->priv->button_mask & ST_BUTTON_ONE) { - st_button_press (ST_BUTTON (actor)); - return TRUE; + if (event->keyval == CLUTTER_KEY_space || + event->keyval == CLUTTER_KEY_Return) + { + st_button_press (button, ST_BUTTON_ONE); + return TRUE; + } } return FALSE; @@ -211,16 +225,34 @@ static gboolean st_button_key_release (ClutterActor *actor, ClutterKeyEvent *event) { - if (event->keyval == CLUTTER_KEY_space || - event->keyval == CLUTTER_KEY_Return) + StButton *button = ST_BUTTON (actor); + + if (button->priv->button_mask & ST_BUTTON_ONE) { - st_button_release (ST_BUTTON (actor), TRUE); - return TRUE; + if (event->keyval == CLUTTER_KEY_space || + event->keyval == CLUTTER_KEY_Return) + { + st_button_release (button, ST_BUTTON_ONE, 1); + return TRUE; + } } return FALSE; } +static void +st_button_key_focus_out (ClutterActor *actor) +{ + StButton *button = ST_BUTTON (actor); + + /* If we lose focus between a key press and release, undo the press */ + if ((button->priv->pressed & ST_BUTTON_ONE) && + !(button->priv->grabbed & ST_BUTTON_ONE)) + st_button_release (button, ST_BUTTON_ONE, 0); + + CLUTTER_ACTOR_CLASS (st_button_parent_class)->key_focus_out (actor); +} + static gboolean st_button_enter (ClutterActor *actor, ClutterCrossingEvent *event) @@ -230,12 +262,12 @@ st_button_enter (ClutterActor *actor, ret = CLUTTER_ACTOR_CLASS (st_button_parent_class)->enter_event (actor, event); - if (button->priv->has_grab) + if (button->priv->grabbed) { if (st_widget_get_hover (ST_WIDGET (button))) - st_button_press (button); + st_button_press (button, button->priv->grabbed); else - st_button_release (button, FALSE); + st_button_release (button, button->priv->grabbed, 0); } return ret; @@ -250,12 +282,12 @@ st_button_leave (ClutterActor *actor, ret = CLUTTER_ACTOR_CLASS (st_button_parent_class)->leave_event (actor, event); - if (button->priv->has_grab) + if (button->priv->grabbed) { if (st_widget_get_hover (ST_WIDGET (button))) - st_button_press (button); + st_button_press (button, button->priv->grabbed); else - st_button_release (button, FALSE); + st_button_release (button, button->priv->grabbed, 0); } return ret; @@ -274,6 +306,9 @@ st_button_set_property (GObject *gobject, case PROP_LABEL: st_button_set_label (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; @@ -301,12 +336,18 @@ st_button_get_property (GObject *gobject, case PROP_LABEL: g_value_set_string (value, priv->text); 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); + break; default: @@ -343,6 +384,7 @@ st_button_class_init (StButtonClass *klass) 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; @@ -354,6 +396,13 @@ st_button_class_init (StButtonClass *klass) NULL, G_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_LABEL, pspec); + pspec = g_param_spec_flags ("button-mask", + "Button mask", + "Which buttons trigger the 'clicked' signal", + ST_TYPE_BUTTON_MASK, ST_BUTTON_ONE, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_BUTTON_MASK, pspec); + pspec = g_param_spec_boolean ("toggle-mode", "Toggle Mode", "Enable or disable toggling", @@ -367,23 +416,30 @@ st_button_class_init (StButtonClass *klass) FALSE, G_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_CHECKED, pspec); + pspec = g_param_spec_boolean ("pressed", + "Pressed", + "Indicates if the button is pressed in", + FALSE, G_PARAM_READABLE); + g_object_class_install_property (gobject_class, PROP_PRESSED, pspec); + /** * 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, - _st_marshal_VOID__VOID, - G_TYPE_NONE, 0); + _st_marshal_VOID__INT, + G_TYPE_NONE, 1, + G_TYPE_INT); } static void @@ -391,6 +447,7 @@ st_button_init (StButton *button) { button->priv = ST_BUTTON_GET_PRIVATE (button); button->priv->spacing = 6; + button->priv->button_mask = ST_BUTTON_ONE; clutter_actor_set_reactive (CLUTTER_ACTOR (button), TRUE); st_widget_set_track_hover (ST_WIDGET (button), TRUE); @@ -431,7 +488,7 @@ st_button_new_with_label (const gchar *text) * * Returns: the text for the button. This must not be freed by the application */ -G_CONST_RETURN gchar * +const gchar * st_button_get_label (StButton *button) { g_return_val_if_fail (ST_IS_BUTTON (button), NULL); @@ -487,6 +544,42 @@ st_button_set_label (StButton *button, g_object_notify (G_OBJECT (button), "label"); } +/** + * 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 button->priv->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) +{ + g_return_if_fail (ST_IS_BUTTON (button)); + + button->priv->button_mask = mask; + + g_object_notify (G_OBJECT (button), "button-mask"); +} + /** * st_button_get_toggle_mode: * @button: a #StButton @@ -564,3 +657,29 @@ st_button_set_checked (StButton *button, g_object_notify (G_OBJECT (button), "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 pressed state. The effect is + * similar to if the user had released the mouse button, but without + * emitting the 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) +{ + if (button->priv->pressed) + st_button_release (button, button->priv->pressed, 0); + + if (button->priv->grabbed) + { + button->priv->grabbed = 0; + clutter_ungrab_pointer (); + } +} diff --git a/src/st/st-button.h b/src/st/st-button.h index 15a6983dd..23ae81bd1 100644 --- a/src/st/st-button.h +++ b/src/st/st-button.h @@ -68,17 +68,39 @@ struct _StButtonClass GType st_button_get_type (void) G_GNUC_CONST; -StWidget * st_button_new (void); -StWidget * st_button_new_with_label (const gchar *text); -G_CONST_RETURN gchar *st_button_get_label (StButton *button); -void st_button_set_label (StButton *button, - const gchar *text); -void st_button_set_toggle_mode (StButton *button, - gboolean toggle); -gboolean st_button_get_toggle_mode (StButton *button); -void st_button_set_checked (StButton *button, - gboolean checked); -gboolean st_button_get_checked (StButton *button); +StWidget *st_button_new (void); +StWidget *st_button_new_with_label (const gchar *text); +const gchar *st_button_get_label (StButton *button); +void st_button_set_label (StButton *button, + const gchar *text); +void st_button_set_toggle_mode (StButton *button, + gboolean toggle); +gboolean st_button_get_toggle_mode (StButton *button); +void st_button_set_checked (StButton *button, + gboolean checked); +gboolean st_button_get_checked (StButton *button); + +void st_button_fake_release (StButton *button); + +/** + * StButtonMask: + * @ST_BUTTON_ONE: button 1 (left) + * @ST_BUTTON_TWO: button 2 (middle) + * @ST_BUTTON_THREE: button 3 (right) + * + * A mask representing which mouse buttons an StButton responds to. + */ +typedef enum { + ST_BUTTON_ONE = (1 << 0), + ST_BUTTON_TWO = (1 << 1), + ST_BUTTON_THREE = (1 << 2), +} StButtonMask; + +#define ST_BUTTON_MASK_FROM_BUTTON(button) (1 << ((button) - 1)) + +void st_button_set_button_mask (StButton *button, + StButtonMask mask); +StButtonMask st_button_get_button_mask (StButton *button); G_END_DECLS diff --git a/src/st/st-clickable.c b/src/st/st-clickable.c deleted file mode 100644 index 5787aaf42..000000000 --- a/src/st/st-clickable.c +++ /dev/null @@ -1,363 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ -/* - * st-clickable.h: A bin with methods and properties useful for implementing buttons - * - * Copyright 2009, 2010 Red Hat, Inc. - * - * This program 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.1 of - * the License, or (at your option) any later version. - * - * 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 . - */ - -/** - * SECTION:st-clickable - * @short_description: A bin with methods and properties useful for implementing buttons - * - * A #StBin subclass which translates lower-level Clutter button events - * into higher level properties which are useful for implementing "button-like" - * actors. - */ - -#include "st-clickable.h" - -#include "st-private.h" - -G_DEFINE_TYPE (StClickable, st_clickable, ST_TYPE_BIN); - -struct _StClickablePrivate { - gboolean active; - gboolean held; - gboolean pressed; - - guint initiating_button; -}; - -/* Signals */ -enum -{ - CLICKED, - LAST_SIGNAL -}; - -enum { - PROP_0, - - PROP_ACTIVE, - PROP_HELD, - PROP_PRESSED -}; - -static guint st_clickable_signals [LAST_SIGNAL] = { 0 }; - -static void -sync_pseudo_class (StClickable *self) -{ - if (self->priv->pressed || self->priv->active) - st_widget_add_style_pseudo_class (ST_WIDGET (self), "pressed"); - else - st_widget_remove_style_pseudo_class (ST_WIDGET (self), "pressed"); -} - -static void -set_active (StClickable *self, - gboolean active) -{ - if (self->priv->active == active) - return; - self->priv->active = active; - sync_pseudo_class (self); - g_object_notify (G_OBJECT (self), "active"); -} - -static void -set_pressed (StClickable *self, - gboolean pressed) -{ - if (self->priv->pressed == pressed) - return; - self->priv->pressed = pressed; - sync_pseudo_class (self); - g_object_notify (G_OBJECT (self), "pressed"); -} - -static gboolean -st_clickable_enter_event (ClutterActor *actor, - ClutterCrossingEvent *event) -{ - StClickable *self = ST_CLICKABLE (actor); - gboolean result; - - g_object_freeze_notify (G_OBJECT (actor)); - - 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 result; -} - -static gboolean -st_clickable_leave_event (ClutterActor *actor, - ClutterCrossingEvent *event) -{ - StClickable *self = ST_CLICKABLE (actor); - gboolean result; - - g_object_freeze_notify (G_OBJECT (actor)); - - result = CLUTTER_ACTOR_CLASS (st_clickable_parent_class)->leave_event (actor, event); - - /* 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 -st_clickable_button_press_event (ClutterActor *actor, - ClutterButtonEvent *event) -{ - StClickable *self = ST_CLICKABLE (actor); - - if (event->click_count != 1) - return FALSE; - - if (self->priv->held) - return TRUE; - - if (!clutter_actor_contains (actor, event->source)) - return FALSE; - - self->priv->held = TRUE; - self->priv->initiating_button = event->button; - clutter_grab_pointer (CLUTTER_ACTOR (self)); - - set_pressed (self, TRUE); - - return TRUE; -} - -static gboolean -st_clickable_button_release_event (ClutterActor *actor, - ClutterButtonEvent *event) -{ - StClickable *self = ST_CLICKABLE (actor); - - if (event->button != self->priv->initiating_button || event->click_count != 1) - return FALSE; - - if (!self->priv->held) - return TRUE; - - self->priv->held = FALSE; - clutter_ungrab_pointer (); - - if (!clutter_actor_contains (actor, event->source)) - return FALSE; - - set_pressed (self, FALSE); - - g_signal_emit (G_OBJECT (self), st_clickable_signals[CLICKED], 0, event); - - return TRUE; -} - -static gboolean -st_clickable_key_press_event (ClutterActor *actor, - ClutterKeyEvent *event) -{ - StClickable *self = ST_CLICKABLE (actor); - - if (event->keyval == CLUTTER_KEY_space || - event->keyval == CLUTTER_KEY_Return) - { - set_pressed (self, TRUE); - return TRUE; - } - return FALSE; -} - -static gboolean -st_clickable_key_release_event (ClutterActor *actor, - ClutterKeyEvent *event) -{ - StClickable *self = ST_CLICKABLE (actor); - - if (event->keyval != CLUTTER_KEY_space && - event->keyval != CLUTTER_KEY_Return) - return FALSE; - - set_pressed (self, FALSE); - - g_signal_emit (G_OBJECT (self), st_clickable_signals[CLICKED], 0, event); - return TRUE; -} - -/** - * st_clickable_fake_release: - * @box: - * - * If this widget is holding a pointer grab, this function will - * will ungrab it, and reset the pressed state. The effect is - * similar to if the user had released the mouse button, but without - * emitting the 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_clickable_fake_release (StClickable *self) -{ - if (!self->priv->held) - return; - - self->priv->held = FALSE; - clutter_ungrab_pointer (); - - set_pressed (self, FALSE); -} - -static void -st_clickable_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - StClickable *self = ST_CLICKABLE (object); - - switch (prop_id) - { - case PROP_ACTIVE: - set_active (self, g_value_get_boolean (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -st_clickable_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - StClickable *self = ST_CLICKABLE (object); - - switch (prop_id) - { - case PROP_ACTIVE: - g_value_set_boolean (value, self->priv->active); - break; - case PROP_HELD: - g_value_set_boolean (value, self->priv->held); - break; - case PROP_PRESSED: - g_value_set_boolean (value, self->priv->pressed); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -st_clickable_class_init (StClickableClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); - - gobject_class->get_property = st_clickable_get_property; - gobject_class->set_property = st_clickable_set_property; - - actor_class->enter_event = st_clickable_enter_event; - actor_class->leave_event = st_clickable_leave_event; - actor_class->button_press_event = st_clickable_button_press_event; - actor_class->button_release_event = st_clickable_button_release_event; - actor_class->key_press_event = st_clickable_key_press_event; - actor_class->key_release_event = st_clickable_key_release_event; - - /** - * StClickable::clicked - * @box: The #StClickable - * - * This signal is emitted when the button should take the action - * associated with button click+release. - */ - st_clickable_signals[CLICKED] = - g_signal_new ("clicked", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__BOXED, - G_TYPE_NONE, 1, CLUTTER_TYPE_EVENT); - - /** - * StClickable:active - * - * The property allows the button to be used as a "toggle button"; it's up to the - * application to update the active property in response to the activate signal; - * it doesn't happen automatically. - */ - g_object_class_install_property (gobject_class, - PROP_ACTIVE, - g_param_spec_boolean ("active", - "Active", - "Whether the button persistently active", - FALSE, - G_PARAM_READWRITE)); - - /** - * StClickable:held - * - * This property tracks whether the button has the pointer grabbed, - * whether or not the pointer is currently hovering over the button. - */ - g_object_class_install_property (gobject_class, - PROP_HELD, - g_param_spec_boolean ("held", - "Held state", - "Whether the mouse button is currently pressed", - FALSE, - G_PARAM_READABLE)); - - /** - * StClickable:pressed - * - * This property tracks whether the button should have a "pressed in" - * effect. - */ - g_object_class_install_property (gobject_class, - PROP_PRESSED, - g_param_spec_boolean ("pressed", - "Pressed state", - "Whether the button is currently pressed", - FALSE, - G_PARAM_READABLE)); - - g_type_class_add_private (gobject_class, sizeof (StClickablePrivate)); -} - -static void -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); -} diff --git a/src/st/st-clickable.h b/src/st/st-clickable.h deleted file mode 100644 index 2f1c3b0ee..000000000 --- a/src/st/st-clickable.h +++ /dev/null @@ -1,54 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ -/* - * st-clickable.h: A bin with methods and properties useful for implementing buttons - * - * Copyright 2009, 2010 Red Hat, Inc. - * - * This program 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.1 of - * the License, or (at your option) any later version. - * - * 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 . - */ - -#ifndef __ST_CLICKABLE_H__ -#define __ST_CLICKABLE_H__ - -#include "st-bin.h" - -#define ST_TYPE_CLICKABLE (st_clickable_get_type ()) -#define ST_CLICKABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_CLICKABLE, StClickable)) -#define ST_CLICKABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_CLICKABLE, StClickableClass)) -#define ST_IS_CLICKABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_CLICKABLE)) -#define ST_IS_CLICKABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_CLICKABLE)) -#define ST_CLICKABLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_CLICKABLE, StClickableClass)) - -typedef struct _StClickable StClickable; -typedef struct _StClickableClass StClickableClass; - -typedef struct _StClickablePrivate StClickablePrivate; - -struct _StClickable -{ - StBin parent; - - StClickablePrivate *priv; -}; - -struct _StClickableClass -{ - StBinClass parent_class; -}; - -GType st_clickable_get_type (void) G_GNUC_CONST; - -void st_clickable_fake_release (StClickable *box); - -#endif /* __ST_CLICKABLE_H__ */ diff --git a/src/st/st-marshal.list b/src/st/st-marshal.list index 9b3bed671..69b75cc92 100644 --- a/src/st/st-marshal.list +++ b/src/st/st-marshal.list @@ -2,6 +2,7 @@ VOID:OBJECT VOID:VOID VOID:PARAM VOID:POINTER +VOID:INT VOID:UINT VOID:UINT,UINT VOID:OBJECT,OBJECT