From 12dfdc3fd0de8cdc9b00069f27b4c2c3638f73b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A3o=20Gottwald?= Date: Mon, 27 Aug 2012 19:44:00 +0200 Subject: [PATCH] Bug 783282 - When dragging a tab within the tab strip, move it directly instead of displaying a drop indicator. r=jaws --- browser/base/content/browser.css | 10 ++ browser/base/content/tabbrowser.xml | 244 +++++++++++++++++++--------- 2 files changed, 179 insertions(+), 75 deletions(-) diff --git a/browser/base/content/browser.css b/browser/base/content/browser.css index c0166b7d5d5c..66fbdd736821 100644 --- a/browser/base/content/browser.css +++ b/browser/base/content/browser.css @@ -64,6 +64,16 @@ tabbrowser { display: block; /* position:fixed already does this (bug 579776), but let's be explicit */ } +.tabbrowser-tabs[movingtab] > .tabbrowser-tab[selected] { + position: relative; + z-index: 2; + pointer-events: none; /* avoid blocking dragover events on scroll buttons */ +} + +.tabbrowser-tabs[movingtab] > .tabbrowser-tab[fadein]:not([selected]) { + transition: transform 200ms ease-out; +} + #alltabs-popup { -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-alltabs-popup"); } diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index 3ef4ffd08714..0cd52f7a8ce3 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -3127,6 +3127,127 @@ ]]> + + + draggedTab._dragData.animLastScreenX; + draggedTab._dragData.animLastScreenX = screenX; + + let rtl = (window.getComputedStyle(this).direction == "rtl"); + let pinned = draggedTab.pinned; + let numPinned = this.tabbrowser._numPinnedTabs; + let tabs = this.tabbrowser.visibleTabs + .slice(pinned ? 0 : numPinned, + pinned ? numPinned : undefined); + if (rtl) + tabs.reverse(); + let tabWidth = draggedTab.getBoundingClientRect().width; + + // Move the dragged tab based on the mouse position. + + let leftTab = tabs[0]; + let rightTab = tabs[tabs.length - 1]; + let tabScreenX = draggedTab.boxObject.screenX; + let translateX = screenX - draggedTab._dragData.screenX; + if (!pinned) + translateX += this.mTabstrip.scrollPosition - draggedTab._dragData.scrollX; + let leftBound = leftTab.boxObject.screenX - tabScreenX; + let rightBound = (rightTab.boxObject.screenX + rightTab.boxObject.width) - + (tabScreenX + tabWidth); + translateX = Math.max(translateX, leftBound); + translateX = Math.min(translateX, rightBound); + draggedTab.style.transform = "translateX(" + translateX + "px)"; + + // Determine what tab we're dragging over. + // * Point of reference is the center of the dragged tab. If that + // point touches a background tab, the dragged tab would take that + // tab's position when dropped. + // * We're doing a binary search in order to reduce the amount of + // tabs we need to check. + + let tabCenter = tabScreenX + translateX + tabWidth / 2; + let newIndex = -1; + let oldIndex = "animDropIndex" in draggedTab._dragData ? + draggedTab._dragData.animDropIndex : draggedTab._tPos; + let low = 0; + let high = tabs.length - 1; + while (low <= high) { + let mid = Math.floor((low + high) / 2); + if (tabs[mid] == draggedTab && + ++mid > high) + break; + let boxObject = tabs[mid].boxObject; + let screenX = boxObject.screenX + getTabShift(tabs[mid], oldIndex); + if (screenX > tabCenter) { + high = mid - 1; + } else if (screenX + boxObject.width < tabCenter) { + low = mid + 1; + } else { + newIndex = tabs[mid]._tPos; + break; + } + } + if (newIndex >= oldIndex) + newIndex++; + if (newIndex < 0 || newIndex == oldIndex) + return; + draggedTab._dragData.animDropIndex = newIndex; + + // Shift background tabs to leave a gap where the dragged tab + // would currently be dropped. + + for (let tab of tabs) { + if (tab != draggedTab) { + let shift = getTabShift(tab, newIndex); + tab.style.transform = shift ? "translateX(" + shift + "px)" : ""; + } + } + + function getTabShift(tab, dropIndex) { + if (tab._tPos < draggedTab._tPos && tab._tPos >= dropIndex) + return rtl ? -tabWidth : tabWidth; + if (tab._tPos > draggedTab._tPos && tab._tPos < dropIndex) + return rtl ? tabWidth : -tabWidth; + return 0; + } + ]]> + + + + + draggedTab._tPos) + newIndex--; + this.tabbrowser.moveTabTo(draggedTab, newIndex); + } + + for (let tab of this.tabbrowser.visibleTabs) + tab.style.transform = ""; + + this.removeAttribute("movingtab"); + ]]> + + = sourceNode.boxObject.screenX && - event.screenX <= (sourceNode.boxObject.screenX + - sourceNode.boxObject.width))) { - return dt.effectAllowed = "none"; - } - - return dt.effectAllowed = "copyMove"; + sourceNode.ownerDocument.defaultView instanceof ChromeWindow && + sourceNode.ownerDocument.documentElement.getAttribute("windowtype") == "navigator:browser" && + sourceNode.ownerDocument.defaultView.gBrowser.tabContainer == sourceNode.parentNode) { +#ifdef XP_MACOSX + return dt.effectAllowed = event.altKey ? "copy" : "move"; +#else + return dt.effectAllowed = event.ctrlKey ? "copy" : "move"; +#endif } } @@ -3280,20 +3398,6 @@ ]]> - - - = t.boxObject.screenX && - event.screenX <= t.boxObject.screenX + t.boxObject.width && - event.screenY >= t.boxObject.screenY && - event.screenY <= t.boxObject.screenY + t.boxObject.height) - this.mTabstrip.ensureElementIsVisible(t); - ]]> - - @@ -3487,7 +3588,6 @@ var ind = this._tabDropIndicator; if (effects == "" || effects == "none") { ind.collapsed = true; - this._continueScroll(event); return; } event.preventDefault(); @@ -3514,6 +3614,15 @@ tabStrip.scrollByPixels((ltr ? 1 : -1) * pixelsToScroll); } + if (effects == "move" && + this == event.dataTransfer.mozGetDataAt(TAB_DROP_TYPE, 0).parentNode) { + ind.collapsed = true; + this._animateTabMove(event); + return; + } + + this._finishAnimateTabMove(event); + if (effects == "link") { let tab = this._getDragTargetTab(event); if (tab) { @@ -3581,31 +3690,15 @@ this._tabDropIndicator.collapsed = true; event.stopPropagation(); - - if (draggedTab && (dropEffect == "copy" || - draggedTab.parentNode == this)) { + if (draggedTab && dropEffect == "copy") { + // copy the dropped tab (wherever it's from) let newIndex = this._getDropIndex(event); - if (dropEffect == "copy") { - // copy the dropped tab (wherever it's from) - let newTab = this.tabbrowser.duplicateTab(draggedTab); - this.tabbrowser.moveTabTo(newTab, newIndex); - if (draggedTab.parentNode != this || event.shiftKey) - this.selectedItem = newTab; - } else { - // move the dropped tab - if (newIndex > draggedTab._tPos) - newIndex--; - - if (draggedTab.pinned) { - if (newIndex >= this.tabbrowser._numPinnedTabs) - this.tabbrowser.unpinTab(draggedTab); - } else { - if (newIndex <= this.tabbrowser._numPinnedTabs - 1) - this.tabbrowser.pinTab(draggedTab); - } - - this.tabbrowser.moveTabTo(draggedTab, newIndex); - } + let newTab = this.tabbrowser.duplicateTab(draggedTab); + this.tabbrowser.moveTabTo(newTab, newIndex); + if (draggedTab.parentNode != this || event.shiftKey) + this.selectedItem = newTab; + } else if (draggedTab && draggedTab.parentNode == this) { + this._finishAnimateTabMove(event); } else if (draggedTab) { // swap the dropped tab with a new one we create and then close // it in the other window (making it seem to have moved between @@ -3618,6 +3711,9 @@ // make sure it has a docshell newBrowser.docShell; + let numPinned = this.tabbrowser._numPinnedTabs; + if (newIndex < numPinned || draggedTab.pinned && newIndex == numPinned) + this.tabbrowser.pinTab(newTab); this.tabbrowser.moveTabTo(newTab, newIndex); this.tabbrowser.swapBrowsersAndCloseOther(newTab, draggedTab); @@ -3660,11 +3756,8 @@ } } - // these offsets are only used in dragend, but we need to free them here - // as well if (draggedTab) { - delete draggedTab._dragOffsetX; - delete draggedTab._dragOffsetY; + delete draggedTab._dragData; } ]]> @@ -3673,10 +3766,14 @@ // isn't dispatched when the tab is moved within the tabstrip, // see bug 460801. - // * mozUserCancelled = the user pressed ESC to cancel the drag + this._finishAnimateTabMove(event); + var dt = event.dataTransfer; - if (dt.mozUserCancelled || dt.dropEffect != "none") + var draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0); + if (dt.mozUserCancelled || dt.dropEffect != "none") { + delete draggedTab._dragData; return; + } // Disable detach within the browser toolbox var eX = event.screenX; @@ -3692,7 +3789,6 @@ return; } - var draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0); // screen.availLeft et. al. only check the screen that this window is on, // but we want to look at the screen the tab is being dropped onto. var sX = {}, sY = {}, sWidth = {}, sHeight = {}; @@ -3703,13 +3799,12 @@ // ensure new window entirely within screen var winWidth = Math.min(window.outerWidth, sWidth.value); var winHeight = Math.min(window.outerHeight, sHeight.value); - var left = Math.min(Math.max(eX - draggedTab._dragOffsetX, sX.value), + var left = Math.min(Math.max(eX - draggedTab._dragData.offsetX, sX.value), sX.value + sWidth.value - winWidth); - var top = Math.min(Math.max(eY - draggedTab._dragOffsetY, sY.value), + var top = Math.min(Math.max(eY - draggedTab._dragData.offsetY, sY.value), sY.value + sHeight.value - winHeight); - delete draggedTab._dragOffsetX; - delete draggedTab._dragOffsetY; + delete draggedTab._dragData; if (this.tabbrowser.tabs.length == 1) { // resize _before_ move to ensure the window fits the new screen. if @@ -3741,7 +3836,6 @@ return; this._tabDropIndicator.collapsed = true; - this._continueScroll(event); event.stopPropagation(); ]]>