Bug 1355507 - Releasing a tab while dragging through the tabstrip on the same window should show a transition to its final resting place. draft
authorJared Wein <jwein@mozilla.com>
Thu, 13 Apr 2017 18:09:57 -0400
changeset 562661 9035e8af063e1e8d34f5601357531b7007a56e98
parent 562653 f77f3057b8ee04f1a7546e9cd69066d201e9a221
child 562662 d86b7ec754e3f568b928511d28ac09609c1ab625
push id54075
push userbmo:jaws@mozilla.com
push dateFri, 14 Apr 2017 06:24:14 +0000
bugs1355507
milestone55.0a1
Bug 1355507 - Releasing a tab while dragging through the tabstrip on the same window should show a transition to its final resting place. MozReview-Commit-ID: Lb7D1wnifBp
browser/base/content/browser.css
browser/base/content/tabbrowser.xml
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -189,17 +189,21 @@ tabbrowser {
 
 .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;
+  transition: transform 200ms cubic-bezier(.07, .95, 0, 1);
+}
+
+.tabbrowser-tab[finish-movingtab] {
+  transition: transform 200ms cubic-bezier(.07, .95, 0, 1);
 }
 
 .new-tab-popup,
 #alltabs-popup {
   -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-alltabs-popup");
 }
 
 toolbar[printpreview="true"] {
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -6226,20 +6226,25 @@
             if (tab._tPos > draggedTab._tPos && tab._tPos < dropIndex)
               return rtl ? tabWidth : -tabWidth;
             return 0;
           }
         ]]></body>
       </method>
 
       <method name="_finishAnimateTabMove">
+        <parameter name="draggedTab"/>
         <body><![CDATA[
           if (this.getAttribute("movingtab") != "true")
             return;
 
+          // for (let tab of this.tabbrowser.visibleTabs)
+          //   if (tab != draggedTab)
+          //     tab.style.transform = "";
+
           for (let tab of this.tabbrowser.visibleTabs)
             tab.style.transform = "";
 
           this.removeAttribute("movingtab");
 
           this._handleTabSelect();
         ]]></body>
       </method>
@@ -6917,24 +6922,60 @@
         if (draggedTab && dropEffect == "copy") {
           // copy the dropped tab (wherever it's from)
           let newIndex = this._getDropIndex(event, false);
           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();
-
-          // actually move the dragged tab
+          let newIndex = draggedTab._dragData.animDropIndex;
+          if (newIndex > draggedTab._tPos)
+            newIndex--;
           if ("animDropIndex" in draggedTab._dragData) {
-            let newIndex = draggedTab._dragData.animDropIndex;
-            if (newIndex > draggedTab._tPos)
-              newIndex--;
-            this.tabbrowser.moveTabTo(draggedTab, newIndex);
+            let tabWidth = draggedTab.getBoundingClientRect().width;
+            let translateX = parseFloat(draggedTab.style.transform.substring(11));
+            let mod = translateX % tabWidth;
+            if (translateX > 0) {
+              translateX = mod > tabWidth / 2 ?
+                translateX + tabWidth - mod :
+                translateX - mod;
+            } else {
+              translateX = -mod > tabWidth / 2 ?
+                translateX - tabWidth - mod :
+                translateX - mod;
+            }
+
+            Services.console.logStringMessage(
+              `animDropIndex: ${draggedTab._dragData.animDropIndex}; ` +
+              `_tPos: ${draggedTab._tPos}; ` +
+              `newIndex: ${newIndex}; ` +
+              `tabWidth: ${tabWidth}; ` +
+              `translateX: ${translateX}; ` +
+              `originalTransform: ${draggedTab.style.transform}`);
+
+            this.setAttribute("finish-movingtab", "true");
+            draggedTab.setAttribute("finish-movingtab", "true");
+            draggedTab.style.transform = `translateX(${translateX}px)`;
+            draggedTab.addEventListener("transitionend", event => {
+              event.target.removeAttribute("finish-movingtab");
+              this.removeAttribute("finish-movingtab");
+
+              this._finishAnimateTabMove(draggedTab);
+
+              this.tabbrowser.moveTabTo(draggedTab, newIndex);
+              draggedTab.style.transform = "";
+            }, {once: true});
+          } else {
+            this._finishAnimateTabMove();
+
+            // actually move the dragged tab
+            if ("animDropIndex" in draggedTab._dragData) {
+              this.tabbrowser.moveTabTo(draggedTab, newIndex);
+            }
           }
         } else if (draggedTab) {
           let newIndex = this._getDropIndex(event, false);
           this.tabbrowser.adoptTab(draggedTab, newIndex, true);
         } else {
           // Pass true to disallow dropping javascript: or data: urls
           let links;
           try {
@@ -6969,16 +7010,19 @@
         }
       ]]></handler>
 
       <handler event="dragend"><![CDATA[
         // Note: while this case is correctly handled here, this event
         // isn't dispatched when the tab is moved within the tabstrip,
         // see bug 460801.
 
+        if (this.getAttribute("finish-movingtab"))
+          return;
+
         this._finishAnimateTabMove();
 
         var dt = event.dataTransfer;
         var draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
         if (dt.mozUserCancelled || dt.dropEffect != "none" || this._isCustomizing) {
           delete draggedTab._dragData;
           return;
         }