Compare commits
	
		
			27 Commits
		
	
	
		
			3.23.91
			...
			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