Compare commits
	
		
			33 Commits
		
	
	
		
			wip/fmuell
			...
			wip/re-sea
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					555d45f06c | ||
| 
						 | 
					0a8713770b | ||
| 
						 | 
					7d3ea1ac68 | ||
| 
						 | 
					7785710964 | ||
| 
						 | 
					3730dc01cf | ||
| 
						 | 
					66bf0df737 | ||
| 
						 | 
					4590b33f2e | ||
| 
						 | 
					6538f60322 | ||
| 
						 | 
					4b0ba8b7b8 | ||
| 
						 | 
					bc8965fe63 | ||
| 
						 | 
					e7fcce3484 | ||
| 
						 | 
					b1e4d3335c | ||
| 
						 | 
					a3a3f24ed3 | ||
| 
						 | 
					96191a9c96 | ||
| 
						 | 
					f4814d200b | ||
| 
						 | 
					52156930d3 | ||
| 
						 | 
					cbaa999ced | ||
| 
						 | 
					9491f6bd23 | ||
| 
						 | 
					afe8198d4b | ||
| 
						 | 
					6aa8f14285 | ||
| 
						 | 
					e073670c4d | ||
| 
						 | 
					17a3d2c63f | ||
| 
						 | 
					ca38e05ed4 | ||
| 
						 | 
					9841e56ebf | ||
| 
						 | 
					c0d3a14ac2 | ||
| 
						 | 
					d485fcf9ec | ||
| 
						 | 
					ce2c5106f8 | ||
| 
						 | 
					5e96c3dfb4 | ||
| 
						 | 
					a72b642f3e | ||
| 
						 | 
					8507d3c4e4 | ||
| 
						 | 
					c985fdccba | ||
| 
						 | 
					1d7c2b1c26 | ||
| 
						 | 
					ab60c628e7 | 
@@ -113,9 +113,13 @@
 | 
			
		||||
          <doc:summary>
 | 
			
		||||
            <doc:para>
 | 
			
		||||
              A dictionary describing the given search result, containing
 | 
			
		||||
              'id', 'name' (both strings) and either 'icon' (a serialized
 | 
			
		||||
              GIcon) or 'icon-data' (raw image data as (iiibiiay) - width,
 | 
			
		||||
              height, rowstride, has-alpha, bits per sample, channels, data)
 | 
			
		||||
              'id' and 'name' (both strings). Optionally, either 'gicon' (a
 | 
			
		||||
              serialized GIcon) or 'icon-data' (raw image data as (iiibiiay)
 | 
			
		||||
              - width, height, rowstride, has-alpha, bits per sample,
 | 
			
		||||
              channels, data) can be specified if the result can be better
 | 
			
		||||
              served with a thumbnail of the content (such as with images).
 | 
			
		||||
              A 'description' field (string) may also be specified if more
 | 
			
		||||
              context would help the user find the desired result.
 | 
			
		||||
            </doc:para>
 | 
			
		||||
          </doc:summary>
 | 
			
		||||
        </doc:doc>
 | 
			
		||||
@@ -143,5 +147,25 @@
 | 
			
		||||
        </doc:doc>
 | 
			
		||||
      </arg>
 | 
			
		||||
    </method>
 | 
			
		||||
 | 
			
		||||
    <method name="LaunchSearch">
 | 
			
		||||
      <doc:doc>
 | 
			
		||||
        <doc:description>
 | 
			
		||||
          <doc:para>
 | 
			
		||||
            Called when the user clicks on the provider icon. The provider
 | 
			
		||||
            application should open and run the active search term itself.
 | 
			
		||||
          </doc:para>
 | 
			
		||||
        </doc:description>
 | 
			
		||||
      </doc:doc>
 | 
			
		||||
      <arg type="as" direction="in">
 | 
			
		||||
        <doc:doc>
 | 
			
		||||
          <doc:summary>
 | 
			
		||||
            <doc:para>
 | 
			
		||||
              The current search term(s).
 | 
			
		||||
            </doc:para>
 | 
			
		||||
          </doc:summary>
 | 
			
		||||
        </doc:doc>
 | 
			
		||||
      </arg>
 | 
			
		||||
    </method>
 | 
			
		||||
  </interface>
 | 
			
		||||
</node>
 | 
			
		||||
 
 | 
			
		||||
@@ -577,6 +577,10 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    spacing: 40px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#overview-group {
 | 
			
		||||
    spacing: 12px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.window-caption {
 | 
			
		||||
    spacing: 25px;
 | 
			
		||||
}
 | 
			
		||||
@@ -711,7 +715,7 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
 | 
			
		||||
#searchResultsContent {
 | 
			
		||||
    padding-right: 20px;
 | 
			
		||||
    spacing: 36px;
 | 
			
		||||
    spacing: 16px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#searchResultsContent:rtl {
 | 
			
		||||
@@ -719,6 +723,25 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    padding-left: 20px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.search-section {
 | 
			
		||||
    /* This should be equal to #searchResultsContent spacing */
 | 
			
		||||
    spacing: 16px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.search-section-separator {
 | 
			
		||||
    -gradient-height: 1px;
 | 
			
		||||
    -gradient-start: rgba(255,255,255,0);
 | 
			
		||||
    -gradient-end: rgba(255,255,255,0.5);
 | 
			
		||||
    -margin-horizontal: 1.5em;
 | 
			
		||||
    height: 1px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.search-section-content {
 | 
			
		||||
    /* This is the space between the provider icon and the results container */
 | 
			
		||||
    spacing: 25px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.search-statustext,
 | 
			
		||||
.search-section-header {
 | 
			
		||||
    padding: 4px 12px;
 | 
			
		||||
    spacing: 4px;
 | 
			
		||||
@@ -744,6 +767,14 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    spacing: 4px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.search-providers-box {
 | 
			
		||||
    spacing: 12px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.results-list {
 | 
			
		||||
    spacing: 5px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Text labels are an odd number of pixels tall. The uneven top and bottom
 | 
			
		||||
 * padding compensates for this and ensures that the label is vertically
 | 
			
		||||
 * centered */
 | 
			
		||||
@@ -824,7 +855,10 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
 | 
			
		||||
.app-well-app > .overview-icon,
 | 
			
		||||
.show-apps > .overview-icon,
 | 
			
		||||
.search-result-content > .overview-icon {
 | 
			
		||||
.remove-favorite > .overview-icon,
 | 
			
		||||
.search-section-icon-bin,
 | 
			
		||||
.search-result,
 | 
			
		||||
.grid-search-result-content > .overview-icon {
 | 
			
		||||
    border-radius: 4px;
 | 
			
		||||
    padding: 3px;
 | 
			
		||||
    border: 1px rgba(0,0,0,0);
 | 
			
		||||
@@ -840,7 +874,10 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
 | 
			
		||||
.app-well-app:hover > .overview-icon,
 | 
			
		||||
.show-apps:hover > .overview-icon,
 | 
			
		||||
.search-result-content:hover > .overview-icon {
 | 
			
		||||
.remove-favorite:hover > .overview-icon,
 | 
			
		||||
.search-section-icon-bin:hover,
 | 
			
		||||
.search-result:hover,
 | 
			
		||||
.grid-search-result-content:hover > .overview-icon {
 | 
			
		||||
    background-color: rgba(255,255,255,0.1);
 | 
			
		||||
    text-shadow: black 0px 2px 2px;
 | 
			
		||||
    transition-duration: 100;
 | 
			
		||||
@@ -875,13 +912,39 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.app-well-app:focus > .overview-icon,
 | 
			
		||||
.search-result-content:focus > .overview-icon,
 | 
			
		||||
.show-apps:focus > .overview-icon,
 | 
			
		||||
.search-section-icon-bin:focus,
 | 
			
		||||
.search-result:focus,
 | 
			
		||||
.grid-search-result-content:focus > .overview-icon,
 | 
			
		||||
.app-well-app:selected > .overview-icon,
 | 
			
		||||
.search-result-content:selected > .overview-icon {
 | 
			
		||||
.search-section-icon-bin:selected,
 | 
			
		||||
.search-result:selected,
 | 
			
		||||
.grid-search-result-content:selected > .overview-icon {
 | 
			
		||||
    background-color: rgba(255,255,255,0.33);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* List Results */
 | 
			
		||||
 | 
			
		||||
.search-result {
 | 
			
		||||
    padding: 15px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.search-result-content {
 | 
			
		||||
  spacing: 12px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.search-result-details {
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.search-result-details-title {
 | 
			
		||||
    font-size: 16pt;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.search-result-details-description {
 | 
			
		||||
    font-size: 14pt;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* LookingGlass */
 | 
			
		||||
 | 
			
		||||
#LookingGlassDialog {
 | 
			
		||||
 
 | 
			
		||||
@@ -41,6 +41,7 @@ nobase_dist_js_DATA = 	\
 | 
			
		||||
	ui/boxpointer.js	\
 | 
			
		||||
	ui/calendar.js		\
 | 
			
		||||
	ui/checkBox.js		\
 | 
			
		||||
	ui/centerLayout.js	\
 | 
			
		||||
	ui/ctrlAltTab.js	\
 | 
			
		||||
	ui/dash.js		\
 | 
			
		||||
	ui/dateMenu.js		\
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const GMenu = imports.gi.GMenu;
 | 
			
		||||
@@ -371,6 +372,8 @@ const SettingsSearchProvider = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._appSys = Shell.AppSystem.get_default();
 | 
			
		||||
        this._gnomecc = this._appSys.lookup_app('gnome-control-center.desktop');
 | 
			
		||||
        let appInfo = Gio.DesktopAppInfo.new('gnome-control-center.desktop');
 | 
			
		||||
        this.icon = appInfo.get_icon();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getResultMetas: function(prefs, callback) {
 | 
			
		||||
@@ -379,9 +382,7 @@ const SettingsSearchProvider = new Lang.Class({
 | 
			
		||||
            let pref = prefs[i];
 | 
			
		||||
            metas.push({ 'id': pref,
 | 
			
		||||
                         'name': pref.get_name(),
 | 
			
		||||
                         'createIcon': function(size) {
 | 
			
		||||
                             return pref.create_icon_texture(size);
 | 
			
		||||
                         }
 | 
			
		||||
                         'createIcon': function(size) { return; }
 | 
			
		||||
                       });
 | 
			
		||||
        }
 | 
			
		||||
        callback(metas);
 | 
			
		||||
@@ -461,15 +462,15 @@ const AppWellIcon = new Lang.Class({
 | 
			
		||||
        this._draggable.connect('drag-begin', Lang.bind(this,
 | 
			
		||||
            function () {
 | 
			
		||||
                this._removeMenuTimeout();
 | 
			
		||||
                Main.overview.beginItemDrag(this);
 | 
			
		||||
                Main.overview.beginAppDrag(this);
 | 
			
		||||
            }));
 | 
			
		||||
        this._draggable.connect('drag-cancelled', Lang.bind(this,
 | 
			
		||||
            function () {
 | 
			
		||||
                Main.overview.cancelledItemDrag(this);
 | 
			
		||||
                Main.overview.cancelledAppDrag(this);
 | 
			
		||||
            }));
 | 
			
		||||
        this._draggable.connect('drag-end', Lang.bind(this,
 | 
			
		||||
            function () {
 | 
			
		||||
               Main.overview.endItemDrag(this);
 | 
			
		||||
               Main.overview.endAppDrag(this);
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										93
									
								
								js/ui/centerLayout.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								js/ui/centerLayout.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,93 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
function connectLayoutManager(layoutManager, styleChanged) {
 | 
			
		||||
    let widget, styleChangedId;
 | 
			
		||||
 | 
			
		||||
    function _styleChanged() {
 | 
			
		||||
        let themeNode = widget.get_theme_node();
 | 
			
		||||
        styleChanged(themeNode, widget);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function actorChanged() {
 | 
			
		||||
        if (widget) {
 | 
			
		||||
            widget.disconnect(styleChangedId);
 | 
			
		||||
            styleChangedId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let actor = layoutManager.get_actor();
 | 
			
		||||
        if (actor && actor instanceof St.Widget) {
 | 
			
		||||
            widget = actor;
 | 
			
		||||
            styleChangedId = widget.connect('style-changed', _styleChanged);
 | 
			
		||||
            _styleChanged();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    layoutManager.connect('notify::actor', actorChanged);
 | 
			
		||||
    return layoutManager;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function connectSpacing(layoutManager) {
 | 
			
		||||
    return connectLayoutManager(layoutManager, function(themeNode, widget) {
 | 
			
		||||
        layoutManager.spacing = themeNode.get_length('spacing');
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const CenterLayout = new Lang.Class({
 | 
			
		||||
    Name: 'CenterLayout',
 | 
			
		||||
    Extends: Clutter.BoxLayout,
 | 
			
		||||
 | 
			
		||||
    vfunc_allocate: function(container, box, flags) {
 | 
			
		||||
        let rtl = container.get_text_direction() == Clutter.TextDirection.RTL;
 | 
			
		||||
 | 
			
		||||
        let availWidth = box.x2 - box.x1;
 | 
			
		||||
        let availHeight = box.y2 - box.y1;
 | 
			
		||||
 | 
			
		||||
        // Assume that these are the first three widgets and they are all visible.
 | 
			
		||||
        let [left, center, right] = container.get_children();
 | 
			
		||||
 | 
			
		||||
        // Only support horizontal layouts for now.
 | 
			
		||||
        let [centerMinWidth, centerNaturalWidth] = center.get_preferred_width(availWidth);
 | 
			
		||||
 | 
			
		||||
        let sideWidth = (availWidth - centerNaturalWidth) / 2;
 | 
			
		||||
 | 
			
		||||
        let childBox = new Clutter.ActorBox();
 | 
			
		||||
        childBox.y1 = box.y1;
 | 
			
		||||
        childBox.y2 = box.y1 + availHeight;
 | 
			
		||||
 | 
			
		||||
        if (left) {
 | 
			
		||||
            let [leftMinWidth, leftNaturalWidth] = left.get_preferred_width(availWidth);
 | 
			
		||||
            let leftSide = Math.min(Math.floor(sideWidth), leftNaturalWidth);
 | 
			
		||||
            if (rtl) {
 | 
			
		||||
                childBox.x1 = availWidth - leftSide;
 | 
			
		||||
                childBox.x2 = availWidth;
 | 
			
		||||
            } else {
 | 
			
		||||
                childBox.x1 = 0;
 | 
			
		||||
                childBox.x2 = leftSide;
 | 
			
		||||
            }
 | 
			
		||||
            childBox.x1 += box.x1;
 | 
			
		||||
            left.allocate(childBox, flags);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        childBox.x1 = box.x1 + Math.ceil(sideWidth);
 | 
			
		||||
        childBox.x2 = childBox.x1 + centerNaturalWidth;
 | 
			
		||||
        center.allocate(childBox, flags);
 | 
			
		||||
 | 
			
		||||
        if (right) {
 | 
			
		||||
            let [rightMinWidth, rightNaturalWidth] = right.get_preferred_width(availWidth);
 | 
			
		||||
            let rightSide = Math.min(Math.floor(sideWidth), rightNaturalWidth);
 | 
			
		||||
            if (rtl) {
 | 
			
		||||
                childBox.x1 = 0;
 | 
			
		||||
                childBox.x2 = rightSide;
 | 
			
		||||
            } else {
 | 
			
		||||
                childBox.x1 = availWidth - rightSide;
 | 
			
		||||
                childBox.x2 = availWidth;
 | 
			
		||||
            }
 | 
			
		||||
            childBox.x1 += box.x1;
 | 
			
		||||
            right.allocate(childBox, flags);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
@@ -372,6 +372,9 @@ const Dash = new Lang.Class({
 | 
			
		||||
        this._maxHeight = -1;
 | 
			
		||||
        this.iconSize = 64;
 | 
			
		||||
        this._shownInitially = false;
 | 
			
		||||
        this.visible = false;
 | 
			
		||||
        this._hiddenX;
 | 
			
		||||
        this._targetX;
 | 
			
		||||
 | 
			
		||||
        this._dragPlaceholder = null;
 | 
			
		||||
        this._dragPlaceholderPos = -1;
 | 
			
		||||
@@ -399,7 +402,8 @@ const Dash = new Lang.Class({
 | 
			
		||||
            function() {
 | 
			
		||||
                if (this._maxHeight != this.actor.height)
 | 
			
		||||
                    this._queueRedisplay();
 | 
			
		||||
                this._maxHeight = this.actor.height;
 | 
			
		||||
                if (this.actor.height > this._maxHeight)
 | 
			
		||||
                    this._maxHeight = this.actor.height;
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        this._workId = Main.initializeDeferredWork(this._box, Lang.bind(this, this._redisplay));
 | 
			
		||||
@@ -410,11 +414,11 @@ const Dash = new Lang.Class({
 | 
			
		||||
        AppFavorites.getAppFavorites().connect('changed', Lang.bind(this, this._queueRedisplay));
 | 
			
		||||
        this._appSystem.connect('app-state-changed', Lang.bind(this, this._queueRedisplay));
 | 
			
		||||
 | 
			
		||||
        Main.overview.connect('item-drag-begin',
 | 
			
		||||
        Main.overview.connect('app-drag-begin',
 | 
			
		||||
                              Lang.bind(this, this._onDragBegin));
 | 
			
		||||
        Main.overview.connect('item-drag-end',
 | 
			
		||||
        Main.overview.connect('app-drag-end',
 | 
			
		||||
                              Lang.bind(this, this._onDragEnd));
 | 
			
		||||
        Main.overview.connect('item-drag-cancelled',
 | 
			
		||||
        Main.overview.connect('app-drag-cancelled',
 | 
			
		||||
                              Lang.bind(this, this._onDragCancelled));
 | 
			
		||||
        Main.overview.connect('window-drag-begin',
 | 
			
		||||
                              Lang.bind(this, this._onDragBegin));
 | 
			
		||||
@@ -546,6 +550,17 @@ const Dash = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _computeDashX: function() {
 | 
			
		||||
        this._targetX = this.actor.get_x();
 | 
			
		||||
 | 
			
		||||
        let rtl = (this.actor.get_text_direction() == Clutter.TextDirection.RTL);
 | 
			
		||||
 | 
			
		||||
        if (rtl)
 | 
			
		||||
            this._hiddenX = this._targetX + this.actor.width;
 | 
			
		||||
        else
 | 
			
		||||
            this._hiddenX = -this.actor.width;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _adjustIconSize: function() {
 | 
			
		||||
        // For the icon size, we only consider children which are "proper"
 | 
			
		||||
        // icons (i.e. ignoring drag placeholders) and which are not
 | 
			
		||||
@@ -641,6 +656,8 @@ const Dash = new Lang.Class({
 | 
			
		||||
                               }
 | 
			
		||||
                             });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._computeDashX();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _redisplay: function () {
 | 
			
		||||
@@ -757,6 +774,7 @@ const Dash = new Lang.Class({
 | 
			
		||||
        // of items, to avoid all items zooming in at once
 | 
			
		||||
        if (!this._shownInitially) {
 | 
			
		||||
            this._shownInitially = true;
 | 
			
		||||
            this.visible = true;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -901,6 +919,34 @@ const Dash = new Lang.Class({
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    show: function() {
 | 
			
		||||
        if (this.visible)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.visible = true;
 | 
			
		||||
 | 
			
		||||
        this.actor.show();
 | 
			
		||||
        Tweener.addTween(this.actor, { translation_x: this._targetX,
 | 
			
		||||
                                       transition: 'easeOutQuad',
 | 
			
		||||
                                       time: DASH_ANIMATION_TIME
 | 
			
		||||
                                     });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hide: function() {
 | 
			
		||||
        if (!this.visible)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.visible = false;
 | 
			
		||||
 | 
			
		||||
        Tweener.addTween(this.actor, { translation_x: this._hiddenX,
 | 
			
		||||
                                       transition: 'easeOutQuad',
 | 
			
		||||
                                       time: DASH_ANIMATION_TIME,
 | 
			
		||||
                                       onComplete: Lang.bind(this, function () {
 | 
			
		||||
                                                       this.actor.hide();
 | 
			
		||||
                                                   })
 | 
			
		||||
                                     });
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -198,7 +198,11 @@ const IconGrid = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _getPreferredHeight: function (grid, forWidth, alloc) {
 | 
			
		||||
        let children = this._getVisibleChildren();
 | 
			
		||||
        let [nColumns, usedWidth] = this._computeLayout(forWidth);
 | 
			
		||||
        let nColumns;
 | 
			
		||||
        if (forWidth < 0)
 | 
			
		||||
            nColumns = children.length;
 | 
			
		||||
        else
 | 
			
		||||
            nColumns = this._computeLayout(forWidth)[0];
 | 
			
		||||
        let nRows;
 | 
			
		||||
        if (nColumns > 0)
 | 
			
		||||
            nRows = Math.ceil(children.length / nColumns);
 | 
			
		||||
 
 | 
			
		||||
@@ -1498,7 +1498,7 @@ const MessageTray = new Lang.Class({
 | 
			
		||||
                this._overviewVisible = true;
 | 
			
		||||
                this._grabHelper.ungrab(); // drop modal grab if necessary
 | 
			
		||||
                this.actor.add_style_pseudo_class('overview');
 | 
			
		||||
                this._updateState();
 | 
			
		||||
                this.show();
 | 
			
		||||
            }));
 | 
			
		||||
        Main.overview.connect('hiding', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
@@ -1736,6 +1736,11 @@ const MessageTray = new Lang.Class({
 | 
			
		||||
        this._updateState();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    show: function() {
 | 
			
		||||
        this._traySummoned = true;
 | 
			
		||||
        this._updateState();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onNotify: function(source, notification) {
 | 
			
		||||
        if (this._summaryBoxPointerItem && this._summaryBoxPointerItem.source == source) {
 | 
			
		||||
            if (this._summaryBoxPointerState == State.HIDING) {
 | 
			
		||||
@@ -1978,7 +1983,7 @@ const MessageTray = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Summary
 | 
			
		||||
        let summarySummoned = this._pointerInSummary || this._overviewVisible ||  this._traySummoned;
 | 
			
		||||
        let summarySummoned = this._pointerInSummary ||  this._traySummoned;
 | 
			
		||||
        let summaryPinned = this._pointerInTray || summarySummoned || this._locked;
 | 
			
		||||
        let summaryHovered = this._pointerInTray || this._pointerInSummary;
 | 
			
		||||
 | 
			
		||||
@@ -1986,7 +1991,7 @@ const MessageTray = new Lang.Class({
 | 
			
		||||
                                    this._notificationState == State.SHOWN);
 | 
			
		||||
        let notificationsDone = !notificationsVisible && !notificationsPending;
 | 
			
		||||
 | 
			
		||||
        let summaryOptionalInOverview = this._overviewVisible && !this._locked && !summaryHovered;
 | 
			
		||||
        let summaryOptionalInOverview = !this._locked && !summaryHovered;
 | 
			
		||||
        let mustHideSummary = (notificationsPending && (notificationUrgent || summaryOptionalInOverview))
 | 
			
		||||
                              || notificationsVisible || !Main.sessionMode.hasNotifications;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ const St = imports.gi.St;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Gdk = imports.gi.Gdk;
 | 
			
		||||
 | 
			
		||||
const CenterLayout = imports.ui.centerLayout;
 | 
			
		||||
const Dash = imports.ui.dash;
 | 
			
		||||
const DND = imports.ui.dnd;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
@@ -23,9 +24,6 @@ const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
 | 
			
		||||
// Time for initial animation going into Overview mode
 | 
			
		||||
const ANIMATION_TIME = 0.25;
 | 
			
		||||
 | 
			
		||||
// XXX -- grab this automatically. Hard to do.
 | 
			
		||||
const DASH_MAX_WIDTH = 96;
 | 
			
		||||
 | 
			
		||||
const DND_WINDOW_SWITCH_TIMEOUT = 1250;
 | 
			
		||||
 | 
			
		||||
const SwipeScrollDirection = {
 | 
			
		||||
@@ -119,23 +117,18 @@ const Overview = new Lang.Class({
 | 
			
		||||
        this._desktopFade = new St.Bin();
 | 
			
		||||
        global.overlay_group.add_actor(this._desktopFade);
 | 
			
		||||
 | 
			
		||||
        this._spacing = 0;
 | 
			
		||||
 | 
			
		||||
        /* Translators: This is the main view to select
 | 
			
		||||
           activities. See also note for "Activities" string. */
 | 
			
		||||
        this._group = new St.Widget({ name: 'overview',
 | 
			
		||||
                                      accessible_name: _("Overview"),
 | 
			
		||||
                                      reactive: true });
 | 
			
		||||
        this._group._delegate = this;
 | 
			
		||||
        this._group.connect('style-changed',
 | 
			
		||||
            Lang.bind(this, function() {
 | 
			
		||||
                let node = this._group.get_theme_node();
 | 
			
		||||
                let spacing = node.get_length('spacing');
 | 
			
		||||
                if (spacing != this._spacing) {
 | 
			
		||||
                    this._spacing = spacing;
 | 
			
		||||
                    this._relayout();
 | 
			
		||||
                }
 | 
			
		||||
            }));
 | 
			
		||||
        this._overview = new St.BoxLayout({ name: 'overview',
 | 
			
		||||
                                            accessible_name: _("Overview"),
 | 
			
		||||
                                            reactive: true,
 | 
			
		||||
                                            vertical: true });
 | 
			
		||||
        this._overview._delegate = this;
 | 
			
		||||
 | 
			
		||||
        let layout = new CenterLayout.CenterLayout();
 | 
			
		||||
        CenterLayout.connectSpacing(layout);
 | 
			
		||||
        this._group = new St.Widget({ name: 'overview-group',
 | 
			
		||||
                                      layout_manager: layout });
 | 
			
		||||
 | 
			
		||||
        this._scrollDirection = SwipeScrollDirection.NONE;
 | 
			
		||||
        this._scrollAdjustment = null;
 | 
			
		||||
@@ -148,18 +141,19 @@ const Overview = new Lang.Class({
 | 
			
		||||
        this._modal = false;            // have a modal grab
 | 
			
		||||
        this.animationInProgress = false;
 | 
			
		||||
        this._hideInProgress = false;
 | 
			
		||||
        this.searchActive = false;
 | 
			
		||||
        this.appsActive = false;
 | 
			
		||||
 | 
			
		||||
        // During transitions, we raise this to the top to avoid having the overview
 | 
			
		||||
        // area be reactive; it causes too many issues such as double clicks on
 | 
			
		||||
        // Dash elements, or mouseover handlers in the workspaces.
 | 
			
		||||
        this._coverPane = new Clutter.Rectangle({ opacity: 0,
 | 
			
		||||
                                                  reactive: true });
 | 
			
		||||
        this._group.add_actor(this._coverPane);
 | 
			
		||||
        this._overview.add_actor(this._coverPane);
 | 
			
		||||
        this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return true; }));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        this._group.hide();
 | 
			
		||||
        global.overlay_group.add_actor(this._group);
 | 
			
		||||
        this._overview.hide();
 | 
			
		||||
        global.overlay_group.add_actor(this._overview);
 | 
			
		||||
 | 
			
		||||
        this._coverPane.hide();
 | 
			
		||||
 | 
			
		||||
@@ -171,6 +165,8 @@ const Overview = new Lang.Class({
 | 
			
		||||
        Main.xdndHandler.connect('drag-begin', Lang.bind(this, this._onDragBegin));
 | 
			
		||||
        Main.xdndHandler.connect('drag-end', Lang.bind(this, this._onDragEnd));
 | 
			
		||||
 | 
			
		||||
        global.screen.connect('restacked', Lang.bind(this, this._onRestacked));
 | 
			
		||||
 | 
			
		||||
        this._windowSwitchTimeoutId = 0;
 | 
			
		||||
        this._windowSwitchTimestamp = 0;
 | 
			
		||||
        this._lastActiveWorkspaceIndex = -1;
 | 
			
		||||
@@ -193,6 +189,13 @@ const Overview = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._shellInfo = new ShellInfo();
 | 
			
		||||
 | 
			
		||||
        // Add a clone of the panel to the overview so spacing and such is
 | 
			
		||||
        // automatic
 | 
			
		||||
        this._panelGhost = new St.Bin({ child: new Clutter.Clone({ source: Main.panel.actor }),
 | 
			
		||||
                                        reactive: false,
 | 
			
		||||
                                        opacity: 0 });
 | 
			
		||||
        this._overview.add_actor(this._panelGhost);
 | 
			
		||||
 | 
			
		||||
        this._searchEntry = new St.Entry({ name: 'searchEntry',
 | 
			
		||||
                                           /* Translators: this is the text displayed
 | 
			
		||||
                                              in the search entry when no search is
 | 
			
		||||
@@ -201,16 +204,13 @@ const Overview = new Lang.Class({
 | 
			
		||||
                                           hint_text: _("Type to search..."),
 | 
			
		||||
                                           track_hover: true,
 | 
			
		||||
                                           can_focus: true });
 | 
			
		||||
        this._group.add_actor(this._searchEntry);
 | 
			
		||||
 | 
			
		||||
        this._dash = new Dash.Dash();
 | 
			
		||||
        this._viewSelector = new ViewSelector.ViewSelector(this._searchEntry,
 | 
			
		||||
                                                           this._dash.showAppsButton);
 | 
			
		||||
        this._group.add_actor(this._viewSelector.actor);
 | 
			
		||||
        this._group.add_actor(this._dash.actor);
 | 
			
		||||
        this._searchEntryBin = new St.Bin({ child: this._searchEntry,
 | 
			
		||||
                                            x_align: St.Align.MIDDLE });
 | 
			
		||||
        this._overview.add_actor(this._searchEntryBin);
 | 
			
		||||
 | 
			
		||||
        // TODO - recalculate everything when desktop size changes
 | 
			
		||||
        this._dash.actor.add_constraint(this._viewSelector.constrainHeight);
 | 
			
		||||
        this._dash = new Dash.Dash();
 | 
			
		||||
        this._group.add_actor(this._dash.actor);
 | 
			
		||||
        this.dashIconSize = this._dash.iconSize;
 | 
			
		||||
        this._dash.connect('icon-size-changed',
 | 
			
		||||
                           Lang.bind(this, function() {
 | 
			
		||||
@@ -221,6 +221,82 @@ const Overview = new Lang.Class({
 | 
			
		||||
        // the left of the overview
 | 
			
		||||
        Main.ctrlAltTabManager.addGroup(this._dash.actor, _("Dash"), 'user-bookmarks-symbolic');
 | 
			
		||||
 | 
			
		||||
        this._viewSelector = new ViewSelector.ViewSelector(this._searchEntry,
 | 
			
		||||
                                                           this._dash.showAppsButton);
 | 
			
		||||
        this._group.add_actor(this._viewSelector.actor);
 | 
			
		||||
 | 
			
		||||
        this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox();
 | 
			
		||||
        this._group.add_actor(this._thumbnailsBox.actor);
 | 
			
		||||
 | 
			
		||||
        // TODO: unique icon
 | 
			
		||||
        Main.ctrlAltTabManager.addGroup(this._thumbnailsBox.actor, _("Workspaces"), 'user-bookmarks-symbolic');
 | 
			
		||||
 | 
			
		||||
        // Add our same-line elements after the search entry
 | 
			
		||||
        this._overview.add_actor(this._group);
 | 
			
		||||
 | 
			
		||||
        // Then account for message tray
 | 
			
		||||
        this._messageTrayGhost = new St.Bin({ child: new Clutter.Clone({ source: Main.messageTray.actor }),
 | 
			
		||||
                                              reactive: false,
 | 
			
		||||
                                              opacity: 0 });
 | 
			
		||||
        this._overview.add_actor(this._messageTrayGhost);
 | 
			
		||||
 | 
			
		||||
        this._viewSelector.connect('search-begin', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                this.searchActive = true;
 | 
			
		||||
                this._dash.hide();
 | 
			
		||||
                this._thumbnailsBox.hide();
 | 
			
		||||
                Main.messageTray.hide();
 | 
			
		||||
            }));
 | 
			
		||||
        this._viewSelector.connect('search-cancelled', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                this.searchActive = false;
 | 
			
		||||
                this._dash.show();
 | 
			
		||||
                this._thumbnailsBox.show();
 | 
			
		||||
                // search-cancelled is emitted if we leave the overview
 | 
			
		||||
                // directly from searching. That means the message tray will
 | 
			
		||||
                // be shown when we reach the workspace. Don't do this, only
 | 
			
		||||
                // show the message tray on search-cancelled if we are still
 | 
			
		||||
                // in the overview/not transitioning out of it.
 | 
			
		||||
                if (this.visible && !this.animationInProgress)
 | 
			
		||||
                    Main.messageTray.show();
 | 
			
		||||
            }));
 | 
			
		||||
        this._viewSelector.connect('apps-button-checked', Lang.bind(this,
 | 
			
		||||
            function(vs, checked) {
 | 
			
		||||
                this.appsActive = checked;
 | 
			
		||||
                if (checked) {
 | 
			
		||||
                    this._thumbnailsBox.hide();
 | 
			
		||||
                    Main.messageTray.hide();
 | 
			
		||||
                } else {
 | 
			
		||||
                    this._thumbnailsBox.show();
 | 
			
		||||
                    if (this.visible && !this.animationInProgress)
 | 
			
		||||
                        Main.messageTray.show();
 | 
			
		||||
                }
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        this.connect('app-drag-begin',
 | 
			
		||||
                              Lang.bind(this, function () {
 | 
			
		||||
                                  this._dash.show();
 | 
			
		||||
                                  this._thumbnailsBox.show();
 | 
			
		||||
                              }));
 | 
			
		||||
        this.connect('app-drag-cancelled',
 | 
			
		||||
                              Lang.bind(this, function () {
 | 
			
		||||
                                  if (this.searchActive) {
 | 
			
		||||
                                      this._dash.hide();
 | 
			
		||||
                                      this._thumbnailsBox.hide();
 | 
			
		||||
                                  } else if (this.appsActive) {
 | 
			
		||||
                                      this._thumbnailsBox.hide();
 | 
			
		||||
                                  }
 | 
			
		||||
                              }));
 | 
			
		||||
        this.connect('app-drag-end',
 | 
			
		||||
                              Lang.bind(this, function () {
 | 
			
		||||
                                  if (this.searchActive) {
 | 
			
		||||
                                      this._dash.hide();
 | 
			
		||||
                                      this._thumbnailsBox.hide();
 | 
			
		||||
                                  } else if (this.appsActive) {
 | 
			
		||||
                                      this._thumbnailsBox.hide();
 | 
			
		||||
                                  }
 | 
			
		||||
                              }));
 | 
			
		||||
 | 
			
		||||
        Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout));
 | 
			
		||||
        this._relayout();
 | 
			
		||||
    },
 | 
			
		||||
@@ -486,55 +562,41 @@ const Overview = new Lang.Class({
 | 
			
		||||
        this.hide();
 | 
			
		||||
 | 
			
		||||
        let primary = Main.layoutManager.primaryMonitor;
 | 
			
		||||
        let rtl = (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL);
 | 
			
		||||
 | 
			
		||||
        let contentY = Main.panel.actor.height;
 | 
			
		||||
        let contentHeight = primary.height - contentY - Main.messageTray.actor.height;
 | 
			
		||||
 | 
			
		||||
        this._group.set_position(primary.x, primary.y);
 | 
			
		||||
        this._group.set_size(primary.width, primary.height);
 | 
			
		||||
        this._overview.set_position(primary.x, primary.y);
 | 
			
		||||
        this._overview.set_size(primary.width, primary.height);
 | 
			
		||||
 | 
			
		||||
        this._coverPane.set_position(0, contentY);
 | 
			
		||||
        this._coverPane.set_size(primary.width, contentHeight);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
        let searchWidth = this._searchEntry.get_width();
 | 
			
		||||
        let searchHeight = this._searchEntry.get_height();
 | 
			
		||||
        let searchX = (primary.width - searchWidth) / 2;
 | 
			
		||||
        let searchY = contentY + this._spacing;
 | 
			
		||||
    _onRestacked: function() {
 | 
			
		||||
        let stack = global.get_window_actors();
 | 
			
		||||
        let stackIndices = {};
 | 
			
		||||
 | 
			
		||||
        let dashWidth = DASH_MAX_WIDTH;
 | 
			
		||||
        let dashY = searchY + searchHeight + this._spacing;
 | 
			
		||||
        let dashX;
 | 
			
		||||
        if (rtl) {
 | 
			
		||||
            this._dash.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
 | 
			
		||||
            dashX = primary.width;
 | 
			
		||||
        } else {
 | 
			
		||||
            dashX = 0;
 | 
			
		||||
        for (let i = 0; i < stack.length; i++) {
 | 
			
		||||
            // Use the stable sequence for an integer to use as a hash key
 | 
			
		||||
            stackIndices[stack[i].get_meta_window().get_stable_sequence()] = i;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let viewX = rtl ? 0 : dashWidth + this._spacing;
 | 
			
		||||
        let viewY = searchY + searchHeight + this._spacing;
 | 
			
		||||
        let viewWidth = primary.width - dashWidth - this._spacing;
 | 
			
		||||
        let viewHeight = contentHeight - this._spacing - viewY;
 | 
			
		||||
 | 
			
		||||
        this._searchEntry.set_position(searchX, searchY);
 | 
			
		||||
        this._dash.actor.set_position(dashX, dashY);
 | 
			
		||||
        this._viewSelector.actor.set_position(viewX, viewY);
 | 
			
		||||
        this._viewSelector.actor.set_size(viewWidth, viewHeight);
 | 
			
		||||
        this.emit('sync-window-stacking', stackIndices);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    //// Public methods ////
 | 
			
		||||
 | 
			
		||||
    beginItemDrag: function(source) {
 | 
			
		||||
        this.emit('item-drag-begin');
 | 
			
		||||
    beginAppDrag: function(source) {
 | 
			
		||||
        this.emit('app-drag-begin');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    cancelledItemDrag: function(source) {
 | 
			
		||||
        this.emit('item-drag-cancelled');
 | 
			
		||||
    cancelledAppDrag: function(source) {
 | 
			
		||||
        this.emit('app-drag-cancelled');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    endItemDrag: function(source) {
 | 
			
		||||
        this.emit('item-drag-end');
 | 
			
		||||
    endAppDrag: function(source) {
 | 
			
		||||
        this.emit('app-drag-end');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    beginWindowDrag: function(source) {
 | 
			
		||||
@@ -558,13 +620,13 @@ const Overview = new Lang.Class({
 | 
			
		||||
        if (this._shown)
 | 
			
		||||
            return;
 | 
			
		||||
        // Do this manually instead of using _syncInputMode, to handle failure
 | 
			
		||||
        if (!Main.pushModal(this._group))
 | 
			
		||||
        if (!Main.pushModal(this._overview))
 | 
			
		||||
            return;
 | 
			
		||||
        this._modal = true;
 | 
			
		||||
        this._animateVisible();
 | 
			
		||||
        this._shown = true;
 | 
			
		||||
 | 
			
		||||
        this._buttonPressId = this._group.connect('button-press-event',
 | 
			
		||||
        this._buttonPressId = this._overview.connect('button-press-event',
 | 
			
		||||
            Lang.bind(this, this._onButtonPress));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -608,12 +670,12 @@ const Overview = new Lang.Class({
 | 
			
		||||
        // Disable unredirection while in the overview
 | 
			
		||||
        Meta.disable_unredirect_for_screen(global.screen);
 | 
			
		||||
        global.window_group.hide();
 | 
			
		||||
        this._group.show();
 | 
			
		||||
        this._overview.show();
 | 
			
		||||
        this._background.show();
 | 
			
		||||
        this._viewSelector.show();
 | 
			
		||||
 | 
			
		||||
        this._group.opacity = 0;
 | 
			
		||||
        Tweener.addTween(this._group,
 | 
			
		||||
        this._overview.opacity = 0;
 | 
			
		||||
        Tweener.addTween(this._overview,
 | 
			
		||||
                         { opacity: 255,
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                           time: ANIMATION_TIME,
 | 
			
		||||
@@ -667,7 +729,7 @@ const Overview = new Lang.Class({
 | 
			
		||||
        this._syncInputMode();
 | 
			
		||||
 | 
			
		||||
        if (this._buttonPressId > 0)
 | 
			
		||||
            this._group.disconnect(this._buttonPressId);
 | 
			
		||||
            this._overview.disconnect(this._buttonPressId);
 | 
			
		||||
        this._buttonPressId = 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -709,20 +771,20 @@ const Overview = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        if (this._shown) {
 | 
			
		||||
            if (!this._modal) {
 | 
			
		||||
                if (Main.pushModal(this._group))
 | 
			
		||||
                if (Main.pushModal(this._overview))
 | 
			
		||||
                    this._modal = true;
 | 
			
		||||
                else
 | 
			
		||||
                    this.hide();
 | 
			
		||||
            }
 | 
			
		||||
        } else if (this._shownTemporarily) {
 | 
			
		||||
            if (this._modal) {
 | 
			
		||||
                Main.popModal(this._group);
 | 
			
		||||
                Main.popModal(this._overview);
 | 
			
		||||
                this._modal = false;
 | 
			
		||||
            }
 | 
			
		||||
            global.stage_input_mode = Shell.StageInputMode.FULLSCREEN;
 | 
			
		||||
        } else {
 | 
			
		||||
            if (this._modal) {
 | 
			
		||||
                Main.popModal(this._group);
 | 
			
		||||
                Main.popModal(this._overview);
 | 
			
		||||
                this._modal = false;
 | 
			
		||||
            }
 | 
			
		||||
            else if (global.stage_input_mode == Shell.StageInputMode.FULLSCREEN)
 | 
			
		||||
@@ -740,7 +802,7 @@ const Overview = new Lang.Class({
 | 
			
		||||
        this._viewSelector.zoomFromOverview();
 | 
			
		||||
 | 
			
		||||
        // Make other elements fade out.
 | 
			
		||||
        Tweener.addTween(this._group,
 | 
			
		||||
        Tweener.addTween(this._overview,
 | 
			
		||||
                         { opacity: 0,
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                           time: ANIMATION_TIME,
 | 
			
		||||
@@ -782,7 +844,7 @@ const Overview = new Lang.Class({
 | 
			
		||||
        this._viewSelector.hide();
 | 
			
		||||
        this._desktopFade.hide();
 | 
			
		||||
        this._background.hide();
 | 
			
		||||
        this._group.hide();
 | 
			
		||||
        this._overview.hide();
 | 
			
		||||
 | 
			
		||||
        this.visible = false;
 | 
			
		||||
        this.animationInProgress = false;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										121
									
								
								js/ui/panel.js
									
									
									
									
									
								
							
							
						
						
									
										121
									
								
								js/ui/panel.js
									
									
									
									
									
								
							@@ -15,6 +15,7 @@ const Signals = imports.signals;
 | 
			
		||||
const Atk = imports.gi.Atk;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const CenterLayout = imports.ui.centerLayout;
 | 
			
		||||
const Config = imports.misc.config;
 | 
			
		||||
const CtrlAltTab = imports.ui.ctrlAltTab;
 | 
			
		||||
const DND = imports.ui.dnd;
 | 
			
		||||
@@ -931,12 +932,47 @@ try {
 | 
			
		||||
    log('NMApplet is not supported. It is possible that your NetworkManager version is too old');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const PanelLayout = new Lang.Class({
 | 
			
		||||
    Name: 'PanelLayout',
 | 
			
		||||
    Extends: CenterLayout.CenterLayout,
 | 
			
		||||
 | 
			
		||||
    vfunc_allocate: function(container, box, flags) {
 | 
			
		||||
        this.parent(container, box, flags);
 | 
			
		||||
 | 
			
		||||
        let availWidth = box.x2 - box.x1;
 | 
			
		||||
        let availHeight = box.y2 - box.y1;
 | 
			
		||||
 | 
			
		||||
        let [left, center, right, leftCorner, rightCorner] = container.get_children();
 | 
			
		||||
        let childBox = new Clutter.ActorBox();
 | 
			
		||||
 | 
			
		||||
        let cornerMinWidth, cornerMinHeight;
 | 
			
		||||
        let cornerWidth, cornerHeight;
 | 
			
		||||
 | 
			
		||||
        [cornerMinWidth, cornerWidth] = leftCorner.get_preferred_width(-1);
 | 
			
		||||
        [cornerMinHeight, cornerHeight] = leftCorner.get_preferred_height(-1);
 | 
			
		||||
        childBox.x1 = 0;
 | 
			
		||||
        childBox.x2 = cornerWidth;
 | 
			
		||||
        childBox.y1 = availHeight;
 | 
			
		||||
        childBox.y2 = availHeight + cornerHeight;
 | 
			
		||||
        leftCorner.allocate(childBox, flags);
 | 
			
		||||
 | 
			
		||||
        [cornerMinWidth, cornerWidth] = rightCorner.get_preferred_width(-1);
 | 
			
		||||
        [cornerMinHeight, cornerHeight] = rightCorner.get_preferred_height(-1);
 | 
			
		||||
        childBox.x1 = availWidth - cornerWidth;
 | 
			
		||||
        childBox.x2 = availWidth;
 | 
			
		||||
        childBox.y1 = availHeight;
 | 
			
		||||
        childBox.y2 = availHeight + cornerHeight;
 | 
			
		||||
        rightCorner.allocate(childBox, flags);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const Panel = new Lang.Class({
 | 
			
		||||
    Name: 'Panel',
 | 
			
		||||
 | 
			
		||||
    _init : function() {
 | 
			
		||||
        this.actor = new Shell.GenericContainer({ name: 'panel',
 | 
			
		||||
                                                  reactive: true });
 | 
			
		||||
        this.actor = new St.Widget({ name: 'panel',
 | 
			
		||||
                                     reactive: true,
 | 
			
		||||
                                     layoutManager: new PanelLayout() });
 | 
			
		||||
        this.actor._delegate = this;
 | 
			
		||||
 | 
			
		||||
        this.statusArea = {};
 | 
			
		||||
@@ -961,7 +997,6 @@ const Panel = new Lang.Class({
 | 
			
		||||
            this._leftCorner = new PanelCorner(this._rightBox, St.Side.LEFT);
 | 
			
		||||
        else
 | 
			
		||||
            this._leftCorner = new PanelCorner(this._leftBox, St.Side.LEFT);
 | 
			
		||||
 | 
			
		||||
        this.actor.add_actor(this._leftCorner.actor);
 | 
			
		||||
 | 
			
		||||
        if (this.actor.get_text_direction() == Clutter.TextDirection.RTL)
 | 
			
		||||
@@ -970,9 +1005,6 @@ const Panel = new Lang.Class({
 | 
			
		||||
            this._rightCorner = new PanelCorner(this._rightBox, St.Side.RIGHT);
 | 
			
		||||
        this.actor.add_actor(this._rightCorner.actor);
 | 
			
		||||
 | 
			
		||||
        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.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
 | 
			
		||||
 | 
			
		||||
        Main.layoutManager.panelBox.add(this.actor);
 | 
			
		||||
@@ -983,83 +1015,6 @@ const Panel = new Lang.Class({
 | 
			
		||||
        this._updatePanel();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getPreferredWidth: function(actor, forHeight, alloc) {
 | 
			
		||||
        alloc.min_size = -1;
 | 
			
		||||
        alloc.natural_size = Main.layoutManager.primaryMonitor.width;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getPreferredHeight: function(actor, forWidth, alloc) {
 | 
			
		||||
        // We don't need to implement this; it's forced by the CSS
 | 
			
		||||
        alloc.min_size = -1;
 | 
			
		||||
        alloc.natural_size = -1;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _allocate: function(actor, box, flags) {
 | 
			
		||||
        let allocWidth = box.x2 - box.x1;
 | 
			
		||||
        let allocHeight = box.y2 - box.y1;
 | 
			
		||||
 | 
			
		||||
        let [leftMinWidth, leftNaturalWidth] = this._leftBox.get_preferred_width(-1);
 | 
			
		||||
        let [centerMinWidth, centerNaturalWidth] = this._centerBox.get_preferred_width(-1);
 | 
			
		||||
        let [rightMinWidth, rightNaturalWidth] = this._rightBox.get_preferred_width(-1);
 | 
			
		||||
 | 
			
		||||
        let sideWidth, centerWidth;
 | 
			
		||||
        centerWidth = centerNaturalWidth;
 | 
			
		||||
        sideWidth = (allocWidth - centerWidth) / 2;
 | 
			
		||||
 | 
			
		||||
        let childBox = new Clutter.ActorBox();
 | 
			
		||||
 | 
			
		||||
        childBox.y1 = 0;
 | 
			
		||||
        childBox.y2 = allocHeight;
 | 
			
		||||
        if (this.actor.get_text_direction() == Clutter.TextDirection.RTL) {
 | 
			
		||||
            childBox.x1 = allocWidth - Math.min(Math.floor(sideWidth),
 | 
			
		||||
                                                leftNaturalWidth);
 | 
			
		||||
            childBox.x2 = allocWidth;
 | 
			
		||||
        } else {
 | 
			
		||||
            childBox.x1 = 0;
 | 
			
		||||
            childBox.x2 = Math.min(Math.floor(sideWidth),
 | 
			
		||||
                                   leftNaturalWidth);
 | 
			
		||||
        }
 | 
			
		||||
        this._leftBox.allocate(childBox, flags);
 | 
			
		||||
 | 
			
		||||
        childBox.x1 = Math.ceil(sideWidth);
 | 
			
		||||
        childBox.y1 = 0;
 | 
			
		||||
        childBox.x2 = childBox.x1 + centerWidth;
 | 
			
		||||
        childBox.y2 = allocHeight;
 | 
			
		||||
        this._centerBox.allocate(childBox, flags);
 | 
			
		||||
 | 
			
		||||
        childBox.y1 = 0;
 | 
			
		||||
        childBox.y2 = allocHeight;
 | 
			
		||||
        if (this.actor.get_text_direction() == Clutter.TextDirection.RTL) {
 | 
			
		||||
            childBox.x1 = 0;
 | 
			
		||||
            childBox.x2 = Math.min(Math.floor(sideWidth),
 | 
			
		||||
                                   rightNaturalWidth);
 | 
			
		||||
        } else {
 | 
			
		||||
            childBox.x1 = allocWidth - Math.min(Math.floor(sideWidth),
 | 
			
		||||
                                                rightNaturalWidth);
 | 
			
		||||
            childBox.x2 = allocWidth;
 | 
			
		||||
        }
 | 
			
		||||
        this._rightBox.allocate(childBox, flags);
 | 
			
		||||
 | 
			
		||||
        let cornerMinWidth, cornerMinHeight;
 | 
			
		||||
        let cornerWidth, cornerHeight;
 | 
			
		||||
 | 
			
		||||
        [cornerMinWidth, cornerWidth] = this._leftCorner.actor.get_preferred_width(-1);
 | 
			
		||||
        [cornerMinHeight, cornerHeight] = this._leftCorner.actor.get_preferred_height(-1);
 | 
			
		||||
        childBox.x1 = 0;
 | 
			
		||||
        childBox.x2 = cornerWidth;
 | 
			
		||||
        childBox.y1 = allocHeight;
 | 
			
		||||
        childBox.y2 = allocHeight + cornerHeight;
 | 
			
		||||
        this._leftCorner.actor.allocate(childBox, flags);
 | 
			
		||||
 | 
			
		||||
        [cornerMinWidth, cornerWidth] = this._rightCorner.actor.get_preferred_width(-1);
 | 
			
		||||
        [cornerMinHeight, cornerHeight] = this._rightCorner.actor.get_preferred_height(-1);
 | 
			
		||||
        childBox.x1 = allocWidth - cornerWidth;
 | 
			
		||||
        childBox.x2 = allocWidth;
 | 
			
		||||
        childBox.y1 = allocHeight;
 | 
			
		||||
        childBox.y2 = allocHeight + cornerHeight;
 | 
			
		||||
        this._rightCorner.actor.allocate(childBox, flags);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onButtonPress: function(actor, event) {
 | 
			
		||||
        if (event.get_source() != actor)
 | 
			
		||||
            return false;
 | 
			
		||||
 
 | 
			
		||||
@@ -404,9 +404,17 @@ const PopupSeparatorMenuItem = new Lang.Class({
 | 
			
		||||
        this.parent({ reactive: false,
 | 
			
		||||
                      can_focus: false});
 | 
			
		||||
 | 
			
		||||
        this._drawingArea = new St.DrawingArea({ style_class: 'popup-separator-menu-item' });
 | 
			
		||||
        this.addActor(this._drawingArea, { span: -1, expand: true });
 | 
			
		||||
        this._drawingArea.connect('repaint', Lang.bind(this, this._onRepaint));
 | 
			
		||||
        this._separator = new HorzSeparator({ style_class: 'popup-separator-menu-item' });
 | 
			
		||||
        this.addActor(this._separator.actor, { span: -1, expand: true });
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const HorzSeparator = new Lang.Class({
 | 
			
		||||
    Name: 'HorzSeparator',
 | 
			
		||||
 | 
			
		||||
    _init: function (params) {
 | 
			
		||||
        this.actor = new St.DrawingArea(params);
 | 
			
		||||
        this.actor.connect('repaint', Lang.bind(this, this._onRepaint));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onRepaint: function(area) {
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,9 @@ const SearchProviderIface = <interface name="org.gnome.Shell.SearchProvider">
 | 
			
		||||
<method name="ActivateResult">
 | 
			
		||||
    <arg type="s" direction="in" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="LaunchSearch">
 | 
			
		||||
    <arg type="as" direction="in" />
 | 
			
		||||
</method>
 | 
			
		||||
</interface>;
 | 
			
		||||
 | 
			
		||||
var SearchProviderProxy = Gio.DBusProxy.makeProxyWrapper(SearchProviderIface);
 | 
			
		||||
@@ -112,6 +115,7 @@ const RemoteSearchProvider = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.parent(title.toUpperCase());
 | 
			
		||||
        this._cancellable = new Gio.Cancellable();
 | 
			
		||||
        this.icon = icon;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    createIcon: function(size, meta) {
 | 
			
		||||
@@ -126,9 +130,7 @@ const RemoteSearchProvider = new Lang.Class({
 | 
			
		||||
                                              width, height, rowStride, size);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Ugh, but we want to fall back to something ...
 | 
			
		||||
        return new St.Icon({ icon_name: 'text-x-generic',
 | 
			
		||||
                             icon_size: size });
 | 
			
		||||
        return null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getResultsFinished: function(results, error) {
 | 
			
		||||
@@ -196,6 +198,10 @@ const RemoteSearchProvider = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    activateResult: function(id) {
 | 
			
		||||
        this._proxy.ActivateResultRemote(id);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    launchSearch: function(terms) {
 | 
			
		||||
        this._proxy.LaunchSearchRemote(terms);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -163,6 +163,16 @@ const SearchProvider = new Lang.Class({
 | 
			
		||||
     */
 | 
			
		||||
    activateResult: function(id) {
 | 
			
		||||
        throw new Error('Not implemented');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * launchSearch:
 | 
			
		||||
     * @terms: Current search terms
 | 
			
		||||
     *
 | 
			
		||||
     * Called when the user clicks the provider icon.
 | 
			
		||||
     */
 | 
			
		||||
    launchSearch: function(terms) {
 | 
			
		||||
        throw new Error('Not implemented');
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(SearchProvider.prototype);
 | 
			
		||||
@@ -261,5 +271,11 @@ const SearchSystem = new Lang.Class({
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    launchSearch: function(provider) {
 | 
			
		||||
        let terms = this.getTerms();
 | 
			
		||||
        provider.launchSearch(terms);
 | 
			
		||||
        Main.overview.toggle();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(SearchSystem.prototype);
 | 
			
		||||
 
 | 
			
		||||
@@ -4,25 +4,191 @@ const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Atk = imports.gi.Atk;
 | 
			
		||||
 | 
			
		||||
const AppDisplay = imports.ui.appDisplay;
 | 
			
		||||
const DND = imports.ui.dnd;
 | 
			
		||||
const IconGrid = imports.ui.iconGrid;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Overview = imports.ui.overview;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
const Search = imports.ui.search;
 | 
			
		||||
 | 
			
		||||
const MAX_SEARCH_RESULTS_ROWS = 1;
 | 
			
		||||
const MAX_SEARCH_RESULTS_ROWS = 3;
 | 
			
		||||
const MAX_GRID_SEARCH_RESULTS_ROWS = 1;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const SearchResult = new Lang.Class({
 | 
			
		||||
    Name: 'SearchResult',
 | 
			
		||||
const ListSearchResult = new Lang.Class({
 | 
			
		||||
    Name: 'ListSearchResult',
 | 
			
		||||
 | 
			
		||||
    ICON_SIZE: 64,
 | 
			
		||||
 | 
			
		||||
    _init: function(provider, metaInfo, terms) {
 | 
			
		||||
        this.provider = provider;
 | 
			
		||||
        this.metaInfo = metaInfo;
 | 
			
		||||
        this.actor = new St.Button({ style_class: 'search-result',
 | 
			
		||||
                                     reactive: true,
 | 
			
		||||
                                     can_focus: true,
 | 
			
		||||
                                     track_hover: true,
 | 
			
		||||
                                     x_align: St.Align.START,
 | 
			
		||||
                                     x_fill: true,
 | 
			
		||||
                                     y_fill: true });
 | 
			
		||||
        this.actor._delegate = this;
 | 
			
		||||
 | 
			
		||||
        let content = new St.BoxLayout({ style_class: 'search-result-content',
 | 
			
		||||
                                         vertical: false });
 | 
			
		||||
        this._content = content;
 | 
			
		||||
        this.actor.set_child(content);
 | 
			
		||||
 | 
			
		||||
        // An icon for, or thumbnail of, content
 | 
			
		||||
        let icon = this.metaInfo['createIcon'](this.ICON_SIZE);
 | 
			
		||||
        if (icon) {
 | 
			
		||||
            let iconBin = new St.Bin({ style_class: 'search-result-icon',
 | 
			
		||||
                                    child: icon });
 | 
			
		||||
            content.add(iconBin);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let details = new St.BoxLayout({ style_class: 'search-result-details',
 | 
			
		||||
                                         vertical: true });
 | 
			
		||||
        content.add(details, { x_fill: true,
 | 
			
		||||
                               y_fill: false,
 | 
			
		||||
                               x_align: St.Align.START,
 | 
			
		||||
                               y_align: St.Align.MIDDLE });
 | 
			
		||||
 | 
			
		||||
        let title = new St.Label({ style_class: 'search-result-details-title',
 | 
			
		||||
                                   text: this.metaInfo['name'] })
 | 
			
		||||
        details.add(title, { x_fill: true,
 | 
			
		||||
                             y_fill: false,
 | 
			
		||||
                             x_align: St.Align.START,
 | 
			
		||||
                             y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        // TODO: could highlight terms in the description here, or should
 | 
			
		||||
        // providers provide what should be highlighted?
 | 
			
		||||
        if (this.metaInfo['description']) {
 | 
			
		||||
            let description = new St.Label({ style_class: 'search-result-details-description',
 | 
			
		||||
                                             text: this.metaInfo['description'] });
 | 
			
		||||
            details.add(description, { x_fill: false,
 | 
			
		||||
                                       y_fill: true,
 | 
			
		||||
                                       x_align: St.Align.START,
 | 
			
		||||
                                       y_align: St.Align.END });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('clicked', Lang.bind(this, this._onResultClicked));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setSelected: function(selected) {
 | 
			
		||||
        if (selected)
 | 
			
		||||
            this.actor.add_style_pseudo_class('selected');
 | 
			
		||||
        else
 | 
			
		||||
            this.actor.remove_style_pseudo_class('selected');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    activate: function() {
 | 
			
		||||
        this.provider.activateResult(this.metaInfo.id);
 | 
			
		||||
        Main.overview.toggle();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onResultClicked: function(actor) {
 | 
			
		||||
        this.activate();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const ListSearchResults = new Lang.Class({
 | 
			
		||||
    Name: 'ListSearchResults',
 | 
			
		||||
    Extends: Search.SearchResultDisplay,
 | 
			
		||||
 | 
			
		||||
    _init: function(provider) {
 | 
			
		||||
        this.parent(provider);
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.BoxLayout({ style_class: 'results-list',
 | 
			
		||||
                                        vertical: true });
 | 
			
		||||
        this.actor.connect('notify::width', Lang.bind(this, function() {
 | 
			
		||||
            Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
 | 
			
		||||
                let results = this.getResultsForDisplay();
 | 
			
		||||
                if (results.length == 0)
 | 
			
		||||
                    return;
 | 
			
		||||
 | 
			
		||||
                provider.getResultMetas(results, Lang.bind(this, this.renderResults));
 | 
			
		||||
            }));
 | 
			
		||||
        }));
 | 
			
		||||
        this._notDisplayedResult = [];
 | 
			
		||||
        this._terms = [];
 | 
			
		||||
        this._pendingClear = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getResultsForDisplay: function() {
 | 
			
		||||
        let alreadyVisible = this._pendingClear ? 0 : this.getVisibleResultCount();
 | 
			
		||||
        let canDisplay = MAX_SEARCH_RESULTS_ROWS - alreadyVisible;
 | 
			
		||||
 | 
			
		||||
        let numResults = Math.min(this._notDisplayedResult.length, canDisplay);
 | 
			
		||||
 | 
			
		||||
        return this._notDisplayedResult.splice(0, numResults);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getVisibleResultCount: function() {
 | 
			
		||||
        return this.actor.get_n_children();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hasMoreResults: function() {
 | 
			
		||||
        return this._notDisplayedResult.length > 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setResults: function(results, terms) {
 | 
			
		||||
        // copy the lists
 | 
			
		||||
        this._notDisplayedResult = results.slice(0);
 | 
			
		||||
        this._terms = terms.slice(0);
 | 
			
		||||
        this._pendingClear = true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    renderResults: function(metas) {
 | 
			
		||||
        for (let i = 0; i < metas.length; i++) {
 | 
			
		||||
            let display = new ListSearchResult(this.provider, metas[i], this._terms);
 | 
			
		||||
            this.addItem(display.actor);
 | 
			
		||||
 | 
			
		||||
            display.actor.connect('key-focus-in', Lang.bind(this, this._onFocusedProviderChanged));
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    clear: function () {
 | 
			
		||||
        this.actor.destroy_all_children();
 | 
			
		||||
        this._pendingClear = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getFirstResult: function() {
 | 
			
		||||
        if (this.getVisibleResultCount() > 0)
 | 
			
		||||
            return this.getItemAtIndex(0)._delegate;
 | 
			
		||||
        else
 | 
			
		||||
            return null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addItem: function(actor, index) {
 | 
			
		||||
        if (index !== undefined)
 | 
			
		||||
            this.actor.insert_child_at_index(actor, index);
 | 
			
		||||
        else
 | 
			
		||||
            this.actor.add_actor(actor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getItemAtIndex: function(index) {
 | 
			
		||||
        return this.actor.get_child_at_index(index);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onFocusedProviderChanged: function() {
 | 
			
		||||
        this.emit('focused-provider-changed');
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(ListSearchResults.prototype);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const GridSearchResult = new Lang.Class({
 | 
			
		||||
    Name: 'GridSearchResult',
 | 
			
		||||
 | 
			
		||||
    _init: function(provider, metaInfo, terms) {
 | 
			
		||||
        this.provider = provider;
 | 
			
		||||
        this.metaInfo = metaInfo;
 | 
			
		||||
        this.actor = new St.Button({ style_class: 'grid-search-result',
 | 
			
		||||
                                     reactive: true,
 | 
			
		||||
                                     x_align: St.Align.START,
 | 
			
		||||
                                     y_fill: true });
 | 
			
		||||
@@ -31,7 +197,7 @@ const SearchResult = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        let content = provider.createResultActor(metaInfo, terms);
 | 
			
		||||
        if (content == null) {
 | 
			
		||||
            content = new St.Bin({ style_class: 'search-result-content',
 | 
			
		||||
            content = new St.Bin({ style_class: 'grid-search-result-content',
 | 
			
		||||
                                   reactive: true,
 | 
			
		||||
                                   can_focus: true,
 | 
			
		||||
                                   track_hover: true,
 | 
			
		||||
@@ -45,7 +211,7 @@ const SearchResult = new Lang.Class({
 | 
			
		||||
            if (content._delegate && content._delegate.getDragActorSource)
 | 
			
		||||
                this._dragActorSource = content._delegate.getDragActorSource();
 | 
			
		||||
        }
 | 
			
		||||
        this._content = content;
 | 
			
		||||
        this.content = content;
 | 
			
		||||
        this.actor.set_child(content);
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('clicked', Lang.bind(this, this._onResultClicked));
 | 
			
		||||
@@ -53,23 +219,23 @@ const SearchResult = new Lang.Class({
 | 
			
		||||
        let draggable = DND.makeDraggable(this.actor);
 | 
			
		||||
        draggable.connect('drag-begin',
 | 
			
		||||
                          Lang.bind(this, function() {
 | 
			
		||||
                              Main.overview.beginItemDrag(this);
 | 
			
		||||
                              Main.overview.beginAppDrag(this);
 | 
			
		||||
                          }));
 | 
			
		||||
        draggable.connect('drag-cancelled',
 | 
			
		||||
                          Lang.bind(this, function() {
 | 
			
		||||
                              Main.overview.cancelledItemDrag(this);
 | 
			
		||||
                              Main.overview.cancelledAppDrag(this);
 | 
			
		||||
                          }));
 | 
			
		||||
        draggable.connect('drag-end',
 | 
			
		||||
                          Lang.bind(this, function() {
 | 
			
		||||
                              Main.overview.endItemDrag(this);
 | 
			
		||||
                              Main.overview.endAppDrag(this);
 | 
			
		||||
                          }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setSelected: function(selected) {
 | 
			
		||||
        if (selected)
 | 
			
		||||
            this._content.add_style_pseudo_class('selected');
 | 
			
		||||
            this.content.add_style_pseudo_class('selected');
 | 
			
		||||
        else
 | 
			
		||||
            this._content.remove_style_pseudo_class('selected');
 | 
			
		||||
            this.content.remove_style_pseudo_class('selected');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    activate: function() {
 | 
			
		||||
@@ -85,7 +251,7 @@ const SearchResult = new Lang.Class({
 | 
			
		||||
        if (this._dragActorSource)
 | 
			
		||||
            return this._dragActorSource;
 | 
			
		||||
        // not exactly right, but alignment problems are hard to notice
 | 
			
		||||
        return this._content;
 | 
			
		||||
        return this.content;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getDragActor: function(stageX, stageY) {
 | 
			
		||||
@@ -108,11 +274,11 @@ const GridSearchResults = new Lang.Class({
 | 
			
		||||
    _init: function(provider, grid) {
 | 
			
		||||
        this.parent(provider);
 | 
			
		||||
 | 
			
		||||
        this._grid = grid || new IconGrid.IconGrid({ rowLimit: MAX_SEARCH_RESULTS_ROWS,
 | 
			
		||||
        this._grid = grid || new IconGrid.IconGrid({ rowLimit: MAX_GRID_SEARCH_RESULTS_ROWS,
 | 
			
		||||
                                                     xAlign: St.Align.START });
 | 
			
		||||
        this.actor = new St.Bin({ x_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.Bin({ x_align: St.Align.MIDDLE });
 | 
			
		||||
        this.actor.set_child(this._grid.actor);
 | 
			
		||||
 | 
			
		||||
        this._width = 0;
 | 
			
		||||
        this.actor.connect('notify::width', Lang.bind(this, function() {
 | 
			
		||||
            this._width = this.actor.width;
 | 
			
		||||
@@ -130,6 +296,7 @@ const GridSearchResults = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getResultsForDisplay: function() {
 | 
			
		||||
        this._grid.actor.ensure_style();
 | 
			
		||||
        let alreadyVisible = this._pendingClear ? 0 : this._grid.visibleItemsCount();
 | 
			
		||||
        let canDisplay = this._grid.childrenInRow(this._width) * this._grid.getRowLimit()
 | 
			
		||||
                         - alreadyVisible;
 | 
			
		||||
@@ -143,6 +310,10 @@ const GridSearchResults = new Lang.Class({
 | 
			
		||||
        return this._grid.visibleItemsCount();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hasMoreResults: function() {
 | 
			
		||||
        return this._notDisplayedResult.length > 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setResults: function(results, terms) {
 | 
			
		||||
        // copy the lists
 | 
			
		||||
        this._notDisplayedResult = results.slice(0);
 | 
			
		||||
@@ -152,8 +323,10 @@ const GridSearchResults = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    renderResults: function(metas) {
 | 
			
		||||
        for (let i = 0; i < metas.length; i++) {
 | 
			
		||||
            let display = new SearchResult(this.provider, metas[i], this._terms);
 | 
			
		||||
            let display = new GridSearchResult(this.provider, metas[i], this._terms);
 | 
			
		||||
            this._grid.addItem(display.actor);
 | 
			
		||||
 | 
			
		||||
            display.content.connect('key-focus-in', Lang.bind(this, this._onFocusedProviderChanged));
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -167,11 +340,17 @@ const GridSearchResults = new Lang.Class({
 | 
			
		||||
            return this._grid.getItemAtIndex(0)._delegate;
 | 
			
		||||
        else
 | 
			
		||||
            return null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onFocusedProviderChanged: function() {
 | 
			
		||||
        this.emit('focused-provider-changed');
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(GridSearchResults.prototype);
 | 
			
		||||
 | 
			
		||||
const SearchResults = new Lang.Class({
 | 
			
		||||
    Name: 'SearchResults',
 | 
			
		||||
 | 
			
		||||
const SearchDisplay = new Lang.Class({
 | 
			
		||||
    Name: 'SearchDisplay',
 | 
			
		||||
 | 
			
		||||
    _init: function(searchSystem) {
 | 
			
		||||
        this._searchSystem = searchSystem;
 | 
			
		||||
@@ -222,19 +401,43 @@ const SearchResults = new Lang.Class({
 | 
			
		||||
    createProviderMeta: function(provider) {
 | 
			
		||||
        let providerBox = new St.BoxLayout({ style_class: 'search-section',
 | 
			
		||||
                                             vertical: true });
 | 
			
		||||
        let title = new St.Label({ style_class: 'search-section-header',
 | 
			
		||||
                                   text: provider.title });
 | 
			
		||||
        providerBox.add(title, { x_fill: false, x_align: St.Align.START });
 | 
			
		||||
        let providerBoxContent = new St.BoxLayout({ style_class: 'search-section-content' });
 | 
			
		||||
        let isAppsProvider = (provider instanceof AppDisplay.AppSearchProvider);
 | 
			
		||||
 | 
			
		||||
        let providerIcon;
 | 
			
		||||
        if (!isAppsProvider) {
 | 
			
		||||
            let separator = new PopupMenu.HorzSeparator({ style_class: 'search-section-separator' });
 | 
			
		||||
            providerBox.add(separator.actor, { expand: true });
 | 
			
		||||
 | 
			
		||||
            providerIcon = new ProviderIcon(provider);
 | 
			
		||||
            providerIcon.connect('launch-search', Lang.bind(this, function(providerIcon) {
 | 
			
		||||
                this._searchSystem.launchSearch(providerIcon.provider);
 | 
			
		||||
            }));
 | 
			
		||||
            providerBoxContent.add(providerIcon.actor, { x_fill: false,
 | 
			
		||||
                                                         y_fill: false,
 | 
			
		||||
                                                         x_align: St.Align.START,
 | 
			
		||||
                                                         y_align: St.Align.START });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let resultDisplayBin = new St.Bin({ style_class: 'search-section-results',
 | 
			
		||||
                                            x_fill: true,
 | 
			
		||||
                                            y_fill: true });
 | 
			
		||||
        providerBox.add(resultDisplayBin, { expand: true });
 | 
			
		||||
        let resultDisplay = new GridSearchResults(provider);
 | 
			
		||||
        providerBoxContent.add(resultDisplayBin, { expand: true });
 | 
			
		||||
 | 
			
		||||
        providerBox.add(providerBoxContent);
 | 
			
		||||
 | 
			
		||||
        let resultDisplay;
 | 
			
		||||
        if (isAppsProvider)
 | 
			
		||||
            resultDisplay = new GridSearchResults(provider);
 | 
			
		||||
        else
 | 
			
		||||
            resultDisplay = new ListSearchResults(provider);
 | 
			
		||||
 | 
			
		||||
        resultDisplay.connect('focused-provider-changed', Lang.bind(this, this._updateProviderIconCanFocus));
 | 
			
		||||
        resultDisplayBin.set_child(resultDisplay.actor);
 | 
			
		||||
 | 
			
		||||
        this._providerMeta.push({ provider: provider,
 | 
			
		||||
                                  actor: providerBox,
 | 
			
		||||
                                  icon: providerIcon,
 | 
			
		||||
                                  resultDisplay: resultDisplay });
 | 
			
		||||
        this._content.add(providerBox);
 | 
			
		||||
    },
 | 
			
		||||
@@ -255,6 +458,7 @@ const SearchResults = new Lang.Class({
 | 
			
		||||
            let meta = this._providerMeta[i];
 | 
			
		||||
            meta.resultDisplay.clear();
 | 
			
		||||
            meta.actor.hide();
 | 
			
		||||
            if (meta.icon) meta.icon.actor.can_focus = false;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -262,6 +466,7 @@ const SearchResults = new Lang.Class({
 | 
			
		||||
        let meta = this._metaForProvider(provider);
 | 
			
		||||
        meta.resultDisplay.clear();
 | 
			
		||||
        meta.actor.hide();
 | 
			
		||||
        if (meta.icon) meta.icon.actor.can_focus = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    reset: function() {
 | 
			
		||||
@@ -284,6 +489,11 @@ const SearchResults = new Lang.Class({
 | 
			
		||||
        return this._providerMeta[this._providers.indexOf(provider)];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _metaForProvidersExcept: function(provider) {
 | 
			
		||||
        let skip = this._providers.indexOf(provider);
 | 
			
		||||
        return this._providerMeta.filter(function(meta, index) { return index != skip }, this);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _maybeSetInitialSelection: function() {
 | 
			
		||||
        let newDefaultResult = null;
 | 
			
		||||
 | 
			
		||||
@@ -341,6 +551,9 @@ const SearchResults = new Lang.Class({
 | 
			
		||||
            meta.resultDisplay.setResults(providerResults, terms);
 | 
			
		||||
            let results = meta.resultDisplay.getResultsForDisplay();
 | 
			
		||||
 | 
			
		||||
            if (meta.icon)
 | 
			
		||||
                meta.icon.moreIcon.visible = meta.resultDisplay.hasMoreResults();
 | 
			
		||||
 | 
			
		||||
            provider.getResultMetas(results, Lang.bind(this, function(metas) {
 | 
			
		||||
                this._clearDisplayForProvider(provider);
 | 
			
		||||
                meta.actor.show();
 | 
			
		||||
@@ -374,6 +587,15 @@ const SearchResults = new Lang.Class({
 | 
			
		||||
            this._defaultResult.setSelected(highlight);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateProviderIconCanFocus: function(resultDisplay) {
 | 
			
		||||
        let meta = this._metaForProvider(resultDisplay.provider);
 | 
			
		||||
        if (meta.icon)
 | 
			
		||||
            meta.icon.actor.can_focus = true;
 | 
			
		||||
 | 
			
		||||
        let others = this._metaForProvidersExcept(resultDisplay.provider);
 | 
			
		||||
        others.forEach(function(meta) { if (meta.icon) meta.icon.actor.can_focus = false; });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    navigateFocus: function(direction) {
 | 
			
		||||
        let rtl = this.actor.get_text_direction() == Clutter.TextDirection.RTL;
 | 
			
		||||
        if (direction == Gtk.DirectionType.TAB_BACKWARD ||
 | 
			
		||||
@@ -393,3 +615,65 @@ const SearchResults = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const ProviderIcon = new Lang.Class({
 | 
			
		||||
    Name: 'ProviderIcon',
 | 
			
		||||
 | 
			
		||||
    PROVIDER_ICON_SIZE: 48,
 | 
			
		||||
 | 
			
		||||
    MORE_ICON_SIZE: 16,
 | 
			
		||||
 | 
			
		||||
    _init: function(provider) {
 | 
			
		||||
        this.provider = provider;
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.Button({ style_class: 'search-section-icon-bin',
 | 
			
		||||
                                     reactive: true,
 | 
			
		||||
                                     track_hover: true });
 | 
			
		||||
        this.actor.connect('clicked', Lang.bind(this, this._onIconClicked));
 | 
			
		||||
 | 
			
		||||
        this._content = new St.Widget({ layout_manager: new Clutter.BinLayout() });
 | 
			
		||||
        this.actor.set_child(this._content);
 | 
			
		||||
 | 
			
		||||
        let rtl = (this.actor.get_text_direction() == Clutter.TextDirection.RTL);
 | 
			
		||||
 | 
			
		||||
        this.moreIcon = new St.Icon({ style_class: 'search-section-icon-more',
 | 
			
		||||
                                      icon_size: this.MORE_ICON_SIZE,
 | 
			
		||||
                                      icon_name: 'list-add-symbolic',
 | 
			
		||||
                                      visible: false,
 | 
			
		||||
                                      x_align: rtl ? Clutter.ActorAlign.START : Clutter.ActorAlign.END,
 | 
			
		||||
                                      y_align: Clutter.ActorAlign.END,
 | 
			
		||||
                                      // HACK: without these, ClutterBinLayout
 | 
			
		||||
                                      // ignores alignment properties on the actor
 | 
			
		||||
                                      x_expand: true,
 | 
			
		||||
                                      y_expand: true });
 | 
			
		||||
 | 
			
		||||
        this._iconBin = new St.Bin({ style_class: 'search-section-icon',
 | 
			
		||||
                                     width: this.PROVIDER_ICON_SIZE,
 | 
			
		||||
                                     height: this.PROVIDER_ICON_SIZE });
 | 
			
		||||
 | 
			
		||||
        this._content.add_actor(this._iconBin);
 | 
			
		||||
        this._content.add_actor(this.moreIcon);
 | 
			
		||||
 | 
			
		||||
        this._createProviderIcon();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createProviderIcon: function() {
 | 
			
		||||
        let icon = new St.Icon({ icon_size: this.PROVIDER_ICON_SIZE });
 | 
			
		||||
 | 
			
		||||
        if (this.provider.icon)
 | 
			
		||||
            icon.gicon = this.provider.icon;
 | 
			
		||||
        else
 | 
			
		||||
            icon.icon_name = 'application-x-executable';
 | 
			
		||||
 | 
			
		||||
        this._iconBin.set_child(icon);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    activate: function() {
 | 
			
		||||
        this.emit('launch-search');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onIconClicked: function(actor) {
 | 
			
		||||
        this.activate();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(ProviderIcon.prototype);
 | 
			
		||||
 
 | 
			
		||||
@@ -52,7 +52,7 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._activePage = null;
 | 
			
		||||
 | 
			
		||||
        this.active = false;
 | 
			
		||||
        this.entryNonEmpty = false;
 | 
			
		||||
        this._searchPending = false;
 | 
			
		||||
        this._searchTimeoutId = 0;
 | 
			
		||||
 | 
			
		||||
@@ -90,7 +90,7 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
        this._appsPage = this._addPage(this._appDisplay.actor, null,
 | 
			
		||||
                                       _("Applications"), 'system-run-symbolic');
 | 
			
		||||
 | 
			
		||||
        this._searchResults = new SearchDisplay.SearchResults(this._searchSystem);
 | 
			
		||||
        this._searchResults = new SearchDisplay.SearchDisplay(this._searchSystem);
 | 
			
		||||
        this._searchPage = this._addPage(this._searchResults.actor, this._entry,
 | 
			
		||||
                                         _("Search"), 'edit-find-symbolic');
 | 
			
		||||
 | 
			
		||||
@@ -114,9 +114,6 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        global.focus_manager.add_group(this._searchResults.actor);
 | 
			
		||||
 | 
			
		||||
        Main.overview.connect('item-drag-begin',
 | 
			
		||||
                              Lang.bind(this, this._resetShowAppsButton));
 | 
			
		||||
 | 
			
		||||
        this._stageKeyPressId = 0;
 | 
			
		||||
        Main.overview.connect('showing', Lang.bind(this,
 | 
			
		||||
            function () {
 | 
			
		||||
@@ -133,17 +130,6 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
                }
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        // Public constraints which may be used to tie actors' height or
 | 
			
		||||
        // vertical position to the current tab's content; as the content's
 | 
			
		||||
        // height and position depend on the view selector's style properties
 | 
			
		||||
        // (e.g. font size, padding, spacing, ...) it would be extremely hard
 | 
			
		||||
        // and ugly to get these from the outside. While it would be possible
 | 
			
		||||
        // to use position and height properties directly, outside code would
 | 
			
		||||
        // need to ensure that the content is properly allocated before
 | 
			
		||||
        // accessing the properties.
 | 
			
		||||
        this.constrainHeight = new Clutter.BindConstraint({ source: this._pageArea,
 | 
			
		||||
                                                            coordinate: Clutter.BindCoordinate.HEIGHT });
 | 
			
		||||
 | 
			
		||||
        global.display.add_keybinding('toggle-application-view',
 | 
			
		||||
                                      new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
 | 
			
		||||
                                      Meta.KeyBindingFlags.NONE,
 | 
			
		||||
@@ -230,7 +216,9 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onShowAppsButtonToggled: function() {
 | 
			
		||||
        if (this.active)
 | 
			
		||||
        this.emit('apps-button-checked', this._showAppsButton.checked);
 | 
			
		||||
 | 
			
		||||
        if (this.entryNonEmpty)
 | 
			
		||||
            this.reset();
 | 
			
		||||
        else
 | 
			
		||||
            this._showPage(this._showAppsButton.checked ? this._appsPage
 | 
			
		||||
@@ -246,7 +234,7 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
        let symbol = event.get_key_symbol();
 | 
			
		||||
 | 
			
		||||
        if (symbol == Clutter.Escape) {
 | 
			
		||||
            if (this.active)
 | 
			
		||||
            if (this.entryNonEmpty)
 | 
			
		||||
                this.reset();
 | 
			
		||||
            else if (this._showAppsButton.checked)
 | 
			
		||||
                this._resetShowAppsButton();
 | 
			
		||||
@@ -254,9 +242,9 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
                Main.overview.hide();
 | 
			
		||||
            return true;
 | 
			
		||||
        } else if (Clutter.keysym_to_unicode(symbol) ||
 | 
			
		||||
                   (symbol == Clutter.BackSpace && this.active)) {
 | 
			
		||||
                   (symbol == Clutter.BackSpace && this.entryNonEmpty)) {
 | 
			
		||||
            this.startSearch(event);
 | 
			
		||||
        } else if (!this.active) {
 | 
			
		||||
        } else if (!this.entryNonEmpty) {
 | 
			
		||||
            if (symbol == Clutter.Tab || symbol == Clutter.Down) {
 | 
			
		||||
                this._activePage.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
 | 
			
		||||
                return true;
 | 
			
		||||
@@ -269,6 +257,7 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _searchCancelled: function() {
 | 
			
		||||
        this.emit('search-cancelled');
 | 
			
		||||
        this._showPage(this._showAppsButton.checked ? this._appsPage
 | 
			
		||||
                                                    : this._workspacesPage);
 | 
			
		||||
 | 
			
		||||
@@ -330,13 +319,14 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onTextChanged: function (se, prop) {
 | 
			
		||||
        let searchPreviouslyActive = this.active;
 | 
			
		||||
        this.active = this._entry.get_text() != '';
 | 
			
		||||
        this._searchPending = this.active && !searchPreviouslyActive;
 | 
			
		||||
        let searchPreviouslyActive = this.entryNonEmpty;
 | 
			
		||||
        this.entryNonEmpty = this._entry.get_text() != '';
 | 
			
		||||
        this._searchPending = this.entryNonEmpty && !searchPreviouslyActive;
 | 
			
		||||
        if (this._searchPending) {
 | 
			
		||||
            this._searchResults.startingSearch();
 | 
			
		||||
        }
 | 
			
		||||
        if (this.active) {
 | 
			
		||||
        if (this.entryNonEmpty) {
 | 
			
		||||
            this.emit('search-begin');
 | 
			
		||||
            this._entry.set_secondary_icon(this._activeIcon);
 | 
			
		||||
 | 
			
		||||
            if (this._iconClickedId == 0) {
 | 
			
		||||
@@ -353,7 +343,7 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
            this._entry.set_secondary_icon(this._inactiveIcon);
 | 
			
		||||
            this._searchCancelled();
 | 
			
		||||
        }
 | 
			
		||||
        if (!this.active) {
 | 
			
		||||
        if (!this.entryNonEmpty) {
 | 
			
		||||
            if (this._searchTimeoutId > 0) {
 | 
			
		||||
                Mainloop.source_remove(this._searchTimeoutId);
 | 
			
		||||
                this._searchTimeoutId = 0;
 | 
			
		||||
@@ -381,7 +371,7 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
            }
 | 
			
		||||
            this._searchResults.activateDefault();
 | 
			
		||||
            return true;
 | 
			
		||||
        } else if (this.active) {
 | 
			
		||||
        } else if (this.entryNonEmpty) {
 | 
			
		||||
            let arrowNext, nextDirection;
 | 
			
		||||
            if (entry.get_text_direction() == Clutter.TextDirection.RTL) {
 | 
			
		||||
                arrowNext = Clutter.Left;
 | 
			
		||||
 
 | 
			
		||||
@@ -1073,6 +1073,9 @@ const Workspace = new Lang.Class({
 | 
			
		||||
     *  ANIMATE - Indicates that we need animate changing position.
 | 
			
		||||
     */
 | 
			
		||||
    positionWindows: function(flags) {
 | 
			
		||||
        if (this.leavingOverview)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._positionWindowsFlags |= flags;
 | 
			
		||||
 | 
			
		||||
        if (this._positionWindowsId > 0)
 | 
			
		||||
 
 | 
			
		||||
@@ -493,6 +493,7 @@ const ThumbnailsBox = new Lang.Class({
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.actor = new Shell.GenericContainer({ reactive: true,
 | 
			
		||||
                                                  style_class: 'workspace-thumbnails',
 | 
			
		||||
                                                  can_focus: true,
 | 
			
		||||
                                                  request_mode: Clutter.RequestMode.WIDTH_FOR_HEIGHT });
 | 
			
		||||
        this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
 | 
			
		||||
        this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
 | 
			
		||||
@@ -526,6 +527,8 @@ const ThumbnailsBox = new Lang.Class({
 | 
			
		||||
        this._dropPlaceholder = new St.Bin({ style_class: 'placeholder' });
 | 
			
		||||
        this.actor.add_actor(this._dropPlaceholder);
 | 
			
		||||
 | 
			
		||||
        this.visible = true;
 | 
			
		||||
 | 
			
		||||
        this._targetScale = 0;
 | 
			
		||||
        this._scale = 0;
 | 
			
		||||
        this._pendingScaleUpdate = false;
 | 
			
		||||
@@ -541,12 +544,21 @@ const ThumbnailsBox = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('button-press-event', function() { return true; });
 | 
			
		||||
        this.actor.connect('button-release-event', Lang.bind(this, this._onButtonRelease));
 | 
			
		||||
        this.actor.connect('scroll-event',
 | 
			
		||||
                           Lang.bind(this, this._onScrollEvent));
 | 
			
		||||
        this.actor.connect('key-release-event',
 | 
			
		||||
                           Lang.bind(this, this._onKeyRelease));
 | 
			
		||||
 | 
			
		||||
        Main.overview.connect('item-drag-begin',
 | 
			
		||||
        Main.overview.connect('showing',
 | 
			
		||||
                              Lang.bind(this, this._createThumbnails));
 | 
			
		||||
        Main.overview.connect('hidden',
 | 
			
		||||
                              Lang.bind(this, this._destroyThumbnails));
 | 
			
		||||
 | 
			
		||||
        Main.overview.connect('app-drag-begin',
 | 
			
		||||
                              Lang.bind(this, this._onDragBegin));
 | 
			
		||||
        Main.overview.connect('item-drag-end',
 | 
			
		||||
        Main.overview.connect('app-drag-end',
 | 
			
		||||
                              Lang.bind(this, this._onDragEnd));
 | 
			
		||||
        Main.overview.connect('item-drag-cancelled',
 | 
			
		||||
        Main.overview.connect('app-drag-cancelled',
 | 
			
		||||
                              Lang.bind(this, this._onDragCancelled));
 | 
			
		||||
        Main.overview.connect('window-drag-begin',
 | 
			
		||||
                              Lang.bind(this, this._onDragBegin));
 | 
			
		||||
@@ -719,10 +731,16 @@ const ThumbnailsBox = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    show: function() {
 | 
			
		||||
    _createThumbnails: function() {
 | 
			
		||||
        this._switchWorkspaceNotifyId =
 | 
			
		||||
            global.window_manager.connect('switch-workspace',
 | 
			
		||||
                                          Lang.bind(this, this._activeWorkspaceChanged));
 | 
			
		||||
        this._nWorkspacesNotifyId =
 | 
			
		||||
            global.screen.connect('notify::n-workspaces',
 | 
			
		||||
                                  Lang.bind(this, this._workspacesChanged));
 | 
			
		||||
        this._syncStackingId =
 | 
			
		||||
            Main.overview.connect('sync-window-stacking',
 | 
			
		||||
                                  Lang.bind(this, this.syncStacking));
 | 
			
		||||
 | 
			
		||||
        this._targetScale = 0;
 | 
			
		||||
        this._scale = 0;
 | 
			
		||||
@@ -746,17 +764,86 @@ const ThumbnailsBox = new Lang.Class({
 | 
			
		||||
        this.addThumbnails(0, global.screen.n_workspaces);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hide: function() {
 | 
			
		||||
    _destroyThumbnails: function() {
 | 
			
		||||
        if (this._switchWorkspaceNotifyId > 0) {
 | 
			
		||||
            global.window_manager.disconnect(this._switchWorkspaceNotifyId);
 | 
			
		||||
            this._switchWorkspaceNotifyId = 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (this._nWorkspacesNotifyId > 0) {
 | 
			
		||||
            global.screen.disconnect(this._nWorkspacesNotifyId);
 | 
			
		||||
            this._nWorkspacesNotifyId = 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (this._syncStackingId > 0) {
 | 
			
		||||
            Main.overview.disconnect(this._syncStackingId);
 | 
			
		||||
            this._syncStackingId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (let w = 0; w < this._thumbnails.length; w++)
 | 
			
		||||
            this._thumbnails[w].destroy();
 | 
			
		||||
        this._thumbnails = [];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _computeThumbnailsX: function() {
 | 
			
		||||
        this._targetX = this.actor.get_x();
 | 
			
		||||
 | 
			
		||||
        let rtl = (this.actor.get_text_direction() == Clutter.TextDirection.RTL);
 | 
			
		||||
 | 
			
		||||
        if (rtl)
 | 
			
		||||
            this._hiddenX = -this.actor.width;
 | 
			
		||||
        else
 | 
			
		||||
            this._hiddenX = this._targetX + this.actor.width;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    show: function() {
 | 
			
		||||
        if (this.visible)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.visible = true;
 | 
			
		||||
 | 
			
		||||
        this.actor.show();
 | 
			
		||||
        Tweener.addTween(this.actor, { translation_x: this._targetX,
 | 
			
		||||
                                       transition: 'easeOutQuad',
 | 
			
		||||
                                       time: SLIDE_ANIMATION_TIME
 | 
			
		||||
                                     });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hide: function() {
 | 
			
		||||
        if (!this.visible)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.visible = false;
 | 
			
		||||
 | 
			
		||||
        Tweener.addTween(this.actor, { translation_x: this._hiddenX,
 | 
			
		||||
                                       transition: 'easeOutQuad',
 | 
			
		||||
                                       time: SLIDE_ANIMATION_TIME,
 | 
			
		||||
                                       onComplete: Lang.bind(this, function () {
 | 
			
		||||
                                           this.actor.hide();
 | 
			
		||||
                                       })
 | 
			
		||||
                                     });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _workspacesChanged: function() {
 | 
			
		||||
        let oldNumWorkspaces = this._thumbnails.length;
 | 
			
		||||
        let newNumWorkspaces = global.screen.n_workspaces;
 | 
			
		||||
        let active = global.screen.get_active_workspace_index();
 | 
			
		||||
 | 
			
		||||
        if (newNumWorkspaces > oldNumWorkspaces) {
 | 
			
		||||
            this.addThumbnails(oldNumWorkspaces, newNumWorkspaces - oldNumWorkspaces);
 | 
			
		||||
        } else {
 | 
			
		||||
            let removedIndex;
 | 
			
		||||
            let removedNum = oldNumWorkspaces - newNumWorkspaces;
 | 
			
		||||
            for (let w = 0; w < oldNumWorkspaces; w++) {
 | 
			
		||||
                let metaWorkspace = global.screen.get_workspace_by_index(w);
 | 
			
		||||
                if (this._thumbnails[w].metaWorkspace != metaWorkspace) {
 | 
			
		||||
                    removedIndex = w;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.removeThumbnails(removedIndex, removedNum);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addThumbnails: function(start, count) {
 | 
			
		||||
        for (let k = start; k < start + count; k++) {
 | 
			
		||||
            let metaWorkspace = global.screen.get_workspace_by_index(k);
 | 
			
		||||
@@ -802,7 +889,7 @@ const ThumbnailsBox = new Lang.Class({
 | 
			
		||||
        this._queueUpdateStates();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    syncStacking: function(stackIndices) {
 | 
			
		||||
    syncStacking: function(actor, stackIndices) {
 | 
			
		||||
        for (let i = 0; i < this._thumbnails.length; i++)
 | 
			
		||||
            this._thumbnails[i].syncStacking(stackIndices);
 | 
			
		||||
    },
 | 
			
		||||
@@ -924,6 +1011,8 @@ const ThumbnailsBox = new Lang.Class({
 | 
			
		||||
                                   onCompleteScope: this
 | 
			
		||||
                                 });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        this._computeThumbnailsX();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _queueUpdateStates: function() {
 | 
			
		||||
@@ -1157,5 +1246,30 @@ const ThumbnailsBox = new Lang.Class({
 | 
			
		||||
                           },
 | 
			
		||||
                           onCompleteScope: this
 | 
			
		||||
                         });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onScrollEvent: function (actor, event) {
 | 
			
		||||
        switch (event.get_scroll_direction()) {
 | 
			
		||||
        case Clutter.ScrollDirection.UP:
 | 
			
		||||
            Main.wm.actionMoveWorkspace(Meta.MotionDirection.UP);
 | 
			
		||||
            break;
 | 
			
		||||
        case Clutter.ScrollDirection.DOWN:
 | 
			
		||||
            Main.wm.actionMoveWorkspace(Meta.MotionDirection.DOWN);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onKeyRelease: function (actor, event) {
 | 
			
		||||
        switch (event.get_key_symbol()) {
 | 
			
		||||
        case Clutter.KEY_Up:
 | 
			
		||||
            Main.wm.actionMoveWorkspace(Meta.MotionDirection.UP);
 | 
			
		||||
            break;
 | 
			
		||||
        case Clutter.KEY_Down:
 | 
			
		||||
            Main.wm.actionMoveWorkspace(Meta.MotionDirection.DOWN);
 | 
			
		||||
            break;
 | 
			
		||||
        case Clutter.KEY_Return:
 | 
			
		||||
            Main.overview.toggle();
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -22,8 +22,6 @@ const MAX_WORKSPACES = 16;
 | 
			
		||||
 | 
			
		||||
const OVERRIDE_SCHEMA = 'org.gnome.shell.overrides';
 | 
			
		||||
 | 
			
		||||
const CONTROLS_POP_IN_TIME = 0.1;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const WorkspacesView = new Lang.Class({
 | 
			
		||||
    Name: 'WorkspacesView',
 | 
			
		||||
@@ -106,9 +104,9 @@ const WorkspacesView = new Lang.Class({
 | 
			
		||||
            global.window_manager.connect('switch-workspace',
 | 
			
		||||
                                          Lang.bind(this, this._activeWorkspaceChanged));
 | 
			
		||||
 | 
			
		||||
        this._itemDragBeginId = Main.overview.connect('item-drag-begin',
 | 
			
		||||
        this._appDragBeginId = Main.overview.connect('app-drag-begin',
 | 
			
		||||
                                                      Lang.bind(this, this._dragBegin));
 | 
			
		||||
        this._itemDragEndId = Main.overview.connect('item-drag-end',
 | 
			
		||||
        this._appDragEndId = Main.overview.connect('app-drag-end',
 | 
			
		||||
                                                     Lang.bind(this, this._dragEnd));
 | 
			
		||||
        this._windowDragBeginId = Main.overview.connect('window-drag-begin',
 | 
			
		||||
                                                        Lang.bind(this, this._dragBegin));
 | 
			
		||||
@@ -327,13 +325,13 @@ const WorkspacesView = new Lang.Class({
 | 
			
		||||
        if (this._inDrag)
 | 
			
		||||
            this._dragEnd();
 | 
			
		||||
 | 
			
		||||
        if (this._itemDragBeginId > 0) {
 | 
			
		||||
            Main.overview.disconnect(this._itemDragBeginId);
 | 
			
		||||
            this._itemDragBeginId = 0;
 | 
			
		||||
        if (this._appDragBeginId > 0) {
 | 
			
		||||
            Main.overview.disconnect(this._appDragBeginId);
 | 
			
		||||
            this._appDragBeginId = 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (this._itemDragEndId > 0) {
 | 
			
		||||
            Main.overview.disconnect(this._itemDragEndId);
 | 
			
		||||
            this._itemDragEndId = 0;
 | 
			
		||||
        if (this._appDragEndId > 0) {
 | 
			
		||||
            Main.overview.disconnect(this._appDragEndId);
 | 
			
		||||
            this._appDragEndId = 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (this._windowDragBeginId > 0) {
 | 
			
		||||
            Main.overview.disconnect(this._windowDragBeginId);
 | 
			
		||||
@@ -460,25 +458,8 @@ const WorkspacesDisplay = new Lang.Class({
 | 
			
		||||
        this.actor.connect('parent-set', Lang.bind(this, this._parentSet));
 | 
			
		||||
        this.actor.set_clip_to_allocation(true);
 | 
			
		||||
 | 
			
		||||
        let controls = new St.Bin({ style_class: 'workspace-controls',
 | 
			
		||||
                                    request_mode: Clutter.RequestMode.WIDTH_FOR_HEIGHT,
 | 
			
		||||
                                    y_align: St.Align.START,
 | 
			
		||||
                                    y_fill: true });
 | 
			
		||||
        this._controls = controls;
 | 
			
		||||
        this.actor.add_actor(controls);
 | 
			
		||||
 | 
			
		||||
        controls.reactive = true;
 | 
			
		||||
        controls.track_hover = true;
 | 
			
		||||
        controls.connect('notify::hover',
 | 
			
		||||
                         Lang.bind(this, this._onControlsHoverChanged));
 | 
			
		||||
        controls.connect('scroll-event',
 | 
			
		||||
                         Lang.bind(this, this._onScrollEvent));
 | 
			
		||||
 | 
			
		||||
        this._primaryIndex = Main.layoutManager.primaryIndex;
 | 
			
		||||
 | 
			
		||||
        this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox();
 | 
			
		||||
        controls.add_actor(this._thumbnailsBox.actor);
 | 
			
		||||
 | 
			
		||||
        this._workspacesViews = null;
 | 
			
		||||
        this._primaryScrollAdjustment = null;
 | 
			
		||||
 | 
			
		||||
@@ -491,34 +472,14 @@ const WorkspacesDisplay = new Lang.Class({
 | 
			
		||||
        this._inDrag = false;
 | 
			
		||||
        this._cancelledDrag = false;
 | 
			
		||||
 | 
			
		||||
        this._controlsInitiallyHovered = false;
 | 
			
		||||
        this._alwaysZoomOut = false;
 | 
			
		||||
        this._zoomOut = false;
 | 
			
		||||
        this._zoomFraction = 0;
 | 
			
		||||
 | 
			
		||||
        this._updateAlwaysZoom();
 | 
			
		||||
 | 
			
		||||
        // If we stop hiding the overview on layout changes, we will need to
 | 
			
		||||
        // update the _workspacesViews here
 | 
			
		||||
        Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._updateAlwaysZoom));
 | 
			
		||||
 | 
			
		||||
        Main.xdndHandler.connect('drag-begin', Lang.bind(this, function(){
 | 
			
		||||
            this._alwaysZoomOut = true;
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        Main.xdndHandler.connect('drag-end', Lang.bind(this, function(){
 | 
			
		||||
            this._alwaysZoomOut = false;
 | 
			
		||||
            this._updateAlwaysZoom();
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        global.screen.connect('notify::n-workspaces',
 | 
			
		||||
                              Lang.bind(this, this._workspacesChanged));
 | 
			
		||||
 | 
			
		||||
        this._switchWorkspaceNotifyId = 0;
 | 
			
		||||
 | 
			
		||||
        this._itemDragBeginId = 0;
 | 
			
		||||
        this._itemDragCancelledId = 0;
 | 
			
		||||
        this._itemDragEndId = 0;
 | 
			
		||||
        this._appDragBeginId = 0;
 | 
			
		||||
        this._appDragCancelledId = 0;
 | 
			
		||||
        this._appDragEndId = 0;
 | 
			
		||||
        this._windowDragBeginId = 0;
 | 
			
		||||
        this._windowDragCancelledId = 0;
 | 
			
		||||
        this._windowDragEndId = 0;
 | 
			
		||||
@@ -538,41 +499,20 @@ const WorkspacesDisplay = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    show: function() {
 | 
			
		||||
        if(!this._alwaysZoomOut) {
 | 
			
		||||
            let [mouseX, mouseY] = global.get_pointer();
 | 
			
		||||
            let [x, y] = this._controls.get_transformed_position();
 | 
			
		||||
            let [width, height] = this._controls.get_transformed_size();
 | 
			
		||||
            let visibleWidth = this._controls.get_theme_node().get_length('visible-width');
 | 
			
		||||
            let rtl = (Clutter.get_default_text_direction () == Clutter.TextDirection.RTL);
 | 
			
		||||
            if(rtl)
 | 
			
		||||
                x = x + width - visibleWidth;
 | 
			
		||||
            if(mouseX > x - 0.5 && mouseX < x + visibleWidth + 0.5 &&
 | 
			
		||||
               mouseY > y - 0.5 && mouseY < y + height + 0.5)
 | 
			
		||||
                this._controlsInitiallyHovered = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._zoomOut = this._alwaysZoomOut;
 | 
			
		||||
        this._zoomFraction = this._alwaysZoomOut ? 1 : 0;
 | 
			
		||||
        this._updateZoom();
 | 
			
		||||
 | 
			
		||||
        this._controls.show();
 | 
			
		||||
        this._thumbnailsBox.show();
 | 
			
		||||
        this._updateSwitcherVisibility();
 | 
			
		||||
 | 
			
		||||
        this._updateWorkspacesViews();
 | 
			
		||||
 | 
			
		||||
        this._restackedNotifyId =
 | 
			
		||||
            global.screen.connect('restacked',
 | 
			
		||||
            Main.overview.connect('sync-window-stacking',
 | 
			
		||||
                                  Lang.bind(this, this._onRestacked));
 | 
			
		||||
 | 
			
		||||
        if (this._itemDragBeginId == 0)
 | 
			
		||||
            this._itemDragBeginId = Main.overview.connect('item-drag-begin',
 | 
			
		||||
        if (this._appDragBeginId == 0)
 | 
			
		||||
            this._appDragBeginId = Main.overview.connect('app-drag-begin',
 | 
			
		||||
                                                          Lang.bind(this, this._dragBegin));
 | 
			
		||||
        if (this._itemDragCancelledId == 0)
 | 
			
		||||
            this._itemDragCancelledId = Main.overview.connect('item-drag-cancelled',
 | 
			
		||||
        if (this._appDragCancelledId == 0)
 | 
			
		||||
            this._appDragCancelledId = Main.overview.connect('app-drag-cancelled',
 | 
			
		||||
                                                              Lang.bind(this, this._dragCancelled));
 | 
			
		||||
        if (this._itemDragEndId == 0)
 | 
			
		||||
            this._itemDragEndId = Main.overview.connect('item-drag-end',
 | 
			
		||||
        if (this._appDragEndId == 0)
 | 
			
		||||
            this._appDragEndId = Main.overview.connect('app-drag-end',
 | 
			
		||||
                                                        Lang.bind(this, this._dragEnd));
 | 
			
		||||
        if (this._windowDragBeginId == 0)
 | 
			
		||||
            this._windowDragBeginId = Main.overview.connect('window-drag-begin',
 | 
			
		||||
@@ -583,8 +523,6 @@ const WorkspacesDisplay = new Lang.Class({
 | 
			
		||||
        if (this._windowDragEndId == 0)
 | 
			
		||||
            this._windowDragEndId = Main.overview.connect('window-drag-end',
 | 
			
		||||
                                                          Lang.bind(this, this._dragEnd));
 | 
			
		||||
 | 
			
		||||
        this._onRestacked();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    zoomFromOverview: function() {
 | 
			
		||||
@@ -594,27 +532,21 @@ const WorkspacesDisplay = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hide: function() {
 | 
			
		||||
        this._controls.hide();
 | 
			
		||||
        this._thumbnailsBox.hide();
 | 
			
		||||
 | 
			
		||||
        if (!this._alwaysZoomOut)
 | 
			
		||||
            this.zoomFraction = 0;
 | 
			
		||||
 | 
			
		||||
        if (this._restackedNotifyId > 0){
 | 
			
		||||
            global.screen.disconnect(this._restackedNotifyId);
 | 
			
		||||
            Main.overview.disconnect(this._restackedNotifyId);
 | 
			
		||||
            this._restackedNotifyId = 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (this._itemDragBeginId > 0) {
 | 
			
		||||
            Main.overview.disconnect(this._itemDragBeginId);
 | 
			
		||||
            this._itemDragBeginId = 0;
 | 
			
		||||
        if (this._appDragBeginId > 0) {
 | 
			
		||||
            Main.overview.disconnect(this._appDragBeginId);
 | 
			
		||||
            this._appDragBeginId = 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (this._itemDragCancelledId > 0) {
 | 
			
		||||
            Main.overview.disconnect(this._itemDragCancelledId);
 | 
			
		||||
            this._itemDragCancelledId = 0;
 | 
			
		||||
        if (this._appDragCancelledId > 0) {
 | 
			
		||||
            Main.overview.disconnect(this._appDragCancelledId);
 | 
			
		||||
            this._appDragCancelledId = 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (this._itemDragEndId > 0) {
 | 
			
		||||
            Main.overview.disconnect(this._itemDragEndId);
 | 
			
		||||
            this._itemDragEndId = 0;
 | 
			
		||||
        if (this._appDragEndId > 0) {
 | 
			
		||||
            Main.overview.disconnect(this._appDragEndId);
 | 
			
		||||
            this._appDragEndId = 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (this._windowDragBeginId > 0) {
 | 
			
		||||
            Main.overview.disconnect(this._windowDragBeginId);
 | 
			
		||||
@@ -745,73 +677,15 @@ const WorkspacesDisplay = new Lang.Class({
 | 
			
		||||
        return this._getPrimaryView().getActiveWorkspace().hasMaximizedWindows();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // zoomFraction property allows us to tween the controls sliding in and out
 | 
			
		||||
    set zoomFraction(fraction) {
 | 
			
		||||
        this._zoomFraction = fraction;
 | 
			
		||||
        this.actor.queue_relayout();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get zoomFraction() {
 | 
			
		||||
        return this._zoomFraction;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateAlwaysZoom: function()  {
 | 
			
		||||
        // Always show the pager if workspaces are actually used,
 | 
			
		||||
        // e.g. there are windows on more than one
 | 
			
		||||
        this._alwaysZoomOut = global.screen.n_workspaces > 2;
 | 
			
		||||
 | 
			
		||||
        if (this._alwaysZoomOut)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let monitors = Main.layoutManager.monitors;
 | 
			
		||||
        let primary = Main.layoutManager.primaryMonitor;
 | 
			
		||||
 | 
			
		||||
        /* Look for any monitor to the right of the primary, if there is
 | 
			
		||||
         * one, we always keep zoom out, otherwise its hard to reach
 | 
			
		||||
         * the thumbnail area without passing into the next monitor. */
 | 
			
		||||
        for (let i = 0; i < monitors.length; i++) {
 | 
			
		||||
            if (monitors[i].x >= primary.x + primary.width) {
 | 
			
		||||
                this._alwaysZoomOut = true;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getPreferredWidth: function (actor, forHeight, alloc) {
 | 
			
		||||
        // pass through the call in case the child needs it, but report 0x0
 | 
			
		||||
        this._controls.get_preferred_width(forHeight);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getPreferredHeight: function (actor, forWidth, alloc) {
 | 
			
		||||
        // pass through the call in case the child needs it, but report 0x0
 | 
			
		||||
        this._controls.get_preferred_height(forWidth);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _allocate: function (actor, box, flags) {
 | 
			
		||||
        let childBox = new Clutter.ActorBox();
 | 
			
		||||
 | 
			
		||||
        let totalWidth = box.x2 - box.x1;
 | 
			
		||||
 | 
			
		||||
        // width of the controls
 | 
			
		||||
        let [controlsMin, controlsNatural] = this._controls.get_preferred_width(box.y2 - box.y1);
 | 
			
		||||
 | 
			
		||||
        // Amount of space on the screen we reserve for the visible control
 | 
			
		||||
        let controlsVisible = this._controls.get_theme_node().get_length('visible-width');
 | 
			
		||||
        let controlsReserved = controlsVisible * (1 - this._zoomFraction) + controlsNatural * this._zoomFraction;
 | 
			
		||||
 | 
			
		||||
        let rtl = (Clutter.get_default_text_direction () == Clutter.TextDirection.RTL);
 | 
			
		||||
        if (rtl) {
 | 
			
		||||
            childBox.x2 = controlsReserved;
 | 
			
		||||
            childBox.x1 = childBox.x2 - controlsNatural;
 | 
			
		||||
        } else {
 | 
			
		||||
            childBox.x1 = totalWidth - controlsReserved;
 | 
			
		||||
            childBox.x2 = childBox.x1 + controlsNatural;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        childBox.y1 = 0;
 | 
			
		||||
        childBox.y2 = box.y2- box.y1;
 | 
			
		||||
        this._controls.allocate(childBox, flags);
 | 
			
		||||
 | 
			
		||||
        this._updateWorkspacesGeometry();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -851,24 +725,15 @@ const WorkspacesDisplay = new Lang.Class({
 | 
			
		||||
        let width = fullWidth;
 | 
			
		||||
        let height = fullHeight;
 | 
			
		||||
 | 
			
		||||
        let [controlsMin, controlsNatural] = this._controls.get_preferred_width(height);
 | 
			
		||||
        let controlsVisible = this._controls.get_theme_node().get_length('visible-width');
 | 
			
		||||
 | 
			
		||||
        let [x, y] = this.actor.get_transformed_position();
 | 
			
		||||
 | 
			
		||||
        let rtl = (Clutter.get_default_text_direction () == Clutter.TextDirection.RTL);
 | 
			
		||||
 | 
			
		||||
        let clipWidth = width - controlsVisible;
 | 
			
		||||
        let clipWidth = width;
 | 
			
		||||
        let clipHeight = fullHeight;
 | 
			
		||||
        let clipX = rtl ? x + controlsVisible : x;
 | 
			
		||||
        let clipX = x;
 | 
			
		||||
        let clipY = y + (fullHeight - clipHeight) / 2;
 | 
			
		||||
 | 
			
		||||
        let widthAdjust = this._zoomOut ? controlsNatural : controlsVisible;
 | 
			
		||||
        widthAdjust += Main.overview._spacing;
 | 
			
		||||
        width -= widthAdjust;
 | 
			
		||||
        if (rtl)
 | 
			
		||||
            x += widthAdjust;
 | 
			
		||||
 | 
			
		||||
        let monitors = Main.layoutManager.monitors;
 | 
			
		||||
        let m = 0;
 | 
			
		||||
        for (let i = 0; i < monitors.length; i++) {
 | 
			
		||||
@@ -891,25 +756,12 @@ const WorkspacesDisplay = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onRestacked: function() {
 | 
			
		||||
        let stack = global.get_window_actors();
 | 
			
		||||
        let stackIndices = {};
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < stack.length; i++) {
 | 
			
		||||
            // Use the stable sequence for an integer to use as a hash key
 | 
			
		||||
            stackIndices[stack[i].get_meta_window().get_stable_sequence()] = i;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    _onRestacked: function(actor, stackIndices) {
 | 
			
		||||
        for (let i = 0; i < this._workspacesViews.length; i++)
 | 
			
		||||
            this._workspacesViews[i].syncStacking(stackIndices);
 | 
			
		||||
 | 
			
		||||
        this._thumbnailsBox.syncStacking(stackIndices);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _workspacesChanged: function() {
 | 
			
		||||
        this._updateAlwaysZoom();
 | 
			
		||||
        this._updateZoom();
 | 
			
		||||
 | 
			
		||||
        if (this._workspacesViews == null)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
@@ -934,8 +786,6 @@ const WorkspacesDisplay = new Lang.Class({
 | 
			
		||||
                }
 | 
			
		||||
                m++;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this._thumbnailsBox.addThumbnails(oldNumWorkspaces, newNumWorkspaces - oldNumWorkspaces);
 | 
			
		||||
        } else {
 | 
			
		||||
            // Assume workspaces are only removed sequentially
 | 
			
		||||
            // (e.g. 2,3,4 - not 2,4,7)
 | 
			
		||||
@@ -958,8 +808,6 @@ const WorkspacesDisplay = new Lang.Class({
 | 
			
		||||
                    lostWorkspaces[l].destroy();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this._thumbnailsBox.removeThumbnails(removedIndex, removedNum);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < this._workspacesViews.length; i++)
 | 
			
		||||
@@ -968,35 +816,6 @@ const WorkspacesDisplay = new Lang.Class({
 | 
			
		||||
        this._updateSwitcherVisibility();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateZoom : function() {
 | 
			
		||||
        if (Main.overview.animationInProgress)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let shouldZoom = this._alwaysZoomOut || this._controls.hover;
 | 
			
		||||
        if (shouldZoom != this._zoomOut) {
 | 
			
		||||
            this._zoomOut = shouldZoom;
 | 
			
		||||
            this._updateWorkspacesGeometry();
 | 
			
		||||
 | 
			
		||||
            if (!this._workspacesViews)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            Tweener.addTween(this,
 | 
			
		||||
                             { zoomFraction: this._zoomOut ? 1 : 0,
 | 
			
		||||
                               time: WORKSPACE_SWITCH_TIME,
 | 
			
		||||
                               transition: 'easeOutQuad' });
 | 
			
		||||
 | 
			
		||||
            for (let i = 0; i < this._workspacesViews.length; i++)
 | 
			
		||||
                this._workspacesViews[i].updateWindowPositions();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onControlsHoverChanged: function() {
 | 
			
		||||
        if(!this._controls.hover)
 | 
			
		||||
            this._controlsInitiallyHovered = false;
 | 
			
		||||
        if(!this._controlsInitiallyHovered)
 | 
			
		||||
            this._updateZoom();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _dragBegin: function() {
 | 
			
		||||
        this._inDrag = true;
 | 
			
		||||
        this._cancelledDrag = false;
 | 
			
		||||
@@ -1012,33 +831,11 @@ const WorkspacesDisplay = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDragMotion: function(dragEvent) {
 | 
			
		||||
        let controlsHovered = this._controls.contains(dragEvent.targetActor);
 | 
			
		||||
        this._controls.set_hover(controlsHovered);
 | 
			
		||||
 | 
			
		||||
        return DND.DragMotionResult.CONTINUE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _dragEnd: function() {
 | 
			
		||||
        this._inDrag = false;
 | 
			
		||||
 | 
			
		||||
        // We do this deferred because drag-end is emitted before dnd.js emits
 | 
			
		||||
        // event/leave events that were suppressed during the drag. If we didn't
 | 
			
		||||
        // defer this, we'd zoom out then immediately zoom in because of the
 | 
			
		||||
        // enter event we received. That would normally be invisible but we
 | 
			
		||||
        // might as well avoid it.
 | 
			
		||||
        Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
 | 
			
		||||
                       Lang.bind(this, this._updateZoom));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onScrollEvent: function (actor, event) {
 | 
			
		||||
        switch ( event.get_scroll_direction() ) {
 | 
			
		||||
        case Clutter.ScrollDirection.UP:
 | 
			
		||||
            Main.wm.actionMoveWorkspace(Meta.MotionDirection.UP);
 | 
			
		||||
            break;
 | 
			
		||||
        case Clutter.ScrollDirection.DOWN:
 | 
			
		||||
            Main.wm.actionMoveWorkspace(Meta.MotionDirection.DOWN);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(WorkspacesDisplay.prototype);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										30
									
								
								tests/interactive/center-layout.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								tests/interactive/center-layout.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const CenterLayout = imports.ui.centerLayout;
 | 
			
		||||
const UI = imports.testcommon.ui;
 | 
			
		||||
 | 
			
		||||
function test() {
 | 
			
		||||
    let stage = new Clutter.Stage({ user_resizable: true });
 | 
			
		||||
    UI.init(stage);
 | 
			
		||||
 | 
			
		||||
    ////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    let container = new St.Widget({ style: 'border: 2px solid black;',
 | 
			
		||||
                                    layout_manager: new CenterLayout.CenterLayout() });
 | 
			
		||||
    container.add_constraint(new Clutter.BindConstraint({ coordinate: Clutter.BindCoordinate.SIZE, source: stage }));
 | 
			
		||||
    stage.add_actor(container);
 | 
			
		||||
 | 
			
		||||
    let left = new Clutter.Actor({ background_color: Clutter.Color.get_static(Clutter.StaticColor.RED), width: 300 });
 | 
			
		||||
    let center = new Clutter.Actor({ background_color: Clutter.Color.get_static(Clutter.StaticColor.BLUE), width: 100 });
 | 
			
		||||
    let right = new Clutter.Actor({ background_color: Clutter.Color.get_static(Clutter.StaticColor.YELLOW), width: 200 });
 | 
			
		||||
 | 
			
		||||
    container.add_actor(left);
 | 
			
		||||
    container.add_actor(center);
 | 
			
		||||
    container.add_actor(right);
 | 
			
		||||
 | 
			
		||||
    UI.main(stage);
 | 
			
		||||
}
 | 
			
		||||
test();
 | 
			
		||||
		Reference in New Issue
	
	Block a user