Skip to content

Commit 21391bb

Browse files
committed
Web Inspector: Color picker should have explicit format and gamut toggles for better discoverability
https://bugs.webkit.org/show_bug.cgi?id=305557 rdar://168216591 Reviewed by Devin Rousso. Add a gear icon to the color picker popover that shows a context menu with format and gamut conversion options, matching the existing InlineSwatch context menu behavior. * Source/WebInspectorUI/UserInterface/Models/Color.js: (WI.Color.prototype.getNextValidHEXFormat): * Source/WebInspectorUI/UserInterface/Views/ColorPicker.css: (.color-picker > .color-inputs-wrapper > :is(.pick-color-from-screen, .format-options)): (.color-picker > .color-inputs-wrapper > :is(.pick-color-from-screen, .format-options):active,): (.color-picker > .color-inputs-wrapper > .pick-color-from-screen): Deleted. (.color-picker > .color-inputs-wrapper > .pick-color-from-screen.active): Deleted. * Source/WebInspectorUI/UserInterface/Views/ColorPicker.js: (WI.ColorPicker.addContextMenuFormatItems): (WI.ColorPicker.prototype._changeFormat): (WI.ColorPicker.prototype._populateFormatContextMenu): (WI.ColorPicker): * Source/WebInspectorUI/UserInterface/Views/InlineSwatch.js: (WI.InlineSwatch.prototype._handleContextMenuEvent): (WI.InlineSwatch.prototype._getNextValidHEXFormat.hexMatchesCurrentColor): Deleted. (WI.InlineSwatch.prototype._getNextValidHEXFormat): Deleted. Canonical link: https://commits.webkit.org/306691@main
1 parent 5de150e commit 21391bb

File tree

4 files changed

+141
-110
lines changed

4 files changed

+141
-110
lines changed

Source/WebInspectorUI/UserInterface/Models/Color.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -769,6 +769,43 @@ WI.Color = class Color
769769
return true;
770770
}
771771

772+
getNextValidHEXFormat()
773+
{
774+
const hexFormats = [
775+
{format: WI.Color.Format.ShortHEX, title: WI.UIString("Format: Short Hex")},
776+
{format: WI.Color.Format.ShortHEXAlpha, title: WI.UIString("Format: Short Hex with Alpha")},
777+
{format: WI.Color.Format.HEX, title: WI.UIString("Format: Hex")},
778+
{format: WI.Color.Format.HEXAlpha, title: WI.UIString("Format: Hex with Alpha")},
779+
];
780+
781+
let hexMatchesColor = (hexInfo) => {
782+
let nextIsSimple = hexInfo.format === WI.Color.Format.ShortHEX || hexInfo.format === WI.Color.Format.HEX;
783+
if (nextIsSimple && !this.simple)
784+
return false;
785+
786+
let nextIsShort = hexInfo.format === WI.Color.Format.ShortHEX || hexInfo.format === WI.Color.Format.ShortHEXAlpha;
787+
if (nextIsShort && !this.canBeSerializedAsShortHEX())
788+
return false;
789+
790+
return true;
791+
};
792+
793+
let currentColorIsHEX = hexFormats.some((info) => info.format === this.format);
794+
795+
for (let i = 0; i < hexFormats.length; ++i) {
796+
if (currentColorIsHEX && this.format !== hexFormats[i].format)
797+
continue;
798+
799+
for (let j = ~~currentColorIsHEX; j < hexFormats.length; ++j) {
800+
let nextIndex = (i + j) % hexFormats.length;
801+
if (hexMatchesColor(hexFormats[nextIndex]))
802+
return hexFormats[nextIndex];
803+
}
804+
return null;
805+
}
806+
return hexFormats[0];
807+
}
808+
772809
// Private
773810

774811
_toOriginalString()

Source/WebInspectorUI/UserInterface/Views/ColorPicker.css

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,15 +97,16 @@
9797
margin: 0 0.25em;
9898
}
9999

100-
.color-picker > .color-inputs-wrapper > .pick-color-from-screen {
100+
.color-picker > .color-inputs-wrapper > :is(.pick-color-from-screen, .format-options) {
101101
width: 16px;
102102
height: 16px;
103103
color: var(--glyph-color);
104104
opacity: var(--glyph-opacity);
105105
}
106106

107+
.color-picker > .color-inputs-wrapper > :is(.pick-color-from-screen, .format-options):active,
107108
.color-picker > .color-inputs-wrapper > .pick-color-from-screen.active {
108-
color: var(--glyph-color-active);
109+
color: var(--glyph-color-pressed);
109110
}
110111

111112
.color-picker > .variable-color-swatches {

Source/WebInspectorUI/UserInterface/Views/ColorPicker.js

Lines changed: 96 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,14 @@ WI.ColorPicker = class ColorPicker extends WI.Object
6060

6161
let colorInputsWrapperElement = this._element.appendChild(document.createElement("div"));
6262
colorInputsWrapperElement.classList.add("color-inputs-wrapper");
63+
64+
if (!this._preventChangingColorFormats) {
65+
let formatOptionsElement = WI.ImageUtilities.useSVGSymbol("Images/Gear.svg", "format-options");
66+
formatOptionsElement.role = "button";
67+
WI.addMouseDownContextMenuHandlers(formatOptionsElement, this._populateFormatContextMenu.bind(this));
68+
colorInputsWrapperElement.appendChild(formatOptionsElement);
69+
}
70+
6371
colorInputsWrapperElement.appendChild(this._colorInputsContainerElement);
6472

6573
if (InspectorFrontendHost.canPickColorFromScreen()) {
@@ -139,10 +147,8 @@ WI.ColorPicker = class ColorPicker extends WI.Object
139147
return WI.Color.fromStringBestMatchingSuggestedFormatAndGamut(pickedColorCSSString, {suggestedFormat, suggestedGamut, forceSuggestedFormatAndGamut});
140148
}
141149

142-
// Static
143-
144150
static sortColorVariables(colorVariables)
145-
{
151+
{
146152
const rotation = 2;
147153
const weights = [Math.E / 11.279, Math.E / 3.934, Math.E / 39.975];
148154

@@ -171,6 +177,54 @@ WI.ColorPicker = class ColorPicker extends WI.Object
171177
});
172178
}
173179

180+
static addContextMenuFormatItems(contextMenu, color, formatChangedCallback)
181+
{
182+
let isColorOutsideSRGB = color.isOutsideSRGB();
183+
184+
if (!isColorOutsideSRGB) {
185+
if (color.isKeyword() && color.format !== WI.Color.Format.Keyword) {
186+
contextMenu.appendItem(WI.UIString("Format: Keyword"), () => {
187+
formatChangedCallback(WI.Color.Format.Keyword);
188+
});
189+
}
190+
191+
let hexInfo = color.getNextValidHEXFormat();
192+
if (hexInfo) {
193+
contextMenu.appendItem(hexInfo.title, () => {
194+
formatChangedCallback(hexInfo.format);
195+
});
196+
}
197+
198+
if (color.simple && color.format !== WI.Color.Format.HSL) {
199+
contextMenu.appendItem(WI.UIString("Format: HSL"), () => {
200+
formatChangedCallback(WI.Color.Format.HSL);
201+
});
202+
} else if (color.format !== WI.Color.Format.HSLA) {
203+
contextMenu.appendItem(WI.UIString("Format: HSLA"), () => {
204+
formatChangedCallback(WI.Color.Format.HSLA);
205+
});
206+
}
207+
208+
if (color.simple && color.format !== WI.Color.Format.RGB) {
209+
contextMenu.appendItem(WI.UIString("Format: RGB"), () => {
210+
formatChangedCallback(WI.Color.Format.RGB);
211+
});
212+
} else if (color.format !== WI.Color.Format.RGBA) {
213+
contextMenu.appendItem(WI.UIString("Format: RGBA"), () => {
214+
formatChangedCallback(WI.Color.Format.RGBA);
215+
});
216+
}
217+
218+
if (color.format !== WI.Color.Format.ColorFunction) {
219+
contextMenu.appendItem(WI.UIString("Format: Color Function"), () => {
220+
formatChangedCallback(WI.Color.Format.ColorFunction);
221+
});
222+
}
223+
224+
contextMenu.appendSeparator();
225+
}
226+
}
227+
174228
// Public
175229

176230
get element() { return this._element; }
@@ -401,6 +455,45 @@ WI.ColorPicker = class ColorPicker extends WI.Object
401455
this.color = resolvedColor;
402456
this.dispatchEventToListeners(WI.ColorPicker.Event.ColorChanged, {color: this._color, variableName});
403457
}
458+
459+
_changeFormat(format)
460+
{
461+
if (this._color.format === format)
462+
return;
463+
464+
this._color.format = format;
465+
this._colorInputsFormat = null;
466+
this._showColorComponentInputs();
467+
this.dispatchEventToListeners(WI.ColorPicker.Event.ColorChanged, {color: this._color});
468+
}
469+
470+
_populateFormatContextMenu(contextMenu)
471+
{
472+
WI.ColorPicker.addContextMenuFormatItems(contextMenu, this._color, (format) => {
473+
this._changeFormat(format);
474+
});
475+
476+
if (this._color.gamut !== WI.Color.Gamut.DisplayP3) {
477+
contextMenu.appendItem(WI.UIString("Convert to Display-P3"), () => {
478+
this._color.gamut = WI.Color.Gamut.DisplayP3;
479+
this._colorInputsFormat = null;
480+
this._showColorComponentInputs();
481+
this._updateColorGamut();
482+
this.dispatchEventToListeners(WI.ColorPicker.Event.ColorChanged, {color: this._color});
483+
});
484+
}
485+
486+
if (this._color.gamut !== WI.Color.Gamut.SRGB) {
487+
let label = this._color.isOutsideSRGB() ? WI.UIString("Clamp to sRGB") : WI.UIString("Convert to sRGB");
488+
contextMenu.appendItem(label, () => {
489+
this._color.gamut = WI.Color.Gamut.SRGB;
490+
this._colorInputsFormat = null;
491+
this._showColorComponentInputs();
492+
this._updateColorGamut();
493+
this.dispatchEventToListeners(WI.ColorPicker.Event.ColorChanged, {color: this._color});
494+
});
495+
}
496+
}
404497
};
405498

406499
WI.ColorPicker.Event = {

Source/WebInspectorUI/UserInterface/Views/InlineSwatch.js

Lines changed: 5 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -550,57 +550,11 @@ WI.InlineSwatch = class InlineSwatch extends WI.Object
550550
return;
551551

552552
let contextMenu = WI.ContextMenu.createFromEvent(event);
553-
let isColorOutsideSRGB = value.isOutsideSRGB();
554553

555-
if (!isColorOutsideSRGB) {
556-
if (value.isKeyword() && value.format !== WI.Color.Format.Keyword) {
557-
contextMenu.appendItem(WI.UIString("Format: Keyword"), () => {
558-
value.format = WI.Color.Format.Keyword;
559-
this._updateSwatch();
560-
});
561-
}
562-
563-
let hexInfo = this._getNextValidHEXFormat();
564-
if (hexInfo) {
565-
contextMenu.appendItem(hexInfo.title, () => {
566-
value.format = hexInfo.format;
567-
this._updateSwatch();
568-
});
569-
}
570-
571-
if (value.simple && value.format !== WI.Color.Format.HSL) {
572-
contextMenu.appendItem(WI.UIString("Format: HSL"), () => {
573-
value.format = WI.Color.Format.HSL;
574-
this._updateSwatch();
575-
});
576-
} else if (value.format !== WI.Color.Format.HSLA) {
577-
contextMenu.appendItem(WI.UIString("Format: HSLA"), () => {
578-
value.format = WI.Color.Format.HSLA;
579-
this._updateSwatch();
580-
});
581-
}
582-
583-
if (value.simple && value.format !== WI.Color.Format.RGB) {
584-
contextMenu.appendItem(WI.UIString("Format: RGB"), () => {
585-
value.format = WI.Color.Format.RGB;
586-
this._updateSwatch();
587-
});
588-
} else if (value.format !== WI.Color.Format.RGBA) {
589-
contextMenu.appendItem(WI.UIString("Format: RGBA"), () => {
590-
value.format = WI.Color.Format.RGBA;
591-
this._updateSwatch();
592-
});
593-
}
594-
595-
if (value.format !== WI.Color.Format.ColorFunction) {
596-
contextMenu.appendItem(WI.UIString("Format: Color Function"), () => {
597-
value.format = WI.Color.Format.ColorFunction;
598-
this._updateSwatch();
599-
});
600-
}
601-
602-
contextMenu.appendSeparator();
603-
}
554+
WI.ColorPicker.addContextMenuFormatItems(contextMenu, value, (format) => {
555+
value.format = format;
556+
this._updateSwatch();
557+
});
604558

605559
if (value.gamut !== WI.Color.Gamut.DisplayP3) {
606560
contextMenu.appendItem(WI.UIString("Convert to Display-P3"), () => {
@@ -610,68 +564,14 @@ WI.InlineSwatch = class InlineSwatch extends WI.Object
610564
}
611565

612566
if (value.gamut !== WI.Color.Gamut.SRGB) {
613-
let label = isColorOutsideSRGB ? WI.UIString("Clamp to sRGB") : WI.UIString("Convert to sRGB");
567+
let label = value.isOutsideSRGB() ? WI.UIString("Clamp to sRGB") : WI.UIString("Convert to sRGB");
614568
contextMenu.appendItem(label, () => {
615569
value.gamut = WI.Color.Gamut.SRGB;
616570
this._updateSwatch();
617571
});
618572
}
619573
}
620574

621-
_getNextValidHEXFormat()
622-
{
623-
if (this._type !== WI.InlineSwatch.Type.Color)
624-
return false;
625-
626-
let value = this.value;
627-
628-
function hexMatchesCurrentColor(hexInfo) {
629-
let nextIsSimple = hexInfo.format === WI.Color.Format.ShortHEX || hexInfo.format === WI.Color.Format.HEX;
630-
if (nextIsSimple && !value.simple)
631-
return false;
632-
633-
let nextIsShort = hexInfo.format === WI.Color.Format.ShortHEX || hexInfo.format === WI.Color.Format.ShortHEXAlpha;
634-
if (nextIsShort && !value.canBeSerializedAsShortHEX())
635-
return false;
636-
637-
return true;
638-
}
639-
640-
const hexFormats = [
641-
{
642-
format: WI.Color.Format.ShortHEX,
643-
title: WI.UIString("Format: Short Hex")
644-
},
645-
{
646-
format: WI.Color.Format.ShortHEXAlpha,
647-
title: WI.UIString("Format: Short Hex with Alpha")
648-
},
649-
{
650-
format: WI.Color.Format.HEX,
651-
title: WI.UIString("Format: Hex")
652-
},
653-
{
654-
format: WI.Color.Format.HEXAlpha,
655-
title: WI.UIString("Format: Hex with Alpha")
656-
}
657-
];
658-
659-
let currentColorIsHEX = hexFormats.some((info) => info.format === value.format);
660-
661-
for (let i = 0; i < hexFormats.length; ++i) {
662-
if (currentColorIsHEX && value.format !== hexFormats[i].format)
663-
continue;
664-
665-
for (let j = ~~currentColorIsHEX; j < hexFormats.length; ++j) {
666-
let nextIndex = (i + j) % hexFormats.length;
667-
if (hexMatchesCurrentColor(hexFormats[nextIndex]))
668-
return hexFormats[nextIndex];
669-
}
670-
return null;
671-
}
672-
return hexFormats[0];
673-
}
674-
675575
_findMatchingColorVariable(variableName)
676576
{
677577
let colorVariables = this._delegate?.inlineSwatchGetColorVariables?.(this) || [];

0 commit comments

Comments
 (0)