From c6cea277ebb5dd40faf9aa5f850618176b8abc3d Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Thu, 28 Jun 2018 12:34:01 -0300 Subject: [PATCH] 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 | 171 +++++++++++++++++++---------------------- 3 files changed, 183 insertions(+), 151 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 arrowHeight = Math.floor(this.actor.get_theme_node().get_padding(St.Side.BOTTOM) / 3); + let contentBox = this.get_theme_node().get_content_box(box); + + 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)); + 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(); - // 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._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 [maxChildMin, maxChildNat] = this._maxChildWidth(childHeight); - let totalSpacing = Math.max(this._list.spacing * (this._items.length - 1), 0); + let contentBox = this.get_theme_node().get_content_box(box); + let width = contentBox.x2 - contentBox.x1; + let height = contentBox.y2 - contentBox.y1; - let childWidth = Math.floor(Math.max(0, box.x2 - box.x1 - totalSpacing) / this._items.length); + let leftPadding = this.get_theme_node().get_padding(St.Side.LEFT); + let rightPadding = this.get_theme_node().get_padding(St.Side.RIGHT); + + 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); + this._scrollView.allocate(contentBox, flags); - 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); + 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; - x += this._list.spacing + childWidth; - } else { - // Something else, eg, AppSwitcher's arrows; - // we don't allocate it. - } - } + 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();