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 |