-
Notifications
You must be signed in to change notification settings - Fork 7.6k
Use upper/lowercase colors in Inline Color Editor #9596
Conversation
The "U" toggle button doesn't seem appropriate for a button of it's type. Perhaps it should be separated from the RGBa, Hex, and HSLa button-container. @larz0 will probably have a better idea, though. At the very least, I would perhaps make the button have more text than "U" as that seems rather odd. Another improvement would be having the button change state depending on the current state (U/L, for example (Uppercase/Lowercase)). Again, @larz0 will probably have better design suggestions. |
I also found what seems like a bug. Whenever I:
Any change to the color (opacity, color wheel, actual color) also seems to reset the uppercase to lowercase. |
This should be a property in brackets.json so that the future inline gradient editor can stick to the preferred case preference; brackets.json will have a UI eventually. |
@larz0 I haven't touched the preferences system before, that will be new to me. IIRC, I did a toggle button because it was the easiest for me to test with at the time. I will switch it to a preference. Ideas on the name? @Mark-Simulacrum That's the bug I forgot to document. :oops: That bug will still be valid after I switch to a preference as well. |
@le717 My suggestion for the preference is |
@@ -64,6 +67,7 @@ define(function (require, exports, module) { | |||
this._color = tinycolor(color); | |||
this._originalColor = color; | |||
this._redoColor = null; | |||
this._isUpperCase = PreferencesManager.get("uppercaseColors"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I should rename this as originally it represented if the text was currently uppercase or not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need to listen for this preference changing as follows:
PreferencesManager.on("change", "uppercaseColors", function () {
this._isUpperCase = PreferencesManager.get("uppercaseColors");
}.bind(this));
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, I have been successfully testing the pref change without reloading or listening with no issues. Change made.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The snippet is causing this._isUpperCase
to return undefined
no matter the preference. Retrieving the preference directly works every time. 😕
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I wasn't clear. I think it needs to be done both ways.
This initializes setting when inline editor is opened:
this._isUpperCase = PreferencesManager.get("uppercaseColors");
As long as you close inline editor, change preference, and re-open inline editor, then everything should work correctly.
The following block of code handles case where preference is changed while inline editor is open.
PreferencesManager.on("change", "uppercaseColors", function () {
this._isUpperCase = PreferencesManager.get("uppercaseColors");
}.bind(this));
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@redmunds I think the error is coming in here. Notice how this._color
is also in the prototype, but this is not. I have gone back and forth including it there too or not, but I've always hit issues either way. I have a feeling that somehow, the unit tests editor is not picking up the updated this._isUpperCase
value, and including it in the prototype as well will fix it. My question then is how to ensure listening to the change
will properly update the prototype and not the object.
Yea, the inline color editor is rather a mess (as already documented)...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@le717 The problem is in the test in this line of code in testConvert()
:
expect(colorEditor.getColor().toString()).toBe(result);
colorEditor.getColor()
returns a tinycolor with _originalInput
correctly stored as UPPER case. But tinycolor toString()
seems to ignore _originalInput
, so I think this is a bug in tinycolor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@le717 As a workaround, I think that line of testConvert()
could be changed to:
expect(colorEditor.getColor().getOriginalInput()).toBe(result);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tinycolor.toString()
actually works as the author intended. It does not ignore the original case, but rather normalizes (to lowercase) it before returning it. There was actually no way to get the original input (it wasn't even stored) until I suggested it and sent a patch in the form of tinycolor.getOriginalInput()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but since you added tinycolor.getOriginalInput()
, I think it changes expectations. I would expect that method to be called something like toNormalizedString()
. I'll leave it up to you if you want to pursue that.
@redmunds The current code should be ready for an initial pass now. @Mark-Simulacrum |
@le717, there are JSHint errors in tests ( |
I guess I wonder if we should restrict uppercase language names to Hex colors only, as all the others look quite odd (haven't looked at the code though, so maybe this is already the case). |
@busykai Fully aware of the JSHint errors. Those are the new tests I initially added. I'll be reusing them once the reworking reaches a more final state. ;) @marcelgerber
Yea, that would be a nice switch, but sounds like another preference-debate akin to
I've gone back and forth on that. The feature request asked for making only Hex values upper/lowercase, but somewhere there are probably devs who write the other colors in uppercase too, so why should we restrict them from using their preferred case? Right now, the case preference applies universally. |
@@ -92,7 +92,8 @@ define(function (require, exports, module) { | |||
STYLE_ACTIVE_LINE = "styleActiveLine", | |||
TAB_SIZE = "tabSize", | |||
WORD_WRAP = "wordWrap", | |||
USE_TAB_CHAR = "useTabChar"; | |||
USE_TAB_CHAR = "useTabChar", | |||
UPPERCASE_COLORS = "uppercaseColors"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer "uppercaseColors" for a preference key in case anyone ever wants to use it outside of a quick editor.
List of preferences should be in alphabetic order:
UPPERCASE_COLORS = "uppercaseColors",
USE_TAB_CHAR = "useTabChar",
WORD_WRAP = "wordWrap";
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
@redmunds Changes pushed. What else do you see? |
colorObject = tinycolor(newColor); | ||
var newFormat = $(event.currentTarget).html().toLowerCase().replace("%", "p"), | ||
newColor = _this.getColor(), | ||
isUpperCase = _this._isUpperCase; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't really need a new var for _this._isUpperCase
, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it is not. Leftover var from toggle button. Fixed.
Done with initial review. |
@redmunds Requested changes pushed, still have a comment on the preference listening. |
@@ -217,10 +229,10 @@ define(function (require, exports, module) { | |||
var handler, | |||
_this = this; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just for consistency (I know it's not something you've changed), we usually call this variable self
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll change that to self
next push.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
@le717 This looks good, so you can work on the unit tests now. |
@redmunds Is there a test suite I can look at for reference? It seems that no matter what I try, I cannot get the test to set |
The FileFilters test suite uses some preferences. |
@le717 Take a look in Editor-test.js at the "with soft tabs preference off" group of tests. |
Neither of those are working. :\ Is there a dependency or state that needs to be loaded/enabled first? |
@@ -473,34 +473,77 @@ define(function (require, exports, module) { | |||
expect(colorEditor.getColor().toString()).toBe(result); | |||
} | |||
|
|||
it("should convert a hex color to rgb when mode button clicked", function () { | |||
// Use uppercase colors | |||
PreferencesManager.set("uppercaseColors", true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@le717 Code should only be executed inside it()
callback functions to keep each test independent.
Here's what you should do:
- Copy entire original
describe("conversions", function () {
block and give it a unique name such asdescribe("conversions in UPPER CASE", function () {
- At top of
describe()
block and before firstit()
call, define these callbacks:
beforeEach(function () {
// Enable uppercase colors
PreferencesManager.set("uppercaseColors", true);
});
afterEach(function () {
// Re-disable uppercase colors
PreferencesManager.set("uppercaseColors", false);
});
This will automatically set preference before each test and cleanup after each test in that describe()
block.
Note: the testConvert()
function will be defined in both the original and new describe()
blocks, so you can move it up 1 level so there's only a single shared copy.
@redmunds All changes except mixed-case tests made. However, the uppercase tests are still failing despite |
it("should use uppercase colors", function () { | ||
expect(PreferencesManager.get("uppercaseColors")).toBe(true); | ||
}); | ||
it("should convert a hex color to rgb when mode button clicked", function () { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Each test should have a unique name, so you'll need to add something like "in uppercase" to each of these duplicated tests.
@redmunds Test names, mixed case tests, and use If everything looks good, I'll flatten this. |
@le717 Looks good! Squash it. |
this._isUpperCase = PreferencesManager.get("uppercaseColors"); | ||
PreferencesManager.on("change", "uppercaseColors", function () { | ||
this._isUpperCase = PreferencesManager.get("uppercaseColors"); | ||
}.bind(this)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Drive-by comment: this leaks listeners. Each time a color editor is opened, another change listener is added to PreferencesManager. The change listener will also keep the entire color editor in memory forever, since it's part of its closure. We shouldn't merge until this is fixed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! It sucks that JS objects have a constructor but not a destructor.
@le717 You could add a ColorEditor.destroy()
method to remove the listener, which could be called from the InlineColorEditor.onClosed()
method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@redmunds Doing so always throws "undefined is not a function", using both ColorEditor.destroy()
and this.colorEditor.destroy()
(like surrounding code).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make sure new method is part of object prototype: ColorEditor.prototype.destroy()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I misread that (my thoughts are a bit scrambled right now with classes just starting back). Fixed.
@peterflynn @redmunds Listener hopefully patched, hopefully passes your inspection. Already squashed the commits. |
@@ -503,6 +503,65 @@ define(function (require, exports, module) { | |||
it("should convert an hsla color to rgba when mode button clicked", function () { | |||
testConvert("hsla(152, 12%, 22%, 0.7)", "rgba", "rgba(49, 63, 57, 0.7)"); | |||
}); | |||
it("should convert a mixed case hsla color to rgba when mode button clicked", function () { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the mixed case tests!
Done with review. This looks good. Just one last question about unit tests. |
Questions answered, switched unit tests to use |
Looks good. Merging. @le717 Don't forget to add new preference to https://github.com/adobe/brackets/wiki/How-to-Use-Brackets#list-of-supported-preferences Note that prefs are now in alphabetic order. |
Use upper/lowercase colors in Inline Color Editor
@redmunds Done. I wanted to add that this has the chance of breaking extension if they for whatever reason use the TinyColor version from here (which I don't think they do, they host their own) because of the API changes, so it might be good to list it as a library update in the change log. |
I just searched through all the extensions on the registry and luckily, there's not a single one using tinycolor. So we're pretty much good to go. |
For #9519.
Features
This code is rather confusing (as already documented), so it was a bit of a challenge to get this written up. I once spent 30 digging around in various places for a one-line fix.
Now for the issues present here: