Prevent from being partly offscreen
Limit the AppSwitcher to the screen size by either downscaling or scrolling. We scale the icons down up from 96, 64, 48, 32 to 22 and start scrolling if we still fail to fit on screen. The thumbnail box is shifted to either left or right, when failing to fit we scroll here to. To prevent from being offscreen at the buttom we adjust the thumbnail height to fit. The old positioning logic is replaced with a ShellGenericContainer to implement a custom allocation system. https://bugzilla.gnome.org/show_bug.cgi?id=597983
This commit is contained in:
parent
f0e531310d
commit
367eaf9161
@ -702,6 +702,22 @@ StTooltip {
|
|||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.thumbnail-scroll-gradient-left {
|
||||||
|
background-gradient-direction: horizontal;
|
||||||
|
background-gradient-start: rgba(51, 51, 51, 1.0);
|
||||||
|
background-gradient-end: rgba(51, 51, 51, 0);
|
||||||
|
border-radius: 8px;
|
||||||
|
width: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnail-scroll-gradient-right {
|
||||||
|
background-gradient-direction: horizontal;
|
||||||
|
background-gradient-start: rgba(51, 51, 51, 0);
|
||||||
|
background-gradient-end: rgba(51, 51, 51, 1.0);
|
||||||
|
border-radius: 8px;
|
||||||
|
width: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
.switcher-list .item-box {
|
.switcher-list .item-box {
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
@ -714,7 +730,6 @@ StTooltip {
|
|||||||
|
|
||||||
.switcher-list .thumbnail {
|
.switcher-list .thumbnail {
|
||||||
width: 256px;
|
width: 256px;
|
||||||
height: 256px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.switcher-list .outlined-item-box {
|
.switcher-list .outlined-item-box {
|
||||||
|
320
js/ui/altTab.js
320
js/ui/altTab.js
@ -23,13 +23,16 @@ TRANSPARENT_COLOR.from_pixel(0x00000000);
|
|||||||
|
|
||||||
const POPUP_APPICON_SIZE = 96;
|
const POPUP_APPICON_SIZE = 96;
|
||||||
const POPUP_LIST_SPACING = 8;
|
const POPUP_LIST_SPACING = 8;
|
||||||
|
const POPUP_SCROLL_TIME = 0.10; // seconds
|
||||||
|
|
||||||
const DISABLE_HOVER_TIMEOUT = 500; // milliseconds
|
const DISABLE_HOVER_TIMEOUT = 500; // milliseconds
|
||||||
|
|
||||||
const THUMBNAIL_SIZE = 256;
|
const THUMBNAIL_DEFAULT_SIZE = 256;
|
||||||
const THUMBNAIL_POPUP_TIME = 500; // milliseconds
|
const THUMBNAIL_POPUP_TIME = 500; // milliseconds
|
||||||
const THUMBNAIL_FADE_TIME = 0.2; // seconds
|
const THUMBNAIL_FADE_TIME = 0.2; // seconds
|
||||||
|
|
||||||
|
const iconSizes = [96, 64, 48, 32, 22];
|
||||||
|
|
||||||
function mod(a, b) {
|
function mod(a, b) {
|
||||||
return (a + b) % b;
|
return (a + b) % b;
|
||||||
}
|
}
|
||||||
@ -40,11 +43,11 @@ function AltTabPopup() {
|
|||||||
|
|
||||||
AltTabPopup.prototype = {
|
AltTabPopup.prototype = {
|
||||||
_init : function() {
|
_init : function() {
|
||||||
this.actor = new Clutter.Group({ reactive: true,
|
this.actor = new Shell.GenericContainer({ reactive: true });
|
||||||
x: 0,
|
|
||||||
y: 0,
|
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||||
width: global.screen_width,
|
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||||
height: global.screen_height });
|
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
||||||
|
|
||||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||||
|
|
||||||
@ -62,6 +65,55 @@ AltTabPopup.prototype = {
|
|||||||
global.stage.add_actor(this.actor);
|
global.stage.add_actor(this.actor);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_getPreferredWidth: function (actor, forHeight, alloc) {
|
||||||
|
alloc.min_size = global.screen_width;
|
||||||
|
alloc.natural_size = global.screen_width;
|
||||||
|
},
|
||||||
|
|
||||||
|
_getPreferredHeight: function (actor, forWidth, alloc) {
|
||||||
|
alloc.min_size = global.screen_height;
|
||||||
|
alloc.natural_size = global.screen_height;
|
||||||
|
},
|
||||||
|
|
||||||
|
_allocate: function (actor, box, flags) {
|
||||||
|
let childBox = new Clutter.ActorBox();
|
||||||
|
let focus = global.get_focus_monitor();
|
||||||
|
|
||||||
|
// Allocate the appSwitcher
|
||||||
|
// We select a size based on an icon size that does not overflow the screen
|
||||||
|
let [childMinHeight, childNaturalHeight] = this._appSwitcher.actor.get_preferred_height(focus.width - POPUP_LIST_SPACING * 2);
|
||||||
|
let [childMinWidth, childNaturalWidth] = this._appSwitcher.actor.get_preferred_width(childNaturalHeight);
|
||||||
|
childBox.x1 = Math.max(POPUP_LIST_SPACING, focus.x + Math.floor((focus.width - childNaturalWidth) / 2));
|
||||||
|
childBox.x2 = Math.min(childBox.x1 + focus.width - POPUP_LIST_SPACING * 2, childBox.x1 + childNaturalWidth);
|
||||||
|
childBox.y1 = focus.y + Math.floor((focus.height - childNaturalHeight) / 2);
|
||||||
|
childBox.y2 = childBox.y1 + childNaturalHeight;
|
||||||
|
this._appSwitcher.actor.allocate(childBox, flags);
|
||||||
|
|
||||||
|
// Allocate the thumbnails
|
||||||
|
// We try to avoid overflowing the screen so we base the resulting size on
|
||||||
|
// those calculations
|
||||||
|
if (this._thumbnails) {
|
||||||
|
let icon = this._appIcons[this._currentApp].actor;
|
||||||
|
let [posX, posY] = icon.get_transformed_position();
|
||||||
|
let thumbnailCenter = posX + icon.width / 2;
|
||||||
|
let [childMinWidth, childNaturalWidth] = this._thumbnails.actor.get_preferred_width(-1);
|
||||||
|
childBox.x1 = Math.max(POPUP_LIST_SPACING, Math.floor(thumbnailCenter - childNaturalWidth / 2));
|
||||||
|
if (childBox.x1 + childNaturalWidth > focus.width - POPUP_LIST_SPACING * 2) {
|
||||||
|
let offset = childBox.x1 + childNaturalWidth - focus.width + POPUP_LIST_SPACING * 2;
|
||||||
|
childBox.x1 = Math.max(POPUP_LIST_SPACING, childBox.x1 - offset - POPUP_LIST_SPACING * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
childBox.x2 = childBox.x1 + childNaturalWidth;
|
||||||
|
if (childBox.x2 > focus.width - POPUP_LIST_SPACING)
|
||||||
|
childBox.x2 = focus.width - POPUP_LIST_SPACING;
|
||||||
|
childBox.y1 = this._appSwitcher.actor.allocation.y2 + POPUP_LIST_SPACING * 2;
|
||||||
|
this._thumbnails.addClones(focus.height - POPUP_LIST_SPACING - childBox.y1);
|
||||||
|
let [childMinHeight, childNaturalHeight] = this._thumbnails.actor.get_preferred_height(-1);
|
||||||
|
childBox.y2 = childBox.y1 + childNaturalHeight;
|
||||||
|
this._thumbnails.actor.allocate(childBox, flags);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
show : function(backward) {
|
show : function(backward) {
|
||||||
let tracker = Shell.WindowTracker.get_default();
|
let tracker = Shell.WindowTracker.get_default();
|
||||||
let apps = tracker.get_running_apps ("");
|
let apps = tracker.get_running_apps ("");
|
||||||
@ -84,10 +136,6 @@ AltTabPopup.prototype = {
|
|||||||
this._appSwitcher.connect('item-activated', Lang.bind(this, this._appActivated));
|
this._appSwitcher.connect('item-activated', Lang.bind(this, this._appActivated));
|
||||||
this._appSwitcher.connect('item-entered', Lang.bind(this, this._appEntered));
|
this._appSwitcher.connect('item-entered', Lang.bind(this, this._appEntered));
|
||||||
|
|
||||||
let focus = global.get_focus_monitor();
|
|
||||||
this._appSwitcher.actor.x = focus.x + Math.floor((focus.width - this._appSwitcher.actor.width) / 2);
|
|
||||||
this._appSwitcher.actor.y = focus.y + Math.floor((focus.height - this._appSwitcher.actor.height) / 2);
|
|
||||||
|
|
||||||
this._appIcons = this._appSwitcher.icons;
|
this._appIcons = this._appSwitcher.icons;
|
||||||
|
|
||||||
// Make the initial selection
|
// Make the initial selection
|
||||||
@ -289,6 +337,9 @@ AltTabPopup.prototype = {
|
|||||||
if (this._haveModal)
|
if (this._haveModal)
|
||||||
Main.popModal(this.actor);
|
Main.popModal(this.actor);
|
||||||
|
|
||||||
|
if (this._thumbnails)
|
||||||
|
this._destroyThumbnails();
|
||||||
|
|
||||||
if (this._keyPressEventId)
|
if (this._keyPressEventId)
|
||||||
global.stage.disconnect(this._keyPressEventId);
|
global.stage.disconnect(this._keyPressEventId);
|
||||||
if (this._keyReleaseEventId)
|
if (this._keyReleaseEventId)
|
||||||
@ -375,33 +426,6 @@ AltTabPopup.prototype = {
|
|||||||
|
|
||||||
this.actor.add_actor(this._thumbnails.actor);
|
this.actor.add_actor(this._thumbnails.actor);
|
||||||
|
|
||||||
let thumbnailCenter;
|
|
||||||
if (this._thumbnails.actor.width < this._appSwitcher.actor.width) {
|
|
||||||
// Center the thumbnails under the corresponding AppIcon.
|
|
||||||
// If this is being called when the switcher is first
|
|
||||||
// being brought up, then nothing will have been assigned
|
|
||||||
// an allocation yet, and the get_transformed_position()
|
|
||||||
// call will return 0,0.
|
|
||||||
// (http://bugzilla.openedhand.com/show_bug.cgi?id=1115).
|
|
||||||
// Calling clutter_actor_get_allocation_box() would force
|
|
||||||
// it to properly allocate itself, but we can't call that
|
|
||||||
// because it has an out-caller-allocates arg. So we use
|
|
||||||
// clutter_stage_get_actor_at_pos(), which will force a
|
|
||||||
// reallocation as a side effect.
|
|
||||||
global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, 0, 0);
|
|
||||||
|
|
||||||
let icon = this._appIcons[this._currentApp].actor;
|
|
||||||
let [stageX, stageY] = icon.get_transformed_position();
|
|
||||||
thumbnailCenter = stageX + icon.width / 2;
|
|
||||||
} else {
|
|
||||||
// Center the thumbnails on the monitor
|
|
||||||
let focus = global.get_focus_monitor();
|
|
||||||
thumbnailCenter = focus.x + focus.width / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._thumbnails.actor.x = Math.floor(thumbnailCenter - this._thumbnails.actor.width / 2);
|
|
||||||
this._thumbnails.actor.y = this._appSwitcher.actor.y + this._appSwitcher.actor.height + POPUP_LIST_SPACING;
|
|
||||||
|
|
||||||
this._thumbnails.actor.opacity = 0;
|
this._thumbnails.actor.opacity = 0;
|
||||||
Tweener.addTween(this._thumbnails.actor,
|
Tweener.addTween(this._thumbnails.actor,
|
||||||
{ opacity: 255,
|
{ opacity: 255,
|
||||||
@ -417,7 +441,7 @@ function SwitcherList(squareItems) {
|
|||||||
|
|
||||||
SwitcherList.prototype = {
|
SwitcherList.prototype = {
|
||||||
_init : function(squareItems) {
|
_init : function(squareItems) {
|
||||||
this.actor = new St.Bin({ style_class: 'switcher-list' });
|
this.actor = new St.BoxLayout({ style_class: 'switcher-list' });
|
||||||
|
|
||||||
// Here we use a GenericContainer so that we can force all the
|
// Here we use a GenericContainer so that we can force all the
|
||||||
// children except the separator to have the same width.
|
// children except the separator to have the same width.
|
||||||
@ -428,12 +452,36 @@ SwitcherList.prototype = {
|
|||||||
this._list.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
this._list.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||||
this._list.connect('allocate', Lang.bind(this, this._allocate));
|
this._list.connect('allocate', Lang.bind(this, this._allocate));
|
||||||
|
|
||||||
this.actor.add_actor(this._list);
|
this._clipBin = new St.Bin({style_class: 'cbin'});
|
||||||
|
this._clipBin.child = this._list;
|
||||||
|
this.actor.add_actor(this._clipBin);
|
||||||
|
|
||||||
|
this._leftGradient = new St.BoxLayout({style_class: 'thumbnail-scroll-gradient-left', vertical: true});
|
||||||
|
this._rightGradient = new St.BoxLayout({style_class: 'thumbnail-scroll-gradient-right', vertical: true});
|
||||||
|
this.actor.add_actor(this._leftGradient);
|
||||||
|
this.actor.add_actor(this._rightGradient);
|
||||||
|
|
||||||
|
// Those arrows indicate whether scrolling in one direction is possible
|
||||||
|
this._leftArrow = new St.DrawingArea();
|
||||||
|
this._leftArrow.connect('repaint', Lang.bind(this,
|
||||||
|
function (area) {
|
||||||
|
Shell.draw_box_pointer(area, Shell.PointerDirection.LEFT, TRANSPARENT_COLOR, POPUP_ARROW_COLOR);
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._rightArrow = new St.DrawingArea();
|
||||||
|
this._rightArrow.connect('repaint', Lang.bind(this,
|
||||||
|
function (area) {
|
||||||
|
Shell.draw_box_pointer(area, Shell.PointerDirection.RIGHT, TRANSPARENT_COLOR, POPUP_ARROW_COLOR);
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._leftGradient.add_actor(this._leftArrow);
|
||||||
|
this._rightGradient.add_actor(this._rightArrow);
|
||||||
|
|
||||||
this._items = [];
|
this._items = [];
|
||||||
this._highlighted = -1;
|
this._highlighted = -1;
|
||||||
this._separator = null;
|
this._separator = null;
|
||||||
this._squareItems = squareItems;
|
this._squareItems = squareItems;
|
||||||
|
this._scrollable = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
addItem : function(item) {
|
addItem : function(item) {
|
||||||
@ -472,6 +520,45 @@ SwitcherList.prototype = {
|
|||||||
else
|
else
|
||||||
this._items[this._highlighted].style_class = 'selected-item-box';
|
this._items[this._highlighted].style_class = 'selected-item-box';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let monitor = global.get_focus_monitor();
|
||||||
|
let itemSize = this._items[index].allocation.x2 - this._items[index].allocation.x1;
|
||||||
|
let [posX, posY] = this._items[index].get_transformed_position();
|
||||||
|
posX += this.actor.x;
|
||||||
|
|
||||||
|
if (posX + itemSize > monitor.width)
|
||||||
|
this._scrollToRight();
|
||||||
|
else if (posX < 0)
|
||||||
|
this._scrollToLeft();
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
_scrollToLeft : function() {
|
||||||
|
let x = this._items[this._highlighted].allocation.x1;
|
||||||
|
this._rightGradient.show();
|
||||||
|
Tweener.addTween(this._list, { anchor_x: x,
|
||||||
|
time: POPUP_SCROLL_TIME,
|
||||||
|
transition: 'easeOutQuad',
|
||||||
|
onComplete: Lang.bind(this, function () {
|
||||||
|
if (this._highlighted == 0)
|
||||||
|
this._leftGradient.hide();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_scrollToRight : function() {
|
||||||
|
let monitor = global.get_focus_monitor();
|
||||||
|
let padding = this.actor.get_theme_node().get_horizontal_padding();
|
||||||
|
let x = this._items[this._highlighted].allocation.x2 - monitor.width + padding + POPUP_LIST_SPACING * 2;
|
||||||
|
this._leftGradient.show();
|
||||||
|
Tweener.addTween(this._list, { anchor_x: x,
|
||||||
|
time: POPUP_SCROLL_TIME,
|
||||||
|
transition: 'easeOutQuad',
|
||||||
|
onComplete: Lang.bind(this, function () {
|
||||||
|
if (this._highlighted == this._items.length - 1)
|
||||||
|
this._rightGradient.hide();
|
||||||
|
})
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
_itemActivated: function(n) {
|
_itemActivated: function(n) {
|
||||||
@ -553,6 +640,15 @@ SwitcherList.prototype = {
|
|||||||
let x = 0;
|
let x = 0;
|
||||||
let children = this._list.get_children();
|
let children = this._list.get_children();
|
||||||
let childBox = new Clutter.ActorBox();
|
let childBox = new Clutter.ActorBox();
|
||||||
|
|
||||||
|
let focus = global.get_focus_monitor();
|
||||||
|
if (this.actor.allocation.x2 == focus.width - POPUP_LIST_SPACING) {
|
||||||
|
if (this._squareItems)
|
||||||
|
childWidth = childHeight;
|
||||||
|
else
|
||||||
|
childWidth = children[0].get_preferred_width(childHeight)[0];
|
||||||
|
}
|
||||||
|
|
||||||
for (let i = 0; i < children.length; i++) {
|
for (let i = 0; i < children.length; i++) {
|
||||||
if (this._items.indexOf(children[i]) != -1) {
|
if (this._items.indexOf(children[i]) != -1) {
|
||||||
let [childMin, childNat] = children[i].get_preferred_height(childWidth);
|
let [childMin, childNat] = children[i].get_preferred_height(childWidth);
|
||||||
@ -577,6 +673,44 @@ SwitcherList.prototype = {
|
|||||||
// we don't allocate it.
|
// we don't allocate it.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 topPadding = this.actor.get_theme_node().get_padding(St.Side.TOP);
|
||||||
|
let bottomPadding = this.actor.get_theme_node().get_padding(St.Side.BOTTOM);
|
||||||
|
|
||||||
|
// Show the arrows and gradients when scrolling is needed
|
||||||
|
if (children[children.length - 1].allocation.x2 > this.actor.width - leftPadding - rightPadding && !this._scrollable) {
|
||||||
|
this._leftGradient.set_height(this.actor.height);
|
||||||
|
this._leftGradient.x = this.actor.x;
|
||||||
|
this._leftGradient.y = this.actor.y;
|
||||||
|
|
||||||
|
this._rightGradient.set_height(this.actor.height);
|
||||||
|
this._rightGradient.x = this.actor.x + (this.actor.allocation.x2 - this.actor.allocation.x1) - this._rightGradient.width;
|
||||||
|
this._rightGradient.y = this.actor.y;
|
||||||
|
|
||||||
|
let arrowWidth = Math.floor(leftPadding / 3);
|
||||||
|
let arrowHeight = arrowWidth * 2;
|
||||||
|
this._leftArrow.set_size(arrowWidth, arrowHeight);
|
||||||
|
this._leftArrow.set_position(POPUP_LIST_SPACING, this.actor.height / 2 - arrowWidth);
|
||||||
|
|
||||||
|
arrowWidth = Math.floor(rightPadding / 3);
|
||||||
|
arrowHeight = arrowWidth * 2;
|
||||||
|
this._rightArrow.set_size(arrowWidth, arrowHeight);
|
||||||
|
this._rightArrow.set_position(this._rightGradient.width - arrowHeight, this.actor.height / 2 - arrowWidth);
|
||||||
|
|
||||||
|
this._scrollable = true;
|
||||||
|
|
||||||
|
this._leftGradient.hide();
|
||||||
|
this._rightGradient.show();
|
||||||
|
}
|
||||||
|
else if (!this._scrollable){
|
||||||
|
this._leftGradient.hide();
|
||||||
|
this._rightGradient.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clip the area for scrolling
|
||||||
|
this._clipBin.set_clip(0, -topPadding, (this.actor.allocation.x2 - this.actor.allocation.x1) - leftPadding - rightPadding, this.actor.height + bottomPadding);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -591,13 +725,18 @@ AppIcon.prototype = {
|
|||||||
this.app = app;
|
this.app = app;
|
||||||
this.actor = new St.BoxLayout({ style_class: "alt-tab-app",
|
this.actor = new St.BoxLayout({ style_class: "alt-tab-app",
|
||||||
vertical: true });
|
vertical: true });
|
||||||
this._icon = this.app.create_icon_texture(POPUP_APPICON_SIZE);
|
this.icon = null;
|
||||||
let iconBin = new St.Bin({height: POPUP_APPICON_SIZE, width: POPUP_APPICON_SIZE});
|
this._iconBin = new St.Bin();
|
||||||
iconBin.child = this._icon;
|
|
||||||
|
|
||||||
this.actor.add(iconBin, { x_fill: false, y_fill: false } );
|
this.actor.add(this._iconBin, { x_fill: false, y_fill: false } );
|
||||||
this._label = new St.Label({ text: this.app.get_name() });
|
this.label = new St.Label({ text: this.app.get_name() });
|
||||||
this.actor.add(this._label, { x_fill: false });
|
this.actor.add(this.label, { x_fill: false });
|
||||||
|
},
|
||||||
|
|
||||||
|
set_size: function(size) {
|
||||||
|
this.icon = this.app.create_icon_texture(size);
|
||||||
|
this._iconBin.set_size(size, size);
|
||||||
|
this._iconBin.child = this.icon;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -639,9 +778,50 @@ AppSwitcher.prototype = {
|
|||||||
this._addIcon(otherIcons[i]);
|
this._addIcon(otherIcons[i]);
|
||||||
|
|
||||||
this._curApp = -1;
|
this._curApp = -1;
|
||||||
|
this._iconSize = 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
_getPreferredHeight: function (actor, forWidth, alloc) {
|
||||||
|
let j = 0;
|
||||||
|
while(this._items.length > 1 && this._items[j].style_class != 'item-box') {
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
let iconPadding = this._items[j].get_theme_node().get_horizontal_padding();
|
||||||
|
let [iconMinHeight, iconNaturalHeight] = this.icons[j].label.get_preferred_height(-1);
|
||||||
|
let iconSpacing = iconNaturalHeight + iconPadding;
|
||||||
|
let totalSpacing = this._list.spacing * (this._items.length - 1);
|
||||||
|
if (this._separator)
|
||||||
|
totalSpacing += this._separator.width + this._list.spacing;
|
||||||
|
|
||||||
|
// We just assume the whole screen here due to weirdness happing with the passed width
|
||||||
|
let focus = global.get_focus_monitor();
|
||||||
|
let availWidth = focus.width - POPUP_LIST_SPACING * 2 - this.actor.get_theme_node().get_horizontal_padding();
|
||||||
|
let height = 0;
|
||||||
|
|
||||||
|
for(let i = 0; i < iconSizes.length; i++) {
|
||||||
|
this._iconSize = iconSizes[i];
|
||||||
|
height = iconSizes[i] + iconSpacing;
|
||||||
|
let w = height * this._items.length + totalSpacing;
|
||||||
|
if (w <= availWidth)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._items.length == 1) {
|
||||||
|
this._iconSize = iconSizes[0];
|
||||||
|
height = iconSizes[0] + iconSpacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
alloc.min_size = height;
|
||||||
|
alloc.natural_size = height;
|
||||||
},
|
},
|
||||||
|
|
||||||
_allocate: function (actor, box, flags) {
|
_allocate: function (actor, box, flags) {
|
||||||
|
for(let i = 0; i < this.icons.length; i++) {
|
||||||
|
if (this.icons[i].icon != null)
|
||||||
|
break;
|
||||||
|
this.icons[i].set_size(this._iconSize);
|
||||||
|
}
|
||||||
|
|
||||||
// Allocate the main list items
|
// Allocate the main list items
|
||||||
SwitcherList.prototype._allocate.call(this, actor, box, flags);
|
SwitcherList.prototype._allocate.call(this, actor, box, flags);
|
||||||
|
|
||||||
@ -739,39 +919,69 @@ ThumbnailList.prototype = {
|
|||||||
// that case.
|
// that case.
|
||||||
let separatorAdded = windows.length == 0 || windows[0].get_workspace() != activeWorkspace;
|
let separatorAdded = windows.length == 0 || windows[0].get_workspace() != activeWorkspace;
|
||||||
|
|
||||||
|
this._labels = new Array();
|
||||||
|
this._thumbnailBins = new Array();
|
||||||
|
this._clones = new Array();
|
||||||
|
this._windows = windows;
|
||||||
|
|
||||||
for (let i = 0; i < windows.length; i++) {
|
for (let i = 0; i < windows.length; i++) {
|
||||||
if (!separatorAdded && windows[i].get_workspace() != activeWorkspace) {
|
if (!separatorAdded && windows[i].get_workspace() != activeWorkspace) {
|
||||||
this.addSeparator();
|
this.addSeparator();
|
||||||
separatorAdded = true;
|
separatorAdded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mutterWindow = windows[i].get_compositor_private();
|
|
||||||
let windowTexture = mutterWindow.get_texture ();
|
|
||||||
let [width, height] = windowTexture.get_size();
|
|
||||||
let scale = Math.min(1.0, THUMBNAIL_SIZE / width, THUMBNAIL_SIZE / height);
|
|
||||||
|
|
||||||
let box = new St.BoxLayout({ style_class: "thumbnail-box",
|
let box = new St.BoxLayout({ style_class: "thumbnail-box",
|
||||||
vertical: true });
|
vertical: true });
|
||||||
|
|
||||||
let bin = new St.Bin({ style_class: "thumbnail" });
|
let bin = new St.Bin({ style_class: "thumbnail" });
|
||||||
let clone = new Clutter.Clone ({ source: windowTexture,
|
|
||||||
reactive: true,
|
|
||||||
width: width * scale,
|
|
||||||
height: height * scale });
|
|
||||||
|
|
||||||
bin.add_actor(clone);
|
|
||||||
box.add_actor(bin);
|
box.add_actor(bin);
|
||||||
|
this._thumbnailBins.push(bin);
|
||||||
|
|
||||||
let title = windows[i].get_title();
|
let title = windows[i].get_title();
|
||||||
if (title) {
|
if (title) {
|
||||||
let name = new St.Label({ text: title });
|
let name = new St.Label({ text: title });
|
||||||
// St.Label doesn't support text-align so use a Bin
|
// St.Label doesn't support text-align so use a Bin
|
||||||
let bin = new St.Bin({ x_align: St.Align.MIDDLE });
|
let bin = new St.Bin({ x_align: St.Align.MIDDLE });
|
||||||
|
this._labels.push(bin);
|
||||||
bin.add_actor(name);
|
bin.add_actor(name);
|
||||||
box.add_actor(bin);
|
box.add_actor(bin);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.addItem(box);
|
this.addItem(box);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
addClones : function (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();
|
||||||
|
let [labelMinHeight, labelNaturalHeight] = this._labels[0].get_preferred_height(-1);
|
||||||
|
let [found, spacing] = this._items[0].child.get_theme_node().get_length('spacing', false);
|
||||||
|
if (!found)
|
||||||
|
spacing = 0;
|
||||||
|
|
||||||
|
availHeight = Math.min(availHeight - labelNaturalHeight - totalPadding - spacing, THUMBNAIL_DEFAULT_SIZE);
|
||||||
|
let binHeight = availHeight + this._items[0].get_theme_node().get_vertical_padding() + this.actor.get_theme_node().get_vertical_padding() - spacing;
|
||||||
|
binHeight = Math.min(THUMBNAIL_DEFAULT_SIZE, binHeight);
|
||||||
|
|
||||||
|
for (let i = 0; i < this._thumbnailBins.length; i++) {
|
||||||
|
let mutterWindow = this._windows[i].get_compositor_private();
|
||||||
|
let windowTexture = mutterWindow.get_texture ();
|
||||||
|
let [width, height] = windowTexture.get_size();
|
||||||
|
let scale = Math.min(1.0, THUMBNAIL_DEFAULT_SIZE / width, availHeight / height);
|
||||||
|
let clone = new Clutter.Clone ({ source: windowTexture,
|
||||||
|
reactive: true,
|
||||||
|
width: width * scale,
|
||||||
|
height: height * scale });
|
||||||
|
|
||||||
|
this._thumbnailBins[i].set_height(binHeight);
|
||||||
|
this._thumbnailBins[i].add_actor(clone);
|
||||||
|
this._clones.push(clone);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we only do this once
|
||||||
|
this._thumbnailBins = new Array();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user