avatarSelectionHelpers.mjs (7692B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 const MIN_SELECTION_SIZE = 48; 6 7 /** 8 * This class is copied from https://searchfox.org/mozilla-central/source/browser/components/screenshots/overlayHelpers.mjs 9 * with some slight modifications such as forcing the region to be a square. 10 * Bug 1974999: Actually import and use the screenshots Region class. 11 * The class holds references to each side the of region. 12 * x1 is the left boundary, x2 is the right boundary, y1 is the top boundary, 13 * and y2 is the bottom boundary. Sometimes the sides can get switched where 14 * x1 > x2 or y1 > y2. To mitigate this, the getters (left, right, top, bottom) 15 * will choose to return the "correct" value. For example, the left getter 16 * returns the min of x1 and x2. The same goes for the other three getters. 17 */ 18 export class Region { 19 #x1; 20 #x2; 21 #y1; 22 #y2; 23 #viewDimensions; 24 25 constructor(viewDimensions) { 26 this.resetDimensions(); 27 this.#viewDimensions = viewDimensions; 28 } 29 30 /** 31 * Sets the dimensions if the given dimension is defined. 32 * Otherwise will reset the dimensions 33 * 34 * @param {object} dims The new region dimensions 35 * { 36 * left: new left dimension value or undefined 37 * top: new top dimension value or undefined 38 * right: new right dimension value or undefined 39 * bottom: new bottom dimension value or undefined 40 * } 41 */ 42 set #dimensions(dims) { 43 if (dims == null) { 44 this.resetDimensions(); 45 return; 46 } 47 48 if (dims.left != null) { 49 this.left = dims.left; 50 } 51 if (dims.top != null) { 52 this.top = dims.top; 53 } 54 if (dims.right != null) { 55 this.right = dims.right; 56 } 57 if (dims.bottom != null) { 58 this.bottom = dims.bottom; 59 } 60 } 61 62 /** 63 * Set the new dimension for the region. This is called from pointer move 64 * events where the region is being moved. 65 * 66 * @param {object} dims The new dimensions. The object should contain left, 67 * top, right, bottom. 68 * @param {string} direction The corner of the region being dragged. It is 69 * used to determine which sides of the region to contain to a square. 70 */ 71 resizeToSquare(dims, direction) { 72 // eslint-disable-next-line no-shadow 73 let { left, right, top, bottom } = dims; 74 switch (direction) { 75 case "mover-topLeft": { 76 let newDiameter = Math.max( 77 MIN_SELECTION_SIZE, 78 this.right - left, 79 this.bottom - top 80 ); 81 this.left = this.right - newDiameter; 82 this.top = this.bottom - newDiameter; 83 break; 84 } 85 case "mover-topRight": { 86 let newDiameter = Math.max( 87 MIN_SELECTION_SIZE, 88 right - this.left, 89 this.bottom - top 90 ); 91 this.right = this.left + newDiameter; 92 this.top = this.bottom - newDiameter; 93 break; 94 } 95 case "mover-bottomRight": { 96 let newDiameter = Math.max( 97 MIN_SELECTION_SIZE, 98 right - this.left, 99 bottom - this.top 100 ); 101 this.right = this.left + newDiameter; 102 this.bottom = this.top + newDiameter; 103 break; 104 } 105 case "mover-bottomLeft": { 106 let newDiameter = Math.max( 107 MIN_SELECTION_SIZE, 108 this.right - left, 109 bottom - this.top 110 ); 111 this.left = this.right - newDiameter; 112 this.bottom = this.top + newDiameter; 113 break; 114 } 115 default: { 116 if ( 117 left < 0 || 118 right > this.#viewDimensions.width || 119 top < 0 || 120 bottom > this.#viewDimensions.height 121 ) { 122 // The region would be invalid so just return 123 return; 124 } 125 this.#dimensions = dims; 126 break; 127 } 128 } 129 130 this.forceSquare(direction); 131 } 132 133 get dimensions() { 134 return { 135 left: this.left, 136 top: this.top, 137 right: this.right, 138 bottom: this.bottom, 139 width: this.width, 140 height: this.height, 141 radius: this.radius, 142 }; 143 } 144 145 resetDimensions() { 146 this.#x1 = 0; 147 this.#x2 = 0; 148 this.#y1 = 0; 149 this.#y2 = 0; 150 } 151 152 /** 153 * Sort the coordinates so x1 < x2 and y1 < y2 154 */ 155 sortCoords() { 156 if (this.#x1 > this.#x2) { 157 [this.#x1, this.#x2] = [this.#x2, this.#x1]; 158 } 159 if (this.#y1 > this.#y2) { 160 [this.#y1, this.#y2] = [this.#y2, this.#y1]; 161 } 162 } 163 164 forceSquare(direction) { 165 if (this.width === this.height && this.width >= MIN_SELECTION_SIZE) { 166 // Already square and large enough 167 return; 168 } 169 170 let newDiameter = Math.max( 171 MIN_SELECTION_SIZE, 172 Math.min(this.width, this.height) 173 ); 174 switch (direction) { 175 case "mover-topLeft": { 176 this.left = this.right - newDiameter; 177 this.top = this.bottom - newDiameter; 178 break; 179 } 180 case "mover-topRight": { 181 this.right = this.left + newDiameter; 182 this.top = this.bottom - newDiameter; 183 break; 184 } 185 case "mover-bottomRight": { 186 this.right = this.left + newDiameter; 187 this.bottom = this.top + newDiameter; 188 break; 189 } 190 case "mover-bottomLeft": { 191 this.left = this.right - newDiameter; 192 this.bottom = this.top + newDiameter; 193 break; 194 } 195 default: { 196 newDiameter = Math.max(MIN_SELECTION_SIZE, this.width, this.height); 197 if (this.left === 0) { 198 this.right = newDiameter; 199 } 200 if (this.right === this.#viewDimensions.width) { 201 this.left = this.right - newDiameter; 202 } 203 if (this.top === 0) { 204 this.bottom = newDiameter; 205 } 206 if (this.bottom === this.#viewDimensions.height) { 207 this.top = this.bottom - newDiameter; 208 } 209 } 210 } 211 } 212 213 get top() { 214 return Math.min(this.#y1, this.#y2); 215 } 216 set top(val) { 217 this.#y1 = Math.min(this.#viewDimensions.height, Math.max(0, val)); 218 } 219 220 get left() { 221 return Math.min(this.#x1, this.#x2); 222 } 223 set left(val) { 224 this.#x1 = Math.min(this.#viewDimensions.width, Math.max(0, val)); 225 } 226 227 get right() { 228 return Math.max(this.#x1, this.#x2); 229 } 230 set right(val) { 231 this.#x2 = Math.min(this.#viewDimensions.width, Math.max(0, val)); 232 } 233 234 get bottom() { 235 return Math.max(this.#y1, this.#y2); 236 } 237 set bottom(val) { 238 this.#y2 = Math.min(this.#viewDimensions.height, Math.max(0, val)); 239 } 240 241 get width() { 242 return Math.abs(this.#x2 - this.#x1); 243 } 244 get height() { 245 return Math.abs(this.#y2 - this.#y1); 246 } 247 248 get radius() { 249 return Math.floor(this.width / 2); 250 } 251 252 get x1() { 253 return this.#x1; 254 } 255 get x2() { 256 return this.#x2; 257 } 258 get y1() { 259 return this.#y1; 260 } 261 get y2() { 262 return this.#y2; 263 } 264 } 265 266 /** 267 * A class which saves the current view dimensions. 268 */ 269 export class ViewDimensions { 270 #height = null; 271 #width = null; 272 #devicePixelRatio = null; 273 274 set dimensions(dimensions) { 275 if (dimensions.height != null) { 276 this.#height = dimensions.height; 277 } 278 if (dimensions.width != null) { 279 this.#width = dimensions.width; 280 } 281 if (dimensions.devicePixelRatio != null) { 282 this.#devicePixelRatio = dimensions.devicePixelRatio; 283 } 284 } 285 286 get dimensions() { 287 return { 288 height: this.height, 289 width: this.width, 290 devicePixelRatio: this.devicePixelRatio, 291 }; 292 } 293 294 get width() { 295 return this.#width; 296 } 297 298 get height() { 299 return this.#height; 300 } 301 302 get devicePixelRatio() { 303 return this.#devicePixelRatio; 304 } 305 306 reset() { 307 this.#width = 0; 308 this.#height = 0; 309 } 310 }