| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | |
| 5 /** | 4 /** |
| 6 * @constructor | 5 * @unrestricted |
| 7 * @extends {WebInspector.VBox} | |
| 8 */ | 6 */ |
| 9 WebInspector.CSSShadowEditor = function() | 7 WebInspector.CSSShadowEditor = class extends WebInspector.VBox { |
| 10 { | 8 constructor() { |
| 11 WebInspector.VBox.call(this, true); | 9 super(true); |
| 12 this.registerRequiredCSS("ui/cssShadowEditor.css"); | 10 this.registerRequiredCSS('ui/cssShadowEditor.css'); |
| 13 this.contentElement.tabIndex = 0; | 11 this.contentElement.tabIndex = 0; |
| 14 | 12 |
| 15 this._typeField = this.contentElement.createChild("div", "shadow-editor-fiel
d"); | 13 this._typeField = this.contentElement.createChild('div', 'shadow-editor-fiel
d'); |
| 16 this._typeField.createChild("label", "shadow-editor-label").textContent = We
bInspector.UIString("Type"); | 14 this._typeField.createChild('label', 'shadow-editor-label').textContent = We
bInspector.UIString('Type'); |
| 17 this._outsetButton = this._typeField.createChild("button", "shadow-editor-bu
tton-left"); | 15 this._outsetButton = this._typeField.createChild('button', 'shadow-editor-bu
tton-left'); |
| 18 this._outsetButton.textContent = WebInspector.UIString("Outset"); | 16 this._outsetButton.textContent = WebInspector.UIString('Outset'); |
| 19 this._outsetButton.addEventListener("click", this._onButtonClick.bind(this),
false); | 17 this._outsetButton.addEventListener('click', this._onButtonClick.bind(this),
false); |
| 20 this._insetButton = this._typeField.createChild("button", "shadow-editor-but
ton-right"); | 18 this._insetButton = this._typeField.createChild('button', 'shadow-editor-but
ton-right'); |
| 21 this._insetButton.textContent = WebInspector.UIString("Inset"); | 19 this._insetButton.textContent = WebInspector.UIString('Inset'); |
| 22 this._insetButton.addEventListener("click", this._onButtonClick.bind(this),
false); | 20 this._insetButton.addEventListener('click', this._onButtonClick.bind(this),
false); |
| 23 | 21 |
| 24 var xField = this.contentElement.createChild("div", "shadow-editor-field"); | 22 var xField = this.contentElement.createChild('div', 'shadow-editor-field'); |
| 25 this._xInput = this._createTextInput(xField, WebInspector.UIString("X offset
")); | 23 this._xInput = this._createTextInput(xField, WebInspector.UIString('X offset
')); |
| 26 var yField = this.contentElement.createChild("div", "shadow-editor-field"); | 24 var yField = this.contentElement.createChild('div', 'shadow-editor-field'); |
| 27 this._yInput = this._createTextInput(yField, WebInspector.UIString("Y offset
")); | 25 this._yInput = this._createTextInput(yField, WebInspector.UIString('Y offset
')); |
| 28 this._xySlider = xField.createChild("canvas", "shadow-editor-2D-slider"); | 26 this._xySlider = xField.createChild('canvas', 'shadow-editor-2D-slider'); |
| 29 this._xySlider.width = WebInspector.CSSShadowEditor.canvasSize; | 27 this._xySlider.width = WebInspector.CSSShadowEditor.canvasSize; |
| 30 this._xySlider.height = WebInspector.CSSShadowEditor.canvasSize; | 28 this._xySlider.height = WebInspector.CSSShadowEditor.canvasSize; |
| 31 this._xySlider.tabIndex = -1; | 29 this._xySlider.tabIndex = -1; |
| 32 this._halfCanvasSize = WebInspector.CSSShadowEditor.canvasSize / 2; | 30 this._halfCanvasSize = WebInspector.CSSShadowEditor.canvasSize / 2; |
| 33 this._innerCanvasSize = this._halfCanvasSize - WebInspector.CSSShadowEditor.
sliderThumbRadius; | 31 this._innerCanvasSize = this._halfCanvasSize - WebInspector.CSSShadowEditor.
sliderThumbRadius; |
| 34 WebInspector.installDragHandle(this._xySlider, this._dragStart.bind(this), t
his._dragMove.bind(this), null, "default"); | 32 WebInspector.installDragHandle( |
| 35 this._xySlider.addEventListener("keydown", this._onCanvasArrowKey.bind(this)
, false); | 33 this._xySlider, this._dragStart.bind(this), this._dragMove.bind(this), n
ull, 'default'); |
| 36 this._xySlider.addEventListener("blur", this._onCanvasBlur.bind(this), false
); | 34 this._xySlider.addEventListener('keydown', this._onCanvasArrowKey.bind(this)
, false); |
| 37 | 35 this._xySlider.addEventListener('blur', this._onCanvasBlur.bind(this), false
); |
| 38 var blurField = this.contentElement.createChild("div", "shadow-editor-blur-f
ield"); | 36 |
| 39 this._blurInput = this._createTextInput(blurField, WebInspector.UIString("Bl
ur")); | 37 var blurField = this.contentElement.createChild('div', 'shadow-editor-blur-f
ield'); |
| 38 this._blurInput = this._createTextInput(blurField, WebInspector.UIString('Bl
ur')); |
| 40 this._blurSlider = this._createSlider(blurField); | 39 this._blurSlider = this._createSlider(blurField); |
| 41 | 40 |
| 42 this._spreadField = this.contentElement.createChild("div", "shadow-editor-fi
eld"); | 41 this._spreadField = this.contentElement.createChild('div', 'shadow-editor-fi
eld'); |
| 43 this._spreadInput = this._createTextInput(this._spreadField, WebInspector.UI
String("Spread")); | 42 this._spreadInput = this._createTextInput(this._spreadField, WebInspector.UI
String('Spread')); |
| 44 this._spreadSlider = this._createSlider(this._spreadField); | 43 this._spreadSlider = this._createSlider(this._spreadField); |
| 44 } |
| 45 |
| 46 /** |
| 47 * @param {!Element} field |
| 48 * @param {string} propertyName |
| 49 * @return {!Element} |
| 50 */ |
| 51 _createTextInput(field, propertyName) { |
| 52 var label = field.createChild('label', 'shadow-editor-label'); |
| 53 label.textContent = propertyName; |
| 54 label.setAttribute('for', propertyName); |
| 55 var textInput = field.createChild('input', 'shadow-editor-text-input'); |
| 56 textInput.type = 'text'; |
| 57 textInput.id = propertyName; |
| 58 textInput.addEventListener('keydown', this._handleValueModification.bind(thi
s), false); |
| 59 textInput.addEventListener('mousewheel', this._handleValueModification.bind(
this), false); |
| 60 textInput.addEventListener('input', this._onTextInput.bind(this), false); |
| 61 textInput.addEventListener('blur', this._onTextBlur.bind(this), false); |
| 62 return textInput; |
| 63 } |
| 64 |
| 65 /** |
| 66 * @param {!Element} field |
| 67 * @return {!Element} |
| 68 */ |
| 69 _createSlider(field) { |
| 70 var slider = createSliderLabel(0, WebInspector.CSSShadowEditor.maxRange, -1)
; |
| 71 slider.addEventListener('input', this._onSliderInput.bind(this), false); |
| 72 field.appendChild(slider); |
| 73 return slider; |
| 74 } |
| 75 |
| 76 /** |
| 77 * @override |
| 78 */ |
| 79 wasShown() { |
| 80 this._updateUI(); |
| 81 } |
| 82 |
| 83 /** |
| 84 * @param {!WebInspector.CSSShadowModel} model |
| 85 */ |
| 86 setModel(model) { |
| 87 this._model = model; |
| 88 this._typeField.hidden = !model.isBoxShadow(); |
| 89 this._spreadField.hidden = !model.isBoxShadow(); |
| 90 this._updateUI(); |
| 91 } |
| 92 |
| 93 _updateUI() { |
| 94 this._updateButtons(); |
| 95 this._xInput.value = this._model.offsetX().asCSSText(); |
| 96 this._yInput.value = this._model.offsetY().asCSSText(); |
| 97 this._blurInput.value = this._model.blurRadius().asCSSText(); |
| 98 this._spreadInput.value = this._model.spreadRadius().asCSSText(); |
| 99 this._blurSlider.value = this._model.blurRadius().amount; |
| 100 this._spreadSlider.value = this._model.spreadRadius().amount; |
| 101 this._updateCanvas(false); |
| 102 } |
| 103 |
| 104 _updateButtons() { |
| 105 this._insetButton.classList.toggle('enabled', this._model.inset()); |
| 106 this._outsetButton.classList.toggle('enabled', !this._model.inset()); |
| 107 } |
| 108 |
| 109 /** |
| 110 * @param {boolean} drawFocus |
| 111 */ |
| 112 _updateCanvas(drawFocus) { |
| 113 var context = this._xySlider.getContext('2d'); |
| 114 context.clearRect(0, 0, this._xySlider.width, this._xySlider.height); |
| 115 |
| 116 // Draw dashed axes. |
| 117 context.save(); |
| 118 context.setLineDash([1, 1]); |
| 119 context.strokeStyle = 'rgba(210, 210, 210, 0.8)'; |
| 120 context.beginPath(); |
| 121 context.moveTo(this._halfCanvasSize, 0); |
| 122 context.lineTo(this._halfCanvasSize, WebInspector.CSSShadowEditor.canvasSize
); |
| 123 context.moveTo(0, this._halfCanvasSize); |
| 124 context.lineTo(WebInspector.CSSShadowEditor.canvasSize, this._halfCanvasSize
); |
| 125 context.stroke(); |
| 126 context.restore(); |
| 127 |
| 128 var thumbPoint = this._sliderThumbPosition(); |
| 129 // Draw 2D slider line. |
| 130 context.save(); |
| 131 context.translate(this._halfCanvasSize, this._halfCanvasSize); |
| 132 context.lineWidth = 2; |
| 133 context.strokeStyle = 'rgba(130, 130, 130, 0.75)'; |
| 134 context.beginPath(); |
| 135 context.moveTo(0, 0); |
| 136 context.lineTo(thumbPoint.x, thumbPoint.y); |
| 137 context.stroke(); |
| 138 // Draw 2D slider thumb. |
| 139 if (drawFocus) { |
| 140 context.beginPath(); |
| 141 context.fillStyle = 'rgba(66, 133, 244, 0.4)'; |
| 142 context.arc(thumbPoint.x, thumbPoint.y, WebInspector.CSSShadowEditor.slide
rThumbRadius + 2, 0, 2 * Math.PI); |
| 143 context.fill(); |
| 144 } |
| 145 context.beginPath(); |
| 146 context.fillStyle = '#4285F4'; |
| 147 context.arc(thumbPoint.x, thumbPoint.y, WebInspector.CSSShadowEditor.sliderT
humbRadius, 0, 2 * Math.PI); |
| 148 context.fill(); |
| 149 context.restore(); |
| 150 } |
| 151 |
| 152 /** |
| 153 * @param {!Event} event |
| 154 */ |
| 155 _onButtonClick(event) { |
| 156 var insetClicked = (event.currentTarget === this._insetButton); |
| 157 if (insetClicked && this._model.inset() || !insetClicked && !this._model.ins
et()) |
| 158 return; |
| 159 this._model.setInset(insetClicked); |
| 160 this._updateButtons(); |
| 161 this.dispatchEventToListeners(WebInspector.CSSShadowEditor.Events.ShadowChan
ged, this._model); |
| 162 } |
| 163 |
| 164 /** |
| 165 * @param {!Event} event |
| 166 */ |
| 167 _handleValueModification(event) { |
| 168 var modifiedValue = WebInspector.createReplacementString(event.currentTarget
.value, event, customNumberHandler); |
| 169 if (!modifiedValue) |
| 170 return; |
| 171 var length = WebInspector.CSSLength.parse(modifiedValue); |
| 172 if (!length) |
| 173 return; |
| 174 if (event.currentTarget === this._blurInput && length.amount < 0) |
| 175 length.amount = 0; |
| 176 event.currentTarget.value = length.asCSSText(); |
| 177 event.currentTarget.selectionStart = 0; |
| 178 event.currentTarget.selectionEnd = event.currentTarget.value.length; |
| 179 this._onTextInput(event); |
| 180 event.consume(true); |
| 181 |
| 182 /** |
| 183 * @param {string} prefix |
| 184 * @param {number} number |
| 185 * @param {string} suffix |
| 186 * @return {string} |
| 187 */ |
| 188 function customNumberHandler(prefix, number, suffix) { |
| 189 if (!suffix.length) |
| 190 suffix = WebInspector.CSSShadowEditor.defaultUnit; |
| 191 return prefix + number + suffix; |
| 192 } |
| 193 } |
| 194 |
| 195 /** |
| 196 * @param {!Event} event |
| 197 */ |
| 198 _onTextInput(event) { |
| 199 this._changedElement = event.currentTarget; |
| 200 this._changedElement.classList.remove('invalid'); |
| 201 var length = WebInspector.CSSLength.parse(event.currentTarget.value); |
| 202 if (!length || event.currentTarget === this._blurInput && length.amount < 0) |
| 203 return; |
| 204 if (event.currentTarget === this._xInput) { |
| 205 this._model.setOffsetX(length); |
| 206 this._updateCanvas(false); |
| 207 } else if (event.currentTarget === this._yInput) { |
| 208 this._model.setOffsetY(length); |
| 209 this._updateCanvas(false); |
| 210 } else if (event.currentTarget === this._blurInput) { |
| 211 this._model.setBlurRadius(length); |
| 212 this._blurSlider.value = length.amount; |
| 213 } else if (event.currentTarget === this._spreadInput) { |
| 214 this._model.setSpreadRadius(length); |
| 215 this._spreadSlider.value = length.amount; |
| 216 } |
| 217 this.dispatchEventToListeners(WebInspector.CSSShadowEditor.Events.ShadowChan
ged, this._model); |
| 218 } |
| 219 |
| 220 _onTextBlur() { |
| 221 if (!this._changedElement) |
| 222 return; |
| 223 var length = !this._changedElement.value.trim() ? WebInspector.CSSLength.zer
o() : |
| 224 WebInspector.CSSLength.par
se(this._changedElement.value); |
| 225 if (!length) |
| 226 length = WebInspector.CSSLength.parse(this._changedElement.value + WebInsp
ector.CSSShadowEditor.defaultUnit); |
| 227 if (!length) { |
| 228 this._changedElement.classList.add('invalid'); |
| 229 this._changedElement = null; |
| 230 return; |
| 231 } |
| 232 if (this._changedElement === this._xInput) { |
| 233 this._model.setOffsetX(length); |
| 234 this._xInput.value = length.asCSSText(); |
| 235 this._updateCanvas(false); |
| 236 } else if (this._changedElement === this._yInput) { |
| 237 this._model.setOffsetY(length); |
| 238 this._yInput.value = length.asCSSText(); |
| 239 this._updateCanvas(false); |
| 240 } else if (this._changedElement === this._blurInput) { |
| 241 if (length.amount < 0) |
| 242 length = WebInspector.CSSLength.zero(); |
| 243 this._model.setBlurRadius(length); |
| 244 this._blurInput.value = length.asCSSText(); |
| 245 this._blurSlider.value = length.amount; |
| 246 } else if (this._changedElement === this._spreadInput) { |
| 247 this._model.setSpreadRadius(length); |
| 248 this._spreadInput.value = length.asCSSText(); |
| 249 this._spreadSlider.value = length.amount; |
| 250 } |
| 251 this._changedElement = null; |
| 252 this.dispatchEventToListeners(WebInspector.CSSShadowEditor.Events.ShadowChan
ged, this._model); |
| 253 } |
| 254 |
| 255 /** |
| 256 * @param {!Event} event |
| 257 */ |
| 258 _onSliderInput(event) { |
| 259 if (event.currentTarget === this._blurSlider) { |
| 260 this._model.setBlurRadius(new WebInspector.CSSLength( |
| 261 this._blurSlider.value, this._model.blurRadius().unit || WebInspector.
CSSShadowEditor.defaultUnit)); |
| 262 this._blurInput.value = this._model.blurRadius().asCSSText(); |
| 263 this._blurInput.classList.remove('invalid'); |
| 264 } else if (event.currentTarget === this._spreadSlider) { |
| 265 this._model.setSpreadRadius(new WebInspector.CSSLength( |
| 266 this._spreadSlider.value, this._model.spreadRadius().unit || WebInspec
tor.CSSShadowEditor.defaultUnit)); |
| 267 this._spreadInput.value = this._model.spreadRadius().asCSSText(); |
| 268 this._spreadInput.classList.remove('invalid'); |
| 269 } |
| 270 this.dispatchEventToListeners(WebInspector.CSSShadowEditor.Events.ShadowChan
ged, this._model); |
| 271 } |
| 272 |
| 273 /** |
| 274 * @param {!MouseEvent} event |
| 275 * @return {boolean} |
| 276 */ |
| 277 _dragStart(event) { |
| 278 this._xySlider.focus(); |
| 279 this._updateCanvas(true); |
| 280 this._canvasOrigin = new WebInspector.Geometry.Point( |
| 281 this._xySlider.totalOffsetLeft() + this._halfCanvasSize, |
| 282 this._xySlider.totalOffsetTop() + this._halfCanvasSize); |
| 283 var clickedPoint = new WebInspector.Geometry.Point(event.x - this._canvasOri
gin.x, event.y - this._canvasOrigin.y); |
| 284 var thumbPoint = this._sliderThumbPosition(); |
| 285 if (clickedPoint.distanceTo(thumbPoint) >= WebInspector.CSSShadowEditor.slid
erThumbRadius) |
| 286 this._dragMove(event); |
| 287 return true; |
| 288 } |
| 289 |
| 290 /** |
| 291 * @param {!MouseEvent} event |
| 292 */ |
| 293 _dragMove(event) { |
| 294 var point = new WebInspector.Geometry.Point(event.x - this._canvasOrigin.x,
event.y - this._canvasOrigin.y); |
| 295 if (event.shiftKey) |
| 296 point = this._snapToClosestDirection(point); |
| 297 var constrainedPoint = this._constrainPoint(point, this._innerCanvasSize); |
| 298 var newX = Math.round((constrainedPoint.x / this._innerCanvasSize) * WebInsp
ector.CSSShadowEditor.maxRange); |
| 299 var newY = Math.round((constrainedPoint.y / this._innerCanvasSize) * WebInsp
ector.CSSShadowEditor.maxRange); |
| 300 |
| 301 if (event.shiftKey) { |
| 302 this._model.setOffsetX( |
| 303 new WebInspector.CSSLength(newX, this._model.offsetX().unit || WebInsp
ector.CSSShadowEditor.defaultUnit)); |
| 304 this._model.setOffsetY( |
| 305 new WebInspector.CSSLength(newY, this._model.offsetY().unit || WebInsp
ector.CSSShadowEditor.defaultUnit)); |
| 306 } else { |
| 307 if (!event.altKey) |
| 308 this._model.setOffsetX( |
| 309 new WebInspector.CSSLength(newX, this._model.offsetX().unit || WebIn
spector.CSSShadowEditor.defaultUnit)); |
| 310 if (!WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(event)) |
| 311 this._model.setOffsetY( |
| 312 new WebInspector.CSSLength(newY, this._model.offsetY().unit || WebIn
spector.CSSShadowEditor.defaultUnit)); |
| 313 } |
| 314 this._xInput.value = this._model.offsetX().asCSSText(); |
| 315 this._yInput.value = this._model.offsetY().asCSSText(); |
| 316 this._xInput.classList.remove('invalid'); |
| 317 this._yInput.classList.remove('invalid'); |
| 318 this._updateCanvas(true); |
| 319 this.dispatchEventToListeners(WebInspector.CSSShadowEditor.Events.ShadowChan
ged, this._model); |
| 320 } |
| 321 |
| 322 _onCanvasBlur() { |
| 323 this._updateCanvas(false); |
| 324 } |
| 325 |
| 326 /** |
| 327 * @param {!Event} event |
| 328 */ |
| 329 _onCanvasArrowKey(event) { |
| 330 var shiftX = 0; |
| 331 var shiftY = 0; |
| 332 if (event.key === 'ArrowRight') |
| 333 shiftX = 1; |
| 334 else if (event.key === 'ArrowLeft') |
| 335 shiftX = -1; |
| 336 else if (event.key === 'ArrowUp') |
| 337 shiftY = -1; |
| 338 else if (event.key === 'ArrowDown') |
| 339 shiftY = 1; |
| 340 |
| 341 if (!shiftX && !shiftY) |
| 342 return; |
| 343 event.consume(true); |
| 344 |
| 345 if (shiftX) { |
| 346 var offsetX = this._model.offsetX(); |
| 347 var newAmount = Number.constrain( |
| 348 offsetX.amount + shiftX, -WebInspector.CSSShadowEditor.maxRange, WebIn
spector.CSSShadowEditor.maxRange); |
| 349 if (newAmount === offsetX.amount) |
| 350 return; |
| 351 this._model.setOffsetX( |
| 352 new WebInspector.CSSLength(newAmount, offsetX.unit || WebInspector.CSS
ShadowEditor.defaultUnit)); |
| 353 this._xInput.value = this._model.offsetX().asCSSText(); |
| 354 this._xInput.classList.remove('invalid'); |
| 355 } |
| 356 if (shiftY) { |
| 357 var offsetY = this._model.offsetY(); |
| 358 var newAmount = Number.constrain( |
| 359 offsetY.amount + shiftY, -WebInspector.CSSShadowEditor.maxRange, WebIn
spector.CSSShadowEditor.maxRange); |
| 360 if (newAmount === offsetY.amount) |
| 361 return; |
| 362 this._model.setOffsetY( |
| 363 new WebInspector.CSSLength(newAmount, offsetY.unit || WebInspector.CSS
ShadowEditor.defaultUnit)); |
| 364 this._yInput.value = this._model.offsetY().asCSSText(); |
| 365 this._yInput.classList.remove('invalid'); |
| 366 } |
| 367 this._updateCanvas(true); |
| 368 this.dispatchEventToListeners(WebInspector.CSSShadowEditor.Events.ShadowChan
ged, this._model); |
| 369 } |
| 370 |
| 371 /** |
| 372 * @param {!WebInspector.Geometry.Point} point |
| 373 * @param {number} max |
| 374 * @return {!WebInspector.Geometry.Point} |
| 375 */ |
| 376 _constrainPoint(point, max) { |
| 377 if (Math.abs(point.x) <= max && Math.abs(point.y) <= max) |
| 378 return new WebInspector.Geometry.Point(point.x, point.y); |
| 379 return point.scale(max / Math.max(Math.abs(point.x), Math.abs(point.y))); |
| 380 } |
| 381 |
| 382 /** |
| 383 * @param {!WebInspector.Geometry.Point} point |
| 384 * @return {!WebInspector.Geometry.Point} |
| 385 */ |
| 386 _snapToClosestDirection(point) { |
| 387 var minDistance = Number.MAX_VALUE; |
| 388 var closestPoint = point; |
| 389 |
| 390 var directions = [ |
| 391 new WebInspector.Geometry.Point(0, -1), // North |
| 392 new WebInspector.Geometry.Point(1, -1), // Northeast |
| 393 new WebInspector.Geometry.Point(1, 0), // East |
| 394 new WebInspector.Geometry.Point(1, 1) // Southeast |
| 395 ]; |
| 396 |
| 397 for (var direction of directions) { |
| 398 var projection = point.projectOn(direction); |
| 399 var distance = point.distanceTo(projection); |
| 400 if (distance < minDistance) { |
| 401 minDistance = distance; |
| 402 closestPoint = projection; |
| 403 } |
| 404 } |
| 405 |
| 406 return closestPoint; |
| 407 } |
| 408 |
| 409 /** |
| 410 * @return {!WebInspector.Geometry.Point} |
| 411 */ |
| 412 _sliderThumbPosition() { |
| 413 var x = (this._model.offsetX().amount / WebInspector.CSSShadowEditor.maxRang
e) * this._innerCanvasSize; |
| 414 var y = (this._model.offsetY().amount / WebInspector.CSSShadowEditor.maxRang
e) * this._innerCanvasSize; |
| 415 return this._constrainPoint(new WebInspector.Geometry.Point(x, y), this._inn
erCanvasSize); |
| 416 } |
| 45 }; | 417 }; |
| 46 | 418 |
| 47 /** @enum {symbol} */ | 419 /** @enum {symbol} */ |
| 48 WebInspector.CSSShadowEditor.Events = { | 420 WebInspector.CSSShadowEditor.Events = { |
| 49 ShadowChanged: Symbol("ShadowChanged") | 421 ShadowChanged: Symbol('ShadowChanged') |
| 50 }; | 422 }; |
| 51 | 423 |
| 52 /** @type {number} */ | 424 /** @type {number} */ |
| 53 WebInspector.CSSShadowEditor.maxRange = 20; | 425 WebInspector.CSSShadowEditor.maxRange = 20; |
| 54 /** @type {string} */ | 426 /** @type {string} */ |
| 55 WebInspector.CSSShadowEditor.defaultUnit = "px"; | 427 WebInspector.CSSShadowEditor.defaultUnit = 'px'; |
| 56 /** @type {number} */ | 428 /** @type {number} */ |
| 57 WebInspector.CSSShadowEditor.sliderThumbRadius = 6; | 429 WebInspector.CSSShadowEditor.sliderThumbRadius = 6; |
| 58 /** @type {number} */ | 430 /** @type {number} */ |
| 59 WebInspector.CSSShadowEditor.canvasSize = 88; | 431 WebInspector.CSSShadowEditor.canvasSize = 88; |
| 60 | |
| 61 WebInspector.CSSShadowEditor.prototype = { | |
| 62 /** | |
| 63 * @param {!Element} field | |
| 64 * @param {string} propertyName | |
| 65 * @return {!Element} | |
| 66 */ | |
| 67 _createTextInput: function(field, propertyName) | |
| 68 { | |
| 69 var label = field.createChild("label", "shadow-editor-label"); | |
| 70 label.textContent = propertyName; | |
| 71 label.setAttribute("for", propertyName); | |
| 72 var textInput = field.createChild("input", "shadow-editor-text-input"); | |
| 73 textInput.type = "text"; | |
| 74 textInput.id = propertyName; | |
| 75 textInput.addEventListener("keydown", this._handleValueModification.bind
(this), false); | |
| 76 textInput.addEventListener("mousewheel", this._handleValueModification.b
ind(this), false); | |
| 77 textInput.addEventListener("input", this._onTextInput.bind(this), false)
; | |
| 78 textInput.addEventListener("blur", this._onTextBlur.bind(this), false); | |
| 79 return textInput; | |
| 80 }, | |
| 81 | |
| 82 /** | |
| 83 * @param {!Element} field | |
| 84 * @return {!Element} | |
| 85 */ | |
| 86 _createSlider: function(field) | |
| 87 { | |
| 88 var slider = createSliderLabel(0, WebInspector.CSSShadowEditor.maxRange,
-1); | |
| 89 slider.addEventListener("input", this._onSliderInput.bind(this), false); | |
| 90 field.appendChild(slider); | |
| 91 return slider; | |
| 92 }, | |
| 93 | |
| 94 /** | |
| 95 * @override | |
| 96 */ | |
| 97 wasShown: function() | |
| 98 { | |
| 99 this._updateUI(); | |
| 100 }, | |
| 101 | |
| 102 /** | |
| 103 * @param {!WebInspector.CSSShadowModel} model | |
| 104 */ | |
| 105 setModel: function(model) | |
| 106 { | |
| 107 this._model = model; | |
| 108 this._typeField.hidden = !model.isBoxShadow(); | |
| 109 this._spreadField.hidden = !model.isBoxShadow(); | |
| 110 this._updateUI(); | |
| 111 }, | |
| 112 | |
| 113 _updateUI: function() | |
| 114 { | |
| 115 this._updateButtons(); | |
| 116 this._xInput.value = this._model.offsetX().asCSSText(); | |
| 117 this._yInput.value = this._model.offsetY().asCSSText(); | |
| 118 this._blurInput.value = this._model.blurRadius().asCSSText(); | |
| 119 this._spreadInput.value = this._model.spreadRadius().asCSSText(); | |
| 120 this._blurSlider.value = this._model.blurRadius().amount; | |
| 121 this._spreadSlider.value = this._model.spreadRadius().amount; | |
| 122 this._updateCanvas(false); | |
| 123 }, | |
| 124 | |
| 125 _updateButtons: function() | |
| 126 { | |
| 127 this._insetButton.classList.toggle("enabled", this._model.inset()); | |
| 128 this._outsetButton.classList.toggle("enabled", !this._model.inset()); | |
| 129 }, | |
| 130 | |
| 131 /** | |
| 132 * @param {boolean} drawFocus | |
| 133 */ | |
| 134 _updateCanvas: function(drawFocus) | |
| 135 { | |
| 136 var context = this._xySlider.getContext("2d"); | |
| 137 context.clearRect(0, 0, this._xySlider.width, this._xySlider.height); | |
| 138 | |
| 139 // Draw dashed axes. | |
| 140 context.save(); | |
| 141 context.setLineDash([1, 1]); | |
| 142 context.strokeStyle = "rgba(210, 210, 210, 0.8)"; | |
| 143 context.beginPath(); | |
| 144 context.moveTo(this._halfCanvasSize, 0); | |
| 145 context.lineTo(this._halfCanvasSize, WebInspector.CSSShadowEditor.canvas
Size); | |
| 146 context.moveTo(0, this._halfCanvasSize); | |
| 147 context.lineTo(WebInspector.CSSShadowEditor.canvasSize, this._halfCanvas
Size); | |
| 148 context.stroke(); | |
| 149 context.restore(); | |
| 150 | |
| 151 var thumbPoint = this._sliderThumbPosition(); | |
| 152 // Draw 2D slider line. | |
| 153 context.save(); | |
| 154 context.translate(this._halfCanvasSize, this._halfCanvasSize); | |
| 155 context.lineWidth = 2; | |
| 156 context.strokeStyle = "rgba(130, 130, 130, 0.75)"; | |
| 157 context.beginPath(); | |
| 158 context.moveTo(0, 0); | |
| 159 context.lineTo(thumbPoint.x, thumbPoint.y); | |
| 160 context.stroke(); | |
| 161 // Draw 2D slider thumb. | |
| 162 if (drawFocus) { | |
| 163 context.beginPath(); | |
| 164 context.fillStyle = "rgba(66, 133, 244, 0.4)"; | |
| 165 context.arc(thumbPoint.x, thumbPoint.y, WebInspector.CSSShadowEditor
.sliderThumbRadius + 2, 0, 2 * Math.PI); | |
| 166 context.fill(); | |
| 167 } | |
| 168 context.beginPath(); | |
| 169 context.fillStyle = "#4285F4"; | |
| 170 context.arc(thumbPoint.x, thumbPoint.y, WebInspector.CSSShadowEditor.sli
derThumbRadius, 0, 2 * Math.PI); | |
| 171 context.fill(); | |
| 172 context.restore(); | |
| 173 }, | |
| 174 | |
| 175 /** | |
| 176 * @param {!Event} event | |
| 177 */ | |
| 178 _onButtonClick: function(event) | |
| 179 { | |
| 180 var insetClicked = (event.currentTarget === this._insetButton); | |
| 181 if (insetClicked && this._model.inset() || !insetClicked && !this._model
.inset()) | |
| 182 return; | |
| 183 this._model.setInset(insetClicked); | |
| 184 this._updateButtons(); | |
| 185 this.dispatchEventToListeners(WebInspector.CSSShadowEditor.Events.Shadow
Changed, this._model); | |
| 186 }, | |
| 187 | |
| 188 /** | |
| 189 * @param {!Event} event | |
| 190 */ | |
| 191 _handleValueModification: function(event) | |
| 192 { | |
| 193 var modifiedValue = WebInspector.createReplacementString(event.currentTa
rget.value, event, customNumberHandler); | |
| 194 if (!modifiedValue) | |
| 195 return; | |
| 196 var length = WebInspector.CSSLength.parse(modifiedValue); | |
| 197 if (!length) | |
| 198 return; | |
| 199 if (event.currentTarget === this._blurInput && length.amount < 0) | |
| 200 length.amount = 0; | |
| 201 event.currentTarget.value = length.asCSSText(); | |
| 202 event.currentTarget.selectionStart = 0; | |
| 203 event.currentTarget.selectionEnd = event.currentTarget.value.length; | |
| 204 this._onTextInput(event); | |
| 205 event.consume(true); | |
| 206 | |
| 207 /** | |
| 208 * @param {string} prefix | |
| 209 * @param {number} number | |
| 210 * @param {string} suffix | |
| 211 * @return {string} | |
| 212 */ | |
| 213 function customNumberHandler(prefix, number, suffix) | |
| 214 { | |
| 215 if (!suffix.length) | |
| 216 suffix = WebInspector.CSSShadowEditor.defaultUnit; | |
| 217 return prefix + number + suffix; | |
| 218 } | |
| 219 }, | |
| 220 | |
| 221 /** | |
| 222 * @param {!Event} event | |
| 223 */ | |
| 224 _onTextInput: function(event) | |
| 225 { | |
| 226 this._changedElement = event.currentTarget; | |
| 227 this._changedElement.classList.remove("invalid"); | |
| 228 var length = WebInspector.CSSLength.parse(event.currentTarget.value); | |
| 229 if (!length || event.currentTarget === this._blurInput && length.amount
< 0) | |
| 230 return; | |
| 231 if (event.currentTarget === this._xInput) { | |
| 232 this._model.setOffsetX(length); | |
| 233 this._updateCanvas(false); | |
| 234 } else if (event.currentTarget === this._yInput) { | |
| 235 this._model.setOffsetY(length); | |
| 236 this._updateCanvas(false); | |
| 237 } else if (event.currentTarget === this._blurInput) { | |
| 238 this._model.setBlurRadius(length); | |
| 239 this._blurSlider.value = length.amount; | |
| 240 } else if (event.currentTarget === this._spreadInput) { | |
| 241 this._model.setSpreadRadius(length); | |
| 242 this._spreadSlider.value = length.amount; | |
| 243 } | |
| 244 this.dispatchEventToListeners(WebInspector.CSSShadowEditor.Events.Shadow
Changed, this._model); | |
| 245 }, | |
| 246 | |
| 247 _onTextBlur: function() | |
| 248 { | |
| 249 if (!this._changedElement) | |
| 250 return; | |
| 251 var length = !this._changedElement.value.trim() ? WebInspector.CSSLength
.zero() : WebInspector.CSSLength.parse(this._changedElement.value); | |
| 252 if (!length) | |
| 253 length = WebInspector.CSSLength.parse(this._changedElement.value + W
ebInspector.CSSShadowEditor.defaultUnit); | |
| 254 if (!length) { | |
| 255 this._changedElement.classList.add("invalid"); | |
| 256 this._changedElement = null; | |
| 257 return; | |
| 258 } | |
| 259 if (this._changedElement === this._xInput) { | |
| 260 this._model.setOffsetX(length); | |
| 261 this._xInput.value = length.asCSSText(); | |
| 262 this._updateCanvas(false); | |
| 263 } else if (this._changedElement === this._yInput) { | |
| 264 this._model.setOffsetY(length); | |
| 265 this._yInput.value = length.asCSSText(); | |
| 266 this._updateCanvas(false); | |
| 267 } else if (this._changedElement === this._blurInput) { | |
| 268 if (length.amount < 0) | |
| 269 length = WebInspector.CSSLength.zero(); | |
| 270 this._model.setBlurRadius(length); | |
| 271 this._blurInput.value = length.asCSSText(); | |
| 272 this._blurSlider.value = length.amount; | |
| 273 } else if (this._changedElement === this._spreadInput) { | |
| 274 this._model.setSpreadRadius(length); | |
| 275 this._spreadInput.value = length.asCSSText(); | |
| 276 this._spreadSlider.value = length.amount; | |
| 277 } | |
| 278 this._changedElement = null; | |
| 279 this.dispatchEventToListeners(WebInspector.CSSShadowEditor.Events.Shadow
Changed, this._model); | |
| 280 }, | |
| 281 | |
| 282 /** | |
| 283 * @param {!Event} event | |
| 284 */ | |
| 285 _onSliderInput: function(event) | |
| 286 { | |
| 287 if (event.currentTarget === this._blurSlider) { | |
| 288 this._model.setBlurRadius(new WebInspector.CSSLength(this._blurSlide
r.value, this._model.blurRadius().unit || WebInspector.CSSShadowEditor.defaultUn
it)); | |
| 289 this._blurInput.value = this._model.blurRadius().asCSSText(); | |
| 290 this._blurInput.classList.remove("invalid"); | |
| 291 } else if (event.currentTarget === this._spreadSlider) { | |
| 292 this._model.setSpreadRadius(new WebInspector.CSSLength(this._spreadS
lider.value, this._model.spreadRadius().unit || WebInspector.CSSShadowEditor.def
aultUnit)); | |
| 293 this._spreadInput.value = this._model.spreadRadius().asCSSText(); | |
| 294 this._spreadInput.classList.remove("invalid"); | |
| 295 } | |
| 296 this.dispatchEventToListeners(WebInspector.CSSShadowEditor.Events.Shadow
Changed, this._model); | |
| 297 }, | |
| 298 | |
| 299 /** | |
| 300 * @param {!MouseEvent} event | |
| 301 * @return {boolean} | |
| 302 */ | |
| 303 _dragStart: function(event) | |
| 304 { | |
| 305 this._xySlider.focus(); | |
| 306 this._updateCanvas(true); | |
| 307 this._canvasOrigin = new WebInspector.Geometry.Point(this._xySlider.tota
lOffsetLeft() + this._halfCanvasSize, this._xySlider.totalOffsetTop() + this._ha
lfCanvasSize); | |
| 308 var clickedPoint = new WebInspector.Geometry.Point(event.x - this._canva
sOrigin.x, event.y - this._canvasOrigin.y); | |
| 309 var thumbPoint = this._sliderThumbPosition(); | |
| 310 if (clickedPoint.distanceTo(thumbPoint) >= WebInspector.CSSShadowEditor.
sliderThumbRadius) | |
| 311 this._dragMove(event); | |
| 312 return true; | |
| 313 }, | |
| 314 | |
| 315 /** | |
| 316 * @param {!MouseEvent} event | |
| 317 */ | |
| 318 _dragMove: function(event) | |
| 319 { | |
| 320 var point = new WebInspector.Geometry.Point(event.x - this._canvasOrigin
.x, event.y - this._canvasOrigin.y); | |
| 321 if (event.shiftKey) | |
| 322 point = this._snapToClosestDirection(point); | |
| 323 var constrainedPoint = this._constrainPoint(point, this._innerCanvasSize
); | |
| 324 var newX = Math.round((constrainedPoint.x / this._innerCanvasSize) * Web
Inspector.CSSShadowEditor.maxRange); | |
| 325 var newY = Math.round((constrainedPoint.y / this._innerCanvasSize) * Web
Inspector.CSSShadowEditor.maxRange); | |
| 326 | |
| 327 if (event.shiftKey) { | |
| 328 this._model.setOffsetX(new WebInspector.CSSLength(newX, this._model.
offsetX().unit || WebInspector.CSSShadowEditor.defaultUnit)); | |
| 329 this._model.setOffsetY(new WebInspector.CSSLength(newY, this._model.
offsetY().unit || WebInspector.CSSShadowEditor.defaultUnit)); | |
| 330 } else { | |
| 331 if (!event.altKey) | |
| 332 this._model.setOffsetX(new WebInspector.CSSLength(newX, this._mo
del.offsetX().unit || WebInspector.CSSShadowEditor.defaultUnit)); | |
| 333 if (!WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(event)) | |
| 334 this._model.setOffsetY(new WebInspector.CSSLength(newY, this._mo
del.offsetY().unit || WebInspector.CSSShadowEditor.defaultUnit)); | |
| 335 } | |
| 336 this._xInput.value = this._model.offsetX().asCSSText(); | |
| 337 this._yInput.value = this._model.offsetY().asCSSText(); | |
| 338 this._xInput.classList.remove("invalid"); | |
| 339 this._yInput.classList.remove("invalid"); | |
| 340 this._updateCanvas(true); | |
| 341 this.dispatchEventToListeners(WebInspector.CSSShadowEditor.Events.Shadow
Changed, this._model); | |
| 342 }, | |
| 343 | |
| 344 _onCanvasBlur: function() | |
| 345 { | |
| 346 this._updateCanvas(false); | |
| 347 }, | |
| 348 | |
| 349 /** | |
| 350 * @param {!Event} event | |
| 351 */ | |
| 352 _onCanvasArrowKey: function(event) | |
| 353 { | |
| 354 var shiftX = 0; | |
| 355 var shiftY = 0; | |
| 356 if (event.key === "ArrowRight") | |
| 357 shiftX = 1; | |
| 358 else if (event.key === "ArrowLeft") | |
| 359 shiftX = -1; | |
| 360 else if (event.key === "ArrowUp") | |
| 361 shiftY = -1; | |
| 362 else if (event.key === "ArrowDown") | |
| 363 shiftY = 1; | |
| 364 | |
| 365 if (!shiftX && !shiftY) | |
| 366 return; | |
| 367 event.consume(true); | |
| 368 | |
| 369 if (shiftX) { | |
| 370 var offsetX = this._model.offsetX(); | |
| 371 var newAmount = Number.constrain(offsetX.amount + shiftX, -WebInspec
tor.CSSShadowEditor.maxRange, WebInspector.CSSShadowEditor.maxRange); | |
| 372 if (newAmount === offsetX.amount) | |
| 373 return; | |
| 374 this._model.setOffsetX(new WebInspector.CSSLength(newAmount, offsetX
.unit || WebInspector.CSSShadowEditor.defaultUnit)); | |
| 375 this._xInput.value = this._model.offsetX().asCSSText(); | |
| 376 this._xInput.classList.remove("invalid"); | |
| 377 } | |
| 378 if (shiftY) { | |
| 379 var offsetY = this._model.offsetY(); | |
| 380 var newAmount = Number.constrain(offsetY.amount + shiftY, -WebInspec
tor.CSSShadowEditor.maxRange, WebInspector.CSSShadowEditor.maxRange); | |
| 381 if (newAmount === offsetY.amount) | |
| 382 return; | |
| 383 this._model.setOffsetY(new WebInspector.CSSLength(newAmount, offsetY
.unit || WebInspector.CSSShadowEditor.defaultUnit)); | |
| 384 this._yInput.value = this._model.offsetY().asCSSText(); | |
| 385 this._yInput.classList.remove("invalid"); | |
| 386 } | |
| 387 this._updateCanvas(true); | |
| 388 this.dispatchEventToListeners(WebInspector.CSSShadowEditor.Events.Shadow
Changed, this._model); | |
| 389 }, | |
| 390 | |
| 391 /** | |
| 392 * @param {!WebInspector.Geometry.Point} point | |
| 393 * @param {number} max | |
| 394 * @return {!WebInspector.Geometry.Point} | |
| 395 */ | |
| 396 _constrainPoint: function(point, max) | |
| 397 { | |
| 398 if (Math.abs(point.x) <= max && Math.abs(point.y) <= max) | |
| 399 return new WebInspector.Geometry.Point(point.x, point.y); | |
| 400 return point.scale(max / Math.max(Math.abs(point.x), Math.abs(point.y)))
; | |
| 401 }, | |
| 402 | |
| 403 /** | |
| 404 * @param {!WebInspector.Geometry.Point} point | |
| 405 * @return {!WebInspector.Geometry.Point} | |
| 406 */ | |
| 407 _snapToClosestDirection: function(point) | |
| 408 { | |
| 409 var minDistance = Number.MAX_VALUE; | |
| 410 var closestPoint = point; | |
| 411 | |
| 412 var directions = [ | |
| 413 new WebInspector.Geometry.Point(0, -1), // North | |
| 414 new WebInspector.Geometry.Point(1, -1), // Northeast | |
| 415 new WebInspector.Geometry.Point(1, 0), // East | |
| 416 new WebInspector.Geometry.Point(1, 1) // Southeast | |
| 417 ]; | |
| 418 | |
| 419 for (var direction of directions) { | |
| 420 var projection = point.projectOn(direction); | |
| 421 var distance = point.distanceTo(projection); | |
| 422 if (distance < minDistance) { | |
| 423 minDistance = distance; | |
| 424 closestPoint = projection; | |
| 425 } | |
| 426 } | |
| 427 | |
| 428 return closestPoint; | |
| 429 }, | |
| 430 | |
| 431 /** | |
| 432 * @return {!WebInspector.Geometry.Point} | |
| 433 */ | |
| 434 _sliderThumbPosition: function() | |
| 435 { | |
| 436 var x = (this._model.offsetX().amount / WebInspector.CSSShadowEditor.max
Range) * this._innerCanvasSize; | |
| 437 var y = (this._model.offsetY().amount / WebInspector.CSSShadowEditor.max
Range) * this._innerCanvasSize; | |
| 438 return this._constrainPoint(new WebInspector.Geometry.Point(x, y), this.
_innerCanvasSize); | |
| 439 }, | |
| 440 | |
| 441 __proto__: WebInspector.VBox.prototype | |
| 442 }; | |
| OLD | NEW |