From 7183aac362cf820340e67afa10ca4e30904b4243 Mon Sep 17 00:00:00 2001 From: Maxim Ermilov Date: Tue, 16 Mar 2010 18:51:24 +0300 Subject: [PATCH] add ability move window in SingleView https://bugzilla.gnome.org/show_bug.cgi?id=607821 --- data/Makefile.am | 1 + data/theme/gnome-shell.css | 32 ++++ data/theme/move-window-on-new.svg | 89 +++++++++ js/ui/workspace.js | 50 ++++- js/ui/workspacesView.js | 301 ++++++++++++++++++++++++++---- 5 files changed, 432 insertions(+), 41 deletions(-) create mode 100644 data/theme/move-window-on-new.svg diff --git a/data/Makefile.am b/data/Makefile.am index 8afd70312..43ec39dc4 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -27,6 +27,7 @@ dist_theme_DATA = \ theme/gnome-shell.css \ theme/mosaic-view-active.svg \ theme/mosaic-view.svg \ + theme/move-window-on-new.svg \ theme/remove-workspace.svg \ theme/scroll-button-down-hover.png \ theme/scroll-button-down.png \ diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index a55923425..888f76cca 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -168,6 +168,38 @@ StTooltip { border: 1px solid #666666; } +.new-workspace-area { + border: 2px solid rgba(255, 255, 255, 0.8); + border-radius: 10px; + background-color: #111; +} + +.new-workspace-area-internal { + background-gradient-direction: horizontal; + background-gradient-start: rgba(16, 16, 16, 0); + background-gradient-end: rgba(16, 16, 16, 1.0); + background-image: url("move-window-on-new.svg"); +} + +.new-workspace-area:hover { + border: 2px solid rgba(255, 255, 255, 1.0); + background-gradient-direction: horizontal; + background-gradient-start: rgba(130, 130, 130, 0.9); + background-gradient-end: rgba(16, 16, 16, 0.9); +} + +.left-workspaces-shadow { + background-gradient-direction: horizontal; + background-gradient-start: rgba(16, 16, 16, 1.0); + background-gradient-end: rgba(16, 16, 16, 0.0); +} + +.right-workspaces-shadow { + background-gradient-direction: horizontal; + background-gradient-end: rgba(16, 16, 16, 1.0); + background-gradient-start: rgba(16, 16, 16, 0); +} + .workspaces { color: white; } diff --git a/data/theme/move-window-on-new.svg b/data/theme/move-window-on-new.svg new file mode 100644 index 000000000..5087c405c --- /dev/null +++ b/data/theme/move-window-on-new.svg @@ -0,0 +1,89 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/js/ui/workspace.js b/js/ui/workspace.js index 672994fb6..20d5fd27c 100644 --- a/js/ui/workspace.js +++ b/js/ui/workspace.js @@ -641,6 +641,8 @@ function Workspace(workspaceNum, parentActor) { Workspace.prototype = { _init : function(workspaceNum, parentActor) { + // When dragging a window, we use this slot for reserve space. + this._reservedSlot = null; this.workspaceNum = workspaceNum; this._windowOverlaysGroup = new Clutter.Group(); // Without this the drop area will be overlapped. @@ -1039,11 +1041,16 @@ Workspace.prototype = { let rect = new Meta.Rectangle(); metaWindow.get_outer_rect(rect); + let buttonOuterHeight, captionHeight; + let buttonOuterWidth = 0; - let [buttonOuterHeight, captionHeight] = this._windowOverlays[1].chromeHeights(); + if (this._windowOverlays[1]) { + [buttonOuterHeight, captionHeight] = this._windowOverlays[1].chromeHeights(); + buttonOuterWidth = this._windowOverlays[1].chromeWidth() / this.scale; + } else + [buttonOuterHeight, captionHeight] = [0, 0]; buttonOuterHeight /= this.scale; captionHeight /= this.scale; - let buttonOuterWidth = this._windowOverlays[1].chromeWidth() / this.scale; let desiredWidth = global.screen_width * fraction; let desiredHeight = global.screen_height * fraction; @@ -1057,6 +1064,19 @@ Workspace.prototype = { return [x, y, scale]; }, + setReservedSlot: function(clone) { + if (clone && this.containsMetaWindow(clone.metaWindow)) { + this._reservedSlot = null; + this.positionWindows(WindowPositionFlags.ANIMATE); + return; + } + if (clone) + this._reservedSlot = clone; + else + this._reservedSlot = null; + this.positionWindows(WindowPositionFlags.ANIMATE); + }, + /** * positionWindows: * @flags: @@ -1072,6 +1092,8 @@ Workspace.prototype = { let totalVisible = 0; let visibleClones = this._getVisibleClones(); + if (this._reservedSlot) + visibleClones.push(this._reservedSlot); let workspaceZooming = flags & WindowPositionFlags.ZOOM; let animate = flags & WindowPositionFlags.ANIMATE; @@ -1089,7 +1111,8 @@ Workspace.prototype = { let [x, y, scale] = this._computeWindowRelativeLayout(metaWindow, slot); - overlay.hide(); + if (overlay) + overlay.hide(); if (animate) { if (!metaWindow.showing_on_its_workspace()) { /* Hidden windows should fade in and grow @@ -1163,8 +1186,10 @@ Workspace.prototype = { cloneWidth = this.scale * clone.actor.scale_x * cloneWidth; cloneHeight = this.scale * clone.actor.scale_y * cloneHeight; - overlay.updatePositions(cloneX, cloneY, cloneWidth, cloneHeight); - overlay.fadeIn(); + if (overlay) { + overlay.updatePositions(cloneX, cloneY, cloneWidth, cloneHeight); + overlay.fadeIn(); + } }, _fadeInAllOverlays: function() { @@ -1205,6 +1230,15 @@ Workspace.prototype = { return false; }, + showWindowsOverlays: function() { + this._windowOverlaysGroup.show(); + this._fadeInAllOverlays(); + }, + + hideWindowsOverlays: function() { + this._windowOverlaysGroup.hide(); + }, + _windowRemoved : function(metaWorkspace, metaWin) { let win = metaWin.get_compositor_private(); @@ -1482,11 +1516,13 @@ Workspace.prototype = { clone.connect('selected', Lang.bind(this, this._onCloneSelected)); clone.connect('drag-begin', - Lang.bind(this, function() { + Lang.bind(this, function(clone) { + this.emit('window-drag-begin', clone.actor); overlay.hide(); })); clone.connect('drag-end', - Lang.bind(this, function() { + Lang.bind(this, function(clone) { + this.emit('window-drag-end', clone.actor); overlay.show(); })); diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js index 64d444d1a..7876cf1ef 100644 --- a/js/ui/workspacesView.js +++ b/js/ui/workspacesView.js @@ -33,6 +33,9 @@ const WorkspacesViewType = { }; const WORKSPACES_VIEW_KEY = 'overview/workspaces_view'; +const WORKSPACE_DRAGGING_SCALE = 0.85; +const WORKSPACE_SHADOW_SCALE = (1 - WORKSPACE_DRAGGING_SCALE) / 2; + function GenericWorkspacesView(width, height, x, y, animate) { this._init(width, height, x, y, animate); } @@ -466,6 +469,33 @@ MosaicView.prototype = { } }; +function NewWorkspaceArea() { + this._init(); +} + +NewWorkspaceArea.prototype = { + _init: function() { + let width = Math.ceil(global.screen_width * WORKSPACE_SHADOW_SCALE); + this.actor = new Clutter.Group({ width: width, + height: global.screen_height, + x: global.screen_width }); + + this._child1 = new St.Bin({ style_class: 'new-workspace-area', + width: width, + height: global.screen_height }); + this._child2 = new St.Bin({ style_class: 'new-workspace-area-internal', + width: width, + height: global.screen_height, + reactive: true }); + this.actor.add_actor(this._child1); + this.actor.add_actor(this._child2); + }, + + setStyle: function(isHover) { + this._child1.set_style_pseudo_class(isHover ? 'hover' : null); + } +}; + function SingleView(width, height, x, y, animate) { this._init(width, height, x, y, animate); } @@ -474,8 +504,22 @@ SingleView.prototype = { __proto__: GenericWorkspacesView.prototype, _init: function(width, height, x, y, animate) { + this._newWorkspaceArea = new NewWorkspaceArea(); + this._leftShadow = new St.Bin({ style_class: 'left-workspaces-shadow', + width: Math.ceil(global.screen_width * WORKSPACE_SHADOW_SCALE), + height: global.screen_height, + x: global.screen_width }) + this._rightShadow = new St.Bin({ style_class: 'right-workspaces-shadow', + width: Math.ceil(global.screen_width * WORKSPACE_SHADOW_SCALE), + height: global.screen_height, + x: global.screen_width }) + GenericWorkspacesView.prototype._init.call(this, width, height, x, y, animate); + this._actor.add_actor(this._newWorkspaceArea.actor); + this._actor.add_actor(this._leftShadow); + this._actor.add_actor(this._rightShadow); + this.actor.style_class = "workspaces single"; this._actor.set_clip(x, y, width, height); this._addButton = null; @@ -486,76 +530,156 @@ SingleView.prototype = { this._lostWorkspaces = []; this._scrolling = false; this._animatingScroll = false; + + let primary = global.get_primary_monitor(); + this._dropGroup = new Clutter.Group({ x: 0, y: 0, + width: primary.width, + height: primary.height }); + this._dropGroup._delegate = this; + global.stage.add_actor(this._dropGroup); + this._dropGroup.lower_bottom(); + this._timeoutId = 0; }, _positionWorkspaces: function() { - let scale = this._width / global.screen_width; + let scale; + + if (this._inDrag) + scale = this._width * WORKSPACE_DRAGGING_SCALE / global.screen_width; + else + scale = this._width / global.screen_width; let active = global.screen.get_active_workspace_index(); + let _width = this._workspaces[0].actor.width * scale; for (let w = 0; w < this._workspaces.length; w++) { let workspace = this._workspaces[w]; + if (this._inDrag) + workspace.opacity = 200; + else + workspace.opacity = 255; + if (active == w) + workspace.opacity = 255; + workspace.gridRow = 0; workspace.gridCol = 0; workspace.scale = scale; - let _width = workspace.actor.width * scale; - workspace.gridX = this._x + (w - active) * (_width + this._spacing); - workspace.gridY = this._y; + workspace.gridX = this._x + (this._width - _width) / 2 + (w - active) * (_width + this._spacing); + workspace.gridY = this._y + (this._height - workspace.actor.height * scale) / 2; workspace.setSelected(false); } - }, - _updateWorkspaceActors: function() { - for (let w = 0; w < this._workspaces.length; w++) { - let workspace = this._workspaces[w]; + this._newWorkspaceArea.scale = scale; + this._newWorkspaceArea.gridX = this._x + (this._width - _width) / 2 + (this._workspaces.length - active) * (_width + this._spacing); + this._newWorkspaceArea.gridY = this._y + (this._height - this._newWorkspaceArea.actor.height * scale) / 2; - workspace.actor.set_scale(workspace.scale, workspace.scale); - workspace.actor.set_position(workspace.gridX, workspace.gridY); - workspace.positionWindows(0); - } + this._leftShadow.scale = scale; + this._leftShadow.gridX = this._x + (this._width - _width) / 2 - (this._leftShadow.width * scale + this._spacing); + this._leftShadow.gridY = this._y + (this._height - this._leftShadow.height * scale) / 2; + + this._rightShadow.scale = scale; + this._rightShadow.gridX = this._x + (this._width - _width) / 2 + (_width + this._spacing); + this._rightShadow.gridY = this._y + (this._height - this._rightShadow.height * scale) / 2; }, _scrollToActive: function(showAnimation) { let active = global.screen.get_active_workspace_index(); - this._scrollWorkspacesToIndex(active, showAnimation); + this._updateWorkspaceActors(showAnimation); this._scrollScrollBarToIndex(active, showAnimation); }, - _scrollWorkspacesToIndex: function(index, showAnimation) { + _updateWorkspaceActors: function(showAnimation) { let active = global.screen.get_active_workspace_index(); - let targetWorkspaceNewX = this._x; - let targetWorkspaceCurrentX = this._workspaces[index].gridX; - let dx = targetWorkspaceNewX - targetWorkspaceCurrentX; + + this._positionWorkspaces(); + + let dx = this._workspaces[0].gridX - this._workspaces[0].actor.x; for (let w = 0; w < this._workspaces.length; w++) { let workspace = this._workspaces[w]; - workspace.gridX += dx; workspace.actor.show(); - workspace._hideAllOverlays(); + workspace.hideWindowsOverlays(); - let visible = (w == active); + let i = w; if (showAnimation) { Tweener.addTween(workspace.actor, { x: workspace.gridX, + y: workspace.gridY, + scale_x: workspace.scale, + scale_y: workspace.scale, time: WORKSPACE_SWITCH_TIME, + opacity: workspace.opacity, transition: 'easeOutQuad', + onCompleteScope: this, onComplete: function() { - if (visible) - workspace._fadeInAllOverlays(); - else - workspace.actor.hide(); + if (i == active) { + if (!this._inDrag) + workspace.showWindowsOverlays(); + } else + workspace.actor.visible = Math.abs(i - active) <= 1; }}); } else { - workspace.actor.x = workspace.gridX; - if (visible) - workspace._fadeInAllOverlays(); - else - workspace.actor.hide(); + workspace.actor.set_scale(workspace.scale, workspace.scale); + workspace.actor.set_position(workspace.gridX, workspace.gridY); + workspace.actor.opacity = workspace.opacity; + if (i == active) { + if (!this._inDrag) + workspace.showWindowsOverlays(); + } else + workspace.actor.visible = Math.abs(i - active) <= 1; } + workspace.positionWindows(0); + } + if (active) + this._leftShadow.show(); + else + this._leftShadow.hide(); + + if (active == this._workspaces.length - 1) + this._rightShadow.hide(); + else + this._rightShadow.show(); + + this._leftShadow.raise_top(); + this._rightShadow.raise_top(); + + if (showAnimation) { + Tweener.addTween(this._newWorkspaceArea.actor, + { x: this._newWorkspaceArea.gridX, + y: this._newWorkspaceArea.gridY, + scale_x: this._newWorkspaceArea.scale, + scale_y: this._newWorkspaceArea.scale, + time: WORKSPACE_SWITCH_TIME, + transition: 'easeOutQuad' + }); + this._leftShadow.x = this._leftShadow.gridX; + Tweener.addTween(this._leftShadow, + { y: this._leftShadow.gridY, + scale_x: this._leftShadow.scale, + scale_y: this._leftShadow.scale, + time: WORKSPACE_SWITCH_TIME, + transition: 'easeOutQuad' + }); + this._rightShadow.x = this._rightShadow.gridX; + Tweener.addTween(this._rightShadow, + { y: this._rightShadow.gridY, + scale_x: this._rightShadow.scale, + scale_y: this._rightShadow.scale, + time: WORKSPACE_SWITCH_TIME, + transition: 'easeOutQuad' + }); + } else { + this._newWorkspaceArea.actor.set_scale(this._newWorkspaceArea.scale, this._newWorkspaceArea.scale); + this._newWorkspaceArea.actor.set_position(this._newWorkspaceArea.gridX, this._newWorkspaceArea.gridY); + + this._leftShadow.set_scale(this._leftShadow.scale, this._leftShadow.scale); + this._leftShadow.set_position(this._leftShadow.gridX, this._leftShadow.gridY); + this._rightShadow.set_scale(this._rightShadow.scale, this._rightShadow.scale); + this._rightShadow.set_position(this._rightShadow.gridX, this._rightShadow.gridY); } for (let l = 0; l < this._lostWorkspaces.length; l++) { @@ -667,9 +791,121 @@ SingleView.prototype = { this._scrollToActive(true); }, + _onDestroy: function() { + GenericWorkspacesView.prototype._onDestroy.call(this); + this._dropGroup.destroy(); + if (this._timeoutId) { + Mainloop.source_remove(this._timeoutId); + this._timeoutId = 0; + } + }, + + acceptDrop: function(source, dropActor, x, y, time) { + for (let i = 0; i < this._workspaces.length; i++) { + let [dx, dy] = this._workspaces[i].actor.get_transformed_position(); + let [dw, dh] = this._workspaces[i].actor.get_transformed_size(); + + if (x > dx && x < dx + dw && y > dy && y < dy + dh) + return this._workspaces[i].acceptDrop(source, dropActor, x, y, time); + } + + let [dx, dy] = this._newWorkspaceArea.actor.get_transformed_position(); + let [dw, dh] = this._newWorkspaceArea.actor.get_transformed_size(); + if (x > dx && x < dx + dw && y > dy && y < dy + dh) + return this._acceptNewWorkspaceDrop(source, dropActor, x, y, time); + + return false; + }, + + _onWindowDragBegin: function(w, actor) { + if (!this._scroll || this._scroll.adjustment.value - Math.round(this._scroll.adjustment.value) != 0) + return; + + this._inDrag = true; + this._updateWorkspaceActors(true); + + this._dropGroup.raise_top(); + }, + + handleDragOver: function(self, actor, x, y) { + let onPanel = false; + + let activeWorkspaceIndex = global.screen.get_active_workspace_index(); + if (x == 0 && activeWorkspaceIndex > 0 && this._dragOverLastX !== 0) { + this._workspaces[activeWorkspaceIndex - 1]._metaWorkspace.activate(global.get_current_time()); + this._workspaces[activeWorkspaceIndex - 1].setReservedSlot(actor._delegate); + this._dragOverLastX = 0; + return; + } + if (x == global.screen_width - 1 && this._workspaces[activeWorkspaceIndex + 1] && + this._dragOverLastX != global.screen_width - 1) { + this._workspaces[activeWorkspaceIndex + 1]._metaWorkspace.activate(global.get_current_time()); + this._workspaces[activeWorkspaceIndex + 1].setReservedSlot(actor._delegate); + this._dragOverLastX = global.screen_width - 1; + return; + } + + this._dragOverLastX = x; + + let [dx, dy] = this._newWorkspaceArea.actor.get_transformed_position(); + let [dw, dh] = this._newWorkspaceArea.actor.get_transformed_size(); + this._newWorkspaceArea.setStyle(x > dx && x < dx + dw && y > dy && y < dy + dh); + + [dx, dy] = this._leftShadow.get_transformed_position(); + [dw, dh] = this._leftShadow.get_transformed_size(); + if (this._workspaces[activeWorkspaceIndex - 1]) { + if (x > dx && x < dx + dw && y > dy && y < dy + dh) { + onPanel = -1; + this._workspaces[activeWorkspaceIndex - 1].actor.opacity = 255; + } else + this._workspaces[activeWorkspaceIndex - 1].actor.opacity = 200; + } + + [dx, dy] = this._rightShadow.get_transformed_position(); + [dw, dh] = this._rightShadow.get_transformed_size(); + if (this._workspaces[activeWorkspaceIndex + 1]) { + if (x > dx && x < dx + dw && y > dy && y < dy + dh) { + onPanel = 1; + this._workspaces[activeWorkspaceIndex + 1].actor.opacity = 255; + } else + this._workspaces[activeWorkspaceIndex + 1].actor.opacity = 200; + } + if (onPanel) { + if (!this._timeoutId) + this._timeoutId = Mainloop.timeout_add_seconds (1, Lang.bind(this, function() { + let i = global.screen.get_active_workspace_index(); + if (this._workspaces[i + onPanel]) { + this._workspaces[i + onPanel]._metaWorkspace.activate(global.get_current_time()); + this._workspaces[i + onPanel].setReservedSlot(actor._delegate); + } + return true; + })); + } else { + if (this._timeoutId) { + Mainloop.source_remove(this._timeoutId); + this._timeoutId = 0; + } + } + }, + + _onWindowDragEnd: function(w, actor) { + if (this._timeoutId) { + Mainloop.source_remove(this._timeoutId); + this._timeoutId = 0; + } + this._dropGroup.lower_bottom(); + actor.opacity = 255; + this._inDrag = false; + this._updateWorkspaceActors(true); + + for (let i = 0; i < this._workspaces.length; i++) + this._workspaces[i].setReservedSlot(null); + }, + _addWorkspaceActor: function(workspaceNum) { let workspace = new Workspace.Workspace(workspaceNum, this._actor); - + workspace.connect('window-drag-begin', Lang.bind(this, this._onWindowDragBegin)); + workspace.connect('window-drag-end', Lang.bind(this, this._onWindowDragEnd)); this._actor.add_actor(workspace.actor); this._workspaces[workspaceNum] = workspace; }, @@ -719,10 +955,7 @@ SingleView.prototype = { for (let i = 0; i < this._workspaces.length; i++) { this._workspaces[i]._hideAllOverlays(); - if (Math.abs(i - adj.value) <= 1) - this._workspaces[i].actor.show(); - else - this._workspaces[i].actor.hide(); + this._workspaces[i].actor.visible = Math.abs(i - adj.value) <= 1; this._workspaces[i].actor.x += dx; }