Commit d70da932 authored by Gayane Petrosyan's avatar Gayane Petrosyan Committed by Commit Bot

[Chrome Colors] Custom color picker for Colors menu.

Custom color picker with following features:
 - keyboard navigation
 - preselect
 - updates color picker colors according to the generated theme
 - maintains the selected color while the dialog is open, even
   when other tiles are selected, but resets after dialog is closed.

screencast:
https://screencast.googleplex.com/cast/NjM3NTk3NTc5MTA5OTkwNHwxNjY5MDE1Mi1kYg

Bug: 988066
Change-Id: I4d1b6a1ecb0e4daf81806327f3b613a704a1df96
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1723019
Commit-Queue: Gayane Petrosyan <gayane@chromium.org>
Reviewed-by: default avatarMustafa Emre Acer <meacer@chromium.org>
Reviewed-by: default avatarKristi Park <kristipark@chromium.org>
Cr-Commit-Position: refs/heads/master@{#683045}
parent 26f1bc03
......@@ -108,7 +108,10 @@ customize.IDS = {
BACKGROUNDS_UPLOAD: 'backgrounds-upload',
BACKGROUNDS_UPLOAD_ICON: 'backgrounds-upload-icon',
CANCEL: 'bg-sel-footer-cancel',
COLOR_PICKER: 'color-picker',
COLOR_PICKER_TILE: 'color-picker-tile',
COLOR_PICKER_CONTAINER: 'color-picker-container',
COLOR_PICKER_ICON: 'color-picker-icon',
COLORS_BUTTON: 'colors-button',
COLORS_DEFAULT_ICON: 'colors-default-icon',
COLORS_THEME: 'colors-theme',
......@@ -1433,6 +1436,7 @@ customize.richerPicker_resetCustomizationMenu = function() {
customize.richerPicker_resetSelectedOptions();
customize.richerPicker_resetImageMenu();
customize.richerPicker_hideOpenSubmenu();
customize.resetColorsMenu();
};
/**
......@@ -2138,6 +2142,26 @@ customize.defaultThemeTileInteraction = function(event) {
ntpApiHandle.applyDefaultTheme();
};
/**
* Called when a new color is picked with color picker. Applies the color, and
* the selected style on the tile.
* @param {Event} event The event attributes for the interaction.
*/
customize.colorPickerTileInteraction = function(event) {
const hex = event.target.value;
const r = parseInt(hex.substring(1, 3), 16);
const g = parseInt(hex.substring(3, 5), 16);
const b = parseInt(hex.substring(5, 7), 16);
// If the picker is preselected and the user picks a new color, we need to
// treat the picker as a new selection and not a preselection.
if (customize.preselectedOptions.colorsMenuTile ===
$(customize.IDS.COLOR_PICKER_TILE)) {
customize.preselectedOptions.colorsMenuTile = null;
}
customize.updateColorsMenuTileSelection($(customize.IDS.COLOR_PICKER_TILE));
ntpApiHandle.applyAutogeneratedTheme(0, [r, g, b, 255]);
};
/**
* Loads Colors menu elements.
*/
......@@ -2171,12 +2195,28 @@ customize.loadColorsMenu = function() {
$(customize.IDS.COLORS_DEFAULT_ICON).onkeydown =
customize.tileOnKeyDownInteraction;
// On arrow keys focus the first element.
$(customize.IDS.COLORS_MENU).onkeydown = function(event) {
if (customize.arrowKeys.includes(event.keyCode)) {
$(customize.IDS.COLORS_DEFAULT_ICON).focus();
if (configData.chromeColorsCustomColorPicker) {
$(customize.IDS.COLOR_PICKER_TILE).focus();
} else {
$(customize.IDS.COLORS_DEFAULT_ICON).focus();
}
}
};
// Configure custom color picker.
if (configData.chromeColorsCustomColorPicker) {
$(customize.IDS.COLOR_PICKER_TILE).onclick = function(event) {
$(customize.IDS.COLOR_PICKER).click();
};
$(customize.IDS.COLOR_PICKER_TILE).onkeydown =
customize.tileOnKeyDownInteraction;
$(customize.IDS.COLOR_PICKER).onchange =
customize.colorPickerTileInteraction;
}
customize.colorsMenuOnThemeChange();
customize.colorsMenuLoaded = true;
......@@ -2213,7 +2253,8 @@ customize.colorsMenuPreselectTile = function() {
let tile;
if (themeInfo.usingDefaultTheme) {
tile = $(customize.IDS.COLORS_DEFAULT_ICON);
} else if (themeInfo.colorId) {
} else if (themeInfo.colorId && themeInfo.colorId > 0) {
// Color from predefined set is selected.
const tiles = Array.from(
$(customize.IDS.COLORS_MENU)
.getElementsByClassName(customize.CLASSES.COLLECTION_TILE));
......@@ -2223,6 +2264,25 @@ customize.colorsMenuPreselectTile = function() {
break;
}
}
} else if (
configData.chromeColorsCustomColorPicker && themeInfo.colorDark &&
themeInfo.colorLight) {
// Custom color is selected.
tile = $(customize.IDS.COLOR_PICKER_TILE);
// Update color picker tile colors.
$(customize.IDS.COLOR_PICKER).value = colorArrayToHex(themeInfo.colorDark);
$(customize.IDS.COLORS_MENU)
.style.setProperty(
'--custom-color-border', colorArrayToHex(themeInfo.colorDark));
$(customize.IDS.COLORS_MENU)
.style.setProperty(
'--custom-color-dark', colorArrayToHex(themeInfo.colorDark));
$(customize.IDS.COLORS_MENU)
.style.setProperty(
'--custom-color-light', colorArrayToHex(themeInfo.colorLight));
$(customize.IDS.COLOR_PICKER_ICON)
.classList.toggle('white', themeInfo.isNtpBackgroundDark);
}
if (tile && tile !== customize.selectedOptions.color) {
......@@ -2272,3 +2332,39 @@ customize.confirmColor = function() {
customize.cancelColor = function() {
ntpApiHandle.revertThemeChanges();
};
/**
* Reset Colors Menu elements to the default state, specifically the color
* picker.
*/
customize.resetColorsMenu = function() {
$(customize.IDS.COLOR_PICKER).value = null;
$(customize.IDS.COLORS_MENU).style.setProperty('--custom-color-border', '');
$(customize.IDS.COLORS_MENU).style.setProperty('--custom-color-dark', '');
$(customize.IDS.COLORS_MENU).style.setProperty('--custom-color-light', '');
$(customize.IDS.COLOR_PICKER_ICON).classList.toggle('white', false);
};
/**
* Converts an RGBA component into hex format.
* @param {number} c RGBA component.
* @return {string} RGBA component in hex format.
*/
function rgbComponentToHex(c) {
const hex = c.toString(16);
return hex.length == 1 ? '0' + hex : hex;
}
/**
* Converts an Array of color components into hex format "#000000".
* @param {Array<number>} color Array of RGBA color components.
* @return {string} color in hex format.
*/
function colorArrayToHex(color) {
if (!color || color.length != 4) {
return '';
}
return '#' + rgbComponentToHex(color[0]) + rgbComponentToHex(color[1]) +
rgbComponentToHex(color[2]);
}
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M17.66 5.41l.92.92-2.69 2.69-.92-.92 2.69-2.69M17.67 3c-.26 0-.51.1-.71.29l-3.12 3.12-1.93-1.91-1.41 1.41 1.42 1.42L3 16.25V21h4.75l8.92-8.92 1.42 1.42 1.41-1.41-1.92-1.92 3.12-3.12c.4-.4.4-1.03.01-1.42l-2.34-2.34c-.2-.19-.45-.29-.7-.29zM6.92 19L5 17.08l8.06-8.06 1.92 1.92L6.92 19z"/></svg>
\ No newline at end of file
......@@ -1570,13 +1570,16 @@ input:checked + .toggle .highlight {
#colors-menu {
--check-mark-size: 20px;
--check-mark-top: 2px;
--custom-color-border: rgb(var(--GG300-rgb));
--custom-color-dark: rgb(var(--GG100-rgb));
--custom-color-light: white;
--tile-size: 64px;
--tile-margin: 25px;
}
#colors-menu .bg-sel-tile-bg {
background-color: unset;
border-radius: calc(var(--tile-size)/2);
border-radius: 50%;
box-sizing: border-box;
height: var(--tile-size);
margin-bottom: var(--tile-margin);
......@@ -1714,17 +1717,54 @@ html[dir=rtl] #colors-menu .bg-sel-tile .selected-check {
}
}
#colors-theme-uninstall {
height: fit-content;
margin-inline-end: 16px;
}
#color-picker-container.visible {
display: inline-block;
}
#color-picker-container {
display: none;
vertical-align: top;
}
#colors-theme-uninstall {
height: fit-content;
margin-inline-end: 16px;
#color-picker-tile {
background-color: var(--custom-color-dark);
border: 1px solid var(--custom-color-border);
border-radius: 50%;
box-sizing: border-box;
opacity: 1;
}
#color-picker-icon {
-webkit-mask-image: url(icons/colorize.svg);
-webkit-mask-repeat: no-repeat;
-webkit-mask-size: 100%;
background-color: rgb(var(--GG700-rgb));
display: inline-block;
height: 20px;
/* Position the icon in the middle with offset of half its size. */
left: calc(50% - 10px);
position: absolute;
/* Position the icon in the middle with offset of half its size. */
top: calc(50% - 10px);
width: 20px;
}
#color-picker-icon.white {
background-color: white;
}
#left-semicircle {
background-color: var(--custom-color-light);
border-bottom-left-radius: var(--tile-size);
border-top-left-radius: var(--tile-size);
display: inline-block;
height: 100%;
width: 50%;
}
#refresh-daily-wrapper {
......
......@@ -315,6 +315,12 @@
</button>
</div>
<div id="color-picker-container" class="bg-sel-tile-bg">
<div id="color-picker-tile" class="bg-sel-tile" tabindex="-1">
<div id="left-semicircle"></div>
<div id="color-picker-icon"></div>
<input id="color-picker" type="color" style="display:none">
</input>
</div>
</div>
<div id="colors-default" class="bg-sel-tile-bg">
<div id="colors-default-icon" class="bg-sel-tile" tabindex="-1"></div>
......
......@@ -726,6 +726,10 @@ void InstantService::BuildThemeInfo() {
} else if (theme_service->UsingAutogenerated()) {
theme_info_->color_id = chrome_colors::ChromeColorsService::GetColorId(
theme_service->GetThemeColor());
theme_info_->color_dark =
theme_provider.GetColor(ThemeProperties::COLOR_FRAME);
theme_info_->color_light =
theme_provider.GetColor(ThemeProperties::COLOR_NTP_BACKGROUND);
}
}
......
......@@ -81,4 +81,6 @@ IPC_STRUCT_TRAITS_BEGIN(ThemeBackgroundInfo)
IPC_STRUCT_TRAITS_MEMBER(has_theme_image)
IPC_STRUCT_TRAITS_MEMBER(theme_name)
IPC_STRUCT_TRAITS_MEMBER(color_id)
IPC_STRUCT_TRAITS_MEMBER(color_dark)
IPC_STRUCT_TRAITS_MEMBER(color_light)
IPC_STRUCT_TRAITS_END()
......@@ -22,7 +22,9 @@ ThemeBackgroundInfo::ThemeBackgroundInfo()
logo_alternate(false),
has_theme_image(false),
theme_name(),
color_id(0) {}
color_id(-1),
color_dark(),
color_light() {}
ThemeBackgroundInfo::~ThemeBackgroundInfo() {
}
......@@ -47,7 +49,8 @@ bool ThemeBackgroundInfo::operator==(const ThemeBackgroundInfo& rhs) const {
has_attribution == rhs.has_attribution &&
logo_alternate == rhs.logo_alternate &&
has_theme_image == rhs.has_theme_image &&
theme_name == rhs.theme_name && color_id == rhs.color_id;
theme_name == rhs.theme_name && color_id == rhs.color_id &&
color_dark == rhs.color_dark && color_light == rhs.color_light;
}
InstantMostVisitedItem::InstantMostVisitedItem()
......
......@@ -108,8 +108,16 @@ struct ThemeBackgroundInfo {
// The theme name.
std::string theme_name;
// The color id for Chrome Colors. Valid only if Chrome Colors is set.
// The color id for Chrome Colors. It is -1 if Chrome Colors is not set, 0
// when Chrome Colors is set but not from predefined color list, and > 0 if
// Chrome Colors is set from predefined color list.
int color_id;
// The dark color for Chrome Colors. Valid only if Chrome Colors is set.
SkColor color_dark;
// The light color for Chrome Colors. Valid only if Chrome Colors is set.
SkColor color_light;
};
struct InstantMostVisitedItem {
......
......@@ -442,6 +442,10 @@ v8::Local<v8::Object> GenerateThemeBackgroundInfo(
SkColorToArray(isolate, internal::GetLogoColor(theme_info)));
builder.Set("colorId", theme_info.color_id);
if (theme_info.color_id != -1) {
builder.Set("colorDark", SkColorToArray(isolate, theme_info.color_dark));
builder.Set("colorLight", SkColorToArray(isolate, theme_info.color_light));
}
return builder.Build();
}
......
......@@ -20,6 +20,8 @@ test.customizeMenu.IDS = {
BACKGROUNDS_BUTTON: 'backgrounds-button',
BACKGROUNDS_IMAGE_MENU: 'backgrounds-image-menu',
BACKGROUNDS_MENU: 'backgrounds-menu',
COLOR_PICKER_CONTAINER: 'color-picker-container',
COLOR_PICKER_TILE: 'color-picker-tile',
COLORS_BUTTON: 'colors-button',
COLORS_DEFAULT: 'colors-default',
COLORS_DEFAULT_ICON: 'colors-default-icon',
......@@ -136,6 +138,7 @@ test.customizeMenu.setUp = function() {
// populate using base::test::ScopedFeatureList.
configData.richerPicker = true;
configData.chromeColors = true;
configData.chromeColorsCustomColorPicker = false;
customize.colorsMenuLoaded = false;
customize.builtTiles = false;
......@@ -292,7 +295,8 @@ test.customizeMenu.testMenu_ApplyUserSelections = function() {
const colorOptions =
$(test.customizeMenu.IDS.COLORS_MENU)
.getElementsByClassName(test.customizeMenu.CLASSES.COLLECTION_TILE);
colorOptions[1].click(); // Skip the default theme option.
// Skip the color picker and the default theme option.
colorOptions[2].click();
// Click done and check that all selections have applied.
$(test.customizeMenu.IDS.MENU_DONE).click();
......@@ -367,7 +371,8 @@ test.customizeMenu.testMenu_CancelUserSelections = function() {
const colorOptions =
$(test.customizeMenu.IDS.COLORS_MENU)
.getElementsByClassName(test.customizeMenu.CLASSES.COLLECTION_TILE);
const color = colorOptions[1]; // Skip the default theme option.
// Skip the color picker and the default theme option.
const color = colorOptions[2];
color.click();
// Click cancel and check that all changes have been reverted.
......@@ -402,7 +407,8 @@ test.customizeMenu.testMenu_CancelUserSelectionsKeyboard = function() {
const colorOptions =
$(test.customizeMenu.IDS.COLORS_MENU)
.getElementsByClassName(test.customizeMenu.CLASSES.COLLECTION_TILE);
const color = colorOptions[1]; // Skip the default theme option.
// Skip the color picker and the default theme option.
const color = colorOptions[2];
color.click();
// Click cancel and check that all changes have been reverted.
......@@ -733,7 +739,7 @@ test.customizeMenu.testColors_DoneButtonWithPreselect = function() {
test.customizeMenu.testColors_PreselectColor = function() {
test.customizeMenu.mockThemeBackgroundInfo = {
usingDefaultTheme: false,
colorId: '1'
colorId: 1
};
init();
$(test.customizeMenu.IDS.EDIT_BG).click();
......@@ -747,7 +753,8 @@ test.customizeMenu.testColors_PreselectColor = function() {
.getElementsByClassName('selected')[0]
.firstChild;
assertTrue(
tile.dataset.id === test.customizeMenu.mockThemeBackgroundInfo.colorId);
parseInt(tile.dataset.id) ===
test.customizeMenu.mockThemeBackgroundInfo.colorId);
};
/**
......@@ -756,7 +763,7 @@ test.customizeMenu.testColors_PreselectColor = function() {
test.customizeMenu.testColors_NoPreselectInvalidColorId = function() {
test.customizeMenu.mockThemeBackgroundInfo = {
usingDefaultTheme: false,
colorId: '0'
colorId: -1
};
init();
$(test.customizeMenu.IDS.EDIT_BG).click();
......@@ -783,6 +790,29 @@ test.customizeMenu.testColors_NoPreselectNoColorId = function() {
.length === 0);
};
/**
* Test preselect when custom color is used.
*/
test.customizeMenu.testColors_PreselectColorPicker = function() {
configData.chromeColorsCustomColorPicker = true;
test.customizeMenu.mockThemeBackgroundInfo = {
usingDefaultTheme: false,
colorId: 0,
colorDark: [100, 100, 100],
colorLight: [200, 200, 200],
};
init();
$(test.customizeMenu.IDS.EDIT_BG).click();
$(test.customizeMenu.IDS.COLORS_BUTTON).click();
assertTrue(
$(test.customizeMenu.IDS.COLORS_MENU)
.getElementsByClassName('selected')
.length === 1);
assertTrue($(test.customizeMenu.IDS.COLOR_PICKER_CONTAINER)
.classList.contains(test.customizeMenu.CLASSES.SELECTED));
};
//// BACKGROUND SUBMENU TESTS ////
/*
......
......@@ -306,6 +306,12 @@
</button>
</div>
<div id="color-picker-container" class="bg-sel-tile-bg">
<div id="color-picker-tile" class="bg-sel-tile" tabindex="-1">
<div id="left-semicircle"></div>
<div id="color-picker-icon"></div>
<input id="color-picker" type="color" style="display:none">
</input>
</div>
</div>
<div id="colors-default" class="bg-sel-tile-bg">
<div id="colors-default-icon" class="bg-sel-tile" tabindex="-1"></div>
......
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