Chromium Code Reviews| Index: third_party/WebKit/Source/devtools/front_end/ui/CSSShadowEditor.js |
| diff --git a/third_party/WebKit/Source/devtools/front_end/ui/CSSShadowEditor.js b/third_party/WebKit/Source/devtools/front_end/ui/CSSShadowEditor.js |
| index 74a23375f05e4e34776ea655363b76441b91a928..58060e00f7cc5ee05fe4504f555124a9dde65309 100644 |
| --- a/third_party/WebKit/Source/devtools/front_end/ui/CSSShadowEditor.js |
| +++ b/third_party/WebKit/Source/devtools/front_end/ui/CSSShadowEditor.js |
| @@ -21,20 +21,27 @@ WebInspector.CSSShadowEditor = function() |
| this._insetButton.textContent = WebInspector.UIString("Inset"); |
| this._insetButton.addEventListener("click", this._onButtonClick.bind(this), false); |
| - var inputs; |
| - inputs = this._createSliderField(WebInspector.UIString("X offset"), true); |
| - this._xInput = inputs.textInput; |
| - this._xSlider = inputs.rangeInput; |
| - inputs = this._createSliderField(WebInspector.UIString("Y offset"), true); |
| - this._yInput = inputs.textInput; |
| - this._ySlider = inputs.rangeInput; |
| - inputs = this._createSliderField(WebInspector.UIString("Blur"), false); |
| - this._blurInput = inputs.textInput; |
| - this._blurSlider = inputs.rangeInput; |
| - inputs = this._createSliderField(WebInspector.UIString("Spread"), false); |
| - this._spreadInput = inputs.textInput; |
| - this._spreadSlider = inputs.rangeInput; |
| - this._spreadField = inputs.field; |
| + var xField = this.contentElement.createChild("div", "shadow-editor-field"); |
| + this._xInput = this._createTextInput(xField, WebInspector.UIString("X offset")); |
| + var yField = this.contentElement.createChild("div", "shadow-editor-field"); |
| + this._yInput = this._createTextInput(yField, WebInspector.UIString("Y offset")); |
| + this._xySlider = xField.createChild("canvas", "shadow-editor-2D-slider"); |
| + this._xySlider.width = WebInspector.CSSShadowEditor.canvasSize; |
| + this._xySlider.height = WebInspector.CSSShadowEditor.canvasSize; |
| + this._xySlider.tabIndex = -1; |
| + this._halfCanvasSize = WebInspector.CSSShadowEditor.canvasSize / 2; |
| + this._innerCanvasSize = this._halfCanvasSize - WebInspector.CSSShadowEditor.sliderThumbRadius; |
| + WebInspector.installDragHandle(this._xySlider, this._dragStart.bind(this), this._dragMove.bind(this), null, "default"); |
| + this._xySlider.addEventListener("keydown", this._onCanvasArrowKey.bind(this), false); |
| + this._xySlider.addEventListener("blur", this._onCanvasBlur.bind(this), false); |
| + |
| + var blurField = this.contentElement.createChild("div", "shadow-editor-blur-field"); |
| + this._blurInput = this._createTextInput(blurField, WebInspector.UIString("Blur")); |
| + this._blurSlider = this._createSlider(blurField); |
| + |
| + this._spreadField = this.contentElement.createChild("div", "shadow-editor-field"); |
| + this._spreadInput = this._createTextInput(this._spreadField, WebInspector.UIString("Spread")); |
| + this._spreadSlider = this._createSlider(this._spreadField); |
| } |
| /** @enum {symbol} */ |
| @@ -43,19 +50,22 @@ WebInspector.CSSShadowEditor.Events = { |
| } |
| /** @type {number} */ |
| -WebInspector.CSSShadowEditor.maxRange = 40; |
| +WebInspector.CSSShadowEditor.maxRange = 20; |
| /** @type {string} */ |
| WebInspector.CSSShadowEditor.defaultUnit = "px"; |
| +/** @type {number} */ |
| +WebInspector.CSSShadowEditor.sliderThumbRadius = 6; |
| +/** @type {number} */ |
| +WebInspector.CSSShadowEditor.canvasSize = 88; |
| WebInspector.CSSShadowEditor.prototype = { |
| /** |
| + * @param {!Element} field |
| * @param {string} propertyName |
| - * @param {boolean} negativeAllowed |
| - * @return {{textInput: !Element, rangeInput: !Element, field: !Element}} |
| + * @return {!Element} |
| */ |
| - _createSliderField: function(propertyName, negativeAllowed) |
| + _createTextInput: function(field, propertyName) |
| { |
| - var field = this.contentElement.createChild("div", "shadow-editor-field"); |
| var label = field.createChild("label", "shadow-editor-label"); |
| label.textContent = propertyName; |
| label.setAttribute("for", propertyName); |
| @@ -66,11 +76,19 @@ WebInspector.CSSShadowEditor.prototype = { |
| textInput.addEventListener("mousewheel", this._handleValueModification.bind(this), false); |
| textInput.addEventListener("input", this._onTextInput.bind(this), false); |
| textInput.addEventListener("blur", this._onTextBlur.bind(this), false); |
| - var halfRange = WebInspector.CSSShadowEditor.maxRange / 2; |
| - var slider = negativeAllowed ? createSliderLabel(-halfRange, halfRange) : createSliderLabel(0, WebInspector.CSSShadowEditor.maxRange); |
| + return textInput; |
| + }, |
| + |
| + /** |
| + * @param {!Element} field |
| + * @return {!Element} |
| + */ |
| + _createSlider: function(field) |
| + { |
| + var slider = createSliderLabel(0, WebInspector.CSSShadowEditor.maxRange, -1); |
| slider.addEventListener("input", this._onSliderInput.bind(this), false); |
| field.appendChild(slider); |
| - return {field: field, textInput: textInput, rangeInput: slider}; |
| + return slider; |
| }, |
| /** |
| @@ -99,10 +117,9 @@ WebInspector.CSSShadowEditor.prototype = { |
| this._yInput.value = this._model.offsetY().asCSSText(); |
| this._blurInput.value = this._model.blurRadius().asCSSText(); |
| this._spreadInput.value = this._model.spreadRadius().asCSSText(); |
| - this._xSlider.value = this._model.offsetX().amount; |
| - this._ySlider.value = this._model.offsetY().amount; |
| this._blurSlider.value = this._model.blurRadius().amount; |
| this._spreadSlider.value = this._model.spreadRadius().amount; |
| + this._updateCanvas(); |
| }, |
| _updateButtons: function() |
| @@ -111,6 +128,47 @@ WebInspector.CSSShadowEditor.prototype = { |
| this._outsetButton.classList.toggle("enabled", !this._model.inset()); |
| }, |
| + _updateCanvas: function() |
| + { |
| + var context = this._xySlider.getContext("2d"); |
| + context.clearRect(0, 0, this._xySlider.width, this._xySlider.height); |
| + |
| + // Draw dashed axes. |
| + context.save(); |
| + context.setLineDash([1, 1]); |
| + context.strokeStyle = "rgba(0, 0, 0, 0.2)"; |
| + context.beginPath(); |
| + context.moveTo(this._halfCanvasSize, 0); |
| + context.lineTo(this._halfCanvasSize, WebInspector.CSSShadowEditor.canvasSize); |
| + context.moveTo(0, this._halfCanvasSize); |
| + context.lineTo(WebInspector.CSSShadowEditor.canvasSize, this._halfCanvasSize); |
| + context.stroke(); |
| + context.restore(); |
| + |
| + var thumbPoint = this._sliderThumbPosition(); |
| + // Draw 2D slider line. |
| + context.save(); |
| + context.translate(this._halfCanvasSize, this._halfCanvasSize); |
| + context.lineWidth = 2; |
| + context.strokeStyle = "rgba(0, 0, 0, 0.3)"; |
| + context.beginPath(); |
| + context.moveTo(0, 0); |
| + context.lineTo(thumbPoint.x, thumbPoint.y); |
| + context.stroke(); |
| + // Draw 2D slider thumb. |
| + context.beginPath(); |
| + if (this._xySlider.ownerDocument.deepActiveElement() === this._xySlider) { |
|
lushnikov
2016/08/31 00:48:03
I actually like your "drawFocus" parameter even mo
flandy
2016/08/31 01:35:05
Done.
|
| + context.fillStyle = "rgba(66, 133, 244, 0.4)"; |
| + context.arc(thumbPoint.x, thumbPoint.y, WebInspector.CSSShadowEditor.sliderThumbRadius + 2, 0, 2 * Math.PI); |
| + context.fill(); |
| + } |
| + context.beginPath() |
| + context.fillStyle = "#4285F4"; |
| + context.arc(thumbPoint.x, thumbPoint.y, WebInspector.CSSShadowEditor.sliderThumbRadius, 0, 2 * Math.PI); |
| + context.fill(); |
| + context.restore(); |
| + }, |
| + |
| /** |
| * @param {!Event} event |
| */ |
| @@ -163,16 +221,16 @@ WebInspector.CSSShadowEditor.prototype = { |
| _onTextInput: function(event) |
| { |
| this._changedElement = event.currentTarget; |
| - this._changedElement.classList.toggle("invalid", false); |
| + this._changedElement.classList.remove("invalid"); |
| var length = WebInspector.CSSLength.parse(event.currentTarget.value); |
| if (!length || event.currentTarget === this._blurInput && length.amount < 0) |
| return; |
| if (event.currentTarget === this._xInput) { |
| this._model.setOffsetX(length); |
| - this._xSlider.value = length.amount; |
| + this._updateCanvas(); |
| } else if (event.currentTarget === this._yInput) { |
| this._model.setOffsetY(length); |
| - this._ySlider.value = length.amount; |
| + this._updateCanvas(); |
| } else if (event.currentTarget === this._blurInput) { |
| this._model.setBlurRadius(length); |
| this._blurSlider.value = length.amount; |
| @@ -187,7 +245,7 @@ WebInspector.CSSShadowEditor.prototype = { |
| { |
| if (!this._changedElement) |
| return; |
| - var length = !this._changedElement.value ? WebInspector.CSSLength.zero() : WebInspector.CSSLength.parse(this._changedElement.value); |
| + var length = !this._changedElement.value.trim() ? WebInspector.CSSLength.zero() : WebInspector.CSSLength.parse(this._changedElement.value); |
| if (!length) |
| length = WebInspector.CSSLength.parse(this._changedElement.value + WebInspector.CSSShadowEditor.defaultUnit); |
| if (!length) { |
| @@ -198,11 +256,11 @@ WebInspector.CSSShadowEditor.prototype = { |
| if (this._changedElement === this._xInput) { |
| this._model.setOffsetX(length); |
| this._xInput.value = length.asCSSText(); |
| - this._xSlider.value = length.amount; |
| + this._updateCanvas(); |
| } else if (this._changedElement === this._yInput) { |
| this._model.setOffsetY(length); |
| this._yInput.value = length.asCSSText(); |
| - this._ySlider.value = length.amount; |
| + this._updateCanvas(); |
| } else if (this._changedElement === this._blurInput) { |
| if (length.amount < 0) |
| length = WebInspector.CSSLength.zero(); |
| @@ -223,25 +281,123 @@ WebInspector.CSSShadowEditor.prototype = { |
| */ |
| _onSliderInput: function(event) |
| { |
| - if (event.currentTarget === this._xSlider) { |
| - this._model.setOffsetX(new WebInspector.CSSLength(this._xSlider.value, this._model.offsetX().unit || WebInspector.CSSShadowEditor.defaultUnit)); |
| - this._xInput.value = this._model.offsetX().asCSSText(); |
| - this._xInput.classList.toggle("invalid", false); |
| - } else if (event.currentTarget === this._ySlider) { |
| - this._model.setOffsetY(new WebInspector.CSSLength(this._ySlider.value, this._model.offsetY().unit || WebInspector.CSSShadowEditor.defaultUnit)); |
| - this._yInput.value = this._model.offsetY().asCSSText(); |
| - this._yInput.classList.toggle("invalid", false); |
| - } else if (event.currentTarget === this._blurSlider) { |
| + if (event.currentTarget === this._blurSlider) { |
| this._model.setBlurRadius(new WebInspector.CSSLength(this._blurSlider.value, this._model.blurRadius().unit || WebInspector.CSSShadowEditor.defaultUnit)); |
| this._blurInput.value = this._model.blurRadius().asCSSText(); |
| - this._blurInput.classList.toggle("invalid", false); |
| + this._blurInput.classList.remove("invalid"); |
| } else if (event.currentTarget === this._spreadSlider) { |
| this._model.setSpreadRadius(new WebInspector.CSSLength(this._spreadSlider.value, this._model.spreadRadius().unit || WebInspector.CSSShadowEditor.defaultUnit)); |
| this._spreadInput.value = this._model.spreadRadius().asCSSText(); |
| - this._spreadInput.classList.toggle("invalid", false); |
| + this._spreadInput.classList.remove("invalid"); |
| + } |
| + this.dispatchEventToListeners(WebInspector.CSSShadowEditor.Events.ShadowChanged, this._model); |
| + }, |
| + |
| + /** |
| + * @param {!Event} event |
| + * @return {boolean} |
| + */ |
| + _dragStart: function(event) |
| + { |
| + this._xySlider.focus(); |
| + this._updateCanvas(); |
| + this._canvasOrigin = new WebInspector.Geometry.Point(this._xySlider.totalOffsetLeft() + this._halfCanvasSize, this._xySlider.totalOffsetTop() + this._halfCanvasSize); |
| + var clickedPoint = new WebInspector.Geometry.Point(event.x - this._canvasOrigin.x, event.y - this._canvasOrigin.y); |
| + var thumbPoint = this._sliderThumbPosition(); |
| + if (clickedPoint.distanceTo(thumbPoint) >= WebInspector.CSSShadowEditor.sliderThumbRadius) |
| + this._dragMove(event); |
| + return true; |
| + }, |
| + |
| + /** |
| + * @param {!Event} event |
| + */ |
| + _dragMove: function(event) |
| + { |
| + var point = new WebInspector.Geometry.Point(event.x - this._canvasOrigin.x, event.y - this._canvasOrigin.y); |
| + var constrainedPoint = this._constrainPoint(point, this._innerCanvasSize); |
| + var newX = Math.round((constrainedPoint.x / this._innerCanvasSize) * WebInspector.CSSShadowEditor.maxRange); |
| + var newY = Math.round((constrainedPoint.y / this._innerCanvasSize) * WebInspector.CSSShadowEditor.maxRange); |
| + this._model.setOffsetX(new WebInspector.CSSLength(newX, this._model.offsetX().unit || WebInspector.CSSShadowEditor.defaultUnit)); |
| + this._model.setOffsetY(new WebInspector.CSSLength(newY, this._model.offsetY().unit || WebInspector.CSSShadowEditor.defaultUnit)); |
| + this._xInput.value = this._model.offsetX().asCSSText(); |
| + this._yInput.value = this._model.offsetY().asCSSText(); |
| + this._xInput.classList.remove("invalid"); |
| + this._yInput.classList.remove("invalid"); |
| + this._updateCanvas(); |
| + this.dispatchEventToListeners(WebInspector.CSSShadowEditor.Events.ShadowChanged, this._model); |
| + }, |
| + |
| + _onCanvasBlur: function() |
| + { |
| + this._updateCanvas(); |
| + }, |
| + |
| + /** |
| + * @param {!Event} event |
| + */ |
| + _onCanvasArrowKey: function(event) |
| + { |
| + var shiftX = 0; |
| + var shiftY = 0; |
| + if (event.key === "ArrowRight") |
| + shiftX = 1; |
| + else if (event.key === "ArrowLeft") |
| + shiftX = -1; |
| + else if (event.key === "ArrowUp") |
| + shiftY = -1; |
| + else if (event.key === "ArrowDown") |
| + shiftY = 1; |
| + else |
| + return; |
|
lushnikov
2016/08/31 00:48:03
let's do a conventional bail-out
if (!shiftX && !
flandy
2016/08/31 01:35:05
Done.
|
| + event.consume(true); |
| + |
| + var newAmount; |
|
lushnikov
2016/08/31 00:48:03
no need to forward-declare
flandy
2016/08/31 01:35:05
Done.
|
| + if (shiftX) { |
| + var offsetX = this._model.offsetX(); |
| + newAmount = Number.constrain(offsetX.amount + shiftX, -WebInspector.CSSShadowEditor.maxRange, WebInspector.CSSShadowEditor.maxRange); |
| + if (newAmount === offsetX.amount) |
| + return; |
| + this._model.setOffsetX(new WebInspector.CSSLength(newAmount, offsetX.unit || WebInspector.CSSShadowEditor.defaultUnit)); |
| + this._xInput.value = this._model.offsetX().asCSSText(); |
| + this._xInput.classList.remove("invalid"); |
| + } |
| + if (shiftY) { |
| + var offsetY = this._model.offsetY(); |
| + newAmount = Number.constrain(offsetY.amount + shiftY, -WebInspector.CSSShadowEditor.maxRange, WebInspector.CSSShadowEditor.maxRange); |
| + if (newAmount === offsetY.amount) |
| + return; |
| + this._model.setOffsetY(new WebInspector.CSSLength(newAmount, offsetY.unit || WebInspector.CSSShadowEditor.defaultUnit)); |
| + this._yInput.value = this._model.offsetY().asCSSText(); |
| + this._yInput.classList.remove("invalid"); |
| } |
| + this._updateCanvas(); |
| this.dispatchEventToListeners(WebInspector.CSSShadowEditor.Events.ShadowChanged, this._model); |
| }, |
| + /** |
| + * @param {!WebInspector.Geometry.Point} point |
| + * @param {number} max |
| + * @return {!WebInspector.Geometry.Point} |
| + */ |
| + _constrainPoint: function(point, max) |
| + { |
| + var newPoint = new WebInspector.Geometry.Point(point.x, point.y); |
| + if (Math.abs(point.x) <= max && Math.abs(point.y) <= max) |
| + return newPoint; |
| + newPoint.scale(max / Math.max(Math.abs(point.x), Math.abs(point.y))); |
| + return newPoint; |
| + }, |
| + |
| + /** |
| + * @return {!WebInspector.Geometry.Point} |
| + */ |
| + _sliderThumbPosition: function() |
| + { |
| + var x = (this._model.offsetX().amount / WebInspector.CSSShadowEditor.maxRange) * this._innerCanvasSize; |
| + var y = (this._model.offsetY().amount / WebInspector.CSSShadowEditor.maxRange) * this._innerCanvasSize; |
| + return this._constrainPoint(new WebInspector.Geometry.Point(x, y), this._innerCanvasSize); |
| + }, |
| + |
| __proto__: WebInspector.VBox.prototype |
| -} |
| +} |