diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index ed04a4663..5e2b00085 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -257,6 +257,11 @@ StTooltip StLabel { /* Overview */ +#overview { + spacing: 12px; + background-color: rgba(0,0,0,0.6); +} + .workspaces-view { color: white; spacing: 25px; @@ -336,10 +341,6 @@ StTooltip StLabel { width: 60px; } -#dashSections { - spacing: 12px; -} - #viewSelector { spacing: 16px; } diff --git a/js/ui/overview.js b/js/ui/overview.js index 6c351fd52..19a24ed51 100644 --- a/js/ui/overview.js +++ b/js/ui/overview.js @@ -9,76 +9,27 @@ const St = imports.gi.St; const Gettext = imports.gettext.domain('gnome-shell'); const _ = Gettext.gettext; +const AppDisplay = imports.ui.appDisplay; +const Dash = imports.ui.dash; +const DocDisplay = imports.ui.docDisplay; const GenericDisplay = imports.ui.genericDisplay; const Lightbox = imports.ui.lightbox; const Main = imports.ui.main; const MessageTray = imports.ui.messageTray; const Panel = imports.ui.panel; -const Dash = imports.ui.dash; +const PlaceDisplay = imports.ui.placeDisplay; const Tweener = imports.ui.tweener; +const ViewSelector = imports.ui.viewSelector; const WorkspacesView = imports.ui.workspacesView; // Time for initial animation going into Overview mode const ANIMATION_TIME = 0.25; -// Time for pane menus to fade in/out -const PANE_FADE_TIME = 0.1; - -// We divide the screen into a grid of rows and columns, which we use -// to help us position the Overview components, such as the side panel -// that lists applications and documents, the workspaces display, and -// the button for adding additional workspaces. -// In the regular mode, the side panel takes up one column on the left, -// and the workspaces display takes up the remaining columns. -// In the expanded side panel display mode, the side panel takes up two -// columns, and the workspaces display slides all the way to the right, -// being visible only in the last quarter of the right-most column. -// In the future, this mode will have more components, such as a display -// of documents which were recently opened with a given application, which -// will take up the remaining sections of the display. - -const WIDE_SCREEN_CUT_OFF_RATIO = 1.4; -// A common netbook resolution is 1024x600, which trips the widescreen -// ratio. However that leaves way too few pixels for the dash. So -// just treat this as a regular screen. -const WIDE_SCREEN_MINIMUM_HEIGHT = 768; - -const COLUMNS_REGULAR_SCREEN = 4; -const ROWS_REGULAR_SCREEN = 8; -const COLUMNS_WIDE_SCREEN = 5; -const ROWS_WIDE_SCREEN = 10; - -const DEFAULT_PADDING = 4; - -// Padding around workspace grid / Spacing between Dash and Workspaces -const WORKSPACE_GRID_PADDING = 12; - -const COLUMNS_FOR_WORKSPACES_REGULAR_SCREEN = 3; -const ROWS_FOR_WORKSPACES_REGULAR_SCREEN = 6; - -const COLUMNS_FOR_WORKSPACES_WIDE_SCREEN = 4; -const ROWS_FOR_WORKSPACES_WIDE_SCREEN = 8; - -// A multi-state; PENDING is used during animations -const STATE_ACTIVE = true; -const STATE_PENDING_INACTIVE = false; -const STATE_INACTIVE = false; - -const SHADOW_COLOR = new Clutter.Color(); -SHADOW_COLOR.from_pixel(0x00000033); -const TRANSPARENT_COLOR = new Clutter.Color(); -TRANSPARENT_COLOR.from_pixel(0x00000000); - -const SHADOW_WIDTH = 6; - -const NUMBER_OF_SECTIONS_IN_SEARCH = 2; +// We split the screen vertically between the dash and the view selector. +const DASH_SPLIT_FRACTION = 0.1; const SHELL_INFO_HIDE_TIMEOUT = 10; -let wideScreen = false; -let displayGridColumnWidth = null; -let displayGridRowHeight = null; - function Source() { this._init(); } @@ -182,8 +133,19 @@ Overview.prototype = { this._desktopFade = new St.Bin(); global.overlay_group.add_actor(this._desktopFade); + this._spacing = 0; + this._group = new St.Group({ name: 'overview' }); this._group._delegate = this; + this._group.connect('style-changed', + Lang.bind(this, function() { + let node = this._group.get_theme_node(); + let spacing = node.get_length('spacing'); + if (spacing != this._spacing) { + this._spacing = spacing; + this.relayout(); + } + })); this.shellInfo = new ShellInfo(); @@ -193,10 +155,6 @@ Overview.prototype = { this.animationInProgress = false; this._hideInProgress = false; - this._recalculateGridSizes(); - - this._activeDisplayPane = null; - // During transitions, we raise this to the top to avoid having the overview // area be reactive; it causes too many issues such as double clicks on // Dash elements, or mouseover handlers in the workspaces. @@ -205,31 +163,30 @@ Overview.prototype = { this._group.add_actor(this._coverPane); this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return true; })); - // Similar to the cover pane but used for dialogs ("panes"); see the comments - // in addPane below. - this._transparentBackground = new Clutter.Rectangle({ opacity: 0, - reactive: true }); - this._group.add_actor(this._transparentBackground); this._group.hide(); global.overlay_group.add_actor(this._group); + this.viewSelector = new ViewSelector.ViewSelector(); + this._group.add_actor(this.viewSelector.actor); + + this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay(); + this.viewSelector.addViewTab("Windows", this._workspacesDisplay.actor); + + let appView = new AppDisplay.AllAppDisplay(); + this.viewSelector.addViewTab("Applications", appView.actor); + + // Default search providers + this.viewSelector.addSearchProvider(new AppDisplay.AppSearchProvider()); + this.viewSelector.addSearchProvider(new AppDisplay.PrefsSearchProvider()); + this.viewSelector.addSearchProvider(new PlaceDisplay.PlaceSearchProvider()); + this.viewSelector.addSearchProvider(new DocDisplay.DocSearchProvider()); + // TODO - recalculate everything when desktop size changes this._dash = new Dash.Dash(); this._group.add_actor(this._dash.actor); - - // Container to hold popup pane chrome. - this._paneContainer = new St.BoxLayout({ style_class: 'overview-pane' }); - // Note here we explicitly don't set the paneContainer to be reactive yet; that's done - // inside the notify::visible handler on panes. - this._paneContainer.connect('button-release-event', Lang.bind(this, function(background) { - this._activeDisplayPane.close(); - return true; - })); - this._group.add_actor(this._paneContainer); - - this._transparentBackground.lower_bottom(); - this._paneContainer.hide(); + this._dash.actor.add_constraint(this.viewSelector.constrainY); + this._dash.actor.add_constraint(this.viewSelector.constrainHeight); this._coverPane.lower_bottom(); @@ -250,127 +207,39 @@ Overview.prototype = { return clone; }, - _recalculateGridSizes: function () { - let primary = global.get_primary_monitor(); - wideScreen = (primary.width/primary.height > WIDE_SCREEN_CUT_OFF_RATIO) && - (primary.height >= WIDE_SCREEN_MINIMUM_HEIGHT); - - // We divide the screen into an imaginary grid which helps us determine the layout of - // different visual components. - if (wideScreen) { - displayGridColumnWidth = Math.floor(primary.width / COLUMNS_WIDE_SCREEN); - displayGridRowHeight = Math.floor(primary.height / ROWS_WIDE_SCREEN); - } else { - displayGridColumnWidth = Math.floor(primary.width / COLUMNS_REGULAR_SCREEN); - displayGridRowHeight = Math.floor(primary.height / ROWS_REGULAR_SCREEN); - } - }, - relayout: function () { let primary = global.get_primary_monitor(); let rtl = (St.Widget.get_default_direction () == St.TextDirection.RTL); - this._recalculateGridSizes(); - - this._group.set_position(primary.x, primary.y); - this._group.set_size(primary.width, primary.height); - let contentY = Panel.PANEL_HEIGHT; - let contentHeight = primary.height - contentY; + let contentHeight = primary.height - contentY - Main.messageTray.actor.height; this._coverPane.set_position(0, contentY); this._coverPane.set_size(primary.width, contentHeight); - let workspaceColumnsUsed = wideScreen ? COLUMNS_FOR_WORKSPACES_WIDE_SCREEN : COLUMNS_FOR_WORKSPACES_REGULAR_SCREEN; - let workspaceRowsUsed = wideScreen ? ROWS_FOR_WORKSPACES_WIDE_SCREEN : ROWS_FOR_WORKSPACES_REGULAR_SCREEN; - - this._workspacesWidth = displayGridColumnWidth * workspaceColumnsUsed - - WORKSPACE_GRID_PADDING * 2; - // We scale the vertical padding by (primary.height / primary.width) - // so that the workspace preserves its aspect ratio. - this._workspacesHeight = Math.floor(displayGridRowHeight * workspaceRowsUsed - - WORKSPACE_GRID_PADDING * (primary.height / primary.width) * 2); + let viewWidth = (1.0 - DASH_SPLIT_FRACTION) * primary.width - this._spacing; + let viewHeight = contentHeight - 2 * this._spacing; + let viewY = contentY + this._spacing; + let viewX = rtl ? 0 + : Math.floor(DASH_SPLIT_FRACTION * primary.width) + this._spacing; + // Set the dash's x position - y is handled by a constraint + let dashX; if (rtl) { - this._workspacesX = WORKSPACE_GRID_PADDING; + this._dash.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST); + dashX = primary.width; } else { - this._workspacesX = displayGridColumnWidth + WORKSPACE_GRID_PADDING; + dashX = 0; } - this._workspacesY = Math.floor(displayGridRowHeight + WORKSPACE_GRID_PADDING * (primary.height / primary.width)); + this._dash.actor.set_x(dashX); - if (rtl) { - this._dash.actor.set_position(primary.width - displayGridColumnWidth - WORKSPACE_GRID_PADDING / 2, - this._workspacesY); - } else { - this._dash.actor.set_position(0, this._workspacesY); - } - this._dash.actor.height = this._workspacesHeight; - - - this._paneContainer.set_position(this._dash.actor.x + this._dash.actor.width + DEFAULT_PADDING, - this._workspacesY); - // Dynamic width - this._paneContainer.height = this._workspacesHeight; - if (rtl) { - this._paneContainer.connect('notify::width', Lang.bind(this, function (paneContainer) { - paneContainer.x = this._dash.actor.x - (DEFAULT_PADDING + paneContainer.width); - })); - } - - this._transparentBackground.set_position(primary.x, primary.y); - this._transparentBackground.set_size(primary.width, primary.height); - - }, - - addPane: function (pane, align) { - pane.actor.height = .9 * this._workspacesHeight; - this._paneContainer.add(pane.actor, { expand: true, - y_fill: false, - y_align: align }); - // When a pane is displayed, we raise the transparent background to the top - // and connect to button-release-event on it, then raise the pane above that. - // The idea here is that clicking anywhere outside the pane should close it. - // When the active pane is closed, undo the effect. - let backgroundEventId = null; - pane.connect('open-state-changed', Lang.bind(this, function (pane, isOpen) { - if (isOpen) { - this._activeDisplayPane = pane; - this._transparentBackground.raise_top(); - this._paneContainer.raise_top(); - this._paneContainer.show(); - this._paneReady = false; - if (backgroundEventId != null) - this._transparentBackground.disconnect(backgroundEventId); - backgroundEventId = this._transparentBackground.connect('captured-event', Lang.bind(this, function (actor, event) { - if (event.get_source() != this._transparentBackground) - return false; - if (event.type() == Clutter.EventType.BUTTON_PRESS) - this._paneReady = true; - if (event.type() == Clutter.EventType.BUTTON_RELEASE - && this._paneReady) - this._activeDisplayPane.close(); - return true; - })); - } else if (pane == this._activeDisplayPane) { - this._activeDisplayPane = null; - if (backgroundEventId != null) { - this._transparentBackground.disconnect(backgroundEventId); - backgroundEventId = null; - } - this._transparentBackground.lower_bottom(); - this._paneContainer.hide(); - } - })); + this.viewSelector.actor.set_position(viewX, viewY); + this.viewSelector.actor.set_size(viewWidth, viewHeight); }, //// Public methods //// beginItemDrag: function(source) { - // Close any active panes if @source is a GenericDisplayItem. - // This allows the user to place the item on any workspace. - if (source instanceof GenericDisplay.GenericDisplayItem) - if (this._activeDisplayPane != null) - this._activeDisplayPane.close(); this.emit('item-drag-begin'); }, @@ -406,28 +275,27 @@ Overview.prototype = { show : function() { if (this.visible) return; - if (!Main.pushModal(this._dash.actor)) + if (!Main.pushModal(this.viewSelector.actor)) return; this.visible = true; this.animationInProgress = true; - /* TODO: make this stuff dynamic */ - this._workspacesDisplay = - new WorkspacesView.WorkspacesDisplay(this._workspacesWidth, - this._workspacesHeight, - this._workspacesX, - this._workspacesY); - this._workspacesDisplay.actor.set_size(this._workspacesWidth, - this._workspacesHeight); - this._workspacesDisplay.actor.set_position(this._workspacesX, - this._workspacesY); - this._group.add_actor(this._workspacesDisplay.actor); + // All the the actors in the window group are completely obscured, + // hiding the group holding them while the Overview is displayed greatly + // increases performance of the Overview especially when there are many + // windows visible. + // + // If we switched to displaying the actors in the Overview rather than + // clones of them, this would obviously no longer be necessary. + global.window_group.hide(); + this._group.show(); + this._background.show(); + this.viewSelector.show(); this._workspacesDisplay.show(); - this.workspaces = this._workspacesDisplay.workspacesView; - // Show new workspacesView + this.workspaces = this._workspacesDisplay.workspacesView; this._group.add_actor(this.workspaces.actor); if (!this._desktopFade.child) @@ -443,17 +311,6 @@ Overview.prototype = { }); } - // All the the actors in the window group are completely obscured, - // hiding the group holding them while the Overview is displayed greatly - // increases performance of the Overview especially when there are many - // windows visible. - // - // If we switched to displaying the actors in the Overview rather than - // clones of them, this would obviously no longer be necessary. - global.window_group.hide(); - this._group.show(); - this._background.show(); - // Create a zoom out effect. First scale the Overview group up and // position it so that the active workspace fills up the whole screen, // then transform the group to its normal dimensions and position. @@ -500,8 +357,6 @@ Overview.prototype = { transition: 'easeOutQuad' }); } - if (this._activeDisplayPane != null) - this._activeDisplayPane.close(); this.workspaces.hide(); // Create a zoom in effect by transforming the Overview group so that @@ -569,20 +424,28 @@ Overview.prototype = { this.workspaces.destroy(); this.workspaces = null; - this._workspacesDisplay.actor.destroy(); - this._workspacesDisplay = null; + this._workspacesDisplay.hide(); + this.viewSelector.hide(); this._desktopFade.hide(); this._background.hide(); this._group.hide(); + // Reset the overview actor's scale/position, so that other elements + // can calculate their position correctly the next time the overview + // is shown + let primary = global.get_primary_monitor(); + this._group.set_scale(1, 1); + this._group.set_position(primary.x, primary.y); + this._group.set_size(primary.width, primary.height); + this.visible = false; this.animationInProgress = false; this._hideInProgress = false; this._coverPane.lower_bottom(); - Main.popModal(this._dash.actor); + Main.popModal(this.viewSelector.actor); this.emit('hidden'); } }; diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js index f05b7bea3..ec0dfd26d 100644 --- a/js/ui/workspacesView.js +++ b/js/ui/workspacesView.js @@ -1028,30 +1028,20 @@ WorkspaceControlsContainer.prototype = { } }; -function WorkspacesDisplay(width, height, x, y) { - this._init(width, height, x, y); +function WorkspacesDisplay() { + this._init(); } WorkspacesDisplay.prototype = { - _init: function(width, height, x, y) { - this._workspacesWidth = width; - this._workspacesHeight = height; - this._workspacesX = x; - this._workspacesY = y; - - this._workspaces = []; - for (let w = 0; w < global.screen.n_workspaces; w++) { - let metaWorkspace = global.screen.get_workspace_by_index(w); - this._workspaces[w] = new Workspace.Workspace(metaWorkspace); - } - + _init: function() { this.actor = new St.BoxLayout(); let workspacesBox = new St.BoxLayout({ vertical: true }); this.actor.add(workspacesBox, { expand: true }); // placeholder for window previews - workspacesBox.add(new St.Bin(), { expand: true }); + this._workspacesBin = new St.Bin(); + workspacesBox.add(this._workspacesBin, { expand: true }); this._workspaceIndicatorPanel = new WorkspaceIndicatorPanel(); workspacesBox.add(this._workspaceIndicatorPanel.actor); @@ -1086,16 +1076,45 @@ WorkspacesDisplay.prototype = { controls.add(this._addButton, { expand: true }); this.workspacesView = null; + this._nWorkspacesNotifyId = 0; }, show: function() { this._controlsContainer.show(); - let newView = new WorkspacesView(this._workspacesWidth, - this._workspacesHeight, - this._workspacesX, - this._workspacesY, - this._workspaces); + this._workspaces = []; + for (let i = 0; i < global.screen.n_workspaces; i++) { + let metaWorkspace = global.screen.get_workspace_by_index(i); + this._workspaces[i] = new Workspace.Workspace(metaWorkspace); + } + + this._nWorkspacesNotifyId = + global.screen.connect('notify::n-workspaces', + Lang.bind(this, this._workspacesChanged)); + + let binAllocation = this._workspacesBin.allocation; + let binWidth = binAllocation.x2 - binAllocation.x1; + let binHeight = binAllocation.y2 - binAllocation.y1; + + // Workspaces expect to have the same ratio as the screen, so take + // this into account when fitting the workspace into the bin + let width, height; + let binRatio = binWidth / binHeight; + let wsRatio = global.screen_width / global.screen_height; + if (wsRatio > binRatio) { + width = binWidth; + height = Math.floor(binWidth / wsRatio); + } else { + width = Math.floor(binHeight * wsRatio); + height = binHeight; + } + + // Position workspaces as if they were parented to this._workspacesBin + let [x, y] = this._workspacesBin.get_transformed_position(); + x = Math.floor(x + Math.abs(binWidth - width) / 2); + y = Math.floor(y + Math.abs(binHeight - height) / 2); + + let newView = new WorkspacesView(width, height, x, y, this._workspaces); if (this.workspacesView) this.workspacesView.destroy();