summaryrefslogtreecommitdiff
path: root/Source/WebInspectorUI/UserInterface/Views/GradientSlider.js
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebInspectorUI/UserInterface/Views/GradientSlider.js')
-rw-r--r--Source/WebInspectorUI/UserInterface/Views/GradientSlider.js450
1 files changed, 450 insertions, 0 deletions
diff --git a/Source/WebInspectorUI/UserInterface/Views/GradientSlider.js b/Source/WebInspectorUI/UserInterface/Views/GradientSlider.js
new file mode 100644
index 000000000..5a94198d8
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/Views/GradientSlider.js
@@ -0,0 +1,450 @@
+/*
+ * Copyright (C) 2014-2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.GradientSlider = class GradientSlider extends WebInspector.Object
+{
+ constructor(delegate)
+ {
+ super();
+
+ this.delegate = delegate;
+
+ this._element = null;
+ this._stops = [];
+ this._knobs = [];
+
+ this._selectedKnob = null;
+ this._canvas = document.createElement("canvas");
+
+ this._keyboardShortcutEsc = new WebInspector.KeyboardShortcut(null, WebInspector.KeyboardShortcut.Key.Escape);
+ }
+
+ // Public
+
+ get element()
+ {
+ if (!this._element) {
+ this._element = document.createElement("div");
+ this._element.className = "gradient-slider";
+ this._element.appendChild(this._canvas);
+
+ this._addArea = this._element.appendChild(document.createElement("div"));
+ this._addArea.addEventListener("mouseover", this);
+ this._addArea.addEventListener("mousemove", this);
+ this._addArea.addEventListener("mouseout", this);
+ this._addArea.addEventListener("click", this);
+ this._addArea.className = WebInspector.GradientSlider.AddAreaClassName;
+ }
+ return this._element;
+ }
+
+ get stops()
+ {
+ return this._stops;
+ }
+
+ set stops(stops)
+ {
+ this._stops = stops;
+
+ this._updateStops();
+ }
+
+ get selectedStop()
+ {
+ return this._selectedKnob ? this._selectedKnob.stop : null;
+ }
+
+ // Protected
+
+ handleEvent(event)
+ {
+ switch (event.type) {
+ case "mouseover":
+ this._handleMouseover(event);
+ break;
+ case "mousemove":
+ this._handleMousemove(event);
+ break;
+ case "mouseout":
+ this._handleMouseout(event);
+ break;
+ case "click":
+ this._handleClick(event);
+ break;
+ }
+ }
+
+ handleKeydownEvent(event)
+ {
+ if (!this._keyboardShortcutEsc.matchesEvent(event) || !this._selectedKnob || !this._selectedKnob.selected)
+ return false;
+
+ this._selectedKnob.selected = false;
+
+ return true;
+ }
+
+ knobXDidChange(knob)
+ {
+ knob.stop.offset = knob.x / WebInspector.GradientSlider.Width;
+ this._sortStops();
+ this._updateCanvas();
+ }
+
+ knobCanDetach(knob)
+ {
+ return this._knobs.length > 2;
+ }
+
+ knobWillDetach(knob)
+ {
+ knob.element.classList.add(WebInspector.GradientSlider.DetachingClassName);
+
+ this._stops.remove(knob.stop);
+ this._knobs.remove(knob);
+ this._sortStops();
+ this._updateCanvas();
+ }
+
+ knobSelectionChanged(knob)
+ {
+ if (this._selectedKnob && this._selectedKnob !== knob && knob.selected)
+ this._selectedKnob.selected = false;
+
+ this._selectedKnob = knob.selected ? knob : null;
+
+ if (this.delegate && typeof this.delegate.gradientSliderStopWasSelected === "function")
+ this.delegate.gradientSliderStopWasSelected(this, knob.stop);
+
+ if (this._selectedKnob)
+ WebInspector.addWindowKeydownListener(this);
+ else
+ WebInspector.removeWindowKeydownListener(this);
+ }
+
+ // Private
+
+ _handleMouseover(event)
+ {
+ this._updateShadowKnob(event);
+ }
+
+ _handleMousemove(event)
+ {
+ this._updateShadowKnob(event);
+ }
+
+ _handleMouseout(event)
+ {
+ if (!this._shadowKnob)
+ return;
+
+ this._shadowKnob.element.remove();
+ delete this._shadowKnob;
+ }
+
+ _handleClick(event)
+ {
+ this._updateShadowKnob(event);
+
+ this._knobs.push(this._shadowKnob);
+
+ this._shadowKnob.element.classList.remove(WebInspector.GradientSlider.ShadowClassName);
+
+ var stop = {offset: this._shadowKnob.x / WebInspector.GradientSlider.Width, color: this._shadowKnob.wellColor};
+ this._stops.push(stop);
+ this._sortStops();
+ this._updateStops();
+
+ this._knobs[this._stops.indexOf(stop)].selected = true;
+
+ delete this._shadowKnob;
+ }
+
+ _updateShadowKnob(event)
+ {
+ if (!this._shadowKnob) {
+ this._shadowKnob = new WebInspector.GradientSliderKnob(this);
+ this._shadowKnob.element.classList.add(WebInspector.GradientSlider.ShadowClassName);
+ this.element.appendChild(this._shadowKnob.element);
+ }
+
+ this._shadowKnob.x = window.webkitConvertPointFromPageToNode(this.element, new WebKitPoint(event.pageX, event.pageY)).x;
+
+ var colorData = this._canvas.getContext("2d").getImageData(this._shadowKnob.x - 1, 0, 1, 1).data;
+ this._shadowKnob.wellColor = new WebInspector.Color(WebInspector.Color.Format.RGB, [colorData[0], colorData[1], colorData[2], colorData[3] / 255]);
+ }
+
+ _sortStops()
+ {
+ this._stops.sort(function(a, b) {
+ return a.offset - b.offset;
+ });
+ }
+
+ _updateStops()
+ {
+ this._updateCanvas();
+ this._updateKnobs();
+ }
+
+ _updateCanvas()
+ {
+ var w = WebInspector.GradientSlider.Width;
+ var h = WebInspector.GradientSlider.Height;
+
+ this._canvas.width = w;
+ this._canvas.height = h;
+
+ var ctx = this._canvas.getContext("2d");
+ var gradient = ctx.createLinearGradient(0, 0, w, 0);
+ for (var stop of this._stops)
+ gradient.addColorStop(stop.offset, stop.color);
+
+ ctx.clearRect(0, 0, w, h);
+ ctx.fillStyle = gradient;
+ ctx.fillRect(0, 0, w, h);
+
+ if (this.delegate && typeof this.delegate.gradientSliderStopsDidChange === "function")
+ this.delegate.gradientSliderStopsDidChange(this);
+ }
+
+ _updateKnobs()
+ {
+ var selectedStop = this._selectedKnob ? this._selectedKnob.stop : null;
+
+ while (this._knobs.length > this._stops.length)
+ this._knobs.pop().element.remove();
+
+ while (this._knobs.length < this._stops.length) {
+ var knob = new WebInspector.GradientSliderKnob(this);
+ this.element.appendChild(knob.element);
+ this._knobs.push(knob);
+ }
+
+ for (var i = 0; i < this._stops.length; ++i) {
+ var stop = this._stops[i];
+ var knob = this._knobs[i];
+
+ knob.stop = stop;
+ knob.x = Math.round(stop.offset * WebInspector.GradientSlider.Width);
+ knob.selected = stop === selectedStop;
+ }
+ }
+};
+
+WebInspector.GradientSlider.Width = 238;
+WebInspector.GradientSlider.Height = 19;
+
+WebInspector.GradientSlider.AddAreaClassName = "add-area";
+WebInspector.GradientSlider.DetachingClassName = "detaching";
+WebInspector.GradientSlider.ShadowClassName = "shadow";
+
+WebInspector.GradientSliderKnob = class GradientSliderKnob extends WebInspector.Object
+{
+ constructor(delegate)
+ {
+ super();
+
+ this._x = 0;
+ this._y = 0;
+ this._stop = null;
+
+ this.delegate = delegate;
+
+ this._element = document.createElement("div");
+ this._element.className = "gradient-slider-knob";
+
+ // Checkers pattern.
+ this._element.appendChild(document.createElement("img"));
+
+ this._well = this._element.appendChild(document.createElement("div"));
+
+ this._element.addEventListener("mousedown", this);
+ }
+
+ // Public
+
+ get element()
+ {
+ return this._element;
+ }
+
+ get stop()
+ {
+ return this._stop;
+ }
+
+ set stop(stop)
+ {
+ this.wellColor = stop.color;
+ this._stop = stop;
+ }
+
+ get x()
+ {
+ return this._x;
+ }
+
+ set x(x) {
+ this._x = x;
+ this._updateTransform();
+ }
+
+ get y()
+ {
+ return this._x;
+ }
+
+ set y(y) {
+ this._y = y;
+ this._updateTransform();
+ }
+
+ get wellColor()
+ {
+ return this._wellColor;
+ }
+
+ set wellColor(color)
+ {
+ this._wellColor = color;
+ this._well.style.backgroundColor = color;
+ }
+
+ get selected()
+ {
+ return this._element.classList.contains(WebInspector.GradientSliderKnob.SelectedClassName);
+ }
+
+ set selected(selected)
+ {
+ if (this.selected === selected)
+ return;
+
+ this._element.classList.toggle(WebInspector.GradientSliderKnob.SelectedClassName, selected);
+
+ if (this.delegate && typeof this.delegate.knobSelectionChanged === "function")
+ this.delegate.knobSelectionChanged(this);
+ }
+
+ // Protected
+
+ handleEvent(event)
+ {
+ event.preventDefault();
+ event.stopPropagation();
+
+ switch (event.type) {
+ case "mousedown":
+ this._handleMousedown(event);
+ break;
+ case "mousemove":
+ this._handleMousemove(event);
+ break;
+ case "mouseup":
+ this._handleMouseup(event);
+ break;
+ case "transitionend":
+ this._handleTransitionEnd(event);
+ break;
+ }
+ }
+
+ // Private
+
+ _handleMousedown(event)
+ {
+ this._moved = false;
+ this._detaching = false;
+
+ window.addEventListener("mousemove", this, true);
+ window.addEventListener("mouseup", this, true);
+
+ this._startX = this.x;
+ this._startMouseX = event.pageX;
+ this._startMouseY = event.pageY;
+ }
+
+ _handleMousemove(event)
+ {
+ var w = WebInspector.GradientSlider.Width;
+
+ this._moved = true;
+
+ if (!this._detaching && Math.abs(event.pageY - this._startMouseY) > 50) {
+ this._detaching = this.delegate && typeof this.delegate.knobCanDetach === "function" && this.delegate.knobCanDetach(this);
+ if (this._detaching && this.delegate && typeof this.delegate.knobWillDetach === "function") {
+ var translationFromParentToBody = window.webkitConvertPointFromNodeToPage(this.element.parentNode, new WebKitPoint(0, 0));
+ this._startMouseX -= translationFromParentToBody.x;
+ this._startMouseY -= translationFromParentToBody.y;
+ document.body.appendChild(this.element);
+ this.delegate.knobWillDetach(this);
+ }
+ }
+
+ var x = this._startX + event.pageX - this._startMouseX;
+ if (!this._detaching)
+ x = Math.min(Math.max(0, x), w);
+ this.x = x;
+
+ if (this._detaching)
+ this.y = event.pageY - this._startMouseY;
+ else if (this.delegate && typeof this.delegate.knobXDidChange === "function")
+ this.delegate.knobXDidChange(this);
+ }
+
+ _handleMouseup(event)
+ {
+ window.removeEventListener("mousemove", this, true);
+ window.removeEventListener("mouseup", this, true);
+
+ if (this._detaching) {
+ this.element.addEventListener("transitionend", this);
+ this.element.classList.add(WebInspector.GradientSliderKnob.FadeOutClassName);
+ this.selected = false;
+ } else if (!this._moved)
+ this.selected = !this.selected;
+ }
+
+ _handleTransitionEnd(event)
+ {
+ this.element.removeEventListener("transitionend", this);
+ this.element.classList.remove(WebInspector.GradientSliderKnob.FadeOutClassName);
+ this.element.remove();
+ }
+
+ _updateTransform()
+ {
+ this.element.style.webkitTransform = "translate3d(" + this._x + "px, " + this._y + "px, 0)";
+ }
+};
+
+WebInspector.GradientSliderKnob.SelectedClassName = "selected";
+WebInspector.GradientSliderKnob.FadeOutClassName = "fade-out";