12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010 |
- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
- const Lang = imports.lang;
- const Clutter = imports.gi.Clutter;
- const St = imports.gi.St;
- const Meta = imports.gi.Meta;
- const Pango = imports.gi.Pango;
- const Cinnamon = imports.gi.Cinnamon;
- const Signals = imports.signals;
- const Mainloop = imports.mainloop;
- const AppSwitcher = imports.ui.appSwitcher.appSwitcher;
- const Main = imports.ui.main;
- const Tweener = imports.ui.tweener;
- const WindowUtils = imports.misc.windowUtils;
- const POPUP_SCROLL_TIME = 0.10; // seconds
- const POPUP_DELAY_TIMEOUT = 150; // milliseconds
- const POPUP_FADE_OUT_TIME = 0.1; // seconds
- const APP_ICON_HOVER_TIMEOUT = 200; // milliseconds
- const THUMBNAIL_DEFAULT_SIZE = 256;
- const THUMBNAIL_POPUP_TIME = 0; // milliseconds
- const THUMBNAIL_FADE_TIME = 0.1; // seconds
- const PREVIEW_DELAY_TIMEOUT = 0; // milliseconds
- var PREVIEW_SWITCHER_FADEOUT_TIME = 0.2; // seconds
- const iconSizes = [96, 64, 48];
- function mod(a, b) {
- return (a + b) % b;
- }
- function ClassicSwitcher() {
- this._init.apply(this, arguments);
- }
- ClassicSwitcher.prototype = {
- __proto__: AppSwitcher.AppSwitcher.prototype,
-
- _init: function() {
- AppSwitcher.AppSwitcher.prototype._init.apply(this, arguments);
- this.actor = new Cinnamon.GenericContainer({ name: 'altTabPopup',
- reactive: true,
- visible: false });
-
- this._thumbnailTimeoutId = 0;
- this.thumbnailsVisible = false;
- this._displayPreviewTimeoutId = 0;
- Main.uiGroup.add_actor(this.actor);
- if (!this._setupModal())
- return;
-
- let styleSettings = global.settings.get_string("alttab-switcher-style");
- let features = styleSettings.split('+');
- this._iconsEnabled = features.indexOf('icons') !== -1;
- this._previewEnabled = features.indexOf('preview') !== -1;
- this._thumbnailsEnabled = features.indexOf('thumbnails') !== -1;
- if (!this._iconsEnabled && !this._previewEnabled && !this._thumbnailsEnabled)
- this._iconsEnabled = true;
- this._showThumbnails = this._thumbnailsEnabled && !this._iconsEnabled;
- this._showArrows = this._thumbnailsEnabled && this._iconsEnabled;
-
- this._updateList(0);
- this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
- this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
- this.actor.connect('allocate', Lang.bind(this, this._allocate));
-
- this._applist_act_id = 0;
- this._applist_enter_id = 0;
- // Need to force an allocation so we can figure out whether we
- // need to scroll when selecting
- this.actor.opacity = 0;
- this.actor.show();
- this.actor.get_allocation_box();
- },
- _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 monitor = this._activeMonitor;
- 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 bottomPadding = this.actor.get_theme_node().get_padding(St.Side.BOTTOM);
- let vPadding = this.actor.get_theme_node().get_vertical_padding();
- let hPadding = leftPadding + rightPadding;
- // Allocate the appSwitcher
- // We select a size based on an icon size that does not overflow the screen
- let [childMinHeight, childNaturalHeight] = this._appList.actor.get_preferred_height(monitor.width - hPadding);
- let [childMinWidth, childNaturalWidth] = this._appList.actor.get_preferred_width(childNaturalHeight);
- childBox.x1 = Math.max(monitor.x + leftPadding, monitor.x + Math.floor((monitor.width - childNaturalWidth) / 2));
- childBox.x2 = Math.min(monitor.x + monitor.width - rightPadding, childBox.x1 + childNaturalWidth);
- childBox.y1 = monitor.y + Math.floor((monitor.height - childNaturalHeight) / 2);
- childBox.y2 = childBox.y1 + childNaturalHeight;
- this._appList.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 && this._appIcons.length > 0) {
- let icon = this._appIcons[this._currentIndex].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(monitor.x + leftPadding, Math.floor(thumbnailCenter - childNaturalWidth / 2));
- if (childBox.x1 + childNaturalWidth > monitor.x + monitor.width - rightPadding) {
- let offset = (childBox.x1 + childNaturalWidth) - (monitor.x + monitor.width - rightPadding);
- childBox.x1 -= offset;
- }
- let spacing = this.actor.get_theme_node().get_length('spacing');
- childBox.x2 = childBox.x1 + childNaturalWidth;
- childBox.y1 = this._appList.actor.allocation.y2 + spacing;
- this._thumbnails.addClones(monitor.y + monitor.height - bottomPadding - 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() {
- Main.panelManager.panels.forEach(function(panel) { panel.actor.set_reactive(false); });
-
- this.actor.opacity = 255;
- this._initialDelayTimeoutId = 0;
- this._next();
- },
-
- _hide: function() {
- // window title and icon
- if(this._windowTitle) {
- this._windowTitle.hide();
- this._applicationIconBox.hide();
- }
- // panels
- Main.panelManager.panels.forEach(function(panel) { panel.actor.set_reactive(true); });
- Tweener.addTween(this.actor, { opacity: 0,
- time: POPUP_FADE_OUT_TIME,
- transition: 'easeOutQuad',
- onComplete: Lang.bind(this, this._destroyActors)
- });
- },
- _destroyActors: function() {
- Main.uiGroup.remove_actor(this.actor);
- this.actor.destroy();
- },
- _updateList: function(direction) {
- if(direction !== 0)
- return;
-
- if (this._appList) {
- if (this._applist_act_id !== 0) {
- this._appList.disconnect(this._applist_act_id);
- this._applist_act_id = 0;
- }
- if (this._applist_enter_id !== 0) {
- this._appList.disconnect(this._applist_enter_id);
- this._applist_enter_id = 0;
- }
- this._clearPreview();
- this._destroyThumbnails();
- this.actor.remove_actor(this._appList.actor);
- this._appList.actor.destroy();
- }
- this._appList = new AppList(this._windows, this._showThumbnails, this._showArrows, this._activeMonitor);
- this.actor.add_actor(this._appList.actor);
- if (!this._iconsEnabled && !this._thumbnailsEnabled) {
- this._appList.actor.hide();
- }
- this._applist_act_id = this._appList.connect('item-activated', Lang.bind(this, this._appActivated));
- this._applist_enter_id = this._appList.connect('item-entered', Lang.bind(this, this._appEntered));
-
- this._appIcons = this._appList.icons;
- this.actor.get_allocation_box();
- },
- _selectNext: function() {
- if (this._currentIndex == this._windows.length - 1) {
- this._currentIndex = 0;
- } else {
- this._currentIndex = this._currentIndex + 1;
- }
- },
- _selectPrevious: function() {
- if (this._currentIndex == 0) {
- this._currentIndex = this._windows.length-1;
- } else {
- this._currentIndex = this._currentIndex - 1;
- }
- },
- _onWorkspaceSelected: function() {
- this._windows = AppSwitcher.getWindowsForBinding(this._binding);
- this._currentIndex = 0;
- this._updateList(0);
- this._select(0);
- },
-
- _setCurrentWindow: function(window) {
- this._appList.highlight(this._currentIndex, false);
- this._doWindowPreview();
- this._destroyThumbnails();
-
- if (this._thumbnailTimeoutId != 0) {
- Mainloop.source_remove(this._thumbnailTimeoutId);
- this._thumbnailTimeoutId = 0;
- }
-
- if (this._showArrows) {
- this._thumbnailTimeoutId = Mainloop.timeout_add(
- THUMBNAIL_POPUP_TIME, Lang.bind(this, function() {
- if (!this._thumbnails)
- this._createThumbnails();
- this._thumbnails.highlight(0, false);
- this._thumbnailTimeoutId = 0;
- }));
- }
- },
- _onDestroy: function() {
- if (this._appList !== null) {
- if (this._applist_act_id > 0) {
- this._appList.disconnect(this._applist_act_id);
- this._applist_act_id = 0;
- }
- if (this._applist_enter_id > 0) {
- this._appList.disconnect(this._applist_enter_id);
- this._applist_enter_id = 0;
- }
- }
- if (this._thumbnailTimeoutId != 0) {
- Mainloop.source_remove(this._thumbnailTimeoutId);
- this._thumbnailTimeoutId = 0;
- }
- if (this._displayPreviewTimeoutId != 0) {
- Mainloop.source_remove(this._displayPreviewTimeoutId);
- this._displayPreviewTimeoutId = 0;
- }
- },
- _appActivated : function(appSwitcher, n) {
- this._activateSelected();
- },
- _appEntered : function(appSwitcher, n) {
- if (!this._mouseActive)
- return;
- this._select(n);
- },
- _windowActivated : function(thumbnailList, n) {
- this._activateSelected();
- },
-
- _clearPreview: function() {
- if (this._previewClones) {
- for (let i = 0; i < this._previewClones.length; ++i) {
- let clone = this._previewClones[i];
- Tweener.addTween(clone, {
- opacity: 0,
- time: PREVIEW_SWITCHER_FADEOUT_TIME / 4,
- transition: 'linear',
- onCompleteScope: this,
- onComplete: function() {
- this.actor.remove_actor(clone);
- clone.destroy();
- }
- });
- }
- this._previewClones = null;
- }
- },
-
- _doWindowPreview: function() {
- if (!this._previewEnabled || this._windows.length < 1)
- {
- return;
- }
- // Use a cancellable timeout to avoid flickering effect when tabbing rapidly through the set.
- if (this._displayPreviewTimeoutId) {
- Mainloop.source_remove(this._displayPreviewTimeoutId);
- this._displayPreviewTimeoutId = 0;
- }
- let delay = PREVIEW_DELAY_TIMEOUT;
- this._displayPreviewTimeoutId = Mainloop.timeout_add(delay, Lang.bind(this, this._showWindowPreview));
- },
-
- _showWindowPreview: function() {
- this._displayPreviewTimeoutId = 0;
- let childBox = new Clutter.ActorBox();
- let lastClone = null;
- let previewClones = [];
- let window = this._windows[this._currentIndex];
- let clones = WindowUtils.createWindowClone(window, 0, 0, true, false);
- for (let i = 0; i < clones.length; i++) {
- let clone = clones[i];
- previewClones.push(clone.actor);
- this.actor.add_actor(clone.actor);
- let [width, height] = clone.actor.get_size();
- childBox.x1 = clone.x;
- childBox.x2 = clone.x + width;
- childBox.y1 = clone.y;
- childBox.y2 = clone.y + height;
- clone.actor.allocate(childBox, 0);
- clone.actor.lower(this._appList.actor);
- if (lastClone) {
- lastClone.lower(clone.actor);
- }
- lastClone = clone.actor;
- }
- this._clearPreview();
- this._previewClones = previewClones;
- if (!this._previewBackdrop) {
- let backdrop = this._previewBackdrop = new St.Bin({style_class: 'switcher-preview-backdrop'});
- this.actor.add_actor(backdrop);
- // Make sure that the backdrop does not overlap the switcher.
- backdrop.lower(this._appList.actor);
- backdrop.lower(lastClone);
- childBox.x1 = this.actor.x;
- childBox.x2 = this.actor.x + this.actor.width;
- childBox.y1 = this.actor.y;
- childBox.y2 = this.actor.y + this.actor.height;
- backdrop.allocate(childBox, 0);
- backdrop.opacity = 0;
- Tweener.addTween(backdrop,
- { opacity: 255,
- time: PREVIEW_SWITCHER_FADEOUT_TIME / 4,
- transition: 'linear'
- });
- }
- },
- _destroyThumbnails : function() {
- if (!this._thumbnails) {
- return;
- }
- let thumbnailsActor = this._thumbnails.actor;
- this._thumbnails = null;
- this.actor.remove_actor(thumbnailsActor);
- thumbnailsActor.destroy();
- this.thumbnailsVisible = false;
-
- },
- _createThumbnails : function() {
- this._thumbnails = new ThumbnailList ([this._windows[this._currentIndex]], this._activeMonitor);
- this._thumbnails.connect('item-activated', Lang.bind(this, this._windowActivated));
- this.actor.add_actor(this._thumbnails.actor);
- // 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.actor.opacity = 0;
- Tweener.addTween(this._thumbnails.actor,
- { opacity: 255,
- time: THUMBNAIL_FADE_TIME,
- transition: 'easeOutQuad',
- onComplete: Lang.bind(this, function () { this.thumbnailsVisible = true; })
- });
- }
- };
- function AppIcon(window, showThumbnail) {
- this._init(window, showThumbnail);
- }
- AppIcon.prototype = {
- _init: function(window, showThumbnail) {
- this.window = window;
- this.showThumbnail = showThumbnail;
- let tracker = Cinnamon.WindowTracker.get_default();
- this.app = tracker.get_window_app(window);
- this.actor = new St.BoxLayout({ style_class: 'alt-tab-app',
- vertical: true });
- this.icon = null;
- this._iconBin = new St.Bin();
- this.actor.add(this._iconBin, { x_fill: false, y_fill: false } );
- let title = window.get_title();
- if (title) {
- if (window.minimized) {
- this.label = new St.Label({ text: "[" + title + "]"});
- let contrast_effect = new Clutter.BrightnessContrastEffect();
- contrast_effect.set_brightness_full(-0.5, -0.5, -0.5);
- this._iconBin.add_effect(contrast_effect);
- }
- else {
- this.label = new St.Label({ text: title });
- }
-
- let bin = new St.Bin({ x_align: St.Align.MIDDLE });
- bin.add_actor(this.label);
- this.actor.add(bin);
- }
- else {
- this.label = new St.Label({ text: this.app ? this.app.get_name() : window.title });
- this.actor.add(this.label, { x_fill: false });
- }
- },
- set_size: function(size) {
- if (this.showThumbnail){
- this.icon = new St.Widget();
- let clones = WindowUtils.createWindowClone(this.window, size * global.ui_scale, size * global.ui_scale, true, true);
- for (let i in clones) {
- let clone = clones[i];
- this.icon.add_actor(clone.actor);
- // the following 2 lines are used when cloning without positions (param #4 = false)
- //let [width, height] = clone.actor.get_size();
- //clone.actor.set_position(Math.round((size - width) / 2), Math.round((size - height) / 2));
- clone.actor.set_position(clone.x, clone.y);
- }
- } else {
- this.icon = this.app ?
- this.app.create_icon_texture_for_window(size, this.window) :
- new St.Icon({ icon_name: 'application-default-icon',
- icon_type: St.IconType.FULLCOLOR,
- icon_size: size });
- }
- size *= global.ui_scale;
- this._iconBin.set_size(size, size);
- this._iconBin.child = this.icon;
- }
- };
- function SwitcherList(squareItems, activeMonitor) {
- this._init(squareItems, activeMonitor);
- }
- SwitcherList.prototype = {
- _init : function(squareItems, activeMonitor) {
- this.actor = new Cinnamon.GenericContainer({ style_class: 'switcher-list' });
- this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
- this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
- this.actor.connect('allocate', Lang.bind(this, this._allocateTop));
- // Here we use a GenericContainer so that we can force all the
- // children except the separator to have the same width.
- // TODO: Separator is gone, we could use an St.ScrollView now.
- this._list = new Cinnamon.GenericContainer({ style_class: 'switcher-list-item-container' });
- this._list.spacing = -1;
- this._list.connect('style-changed', Lang.bind(this, function() {
- this._list.spacing = this._list.get_theme_node().get_length('spacing');
- }));
- this._list.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
- this._list.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
- this._list.connect('allocate', Lang.bind(this, this._allocate));
- 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({ style_class: 'switcher-arrow',
- pseudo_class: 'highlighted' });
- this._leftArrow.connect('repaint', Lang.bind(this,
- function() { _drawArrow(this._leftArrow, St.Side.LEFT); }));
- this._rightArrow = new St.DrawingArea({ style_class: 'switcher-arrow',
- pseudo_class: 'highlighted' });
- this._rightArrow.connect('repaint', Lang.bind(this,
- function() { _drawArrow(this._rightArrow, St.Side.RIGHT); }));
- this.actor.add_actor(this._leftArrow);
- this.actor.add_actor(this._rightArrow);
- this._items = [];
- this._highlighted = -1;
- this._squareItems = squareItems;
- this._minSize = 0;
- this._scrollableRight = true;
- this._scrollableLeft = false;
- this._activeMonitor = activeMonitor;
- },
- _allocateTop: function(actor, box, flags) {
- if (this._list.spacing === -1) {
- this._list.spacing = this._list.get_theme_node().get_length('spacing');
- }
- 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;
- this._clipBin.allocate(box, flags);
- childBox.x1 = 0;
- childBox.y1 = 0;
- childBox.x2 = this._leftGradient.width;
- childBox.y2 = this.actor.height;
- this._leftGradient.allocate(childBox, flags);
- this._leftGradient.opacity = (this._scrollableLeft && scrollable) ? 255 : 0;
- childBox.x1 = (this.actor.allocation.x2 - this.actor.allocation.x1) - this._rightGradient.width;
- childBox.y1 = 0;
- childBox.x2 = childBox.x1 + this._rightGradient.width;
- childBox.y2 = this.actor.height;
- this._rightGradient.allocate(childBox, flags);
- this._rightGradient.opacity = (this._scrollableRight && scrollable) ? 255 : 0;
- 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._leftGradient.opacity;
- 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._rightGradient.opacity;
- },
- addItem : function(item, label) {
- let bbox = new St.Button({ style_class: 'item-box',
- reactive: true });
- bbox.set_child(item);
- this._list.add_actor(bbox);
- let n = this._items.length;
- bbox.connect('clicked', Lang.bind(this, function() { this._onItemClicked(n); }));
- bbox.connect('enter-event', Lang.bind(this, function() { this._onItemEnter(n); }));
- bbox.label_actor = label;
- this._items.push(bbox);
- },
- _onItemClicked: function (index) {
- this._itemActivated(index);
- },
- _onItemEnter: function (index) {
- this._itemEntered(index);
- },
- highlight: function(index, justOutline) {
- if (this._highlighted != -1) {
- this._items[this._highlighted].remove_style_pseudo_class('outlined');
- this._items[this._highlighted].remove_style_pseudo_class('selected');
- }
- this._highlighted = index;
- if (this._highlighted != -1) {
- if (justOutline)
- this._items[this._highlighted].add_style_pseudo_class('outlined');
- else
- this._items[this._highlighted].add_style_pseudo_class('selected');
- }
- 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();
- if (posX + this._items[index].get_width() > containerWidth)
- this._scrollToRight();
- else if (posX < 0)
- this._scrollToLeft();
- },
- _scrollToLeft : function() {
- let x = this._items[this._highlighted].allocation.x1;
- this._scrollableRight = true;
- Tweener.addTween(this._list, { anchor_x: x,
- time: POPUP_SCROLL_TIME,
- transition: 'easeOutQuad',
- onComplete: Lang.bind(this, function () {
- if (this._highlighted == 0) {
- this._scrollableLeft = false;
- this.actor.queue_relayout();
- }
- })
- });
- },
- _scrollToRight : function() {
- this._scrollableLeft = true;
- let monitor = this._activeMonitor;
- let padding = this.actor.get_theme_node().get_horizontal_padding();
- let parentPadding = this.actor.get_parent().get_theme_node().get_horizontal_padding();
- let x = this._items[this._highlighted].allocation.x2 - monitor.width + padding + parentPadding;
- 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._scrollableRight = false;
- this.actor.queue_relayout();
- }
- })
- });
- },
- _itemActivated: function(n) {
- this.emit('item-activated', n);
- },
- _itemEntered: function(n) {
- this.emit('item-entered', n);
- },
- _maxChildWidth: function () {
- let maxChildMin = 0;
- let maxChildNat = 0;
- if (this._items.length > 0) {
- return this._items[0].get_preferred_width(-1);
- }
- return [0, 0]
- },
- _getPreferredWidth: function (actor, forHeight, alloc) {
- let [maxChildMin, maxChildNat] = this._maxChildWidth();
- let totalSpacing = this._list.spacing * Math.max(1, (this._items.length - 1));
- alloc.min_size = this._items.length * maxChildMin + totalSpacing;
- alloc.natural_size = alloc.min_size;
- this._minSize = alloc.min_size;
- },
- _getPreferredHeight: function (actor, forWidth, alloc) {
- let maxChildMin = 0;
- let maxChildNat = 0;
- for (let i = 0; i < this._items.length; i++) {
- let [childMin, childNat] = this._items[i].get_preferred_height(-1);
- maxChildMin = Math.max(childMin, maxChildMin);
- maxChildNat = Math.max(childNat, maxChildNat);
- }
- if (this._squareItems) {
- let [childMin, childNat] = this._maxChildWidth();
- maxChildMin = Math.max(childMin, maxChildMin);
- maxChildNat = maxChildMin;
- }
- alloc.min_size = maxChildMin;
- alloc.natural_size = maxChildNat;
- },
- _allocate: function (actor, box, flags) {
- let childHeight = box.y2 - box.y1;
- let [maxChildMin, maxChildNat] = this._maxChildWidth();
- let totalSpacing = this._list.spacing * (this._items.length - 1);
- let childWidth = Math.floor(Math.max(0, box.x2 - box.x1 - totalSpacing) / this._items.length);
- let x = 0;
- let children = this._list.get_children();
- let childBox = new Clutter.ActorBox();
- let monitor = this._activeMonitor;
- let parentRightPadding = this.actor.get_parent().get_theme_node().get_padding(St.Side.RIGHT);
- if (this.actor.allocation.x2 == monitor.x + monitor.width - parentRightPadding) {
- if (this._squareItems)
- childWidth = childHeight;
- else {
- let [childMin, childNat] = children[0].get_preferred_width(childHeight);
- childWidth = childMin;
- }
- }
- 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, AppList's arrows;
- // 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);
- // 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);
- }
- };
- Signals.addSignalMethods(SwitcherList.prototype);
- function AppList() {
- this._init.apply(this, arguments);
- }
- AppList.prototype = {
- __proto__ : SwitcherList.prototype,
- _init : function(windows, showThumbnails, showArrows, activeMonitor) {
- SwitcherList.prototype._init.call(this, true, activeMonitor);
- // Construct the AppIcons, add to the popup
- let activeWorkspace = global.workspace_manager.get_active_workspace();
- let workspaceIcons = [];
- let otherIcons = [];
- for (let i = 0; i < windows.length; i++) {
- workspaceIcons.push(new AppIcon(windows[i], showThumbnails));
- }
- this.icons = [];
- this._arrows = [];
- for (let i = 0; i < workspaceIcons.length; i++)
- this._addIcon(workspaceIcons[i]);
- if (workspaceIcons.length > 0 && otherIcons.length > 0)
- this.addSeparator();
- for (let i = 0; i < otherIcons.length; i++)
- this._addIcon(otherIcons[i]);
- this._curApp = -1;
- this._iconSize = 0;
- this._showArrows = showArrows;
- this._mouseTimeOutId = 0;
- this._activeMonitor = activeMonitor;
- },
- _getPreferredHeight: function (actor, forWidth, alloc) {
- if (this._items.length < 1) {
- alloc.min_size = alloc.natural_size = 32;
- return;
- }
- let j = 0;
- while(this._items.length > 1 && this._items[j].style_class != 'item-box') {
- j++;
- }
- let themeNode = this._items[j].get_theme_node();
- let iconPadding = themeNode.get_horizontal_padding();
- let iconBorder = themeNode.get_border_width(St.Side.LEFT) + themeNode.get_border_width(St.Side.RIGHT);
- let [iconMinHeight, iconNaturalHeight] = this.icons[j].label.get_preferred_height(-1);
- let iconSpacing = iconNaturalHeight + iconPadding + iconBorder;
- let totalSpacing = this._list.spacing * (this._items.length - 1);
- // We just assume the whole screen here due to weirdness happing with the passed width
- let parentPadding = this.actor.get_parent().get_theme_node().get_horizontal_padding();
- let availWidth = this._activeMonitor.width - parentPadding - 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] * global.ui_scale) + iconSpacing;
- let w = height * this._items.length + totalSpacing;
- if (w <= availWidth)
- break;
- }
- if (this._items.length == 1) {
- this._iconSize = iconSizes[0];
- height = (iconSizes[0] * global.ui_scale) + iconSpacing;
- }
- for(let i = 0; i < this.icons.length; i++) {
- if (this.icons[i].icon != null)
- break;
- this.icons[i].set_size(this._iconSize);
- }
- alloc.min_size = height;
- alloc.natural_size = height;
- },
- _allocate: function (actor, box, flags) {
- // Allocate the main list items
- SwitcherList.prototype._allocate.call(this, actor, box, flags);
- if (this._showArrows) {
- let arrowHeight = Math.floor(this.actor.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.x2 = childBox.x1 + arrowWidth;
- childBox.y1 = itemBox.y2 + arrowHeight;
- childBox.y2 = childBox.y1 + arrowHeight;
- this._arrows[i].allocate(childBox, flags);
- }
- }
- },
- // We override SwitcherList's _onItemEnter method to delay
- // activation when the thumbnail list is open
- _onItemEnter: function (index) {
- if (this._mouseTimeOutId != 0)
- Mainloop.source_remove(this._mouseTimeOutId);
- this._itemEntered(index);
- },
- _enterItem: function(index) {
- let [x, y, mask] = global.get_pointer();
- let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.ALL, x, y);
- if (this._items[index].contains(pickedActor))
- this._itemEntered(index);
- },
- // We override SwitcherList's highlight() method to also deal with
- // the AppList->ThumbnailList arrows.
- highlight : function(n, justOutline) {
- if (this._curApp != -1) {
- this._arrows[this._curApp].hide();
- }
-
- SwitcherList.prototype.highlight.call(this, n, justOutline);
- this._curApp = n;
-
- if (n != -1 && this._showArrows) {
- this._arrows[n].show();
- }
- },
- _addIcon : function(appIcon) {
- this.icons.push(appIcon);
- this.addItem(appIcon.actor, appIcon.label);
- let n = this._arrows.length;
- let arrow = new St.DrawingArea({ style_class: 'switcher-arrow' });
- arrow.connect('repaint', function() { _drawArrow(arrow, St.Side.BOTTOM); });
- this._list.add_actor(arrow);
- this._arrows.push(arrow);
- arrow.hide();
- }
- };
- function ThumbnailList(windows, activeMonitor) {
- this._init(windows, activeMonitor);
- }
- ThumbnailList.prototype = {
- __proto__ : SwitcherList.prototype,
- _init : function(windows, activeMonitor) {
- SwitcherList.prototype._init.call(this, false, activeMonitor);
- let activeWorkspace = global.workspace_manager.get_active_workspace();
- this._labels = new Array();
- this._thumbnailBins = new Array();
- this._clones = new Array();
- this._windows = windows;
- for (let i = 0; i < windows.length; i++) {
- let box = new St.BoxLayout({ style_class: 'thumbnail-box',
- vertical: true });
- let bin = new St.Bin({ style_class: 'thumbnail' });
- box.add_actor(bin);
- this._thumbnailBins.push(bin);
- let title = windows[i].get_title();
- if (title) {
- let name = new St.Label({ text: title });
- // St.Label doesn't support text-align so use a Bin
- let bin = new St.Bin({ x_align: St.Align.MIDDLE });
- this._labels.push(bin);
- bin.add_actor(name);
- box.add_actor(bin);
- this.addItem(box, name);
- } else {
- this.addItem(box, null);
- }
- }
- },
- 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.length > 0 ?
- this._labels[0].get_preferred_height(-1) : [0, 0];
- let spacing = this._items[0].child.get_theme_node().get_length('spacing');
- availHeight = Math.min(availHeight - labelNaturalHeight - totalPadding - spacing, THUMBNAIL_DEFAULT_SIZE * global.ui_scale);
- 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 * global.ui_scale, binHeight);
- for (let i = 0; i < this._thumbnailBins.length; i++) {
- let metaWindow = this._windows[i];
- let container = new St.Widget();
- let clones = WindowUtils.createWindowClone(metaWindow, availHeight, availHeight, true, true);
- for (let j = 0; j < clones.length; j++) {
- let clone = clones[j];
- container.add_actor(clone.actor);
- clone.actor.set_position(clone.x, clone.y);
- }
- this._thumbnailBins[i].set_height(binHeight);
- this._thumbnailBins[i].add_actor(container);
- this._clones.push(container);
- }
- // Make sure we only do this once
- this._thumbnailBins = new Array();
- }
- };
- function _drawArrow(area, side) {
- let themeNode = area.get_theme_node();
- let borderColor = themeNode.get_border_color(side);
- let bodyColor = themeNode.get_foreground_color();
- let [width, height] = area.get_surface_size ();
- let cr = area.get_context();
- cr.setLineWidth(1.0);
- Clutter.cairo_set_source_color(cr, borderColor);
- switch (side) {
- case St.Side.TOP:
- cr.moveTo(0, height);
- cr.lineTo(Math.floor(width * 0.5), 0);
- cr.lineTo(width, height);
- break;
- case St.Side.BOTTOM:
- cr.moveTo(width, 0);
- cr.lineTo(Math.floor(width * 0.5), height);
- cr.lineTo(0, 0);
- break;
- case St.Side.LEFT:
- cr.moveTo(width, height);
- cr.lineTo(0, Math.floor(height * 0.5));
- cr.lineTo(width, 0);
- break;
- case St.Side.RIGHT:
- cr.moveTo(0, 0);
- cr.lineTo(width, Math.floor(height * 0.5));
- cr.lineTo(0, height);
- break;
- }
- cr.strokePreserve();
- Clutter.cairo_set_source_color(cr, bodyColor);
- cr.fill();
- cr.$dispose();
- }
|