Compare commits
	
		
			27 Commits
		
	
	
		
			wip/rancel
			...
			wip/re-sea
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					3124e82838 | ||
| 
						 | 
					6c4daaaa71 | ||
| 
						 | 
					3768e85673 | ||
| 
						 | 
					d1c72e1e4c | ||
| 
						 | 
					2498497cc1 | ||
| 
						 | 
					7c3c6da368 | ||
| 
						 | 
					09e7ab5611 | ||
| 
						 | 
					53cff07eef | ||
| 
						 | 
					cfed6e401d | ||
| 
						 | 
					12b041a569 | ||
| 
						 | 
					f2dd94776c | ||
| 
						 | 
					c0f9c52ba6 | ||
| 
						 | 
					2ee8b0e427 | ||
| 
						 | 
					b250a72fcf | ||
| 
						 | 
					cf08745f6c | ||
| 
						 | 
					58023c81ad | ||
| 
						 | 
					7e8968da52 | ||
| 
						 | 
					fce1d8157e | ||
| 
						 | 
					3b5de01ed6 | ||
| 
						 | 
					a61da42aa7 | ||
| 
						 | 
					81acfdbfc3 | ||
| 
						 | 
					159b789443 | ||
| 
						 | 
					c07b715a3a | ||
| 
						 | 
					1bb09fd0f1 | ||
| 
						 | 
					4441541da6 | ||
| 
						 | 
					afee4d6925 | ||
| 
						 | 
					b62bd0e62f | 
@@ -34,6 +34,7 @@ dist_theme_DATA =				\
 | 
			
		||||
	theme/gnome-shell.css			\
 | 
			
		||||
	theme/logged-in-indicator.svg		\
 | 
			
		||||
	theme/message-tray-background.png	\
 | 
			
		||||
	theme/more-results.svg			\
 | 
			
		||||
	theme/noise-texture.png			\
 | 
			
		||||
	theme/panel-button-border.svg		\
 | 
			
		||||
	theme/panel-button-highlight-narrow.svg	\
 | 
			
		||||
 
 | 
			
		||||
@@ -75,11 +75,13 @@
 | 
			
		||||
    <!--
 | 
			
		||||
        LaunchSearch:
 | 
			
		||||
        @terms: Array of search terms, which the provider should treat as logical AND.
 | 
			
		||||
        @timestamp: A timestamp of the user interaction that triggered this call
 | 
			
		||||
 | 
			
		||||
        Asks the search provider to launch a full search in the application for the provided terms.
 | 
			
		||||
    -->
 | 
			
		||||
    <method name="LaunchSearch">
 | 
			
		||||
      <arg type="as" name="terms" direction="in" />
 | 
			
		||||
      <arg type="u" name="timestamp" direction="in" />
 | 
			
		||||
    </method>
 | 
			
		||||
  </interface>
 | 
			
		||||
</node>
 | 
			
		||||
 
 | 
			
		||||
@@ -49,7 +49,7 @@ stage {
 | 
			
		||||
.switcher-list, 
 | 
			
		||||
.app-well-app > .overview-icon,
 | 
			
		||||
.show-apps > .overview-icon,
 | 
			
		||||
.search-result-content > .overview-icon {
 | 
			
		||||
.grid-search-result .overview-icon {
 | 
			
		||||
    font-size: 9pt;
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
}
 | 
			
		||||
@@ -616,12 +616,12 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    spacing: 40px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.window-caption {
 | 
			
		||||
    spacing: 25px;
 | 
			
		||||
#overview-group {
 | 
			
		||||
    spacing: 32px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.workspace-controls {
 | 
			
		||||
    visible-width: 32px; /* Amount visible before hovering */
 | 
			
		||||
.window-caption {
 | 
			
		||||
    spacing: 25px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.workspace-thumbnails-background {
 | 
			
		||||
@@ -641,6 +641,7 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
 | 
			
		||||
.workspace-thumbnails {
 | 
			
		||||
    spacing: 11px;
 | 
			
		||||
    visible-width: 96px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.workspace-thumbnail-indicator {
 | 
			
		||||
@@ -754,25 +755,33 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
/* Search Results */
 | 
			
		||||
 | 
			
		||||
#searchResults {
 | 
			
		||||
    padding: 20px 10px 10px 10px;
 | 
			
		||||
    spacing: 18px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#searchResultsContent {
 | 
			
		||||
    padding-right: 20px;
 | 
			
		||||
    spacing: 36px;
 | 
			
		||||
}
 | 
			
		||||
    spacing: 16px;
 | 
			
		||||
 | 
			
		||||
#searchResultsContent:rtl {
 | 
			
		||||
    padding-right: 0px;
 | 
			
		||||
    /* for scrollbars */
 | 
			
		||||
    padding-left: 20px;
 | 
			
		||||
    padding-right: 20px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.search-section-header {
 | 
			
		||||
    padding: 4px 12px;
 | 
			
		||||
    spacing: 4px;
 | 
			
		||||
    color: #6f6f6f;
 | 
			
		||||
    font-size: .8em;
 | 
			
		||||
.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: 32px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.search-statustext {
 | 
			
		||||
@@ -781,16 +790,8 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.search-section-results {
 | 
			
		||||
    padding: 6px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.search-section-list-results {
 | 
			
		||||
    spacing: 4px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.results-container {
 | 
			
		||||
    spacing: 4px;
 | 
			
		||||
.list-search-results {
 | 
			
		||||
    spacing: 3px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Text labels are an odd number of pixels tall. The uneven top and bottom
 | 
			
		||||
@@ -819,7 +820,7 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    -x-offset: 8px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Application Launchers and Grid */
 | 
			
		||||
/* Application Launchers, Grid and List results */
 | 
			
		||||
 | 
			
		||||
.icon-grid {
 | 
			
		||||
    spacing: 36px;
 | 
			
		||||
@@ -832,15 +833,10 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.all-app {
 | 
			
		||||
    padding: 16px 25px 16px 16px;
 | 
			
		||||
    padding: 16px;
 | 
			
		||||
    spacing: 20px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.all-app:rtl {
 | 
			
		||||
    padding-right: 16px;
 | 
			
		||||
    padding-left: 25px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.app-filter {
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
    height: 2.85em;
 | 
			
		||||
@@ -871,9 +867,34 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    padding: 4px 8px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.list-search-result-content {
 | 
			
		||||
    spacing: 12px;
 | 
			
		||||
    padding: 12px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.list-search-result-title {
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
    font-size: 14pt;
 | 
			
		||||
    color: white;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.list-search-result-description {
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
    font-size: 12pt;
 | 
			
		||||
    color: #eeeeec;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.search-provider-icon-more {
 | 
			
		||||
    width: 16px;
 | 
			
		||||
    height: 16px;
 | 
			
		||||
    background-image: url("more-results.svg");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.app-well-app > .overview-icon,
 | 
			
		||||
.show-apps > .overview-icon,
 | 
			
		||||
.search-result-content > .overview-icon {
 | 
			
		||||
.search-provider-icon,
 | 
			
		||||
.list-search-result,
 | 
			
		||||
.grid-search-result .overview-icon {
 | 
			
		||||
    border-radius: 4px;
 | 
			
		||||
    padding: 3px;
 | 
			
		||||
    border: 1px rgba(0,0,0,0);
 | 
			
		||||
@@ -889,7 +910,9 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
 | 
			
		||||
.app-well-app:hover > .overview-icon,
 | 
			
		||||
.show-apps:hover > .overview-icon,
 | 
			
		||||
.search-result-content:hover > .overview-icon {
 | 
			
		||||
.search-provider-icon:hover,
 | 
			
		||||
.list-search-result:hover,
 | 
			
		||||
.grid-search-result:hover .overview-icon {
 | 
			
		||||
    background-color: rgba(255,255,255,0.1);
 | 
			
		||||
    text-shadow: black 0px 2px 2px;
 | 
			
		||||
    transition-duration: 100;
 | 
			
		||||
@@ -924,10 +947,14 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.app-well-app:focus > .overview-icon,
 | 
			
		||||
.search-result-content:focus > .overview-icon,
 | 
			
		||||
.grid-search-result:focus .overview-icon,
 | 
			
		||||
.show-apps:focus > .overview-icon,
 | 
			
		||||
.search-provider-icon:focus,
 | 
			
		||||
.list-search-result:focus,
 | 
			
		||||
.app-well-app:selected > .overview-icon,
 | 
			
		||||
.search-result-content:selected > .overview-icon {
 | 
			
		||||
.grid-search-result:selected .overview-icon,
 | 
			
		||||
.search-provider-icon:selected,
 | 
			
		||||
.list-search-result:selected {
 | 
			
		||||
    background-color: rgba(255,255,255,0.33);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										93
									
								
								data/theme/more-results.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								data/theme/more-results.svg
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,93 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
 | 
			
		||||
 | 
			
		||||
<svg
 | 
			
		||||
   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
			
		||||
   xmlns:cc="http://creativecommons.org/ns#"
 | 
			
		||||
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
			
		||||
   xmlns:svg="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:xlink="http://www.w3.org/1999/xlink"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   width="16"
 | 
			
		||||
   height="16"
 | 
			
		||||
   id="svg12430"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.48+devel r11908 custom"
 | 
			
		||||
   sodipodi:docname="more-results.svg">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs12432" />
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     id="base"
 | 
			
		||||
     pagecolor="#7a7a7a"
 | 
			
		||||
     bordercolor="#666666"
 | 
			
		||||
     borderopacity="1.0"
 | 
			
		||||
     inkscape:pageopacity="1"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="1"
 | 
			
		||||
     inkscape:cx="7.4498765"
 | 
			
		||||
     inkscape:cy="9.9072581"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     inkscape:current-layer="g14642-3-0"
 | 
			
		||||
     showgrid="false"
 | 
			
		||||
     borderlayer="true"
 | 
			
		||||
     inkscape:showpageshadow="false"
 | 
			
		||||
     inkscape:window-width="2560"
 | 
			
		||||
     inkscape:window-height="1376"
 | 
			
		||||
     inkscape:window-x="1600"
 | 
			
		||||
     inkscape:window-y="27"
 | 
			
		||||
     inkscape:window-maximized="1">
 | 
			
		||||
    <inkscape:grid
 | 
			
		||||
       type="xygrid"
 | 
			
		||||
       id="grid13002" />
 | 
			
		||||
  </sodipodi:namedview>
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata12435">
 | 
			
		||||
    <rdf:RDF>
 | 
			
		||||
      <cc:Work
 | 
			
		||||
         rdf:about="">
 | 
			
		||||
        <dc:format>image/svg+xml</dc:format>
 | 
			
		||||
        <dc:type
 | 
			
		||||
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
			
		||||
        <dc:title></dc:title>
 | 
			
		||||
      </cc:Work>
 | 
			
		||||
    </rdf:RDF>
 | 
			
		||||
  </metadata>
 | 
			
		||||
  <g
 | 
			
		||||
     inkscape:label="Layer 1"
 | 
			
		||||
     inkscape:groupmode="layer"
 | 
			
		||||
     id="layer1"
 | 
			
		||||
     transform="translate(0,-1036.3622)">
 | 
			
		||||
    <g
 | 
			
		||||
       style="display:inline"
 | 
			
		||||
       transform="translate(-141.99984,638.37113)"
 | 
			
		||||
       inkscape:label="zoom-in"
 | 
			
		||||
       id="g14642-3-0">
 | 
			
		||||
      <path
 | 
			
		||||
         sodipodi:type="inkscape:offset"
 | 
			
		||||
         inkscape:radius="0"
 | 
			
		||||
         inkscape:original="M 145.1875 400 C 144.5248 400 144 400.54899 144 401.21875 L 144 410.78125 C 144 411.45101 144.5248 412 145.1875 412 L 154.8125 412 C 155.4752 412 156 411.45101 156 410.78125 L 156 401.21875 C 156 400.54899 155.4752 400 154.8125 400 L 145.1875 400 z M 149 403 L 151 403 L 151 405 L 153 405 L 153 407 L 151 407 L 151 409 L 149 409 L 149 407 L 147 407 L 147 405 L 149 405 L 149 403 z "
 | 
			
		||||
         xlink:href="#rect11749-5-0-1-8"
 | 
			
		||||
         style="color:#bebebe;fill:#000000;fill-opacity:0.38207546;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible"
 | 
			
		||||
         id="path13004"
 | 
			
		||||
         inkscape:href="#rect11749-5-0-1-8"
 | 
			
		||||
         d="M 145.1875,400 C 144.5248,400 144,400.54899 144,401.21875 l 0,9.5625 c 0,0.66976 0.5248,1.21875 1.1875,1.21875 l 9.625,0 c 0.6627,0 1.1875,-0.54899 1.1875,-1.21875 l 0,-9.5625 C 156,400.54899 155.4752,400 154.8125,400 L 145.1875,400 z m 3.8125,3 2,0 0,2 2,0 0,2 -2,0 0,2 -2,0 0,-2 -2,0 0,-2 2,0 L 149,403 Z"
 | 
			
		||||
         transform="translate(0,1)" />
 | 
			
		||||
      <path
 | 
			
		||||
         inkscape:connector-curvature="0"
 | 
			
		||||
         style="color:#bebebe;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible"
 | 
			
		||||
         d="M 145.1875,400 C 144.5248,400 144,400.54899 144,401.21875 l 0,9.5625 c 0,0.66976 0.5248,1.21875 1.1875,1.21875 l 9.625,0 c 0.6627,0 1.1875,-0.54899 1.1875,-1.21875 l 0,-9.5625 C 156,400.54899 155.4752,400 154.8125,400 L 145.1875,400 z m 3.8125,3 2,0 0,2 2,0 0,2 -2,0 0,2 -2,0 0,-2 -2,0 0,-2 2,0 L 149,403 Z"
 | 
			
		||||
         id="rect11749-5-0-1-8" />
 | 
			
		||||
      <rect
 | 
			
		||||
         style="fill:none;stroke:none"
 | 
			
		||||
         id="rect3620-5-4"
 | 
			
		||||
         width="15.981825"
 | 
			
		||||
         height="16"
 | 
			
		||||
         x="142"
 | 
			
		||||
         y="398"
 | 
			
		||||
         rx="0"
 | 
			
		||||
         ry="0" />
 | 
			
		||||
    </g>
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 3.7 KiB  | 
@@ -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		\
 | 
			
		||||
@@ -62,6 +63,7 @@ nobase_dist_js_DATA = 	\
 | 
			
		||||
	ui/main.js		\
 | 
			
		||||
	ui/messageTray.js	\
 | 
			
		||||
	ui/modalDialog.js	\
 | 
			
		||||
	ui/separator.js		\
 | 
			
		||||
	ui/sessionMode.js	\
 | 
			
		||||
	ui/shellEntry.js	\
 | 
			
		||||
	ui/shellMountOperation.js \
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,6 @@ 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 Tweener = imports.ui.tweener;
 | 
			
		||||
const Workspace = imports.ui.workspace;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
@@ -312,11 +311,10 @@ const AllAppDisplay = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
const AppSearchProvider = new Lang.Class({
 | 
			
		||||
    Name: 'AppSearchProvider',
 | 
			
		||||
    Extends: Search.SearchProvider,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.parent(_("APPLICATIONS"));
 | 
			
		||||
        this._appSys = Shell.AppSystem.get_default();
 | 
			
		||||
        this.id = 'applications';
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getResultMetas: function(apps, callback) {
 | 
			
		||||
@@ -369,13 +367,10 @@ const AppSearchProvider = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
const SettingsSearchProvider = new Lang.Class({
 | 
			
		||||
    Name: 'SettingsSearchProvider',
 | 
			
		||||
    Extends: Search.SearchProvider,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.parent(_("SETTINGS"));
 | 
			
		||||
 | 
			
		||||
        this._appSys = Shell.AppSystem.get_default();
 | 
			
		||||
        this.appInfo = Gio.DesktopAppInfo.new('gnome-control-center.desktop');
 | 
			
		||||
        this._appSys = Shell.AppSystem.get_default();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getResultMetas: function(prefs, callback) {
 | 
			
		||||
@@ -384,9 +379,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() { return null; }
 | 
			
		||||
                       });
 | 
			
		||||
        }
 | 
			
		||||
        callback(metas);
 | 
			
		||||
@@ -404,12 +397,6 @@ const SettingsSearchProvider = new Lang.Class({
 | 
			
		||||
        pref.activate();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    createResultActor: function (resultMeta, terms) {
 | 
			
		||||
        let app = resultMeta['id'];
 | 
			
		||||
        let icon = new AppWellIcon(app);
 | 
			
		||||
        return icon.actor;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    launchSearch: function(terms) {
 | 
			
		||||
        // FIXME: this should be a remote search provider
 | 
			
		||||
        this.appInfo.launch([], global.create_app_launch_context());
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										60
									
								
								js/ui/centerLayout.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								js/ui/centerLayout.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,60 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
 | 
			
		||||
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 [leftMinWidth, leftNaturalWidth] = left.get_preferred_width(availHeight);
 | 
			
		||||
        let [centerMinWidth, centerNaturalWidth] = center.get_preferred_width(availHeight);
 | 
			
		||||
        let [rightMinWidth, rightNaturalWidth] = right.get_preferred_width(availHeight);
 | 
			
		||||
 | 
			
		||||
        let sideWidth = (availWidth - centerMinWidth) / 2;
 | 
			
		||||
 | 
			
		||||
        let childBox = new Clutter.ActorBox();
 | 
			
		||||
        childBox.y1 = box.y1;
 | 
			
		||||
        childBox.y2 = box.y1 + availHeight;
 | 
			
		||||
 | 
			
		||||
        let leftSide = Math.min(Math.floor(sideWidth), leftNaturalWidth);
 | 
			
		||||
        let rightSide = Math.min(Math.floor(sideWidth), rightNaturalWidth);
 | 
			
		||||
 | 
			
		||||
        if (rtl) {
 | 
			
		||||
            childBox.x1 = availWidth - leftSide;
 | 
			
		||||
            childBox.x2 = availWidth;
 | 
			
		||||
        } else {
 | 
			
		||||
            childBox.x1 = 0;
 | 
			
		||||
            childBox.x2 = leftSide;
 | 
			
		||||
        }
 | 
			
		||||
        childBox.x1 += box.x1;
 | 
			
		||||
        left.allocate(childBox, flags);
 | 
			
		||||
 | 
			
		||||
        let maxSide = Math.max(leftSide, rightSide);
 | 
			
		||||
        let sideWidth = Math.max((availWidth - centerNaturalWidth) / 2, maxSide);
 | 
			
		||||
 | 
			
		||||
        childBox.x1 = box.x1 + Math.ceil(sideWidth);
 | 
			
		||||
        childBox.x2 = box.x2 - Math.ceil(sideWidth);
 | 
			
		||||
        center.allocate(childBox, flags);
 | 
			
		||||
 | 
			
		||||
        if (rtl) {
 | 
			
		||||
            childBox.x1 = 0;
 | 
			
		||||
            childBox.x2 = rightSide;
 | 
			
		||||
        } else {
 | 
			
		||||
            childBox.x1 = availWidth - rightSide;
 | 
			
		||||
            childBox.x2 = availWidth;
 | 
			
		||||
        }
 | 
			
		||||
        childBox.x1 += box.x1;
 | 
			
		||||
        right.allocate(childBox, flags);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
@@ -371,6 +371,7 @@ const Dash = new Lang.Class({
 | 
			
		||||
        this._maxHeight = -1;
 | 
			
		||||
        this.iconSize = 64;
 | 
			
		||||
        this._shownInitially = false;
 | 
			
		||||
        this._ignoreHeight = false;
 | 
			
		||||
 | 
			
		||||
        this._dragPlaceholder = null;
 | 
			
		||||
        this._dragPlaceholderPos = -1;
 | 
			
		||||
@@ -396,7 +397,10 @@ const Dash = new Lang.Class({
 | 
			
		||||
        this.actor = new St.Bin({ child: this._container });
 | 
			
		||||
        this.actor.connect('notify::height', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                if (this._maxHeight != this.actor.height)
 | 
			
		||||
                if (this._ignoreHeight)
 | 
			
		||||
                    return;
 | 
			
		||||
 | 
			
		||||
                if (this._maxHeight != this.actor.height);
 | 
			
		||||
                    this._queueRedisplay();
 | 
			
		||||
                this._maxHeight = this.actor.height;
 | 
			
		||||
            }));
 | 
			
		||||
@@ -421,6 +425,8 @@ const Dash = new Lang.Class({
 | 
			
		||||
                              Lang.bind(this, this._onDragCancelled));
 | 
			
		||||
        Main.overview.connect('window-drag-end',
 | 
			
		||||
                              Lang.bind(this, this._onDragEnd));
 | 
			
		||||
        Main.overview.connect('showing',
 | 
			
		||||
                              Lang.bind(this, this._onOverviewShowing));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDragBegin: function() {
 | 
			
		||||
@@ -923,6 +929,65 @@ const Dash = new Lang.Class({
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _computeTranslation: function() {
 | 
			
		||||
        let rtl = (this.actor.get_text_direction() == Clutter.TextDirection.RTL);
 | 
			
		||||
 | 
			
		||||
        if (rtl)
 | 
			
		||||
            return this.actor.width;
 | 
			
		||||
        else
 | 
			
		||||
            return - this.actor.width;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onOverviewShowing: function() {
 | 
			
		||||
        // reset any translation and make sure the actor is visible when
 | 
			
		||||
        // entering the overview
 | 
			
		||||
        this.slideX = 0;
 | 
			
		||||
        this.actor.show();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get slideX() {
 | 
			
		||||
        return this._slideX;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    set slideX(value) {
 | 
			
		||||
        this._slideX = value;
 | 
			
		||||
        this.actor.translation_x = this._slideX;
 | 
			
		||||
 | 
			
		||||
        if (this._slideX > 0) {
 | 
			
		||||
            let rect = new Clutter.Rect();
 | 
			
		||||
            rect.size.width = this.actor.width - this._slideX;
 | 
			
		||||
            rect.size.height = this.actor.height;
 | 
			
		||||
            this.actor.clip_rect = rect;
 | 
			
		||||
        } else {
 | 
			
		||||
            this.actor.clip_rect = null;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    show: function() {
 | 
			
		||||
        this.actor.show();
 | 
			
		||||
        Tweener.addTween(this, { slideX: 0,
 | 
			
		||||
                                 transition: 'easeOutQuad',
 | 
			
		||||
                                 time: DASH_ANIMATION_TIME,
 | 
			
		||||
                                 onComplete: Lang.bind(this,
 | 
			
		||||
                                     function() {
 | 
			
		||||
                                         this._ignoreHeight = false;
 | 
			
		||||
                                     })
 | 
			
		||||
                               });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hide: function() {
 | 
			
		||||
        this._ignoreHeight = true;
 | 
			
		||||
        let hiddenX = this._computeTranslation();
 | 
			
		||||
        Tweener.addTween(this, { slideX: hiddenX,
 | 
			
		||||
                                 transition: 'easeOutQuad',
 | 
			
		||||
                                 time: DASH_ANIMATION_TIME,
 | 
			
		||||
                                 onComplete: Lang.bind(this,
 | 
			
		||||
                                 function() {
 | 
			
		||||
                                     this.actor.hide();
 | 
			
		||||
                                 })
 | 
			
		||||
                               });
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -234,7 +234,7 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
        this._dragY = this._dragStartY = stageY;
 | 
			
		||||
 | 
			
		||||
        if (this.actor._delegate && this.actor._delegate.getDragActor) {
 | 
			
		||||
            this._dragActor = this.actor._delegate.getDragActor(this._dragStartX, this._dragStartY);
 | 
			
		||||
            this._dragActor = this.actor._delegate.getDragActor();
 | 
			
		||||
            this._dragActor.reparent(Main.uiGroup);
 | 
			
		||||
            this._dragActor.raise_top();
 | 
			
		||||
            Shell.util_set_hidden_from_pick(this._dragActor, true);
 | 
			
		||||
 
 | 
			
		||||
@@ -1507,7 +1507,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() {
 | 
			
		||||
@@ -1790,6 +1790,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) {
 | 
			
		||||
@@ -2036,14 +2041,14 @@ 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;
 | 
			
		||||
 | 
			
		||||
        let notificationsVisible = this._notificationState != State.HIDDEN;
 | 
			
		||||
        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 GLSL_DIM_EFFECT_DECLARATIONS = '';
 | 
			
		||||
@@ -100,6 +98,13 @@ const ShellInfo = new Lang.Class({
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const ControlsChange = {
 | 
			
		||||
    BEFORE_PAGE: 1,
 | 
			
		||||
    AFTER_PAGE: 2,
 | 
			
		||||
    DND_START: 3,
 | 
			
		||||
    DND_END: 4
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const Overview = new Lang.Class({
 | 
			
		||||
    Name: 'Overview',
 | 
			
		||||
 | 
			
		||||
@@ -136,21 +141,25 @@ 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._overview = new St.BoxLayout({ name: 'overview',
 | 
			
		||||
                                            accessible_name: _("Overview"),
 | 
			
		||||
                                            reactive: true,
 | 
			
		||||
                                            vertical: true });
 | 
			
		||||
        this._overview._delegate = this;
 | 
			
		||||
 | 
			
		||||
        let layout = new CenterLayout.CenterLayout();
 | 
			
		||||
        this._group = new St.Widget({ name: 'overview-group',
 | 
			
		||||
                                      layout_manager: layout });
 | 
			
		||||
 | 
			
		||||
        this._spacing = 0;
 | 
			
		||||
        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();
 | 
			
		||||
                }
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
@@ -169,12 +178,11 @@ const Overview = new Lang.Class({
 | 
			
		||||
        // 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();
 | 
			
		||||
 | 
			
		||||
@@ -186,6 +194,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;
 | 
			
		||||
@@ -213,6 +223,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
 | 
			
		||||
@@ -221,16 +238,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() {
 | 
			
		||||
@@ -241,10 +255,99 @@ 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);
 | 
			
		||||
 | 
			
		||||
        Main.ctrlAltTabManager.addGroup(this._thumbnailsBox.actor, _("Workspaces"), 'view-list-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,
 | 
			
		||||
                                              x_fill: true,
 | 
			
		||||
                                              y_fill: true });
 | 
			
		||||
        this._overview.add_actor(this._messageTrayGhost);
 | 
			
		||||
 | 
			
		||||
        this._viewSelector.connect('after-page-change', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                this._setSideControlsVisibility(ControlsChange.AFTER_PAGE);
 | 
			
		||||
            }));
 | 
			
		||||
        this._viewSelector.connect('before-page-change', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                this._setSideControlsVisibility(ControlsChange.BEFORE_PAGE);
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        this.connect('item-drag-begin', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                this._setSideControlsVisibility(ControlsChange.DND_START);
 | 
			
		||||
            }));
 | 
			
		||||
        this.connect('item-drag-cancelled', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                this._setSideControlsVisibility(ControlsChange.DND_END);
 | 
			
		||||
            }));
 | 
			
		||||
        this.connect('item-drag-end', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                this._setSideControlsVisibility(ControlsChange.DND_END);
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout));
 | 
			
		||||
        this._relayout();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setSideControlsVisibility: function(changeType) {
 | 
			
		||||
        // Ignore the case when we're leaving the overview, since
 | 
			
		||||
        // actors will be made visible again when entering the overview
 | 
			
		||||
        // next time, and animating them while doing so is just
 | 
			
		||||
        // unnecesary noise
 | 
			
		||||
        if (!this.visible || this.animationInProgress)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let appsActive = this._viewSelector.getAppsActive();
 | 
			
		||||
        let searchActive = this._viewSelector.getSearchActive();
 | 
			
		||||
        let dashVisible = !searchActive || (changeType == ControlsChange.DND_START);
 | 
			
		||||
        let thumbnailsVisible = (!searchActive && !appsActive) || (changeType == ControlsChange.DND_START);
 | 
			
		||||
        let trayVisible = !searchActive;
 | 
			
		||||
        let trayGhostVisible = trayVisible || dashVisible;
 | 
			
		||||
 | 
			
		||||
        if ((changeType == ControlsChange.BEFORE_PAGE) ||
 | 
			
		||||
            (changeType == ControlsChange.DND_START)) {
 | 
			
		||||
            if (dashVisible)
 | 
			
		||||
                this._dash.show();
 | 
			
		||||
            if (thumbnailsVisible)
 | 
			
		||||
                this._thumbnailsBox.show();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ((changeType == ControlsChange.BEFORE_PAGE) ||
 | 
			
		||||
            (changeType == ControlsChange.DND_END)) {
 | 
			
		||||
            if (!dashVisible) {
 | 
			
		||||
                this._dash.hide();
 | 
			
		||||
            }
 | 
			
		||||
            if (!thumbnailsVisible)
 | 
			
		||||
                this._thumbnailsBox.hide();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (changeType == ControlsChange.BEFORE_PAGE ||
 | 
			
		||||
            changeType == ControlsChange.DND_START) {
 | 
			
		||||
            if (trayGhostVisible)
 | 
			
		||||
                this._messageTrayGhost.show();
 | 
			
		||||
            if (trayVisible)
 | 
			
		||||
                Main.messageTray.show();
 | 
			
		||||
            else
 | 
			
		||||
                Main.messageTray.hide();
 | 
			
		||||
        } else if (changeType == ControlsChange.AFTER_PAGE ||
 | 
			
		||||
                   changeType == ControlsChange.DND_END) {
 | 
			
		||||
            if (!trayGhostVisible)
 | 
			
		||||
                this._messageTrayGhost.hide();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addSearchProvider: function(provider) {
 | 
			
		||||
        this._viewSelector.addSearchProvider(provider);
 | 
			
		||||
    },
 | 
			
		||||
@@ -337,7 +440,7 @@ const Overview = new Lang.Class({
 | 
			
		||||
        if (this.isDummy)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._group.add_action(action);
 | 
			
		||||
        this._overview.add_action(action);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getDesktopClone: function() {
 | 
			
		||||
@@ -363,41 +466,27 @@ 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 ////
 | 
			
		||||
@@ -481,12 +570,13 @@ 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._messageTrayGhost.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,
 | 
			
		||||
@@ -578,7 +668,7 @@ const Overview = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        if (this._shown) {
 | 
			
		||||
            if (!this._modal) {
 | 
			
		||||
                if (Main.pushModal(this._group,
 | 
			
		||||
                if (Main.pushModal(this._overview,
 | 
			
		||||
                                   { keybindingMode: Main.KeybindingMode.OVERVIEW }))
 | 
			
		||||
                    this._modal = true;
 | 
			
		||||
                else
 | 
			
		||||
@@ -586,13 +676,13 @@ const Overview = new Lang.Class({
 | 
			
		||||
            }
 | 
			
		||||
        } 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)
 | 
			
		||||
@@ -610,7 +700,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,
 | 
			
		||||
@@ -652,7 +742,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;
 | 
			
		||||
@@ -937,12 +938,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._sessionStyle = null;
 | 
			
		||||
@@ -962,7 +998,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)
 | 
			
		||||
@@ -971,9 +1006,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);
 | 
			
		||||
@@ -984,83 +1016,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;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Cairo = imports.cairo;
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
@@ -15,6 +14,7 @@ const BoxPointer = imports.ui.boxpointer;
 | 
			
		||||
const GrabHelper = imports.ui.grabHelper;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
const Separator = imports.ui.separator;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
 | 
			
		||||
const SLIDER_SCROLL_STEP = 0.05; /* Slider scrolling step in % */
 | 
			
		||||
@@ -405,29 +405,8 @@ 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));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onRepaint: function(area) {
 | 
			
		||||
        let cr = area.get_context();
 | 
			
		||||
        let themeNode = area.get_theme_node();
 | 
			
		||||
        let [width, height] = area.get_surface_size();
 | 
			
		||||
        let margin = themeNode.get_length('-margin-horizontal');
 | 
			
		||||
        let gradientHeight = themeNode.get_length('-gradient-height');
 | 
			
		||||
        let startColor = themeNode.get_color('-gradient-start');
 | 
			
		||||
        let endColor = themeNode.get_color('-gradient-end');
 | 
			
		||||
 | 
			
		||||
        let gradientWidth = (width - margin * 2);
 | 
			
		||||
        let gradientOffset = (height - gradientHeight) / 2;
 | 
			
		||||
        let pattern = new Cairo.LinearGradient(margin, gradientOffset, width - margin, gradientOffset + gradientHeight);
 | 
			
		||||
        pattern.addColorStopRGBA(0, startColor.red / 255, startColor.green / 255, startColor.blue / 255, startColor.alpha / 255);
 | 
			
		||||
        pattern.addColorStopRGBA(0.5, endColor.red / 255, endColor.green / 255, endColor.blue / 255, endColor.alpha / 255);
 | 
			
		||||
        pattern.addColorStopRGBA(1, startColor.red / 255, startColor.green / 255, startColor.blue / 255, startColor.alpha / 255);
 | 
			
		||||
        cr.setSource(pattern);
 | 
			
		||||
        cr.rectangle(margin, gradientOffset, gradientWidth, gradientHeight);
 | 
			
		||||
        cr.fill();
 | 
			
		||||
        this._separator = new Separator.HorizontalSeparator({ style_class: 'popup-separator-menu-item' });
 | 
			
		||||
        this.addActor(this._separator.actor, { span: -1, expand: true });
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -50,6 +50,7 @@ const SearchProvider2Iface = <interface name="org.gnome.Shell.SearchProvider2">
 | 
			
		||||
</method>
 | 
			
		||||
<method name="LaunchSearch">
 | 
			
		||||
    <arg type="as" direction="in" />
 | 
			
		||||
    <arg type="u" direction="in" />
 | 
			
		||||
</method>
 | 
			
		||||
</interface>;
 | 
			
		||||
 | 
			
		||||
@@ -166,7 +167,6 @@ function remoteProvidersLoaded(loadState) {
 | 
			
		||||
 | 
			
		||||
const RemoteSearchProvider = new Lang.Class({
 | 
			
		||||
    Name: 'RemoteSearchProvider',
 | 
			
		||||
    Extends: Search.SearchProvider,
 | 
			
		||||
 | 
			
		||||
    _init: function(appInfo, dbusName, dbusPath, proxyType) {
 | 
			
		||||
        if (!proxyType)
 | 
			
		||||
@@ -175,7 +175,10 @@ const RemoteSearchProvider = new Lang.Class({
 | 
			
		||||
        this.proxy = new proxyType(Gio.DBus.session,
 | 
			
		||||
                dbusName, dbusPath, Lang.bind(this, this._onProxyConstructed));
 | 
			
		||||
 | 
			
		||||
        this.parent(appInfo.get_name().toUpperCase(), appInfo, true);
 | 
			
		||||
        this.appInfo = appInfo;
 | 
			
		||||
        this.id = appInfo.get_id();
 | 
			
		||||
        this.isRemoteProvider = true;
 | 
			
		||||
 | 
			
		||||
        this._cancellable = new Gio.Cancellable();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -195,9 +198,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) {
 | 
			
		||||
@@ -290,6 +291,6 @@ const RemoteSearchProvider2 = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    launchSearch: function(terms) {
 | 
			
		||||
        this.proxy.LaunchSearchRemote(terms);
 | 
			
		||||
        this.proxy.LaunchSearchRemote(terms, global.get_current_time());
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										181
									
								
								js/ui/search.js
									
									
									
									
									
								
							
							
						
						
									
										181
									
								
								js/ui/search.js
									
									
									
									
									
								
							@@ -1,187 +1,10 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Util = imports.misc.util;
 | 
			
		||||
 | 
			
		||||
const FileUtils = imports.misc.fileUtils;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
 | 
			
		||||
const SEARCH_PROVIDERS_SCHEMA = 'org.gnome.desktop.search-providers';
 | 
			
		||||
 | 
			
		||||
// Not currently referenced by the search API, but
 | 
			
		||||
// this enumeration can be useful for provider
 | 
			
		||||
// implementations.
 | 
			
		||||
const MatchType = {
 | 
			
		||||
    NONE: 0,
 | 
			
		||||
    SUBSTRING: 1,
 | 
			
		||||
    PREFIX: 2
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const SearchResultDisplay = new Lang.Class({
 | 
			
		||||
    Name: 'SearchResultDisplay',
 | 
			
		||||
 | 
			
		||||
    _init: function(provider) {
 | 
			
		||||
        this.provider = provider;
 | 
			
		||||
        this.actor = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * renderResults:
 | 
			
		||||
     * @results: List of identifier strings
 | 
			
		||||
     * @terms: List of search term strings
 | 
			
		||||
     *
 | 
			
		||||
     * Display the given search matches which resulted
 | 
			
		||||
     * from the given terms.  It's expected that not
 | 
			
		||||
     * all results will fit in the space for the container
 | 
			
		||||
     * actor; in this case, show as many as makes sense
 | 
			
		||||
     * for your result type.
 | 
			
		||||
     *
 | 
			
		||||
     * The terms are useful for search match highlighting.
 | 
			
		||||
     */
 | 
			
		||||
    renderResults: function(results, terms) {
 | 
			
		||||
        throw new Error('Not implemented');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * clear:
 | 
			
		||||
     * Remove all results from this display.
 | 
			
		||||
     */
 | 
			
		||||
    clear: function() {
 | 
			
		||||
        this.actor.destroy_all_children();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * getVisibleResultCount:
 | 
			
		||||
     *
 | 
			
		||||
     * Returns: The number of actors visible.
 | 
			
		||||
     */
 | 
			
		||||
    getVisibleResultCount: function() {
 | 
			
		||||
        throw new Error('Not implemented');
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * SearchProvider:
 | 
			
		||||
 *
 | 
			
		||||
 * Subclass this object to add a new result type
 | 
			
		||||
 * to the search system, then call registerProvider()
 | 
			
		||||
 * in SearchSystem with an instance.
 | 
			
		||||
 * Search is asynchronous and uses the
 | 
			
		||||
 * getInitialResultSet()/getSubsearchResultSet() methods.
 | 
			
		||||
 */
 | 
			
		||||
const SearchProvider = new Lang.Class({
 | 
			
		||||
    Name: 'SearchProvider',
 | 
			
		||||
 | 
			
		||||
    _init: function(title, appInfo, isRemoteProvider) {
 | 
			
		||||
        this.title = title;
 | 
			
		||||
        this.appInfo = appInfo;
 | 
			
		||||
        this.searchSystem = null;
 | 
			
		||||
        this.isRemoteProvider = !!isRemoteProvider;
 | 
			
		||||
        this.canLaunchSearch = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * getInitialResultSet:
 | 
			
		||||
     * @terms: Array of search terms, treated as logical AND
 | 
			
		||||
     *
 | 
			
		||||
     * Called when the user first begins a search (most likely
 | 
			
		||||
     * therefore a single term of length one or two), or when
 | 
			
		||||
     * a new term is added.
 | 
			
		||||
     *
 | 
			
		||||
     * Should "return" an array of result identifier strings representing
 | 
			
		||||
     * items which match the given search terms.  This
 | 
			
		||||
     * is expected to be a substring match on the metadata for a given
 | 
			
		||||
     * item.  Ordering of returned results is up to the discretion of the provider,
 | 
			
		||||
     * but you should follow these heruistics:
 | 
			
		||||
     *
 | 
			
		||||
     *  * Put items where the term matches multiple criteria (e.g. name and
 | 
			
		||||
     *    description) before single matches
 | 
			
		||||
     *  * Put items which match on a prefix before non-prefix substring matches
 | 
			
		||||
     *
 | 
			
		||||
     * We say "return" above, but in order to make the query asynchronous, use
 | 
			
		||||
     * this.searchSystem.pushResults();. The return value should be ignored.
 | 
			
		||||
     *
 | 
			
		||||
     * This function should be fast; do not perform unindexed full-text searches
 | 
			
		||||
     * or network queries.
 | 
			
		||||
     */
 | 
			
		||||
    getInitialResultSet: function(terms) {
 | 
			
		||||
        throw new Error('Not implemented');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * getSubsearchResultSet:
 | 
			
		||||
     * @previousResults: Array of item identifiers
 | 
			
		||||
     * @newTerms: Updated search terms
 | 
			
		||||
     *
 | 
			
		||||
     * Called when a search is performed which is a "subsearch" of
 | 
			
		||||
     * the previous search; i.e. when every search term has exactly
 | 
			
		||||
     * one corresponding term in the previous search which is a prefix
 | 
			
		||||
     * of the new term.
 | 
			
		||||
     *
 | 
			
		||||
     * This allows search providers to only search through the previous
 | 
			
		||||
     * result set, rather than possibly performing a full re-query.
 | 
			
		||||
     *
 | 
			
		||||
     * Similar to getInitialResultSet, the return value for this will
 | 
			
		||||
     * be ignored; use this.searchSystem.pushResults();.
 | 
			
		||||
     */
 | 
			
		||||
    getSubsearchResultSet: function(previousResults, newTerms) {
 | 
			
		||||
        throw new Error('Not implemented');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * getResultMetas:
 | 
			
		||||
     * @ids: Result identifier strings
 | 
			
		||||
     *
 | 
			
		||||
     * Call callback with array of objects with 'id', 'name', (both strings) and
 | 
			
		||||
     * 'createIcon' (function(size) returning a Clutter.Texture) properties
 | 
			
		||||
     * with the same number of members as @ids
 | 
			
		||||
     */
 | 
			
		||||
    getResultMetas: function(ids, callback) {
 | 
			
		||||
        throw new Error('Not implemented');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * createResultActor:
 | 
			
		||||
     * @resultMeta: Object with result metadata
 | 
			
		||||
     * @terms: Array of search terms, should be used for highlighting
 | 
			
		||||
     *
 | 
			
		||||
     * Search providers may optionally override this to render a
 | 
			
		||||
     * particular serch result in a custom fashion.  The default
 | 
			
		||||
     * implementation will show the icon next to the name.
 | 
			
		||||
     *
 | 
			
		||||
     * The actor should be an instance of St.Widget, with the style class
 | 
			
		||||
     * 'search-result-content'.
 | 
			
		||||
     */
 | 
			
		||||
    createResultActor: function(resultMeta, terms) {
 | 
			
		||||
        return null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * activateResult:
 | 
			
		||||
     * @id: Result identifier string
 | 
			
		||||
     *
 | 
			
		||||
     * Called when the user chooses a given result.
 | 
			
		||||
     */
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
const SearchSystem = new Lang.Class({
 | 
			
		||||
    Name: 'SearchSystem',
 | 
			
		||||
 | 
			
		||||
@@ -273,7 +96,7 @@ const SearchSystem = new Lang.Class({
 | 
			
		||||
                    results.push([provider, []]);
 | 
			
		||||
                    provider.getSubsearchResultSet(previousResults, terms);
 | 
			
		||||
                } catch (error) {
 | 
			
		||||
                    log('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
 | 
			
		||||
                    log('A ' + error.name + ' has occured in ' + provider.id + ': ' + error.message);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
@@ -283,7 +106,7 @@ const SearchSystem = new Lang.Class({
 | 
			
		||||
                    results.push([provider, []]);
 | 
			
		||||
                    provider.getInitialResultSet(terms);
 | 
			
		||||
                } catch (error) {
 | 
			
		||||
                    log('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
 | 
			
		||||
                    log('A ' + error.name + ' has occured in ' + provider.id + ': ' + error.message);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -11,10 +11,11 @@ const DND = imports.ui.dnd;
 | 
			
		||||
const IconGrid = imports.ui.iconGrid;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Overview = imports.ui.overview;
 | 
			
		||||
const Separator = imports.ui.separator;
 | 
			
		||||
const Search = imports.ui.search;
 | 
			
		||||
 | 
			
		||||
const MAX_SEARCH_RESULTS_ROWS = 1;
 | 
			
		||||
 | 
			
		||||
const MAX_LIST_SEARCH_RESULTS_ROWS = 3;
 | 
			
		||||
const MAX_GRID_SEARCH_RESULTS_ROWS = 1;
 | 
			
		||||
 | 
			
		||||
const SearchResult = new Lang.Class({
 | 
			
		||||
    Name: 'SearchResult',
 | 
			
		||||
@@ -23,33 +24,102 @@ const SearchResult = new Lang.Class({
 | 
			
		||||
        this.provider = provider;
 | 
			
		||||
        this.metaInfo = metaInfo;
 | 
			
		||||
        this.terms = terms;
 | 
			
		||||
        this.actor = new St.Button({ style_class: 'search-result',
 | 
			
		||||
                                     reactive: true,
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.Button({ reactive: true,
 | 
			
		||||
                                     can_focus: true,
 | 
			
		||||
                                     track_hover: true,
 | 
			
		||||
                                     x_align: St.Align.START,
 | 
			
		||||
                                     y_fill: true });
 | 
			
		||||
 | 
			
		||||
        this.actor._delegate = this;
 | 
			
		||||
        this._dragActorSource = null;
 | 
			
		||||
        this.actor.connect('clicked', Lang.bind(this, this.activate));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    activate: function() {
 | 
			
		||||
        this.provider.activateResult(this.metaInfo.id, this.terms);
 | 
			
		||||
        Main.overview.toggle();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setSelected: function(selected) {
 | 
			
		||||
        if (selected)
 | 
			
		||||
            this.actor.add_style_pseudo_class('selected');
 | 
			
		||||
        else
 | 
			
		||||
            this.actor.remove_style_pseudo_class('selected');
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const ListSearchResult = new Lang.Class({
 | 
			
		||||
    Name: 'ListSearchResult',
 | 
			
		||||
    Extends: SearchResult,
 | 
			
		||||
 | 
			
		||||
    ICON_SIZE: 64,
 | 
			
		||||
 | 
			
		||||
    _init: function(provider, metaInfo, terms) {
 | 
			
		||||
        this.parent(provider, metaInfo, terms);
 | 
			
		||||
 | 
			
		||||
        this.actor.style_class = 'list-search-result';
 | 
			
		||||
        this.actor.x_fill = true;
 | 
			
		||||
 | 
			
		||||
        let content = new St.BoxLayout({ style_class: 'list-search-result-content',
 | 
			
		||||
                                         vertical: false });
 | 
			
		||||
        this.actor.set_child(content);
 | 
			
		||||
 | 
			
		||||
        // An icon for, or thumbnail of, content
 | 
			
		||||
        let icon = this.metaInfo['createIcon'](this.ICON_SIZE);
 | 
			
		||||
        if (icon) {
 | 
			
		||||
            content.add(icon);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let details = new St.BoxLayout({ 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: 'list-search-result-title',
 | 
			
		||||
                                   text: this.metaInfo['name'] })
 | 
			
		||||
        details.add(title, { x_fill: false,
 | 
			
		||||
                             y_fill: false,
 | 
			
		||||
                             x_align: St.Align.START,
 | 
			
		||||
                             y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        // TODO: should highlight terms in the description here
 | 
			
		||||
        if (this.metaInfo['description']) {
 | 
			
		||||
            let description = new St.Label({ style_class: 'list-search-result-description',
 | 
			
		||||
                                             text: '"' + this.metaInfo['description'] + '"' });
 | 
			
		||||
            details.add(description, { x_fill: false,
 | 
			
		||||
                                       y_fill: false,
 | 
			
		||||
                                       x_align: St.Align.START,
 | 
			
		||||
                                       y_align: St.Align.END });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const GridSearchResult = new Lang.Class({
 | 
			
		||||
    Name: 'GridSearchResult',
 | 
			
		||||
    Extends: SearchResult,
 | 
			
		||||
 | 
			
		||||
    _init: function(provider, metaInfo, terms) {
 | 
			
		||||
        this.parent(provider, metaInfo, terms);
 | 
			
		||||
 | 
			
		||||
        this.actor.style_class = 'grid-search-result';
 | 
			
		||||
 | 
			
		||||
        let content = provider.createResultActor(metaInfo, terms);
 | 
			
		||||
        let dragSource = null;
 | 
			
		||||
 | 
			
		||||
        if (content == null) {
 | 
			
		||||
            content = new St.Bin({ style_class: 'search-result-content',
 | 
			
		||||
                                   reactive: true,
 | 
			
		||||
                                   can_focus: true,
 | 
			
		||||
                                   track_hover: true,
 | 
			
		||||
                                   accessible_role: Atk.Role.PUSH_BUTTON });
 | 
			
		||||
            content = new St.Bin();
 | 
			
		||||
            let icon = new IconGrid.BaseIcon(this.metaInfo['name'],
 | 
			
		||||
                                             { createIcon: this.metaInfo['createIcon'] });
 | 
			
		||||
            content.set_child(icon.actor);
 | 
			
		||||
            this._dragActorSource = icon.icon;
 | 
			
		||||
            content.label_actor = icon.label;
 | 
			
		||||
            dragSource = icon.icon;
 | 
			
		||||
        } else {
 | 
			
		||||
            if (content._delegate && content._delegate.getDragActorSource)
 | 
			
		||||
                this._dragActorSource = content._delegate.getDragActorSource();
 | 
			
		||||
                dragSource = content._delegate.getDragActorSource();
 | 
			
		||||
        }
 | 
			
		||||
        this._content = content;
 | 
			
		||||
        this.actor.set_child(content);
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('clicked', Lang.bind(this, this._onResultClicked));
 | 
			
		||||
        this.actor.set_child(content);
 | 
			
		||||
 | 
			
		||||
        let draggable = DND.makeDraggable(this.actor);
 | 
			
		||||
        draggable.connect('drag-begin',
 | 
			
		||||
@@ -64,32 +134,18 @@ const SearchResult = new Lang.Class({
 | 
			
		||||
                          Lang.bind(this, function() {
 | 
			
		||||
                              Main.overview.endItemDrag(this);
 | 
			
		||||
                          }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setSelected: function(selected) {
 | 
			
		||||
        if (selected)
 | 
			
		||||
            this._content.add_style_pseudo_class('selected');
 | 
			
		||||
        else
 | 
			
		||||
            this._content.remove_style_pseudo_class('selected');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    activate: function() {
 | 
			
		||||
        this.provider.activateResult(this.metaInfo.id, this.terms);
 | 
			
		||||
        Main.overview.toggle();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onResultClicked: function(actor) {
 | 
			
		||||
        this.activate();
 | 
			
		||||
        if (!dragSource)
 | 
			
		||||
            // not exactly right, but alignment problems are hard to notice
 | 
			
		||||
            dragSource = content;
 | 
			
		||||
        this._dragActorSource = dragSource;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getDragActorSource: function() {
 | 
			
		||||
        if (this._dragActorSource)
 | 
			
		||||
            return this._dragActorSource;
 | 
			
		||||
        // not exactly right, but alignment problems are hard to notice
 | 
			
		||||
        return this._content;
 | 
			
		||||
        return this._dragActorSource;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getDragActor: function(stageX, stageY) {
 | 
			
		||||
    getDragActor: function() {
 | 
			
		||||
        return this.metaInfo['createIcon'](Main.overview.dashIconSize);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -101,47 +157,48 @@ const SearchResult = new Lang.Class({
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const ListSearchResults = new Lang.Class({
 | 
			
		||||
    Name: 'ListSearchResults',
 | 
			
		||||
 | 
			
		||||
const GridSearchResults = new Lang.Class({
 | 
			
		||||
    Name: 'GridSearchResults',
 | 
			
		||||
    Extends: Search.SearchResultDisplay,
 | 
			
		||||
    _init: function(provider) {
 | 
			
		||||
        this.provider = provider;
 | 
			
		||||
 | 
			
		||||
    _init: function(provider, grid) {
 | 
			
		||||
        this.parent(provider);
 | 
			
		||||
 | 
			
		||||
        this._grid = grid || new IconGrid.IconGrid({ rowLimit: MAX_SEARCH_RESULTS_ROWS,
 | 
			
		||||
                                                     xAlign: St.Align.START });
 | 
			
		||||
        this.actor = new St.Bin({ x_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        this.actor.set_child(this._grid.actor);
 | 
			
		||||
        this._width = 0;
 | 
			
		||||
        this.actor.connect('notify::width', Lang.bind(this, function() {
 | 
			
		||||
            this._width = this.actor.width;
 | 
			
		||||
            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.actor = new St.BoxLayout({ style_class: 'search-section-content' });
 | 
			
		||||
        this.providerIcon = new ProviderIcon(provider);
 | 
			
		||||
        this.providerIcon.connect('clicked', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                provider.launchSearch(this._terms);
 | 
			
		||||
                Main.overview.toggle();
 | 
			
		||||
            }));
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this.actor.add(this.providerIcon, { x_fill: false,
 | 
			
		||||
                                            y_fill: false,
 | 
			
		||||
                                            x_align: St.Align.START,
 | 
			
		||||
                                            y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        this._content = new St.BoxLayout({ style_class: 'list-search-results',
 | 
			
		||||
                                           vertical: true });
 | 
			
		||||
        this.actor.add_actor(this._content);
 | 
			
		||||
 | 
			
		||||
        this._notDisplayedResult = [];
 | 
			
		||||
        this._terms = [];
 | 
			
		||||
        this._pendingClear = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getResultsForDisplay: function() {
 | 
			
		||||
        let alreadyVisible = this._pendingClear ? 0 : this._grid.visibleItemsCount();
 | 
			
		||||
        let canDisplay = this._grid.childrenInRow(this._width) * this._grid.getRowLimit()
 | 
			
		||||
                         - alreadyVisible;
 | 
			
		||||
        let alreadyVisible = this._pendingClear ? 0 : this.getVisibleResultCount();
 | 
			
		||||
        let canDisplay = MAX_LIST_SEARCH_RESULTS_ROWS - alreadyVisible;
 | 
			
		||||
 | 
			
		||||
        let numResults = Math.min(this._notDisplayedResult.length, canDisplay);
 | 
			
		||||
 | 
			
		||||
        return this._notDisplayedResult.splice(0, numResults);
 | 
			
		||||
        let newResults = this._notDisplayedResult.splice(0, canDisplay);
 | 
			
		||||
        return newResults;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getVisibleResultCount: function() {
 | 
			
		||||
        return this._grid.visibleItemsCount();
 | 
			
		||||
        return this._content.get_n_children();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hasMoreResults: function() {
 | 
			
		||||
        return this._notDisplayedResult.length > 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setResults: function(results, terms) {
 | 
			
		||||
@@ -153,7 +210,68 @@ 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 ListSearchResult(this.provider, metas[i], this._terms);
 | 
			
		||||
            this._content.add_actor(display.actor);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    clear: function () {
 | 
			
		||||
        this._content.destroy_all_children();
 | 
			
		||||
        this._pendingClear = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getFirstResult: function() {
 | 
			
		||||
        if (this.getVisibleResultCount() > 0)
 | 
			
		||||
            return this._content.get_child_at_index(0)._delegate;
 | 
			
		||||
        else
 | 
			
		||||
            return null;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const GridSearchResults = new Lang.Class({
 | 
			
		||||
    Name: 'GridSearchResults',
 | 
			
		||||
 | 
			
		||||
    _init: function(provider) {
 | 
			
		||||
        this.provider = provider;
 | 
			
		||||
 | 
			
		||||
        this._grid = new IconGrid.IconGrid({ rowLimit: MAX_GRID_SEARCH_RESULTS_ROWS,
 | 
			
		||||
                                             xAlign: St.Align.START });
 | 
			
		||||
        this.actor = new St.Bin({ x_align: St.Align.MIDDLE });
 | 
			
		||||
 | 
			
		||||
        this.actor.set_child(this._grid.actor);
 | 
			
		||||
 | 
			
		||||
        this._notDisplayedResult = [];
 | 
			
		||||
        this._terms = [];
 | 
			
		||||
        this._pendingClear = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getResultsForDisplay: function() {
 | 
			
		||||
        let alreadyVisible = this._pendingClear ? 0 : this._grid.visibleItemsCount();
 | 
			
		||||
        let canDisplay = this._grid.childrenInRow(this.actor.width) * this._grid.getRowLimit()
 | 
			
		||||
                         - alreadyVisible;
 | 
			
		||||
 | 
			
		||||
        let newResults = this._notDisplayedResult.splice(0, canDisplay);
 | 
			
		||||
        return newResults;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getVisibleResultCount: function() {
 | 
			
		||||
        return this._grid.visibleItemsCount();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    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 GridSearchResult(this.provider, metas[i], this._terms);
 | 
			
		||||
            this._grid.addItem(display.actor);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
@@ -224,19 +342,27 @@ 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 providerIcon = null;
 | 
			
		||||
        let resultDisplay = null;
 | 
			
		||||
 | 
			
		||||
        let resultDisplayBin = new St.Bin({ style_class: 'search-section-results',
 | 
			
		||||
        if (provider.appInfo) {
 | 
			
		||||
            resultDisplay = new ListSearchResults(provider);
 | 
			
		||||
            providerIcon = resultDisplay.providerIcon;
 | 
			
		||||
        } else {
 | 
			
		||||
            resultDisplay = new GridSearchResults(provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let resultDisplayBin = new St.Bin({ child: resultDisplay.actor,
 | 
			
		||||
                                            x_fill: true,
 | 
			
		||||
                                            y_fill: true });
 | 
			
		||||
        providerBox.add(resultDisplayBin, { expand: true });
 | 
			
		||||
        let resultDisplay = new GridSearchResults(provider);
 | 
			
		||||
        resultDisplayBin.set_child(resultDisplay.actor);
 | 
			
		||||
 | 
			
		||||
        let separator = new Separator.HorizontalSeparator({ style_class: 'search-section-separator' });
 | 
			
		||||
        providerBox.add(separator.actor);
 | 
			
		||||
 | 
			
		||||
        this._providerMeta.push({ provider: provider,
 | 
			
		||||
                                  actor: providerBox,
 | 
			
		||||
                                  icon: providerIcon,
 | 
			
		||||
                                  resultDisplay: resultDisplay });
 | 
			
		||||
        this._content.add(providerBox);
 | 
			
		||||
    },
 | 
			
		||||
@@ -343,6 +469,11 @@ 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.canLaunchSearch;
 | 
			
		||||
 | 
			
		||||
            provider.getResultMetas(results, Lang.bind(this, function(metas) {
 | 
			
		||||
                this._clearDisplayForProvider(provider);
 | 
			
		||||
                meta.actor.show();
 | 
			
		||||
@@ -388,10 +519,37 @@ const SearchResults = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        let from = this._defaultResult ? this._defaultResult.actor : null;
 | 
			
		||||
        this.actor.navigate_focus(from, direction, false);
 | 
			
		||||
        if (this._defaultResult) {
 | 
			
		||||
            // The default result appears focused, so navigate directly to the
 | 
			
		||||
            // next result.
 | 
			
		||||
            this.actor.navigate_focus(global.stage.key_focus, direction, false);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const ProviderIcon = new Lang.Class({
 | 
			
		||||
    Name: 'ProviderIcon',
 | 
			
		||||
    Extends: St.Button,
 | 
			
		||||
 | 
			
		||||
    PROVIDER_ICON_SIZE: 48,
 | 
			
		||||
 | 
			
		||||
    _init: function(provider) {
 | 
			
		||||
        this.provider = provider;
 | 
			
		||||
        this.parent({ style_class: 'search-provider-icon',
 | 
			
		||||
                      reactive: true,
 | 
			
		||||
                      can_focus: true,
 | 
			
		||||
                      track_hover: true });
 | 
			
		||||
 | 
			
		||||
        this._content = new St.Widget({ layout_manager: new Clutter.BinLayout() });
 | 
			
		||||
        this.set_child(this._content);
 | 
			
		||||
 | 
			
		||||
        let rtl = (this.get_text_direction() == Clutter.TextDirection.RTL);
 | 
			
		||||
 | 
			
		||||
        this.moreIcon = new St.Widget({ style_class: 'search-provider-icon-more',
 | 
			
		||||
                                        visible: false,
 | 
			
		||||
                                        x_align: rtl ? Clutter.ActorAlign.START : Clutter.ActorAlign.END,
 | 
			
		||||
                                        y_align: Clutter.ActorAlign.END,
 | 
			
		||||
                                        x_expand: true,
 | 
			
		||||
                                        y_expand: true });
 | 
			
		||||
 | 
			
		||||
        let icon = new St.Icon({ icon_size: this.PROVIDER_ICON_SIZE,
 | 
			
		||||
                                 gicon: provider.appInfo.get_icon() });
 | 
			
		||||
        this._content.add_actor(icon);
 | 
			
		||||
        this._content.add_actor(this.moreIcon);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										34
									
								
								js/ui/separator.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								js/ui/separator.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Cairo = imports.cairo;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const HorizontalSeparator = new Lang.Class({
 | 
			
		||||
    Name: 'HorizontalSeparator',
 | 
			
		||||
 | 
			
		||||
    _init: function (params) {
 | 
			
		||||
        this.actor = new St.DrawingArea(params);
 | 
			
		||||
        this.actor.connect('repaint', Lang.bind(this, this._onRepaint));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onRepaint: function(area) {
 | 
			
		||||
        let cr = area.get_context();
 | 
			
		||||
        let themeNode = area.get_theme_node();
 | 
			
		||||
        let [width, height] = area.get_surface_size();
 | 
			
		||||
        let margin = themeNode.get_length('-margin-horizontal');
 | 
			
		||||
        let gradientHeight = themeNode.get_length('-gradient-height');
 | 
			
		||||
        let startColor = themeNode.get_color('-gradient-start');
 | 
			
		||||
        let endColor = themeNode.get_color('-gradient-end');
 | 
			
		||||
 | 
			
		||||
        let gradientWidth = (width - margin * 2);
 | 
			
		||||
        let gradientOffset = (height - gradientHeight) / 2;
 | 
			
		||||
        let pattern = new Cairo.LinearGradient(margin, gradientOffset, width - margin, gradientOffset + gradientHeight);
 | 
			
		||||
        pattern.addColorStopRGBA(0, startColor.red / 255, startColor.green / 255, startColor.blue / 255, startColor.alpha / 255);
 | 
			
		||||
        pattern.addColorStopRGBA(0.5, endColor.red / 255, endColor.green / 255, endColor.blue / 255, endColor.alpha / 255);
 | 
			
		||||
        pattern.addColorStopRGBA(1, startColor.red / 255, startColor.green / 255, startColor.blue / 255, startColor.alpha / 255);
 | 
			
		||||
        cr.setSource(pattern);
 | 
			
		||||
        cr.rectangle(margin, gradientOffset, gradientWidth, gradientHeight);
 | 
			
		||||
        cr.fill();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
@@ -52,8 +52,7 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._activePage = null;
 | 
			
		||||
 | 
			
		||||
        this.active = false;
 | 
			
		||||
        this._searchPending = false;
 | 
			
		||||
        this._searchActive = false;
 | 
			
		||||
        this._searchTimeoutId = 0;
 | 
			
		||||
 | 
			
		||||
        this._searchSystem = new Search.SearchSystem();
 | 
			
		||||
@@ -119,9 +118,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 () {
 | 
			
		||||
@@ -138,17 +134,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 });
 | 
			
		||||
 | 
			
		||||
        Main.wm.addKeybinding('toggle-application-view',
 | 
			
		||||
                              new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
 | 
			
		||||
                              Meta.KeyBindingFlags.NONE,
 | 
			
		||||
@@ -223,11 +208,16 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
                             });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.emit('before-page-change');
 | 
			
		||||
        page.show();
 | 
			
		||||
        Tweener.addTween(page,
 | 
			
		||||
                         { opacity: 255,
 | 
			
		||||
                           time: 0.1,
 | 
			
		||||
                           transition: 'easeOutQuad'
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                           onComplete: Lang.bind(this,
 | 
			
		||||
                               function() {
 | 
			
		||||
                                   this.emit('after-page-change');
 | 
			
		||||
                               })
 | 
			
		||||
                         });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -237,7 +227,7 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onShowAppsButtonToggled: function() {
 | 
			
		||||
        if (this.active)
 | 
			
		||||
        if (this._searchActive)
 | 
			
		||||
            this.reset();
 | 
			
		||||
        else
 | 
			
		||||
            this._showPage(this._showAppsButton.checked ? this._appsPage
 | 
			
		||||
@@ -258,7 +248,7 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
        let symbol = event.get_key_symbol();
 | 
			
		||||
 | 
			
		||||
        if (symbol == Clutter.Escape) {
 | 
			
		||||
            if (this.active)
 | 
			
		||||
            if (this._searchActive)
 | 
			
		||||
                this.reset();
 | 
			
		||||
            else if (this._showAppsButton.checked)
 | 
			
		||||
                this._resetShowAppsButton();
 | 
			
		||||
@@ -266,9 +256,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._searchActive)) {
 | 
			
		||||
            this.startSearch(event);
 | 
			
		||||
        } else if (!this.active) {
 | 
			
		||||
        } else if (!this._searchActive) {
 | 
			
		||||
            if (symbol == Clutter.Tab || symbol == Clutter.Down) {
 | 
			
		||||
                this._activePage.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
 | 
			
		||||
                return true;
 | 
			
		||||
@@ -342,13 +332,15 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onTextChanged: function (se, prop) {
 | 
			
		||||
        let searchPreviouslyActive = this.active;
 | 
			
		||||
        this.active = this._entry.get_text() != '';
 | 
			
		||||
        this._searchPending = this.active && !searchPreviouslyActive;
 | 
			
		||||
        if (this._searchPending) {
 | 
			
		||||
        let searchPreviouslyActive = this._searchActive;
 | 
			
		||||
        this._searchActive = this._entry.get_text() != '';
 | 
			
		||||
 | 
			
		||||
        let startSearch = this._searchActive && !searchPreviouslyActive;
 | 
			
		||||
        if (startSearch) {
 | 
			
		||||
            this._searchResults.startingSearch();
 | 
			
		||||
        }
 | 
			
		||||
        if (this.active) {
 | 
			
		||||
 | 
			
		||||
        if (this._searchActive) {
 | 
			
		||||
            this._entry.set_secondary_icon(this._activeIcon);
 | 
			
		||||
 | 
			
		||||
            if (this._iconClickedId == 0) {
 | 
			
		||||
@@ -364,14 +356,14 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
            this._entry.set_secondary_icon(this._inactiveIcon);
 | 
			
		||||
            this._searchCancelled();
 | 
			
		||||
        }
 | 
			
		||||
        if (!this.active) {
 | 
			
		||||
 | 
			
		||||
            if (this._searchTimeoutId > 0) {
 | 
			
		||||
                Mainloop.source_remove(this._searchTimeoutId);
 | 
			
		||||
                this._searchTimeoutId = 0;
 | 
			
		||||
            }
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._searchTimeoutId > 0)
 | 
			
		||||
            return;
 | 
			
		||||
        this._searchTimeoutId = Mainloop.timeout_add(150, Lang.bind(this, this._doSearch));
 | 
			
		||||
@@ -393,7 +385,7 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
            }
 | 
			
		||||
            this._searchResults.activateDefault();
 | 
			
		||||
            return true;
 | 
			
		||||
        } else if (this.active) {
 | 
			
		||||
        } else if (this._searchActive) {
 | 
			
		||||
            let arrowNext, nextDirection;
 | 
			
		||||
            if (entry.get_text_direction() == Clutter.TextDirection.RTL) {
 | 
			
		||||
                arrowNext = Clutter.Left;
 | 
			
		||||
@@ -467,6 +459,10 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
        RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, this.addSearchProvider));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getAppsActive: function() {
 | 
			
		||||
        return this._showAppsButton.checked;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addSearchProvider: function(provider) {
 | 
			
		||||
        if (!this._shouldUseSearchProvider(provider))
 | 
			
		||||
            return;
 | 
			
		||||
@@ -478,6 +474,10 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
    removeSearchProvider: function(provider) {
 | 
			
		||||
        this._searchSystem.unregisterProvider(provider);
 | 
			
		||||
        this._searchResults.destroyProviderMeta(provider);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getSearchActive: function() {
 | 
			
		||||
        return this._searchActive;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(ViewSelector.prototype);
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,6 @@ const IconGrid = imports.ui.iconGrid;
 | 
			
		||||
const Layout = imports.ui.layout;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Panel = imports.ui.panel;
 | 
			
		||||
const Search = imports.ui.search;
 | 
			
		||||
 | 
			
		||||
// we could make these gsettings
 | 
			
		||||
const FISH_NAME = 'wanda';
 | 
			
		||||
@@ -71,8 +70,7 @@ const WandaIconBin = new Lang.Class({
 | 
			
		||||
    Name: 'WandaIconBin',
 | 
			
		||||
 | 
			
		||||
    _init: function(fish, label, params) {
 | 
			
		||||
        this.actor = new St.Bin({ style_class: 'search-result-content',
 | 
			
		||||
                                  reactive: true,
 | 
			
		||||
        this.actor = new St.Bin({ reactive: true,
 | 
			
		||||
                                  track_hover: true });
 | 
			
		||||
        this.icon = new WandaIcon(fish, label, params);
 | 
			
		||||
 | 
			
		||||
@@ -133,10 +131,9 @@ function capitalize(str) {
 | 
			
		||||
 | 
			
		||||
const WandaSearchProvider = new Lang.Class({
 | 
			
		||||
    Name: 'WandaSearchProvider',
 | 
			
		||||
    Extends: Search.SearchProvider,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.parent(_("Your favorite Easter Egg"));
 | 
			
		||||
        this.id = 'wanda';
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getResultMetas: function(fish, callback) {
 | 
			
		||||
 
 | 
			
		||||
@@ -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 Lang = imports.lang;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
@@ -27,6 +28,8 @@ const WORKSPACE_CUT_SIZE = 10;
 | 
			
		||||
 | 
			
		||||
const WORKSPACE_KEEP_ALIVE_TIME = 100;
 | 
			
		||||
 | 
			
		||||
const OVERRIDE_SCHEMA = 'org.gnome.shell.overrides';
 | 
			
		||||
 | 
			
		||||
const WindowClone = new Lang.Class({
 | 
			
		||||
    Name: 'WindowClone',
 | 
			
		||||
 | 
			
		||||
@@ -493,6 +496,8 @@ const ThumbnailsBox = new Lang.Class({
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.actor = new Shell.GenericContainer({ reactive: true,
 | 
			
		||||
                                                  style_class: 'workspace-thumbnails',
 | 
			
		||||
                                                  can_focus: true,
 | 
			
		||||
                                                  track_hover: 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));
 | 
			
		||||
@@ -521,6 +526,7 @@ const ThumbnailsBox = new Lang.Class({
 | 
			
		||||
        this._indicator = indicator;
 | 
			
		||||
        this.actor.add_actor(indicator);
 | 
			
		||||
 | 
			
		||||
        this._inDrag = false;
 | 
			
		||||
        this._dropWorkspace = -1;
 | 
			
		||||
        this._dropPlaceholderPos = -1;
 | 
			
		||||
        this._dropPlaceholder = new St.Bin({ style_class: 'placeholder' });
 | 
			
		||||
@@ -541,6 +547,18 @@ 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.layoutManager.connect('monitors-changed', Lang.bind(this, this._updateTranslation));
 | 
			
		||||
        this.actor.connect('notify::hover', Lang.bind(this, this._updateTranslation));
 | 
			
		||||
 | 
			
		||||
        Main.overview.connect('showing',
 | 
			
		||||
                              Lang.bind(this, this._createThumbnails));
 | 
			
		||||
        Main.overview.connect('hidden',
 | 
			
		||||
                              Lang.bind(this, this._destroyThumbnails));
 | 
			
		||||
 | 
			
		||||
        Main.overview.connect('item-drag-begin',
 | 
			
		||||
                              Lang.bind(this, this._onDragBegin));
 | 
			
		||||
@@ -554,6 +572,50 @@ const ThumbnailsBox = new Lang.Class({
 | 
			
		||||
                              Lang.bind(this, this._onDragEnd));
 | 
			
		||||
        Main.overview.connect('window-drag-cancelled',
 | 
			
		||||
                              Lang.bind(this, this._onDragCancelled));
 | 
			
		||||
 | 
			
		||||
        this._settings = new Gio.Settings({ schema: OVERRIDE_SCHEMA });
 | 
			
		||||
        this._settings.connect('changed::dynamic-workspaces',
 | 
			
		||||
            Lang.bind(this, this._updateSwitcherVisibility));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getInitialTranslation: function() {
 | 
			
		||||
        // Always show the pager when hover, during a drag, or if workspaces are
 | 
			
		||||
        // actually used, e.g. there are windows on more than one
 | 
			
		||||
        let alwaysZoomOut = this.actor.hover || this._inDrag || global.screen.n_workspaces > 2;
 | 
			
		||||
 | 
			
		||||
        if (!alwaysZoomOut) {
 | 
			
		||||
            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) {
 | 
			
		||||
                    alwaysZoomOut = true;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (alwaysZoomOut)
 | 
			
		||||
            return 0;
 | 
			
		||||
 | 
			
		||||
        let visibleWidth = this.actor.get_theme_node().get_length('visible-width');
 | 
			
		||||
        let rtl = (this.actor.get_text_direction() == Clutter.TextDirection.RTL);
 | 
			
		||||
        return rtl ? (visibleWidth - this.actor.width) : (this.actor.width - visibleWidth);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateTranslation: function() {
 | 
			
		||||
        Tweener.addTween(this, { slideX: this._getInitialTranslation(),
 | 
			
		||||
                                 time: SLIDE_ANIMATION_TIME,
 | 
			
		||||
                                 transition: 'easeOutQuad' });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateSwitcherVisibility: function() {
 | 
			
		||||
        this.actor.visible =
 | 
			
		||||
            this._settings.get_boolean('dynamic-workspaces') ||
 | 
			
		||||
                global.screen.n_workspaces > 1;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onButtonRelease: function(actor, event) {
 | 
			
		||||
@@ -573,6 +635,9 @@ const ThumbnailsBox = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDragBegin: function() {
 | 
			
		||||
        this._inDrag = true;
 | 
			
		||||
        this._updateTranslation();
 | 
			
		||||
 | 
			
		||||
        this._dragCancelled = false;
 | 
			
		||||
        this._dragMonitor = {
 | 
			
		||||
            dragMotion: Lang.bind(this, this._onDragMotion)
 | 
			
		||||
@@ -595,6 +660,8 @@ const ThumbnailsBox = new Lang.Class({
 | 
			
		||||
    _endDrag: function() {
 | 
			
		||||
        this._clearDragPlaceholder();
 | 
			
		||||
        DND.removeDragMonitor(this._dragMonitor);
 | 
			
		||||
        this._inDrag = false;
 | 
			
		||||
        this._updateTranslation();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDragMotion: function(dragEvent) {
 | 
			
		||||
@@ -719,10 +786,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;
 | 
			
		||||
@@ -744,19 +817,101 @@ const ThumbnailsBox = new Lang.Class({
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        this.addThumbnails(0, global.screen.n_workspaces);
 | 
			
		||||
 | 
			
		||||
        // reset any translation and make sure the actor is visible when
 | 
			
		||||
        // entering the overview
 | 
			
		||||
        this.slideX = this._getInitialTranslation();
 | 
			
		||||
        this.actor.show();
 | 
			
		||||
 | 
			
		||||
        this._updateSwitcherVisibility();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    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 = [];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _computeTranslation: function() {
 | 
			
		||||
        let rtl = (this.actor.get_text_direction() == Clutter.TextDirection.RTL);
 | 
			
		||||
 | 
			
		||||
        if (rtl)
 | 
			
		||||
            return - this.actor.width;
 | 
			
		||||
        else
 | 
			
		||||
            return this.actor.width;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get slideX() {
 | 
			
		||||
        return this._slideX;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    set slideX(value) {
 | 
			
		||||
        this._slideX = value;
 | 
			
		||||
        this.actor.translation_x = this._slideX;
 | 
			
		||||
 | 
			
		||||
        if (this._slideX > 0) {
 | 
			
		||||
            let rect = new Clutter.Rect();
 | 
			
		||||
            rect.size.width = this._background.width - this._slideX;
 | 
			
		||||
            rect.size.height = this._background.height;
 | 
			
		||||
            this.actor.clip_rect = rect;
 | 
			
		||||
        } else {
 | 
			
		||||
            this.actor.clip_rect = null;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    show: function() {
 | 
			
		||||
        this.actor.show();
 | 
			
		||||
        this._updateTranslation();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hide: function() {
 | 
			
		||||
        let hiddenX = this._computeTranslation();
 | 
			
		||||
        Tweener.addTween(this, { slideX: 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);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._updateSwitcherVisibility();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addThumbnails: function(start, count) {
 | 
			
		||||
        for (let k = start; k < start + count; k++) {
 | 
			
		||||
            let metaWorkspace = global.screen.get_workspace_by_index(k);
 | 
			
		||||
@@ -802,7 +957,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);
 | 
			
		||||
    },
 | 
			
		||||
@@ -1157,5 +1312,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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -23,8 +23,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',
 | 
			
		||||
@@ -439,9 +437,7 @@ const WorkspacesDisplay = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.actor = new Shell.GenericContainer();
 | 
			
		||||
        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('allocate', Lang.bind(this, this._updateWorkspacesGeometry));
 | 
			
		||||
        this.actor.connect('parent-set', Lang.bind(this, this._parentSet));
 | 
			
		||||
        this.actor.set_clip_to_allocation(true);
 | 
			
		||||
 | 
			
		||||
@@ -472,25 +468,8 @@ const WorkspacesDisplay = new Lang.Class({
 | 
			
		||||
        Main.overview.addAction(panAction);
 | 
			
		||||
        this.actor.bind_property('mapped', panAction, 'enabled', GObject.BindingFlags.SYNC_CREATE);
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
 | 
			
		||||
@@ -503,26 +482,6 @@ 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));
 | 
			
		||||
 | 
			
		||||
@@ -537,10 +496,6 @@ const WorkspacesDisplay = new Lang.Class({
 | 
			
		||||
        this._notifyOpacityId = 0;
 | 
			
		||||
        this._swipeScrollBeginId = 0;
 | 
			
		||||
        this._swipeScrollEndId = 0;
 | 
			
		||||
 | 
			
		||||
        this._settings = new Gio.Settings({ schema: OVERRIDE_SCHEMA });
 | 
			
		||||
        this._settings.connect('changed::dynamic-workspaces',
 | 
			
		||||
            Lang.bind(this, this._updateSwitcherVisibility));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onPan: function(action) {
 | 
			
		||||
@@ -550,38 +505,11 @@ const WorkspacesDisplay = new Lang.Class({
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateSwitcherVisibility: function() {
 | 
			
		||||
        this._thumbnailsBox.actor.visible =
 | 
			
		||||
            this._settings.get_boolean('dynamic-workspaces') ||
 | 
			
		||||
                global.screen.n_workspaces > 1;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    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)
 | 
			
		||||
@@ -602,8 +530,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() {
 | 
			
		||||
@@ -613,14 +539,8 @@ 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) {
 | 
			
		||||
@@ -736,76 +656,6 @@ 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();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _parentSet: function(actor, oldParent) {
 | 
			
		||||
        if (oldParent && this._notifyOpacityId)
 | 
			
		||||
            oldParent.disconnect(this._notifyOpacityId);
 | 
			
		||||
@@ -842,24 +692,19 @@ 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 overviewSpacing = Main.overview._spacing;
 | 
			
		||||
        let widthAdjust = this._zoomOut ? controlsNatural : controlsVisible;
 | 
			
		||||
        widthAdjust += overviewSpacing;
 | 
			
		||||
        width -= widthAdjust;
 | 
			
		||||
        width -= overviewSpacing;
 | 
			
		||||
        if (rtl)
 | 
			
		||||
            x += widthAdjust;
 | 
			
		||||
            x += overviewSpacing;
 | 
			
		||||
 | 
			
		||||
        let monitors = Main.layoutManager.monitors;
 | 
			
		||||
        let m = 0;
 | 
			
		||||
@@ -883,25 +728,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;
 | 
			
		||||
 | 
			
		||||
@@ -926,8 +758,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)
 | 
			
		||||
@@ -950,43 +780,11 @@ const WorkspacesDisplay = new Lang.Class({
 | 
			
		||||
                    lostWorkspaces[l].destroy();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this._thumbnailsBox.removeThumbnails(removedIndex, removedNum);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < this._workspacesViews.length; i++)
 | 
			
		||||
            this._workspacesViews[i].updateWorkspaces(oldNumWorkspaces,
 | 
			
		||||
                                                      newNumWorkspaces);
 | 
			
		||||
        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() {
 | 
			
		||||
@@ -1004,33 +802,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