Commit ffc95ad7 authored by graouts@apple.com's avatar graouts@apple.com

Web Inspector: allow editing of colors in CSS resources

https://bugs.webkit.org/show_bug.cgi?id=124364

Reviewed by Timothy Hatcher.

Identify colors in CSS and HTML resources so that hovering these colors shows a HoverMenu
allowing a ColorPicker hosted in a Popover to be shown to edit the hovered color.

* UserInterface/CSSStyleDeclarationTextEditor.js:
(WebInspector.CSSStyleDeclarationTextEditor.prototype._createColorSwatches):
Remove the code that goes through the lines of the CodeMirror editor to look for
color strings and replace it with a call to the .createColorMarkers() CodeMirror
extension in which the code was refactored. The callback passed to
.createColorMarkers() handles the CSSStyleDeclarationTextEditor-specific creation
of color swatches to show the popover, keeping the existing behavior of the Styles
sidebar panel.

* UserInterface/CodeMirrorAdditions.js:
Refactor existing code into two new CodeMirror extensions: .boundsForRange(), which
was previously defined on the CodeMirrorTokenTrackingController but was also needed
in the new CodeMirrorColorEditingController, and .createColorMarkers(), discussed above.

* UserInterface/CodeMirrorColorEditingController.js: Added.
(WebInspector.CodeMirrorColorEditingController):
New class used by SourceCodeTextEditor to coordinate the editing of a color in a CodeMirror
TextMarker by a ColorPicker hosted in a Popover shown by the activation of a HoverMenu. This
controller automatically gets the color set by the user using the ColorPicker and updates
the CodeMirror text editor with the new value. When the user presses the Esc. key while the
popover is shown, it's automatically dismissed and the original color is reset in the editor.

(WebInspector.CodeMirrorColorEditingController.prototype.get marker):
(WebInspector.CodeMirrorColorEditingController.prototype.get range):
(WebInspector.CodeMirrorColorEditingController.prototype.get delegate):
(WebInspector.CodeMirrorColorEditingController.prototype.set delegate):
Getters and setters for public properties.

(WebInspector.CodeMirrorColorEditingController.prototype.get color):
(WebInspector.CodeMirrorColorEditingController.prototype.set color):
Get and set the color for the edited color marker, replacing the text in the marker's range
upon setting.

(WebInspector.CodeMirrorColorEditingController.prototype.presentHoverMenu):
Public API allowing for a HoverMenu to be shown around the bounds of the TextMarker passed
in the constructor. This method is called from SourceCodeTextEditor when the
CodeMirrorTokenTrackingController identifies that a token that is part of a TextMarker is
hovered and that token is a color.

(WebInspector.CodeMirrorColorEditingController.prototype.dismissHoverMenu):
Public API allowing for the HoverMenu to be dismissed. This method is called when the
CodeMirrorTokenTrackingController identifies that its "hoveredMarker" is no longer being
hovered as well as when we identify that editing of the color has completed.

(WebInspector.CodeMirrorColorEditingController.prototype.handleEvent):
Event handler for the "keydown" event that are being listened to when the HoverMenu
is activated such that the Esc. key can be used to dimiss the popover and reset the
original color before any edits.

(WebInspector.CodeMirrorColorEditingController.prototype.hoverMenuButtonWasPressed):
Implementation of the HoverMenu delegation method used to show a Popover containing a
ColorPicker upon clicking on the color wheel attached to the HoverMenu. We also remember
the color set on the marker so that it may be restored when Esc. is pressed and used to
set the original state of the ColorPicker. The delegation method
colorEditingControllerDidStartEditing() is also called at this point, which the
SourceCodeTextEditor implements.

(WebInspector.CodeMirrorColorEditingController.prototype.didDismissPopover):
Implementation of the Popover delegation method used to identify that color editing has
completed. The delegation method colorEditingControllerDidFinishEditing() is called
at this point, which the SourceCodeTextEditor implements.

(WebInspector.CodeMirrorColorEditingController.prototype._colorPickerColorChanged):
Apply the color set in the color picker to the CodeMirror text editor.

* UserInterface/CodeMirrorTokenTrackingController.js:
Add two new modes to CodeMirrorTokenTrackingController. The first mode is "None" and is
the default, incurring no specific token handling behavior. The second mode is "MarkedTokens"
which identifies hover of a token contained in a CodeMirror TextMarker range. The new
"MarkedTokens" mode is used by SourceCodeTextEditor to identify when a marked color is being
hovered to display a HoverMenu.

(WebInspector.CodeMirrorTokenTrackingController):
(WebInspector.CodeMirrorTokenTrackingController.prototype.set mode):
Make "None" the new default mode for CodeMirrorTokenTrackingController.

(WebInspector.CodeMirrorTokenTrackingController.prototype.get hoveredMarker):
(WebInspector.CodeMirrorTokenTrackingController.prototype.set hoveredMarker):
(WebInspector.CodeMirrorTokenTrackingController.prototype._updateHoveredTokenInfo):
Check, when we have a "hoveredMarker" set on the CodeMirrorTokenTrackingController,
whether the "hoveredMarker" is still being hovered when there is no token at the current
mouse position. We can then determine when we're mousing out of the "hoveredMarker" and
notify the delegate via the new tokenTrackingControllerMouseOutOfHoveredMarker delegate
method. The SourceCodeTextEditor uses this method to dismiss its CodeMirrorColorEditingController.

(WebInspector.CodeMirrorTokenTrackingController.prototype._processNewHoveredToken):
Add support for the new "MarkedTokens" mode.

(WebInspector.CodeMirrorTokenTrackingController.prototype._processMarkedToken):
For the moment, use the same behavior as the existing "MarkedTokens" mode.

* UserInterface/Color.js:
(WebInspector.Color):
Add a new "valid" property to identify whether a color has any invalid (NaN) component. This property
is used by SourceCodeTextEditor to establish whether a hovered color marker is indeed set to a
valid color still.

(WebInspector.Color.prototype.copy):
New method to create an exact copy of a Color instance, used by CodeMirrorColorEditingController
to duplicate the edited color in case we need to revert it.

* UserInterface/HoverMenu.js:
(WebInspector.HoverMenu.prototype._handleClickEvent):
Rename hoverMenuWasActivated to hoverMenuButtonWasPressed per review feedback.

* UserInterface/Images/ColorIcon.png: Added.
* UserInterface/Images/ColorIcon@2x.png: Added.
New color wheel icon used to customize the HoverMenu shown by a CodeMirrorColorEditingController.

* UserInterface/Main.html:
Link to the new CodeMirrorColorEditingController.js file.

* UserInterface/SourceCodeTextEditor.css:
(.hover-menu.color):
(.hover-menu.color > img):
Customize the HoverMenu shown by a CodeMirrorColorEditingController to use the new ColorIcon asset.

* UserInterface/SourceCodeTextEditor.js:
(WebInspector.SourceCodeTextEditor):
Add a new "_ignoreContentDidChange" ivar that increments and decrements to track when handling
of CodeMirror content changes should be disabled.

(WebInspector.SourceCodeTextEditor.prototype.close):
Adopt the new ._updateTokenTrackingControllerState() method to update the state of the
CodeMirrorTokenTrackingController.

(WebInspector.SourceCodeTextEditor.prototype.contentDidChange):
Override the new TextEditor public API called when content in the CodeMirror text editor is changed.
We use this method to process any newly added line to create any newly added color marker.

(WebInspector.SourceCodeTextEditor.prototype._contentDidPopulate):
(WebInspector.SourceCodeTextEditor.prototype._debuggerDidPause):
(WebInspector.SourceCodeTextEditor.prototype._debuggerDidResume):
(WebInspector.SourceCodeTextEditor.prototype._sourceCodeSourceMapAdded):
Adopt the new ._updateTokenTrackingControllerState() method to update the state of the
CodeMirrorTokenTrackingController.

(WebInspector.SourceCodeTextEditor.prototype._updateTokenTrackingControllerState):
New method acting as the only point where we check the state of the text editor and set the right
mode and settings on the CodeMirrorTokenTrackingController, including setting the new "MarkedTokens"
mode when we have color markers, as determined by ._hasColorMarkers().

(WebInspector.SourceCodeTextEditor.prototype._hasColorMarkers):
Check whether any of the TextMarkers set on the CodeMirror text editor were created for a color.

(WebInspector.SourceCodeTextEditor.prototype.tokenTrackingControllerHighlightedRangeWasClicked):
Check the CodeMirrorTokenTrackingController mode rather than the removed "_jumpToSymbolTrackingModeEnabled"
ivar to identify we're in the "NonSymbolTokens" mode.

(WebInspector.SourceCodeTextEditor.prototype.tokenTrackingControllerNewHighlightCandidate):
Refactor method to use the CodeMirrorTokenTrackingController mode to branch into mode-specific code
and add a new branch for the new "MarkedTokens" mode where we check if the newly hovered token
is part of a color TextMarker range.

(WebInspector.SourceCodeTextEditor.prototype.tokenTrackingControllerMouseOutOfHoveredMarker):
Implement this new CodeMirrorTokenTrackingController delegation method to dismiss the
CodeMirrorColorEditingController as we identify we're no longer hovering over the TextMarker
for which the CodeMirrorColorEditingController was created.

(WebInspector.SourceCodeTextEditor.prototype._showPopover):
Adopt the new TextEditor boundsForRange() method.

(WebInspector.SourceCodeTextEditor.prototype._updateColorMarkers):
Harness the new TextEditor createColorMarkers() method to create new TextMarkers for the provided
line, or the entire text editor content if none provided. We then immediately call _updateTokenTrackingControllerState()
so that the new "MarkedTokens" mode is entered in case color TextMarkers were created for the first
time for this text editor.

(WebInspector.SourceCodeTextEditor.prototype._tokenTrackingControllerHighlightedMarkedExpression):
Called when we've identified the CodeMirrorTokenTrackingController highlighted a TextMarker. We check
if any of the hovered TextMarkers are for a color, and in this case create a CodeMirrorColorEditingController
to coordinate the display of a ColorPicker in a Popover to edit the hovered color token. We also check
whether the hovered marker still contains a valid color, clearing the marker in case it was edited to
no longer contain a color.

(WebInspector.SourceCodeTextEditor.prototype._dismissCodeMirrorColorEditingController):
Used to dismiss the CodeMirrorColorEditingController, if previously presented, and reset some internal state.

(WebInspector.SourceCodeTextEditor.prototype.colorEditingControllerDidStartEditing):
Implement this CodeMirrorColorEditingController delegation method to temporarily disable the
CodeMirrorTokenTrackingController while we edit the color with the ColorPicker, remove
the TextMarker for the edited color and instruct that content changes should be ignored
such that we act on the complete set of color edits upon completion.

(WebInspector.SourceCodeTextEditor.prototype.colorEditingControllerDidFinishEditing):
Update color markers for the edited line such that any color edits are correctly updated for
the future and so that, as a side-effect, the CodeMirrorColorEditingController is reset to the
appropriate mode depending on whether color TextMarkers are indeed available, resetting states
that may have been altered by colorEditingControllerDidStartEditing().

* UserInterface/TextEditor.js:
(WebInspector.TextEditor.prototype.contentDidChange):
New public method meant to be overriden by subclasses, added for the use of SourceCodeTextEditor, exposing
the list of TextRanges affected by the content change, both in the context of the old content and new content.

(WebInspector.TextEditor.prototype.boundsForRange):
(WebInspector.TextEditor.prototype.get markers):
(WebInspector.TextEditor.prototype.findMarkersAtPosition):
(WebInspector.TextEditor.prototype.createColorMarkers):
(WebInspector.TextEditor.prototype.colorEditingControllerForMarker):
New public methods calling into the CodeMirror private ivar for the benefit of SourceCodeTextEditor.

(WebInspector.TextEditor.prototype._contentChanged):
Call the new contentDidChange() method.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@160483 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent af1524c4
This diff is collapsed.
......@@ -390,69 +390,22 @@ WebInspector.CSSStyleDeclarationTextEditor.prototype = {
{
function update()
{
// Matches rgba(0, 0, 0, 0.5), rgb(0, 0, 0), hsl(), hsla(), #fff, #ffffff, white
const colorRegex = /((?:rgb|hsl)a?\([^)]+\)|#[0-9a-fA-F]{6}|#[0-9a-fA-F]{3}|\b\w+\b(?![-.]))/g;
var start = typeof lineNumber === "number" ? lineNumber : 0;
var end = typeof lineNumber === "number" ? lineNumber + 1 : this._codeMirror.lineCount();
// Look for color strings and add swatches in front of them.
for (var i = start; i < end; ++i) {
var lineContent = this._codeMirror.getLine(i);
var match = colorRegex.exec(lineContent);
while (match) {
// Act as a negative look-behind and disallow the color from being prefixing with certain characters.
if (match.index > 0 && /[-.]/.test(lineContent[match.index - 1])) {
match = colorRegex.exec(lineContent);
continue;
}
var from = {line: i, ch: match.index};
var to = {line: i, ch: match.index + match[0].length};
var foundColorMarker = false;
var marks = this._codeMirror.findMarksAt(to);
for (var j = 0; j < marks.length; ++j) {
if (!marks[j].__markedColor)
continue;
foundColorMarker = true;
break;
}
if (foundColorMarker) {
match = colorRegex.exec(lineContent);
continue;
}
var color = WebInspector.Color.fromString(match[0]);
if (!color) {
match = colorRegex.exec(lineContent);
continue;
}
var swatchElement = document.createElement("span");
swatchElement.title = WebInspector.UIString("Click to open a colorpicker. Shift-click to change color format.");
swatchElement.className = WebInspector.CSSStyleDeclarationTextEditor.ColorSwatchElementStyleClassName;
swatchElement.addEventListener("click", this._colorSwatchClicked.bind(this));
var swatchInnerElement = document.createElement("span");
swatchInnerElement.style.backgroundColor = match[0];
swatchElement.appendChild(swatchInnerElement);
var swatchMarker = this._codeMirror.setUniqueBookmark(from, swatchElement);
var colorTextMarker = this._codeMirror.markText(from, to);
colorTextMarker.__markedColor = true;
swatchInnerElement.__colorTextMarker = colorTextMarker;
swatchInnerElement.__color = color;
match = colorRegex.exec(lineContent);
}
}
this._codeMirror.createColorMarkers(lineNumber, function(marker, color, colorString) {
var swatchElement = document.createElement("span");
swatchElement.title = WebInspector.UIString("Click to open a colorpicker. Shift-click to change color format.");
swatchElement.className = WebInspector.CSSStyleDeclarationTextEditor.ColorSwatchElementStyleClassName;
swatchElement.addEventListener("click", this._colorSwatchClicked.bind(this));
var swatchInnerElement = document.createElement("span");
swatchInnerElement.style.backgroundColor = colorString;
swatchElement.appendChild(swatchInnerElement);
var swatchMarker = this._codeMirror.setUniqueBookmark(marker.find().from, swatchElement);
swatchInnerElement.__colorTextMarker = marker;
swatchInnerElement.__color = color;
}.bind(this));
}
if (nonatomic)
......
......@@ -418,6 +418,71 @@
return CodeMirror.Pass;
}
CodeMirror.defineExtension("boundsForRange", function(range) {
var firstCharCoords = this.cursorCoords(range.start);
var lastCharCoords = this.cursorCoords(range.end);
return new WebInspector.Rect(firstCharCoords.left, firstCharCoords.top, lastCharCoords.right - firstCharCoords.left, firstCharCoords.bottom - firstCharCoords.top);
});
CodeMirror.defineExtension("createColorMarkers", function(lineNumber, callback) {
var createdMarkers = [];
var start = typeof lineNumber === "number" ? lineNumber : 0;
var end = typeof lineNumber === "number" ? lineNumber + 1 : this.lineCount();
// Matches rgba(0, 0, 0, 0.5), rgb(0, 0, 0), hsl(), hsla(), #fff, #ffffff, white
const colorRegex = /((?:rgb|hsl)a?\([^)]+\)|#[0-9a-fA-F]{6}|#[0-9a-fA-F]{3}|\b\w+\b(?![-.]))/g;
for (var lineNumber = start; lineNumber < end; ++lineNumber) {
var lineContent = this.getLine(lineNumber);
var match = colorRegex.exec(lineContent);
while (match) {
// Act as a negative look-behind and disallow the color from being prefixing with certain characters.
if (match.index > 0 && /[-.]/.test(lineContent[match.index - 1])) {
match = colorRegex.exec(lineContent);
continue;
}
var from = {line: lineNumber, ch: match.index};
var to = {line: lineNumber, ch: match.index + match[0].length};
var foundColorMarker = false;
var markers = this.findMarksAt(to);
for (var j = 0; j < markers.length; ++j) {
if (!markers[j].__markedColor)
continue;
foundColorMarker = true;
break;
}
if (foundColorMarker) {
match = colorRegex.exec(lineContent);
continue;
}
var colorString = match[0];
var color = WebInspector.Color.fromString(colorString);
if (!color) {
match = colorRegex.exec(lineContent);
continue;
}
var marker = this.markText(from, to);
marker.__markedColor = true;
createdMarkers.push(marker);
if (callback)
callback(marker, color, colorString);
match = colorRegex.exec(lineContent);
}
}
return createdMarkers;
});
function ignoreKey(codeMirror)
{
// Do nothing to ignore the key.
......
/*
* Copyright (C) 2013 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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.CodeMirrorColorEditingController = function(codeMirror, marker)
{
WebInspector.Object.call(this);
this._codeMirror = codeMirror;
this._marker = marker;
this._delegate = null;
this._range = marker.find();
this._color = WebInspector.Color.fromString(codeMirror.getRange(this._range.from, this._range.to));
this._keyboardShortcutEsc = new WebInspector.KeyboardShortcut(null, WebInspector.KeyboardShortcut.Key.Escape);
}
WebInspector.CodeMirrorColorEditingController.prototype = {
constructor: WebInspector.CodeMirrorColorEditingController,
__proto__: WebInspector.Object.prototype,
// Public
get marker()
{
return this._marker;
},
get range()
{
return this._range;
},
get color()
{
return this._color;
},
set color(color)
{
var colorText = color.toString();
this._codeMirror.replaceRange(colorText, this._range.from, this._range.to);
this._range.to.ch = this._range.from.ch + colorText.length;
this._color = color;
},
get delegate()
{
return this._delegate;
},
set delegate(delegate)
{
this._delegate = delegate;
},
presentHoverMenu: function()
{
this._hoverMenu = new WebInspector.HoverMenu(this);
this._hoverMenu.element.classList.add("color");
this._bounds = this._codeMirror.boundsForRange({
start: this._range.from,
end: this._range.to
});
this._hoverMenu.present(this._bounds);
},
dismissHoverMenu: function()
{
this._hoverMenu.dismiss();
},
// Protected
handleEvent: function(event)
{
if (!this._keyboardShortcutEsc.matchesEvent(event) || !this._popover.visible)
return;
this.color = this._originalColor;
this._popover.dismiss();
event.stopPropagation();
event.preventDefault();
},
hoverMenuButtonWasPressed: function(hoverMenu)
{
var colorPicker = new WebInspector.ColorPicker;
colorPicker.addEventListener(WebInspector.ColorPicker.Event.ColorChanged, this._colorPickerColorChanged, this);
this._popover = new WebInspector.Popover(this);
this._popover.content = colorPicker.element;
this._popover.present(this._bounds.pad(2), [WebInspector.RectEdge.MAX_Y, WebInspector.RectEdge.MAX_X]);
window.addEventListener("keydown", this, true);
colorPicker.color = this._color;
hoverMenu.dismiss();
if (this._delegate && typeof this._delegate.colorEditingControllerDidStartEditing === "function")
this._delegate.colorEditingControllerDidStartEditing(this);
this._originalColor = this._color.copy();
},
didDismissPopover: function(popover)
{
delete this._popover;
delete this._originalColor;
window.removeEventListener("keydown", this, true);
if (this._delegate && typeof this._delegate.colorEditingControllerDidFinishEditing === "function")
this._delegate.colorEditingControllerDidFinishEditing(this);
},
// Private
_colorPickerColorChanged: function(event)
{
this.color = event.target.color;
}
}
......@@ -31,7 +31,7 @@ WebInspector.CodeMirrorTokenTrackingController = function(codeMirror, delegate)
this._codeMirror = codeMirror;
this._delegate = delegate || null;
this._mode = WebInspector.CodeMirrorTokenTrackingController.Mode.NonSymbolTokens;
this._mode = WebInspector.CodeMirrorTokenTrackingController.Mode.None;
this._mouseOverDelayDuration = 0;
this._mouseOutReleaseDelayDuration = 0;
......@@ -40,13 +40,16 @@ WebInspector.CodeMirrorTokenTrackingController = function(codeMirror, delegate)
this._enabled = false;
this._tracking = false;
this._hoveredTokenInfo = null;
this._hoveredMarker = null;
};
WebInspector.CodeMirrorTokenTrackingController.JumpToSymbolHighlightStyleClassName = "jump-to-symbol-highlight";
WebInspector.CodeMirrorTokenTrackingController.Mode = {
None: "none",
NonSymbolTokens: "non-symbol-tokens",
JavaScriptExpression: "javascript-expression",
MarkedTokens: "marked-tokens"
}
WebInspector.CodeMirrorTokenTrackingController.prototype = {
......@@ -94,11 +97,11 @@ WebInspector.CodeMirrorTokenTrackingController.prototype = {
return this._mode;
},
set mode(x)
set mode(mode)
{
var oldMode = this._mode;
this._mode = x || WebInspector.CodeMirrorTokenTrackingController.Mode.NonSymbolTokens;
this._mode = mode || WebInspector.CodeMirrorTokenTrackingController.Mode.None;
if (oldMode !== this._mode && this._tracking && this._hoveredTokenInfo)
this._processNewHoveredToken();
......@@ -141,6 +144,16 @@ WebInspector.CodeMirrorTokenTrackingController.prototype = {
return this._candidate;
},
get hoveredMarker()
{
return this._hoveredMarker;
},
set hoveredMarker(hoveredMarker)
{
this._hoveredMarker = hoveredMarker;
},
highlightLastHoveredRange: function()
{
if (this._candidate)
......@@ -176,13 +189,6 @@ WebInspector.CodeMirrorTokenTrackingController.prototype = {
window.removeEventListener("mousemove", this, true);
},
boundsForRange: function(range)
{
var firstCharCoords = this._codeMirror.cursorCoords(range.start);
var lastCharCoords = this._codeMirror.cursorCoords(range.end);
return new WebInspector.Rect(firstCharCoords.left, firstCharCoords.top, lastCharCoords.right - firstCharCoords.left, firstCharCoords.bottom - firstCharCoords.top);
},
// Private
_startTracking: function()
......@@ -298,6 +304,12 @@ WebInspector.CodeMirrorTokenTrackingController.prototype = {
var token = this._codeMirror.getTokenAt(position);
if (!token || !token.type || !token.string) {
if (this._hoveredMarker && this._delegate && typeof this._delegate.tokenTrackingControllerMouseOutOfHoveredMarker === "function") {
var markers = this._codeMirror.findMarksAt(position);
if (!markers.contains(this._hoveredMarker))
this._delegate.tokenTrackingControllerMouseOutOfHoveredMarker(this, this._hoveredMarker);
}
this._resetTrackingStates();
return;
}
......@@ -380,6 +392,9 @@ WebInspector.CodeMirrorTokenTrackingController.prototype = {
case WebInspector.CodeMirrorTokenTrackingController.Mode.JavaScriptExpression:
this._candidate = this._processJavaScriptExpression();
break;
case WebInspector.CodeMirrorTokenTrackingController.Mode.MarkedTokens:
this._candidate = this._processMarkedToken();
break;
}
if (!this._candidate)
......@@ -478,6 +493,11 @@ WebInspector.CodeMirrorTokenTrackingController.prototype = {
};
},
_processMarkedToken: function()
{
return this._processNonSymbolToken();
},
_resetTrackingStates: function()
{
clearTimeout(this._tokenHoverTimer);
......
......@@ -34,6 +34,10 @@ WebInspector.Color = function(format, components)
this._hsla = components;
else
this._rgba = components;
this.valid = !components.some(function(component) {
return isNaN(component);
});
}
WebInspector.Color.Format = {
......@@ -211,6 +215,21 @@ WebInspector.Color.prototype = {
return this._hsla;
},
copy: function()
{
switch (this.format) {
case WebInspector.Color.Format.RGB:
case WebInspector.Color.Format.HEX:
case WebInspector.Color.Format.ShortHEX:
case WebInspector.Color.Format.Nickname:
case WebInspector.Color.Format.RGBA:
return new WebInspector.Color(this.format, this.rgba);
case WebInspector.Color.Format.HSL:
case WebInspector.Color.Format.HSLA:
return new WebInspector.Color(this.format, this.hsla);
}
},
toString: function(format)
{
if (!format)
......
......@@ -101,7 +101,7 @@ WebInspector.HoverMenu.prototype = {
_handleClickEvent: function(event)
{
if (this.delegate && typeof this.delegate.hoverMenuWasActivated === "function")
this.delegate.hoverMenuWasActivated(this);
if (this.delegate && typeof this.delegate.hoverMenuButtonWasPressed === "function")
this.delegate.hoverMenuButtonWasPressed(this);
}
}
......@@ -412,6 +412,7 @@
<script src="GoToLineDialog.js"></script>
<script src="ContentFlowDOMTreeContentView.js"></script>
<script src="HoverMenu.js"></script>
<script src="CodeMirrorColorEditingController.js"></script>
<script src="Main.js"></script>
<script>
......
......@@ -61,3 +61,16 @@
padding-left: 10px;
padding-right: 10px;
}
/* Custom styling for the hover menu attached to color tokens */
.hover-menu.color {
padding-right: 15px;
}
.hover-menu.color > img {
width: 16px;
height: 16px;
-webkit-transform: translateX(14px);
content: -webkit-image-set(url(Images/ColorIcon.png) 1x, url(Images/ColorIcon@2x.png) 2x);
}
......@@ -591,6 +591,37 @@ WebInspector.TextEditor.prototype = {
this._codeMirror.focus();
},
contentDidChange: function(replacedRanges, newRanges)
{
// Implemented by subclasses.
},
boundsForRange: function(range)
{
return this._codeMirror.boundsForRange(range);
},
get markers()
{
// FIXME: we should not return CodeMirror TextMarker objects but rather wrappers.
return this._codeMirror.getAllMarks();
},
findMarkersAtPosition: function(position)
{
return this._codeMirror.findMarksAt(position);
},
createColorMarkers: function(lineNumber)
{
return this._codeMirror.createColorMarkers(lineNumber);
},
colorEditingControllerForMarker: function(colorMarker)
{
return new WebInspector.CodeMirrorColorEditingController(this._codeMirror, colorMarker);
},
// Private
_contentChanged: function(codeMirror, change)
......@@ -598,6 +629,25 @@ WebInspector.TextEditor.prototype = {
if (this._ignoreCodeMirrorContentDidChangeEvent > 0)
return;
var replacedRanges = [];
var newRanges = [];
while (change) {
replacedRanges.push(new WebInspector.TextRange(
change.from.line,
change.from.ch,
change.to.line,
change.to.ch
));
newRanges.push(new WebInspector.TextRange(
change.from.line,
change.from.ch,
change.from.line + change.text.length - 1,
change.text.length === 1 ? change.from.ch + change.text[0].length : change.text.lastValue.length
));
change = change.next;
}
this.contentDidChange(replacedRanges, newRanges);
if (this._formatted) {
this._formatterSourceMap = null;
this._formatted = false;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment