[ShellMenu] port from BigBox to StBoxLayout

The actual changes to shell-menu.[ch] are pretty minimal; most of the
changes there are just style/spacing/indentation.

Also, removed shell_menu_append_separator() since it wasn't needed;
the separators would already have been behaving as intended just
because they were non-reactive.

https://bugzilla.gnome.org/show_bug.cgi?id=614516
This commit is contained in:
Dan Winship 2010-03-31 14:57:07 -04:00
parent b4c3ab6726
commit 46c210c314
4 changed files with 98 additions and 127 deletions

View File

@ -502,7 +502,7 @@ StTooltip {
padding: 4px; padding: 4px;
background-color: rgba(0,0,0,0.9); background-color: rgba(0,0,0,0.9);
color: #ffffff; color: #ffffff;
-shell-menu-spacing: 4px; spacing: 4px;
} }
.app-well-menu-arrow { .app-well-menu-arrow {

View File

@ -613,15 +613,14 @@ AppIconMenu.prototype = {
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight)); this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
this.actor.connect('allocate', Lang.bind(this, this._allocate)); this.actor.connect('allocate', Lang.bind(this, this._allocate));
this._windowContainerBox = new St.Bin({ style_class: 'app-well-menu' }); this._windowContainer = new Shell.Menu({ style_class: 'app-well-menu',
this._windowContainer = new Shell.Menu({ orientation: Big.BoxOrientation.VERTICAL, vertical: true,
width: Main.overview._dash.actor.width }); width: Main.overview._dash.actor.width });
this._windowContainerBox.set_child(this._windowContainer);
this._windowContainer.connect('unselected', Lang.bind(this, this._onItemUnselected)); this._windowContainer.connect('unselected', Lang.bind(this, this._onItemUnselected));
this._windowContainer.connect('selected', Lang.bind(this, this._onItemSelected)); this._windowContainer.connect('selected', Lang.bind(this, this._onItemSelected));
this._windowContainer.connect('cancelled', Lang.bind(this, this._onWindowSelectionCancelled)); this._windowContainer.connect('cancelled', Lang.bind(this, this._onWindowSelectionCancelled));
this._windowContainer.connect('activate', Lang.bind(this, this._onItemActivate)); this._windowContainer.connect('activate', Lang.bind(this, this._onItemActivate));
this.actor.add_actor(this._windowContainerBox); this.actor.add_actor(this._windowContainer);
// Stay popped up on release over application icon // Stay popped up on release over application icon
this._windowContainer.set_persistent_source(this._source.actor); this._windowContainer.set_persistent_source(this._source.actor);
@ -631,8 +630,6 @@ AppIconMenu.prototype = {
this._windowContainer.connect('leave-event', Lang.bind(this, this._onMenuLeave)); this._windowContainer.connect('leave-event', Lang.bind(this, this._onMenuLeave));
this._windowContainer.connect('button-release-event', Lang.bind(this, this._onMenuButtonRelease)); this._windowContainer.connect('button-release-event', Lang.bind(this, this._onMenuButtonRelease));
this._windowContainerBox.connect('style-changed', Lang.bind(this, this._onStyleChanged));
this._arrow = new St.DrawingArea({ style_class: 'app-well-menu-arrow' }); this._arrow = new St.DrawingArea({ style_class: 'app-well-menu-arrow' });
this._arrow.connect('repaint', Lang.bind(this, function (area) { this._arrow.connect('repaint', Lang.bind(this, function (area) {
Shell.draw_box_pointer(area, Shell.PointerDirection.LEFT); Shell.draw_box_pointer(area, Shell.PointerDirection.LEFT);
@ -650,21 +647,21 @@ AppIconMenu.prototype = {
}, },
_getPreferredWidth: function(actor, forHeight, alloc) { _getPreferredWidth: function(actor, forHeight, alloc) {
let [menuMin, menuNatural] = this._windowContainerBox.get_preferred_width(forHeight); let [menuMin, menuNatural] = this._windowContainer.get_preferred_width(forHeight);
let [arrowMin, arrowNatural] = this._arrow.get_preferred_width(forHeight); let [arrowMin, arrowNatural] = this._arrow.get_preferred_width(forHeight);
alloc.min_size = menuMin + arrowMin; alloc.min_size = menuMin + arrowMin;
alloc.natural_size = menuNatural + arrowNatural; alloc.natural_size = menuNatural + arrowNatural;
}, },
_getPreferredHeight: function(actor, forWidth, alloc) { _getPreferredHeight: function(actor, forWidth, alloc) {
let [min, natural] = this._windowContainerBox.get_preferred_height(forWidth); let [min, natural] = this._windowContainer.get_preferred_height(forWidth);
alloc.min_size = min; alloc.min_size = min;
alloc.natural_size = natural; alloc.natural_size = natural;
}, },
_allocate: function(actor, box, flags) { _allocate: function(actor, box, flags) {
let childBox = new Clutter.ActorBox(); let childBox = new Clutter.ActorBox();
let themeNode = this._windowContainerBox.get_theme_node(); let themeNode = this._windowContainer.get_theme_node();
let width = box.x2 - box.x1; let width = box.x2 - box.x1;
let height = box.y2 - box.y1; let height = box.y2 - box.y1;
@ -683,7 +680,7 @@ AppIconMenu.prototype = {
childBox.x2 = width; childBox.x2 = width;
childBox.y1 = 0; childBox.y1 = 0;
childBox.y2 = height; childBox.y2 = height;
this._windowContainerBox.allocate(childBox, flags); this._windowContainer.allocate(childBox, flags);
}, },
_redisplay: function() { _redisplay: function() {
@ -736,7 +733,7 @@ AppIconMenu.prototype = {
_appendSeparator: function () { _appendSeparator: function () {
let bin = new St.Bin({ style_class: "app-well-menu-separator" }); let bin = new St.Bin({ style_class: "app-well-menu-separator" });
this._windowContainer.append_separator(bin, Big.BoxPackFlags.NONE); this._windowContainer.add_actor(bin);
}, },
_appendMenuItem: function(labelText) { _appendMenuItem: function(labelText) {
@ -744,7 +741,7 @@ AppIconMenu.prototype = {
reactive: true }); reactive: true });
let label = new St.Label({ text: labelText }); let label = new St.Label({ text: labelText });
box.add(label); box.add(label);
this._windowContainer.append(box, Big.BoxPackFlags.NONE); this._windowContainer.add_actor(box);
return box; return box;
}, },
@ -862,14 +859,6 @@ AppIconMenu.prototype = {
_onWindowSelectionCancelled: function () { _onWindowSelectionCancelled: function () {
this.emit('highlight-window', null); this.emit('highlight-window', null);
this.popdown(); this.popdown();
},
_onStyleChanged: function() {
let themeNode = this._windowContainerBox.get_theme_node();
[success, len] = themeNode.get_length('-shell-menu-spacing', false)
if (success) {
this._windowContainer.spacing = len;
}
} }
}; };
Signals.addSignalMethods(AppIconMenu.prototype); Signals.addSignalMethods(AppIconMenu.prototype);

View File

@ -4,15 +4,15 @@
* SECTION:shell-menu * SECTION:shell-menu
* @short_description: A box which acts like a popup menu * @short_description: A box which acts like a popup menu
* *
* A #BigBox subclass which adds methods and signals useful for implementing * A #StBoxLayout subclass which adds methods and signals useful for
* popup-menu like actors. * implementing popup-menu like actors.
*/ */
#include "config.h" #include "config.h"
#include "shell-menu.h" #include "shell-menu.h"
G_DEFINE_TYPE(ShellMenu, shell_menu, BIG_TYPE_BOX); G_DEFINE_TYPE (ShellMenu, shell_menu, ST_TYPE_BOX_LAYOUT);
struct _ShellMenuPrivate { struct _ShellMenuPrivate {
gboolean popped_up; gboolean popped_up;
@ -41,7 +41,9 @@ static gboolean
container_contains (ClutterContainer *container, container_contains (ClutterContainer *container,
ClutterActor *actor) ClutterActor *actor)
{ {
while (actor != NULL && actor != (ClutterActor*)container) ClutterActor *container_actor = CLUTTER_ACTOR (container);
while (actor != NULL && actor != container_actor)
{ {
actor = clutter_actor_get_parent (actor); actor = clutter_actor_get_parent (actor);
} }
@ -49,37 +51,42 @@ container_contains (ClutterContainer *container,
} }
static void static void
shell_menu_popdown_nosignal (ShellMenu *box) shell_menu_popdown_nosignal (ShellMenu *menu)
{ {
box->priv->popped_up = FALSE; menu->priv->popped_up = FALSE;
if (box->priv->have_grab) if (menu->priv->have_grab)
clutter_ungrab_pointer (); clutter_ungrab_pointer ();
clutter_actor_hide (CLUTTER_ACTOR (box)); clutter_actor_hide (CLUTTER_ACTOR (menu));
} }
static void static void
on_selected_destroy (ClutterActor *actor, on_selected_destroy (ClutterActor *actor,
ShellMenu *box) ShellMenu *menu)
{ {
box->priv->selected = NULL; menu->priv->selected = NULL;
} }
static void static void
set_selected (ShellMenu *box, set_selected (ShellMenu *menu,
ClutterActor *actor) ClutterActor *actor)
{ {
if (actor == box->priv->selected) if (actor == menu->priv->selected)
return; return;
if (box->priv->selected) if (menu->priv->selected)
{ {
g_signal_handlers_disconnect_by_func (box->priv->selected, G_CALLBACK(on_selected_destroy), box); g_signal_handlers_disconnect_by_func (menu->priv->selected,
g_signal_emit (G_OBJECT (box), shell_menu_signals[UNSELECTED], 0, box->priv->selected); G_CALLBACK (on_selected_destroy),
menu);
g_signal_emit (G_OBJECT (menu), shell_menu_signals[UNSELECTED], 0,
menu->priv->selected);
} }
box->priv->selected = actor; menu->priv->selected = actor;
if (box->priv->selected) if (menu->priv->selected)
{ {
g_signal_connect (box->priv->selected, "destroy", G_CALLBACK(on_selected_destroy), box); g_signal_connect (menu->priv->selected, "destroy",
g_signal_emit (G_OBJECT (box), shell_menu_signals[SELECTED], 0, box->priv->selected); G_CALLBACK (on_selected_destroy), menu);
g_signal_emit (G_OBJECT (menu), shell_menu_signals[SELECTED], 0,
menu->priv->selected);
} }
} }
@ -87,113 +94,108 @@ static gboolean
shell_menu_enter_event (ClutterActor *actor, shell_menu_enter_event (ClutterActor *actor,
ClutterCrossingEvent *event) ClutterCrossingEvent *event)
{ {
ShellMenu *box = SHELL_MENU (actor); ShellMenu *menu = SHELL_MENU (actor);
if (!container_contains (CLUTTER_CONTAINER (box), event->source)) if (container_contains (CLUTTER_CONTAINER (menu), event->source) &&
return TRUE; event->source != CLUTTER_ACTOR (menu))
set_selected (menu, event->source);
if (event->source == (ClutterActor*)box) return CLUTTER_ACTOR_CLASS (shell_menu_parent_class)->enter_event (actor, event);
return TRUE;
if (g_object_get_data (G_OBJECT (event->source), "shell-is-separator"))
return TRUE;
set_selected (box, event->source);
return TRUE;
} }
static gboolean static gboolean
shell_menu_leave_event (ClutterActor *actor, shell_menu_leave_event (ClutterActor *actor,
ClutterCrossingEvent *event) ClutterCrossingEvent *event)
{ {
ShellMenu *box = SHELL_MENU (actor); ShellMenu *menu = SHELL_MENU (actor);
set_selected (box, NULL); set_selected (menu, NULL);
return TRUE; return CLUTTER_ACTOR_CLASS (shell_menu_parent_class)->leave_event (actor, event);
} }
static gboolean static gboolean
shell_menu_button_release_event (ClutterActor *actor, shell_menu_button_release_event (ClutterActor *actor,
ClutterButtonEvent *event) ClutterButtonEvent *event)
{ {
ShellMenu *box = SHELL_MENU (actor); ShellMenu *menu = SHELL_MENU (actor);
/* Until the user releases the button that brought up the menu, we just /* Until the user releases the button that brought up the menu, we just
* ignore other button press/releass. * ignore other button press/releass.
* See https://bugzilla.gnome.org/show_bug.cgi?id=596371 * See https://bugzilla.gnome.org/show_bug.cgi?id=596371
*/ */
if (box->priv->activating_button > 0 && box->priv->activating_button != event->button) if (menu->priv->activating_button > 0 &&
menu->priv->activating_button != event->button)
return FALSE; return FALSE;
box->priv->activating_button = 0; menu->priv->activating_button = 0;
if (box->priv->source_actor && !box->priv->released_on_source) if (menu->priv->source_actor && !menu->priv->released_on_source)
{ {
if (box->priv->source_actor == event->source || if (menu->priv->source_actor == event->source ||
(CLUTTER_IS_CONTAINER (box->priv->source_actor) && (CLUTTER_IS_CONTAINER (menu->priv->source_actor) &&
container_contains (CLUTTER_CONTAINER (box->priv->source_actor), event->source))) container_contains (CLUTTER_CONTAINER (menu->priv->source_actor), event->source)))
{ {
/* On the next release, we want to pop down the menu regardless */ /* On the next release, we want to pop down the menu regardless */
box->priv->released_on_source = TRUE; menu->priv->released_on_source = TRUE;
return TRUE; return TRUE;
} }
} }
shell_menu_popdown_nosignal (box); shell_menu_popdown_nosignal (menu);
if (!container_contains (CLUTTER_CONTAINER (box), event->source) || if (!container_contains (CLUTTER_CONTAINER (menu), event->source) ||
box->priv->selected == NULL) menu->priv->selected == NULL)
{ {
g_signal_emit (G_OBJECT (box), shell_menu_signals[CANCELLED], 0); g_signal_emit (G_OBJECT (menu), shell_menu_signals[CANCELLED], 0);
return FALSE; return FALSE;
} }
g_signal_emit (G_OBJECT (box), shell_menu_signals[ACTIVATE], 0, box->priv->selected); g_signal_emit (G_OBJECT (menu), shell_menu_signals[ACTIVATE], 0,
menu->priv->selected);
return TRUE; return TRUE;
} }
void void
shell_menu_popup (ShellMenu *box, shell_menu_popup (ShellMenu *menu,
guint button, guint button,
guint32 activate_time) guint32 activate_time)
{ {
if (box->priv->popped_up) if (menu->priv->popped_up)
return; return;
box->priv->activating_button = button; menu->priv->activating_button = button;
box->priv->popped_up = TRUE; menu->priv->popped_up = TRUE;
box->priv->have_grab = TRUE; menu->priv->have_grab = TRUE;
box->priv->released_on_source = FALSE; menu->priv->released_on_source = FALSE;
clutter_grab_pointer (CLUTTER_ACTOR (box)); clutter_grab_pointer (CLUTTER_ACTOR (menu));
} }
/** /**
* shell_menu_popdown: * shell_menu_popdown:
* @box: * @menu:
* *
* If the menu is currently active, hide it, emitting the 'cancelled' signal. * If the menu is currently active, hide it, emitting the 'cancelled' signal.
*/ */
void void
shell_menu_popdown (ShellMenu *box) shell_menu_popdown (ShellMenu *menu)
{ {
if (!box->priv->popped_up) if (!menu->priv->popped_up)
return; return;
shell_menu_popdown_nosignal (box); shell_menu_popdown_nosignal (menu);
g_signal_emit (G_OBJECT (box), shell_menu_signals[CANCELLED], 0); g_signal_emit (G_OBJECT (menu), shell_menu_signals[CANCELLED], 0);
} }
static void static void
on_source_destroyed (ClutterActor *actor, on_source_destroyed (ClutterActor *actor,
ShellMenu *box) ShellMenu *menu)
{ {
box->priv->source_actor = NULL; menu->priv->source_actor = NULL;
} }
/** /**
* shell_menu_set_persistent_source: * shell_menu_set_persistent_source:
* @box: * @menu:
* @source: Actor to use as menu origin * @source: Actor to use as menu origin
* *
* This function changes the menu behavior on button release. Normally * This function changes the menu behavior on button release. Normally
@ -204,45 +206,25 @@ on_source_destroyed (ClutterActor *actor,
* The given @source actor must be reactive for this function to work. * The given @source actor must be reactive for this function to work.
*/ */
void void
shell_menu_set_persistent_source (ShellMenu *box, shell_menu_set_persistent_source (ShellMenu *menu,
ClutterActor *source) ClutterActor *source)
{ {
if (box->priv->source_actor) if (menu->priv->source_actor)
{ {
g_signal_handlers_disconnect_by_func (G_OBJECT (box->priv->source_actor), g_signal_handlers_disconnect_by_func (G_OBJECT (menu->priv->source_actor),
G_CALLBACK (on_source_destroyed), G_CALLBACK (on_source_destroyed),
box); menu);
} }
box->priv->source_actor = source; menu->priv->source_actor = source;
if (box->priv->source_actor) if (menu->priv->source_actor)
{ {
g_signal_connect (G_OBJECT (box->priv->source_actor), g_signal_connect (G_OBJECT (menu->priv->source_actor),
"destroy", "destroy",
G_CALLBACK (on_source_destroyed), G_CALLBACK (on_source_destroyed),
box); menu);
} }
} }
/**
* shell_menu_append_separator:
* @box:
* @separator: An actor which functions as a menu separator
* @flags: Packing flags
*
* Actors added to the menu with default functions are treated like
* menu items; this function will add an actor that should instead
* be treated like a menu separator. The current practical effect
* is that the separators will not be selectable.
*/
void
shell_menu_append_separator (ShellMenu *box,
ClutterActor *separator,
BigBoxPackFlags flags)
{
g_object_set_data (G_OBJECT (separator), "shell-is-separator", GUINT_TO_POINTER(TRUE));
big_box_append (BIG_BOX (box), separator, flags);
}
static void static void
shell_menu_dispose (GObject *gobject) shell_menu_dispose (GObject *gobject)
{ {
@ -267,7 +249,7 @@ shell_menu_class_init (ShellMenuClass *klass)
/** /**
* ShellMenu::unselected * ShellMenu::unselected
* @box: The #ShellMenu * @menu: The #ShellMenu
* @actor: The previously hovered-over menu item * @actor: The previously hovered-over menu item
* *
* This signal is emitted when a menu item transitions to * This signal is emitted when a menu item transitions to
@ -284,7 +266,7 @@ shell_menu_class_init (ShellMenuClass *klass)
/** /**
* ShellMenu::selected * ShellMenu::selected
* @box: The #ShellMenu * @menu: The #ShellMenu
* @actor: The hovered-over menu item * @actor: The hovered-over menu item
* *
* This signal is emitted when a menu item is in a selected state. * This signal is emitted when a menu item is in a selected state.
@ -300,7 +282,7 @@ shell_menu_class_init (ShellMenuClass *klass)
/** /**
* ShellMenu::activate * ShellMenu::activate
* @box: The #ShellMenu * @menu: The #ShellMenu
* @actor: The clicked menu item * @actor: The clicked menu item
* *
* This signal is emitted when a menu item is selected. * This signal is emitted when a menu item is selected.
@ -316,7 +298,7 @@ shell_menu_class_init (ShellMenuClass *klass)
/** /**
* ShellMenu::cancelled * ShellMenu::cancelled
* @box: The #ShellMenu * @menu: The #ShellMenu
* *
* This signal is emitted when the menu is closed without an option selected. * This signal is emitted when the menu is closed without an option selected.
*/ */

View File

@ -3,7 +3,7 @@
#define __SHELL_MENU_H__ #define __SHELL_MENU_H__
#include <clutter/clutter.h> #include <clutter/clutter.h>
#include "big/box.h" #include "st.h"
#define SHELL_TYPE_MENU (shell_menu_get_type ()) #define SHELL_TYPE_MENU (shell_menu_get_type ())
#define SHELL_MENU(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_MENU, ShellMenu)) #define SHELL_MENU(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_MENU, ShellMenu))
@ -19,24 +19,24 @@ typedef struct _ShellMenuPrivate ShellMenuPrivate;
struct _ShellMenu struct _ShellMenu
{ {
BigBox parent; StBoxLayout parent;
ShellMenuPrivate *priv; ShellMenuPrivate *priv;
}; };
struct _ShellMenuClass struct _ShellMenuClass
{ {
BigBoxClass parent_class; StBoxLayoutClass parent_class;
}; };
GType shell_menu_get_type (void) G_GNUC_CONST; GType shell_menu_get_type (void) G_GNUC_CONST;
void shell_menu_popup (ShellMenu *behavior, guint button, guint32 activate_time); void shell_menu_popup (ShellMenu *menu,
guint button,
guint32 activate_time);
void shell_menu_popdown (ShellMenu *menu);
void shell_menu_set_persistent_source (ShellMenu *behavior, ClutterActor *source); void shell_menu_set_persistent_source (ShellMenu *menu,
ClutterActor *source);
void shell_menu_append_separator (ShellMenu *behavior, ClutterActor *separator, BigBoxPackFlags flags);
void shell_menu_popdown (ShellMenu *behavior);
#endif /* __SHELL_MENU_H__ */ #endif /* __SHELL_MENU_H__ */