Bug 1307481 - Part 4: Implements the Inline Tooltip widget. r=jdescottes

This commit is contained in:
Sheldon Roddick 2017-03-04 01:59:35 -08:00
Родитель 724677c3ab
Коммит ad70ec2061
7 изменённых файлов: 255 добавлений и 122 удалений

Просмотреть файл

@ -9,6 +9,7 @@
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<link rel="stylesheet" href="chrome://devtools/skin/widgets.css"/>
<link rel="stylesheet" href="chrome://devtools/content/shared/widgets/color-widget.css"/>
<link rel="stylesheet" href="chrome://devtools/skin/inspector.css"/>
<link rel="stylesheet" href="chrome://devtools/skin/rules.css"/>
<link rel="stylesheet" href="chrome://devtools/skin/computed.css"/>

Просмотреть файл

@ -108,7 +108,7 @@ TextPropertyEditor.prototype = {
this.element._textPropertyEditor = this;
this.container = createChild(this.element, "div", {
class: "ruleview-propertycontainer"
class: "ruleview-propertycontainer inline-tooltip-container"
});
// The enable checkbox will disable or enable the rule.

Просмотреть файл

@ -40,102 +40,18 @@ const XHTML_NS = "http://www.w3.org/1999/xhtml";
function ColorWidget(parentEl, rgb) {
EventEmitter.decorate(this);
this.element = parentEl.ownerDocument.createElementNS(XHTML_NS, "div");
this.parentEl = parentEl;
this.element.className = "colorwidget-container";
this.element.innerHTML = `
<div class="colorwidget-top">
<div class="colorwidget-fill"></div>
<div class="colorwidget-top-inner">
<div class="colorwidget-color colorwidget-box">
<div class="colorwidget-sat">
<div class="colorwidget-val">
<div class="colorwidget-dragger"></div>
</div>
</div>
</div>
<div class="colorwidget-hue colorwidget-box">
<div class="colorwidget-slider colorwidget-slider-control"></div>
</div>
</div>
</div>
<div class="colorwidget-alpha colorwidget-checker colorwidget-box">
<div class="colorwidget-alpha-inner">
<div class="colorwidget-alpha-handle colorwidget-slider-control"></div>
</div>
</div>
<div class="colorwidget-value">
<select class="colorwidget-select">
<option value="hex">Hex</option>
<option value="rgba">RGBA</option>
<option value="hsla">HSLA</option>
</select>
<div class="colorwidget-hex">
<input class="colorwidget-hex-input"/>
</div>
<div class="colorwidget-rgba colorwidget-hidden">
<input class="colorwidget-rgba-r" data-id="r" />
<input class="colorwidget-rgba-g" data-id="g" />
<input class="colorwidget-rgba-b" data-id="b" />
<input class="colorwidget-rgba-a" data-id="a" />
</div>
<div class="colorwidget-hsla colorwidget-hidden">
<input class="colorwidget-hsla-h" data-id="h" />
<input class="colorwidget-hsla-s" data-id="s" />
<input class="colorwidget-hsla-l" data-id="l" />
<input class="colorwidget-hsla-a" data-id="a" />
</div>
</div>
`;
this.onSelectValueChange = this.onSelectValueChange.bind(this);
this.onHexInputChange = this.onHexInputChange.bind(this);
this.onRgbaInputChange = this.onRgbaInputChange.bind(this);
this.onHslaInputChange = this.onHslaInputChange.bind(this);
this.onAlphaSliderMove = this.onAlphaSliderMove.bind(this);
this.onElementClick = this.onElementClick.bind(this);
this.element.addEventListener("click", this.onElementClick);
this.onDraggerMove = this.onDraggerMove.bind(this);
this.onHexInputChange = this.onHexInputChange.bind(this);
this.onHslaInputChange = this.onHslaInputChange.bind(this);
this.onRgbaInputChange = this.onRgbaInputChange.bind(this);
this.onSelectValueChange = this.onSelectValueChange.bind(this);
this.onSliderMove = this.onSliderMove.bind(this);
this.parentEl.appendChild(this.element);
this.slider = this.element.querySelector(".colorwidget-hue");
this.slideHelper = this.element.querySelector(".colorwidget-slider");
ColorWidget.draggable(this.slider, this.onSliderMove.bind(this));
this.dragger = this.element.querySelector(".colorwidget-color");
this.dragHelper = this.element.querySelector(".colorwidget-dragger");
ColorWidget.draggable(this.dragger, this.onDraggerMove.bind(this));
this.alphaSlider = this.element.querySelector(".colorwidget-alpha");
this.alphaSliderInner = this.element.querySelector(".colorwidget-alpha-inner");
this.alphaSliderHelper = this.element.querySelector(".colorwidget-alpha-handle");
ColorWidget.draggable(this.alphaSliderInner, this.onAlphaSliderMove.bind(this));
this.colorSelect = this.element.querySelector(".colorwidget-select");
this.colorSelect.addEventListener("change", this.onSelectValueChange);
this.hexValue = this.element.querySelector(".colorwidget-hex");
this.hexValueInput = this.element.querySelector(".colorwidget-hex-input");
this.hexValueInput.addEventListener("input", this.onHexInputChange);
this.rgbaValue = this.element.querySelector(".colorwidget-rgba");
this.rgbaValueInputs = {
r: this.element.querySelector(".colorwidget-rgba-r"),
g: this.element.querySelector(".colorwidget-rgba-g"),
b: this.element.querySelector(".colorwidget-rgba-b"),
a: this.element.querySelector(".colorwidget-rgba-a"),
};
this.rgbaValue.addEventListener("input", this.onRgbaInputChange);
this.hslaValue = this.element.querySelector(".colorwidget-hsla");
this.hslaValueInputs = {
h: this.element.querySelector(".colorwidget-hsla-h"),
s: this.element.querySelector(".colorwidget-hsla-s"),
l: this.element.querySelector(".colorwidget-hsla-l"),
a: this.element.querySelector(".colorwidget-hsla-a"),
};
this.hslaValue.addEventListener("input", this.onHslaInputChange);
this.initializeColorWidget();
if (rgb) {
this.rgb = rgb;
@ -291,7 +207,101 @@ ColorWidget.prototype = {
rgb[3] + ")";
},
initializeColorWidget: function () {
this.parentEl.innerHTML = "";
this.element = this.parentEl.ownerDocument.createElementNS(XHTML_NS, "div");
this.element.className = "colorwidget-container";
this.element.innerHTML = `
<div class="colorwidget-top">
<div class="colorwidget-fill"></div>
<div class="colorwidget-top-inner">
<div class="colorwidget-color colorwidget-box">
<div class="colorwidget-sat">
<div class="colorwidget-val">
<div class="colorwidget-dragger"></div>
</div>
</div>
</div>
<div class="colorwidget-hue colorwidget-box">
<div class="colorwidget-slider colorwidget-slider-control"></div>
</div>
</div>
</div>
<div class="colorwidget-alpha colorwidget-checker colorwidget-box">
<div class="colorwidget-alpha-inner">
<div class="colorwidget-alpha-handle colorwidget-slider-control"></div>
</div>
</div>
<div class="colorwidget-value">
<select class="colorwidget-select">
<option value="hex">Hex</option>
<option value="rgba">RGBA</option>
<option value="hsla">HSLA</option>
</select>
<div class="colorwidget-hex">
<input class="colorwidget-hex-input"/>
</div>
<div class="colorwidget-rgba colorwidget-hidden">
<input class="colorwidget-rgba-r" data-id="r" />
<input class="colorwidget-rgba-g" data-id="g" />
<input class="colorwidget-rgba-b" data-id="b" />
<input class="colorwidget-rgba-a" data-id="a" />
</div>
<div class="colorwidget-hsla colorwidget-hidden">
<input class="colorwidget-hsla-h" data-id="h" />
<input class="colorwidget-hsla-s" data-id="s" />
<input class="colorwidget-hsla-l" data-id="l" />
<input class="colorwidget-hsla-a" data-id="a" />
</div>
</div>
`;
this.element.addEventListener("click", this.onElementClick);
this.parentEl.appendChild(this.element);
this.slider = this.element.querySelector(".colorwidget-hue");
this.slideHelper = this.element.querySelector(".colorwidget-slider");
ColorWidget.draggable(this.slider, this.onSliderMove);
this.dragger = this.element.querySelector(".colorwidget-color");
this.dragHelper = this.element.querySelector(".colorwidget-dragger");
ColorWidget.draggable(this.dragger, this.onDraggerMove);
this.alphaSlider = this.element.querySelector(".colorwidget-alpha");
this.alphaSliderInner = this.element.querySelector(".colorwidget-alpha-inner");
this.alphaSliderHelper = this.element.querySelector(".colorwidget-alpha-handle");
ColorWidget.draggable(this.alphaSliderInner, this.onAlphaSliderMove);
this.colorSelect = this.element.querySelector(".colorwidget-select");
this.colorSelect.addEventListener("change", this.onSelectValueChange);
this.hexValue = this.element.querySelector(".colorwidget-hex");
this.hexValueInput = this.element.querySelector(".colorwidget-hex-input");
this.hexValueInput.addEventListener("input", this.onHexInputChange);
this.rgbaValue = this.element.querySelector(".colorwidget-rgba");
this.rgbaValueInputs = {
r: this.element.querySelector(".colorwidget-rgba-r"),
g: this.element.querySelector(".colorwidget-rgba-g"),
b: this.element.querySelector(".colorwidget-rgba-b"),
a: this.element.querySelector(".colorwidget-rgba-a"),
};
this.rgbaValue.addEventListener("input", this.onRgbaInputChange);
this.hslaValue = this.element.querySelector(".colorwidget-hsla");
this.hslaValueInputs = {
h: this.element.querySelector(".colorwidget-hsla-h"),
s: this.element.querySelector(".colorwidget-hsla-s"),
l: this.element.querySelector(".colorwidget-hsla-l"),
a: this.element.querySelector(".colorwidget-hsla-a"),
};
this.hslaValue.addEventListener("input", this.onHslaInputChange);
},
show: function () {
this.initializeColorWidget();
this.element.classList.add("colorwidget-show");
this.slideHeight = this.slider.offsetHeight;

Просмотреть файл

@ -0,0 +1,97 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const EventEmitter = require("devtools/shared/event-emitter");
/**
* The InlineTooltip can display widgets for the CSS Rules view in an
* inline container.
*
* @param {Document} doc
* The toolbox document to attach the InlineTooltip container.
*/
function InlineTooltip(doc) {
EventEmitter.decorate(this);
this.doc = doc;
this.panel = this.doc.createElement("div");
this.topWindow = this._getTopWindow();
}
InlineTooltip.prototype = {
/**
* Show the tooltip. It might be wise to append some content first if you
* don't want the tooltip to be empty.
*
* @param {Node} anchor
* Which node below which the tooltip should be shown.
*/
show(anchor) {
anchor.parentNode.insertBefore(this.panel, anchor.nextSibling);
this.emit("shown");
},
/**
* Hide the current tooltip.
*/
hide() {
if (!this.isVisible()) {
return;
}
this.panel.parentNode.remove(this.panel);
this.emit("hidden");
},
/**
* Check if the tooltip is currently displayed.
*
* @return {Boolean} true if the tooltip is visible
*/
isVisible() {
return typeof this.panel.parentNode !== "undefined" && this.panel.parentNode !== null;
},
/**
* Clears the HTML content of the tooltip panel
*/
clear() {
this.panel.innerHTML = "";
},
/**
* Set the content of this tooltip. Will first clear the tooltip and then
* append the new content element.
*
* @param {DOMNode} content
* A node that can be appended in the tooltip
*/
setContent(content) {
this.clear();
this.panel.appendChild(content);
},
get content() {
return this.panel.firstChild;
},
_getTopWindow: function () {
return this.doc.defaultView;
},
destroy() {
this.hide();
this.doc = null;
this.panel = null;
},
};
module.exports = InlineTooltip;

Просмотреть файл

@ -7,6 +7,9 @@
const EventEmitter = require("devtools/shared/event-emitter");
const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
const InlineTooltip = require("devtools/client/shared/widgets/tooltip/InlineTooltip");
const INLINE_TOOLTIP_CLASS = "inline-tooltip-container";
/**
* Base class for all (color, gradient, ...)-swatch based value editors inside
@ -16,19 +19,30 @@ const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLToolti
* The document to attach the SwatchBasedEditorTooltip. This is either the toolbox
* document if the tooltip is a popup tooltip or the panel's document if it is an
* inline editor.
* @param {String} stylesheet
* The stylesheet to be used for the HTMLTooltip.
* @param {Boolean} useInline
* A boolean flag representing whether or not the InlineTooltip should be used.
*/
function SwatchBasedEditorTooltip(document, stylesheet) {
function SwatchBasedEditorTooltip(document, stylesheet, useInline) {
EventEmitter.decorate(this);
this.useInline = useInline;
// Creating a tooltip instance
// This one will consume outside clicks as it makes more sense to let the user
// close the tooltip by clicking out
// It will also close on <escape> and <enter>
this.tooltip = new HTMLTooltip(document, {
type: "arrow",
consumeOutsideClicks: true,
useXulWrapper: true,
stylesheet
});
if (useInline) {
this.tooltip = new InlineTooltip(document);
} else {
// This one will consume outside clicks as it makes more sense to let the user
// close the tooltip by clicking out
// It will also close on <escape> and <enter>
this.tooltip = new HTMLTooltip(document, {
type: "arrow",
consumeOutsideClicks: true,
useXulWrapper: true,
stylesheet
});
}
// By default, swatch-based editor tooltips revert value change on <esc> and
// commit value change on <enter>
@ -73,9 +87,13 @@ SwatchBasedEditorTooltip.prototype = {
* immediately if there is no currently active swatch.
*/
show: function () {
if (this.activeSwatch) {
let tooltipAnchor = this.useInline ?
this.activeSwatch.closest(`.${INLINE_TOOLTIP_CLASS}`) :
this.activeSwatch;
if (tooltipAnchor) {
let onShown = this.tooltip.once("shown");
this.tooltip.show(this.activeSwatch, "topcenter bottomleft");
this.tooltip.show(tooltipAnchor, "topcenter bottomleft");
// When the tooltip is closed by clicking outside the panel we want to
// commit any changes.

Просмотреть файл

@ -7,6 +7,7 @@
const Services = require("Services");
const {Task} = require("devtools/shared/task");
const {colorUtils} = require("devtools/shared/css/color");
const {ColorWidget} = require("devtools/client/shared/widgets/ColorWidget");
const {Spectrum} = require("devtools/client/shared/widgets/Spectrum");
const SwatchBasedEditorTooltip = require("devtools/client/shared/widgets/tooltip/SwatchBasedEditorTooltip");
const {LocalizationHelper} = require("devtools/shared/l10n");
@ -38,9 +39,10 @@ function SwatchColorPickerTooltip(document,
inspector,
{supportsCssColor4ColorFunction}) {
let stylesheet = NEW_COLOR_WIDGET ?
"chrome://devtools/content/shared/widgets/color-widget.css" :
null :
"chrome://devtools/content/shared/widgets/spectrum.css";
SwatchBasedEditorTooltip.call(this, document, stylesheet);
let tooltipDocument = NEW_COLOR_WIDGET ? inspector.panelDoc : document;
SwatchBasedEditorTooltip.call(this, tooltipDocument, stylesheet, NEW_COLOR_WIDGET);
this.inspector = inspector;
@ -62,9 +64,20 @@ SwatchColorPickerTooltip.prototype = Heritage.extend(SwatchBasedEditorTooltip.pr
let container = doc.createElementNS(XHTML_NS, "div");
container.id = "spectrum-tooltip";
let spectrumNode = doc.createElementNS(XHTML_NS, "div");
spectrumNode.id = "spectrum";
container.appendChild(spectrumNode);
let widget;
let node = doc.createElementNS(XHTML_NS, "div");
if (NEW_COLOR_WIDGET) {
node.id = "colorwidget";
container.appendChild(node);
widget = new ColorWidget(node, color);
} else {
node.id = "spectrum";
container.appendChild(node);
widget = new Spectrum(node, color);
}
let eyedropper = doc.createElementNS(XHTML_NS, "button");
eyedropper.id = "eyedropper-button";
eyedropper.className = "devtools-button";
@ -74,23 +87,15 @@ SwatchColorPickerTooltip.prototype = Heritage.extend(SwatchBasedEditorTooltip.pr
eyedropper.style.pointerEvents = "auto";
container.appendChild(eyedropper);
let spectrum;
if (NEW_COLOR_WIDGET) {
this.tooltip.setContent(container, { width: 218, height: 271 });
const {ColorWidget} = require("devtools/client/shared/widgets/ColorWidget");
spectrum = new ColorWidget(spectrumNode, color);
} else {
this.tooltip.setContent(container, { width: 218, height: 224 });
spectrum = new Spectrum(spectrumNode, color);
}
this.tooltip.setContent(container, { width: 218, height: 224 });
// Wait for the tooltip to be shown before calling spectrum.show
// Wait for the tooltip to be shown before calling widget.show
// as it expect to be visible in order to compute DOM element sizes.
this.tooltip.once("shown", () => {
spectrum.show();
widget.show();
});
return spectrum;
return widget;
},
/**
@ -100,6 +105,7 @@ SwatchColorPickerTooltip.prototype = Heritage.extend(SwatchBasedEditorTooltip.pr
show: Task.async(function* () {
// Call then parent class' show function
yield SwatchBasedEditorTooltip.prototype.show.call(this);
// Then set spectrum's color and listen to color changes to preview them
if (this.activeSwatch) {
this.currentSwatchColor = this.activeSwatch.nextSibling;

Просмотреть файл

@ -9,6 +9,7 @@ DevToolsModules(
'EventTooltipHelper.js',
'HTMLTooltip.js',
'ImageTooltipHelper.js',
'InlineTooltip.js',
'SwatchBasedEditorTooltip.js',
'SwatchColorPickerTooltip.js',
'SwatchCubicBezierTooltip.js',