From 3cb9cbb862e61b06ace66b54bbb29dac3b5649c4 Mon Sep 17 00:00:00 2001 From: tophf Date: Sun, 18 Oct 2020 16:40:11 +0300 Subject: [PATCH] show a palette for current editor in color picker (#1068) also keep the dialog visible for 30 seconds instead of 5 --- edit/colorpicker-helper.js | 4 +- vendor-overwrites/colorpicker/colorpicker.css | 12 +++++ vendor-overwrites/colorpicker/colorpicker.js | 26 ++++++++++- vendor-overwrites/colorpicker/colorview.js | 45 +++++++++++++++++++ 4 files changed, 83 insertions(+), 4 deletions(-) diff --git a/edit/colorpicker-helper.js b/edit/colorpicker-helper.js index 9736ab00..48297a3e 100644 --- a/edit/colorpicker-helper.js +++ b/edit/colorpicker-helper.js @@ -20,13 +20,11 @@ defaults.extraKeys[keyName] = 'colorpicker'; } defaults.colorpicker = { - // FIXME: who uses this? - // forceUpdate: editor.getEditors().length > 0, tooltip: t('colorpickerTooltip'), popup: { tooltipForSwitcher: t('colorpickerSwitchFormatTooltip'), hexUppercase: prefs.get('editor.colorpicker.hexUppercase'), - hideDelay: 5000, + hideDelay: 30e3, embedderCallback: state => { ['hexUppercase', 'color'] .filter(name => state[name] !== prefs.get('editor.colorpicker.' + name)) diff --git a/vendor-overwrites/colorpicker/colorpicker.css b/vendor-overwrites/colorpicker/colorpicker.css index e02b0d14..6ddfb97f 100644 --- a/vendor-overwrites/colorpicker/colorpicker.css +++ b/vendor-overwrites/colorpicker/colorpicker.css @@ -357,3 +357,15 @@ .colorpicker-format-change-button:hover { color: var(--label-color-hover); } + +.colorpicker-palette:not(:empty) { + padding: 0 8px 8px; + min-height: 14px; /* same as padding-left in .colorview-swatch */ + max-height: 10vw; + overflow: auto; + box-sizing: content-box; +} + +.colorpicker-palette .colorview-swatch { + padding-bottom: 14px; /* same as padding-left in .colorview-swatch */ +} diff --git a/vendor-overwrites/colorpicker/colorpicker.js b/vendor-overwrites/colorpicker/colorpicker.js index c3381495..b16e7380 100644 --- a/vendor-overwrites/colorpicker/colorpicker.js +++ b/vendor-overwrites/colorpicker/colorpicker.js @@ -30,6 +30,7 @@ let $swatch; let $formatChangeButton; let $hexCode; + let $palette; const $inputGroups = {}; const $inputs = {}; const $rgb = {}; @@ -164,6 +165,7 @@ $formatChangeButton = $('format-change-button', {textContent: '↔'}), ]}), ]}), + $palette = $('palette'), ]}); $inputs.hex = [$hexCode]; @@ -221,6 +223,11 @@ if (!isNaN(options.left) && !isNaN(options.top)) { reposition(); } + if (Array.isArray(options.palette)) { + // Might need to clear a lot of elements so this is known to be faster than textContent = '' + while ($palette.firstChild) $palette.firstChild.remove(); + $palette.append(...(options.palette)); + } } function hide() { @@ -454,7 +461,7 @@ const newHSV = color.type === 'hsl' ? colorConverter.HSLtoHSV(color) : colorConverter.RGBtoHSV(color); - if (Object.keys(newHSV).every(k => Math.abs(newHSV[k] - HSV[k]) < 1e-3)) { + if (Object.entries(newHSV).every(([k, v]) => v === HSV[k] || Math.abs(v - HSV[k]) < 1e-3)) { return; } HSV = newHSV; @@ -574,6 +581,19 @@ } } + /** @param {MouseEvent} e */ + function onPaletteClicked(e) { + if (e.target !== e.currentTarget) { + e.preventDefault(); + if (!e.button && setColor(e.target.__color)) { + userActivity = performance.now(); + colorpickerCallback(); + } else if (e.button === 2 && options.paletteCallback) { + options.paletteCallback(e.target); + } + } + } + function onMouseUp(event) { releaseMouse(event, ['saturation', 'hue', 'opacity']); if (onMouseDown.outsideClick) { @@ -710,6 +730,8 @@ $opacity.addEventListener('mousedown', onOpacityMouseDown); $hexLettercase.true.addEventListener('click', onHexLettercaseClicked); $hexLettercase.false.addEventListener('click', onHexLettercaseClicked); + $palette.addEventListener('click', onPaletteClicked); + $palette.addEventListener('contextmenu', onPaletteClicked); stopSnoozing(); if (!options.isShortCut) { @@ -735,6 +757,8 @@ $opacity.removeEventListener('mousedown', onOpacityMouseDown); $hexLettercase.true.removeEventListener('click', onHexLettercaseClicked); $hexLettercase.false.removeEventListener('click', onHexLettercaseClicked); + $palette.removeEventListener('click', onPaletteClicked); + $palette.removeEventListener('contextmenu', onPaletteClicked); releaseMouse(); stopSnoozing(); } diff --git a/vendor-overwrites/colorpicker/colorview.js b/vendor-overwrites/colorpicker/colorview.js index f88fe07b..d205e81f 100644 --- a/vendor-overwrites/colorpicker/colorview.js +++ b/vendor-overwrites/colorpicker/colorview.js @@ -532,6 +532,8 @@ color: data.color, prevColor: data.color || '', callback: popupOnChange, + palette: makePalette(state), + paletteCallback: el => paletteCallback(state, el), })); } @@ -551,6 +553,49 @@ } } + function makePalette({cm, options}) { + const palette = new Map(); + let i = 0; + let nums; + cm.eachLine(({markedSpans}) => { + ++i; + if (!markedSpans) return; + for (const {from, marker: m} of markedSpans) { + if (from == null || m.className !== COLORVIEW_CLASS) continue; + nums = palette.get(m.color); + if (!nums) palette.set(m.color, (nums = [])); + nums.push(i); + } + }); + const res = []; + if (palette.size > 1 || nums && nums.length > 1) { + const old = new Map((options.popup.palette || []).map(el => [el.__color, el])); + for (const [color, data] of palette) { + res.push(old.get(color) || makePaletteSwatch(color, data)); + } + } + return res; + } + + function makePaletteSwatch(color, nums) { + const s = nums.join(', '); + const el = document.createElement('div'); + el.className = COLORVIEW_SWATCH_CLASS; + el.style.cssText = COLORVIEW_SWATCH_CSS + color; + el.title = color + (!s ? '' : `\nLine: ${s.length > 50 ? s.replace(/([^,]+,\s){10}/g, '$&\n') : s}`); + el.__color = color; + return el; + } + + function paletteCallback({cm}, el) { + const lines = el.title.split('\n')[1].match(/\d+/g).map(Number); + const curLine = cm.getCursor().line + 1; + const i = lines.indexOf(curLine) + 1; + const pos = {line: (lines[i] || curLine) - 1, ch: 0}; + cm.scrollIntoView(pos, cm.defaultTextHeight()); + cm.setCursor(pos); + } + //endregion //region Utility