[gnome-shell/user-status-update: 4/5] popup-menu: Add combo box menu item



commit cf736671ad4303febfd90df31bd3929cdcac35c4
Author: Florian MÃllner <fmuellner gnome org>
Date:   Thu Jul 21 05:17:05 2011 +0200

    popup-menu: Add combo box menu item

 data/theme/gnome-shell.css |   13 +++
 js/ui/popupMenu.js         |  197 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 210 insertions(+), 0 deletions(-)
---
diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css
index edbd023..49c5114 100644
--- a/data/theme/gnome-shell.css
+++ b/data/theme/gnome-shell.css
@@ -139,6 +139,15 @@ StTooltip StLabel {
     border-width: 0px;
 }
 
+.popup-combo-menu {
+    background-color: rgba(0,0,0,0.9);
+    padding: 1em 0em;
+    color: #ffffff;
+    font-size: 10.5pt;
+    border: 1px solid #5f5f5f;
+    border-radius: 9px;
+}
+
 /* The remaining popup-menu sizing is all done in ems, so that if you
  * override .popup-menu.font-size, everything else will scale with it.
  */
@@ -158,6 +167,10 @@ StTooltip StLabel {
 .popup-image-menu-item {
 }
 
+.popup-combobox-item {
+    spacing: 1em;
+}
+
 .popup-separator-menu-item {
     -gradient-height: 2px;
     -gradient-start: rgba(8,8,8,0);
diff --git a/js/ui/popupMenu.js b/js/ui/popupMenu.js
index e1c18b0..985d1f9 100644
--- a/js/ui/popupMenu.js
+++ b/js/ui/popupMenu.js
@@ -15,6 +15,19 @@ const Tweener = imports.ui.tweener;
 
 const SLIDER_SCROLL_STEP = 0.05; /* Slider scrolling step in % */
 
+function _ensureStyle(actor) {
+    // Really want to test for Clutter.Container, but that's an interface,
+    // which we don't support :(
+    if (actor instanceof St.Container || actor instanceof Clutter.Group) {
+        let children = actor.get_children();
+        for (let i = 0; i < children.length; i++)
+            _ensureStyle(children[i]);
+    }
+
+    if (actor instanceof St.Widget)
+        actor.ensure_style();
+}
+
 function PopupBaseMenuItem(params) {
     this._init(params);
 }
@@ -986,6 +999,10 @@ PopupMenuBase.prototype = {
             return null;
     },
 
+    get numMenuItems() {
+        return this._getMenuItems().length;
+    },
+
     removeAll: function() {
         let children = this._getMenuItems();
         for (let i = 0; i < children.length; i++) {
@@ -1350,6 +1367,186 @@ PopupSubMenuMenuItem.prototype = {
     }
 };
 
+function PopupComboMenu() {
+    this._init.apply(this, arguments);
+}
+
+PopupComboMenu.prototype = {
+    __proto__: PopupMenuBase.prototype,
+
+    _init: function(sourceActor) {
+        PopupMenuBase.prototype._init.call(this,
+                                           sourceActor, 'popup-combo-menu');
+        this.actor = this.box;
+        this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
+        this.actor.connect('key-focus-in', Lang.bind(this, this._onKeyFocusIn));
+        this._activeItemPos = -1;
+        global.focus_manager.add_group(this.actor);
+    },
+
+    _onKeyPressEvent: function(actor, event) {
+        if (event.get_key_symbol() == Clutter.Escape) {
+            this.close(true);
+            return true;
+        }
+
+        return false;
+    },
+
+    _onKeyFocusIn: function(actor) {
+        let items = this._getMenuItems();
+        let activeItem = items[this._activeItemPos];
+        activeItem.actor.grab_key_focus();
+    },
+
+    open: function() {
+        if (this.isOpen)
+            return;
+
+        this.isOpen = true;
+
+        let [sourceX, sourceY] = this.sourceActor.get_transformed_position();
+        let items = this._getMenuItems();
+        let activeItem = items[this._activeItemPos];
+
+        this.actor.set_position(sourceX, sourceY - activeItem.actor.y);
+        this.actor.raise_top();
+
+        this.actor.opacity = 0;
+        this.actor.show();
+
+        Tweener.addTween(this.actor,
+                         { opacity: 255,
+                           transition: 'linear',
+                           time: BoxPointer.POPUP_ANIMATION_TIME });
+
+        this.emit('open-state-changed', true);
+    },
+
+    close: function() {
+        if (!this.isOpen)
+            return;
+
+        this.isOpen = false;
+        Tweener.addTween(this.actor,
+                         { opacity: 0,
+                           transition: 'linear',
+                           time: BoxPointer.POPUP_ANIMATION_TIME,
+                           onComplete: Lang.bind(this,
+                               function() {
+                                   this.actor.hide();
+                               })
+                         });
+
+        this.emit('open-state-changed', false);
+    },
+
+    setActiveItem: function(position) {
+        this._activeItemPos = position;
+    },
+
+    setItemVisible: function(position, visible) {
+        if (!visible && position == this._activeItemPos) {
+            log('Trying to hide the active menu item.');
+            return;
+        }
+
+        this._getMenuItems()[position].actor.visible = visible;
+    }
+};
+
+function PopupComboBoxMenuItem() {
+    this._init.apply(this, arguments);
+}
+
+PopupComboBoxMenuItem.prototype = {
+    __proto__: PopupBaseMenuItem.prototype,
+
+    _init: function (params) {
+        PopupBaseMenuItem.prototype._init.call(this, params);
+
+        this._itemBox = new Shell.Stack();
+        this.addActor(this._itemBox);
+
+        let expander = new St.Label({ text: '\u2304' });
+        this.addActor(expander, { align: St.Align.END });
+
+        this._menu = new PopupComboMenu(this.actor);
+        Main.chrome.addActor(this._menu.actor, { affectsStruts: false });
+        this._menu.actor.hide();
+
+        this._activeItemPos = -1;
+        this._items = [];
+    },
+
+    _getTopMenu: function() {
+        let actor = this.actor.get_parent();
+        while (actor) {
+            if (actor._delegate && actor._delegate instanceof PopupMenu)
+                return actor._delegate;
+
+            actor = actor.get_parent();
+        }
+
+        return null;
+    },
+
+    activate: function(event) {
+        let topMenu = this._getTopMenu();
+        if (!topMenu)
+            return;
+
+        topMenu.addChildMenu(this._menu);
+        this._menu.open(true);
+    },
+
+    addMenuItem: function(menuItem, position) {
+        if (position === undefined)
+            position = this._menu.numMenuItems;
+
+        this._menu.addMenuItem(menuItem, position);
+        _ensureStyle(this._menu.actor);
+
+        let item = new St.BoxLayout({ style_class: 'popup-combobox-item' });
+
+        let children = menuItem.actor.get_children();
+        for (let i = 0; i < children.length; i++) {
+            let clone = new Clutter.Clone({ source: children[i] });
+            item.add(clone, { y_fill: false });
+        }
+
+        let oldItem = this._items[position];
+        if (oldItem)
+            this._itemBox.remove_actor(oldItem);
+
+        this._items[position] = item;
+        this._itemBox.add_actor(item);
+
+        menuItem.connect('activate',
+                         Lang.bind(this, this._itemActivated, position));
+    },
+
+    setActiveItem: function(position) {
+        let item = this._items[position];
+        if (!item)
+            return;
+        if (this._activeItemPos == position)
+            return;
+        this._menu.setActiveItem(position);
+        this._activeItemPos = position;
+        for (let i = 0; i < this._items.length; i++)
+            this._items[i].visible = (i == this._activeItemPos);
+    },
+
+    setItemVisible: function(position, visible) {
+        this._menu.setItemVisible(position, visible);
+    },
+
+    _itemActivated: function(menuItem, event, position) {
+        this.setActiveItem(position);
+        this.emit('active-item-changed', position);
+    }
+};
 
 /* Basic implementation of a menu manager.
  * Call addMenu to add menus



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]