diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index 9b8210045..e107ab86b 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -626,22 +626,10 @@ StScrollBar StButton#vhandle:active { spacing: 24px; } -#overview-group { - spacing: 32px; -} - -.workspaces-display { - spacing: 32px; /* needs to be the same value as #overview-group */ -} - .window-caption { spacing: 25px; } -.workspace-controls { - visible-width: 32px; /* Amount visible before hovering */ -} - .workspace-thumbnails-background { border: 1px solid rgba(128, 128, 128, 0.4); border-right: 0px; @@ -659,6 +647,7 @@ StScrollBar StButton#vhandle:active { .workspace-thumbnails { spacing: 11px; + visible-width: 32px; /* Amount visible before hovering */ } .workspace-thumbnail-indicator { @@ -722,6 +711,8 @@ StScrollBar StButton#vhandle:active { .window-picker { -horizontal-spacing: 32px; -vertical-spacing: 32px; + padding-left: 32px; + padding-right: 32px; } .window-picker.external-monitor { @@ -857,13 +848,13 @@ StScrollBar StButton#vhandle:active { } .all-app { - padding: 16px 25px 16px 16px; + padding: 16px 16px 16px 48px; spacing: 20px; } .all-app:rtl { - padding-right: 16px; - padding-left: 25px; + padding-left: 16px; + padding-right: 48px; } .app-filter { diff --git a/js/ui/overview.js b/js/ui/overview.js index 5e9063be5..747343163 100644 --- a/js/ui/overview.js +++ b/js/ui/overview.js @@ -14,6 +14,7 @@ const Dash = imports.ui.dash; const DND = imports.ui.dnd; const Main = imports.ui.main; const MessageTray = imports.ui.messageTray; +const OverviewControls = imports.ui.overviewControls; const Panel = imports.ui.panel; const Params = imports.misc.params; const Tweener = imports.ui.tweener; @@ -240,6 +241,10 @@ const Overview = new Lang.Class({ this._group.add(this._viewSelector.actor, { x_fill: true, expand: true }); + this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox(); + this._thumbnailsSlider = new OverviewControls.ThumbnailsSlider(this._thumbnailsBox); + this._group.add_actor(this._thumbnailsSlider.actor); + // Add our same-line elements after the search entry this._overview.add(this._group, { y_fill: true, expand: true }); diff --git a/js/ui/overviewControls.js b/js/ui/overviewControls.js index 2f03f878a..61c3e3f70 100644 --- a/js/ui/overviewControls.js +++ b/js/ui/overviewControls.js @@ -5,6 +5,7 @@ const Lang = imports.lang; const St = imports.gi.St; const Main = imports.ui.main; +const Tweener = imports.ui.tweener; const SIDE_CONTROLS_ANIMATION_TIME = 0.2; @@ -142,3 +143,63 @@ const SlidingControl = new Lang.Class({ this.updateSlide(); } }); + +const ThumbnailsSlider = new Lang.Class({ + Name: 'ThumbnailsSlider', + Extends: SlidingControl, + + _init: function(thumbnailsBox) { + this.parent(); + + this.layout.slideDirection = SlideDirection.RIGHT; + + this._thumbnailsBox = thumbnailsBox; + + // SlideLayout reads the actor's expand flags to decide + // whether to allocate the natural size to its child, or the whole + // available allocation + this._thumbnailsBox.actor.y_expand = true; + + this.actor.request_mode = Clutter.RequestMode.WIDTH_FOR_HEIGHT; + this.actor.reactive = true; + this.actor.track_hover = true; + this.actor.add_actor(this._thumbnailsBox.actor); + + Main.layoutManager.connect('monitors-changed', Lang.bind(this, this.updateSlide)); + this.actor.connect('notify::hover', Lang.bind(this, this.updateSlide)); + }, + + getSlide: function() { + if (!this.visible) + return 0; + + // 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 1; + + let child = this.actor.get_first_child(); + let preferredHeight = child.get_preferred_height(-1)[1]; + let expandedWidth = child.get_preferred_width(preferredHeight)[1]; + let visibleWidth = child.get_theme_node().get_length('visible-width'); + + return visibleWidth / expandedWidth; + } +}); diff --git a/js/ui/workspace.js b/js/ui/workspace.js index abc0cb440..567674169 100644 --- a/js/ui/workspace.js +++ b/js/ui/workspace.js @@ -728,6 +728,7 @@ const WindowOverlay = new Lang.Class({ Signals.addSignalMethods(WindowOverlay.prototype); const WindowPositionFlags = { + NONE: 0, INITIAL: 1 << 0, ANIMATE: 1 << 1 }; @@ -1088,7 +1089,7 @@ const Workspace = new Lang.Class({ return false; })); - this.positionWindows(WindowPositionFlags.ANIMATE); + this.positionWindows(WindowPositionFlags.NONE); }, _lookupIndex: function (metaWindow) { diff --git a/js/ui/workspaceThumbnail.js b/js/ui/workspaceThumbnail.js index 987c886ff..a087f3a2e 100644 --- a/js/ui/workspaceThumbnail.js +++ b/js/ui/workspaceThumbnail.js @@ -992,8 +992,6 @@ const ThumbnailsBox = new Lang.Class({ // See comment about this._background in _init() let themeNode = this._background.get_theme_node(); - forWidth = themeNode.adjust_for_width(forWidth); - // Note that for getPreferredWidth/Height we cheat a bit and skip propagating // the size request to our children because we know how big they are and know // that the actors aren't depending on the virtual functions being called. diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js index 260162ce9..1a5654656 100644 --- a/js/ui/workspacesView.js +++ b/js/ui/workspacesView.js @@ -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', @@ -203,11 +201,6 @@ const WorkspacesView = new Lang.Class({ this._extraWorkspaces[i].syncStacking(stackIndices); }, - updateWindowPositions: function() { - for (let w = 0; w < this._workspaces.length; w++) - this._workspaces[w].positionWindows(Workspace.WindowPositionFlags.ANIMATE); - }, - _scrollToActive: function() { let active = global.screen.get_active_workspace_index(); @@ -437,23 +430,9 @@ const WorkspacesDisplay = new Lang.Class({ Name: 'WorkspacesDisplay', _init: function() { - this.actor = new Shell.GenericContainer({ style_class: 'workspaces-display' }); - 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 = new St.Widget({ clip_to_allocation: true }); + this.actor.connect('notify::allocation', Lang.bind(this, this._updateWorkspacesGeometry)); this.actor.connect('parent-set', Lang.bind(this, this._parentSet)); - this.actor.set_clip_to_allocation(true); - - this._spacing = 0; - this.actor.connect('style-changed', Lang.bind(this, - function() { - let node = this.actor.get_theme_node(); - let spacing = node.get_length('spacing'); - if (spacing != this._spacing) { - this._spacing = spacing; - this._updateWorkspacesGeometry(); - } - })); let clickAction = new Clutter.ClickAction() clickAction.connect('clicked', Lang.bind(this, function(action) { @@ -482,23 +461,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)); - this._primaryIndex = Main.layoutManager.primaryIndex; - this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox(); - controls.add_actor(this._thumbnailsBox.actor); - this._workspacesViews = []; this._workspaces = []; this._primaryScrollAdjustment = null; @@ -509,40 +473,11 @@ const WorkspacesDisplay = new Lang.Class({ this._workspacesOnlyOnPrimaryChanged)); this._workspacesOnlyOnPrimaryChanged(); - this._inDrag = false; - this._cancelledDrag = false; - - this._controlsInitiallyHovered = false; - this._alwaysZoomOut = false; - this._zoomOut = false; - this._zoomFraction = 0; - - this._updateAlwaysZoom(); - - // If we stop hiding the overview on layout changes, we will need to - // update the _workspacesViews here - Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._updateAlwaysZoom)); - - Main.xdndHandler.connect('drag-begin', Lang.bind(this, function(){ - this._alwaysZoomOut = true; - })); - - Main.xdndHandler.connect('drag-end', Lang.bind(this, function(){ - this._alwaysZoomOut = false; - this._updateAlwaysZoom(); - })); - global.screen.connect('notify::n-workspaces', Lang.bind(this, this._workspacesChanged)); this._switchWorkspaceNotifyId = 0; - this._itemDragBeginId = 0; - this._itemDragCancelledId = 0; - this._itemDragEndId = 0; - this._windowDragBeginId = 0; - this._windowDragCancelledId = 0; - this._windowDragEndId = 0; this._notifyOpacityId = 0; this._swipeScrollBeginId = 0; this._swipeScrollEndId = 0; @@ -556,49 +491,11 @@ const WorkspacesDisplay = new Lang.Class({ }, show: function() { - if(!this._alwaysZoomOut) { - let [mouseX, mouseY] = global.get_pointer(); - let [x, y] = this._controls.get_transformed_position(); - let [width, height] = this._controls.get_transformed_size(); - let visibleWidth = this._controls.get_theme_node().get_length('visible-width'); - let rtl = (Clutter.get_default_text_direction () == Clutter.TextDirection.RTL); - if(rtl) - x = x + width - visibleWidth; - if(mouseX > x - 0.5 && mouseX < x + visibleWidth + 0.5 && - mouseY > y - 0.5 && mouseY < y + height + 0.5) - this._controlsInitiallyHovered = true; - } - - this._zoomOut = this._alwaysZoomOut; - this._zoomFraction = this._alwaysZoomOut ? 1 : 0; - this._updateZoom(); - - this._controls.show(); - this._updateWorkspacesViews(); this._restackedNotifyId = Main.overview.connect('windows-restacked', Lang.bind(this, this._onRestacked)); - - if (this._itemDragBeginId == 0) - this._itemDragBeginId = Main.overview.connect('item-drag-begin', - Lang.bind(this, this._dragBegin)); - if (this._itemDragCancelledId == 0) - this._itemDragCancelledId = Main.overview.connect('item-drag-cancelled', - Lang.bind(this, this._dragCancelled)); - if (this._itemDragEndId == 0) - this._itemDragEndId = Main.overview.connect('item-drag-end', - Lang.bind(this, this._dragEnd)); - if (this._windowDragBeginId == 0) - this._windowDragBeginId = Main.overview.connect('window-drag-begin', - Lang.bind(this, this._dragBegin)); - if (this._windowDragCancelledId == 0) - this._windowDragCancelledId = Main.overview.connect('window-drag-cancelled', - Lang.bind(this, this._dragCancelled)); - if (this._windowDragEndId == 0) - this._windowDragEndId = Main.overview.connect('window-drag-end', - Lang.bind(this, this._dragEnd)); }, zoomFromOverview: function() { @@ -608,39 +505,10 @@ const WorkspacesDisplay = new Lang.Class({ }, hide: function() { - this._controls.hide(); - - if (!this._alwaysZoomOut) - this.zoomFraction = 0; - if (this._restackedNotifyId > 0){ Main.overview.disconnect(this._restackedNotifyId); this._restackedNotifyId = 0; } - if (this._itemDragBeginId > 0) { - Main.overview.disconnect(this._itemDragBeginId); - this._itemDragBeginId = 0; - } - if (this._itemDragCancelledId > 0) { - Main.overview.disconnect(this._itemDragCancelledId); - this._itemDragCancelledId = 0; - } - if (this._itemDragEndId > 0) { - Main.overview.disconnect(this._itemDragEndId); - this._itemDragEndId = 0; - } - if (this._windowDragBeginId > 0) { - Main.overview.disconnect(this._windowDragBeginId); - this._windowDragBeginId = 0; - } - if (this._windowDragCancelledId > 0) { - Main.overview.disconnect(this._windowDragCancelledId); - this._windowDragCancelledId = 0; - } - if (this._windowDragEndId > 0) { - Main.overview.disconnect(this._windowDragEndId); - this._windowDragEndId = 0; - } for (let i = 0; i < this._workspacesViews.length; i++) this._workspacesViews[i].destroy(); @@ -728,76 +596,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); @@ -834,24 +632,15 @@ const WorkspacesDisplay = new Lang.Class({ let width = fullWidth; let height = fullHeight; - let [controlsMin, controlsNatural] = this._controls.get_preferred_width(height); - let controlsVisible = this._controls.get_theme_node().get_length('visible-width'); - let [x, y] = this.actor.get_transformed_position(); let rtl = (Clutter.get_default_text_direction () == Clutter.TextDirection.RTL); - let clipWidth = width - controlsVisible; + let clipWidth = width; let clipHeight = fullHeight; - let clipX = rtl ? x + controlsVisible : x; + let clipX = x; let clipY = y + (fullHeight - clipHeight) / 2; - let widthAdjust = this._zoomOut ? controlsNatural : controlsVisible; - widthAdjust += this._spacing; - width -= widthAdjust; - if (rtl) - x += widthAdjust; - let monitors = Main.layoutManager.monitors; let m = 0; for (let i = 0; i < monitors.length; i++) { @@ -880,9 +669,6 @@ const WorkspacesDisplay = new Lang.Class({ }, _workspacesChanged: function() { - this._updateAlwaysZoom(); - this._updateZoom(); - if (!this._workspacesViews.length) return; @@ -934,68 +720,6 @@ const WorkspacesDisplay = new Lang.Class({ for (let i = 0; i < this._workspacesViews.length; i++) this._workspacesViews[i].updateWorkspaces(oldNumWorkspaces, newNumWorkspaces); - }, - - _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.length) - return; - - Tweener.addTween(this, - { zoomFraction: this._zoomOut ? 1 : 0, - time: WORKSPACE_SWITCH_TIME, - transition: 'easeOutQuad' }); - - for (let i = 0; i < this._workspacesViews.length; i++) - this._workspacesViews[i].updateWindowPositions(); - } - }, - - _onControlsHoverChanged: function() { - if(!this._controls.hover) - this._controlsInitiallyHovered = false; - if(!this._controlsInitiallyHovered) - this._updateZoom(); - }, - - _dragBegin: function() { - this._inDrag = true; - this._cancelledDrag = false; - this._dragMonitor = { - dragMotion: Lang.bind(this, this._onDragMotion) - }; - DND.addDragMonitor(this._dragMonitor); - }, - - _dragCancelled: function() { - this._cancelledDrag = true; - DND.removeDragMonitor(this._dragMonitor); - }, - - _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)); } }); Signals.addSignalMethods(WorkspacesDisplay.prototype);