Skip to content
This repository has been archived by the owner on Sep 6, 2021. It is now read-only.

Commit

Permalink
Merge pull request #9596 from le717/issue-9519-take2
Browse files Browse the repository at this point in the history
Use upper/lowercase colors in Inline Color Editor
  • Loading branch information
redmunds committed Jan 16, 2015
2 parents a3068fa + c9c7a2e commit e83807d
Show file tree
Hide file tree
Showing 5 changed files with 179 additions and 114 deletions.
6 changes: 4 additions & 2 deletions src/editor/Editor.js
Expand Up @@ -94,8 +94,9 @@ define(function (require, exports, module) {
SPACE_UNITS = "spaceUnits",
STYLE_ACTIVE_LINE = "styleActiveLine",
TAB_SIZE = "tabSize",
WORD_WRAP = "wordWrap",
USE_TAB_CHAR = "useTabChar";
UPPERCASE_COLORS = "uppercaseColors",
USE_TAB_CHAR = "useTabChar",
WORD_WRAP = "wordWrap";

var cmOptions = {};

Expand Down Expand Up @@ -141,6 +142,7 @@ define(function (require, exports, module) {
PreferencesManager.definePreference(TAB_SIZE, "number", DEFAULT_TAB_SIZE, {
validator: _.partialRight(ValidationUtils.isIntegerInRange, MIN_TAB_SIZE, MAX_TAB_SIZE)
});
PreferencesManager.definePreference(UPPERCASE_COLORS, "boolean", false);
PreferencesManager.definePreference(USE_TAB_CHAR, "boolean", false);
PreferencesManager.definePreference(WORD_WRAP, "boolean", true);

Expand Down
140 changes: 80 additions & 60 deletions src/extensions/default/InlineColorEditor/ColorEditor.js
Expand Up @@ -22,20 +22,23 @@
*/

/*jslint vars: true, plusplus: true, nomen: true, regexp: true, maxerr: 50 */
/*global define, brackets, $, window, tinycolor, Mustache */
/*global define, brackets, $, window, Mustache */

define(function (require, exports, module) {
"use strict";
require("thirdparty/tinycolor-min");

var KeyEvent = brackets.getModule("utils/KeyEvent"),
StringUtils = brackets.getModule("utils/StringUtils"),
Strings = brackets.getModule("strings");
var KeyEvent = brackets.getModule("utils/KeyEvent"),
PreferencesManager = brackets.getModule("preferences/PreferencesManager"),
StringUtils = brackets.getModule("utils/StringUtils"),
Strings = brackets.getModule("strings"),
tinycolor = require("thirdparty/tinycolor-min");

/** Mustache template that forms the bare DOM structure of the UI */
var ColorEditorTemplate = require("text!ColorEditorTemplate.html");

/** @const @type {number} */
/**
* @const @type {number}
*/
var STEP_MULTIPLIER = 5;

/**
Expand Down Expand Up @@ -64,6 +67,10 @@ define(function (require, exports, module) {
this._color = tinycolor(color);
this._originalColor = color;
this._redoColor = null;
this._isUpperCase = PreferencesManager.get("uppercaseColors");
PreferencesManager.on("change", "uppercaseColors", function () {
this._isUpperCase = PreferencesManager.get("uppercaseColors");
}.bind(this));

this.$colorValue = this.$element.find(".color-value");
this.$buttonList = this.$element.find("ul.button-bar");
Expand Down Expand Up @@ -152,11 +159,11 @@ define(function (require, exports, module) {
* incorrect to call this directly; use _commitColor() or setColorAsHsv() instead.
*/
ColorEditor.prototype._synchronize = function () {
var colorValue = this.getColor().toString(),
var colorValue = this.getColor().getOriginalInput(),
colorObject = tinycolor(colorValue),
hueColor = "hsl(" + this._hsv.h + ", 100%, 50%)";
hueColor = "hsl(" + this._hsv.h + ", 100%, 50%)";

this._updateColorTypeRadioButtons(colorObject.format);
this._updateColorTypeRadioButtons(colorObject.getFormat());
this.$colorValue.val(colorValue);
this.$currentColor.css("background-color", colorValue);
this.$selection.css("background-color", hueColor);
Expand All @@ -181,16 +188,28 @@ define(function (require, exports, module) {
});
};

/** Focus the main color square's thumb */
/**
* Focus the main color square's thumb.
* @return {boolean} True if we focused the square, false otherwise.
*/
ColorEditor.prototype.focus = function () {
if (!this.$selectionBase.is(":focus")) {
this.$selectionBase.focus();
return true;
}
return false;
};

/**
* Remove any preference listeners before destroying the editor.
*/
ColorEditor.prototype.destroy = function () {
PreferencesManager.off("change", "uppercaseColors");
};

/** @return {tinycolor|string} The currently selected color (TODO (#2201): type is unpredictable) */
/**
* @return {tinycolor|string} The currently selected color (TODO (#2201): type is unpredictable).
*/
ColorEditor.prototype.getColor = function () {
return this._color;
};
Expand All @@ -215,12 +234,12 @@ define(function (require, exports, module) {
/** Add event listeners to the format button bar */
ColorEditor.prototype._bindColorFormatToRadioButton = function (buttonClass, propertyName, value) {
var handler,
_this = this;
self = this;
handler = function (event) {
var colorObject, newColor, newFormat;
newFormat = $(event.currentTarget).html().toLowerCase().replace("%", "p");
newColor = _this.getColor();
colorObject = tinycolor(newColor);
var newFormat = $(event.currentTarget).html().toLowerCase().replace("%", "p"),
newColor = self.getColor().toString(),
colorObject = tinycolor(newColor);

switch (newFormat) {
case "hsla":
newColor = colorObject.toHslString();
Expand All @@ -233,19 +252,23 @@ define(function (require, exports, module) {
break;
case "hex":
newColor = colorObject.toHexString();
_this._hsv.a = 1;
self._hsv.a = 1;
break;
}
_this._commitColor(newColor, false);

// We need to run this again whenever RGB/HSL/Hex conversions
// are performed to preserve the case
newColor = self._isUpperCase ? newColor.toUpperCase() : newColor;
self._commitColor(newColor, false);
};
this.$element.find("." + buttonClass).click(handler);
};

/** Add event listener to the "original color value" swatch */
ColorEditor.prototype._bindOriginalColorButton = function () {
var _this = this;
var self = this;
this.$originalColor.click(function (event) {
_this._commitColor(_this._originalColor, true);
self._commitColor(self._originalColor, true);
});
};

Expand All @@ -256,7 +279,7 @@ define(function (require, exports, module) {
* @return {string} an RGB color string in the normal format using non-percentage values
*/
ColorEditor.prototype._convertToNormalRGB = function (color) {
var matches = color.match(/^rgb.*?([0-9]+)\%.*?([0-9]+)\%.*?([0-9]+)\%/);
var matches = color.match(/^rgb.*?([0-9]+)\%.*?([0-9]+)\%.*?([0-9]+)\%/i);
if (matches) {
var i, percentStr, value;
for (i = 0; i < 3; i++) {
Expand All @@ -279,30 +302,30 @@ define(function (require, exports, module) {
ColorEditor.prototype._normalizeColorString = function (color) {
var normalizedColor = color;

// Convert 6-digit hex to 3-digit hex as tinycolor (#ffaacc -> #fac)
// Convert 6-digit hex to 3-digit hex as TinyColor (#ffaacc -> #fac)
if (color.match(/^#[0-9a-fA-F]{6}/)) {
return tinycolor(color).toString();
}
if (color.match(/^(rgb|hsl)/)) {
if (color.match(/^(rgb|hsl)/i)) {
normalizedColor = normalizedColor.replace(/,\s*/g, ", ");
normalizedColor = normalizedColor.replace(/\(\s+/, "(");
normalizedColor = normalizedColor.replace(/\s+\)/, ")");
}
return normalizedColor.toLowerCase();
return normalizedColor;
};

/** Handle changes in text field */
ColorEditor.prototype._handleTextFieldInput = function (losingFocus) {
var newColor = $.trim(this.$colorValue.val()),
var newColor = $.trim(this.$colorValue.val()),
newColorObj = tinycolor(newColor),
newColorOk = newColorObj.ok;
newColorOk = newColorObj.isValid();

// tinycolor will auto correct an incomplete rgb or hsl value into a valid color value.
// TinyColor will auto correct an incomplete rgb or hsl value into a valid color value.
// eg. rgb(0,0,0 -> rgb(0, 0, 0)
// We want to avoid having tinycolor do this, because we don't want to sync the color
// We want to avoid having TinyColor do this, because we don't want to sync the color
// to the UI if it's incomplete. To accomplish this, we first normalize the original
// color string into the format tinycolor would generate, and then compare it to what
// tinycolor actually generates to see if it's different. If so, then we assume the color
// color string into the format TinyColor would generate, and then compare it to what
// TinyColor actually generates to see if it's different. If so, then we assume the color
// was incomplete to begin with.
if (newColorOk) {
newColorOk = (newColorObj.toString() === this._normalizeColorString(newColor));
Expand All @@ -320,14 +343,14 @@ define(function (require, exports, module) {
};

ColorEditor.prototype._bindInputHandlers = function () {
var _this = this;
var self = this;

this.$colorValue.bind("input", function (event) {
_this._handleTextFieldInput(false);
self._handleTextFieldInput(false);
});

this.$colorValue.bind("change", function (event) {
_this._handleTextFieldInput(true);
self._handleTextFieldInput(true);
});
};

Expand All @@ -336,13 +359,13 @@ define(function (require, exports, module) {
* @param {!Array.<{value:string, count:number}>} swatches
*/
ColorEditor.prototype._addSwatches = function (swatches) {
var _this = this;
var self = this;

// Create swatches
swatches.forEach(function (swatch) {
var stringFormat = (swatch.count > 1) ? Strings.COLOR_EDITOR_USED_COLOR_TIP_PLURAL : Strings.COLOR_EDITOR_USED_COLOR_TIP_SINGULAR,
usedColorTip = StringUtils.format(stringFormat, swatch.value, swatch.count);
_this.$swatches.append("<li tabindex='0'><div class='swatch-bg'><div class='swatch' style='background-color: " +
self.$swatches.append("<li tabindex='0'><div class='swatch-bg'><div class='swatch' style='background-color: " +
swatch.value + ";' title='" + usedColorTip + "'></div></div> <span class='value'" + " title='" +
usedColorTip + "'>" + swatch.value + "</span></li>");
});
Expand All @@ -353,18 +376,18 @@ define(function (require, exports, module) {
event.keyCode === KeyEvent.DOM_VK_ENTER ||
event.keyCode === KeyEvent.DOM_VK_SPACE) {
// Enter/Space is same as clicking on swatch
_this._commitColor($(event.currentTarget).find(".value").html());
self._commitColor($(event.currentTarget).find(".value").html());
} else if (event.keyCode === KeyEvent.DOM_VK_TAB) {
// Tab on last swatch loops back to color square
if (!event.shiftKey && $(this).next("li").length === 0) {
_this.$selectionBase.focus();
self.$selectionBase.focus();
return false;
}
}
});

this.$swatches.find("li").click(function (event) {
_this._commitColor($(event.currentTarget).find(".value").html());
self._commitColor($(event.currentTarget).find(".value").html());
});
};

Expand All @@ -374,8 +397,8 @@ define(function (require, exports, module) {
* @param {!{h:number=, s:number=, v:number=}} hsv Any missing values use the previous color's values.
*/
ColorEditor.prototype.setColorAsHsv = function (hsv) {
var colorVal, newColor, oldFormat;
oldFormat = tinycolor(this.getColor()).format;
var colorVal, newColor,
oldFormat = tinycolor(this.getColor()).getFormat();

// Set our state to the new color
$.extend(this._hsv, hsv);
Expand All @@ -396,6 +419,7 @@ define(function (require, exports, module) {
colorVal = this._hsv.a < 1 ? newColor.toRgbString() : newColor.toHexString();
break;
}
colorVal = this._isUpperCase ? colorVal.toUpperCase() : colorVal;
this._commitColor(colorVal, false);
};

Expand All @@ -406,17 +430,16 @@ define(function (require, exports, module) {
* @param {boolean=} resetHsv Pass false ONLY if hsv set already been modified to match colorVal. Default: true.
*/
ColorEditor.prototype._commitColor = function (colorVal, resetHsv) {
var colorObj;
if (resetHsv === undefined) {
resetHsv = true;
}
this._callback(colorVal);
this._color = colorVal;
this._color = tinycolor(colorVal);

if (resetHsv) {
colorObj = tinycolor(colorVal);
this._hsv = colorObj.toHsv();
this._color = colorObj;
this._hsv = this._color.toHsv();
}

this._redoColor = null; // if we had undone, this new value blows away the redo history
this._synchronize();
};
Expand All @@ -439,12 +462,11 @@ define(function (require, exports, module) {

/** Dragging color square's thumb */
ColorEditor.prototype._handleSelectionFieldDrag = function (event) {
var height, hsv, width, xOffset, yOffset;
height = this.$selection.height();
width = this.$selection.width();
xOffset = _getNewOffset(event.clientX, this.$selection.offset().left, width);
yOffset = _getNewOffset(event.clientY, this.$selection.offset().top, height);
hsv = {};
var height = this.$selection.height(),
width = this.$selection.width(),
xOffset = _getNewOffset(event.clientX, this.$selection.offset().left, width),
yOffset = _getNewOffset(event.clientY, this.$selection.offset().top, height),
hsv = {};
hsv.s = xOffset / width;
hsv.v = 1 - yOffset / height;
this.setColorAsHsv(hsv, false);
Expand All @@ -455,10 +477,9 @@ define(function (require, exports, module) {

/** Dragging hue slider thumb */
ColorEditor.prototype._handleHueDrag = function (event) {
var height, hsv, offset;
height = this.$hueSlider.height();
offset = _getNewOffset(event.clientY, this.$hueSlider.offset().top, height);
hsv = {};
var height = this.$hueSlider.height(),
offset = _getNewOffset(event.clientY, this.$hueSlider.offset().top, height),
hsv = {};
hsv.h = (1 - offset / height) * 360;
this.setColorAsHsv(hsv, false);
if (!this.$hueSlider.find(".selector-base").is(":focus")) {
Expand All @@ -468,11 +489,10 @@ define(function (require, exports, module) {

/** Dragging opacity slider thumb */
ColorEditor.prototype._handleOpacityDrag = function (event) {
var height, hsv, offset;
height = this.$opacitySlider.height();
offset = _getNewOffset(event.clientY, this.$opacitySlider.offset().top, height);
hsv = {};
hsv.a = 1 - offset / height;
var height = this.$opacitySlider.height(),
offset = _getNewOffset(event.clientY, this.$opacitySlider.offset().top, height),
hsv = {};
hsv.a = 1 - (offset / height);
this.setColorAsHsv(hsv, false);
if (!this.$opacitySlider.find(".selector-base").is(":focus")) {
this.$opacitySlider.find(".selector-base").focus();
Expand Down
Expand Up @@ -212,6 +212,7 @@ define(function (require, exports, module) {
var doc = this.hostEditor.document;
doc.off("change", this._handleHostDocumentChange);
doc.releaseRef();
this.colorEditor.destroy();
};

/** Comparator to sort by which colors are used the most */
Expand Down

0 comments on commit e83807d

Please sign in to comment.