1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/components/tabview/drag.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,269 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +// ********** 1.9 +// Title: drag.js 1.10 + 1.11 +// ---------- 1.12 +// Variable: drag 1.13 +// The Drag that's currently in process. 1.14 +var drag = { 1.15 + info: null, 1.16 + zIndex: 100, 1.17 + lastMoveTime: 0 1.18 +}; 1.19 + 1.20 +//---------- 1.21 +//Variable: resize 1.22 +//The resize (actually a Drag) that is currently in process 1.23 +var resize = { 1.24 + info: null, 1.25 + lastMoveTime: 0 1.26 +}; 1.27 + 1.28 +// ########## 1.29 +// Class: Drag (formerly DragInfo) 1.30 +// Helper class for dragging <Item>s 1.31 +// 1.32 +// ---------- 1.33 +// Constructor: Drag 1.34 +// Called to create a Drag in response to an <Item> draggable "start" event. 1.35 +// Note that it is also used partially during <Item>'s resizable method as well. 1.36 +// 1.37 +// Parameters: 1.38 +// item - The <Item> being dragged 1.39 +// event - The DOM event that kicks off the drag 1.40 +function Drag(item, event) { 1.41 + Utils.assert(item && (item.isAnItem || item.isAFauxItem), 1.42 + 'must be an item, or at least a faux item'); 1.43 + 1.44 + this.item = item; 1.45 + this.el = item.container; 1.46 + this.$el = iQ(this.el); 1.47 + this.parent = this.item.parent; 1.48 + this.startPosition = new Point(event.clientX, event.clientY); 1.49 + this.startTime = Date.now(); 1.50 + 1.51 + this.item.isDragging = true; 1.52 + this.item.setZ(999999); 1.53 + 1.54 + this.safeWindowBounds = Items.getSafeWindowBounds(); 1.55 + 1.56 + Trenches.activateOthersTrenches(this.el); 1.57 +}; 1.58 + 1.59 +Drag.prototype = { 1.60 + // ---------- 1.61 + // Function: toString 1.62 + // Prints [Drag (item)] for debug use 1.63 + toString: function Drag_toString() { 1.64 + return "[Drag (" + this.item + ")]"; 1.65 + }, 1.66 + 1.67 + // ---------- 1.68 + // Function: snapBounds 1.69 + // Adjusts the given bounds according to the currently active trenches. Used by <Drag.snap> 1.70 + // 1.71 + // Parameters: 1.72 + // bounds - (<Rect>) bounds 1.73 + // stationaryCorner - which corner is stationary? by default, the top left in LTR mode, 1.74 + // and top right in RTL mode. 1.75 + // "topleft", "bottomleft", "topright", "bottomright" 1.76 + // assumeConstantSize - (boolean) whether the bounds' dimensions are sacred or not. 1.77 + // keepProportional - (boolean) if assumeConstantSize is false, whether we should resize 1.78 + // proportionally or not 1.79 + // checkItemStatus - (boolean) make sure this is a valid item which should be snapped 1.80 + snapBounds: function Drag_snapBounds(bounds, stationaryCorner, assumeConstantSize, keepProportional, checkItemStatus) { 1.81 + if (!stationaryCorner) 1.82 + stationaryCorner = UI.rtl ? 'topright' : 'topleft'; 1.83 + var update = false; // need to update 1.84 + var updateX = false; 1.85 + var updateY = false; 1.86 + var newRect; 1.87 + var snappedTrenches = {}; 1.88 + 1.89 + // OH SNAP! 1.90 + 1.91 + // if we aren't holding down the meta key or have trenches disabled... 1.92 + if (!Keys.meta && !Trenches.disabled) { 1.93 + // snappable = true if we aren't a tab on top of something else, and 1.94 + // there's no active drop site... 1.95 + let snappable = !(this.item.isATabItem && 1.96 + this.item.overlapsWithOtherItems()) && 1.97 + !iQ(".acceptsDrop").length; 1.98 + if (!checkItemStatus || snappable) { 1.99 + newRect = Trenches.snap(bounds, stationaryCorner, assumeConstantSize, 1.100 + keepProportional); 1.101 + if (newRect) { // might be false if no changes were made 1.102 + update = true; 1.103 + snappedTrenches = newRect.snappedTrenches || {}; 1.104 + bounds = newRect; 1.105 + } 1.106 + } 1.107 + } 1.108 + 1.109 + // make sure the bounds are in the window. 1.110 + newRect = this.snapToEdge(bounds, stationaryCorner, assumeConstantSize, 1.111 + keepProportional); 1.112 + if (newRect) { 1.113 + update = true; 1.114 + bounds = newRect; 1.115 + Utils.extend(snappedTrenches, newRect.snappedTrenches); 1.116 + } 1.117 + 1.118 + Trenches.hideGuides(); 1.119 + for (var edge in snappedTrenches) { 1.120 + var trench = snappedTrenches[edge]; 1.121 + if (typeof trench == 'object') { 1.122 + trench.showGuide = true; 1.123 + trench.show(); 1.124 + } 1.125 + } 1.126 + 1.127 + return update ? bounds : false; 1.128 + }, 1.129 + 1.130 + // ---------- 1.131 + // Function: snap 1.132 + // Called when a drag or mousemove occurs. Set the bounds based on the mouse move first, then 1.133 + // call snap and it will adjust the item's bounds if appropriate. Also triggers the display of 1.134 + // trenches that it snapped to. 1.135 + // 1.136 + // Parameters: 1.137 + // stationaryCorner - which corner is stationary? by default, the top left in LTR mode, 1.138 + // and top right in RTL mode. 1.139 + // "topleft", "bottomleft", "topright", "bottomright" 1.140 + // assumeConstantSize - (boolean) whether the bounds' dimensions are sacred or not. 1.141 + // keepProportional - (boolean) if assumeConstantSize is false, whether we should resize 1.142 + // proportionally or not 1.143 + snap: function Drag_snap(stationaryCorner, assumeConstantSize, keepProportional) { 1.144 + var bounds = this.item.getBounds(); 1.145 + bounds = this.snapBounds(bounds, stationaryCorner, assumeConstantSize, keepProportional, true); 1.146 + if (bounds) { 1.147 + this.item.setBounds(bounds, true); 1.148 + return true; 1.149 + } 1.150 + return false; 1.151 + }, 1.152 + 1.153 + // -------- 1.154 + // Function: snapToEdge 1.155 + // Returns a version of the bounds snapped to the edge if it is close enough. If not, 1.156 + // returns false. If <Keys.meta> is true, this function will simply enforce the 1.157 + // window edges. 1.158 + // 1.159 + // Parameters: 1.160 + // rect - (<Rect>) current bounds of the object 1.161 + // stationaryCorner - which corner is stationary? by default, the top left in LTR mode, 1.162 + // and top right in RTL mode. 1.163 + // "topleft", "bottomleft", "topright", "bottomright" 1.164 + // assumeConstantSize - (boolean) whether the rect's dimensions are sacred or not 1.165 + // keepProportional - (boolean) if we are allowed to change the rect's size, whether the 1.166 + // dimensions should scaled proportionally or not. 1.167 + snapToEdge: function Drag_snapToEdge(rect, stationaryCorner, assumeConstantSize, keepProportional) { 1.168 + 1.169 + var swb = this.safeWindowBounds; 1.170 + var update = false; 1.171 + var updateX = false; 1.172 + var updateY = false; 1.173 + var snappedTrenches = {}; 1.174 + 1.175 + var snapRadius = (Keys.meta ? 0 : Trenches.defaultRadius); 1.176 + if (rect.left < swb.left + snapRadius ) { 1.177 + if (stationaryCorner.indexOf('right') > -1 && !assumeConstantSize) 1.178 + rect.width = rect.right - swb.left; 1.179 + rect.left = swb.left; 1.180 + update = true; 1.181 + updateX = true; 1.182 + snappedTrenches.left = 'edge'; 1.183 + } 1.184 + 1.185 + if (rect.right > swb.right - snapRadius) { 1.186 + if (updateX || !assumeConstantSize) { 1.187 + var newWidth = swb.right - rect.left; 1.188 + if (keepProportional) 1.189 + rect.height = rect.height * newWidth / rect.width; 1.190 + rect.width = newWidth; 1.191 + update = true; 1.192 + } else if (!updateX || !Trenches.preferLeft) { 1.193 + rect.left = swb.right - rect.width; 1.194 + update = true; 1.195 + } 1.196 + snappedTrenches.right = 'edge'; 1.197 + delete snappedTrenches.left; 1.198 + } 1.199 + if (rect.top < swb.top + snapRadius) { 1.200 + if (stationaryCorner.indexOf('bottom') > -1 && !assumeConstantSize) 1.201 + rect.height = rect.bottom - swb.top; 1.202 + rect.top = swb.top; 1.203 + update = true; 1.204 + updateY = true; 1.205 + snappedTrenches.top = 'edge'; 1.206 + } 1.207 + if (rect.bottom > swb.bottom - snapRadius) { 1.208 + if (updateY || !assumeConstantSize) { 1.209 + var newHeight = swb.bottom - rect.top; 1.210 + if (keepProportional) 1.211 + rect.width = rect.width * newHeight / rect.height; 1.212 + rect.height = newHeight; 1.213 + update = true; 1.214 + } else if (!updateY || !Trenches.preferTop) { 1.215 + rect.top = swb.bottom - rect.height; 1.216 + update = true; 1.217 + } 1.218 + snappedTrenches.top = 'edge'; 1.219 + delete snappedTrenches.bottom; 1.220 + } 1.221 + 1.222 + if (update) { 1.223 + rect.snappedTrenches = snappedTrenches; 1.224 + return rect; 1.225 + } 1.226 + return false; 1.227 + }, 1.228 + 1.229 + // ---------- 1.230 + // Function: drag 1.231 + // Called in response to an <Item> draggable "drag" event. 1.232 + drag: function Drag_drag(event) { 1.233 + this.snap(UI.rtl ? 'topright' : 'topleft', true); 1.234 + 1.235 + if (this.parent && this.parent.expanded) { 1.236 + var distance = this.startPosition.distance(new Point(event.clientX, event.clientY)); 1.237 + if (distance > 100) { 1.238 + this.parent.remove(this.item); 1.239 + this.parent.collapse(); 1.240 + } 1.241 + } 1.242 + }, 1.243 + 1.244 + // ---------- 1.245 + // Function: stop 1.246 + // Called in response to an <Item> draggable "stop" event. 1.247 + // 1.248 + // Parameters: 1.249 + // immediately - bool for doing the pushAway immediately, without animation 1.250 + stop: function Drag_stop(immediately) { 1.251 + Trenches.hideGuides(); 1.252 + this.item.isDragging = false; 1.253 + 1.254 + if (this.parent && this.parent != this.item.parent) 1.255 + this.parent.closeIfEmpty(); 1.256 + 1.257 + if (this.parent && this.parent.expanded) 1.258 + this.parent.arrange(); 1.259 + 1.260 + if (this.item.parent) 1.261 + this.item.parent.arrange(); 1.262 + 1.263 + if (this.item.isAGroupItem) { 1.264 + this.item.setZ(drag.zIndex); 1.265 + drag.zIndex++; 1.266 + 1.267 + this.item.pushAway(immediately); 1.268 + } 1.269 + 1.270 + Trenches.disactivate(); 1.271 + } 1.272 +};