[gnome-shell/gbsneto/remove-generic-container: 1/25] switcherList: Stop using Shell.GenericContainer



commit 931730e693c5ecca5555f0409b6ca59fb3dae4d8
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date:   Thu Jun 28 12:34:01 2018 -0300

    switcherList: Stop using Shell.GenericContainer
    
    This commit removes all the uses of Shell.GenericContainer from
    SwitcherPopup.SwitcherList. Compared to the other patches, this
    one was specially trickier to get right, and a few invasive
    changes needed to be done.
    
    The most noticeable one is that the allocation of the items is
    done entirely by St.BoxLayout -- we don't manually allocate them
    anymore. To make it work, get_preferred_width() had to calculate
    the correct value. It now assumes that:
    
     * Minimum width: the minimum width of the widest child.
     * Natural width: the minimum width of the StBoxLayout (use it
       instead of the natural width to force the labels to ellipsize
       when too long.)
    
    The AppIcon class became a St.Widget subclass as well, to override
    get_preferred_width() and be able to keep the squared shape.
    
    Besides that, add a new SwitcherButton class to reimplement squared
    icons without having to resort to hacks in the size allocation
    machinery. This class has a single vfunc override to ensure that it
    is squared when the SwitcherList is.
    
    The arrows indicating multiple windows are now in this._list
    actor to the SwitcherPopup itself, since this._list automatically
    manages its own children now.
    
    At last, adapt (but preserve) the hack in CyclerPopup.
    
    https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/153

 js/ui/altTab.js        | 155 +++++++++++++++++++++++++++----------------
 js/ui/ctrlAltTab.js    |   8 +--
 js/ui/switcherPopup.js | 177 +++++++++++++++++++++++--------------------------
 3 files changed, 186 insertions(+), 154 deletions(-)
---
diff --git a/js/ui/altTab.js b/js/ui/altTab.js
index c0a71b4b6..a98848103 100644
--- a/js/ui/altTab.js
+++ b/js/ui/altTab.js
@@ -3,6 +3,7 @@
 const Clutter = imports.gi.Clutter;
 const Gio = imports.gi.Gio;
 const GLib = imports.gi.GLib;
+const GObject = imports.gi.GObject;
 const Lang = imports.lang;
 const Mainloop = imports.mainloop;
 const Meta = imports.gi.Meta;
@@ -87,7 +88,7 @@ var AppSwitcherPopup = new Lang.Class({
         // We try to avoid overflowing the screen so we base the resulting size on
         // those calculations
         if (this._thumbnails) {
-            let childBox = this._switcherList.actor.get_allocation_box();
+            let childBox = this._switcherList.get_allocation_box();
             let primary = Main.layoutManager.primaryMonitor;
 
             let leftPadding = this.get_theme_node().get_padding(St.Side.LEFT);
@@ -95,10 +96,10 @@ var AppSwitcherPopup = new Lang.Class({
             let bottomPadding = this.get_theme_node().get_padding(St.Side.BOTTOM);
             let hPadding = leftPadding + rightPadding;
 
-            let icon = this._items[this._selectedIndex].actor;
+            let icon = this._items[this._selectedIndex];
             let [posX, posY] = icon.get_transformed_position();
             let thumbnailCenter = posX + icon.width / 2;
-            let [childMinWidth, childNaturalWidth] = this._thumbnails.actor.get_preferred_width(-1);
+            let [childMinWidth, childNaturalWidth] = this._thumbnails.get_preferred_width(-1);
             childBox.x1 = Math.max(primary.x + leftPadding, Math.floor(thumbnailCenter - childNaturalWidth / 
2));
             if (childBox.x1 + childNaturalWidth > primary.x + primary.width - hPadding) {
                 let offset = childBox.x1 + childNaturalWidth - primary.width + hPadding;
@@ -110,11 +111,11 @@ var AppSwitcherPopup = new Lang.Class({
             childBox.x2 = childBox.x1 +  childNaturalWidth;
             if (childBox.x2 > primary.x + primary.width - rightPadding)
                 childBox.x2 = primary.x + primary.width - rightPadding;
-            childBox.y1 = this._switcherList.actor.allocation.y2 + spacing;
+            childBox.y1 = this._switcherList.allocation.y2 + spacing;
             this._thumbnails.addClones(primary.y + primary.height - bottomPadding - childBox.y1);
-            let [childMinHeight, childNaturalHeight] = this._thumbnails.actor.get_preferred_height(-1);
+            let [childMinHeight, childNaturalHeight] = this._thumbnails.get_preferred_height(-1);
             childBox.y2 = childBox.y1 + childNaturalHeight;
-            this._thumbnails.actor.allocate(childBox, flags);
+            this._thumbnails.allocate(childBox, flags);
         }
     },
 
@@ -367,7 +368,7 @@ var AppSwitcherPopup = new Lang.Class({
     },
 
     _destroyThumbnails() {
-        let thumbnailsActor = this._thumbnails.actor;
+        let thumbnailsActor = this._thumbnails;
         Tweener.addTween(thumbnailsActor,
                          { opacity: 0,
                            time: THUMBNAIL_FADE_TIME,
@@ -387,19 +388,19 @@ var AppSwitcherPopup = new Lang.Class({
         this._thumbnails.connect('item-activated', this._windowActivated.bind(this));
         this._thumbnails.connect('item-entered', this._windowEntered.bind(this));
         this._thumbnails.connect('item-removed', this._windowRemoved.bind(this));
-        this._thumbnails.actor.connect('destroy', () => {
+        this._thumbnails.connect('destroy', () => {
             this._thumbnails = null;
             this._thumbnailsFocused = false;
         });
 
-        this.add_actor(this._thumbnails.actor);
+        this.add_actor(this._thumbnails);
 
         // Need to force an allocation so we can figure out whether we
         // need to scroll when selecting
-        this._thumbnails.actor.get_allocation_box();
+        this._thumbnails.get_allocation_box();
 
-        this._thumbnails.actor.opacity = 0;
-        Tweener.addTween(this._thumbnails.actor,
+        this._thumbnails.opacity = 0;
+        Tweener.addTween(this._thumbnails,
                          { opacity: 255,
                            time: THUMBNAIL_FADE_TIME,
                            transition: 'easeOutQuad',
@@ -471,6 +472,21 @@ var CyclerHighlight = new Lang.Class({
     }
 });
 
+// We don't show an actual popup, so just provide what SwitcherPopup
+// expects instead of inheriting from SwitcherList
+var CyclerList = new Lang.Class({
+    Name: 'CyclerList',
+    Extends: St.Widget,
+    Signals: { 'item-activated': { param_types: [GObject.TYPE_INT] },
+               'item-entered': { param_types: [GObject.TYPE_INT] },
+               'item-removed': { param_types: [GObject.TYPE_INT] },
+               'item-highlighted': { param_types: [GObject.TYPE_INT] } },
+
+    highlight(index, justOutline) {
+        this.emit('item-highlighted', index);
+    }
+});
+
 var CyclerPopup = new Lang.Class({
     Name: 'CyclerPopup',
     Extends: SwitcherPopup.SwitcherPopup,
@@ -487,11 +503,10 @@ var CyclerPopup = new Lang.Class({
         this._highlight = new CyclerHighlight();
         global.window_group.add_actor(this._highlight.actor);
 
-        // We don't show an actual popup, so just provide what SwitcherPopup
-        // expects instead of inheriting from SwitcherList
-        this._switcherList = { actor: new St.Widget(),
-                               highlight: this._highlightItem.bind(this),
-                               connect() {} };
+        this._switcherList = new CyclerList();
+        this._switcherList.connect('item-highlighted', (list, index) => {
+            this._highlightItem(index);
+        });
     },
 
     _highlightItem(index, justOutline) {
@@ -653,22 +668,32 @@ var WindowCyclerPopup = new Lang.Class({
 
 var AppIcon = new Lang.Class({
     Name: 'AppIcon',
+    Extends: St.BoxLayout,
 
     _init(app) {
+        this.parent({ style_class: 'alt-tab-app',
+                      vertical: true });
+
         this.app = app;
-        this.actor = new St.BoxLayout({ style_class: 'alt-tab-app',
-                                         vertical: true });
         this.icon = null;
         this._iconBin = new St.Bin({ x_fill: true, y_fill: true });
 
-        this.actor.add(this._iconBin, { x_fill: false, y_fill: false } );
+        this.add(this._iconBin, { x_fill: false, y_fill: false } );
         this.label = new St.Label({ text: this.app.get_name() });
-        this.actor.add(this.label, { x_fill: false });
+        this.add(this.label, { x_fill: false });
     },
 
     set_size(size) {
         this.icon = this.app.create_icon_texture(size);
         this._iconBin.child = this.icon;
+        this._iconBin.set_size(size, size);
+    },
+
+    vfunc_get_preferred_width(forHeight) {
+        let [minWidth, ] = this.parent(forHeight);
+
+        minWidth = Math.max(minWidth, forHeight);
+        return [minWidth, minWidth];
     }
 });
 
@@ -707,11 +732,10 @@ var AppSwitcher = new Lang.Class({
         }
 
         this._curApp = -1;
-        this._iconSize = 0;
         this._altTabPopup = altTabPopup;
         this._mouseTimeOutId = 0;
 
-        this.actor.connect('destroy', this._onDestroy.bind(this));
+        this.connect('destroy', this._onDestroy.bind(this));
     },
 
     _onDestroy() {
@@ -738,17 +762,16 @@ var AppSwitcher = new Lang.Class({
 
         // We just assume the whole screen here due to weirdness happing with the passed width
         let primary = Main.layoutManager.primaryMonitor;
-        let parentPadding = this.actor.get_parent().get_theme_node().get_horizontal_padding();
-        let availWidth = primary.width - parentPadding - 
this.actor.get_theme_node().get_horizontal_padding();
+        let parentPadding = this.get_parent().get_theme_node().get_horizontal_padding();
+        let availWidth = primary.width - parentPadding - this.get_theme_node().get_horizontal_padding();
 
         let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
         let iconSizes = baseIconSizes.map(s => s * scaleFactor);
+        let iconSize = baseIconSizes[0];
 
-        if (this._items.length == 1) {
-            this._iconSize = baseIconSizes[0];
-        } else {
+        if (this._items.length > 1) {
             for(let i =  0; i < baseIconSizes.length; i++) {
-                this._iconSize = baseIconSizes[i];
+                iconSize = baseIconSizes[i];
                 let height = iconSizes[i] + iconSpacing;
                 let w = height * this._items.length + totalSpacing;
                 if (w <= availWidth)
@@ -756,32 +779,36 @@ var AppSwitcher = new Lang.Class({
             }
         }
 
+        this._iconSize = iconSize;
+
         for(let i = 0; i < this.icons.length; i++) {
             if (this.icons[i].icon != null)
                 break;
-            this.icons[i].set_size(this._iconSize);
+            this.icons[i].set_size(iconSize);
         }
     },
 
-    _getPreferredHeight(actor, forWidth, alloc) {
+    vfunc_get_preferred_height(forWidth) {
         this._setIconSize();
-        this.parent(actor, forWidth, alloc);
+        return this.parent(forWidth);
     },
 
-    _allocate(actor, box, flags) {
+    vfunc_allocate(box, flags) {
         // Allocate the main list items
-        this.parent(actor, box, flags);
+        this.parent(box, flags);
+
+        let contentBox = this.get_theme_node().get_content_box(box);
 
-        let arrowHeight = Math.floor(this.actor.get_theme_node().get_padding(St.Side.BOTTOM) / 3);
+        let arrowHeight = Math.floor(this.get_theme_node().get_padding(St.Side.BOTTOM) / 3);
         let arrowWidth = arrowHeight * 2;
 
         // Now allocate each arrow underneath its item
         let childBox = new Clutter.ActorBox();
         for (let i = 0; i < this._items.length; i++) {
             let itemBox = this._items[i].allocation;
-            childBox.x1 = Math.floor(itemBox.x1 + (itemBox.x2 - itemBox.x1 - arrowWidth) / 2);
+            childBox.x1 = contentBox.x1 + Math.floor(itemBox.x1 + (itemBox.x2 - itemBox.x1 - arrowWidth) / 
2);
             childBox.x2 = childBox.x1 + arrowWidth;
-            childBox.y1 = itemBox.y2 + arrowHeight;
+            childBox.y1 = contentBox.y1 + itemBox.y2 + arrowHeight;
             childBox.y2 = childBox.y1 + arrowHeight;
             this._arrows[i].allocate(childBox, flags);
         }
@@ -839,7 +866,7 @@ var AppSwitcher = new Lang.Class({
 
     _addIcon(appIcon) {
         this.icons.push(appIcon);
-        let item = this.addItem(appIcon.actor, appIcon.label);
+        let item = this.addItem(appIcon, appIcon.label);
 
         appIcon._stateChangedId = appIcon.app.connect('notify::state', app => {
             if (app.state != Shell.AppState.RUNNING)
@@ -849,7 +876,7 @@ var AppSwitcher = new Lang.Class({
         let n = this._arrows.length;
         let arrow = new St.DrawingArea({ style_class: 'switcher-arrow' });
         arrow.connect('repaint', () => { SwitcherPopup.drawArrow(arrow, St.Side.BOTTOM); });
-        this._list.add_actor(arrow);
+        this.add_actor(arrow);
         this._arrows.push(arrow);
 
         if (appIcon.cachedWindows.length == 1)
@@ -907,21 +934,21 @@ var ThumbnailList = new Lang.Class({
 
         }
 
-        this.actor.connect('destroy', this._onDestroy.bind(this));
+        this.connect('destroy', this._onDestroy.bind(this));
     },
 
     addClones(availHeight) {
         if (!this._thumbnailBins.length)
             return;
         let totalPadding = this._items[0].get_theme_node().get_horizontal_padding() + 
this._items[0].get_theme_node().get_vertical_padding();
-        totalPadding += this.actor.get_theme_node().get_horizontal_padding() + 
this.actor.get_theme_node().get_vertical_padding();
+        totalPadding += this.get_theme_node().get_horizontal_padding() + 
this.get_theme_node().get_vertical_padding();
         let [labelMinHeight, labelNaturalHeight] = this._labels[0].get_preferred_height(-1);
         let spacing = this._items[0].child.get_theme_node().get_length('spacing');
         let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
         let thumbnailSize = THUMBNAIL_DEFAULT_SIZE * scaleFactor;
 
         availHeight = Math.min(availHeight - labelNaturalHeight - totalPadding - spacing, thumbnailSize);
-        let binHeight = availHeight + this._items[0].get_theme_node().get_vertical_padding() + 
this.actor.get_theme_node().get_vertical_padding() - spacing;
+        let binHeight = availHeight + this._items[0].get_theme_node().get_vertical_padding() + 
this.get_theme_node().get_vertical_padding() - spacing;
         binHeight = Math.min(thumbnailSize, binHeight);
 
         for (let i = 0; i < this._thumbnailBins.length; i++) {
@@ -956,7 +983,7 @@ var ThumbnailList = new Lang.Class({
         if (this._clones.length > 0)
             this.highlight(SwitcherPopup.mod(index, this._clones.length));
         else
-            this.actor.destroy();
+            this.destroy();
     },
 
     _onDestroy() {
@@ -1034,7 +1061,7 @@ var WindowList = new Lang.Class({
 
         this._label = new St.Label({ x_align: Clutter.ActorAlign.CENTER,
                                      y_align: Clutter.ActorAlign.CENTER });
-        this.actor.add_actor(this._label);
+        this.add_actor(this._label);
 
         this.windows = windows;
         this.icons = [];
@@ -1051,7 +1078,7 @@ var WindowList = new Lang.Class({
             });
         }
 
-        this.actor.connect('destroy', () => { this._onDestroy(); });
+        this.connect('destroy', this._onDestroy.bind(this));
     },
 
     _onDestroy() {
@@ -1060,26 +1087,40 @@ var WindowList = new Lang.Class({
         });
     },
 
-    _getPreferredHeight(actor, forWidth, alloc) {
-        this.parent(actor, forWidth, alloc);
+    vfunc_get_preferred_height(forWidth) {
+        let [minHeight, natHeight] = this.parent(forWidth);
 
-        let spacing = this.actor.get_theme_node().get_padding(St.Side.BOTTOM);
+        let spacing = this.get_theme_node().get_padding(St.Side.BOTTOM);
         let [labelMin, labelNat] = this._label.get_preferred_height(-1);
-        alloc.min_size += labelMin + spacing;
-        alloc.natural_size += labelNat + spacing;
+
+        minHeight += labelMin + spacing;
+        natHeight += labelNat + spacing;
+
+        return [minHeight, natHeight];
     },
 
-    _allocateTop(actor, box, flags) {
+    vfunc_allocate(box, flags) {
+        let themeNode = this.get_theme_node();
+        let contentBox = themeNode.get_content_box(box);
+
         let childBox = new Clutter.ActorBox();
-        childBox.x1 = box.x1;
-        childBox.x2 = box.x2;
-        childBox.y2 = box.y2;
+        childBox.x1 = contentBox.x1;
+        childBox.x2 = contentBox.x2;
+        childBox.y2 = contentBox.y2;
         childBox.y1 = childBox.y2 - this._label.height;
         this._label.allocate(childBox, flags);
 
-        let spacing = this.actor.get_theme_node().get_padding(St.Side.BOTTOM);
-        box.y2 -= this._label.height + spacing;
-        this.parent(actor, box, flags);
+        let totalLabelHeight = this._label.height + themeNode.get_padding(St.Side.BOTTOM)
+        childBox.x1 = box.x1;
+        childBox.x2 = box.x2;
+        childBox.y1 = box.y1;
+        childBox.y2 = box.y2 - totalLabelHeight;
+        this.parent(childBox, flags);
+
+        // Hooking up the parent vfunc will call this.set_allocation() with
+        // the height without the label height, so call it again with the
+        // correct size here.
+        this.set_allocation(box, flags);
     },
 
     highlight(index, justOutline) {
diff --git a/js/ui/ctrlAltTab.js b/js/ui/ctrlAltTab.js
index 1ecd2d1f5..8ab1db9ba 100644
--- a/js/ui/ctrlAltTab.js
+++ b/js/ui/ctrlAltTab.js
@@ -125,10 +125,10 @@ var CtrlAltTabManager = new Lang.Class({
             this._popup = new CtrlAltTabPopup(items);
             this._popup.show(backward, binding, mask);
 
-            this._popup.actor.connect('destroy',
-                                      () => {
-                                          this._popup = null;
-                                      });
+            this._popup.connect('destroy',
+                                () => {
+                                    this._popup = null;
+                                });
         }
     },
 
diff --git a/js/ui/switcherPopup.js b/js/ui/switcherPopup.js
index e8681443c..537a6af0c 100644
--- a/js/ui/switcherPopup.js
+++ b/js/ui/switcherPopup.js
@@ -2,6 +2,7 @@
 
 const Clutter = imports.gi.Clutter;
 const GLib = imports.gi.GLib;
+const GObject = imports.gi.GObject;
 const Gtk = imports.gi.Gtk;
 const Lang = imports.lang;
 const Mainloop = imports.mainloop;
@@ -83,13 +84,13 @@ var SwitcherPopup = new Lang.Class({
 
         // Allocate the switcherList
         // We select a size based on an icon size that does not overflow the screen
-        let [childMinHeight, childNaturalHeight] = 
this._switcherList.actor.get_preferred_height(primary.width - hPadding);
-        let [childMinWidth, childNaturalWidth] = 
this._switcherList.actor.get_preferred_width(childNaturalHeight);
+        let [childMinHeight, childNaturalHeight] = this._switcherList.get_preferred_height(primary.width - 
hPadding);
+        let [childMinWidth, childNaturalWidth] = this._switcherList.get_preferred_width(childNaturalHeight);
         childBox.x1 = Math.max(primary.x + leftPadding, primary.x + Math.floor((primary.width - 
childNaturalWidth) / 2));
         childBox.x2 = Math.min(primary.x + primary.width - rightPadding, childBox.x1 + childNaturalWidth);
         childBox.y1 = primary.y + Math.floor((primary.height - childNaturalHeight) / 2);
         childBox.y2 = childBox.y1 + childNaturalHeight;
-        this._switcherList.actor.allocate(childBox, flags);
+        this._switcherList.allocate(childBox, flags);
     },
 
     _initialSelection(backward, binding) {
@@ -119,7 +120,7 @@ var SwitcherPopup = new Lang.Class({
         this.connect('button-press-event', this._clickedOutside.bind(this));
         this.connect('scroll-event', this._scrollEvent.bind(this));
 
-        this.add_actor(this._switcherList.actor);
+        this.add_actor(this._switcherList);
         this._switcherList.connect('item-activated', this._itemActivated.bind(this));
         this._switcherList.connect('item-entered', this._itemEntered.bind(this));
         this._switcherList.connect('item-removed', this._itemRemoved.bind(this));
@@ -324,35 +325,53 @@ var SwitcherPopup = new Lang.Class({
     }
 });
 
+var SwitcherButton = new Lang.Class({
+    Name: 'SwitcherButton',
+    Extends: St.Button,
+
+    _init(square) {
+        this.parent({ style_class: 'item-box',
+                      reactive: true });
+
+        this._square = square;
+    },
+
+    vfunc_get_preferred_width(forHeight) {
+        if (this._square)
+            return this.get_preferred_height(-1);
+        else
+            return this.parent(forHeight);
+    }
+});
+
 var SwitcherList = new Lang.Class({
     Name: 'SwitcherList',
+    Extends: St.Widget,
+    Signals: { 'item-activated': { param_types: [GObject.TYPE_INT] },
+               'item-entered': { param_types: [GObject.TYPE_INT] },
+               'item-removed': { param_types: [GObject.TYPE_INT] } },
 
     _init(squareItems) {
-        this.actor = new Shell.GenericContainer({ style_class: 'switcher-list' });
-        this.actor.connect('get-preferred-width', this._getPreferredWidth.bind(this));
-        this.actor.connect('get-preferred-height', this._getPreferredHeight.bind(this));
-        this.actor.connect('allocate', this._allocateTop.bind(this));
-
-        // Here we use a GenericContainer so that we can force all the
-        // children to have the same width.
-        this._list = new Shell.GenericContainer({ style_class: 'switcher-list-item-container' });
+        this.parent({ style_class: 'switcher-list' });
+
+        this._list = new St.BoxLayout({ style_class: 'switcher-list-item-container',
+                                        vertical: false,
+                                        x_expand: true,
+                                        y_expand: true });
+
+        let layoutManager = this._list.get_layout_manager();
+
         this._list.spacing = 0;
         this._list.connect('style-changed', () => {
             this._list.spacing = this._list.get_theme_node().get_length('spacing');
         });
 
-        this._list.connect('get-preferred-width', this._getPreferredWidth.bind(this));
-        this._list.connect('get-preferred-height', this._getPreferredHeight.bind(this));
-        this._list.connect('allocate', this._allocate.bind(this));
-
         this._scrollView = new St.ScrollView({ style_class: 'hfade',
                                                enable_mouse_scrolling: false });
         this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.NEVER);
 
-        let scrollBox = new St.BoxLayout();
-        scrollBox.add_actor(this._list);
-        this._scrollView.add_actor(scrollBox);
-        this.actor.add_actor(this._scrollView);
+        this._scrollView.add_actor(this._list);
+        this.add_actor(this._scrollView);
 
         // Those arrows indicate whether scrolling in one direction is possible
         this._leftArrow = new St.DrawingArea({ style_class: 'switcher-arrow',
@@ -366,50 +385,20 @@ var SwitcherList = new Lang.Class({
             drawArrow(this._rightArrow, St.Side.RIGHT);
         });
 
-        this.actor.add_actor(this._leftArrow);
-        this.actor.add_actor(this._rightArrow);
+        this.add_actor(this._leftArrow);
+        this.add_actor(this._rightArrow);
 
         this._items = [];
         this._highlighted = -1;
         this._squareItems = squareItems;
-        this._minSize = 0;
         this._scrollableRight = true;
         this._scrollableLeft = false;
-    },
 
-    _allocateTop(actor, box, flags) {
-        let leftPadding = this.actor.get_theme_node().get_padding(St.Side.LEFT);
-        let rightPadding = this.actor.get_theme_node().get_padding(St.Side.RIGHT);
-
-        let childBox = new Clutter.ActorBox();
-        let scrollable = this._minSize > box.x2 - box.x1;
-
-        box.y1 -= this.actor.get_theme_node().get_padding(St.Side.TOP);
-        box.y2 += this.actor.get_theme_node().get_padding(St.Side.BOTTOM);
-        this._scrollView.allocate(box, flags);
-
-        let arrowWidth = Math.floor(leftPadding / 3);
-        let arrowHeight = arrowWidth * 2;
-        childBox.x1 = leftPadding / 2;
-        childBox.y1 = this.actor.height / 2 - arrowWidth;
-        childBox.x2 = childBox.x1 + arrowWidth;
-        childBox.y2 = childBox.y1 + arrowHeight;
-        this._leftArrow.allocate(childBox, flags);
-        this._leftArrow.opacity = (this._scrollableLeft && scrollable) ? 255 : 0;
-
-        arrowWidth = Math.floor(rightPadding / 3);
-        arrowHeight = arrowWidth * 2;
-        childBox.x1 = this.actor.width - arrowWidth - rightPadding / 2;
-        childBox.y1 = this.actor.height / 2 - arrowWidth;
-        childBox.x2 = childBox.x1 + arrowWidth;
-        childBox.y2 = childBox.y1 + arrowHeight;
-        this._rightArrow.allocate(childBox, flags);
-        this._rightArrow.opacity = (this._scrollableRight && scrollable) ? 255 : 0;
+        layoutManager.homogeneous = squareItems;
     },
 
     addItem(item, label) {
-        let bbox = new St.Button({ style_class: 'item-box',
-                                   reactive: true });
+        let bbox = new SwitcherButton(this._squareItems);
 
         bbox.set_child(item);
         this._list.add_actor(bbox);
@@ -462,8 +451,8 @@ var SwitcherList = new Lang.Class({
         let adjustment = this._scrollView.hscroll.adjustment;
         let [value, lower, upper, stepIncrement, pageIncrement, pageSize] = adjustment.get_values();
         let [absItemX, absItemY] = this._items[index].get_transformed_position();
-        let [result, posX, posY] = this.actor.transform_stage_point(absItemX, 0);
-        let [containerWidth, containerHeight] = this.actor.get_transformed_size();
+        let [result, posX, posY] = this.transform_stage_point(absItemX, 0);
+        let [containerWidth, containerHeight] = this.get_transformed_size();
         if (posX + this._items[index].get_width() > containerWidth)
             this._scrollToRight();
         else if (this._items[index].allocation.x1 - value < 0)
@@ -490,7 +479,7 @@ var SwitcherList = new Lang.Class({
                            onComplete: () => {
                                 if (this._highlighted == 0)
                                     this._scrollableLeft = false;
-                                this.actor.queue_relayout();
+                                this.queue_relayout();
                            }
                           });
     },
@@ -514,7 +503,7 @@ var SwitcherList = new Lang.Class({
                            onComplete: () => {
                                 if (this._highlighted == this._items.length - 1)
                                     this._scrollableRight = false;
-                                this.actor.queue_relayout();
+                                this.queue_relayout();
                             }
                           });
     },
@@ -546,16 +535,15 @@ var SwitcherList = new Lang.Class({
         return [maxChildMin, maxChildNat];
     },
 
-    _getPreferredWidth(actor, forHeight, alloc) {
-        let [maxChildMin, maxChildNat] = this._maxChildWidth(forHeight);
+    vfunc_get_preferred_width(forHeight) {
+        let themeNode = this.get_theme_node();
+        let [maxChildMin, ] = this._maxChildWidth(forHeight);
+        let [minListWidth, ] = this._list.get_preferred_width(forHeight);
 
-        let totalSpacing = Math.max(this._list.spacing * (this._items.length - 1), 0);
-        alloc.min_size = this._items.length * maxChildMin + totalSpacing;
-        alloc.natural_size = alloc.min_size;
-        this._minSize = alloc.min_size;
+        return themeNode.adjust_preferred_width(maxChildMin, minListWidth);
     },
 
-    _getPreferredHeight(actor, forWidth, alloc) {
+    vfunc_get_preferred_height(forWidth) {
         let maxChildMin = 0;
         let maxChildNat = 0;
 
@@ -571,43 +559,46 @@ var SwitcherList = new Lang.Class({
             maxChildNat = maxChildMin;
         }
 
-        alloc.min_size = maxChildMin;
-        alloc.natural_size = maxChildNat;
+        let themeNode = this.get_theme_node();
+        return themeNode.adjust_preferred_height(maxChildMin, maxChildNat);
     },
 
-    _allocate(actor, box, flags) {
-        let childHeight = box.y2 - box.y1;
+    vfunc_allocate(box, flags) {
+        this.set_allocation(box, flags);
+
+        let contentBox = this.get_theme_node().get_content_box(box);
+        let width = contentBox.x2 - contentBox.x1;
+        let height = contentBox.y2 - contentBox.y1;
 
-        let [maxChildMin, maxChildNat] = this._maxChildWidth(childHeight);
-        let totalSpacing = Math.max(this._list.spacing * (this._items.length - 1), 0);
+        let leftPadding = this.get_theme_node().get_padding(St.Side.LEFT);
+        let rightPadding = this.get_theme_node().get_padding(St.Side.RIGHT);
 
-        let childWidth = Math.floor(Math.max(0, box.x2 - box.x1 - totalSpacing) / this._items.length);
+        let [, natScrollViewWidth] = this._scrollView.get_preferred_width(height);
 
-        let x = 0;
-        let children = this._list.get_children();
         let childBox = new Clutter.ActorBox();
+        let scrollable = natScrollViewWidth > width;
 
-        let parentRightPadding = this.actor.get_parent().get_theme_node().get_padding(St.Side.RIGHT);
-
-        for (let i = 0; i < children.length; i++) {
-            if (this._items.indexOf(children[i]) != -1) {
-                let [childMin, childNat] = children[i].get_preferred_height(childWidth);
-                let vSpacing = (childHeight - childNat) / 2;
-                childBox.x1 = x;
-                childBox.y1 = vSpacing;
-                childBox.x2 = x + childWidth;
-                childBox.y2 = childBox.y1 + childNat;
-                children[i].allocate(childBox, flags);
-
-                x += this._list.spacing + childWidth;
-            } else {
-                // Something else, eg, AppSwitcher's arrows;
-                // we don't allocate it.
-            }
-        }
+        this._scrollView.allocate(contentBox, flags);
+
+        let arrowWidth = Math.floor(leftPadding / 3);
+        let arrowHeight = arrowWidth * 2;
+        childBox.x1 = leftPadding / 2;
+        childBox.y1 = this.height / 2 - arrowWidth;
+        childBox.x2 = childBox.x1 + arrowWidth;
+        childBox.y2 = childBox.y1 + arrowHeight;
+        this._leftArrow.allocate(childBox, flags);
+        this._leftArrow.opacity = (this._scrollableLeft && scrollable) ? 255 : 0;
+
+        arrowWidth = Math.floor(rightPadding / 3);
+        arrowHeight = arrowWidth * 2;
+        childBox.x1 = this.width - arrowWidth - rightPadding / 2;
+        childBox.y1 = this.height / 2 - arrowWidth;
+        childBox.x2 = childBox.x1 + arrowWidth;
+        childBox.y2 = childBox.y1 + arrowHeight;
+        this._rightArrow.allocate(childBox, flags);
+        this._rightArrow.opacity = (this._scrollableRight && scrollable) ? 255 : 0;
     }
 });
-Signals.addSignalMethods(SwitcherList.prototype);
 
 function drawArrow(area, side) {
     let themeNode = area.get_theme_node();


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