| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271 |
- /*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
- import { PixelRatio } from '../../../../base/browser/browser.js';
- import * as dom from '../../../../base/browser/dom.js';
- import { GlobalPointerMoveMonitor } from '../../../../base/browser/globalPointerMoveMonitor.js';
- import { Widget } from '../../../../base/browser/ui/widget.js';
- import { Color, HSVA, RGBA } from '../../../../base/common/color.js';
- import { Emitter } from '../../../../base/common/event.js';
- import { Disposable } from '../../../../base/common/lifecycle.js';
- import './colorPicker.css';
- import { localize } from '../../../../nls.js';
- import { editorHoverBackground } from '../../../../platform/theme/common/colorRegistry.js';
- import { registerThemingParticipant } from '../../../../platform/theme/common/themeService.js';
- const $ = dom.$;
- export class ColorPickerHeader extends Disposable {
- constructor(container, model, themeService) {
- super();
- this.model = model;
- this.domNode = $('.colorpicker-header');
- dom.append(container, this.domNode);
- this.pickedColorNode = dom.append(this.domNode, $('.picked-color'));
- const tooltip = localize('clickToToggleColorOptions', "Click to toggle color options (rgb/hsl/hex)");
- this.pickedColorNode.setAttribute('title', tooltip);
- const colorBox = dom.append(this.domNode, $('.original-color'));
- colorBox.style.backgroundColor = Color.Format.CSS.format(this.model.originalColor) || '';
- this.backgroundColor = themeService.getColorTheme().getColor(editorHoverBackground) || Color.white;
- this._register(registerThemingParticipant((theme, collector) => {
- this.backgroundColor = theme.getColor(editorHoverBackground) || Color.white;
- }));
- this._register(dom.addDisposableListener(this.pickedColorNode, dom.EventType.CLICK, () => this.model.selectNextColorPresentation()));
- this._register(dom.addDisposableListener(colorBox, dom.EventType.CLICK, () => {
- this.model.color = this.model.originalColor;
- this.model.flushColor();
- }));
- this._register(model.onDidChangeColor(this.onDidChangeColor, this));
- this._register(model.onDidChangePresentation(this.onDidChangePresentation, this));
- this.pickedColorNode.style.backgroundColor = Color.Format.CSS.format(model.color) || '';
- this.pickedColorNode.classList.toggle('light', model.color.rgba.a < 0.5 ? this.backgroundColor.isLighter() : model.color.isLighter());
- this.onDidChangeColor(this.model.color);
- }
- onDidChangeColor(color) {
- this.pickedColorNode.style.backgroundColor = Color.Format.CSS.format(color) || '';
- this.pickedColorNode.classList.toggle('light', color.rgba.a < 0.5 ? this.backgroundColor.isLighter() : color.isLighter());
- this.onDidChangePresentation();
- }
- onDidChangePresentation() {
- this.pickedColorNode.textContent = this.model.presentation ? this.model.presentation.label : '';
- this.pickedColorNode.prepend($('.codicon.codicon-color-mode'));
- }
- }
- export class ColorPickerBody extends Disposable {
- constructor(container, model, pixelRatio) {
- super();
- this.model = model;
- this.pixelRatio = pixelRatio;
- this.domNode = $('.colorpicker-body');
- dom.append(container, this.domNode);
- this.saturationBox = new SaturationBox(this.domNode, this.model, this.pixelRatio);
- this._register(this.saturationBox);
- this._register(this.saturationBox.onDidChange(this.onDidSaturationValueChange, this));
- this._register(this.saturationBox.onColorFlushed(this.flushColor, this));
- this.opacityStrip = new OpacityStrip(this.domNode, this.model);
- this._register(this.opacityStrip);
- this._register(this.opacityStrip.onDidChange(this.onDidOpacityChange, this));
- this._register(this.opacityStrip.onColorFlushed(this.flushColor, this));
- this.hueStrip = new HueStrip(this.domNode, this.model);
- this._register(this.hueStrip);
- this._register(this.hueStrip.onDidChange(this.onDidHueChange, this));
- this._register(this.hueStrip.onColorFlushed(this.flushColor, this));
- }
- flushColor() {
- this.model.flushColor();
- }
- onDidSaturationValueChange({ s, v }) {
- const hsva = this.model.color.hsva;
- this.model.color = new Color(new HSVA(hsva.h, s, v, hsva.a));
- }
- onDidOpacityChange(a) {
- const hsva = this.model.color.hsva;
- this.model.color = new Color(new HSVA(hsva.h, hsva.s, hsva.v, a));
- }
- onDidHueChange(value) {
- const hsva = this.model.color.hsva;
- const h = (1 - value) * 360;
- this.model.color = new Color(new HSVA(h === 360 ? 0 : h, hsva.s, hsva.v, hsva.a));
- }
- layout() {
- this.saturationBox.layout();
- this.opacityStrip.layout();
- this.hueStrip.layout();
- }
- }
- class SaturationBox extends Disposable {
- constructor(container, model, pixelRatio) {
- super();
- this.model = model;
- this.pixelRatio = pixelRatio;
- this._onDidChange = new Emitter();
- this.onDidChange = this._onDidChange.event;
- this._onColorFlushed = new Emitter();
- this.onColorFlushed = this._onColorFlushed.event;
- this.domNode = $('.saturation-wrap');
- dom.append(container, this.domNode);
- // Create canvas, draw selected color
- this.canvas = document.createElement('canvas');
- this.canvas.className = 'saturation-box';
- dom.append(this.domNode, this.canvas);
- // Add selection circle
- this.selection = $('.saturation-selection');
- dom.append(this.domNode, this.selection);
- this.layout();
- this._register(dom.addDisposableListener(this.domNode, dom.EventType.POINTER_DOWN, e => this.onPointerDown(e)));
- this._register(this.model.onDidChangeColor(this.onDidChangeColor, this));
- this.monitor = null;
- }
- onPointerDown(e) {
- if (!e.target || !(e.target instanceof Element)) {
- return;
- }
- this.monitor = this._register(new GlobalPointerMoveMonitor());
- const origin = dom.getDomNodePagePosition(this.domNode);
- if (e.target !== this.selection) {
- this.onDidChangePosition(e.offsetX, e.offsetY);
- }
- this.monitor.startMonitoring(e.target, e.pointerId, e.buttons, event => this.onDidChangePosition(event.pageX - origin.left, event.pageY - origin.top), () => null);
- const pointerUpListener = dom.addDisposableListener(document, dom.EventType.POINTER_UP, () => {
- this._onColorFlushed.fire();
- pointerUpListener.dispose();
- if (this.monitor) {
- this.monitor.stopMonitoring(true);
- this.monitor = null;
- }
- }, true);
- }
- onDidChangePosition(left, top) {
- const s = Math.max(0, Math.min(1, left / this.width));
- const v = Math.max(0, Math.min(1, 1 - (top / this.height)));
- this.paintSelection(s, v);
- this._onDidChange.fire({ s, v });
- }
- layout() {
- this.width = this.domNode.offsetWidth;
- this.height = this.domNode.offsetHeight;
- this.canvas.width = this.width * this.pixelRatio;
- this.canvas.height = this.height * this.pixelRatio;
- this.paint();
- const hsva = this.model.color.hsva;
- this.paintSelection(hsva.s, hsva.v);
- }
- paint() {
- const hsva = this.model.color.hsva;
- const saturatedColor = new Color(new HSVA(hsva.h, 1, 1, 1));
- const ctx = this.canvas.getContext('2d');
- const whiteGradient = ctx.createLinearGradient(0, 0, this.canvas.width, 0);
- whiteGradient.addColorStop(0, 'rgba(255, 255, 255, 1)');
- whiteGradient.addColorStop(0.5, 'rgba(255, 255, 255, 0.5)');
- whiteGradient.addColorStop(1, 'rgba(255, 255, 255, 0)');
- const blackGradient = ctx.createLinearGradient(0, 0, 0, this.canvas.height);
- blackGradient.addColorStop(0, 'rgba(0, 0, 0, 0)');
- blackGradient.addColorStop(1, 'rgba(0, 0, 0, 1)');
- ctx.rect(0, 0, this.canvas.width, this.canvas.height);
- ctx.fillStyle = Color.Format.CSS.format(saturatedColor);
- ctx.fill();
- ctx.fillStyle = whiteGradient;
- ctx.fill();
- ctx.fillStyle = blackGradient;
- ctx.fill();
- }
- paintSelection(s, v) {
- this.selection.style.left = `${s * this.width}px`;
- this.selection.style.top = `${this.height - v * this.height}px`;
- }
- onDidChangeColor() {
- if (this.monitor && this.monitor.isMonitoring()) {
- return;
- }
- this.paint();
- }
- }
- class Strip extends Disposable {
- constructor(container, model) {
- super();
- this.model = model;
- this._onDidChange = new Emitter();
- this.onDidChange = this._onDidChange.event;
- this._onColorFlushed = new Emitter();
- this.onColorFlushed = this._onColorFlushed.event;
- this.domNode = dom.append(container, $('.strip'));
- this.overlay = dom.append(this.domNode, $('.overlay'));
- this.slider = dom.append(this.domNode, $('.slider'));
- this.slider.style.top = `0px`;
- this._register(dom.addDisposableListener(this.domNode, dom.EventType.POINTER_DOWN, e => this.onPointerDown(e)));
- this.layout();
- }
- layout() {
- this.height = this.domNode.offsetHeight - this.slider.offsetHeight;
- const value = this.getValue(this.model.color);
- this.updateSliderPosition(value);
- }
- onPointerDown(e) {
- if (!e.target || !(e.target instanceof Element)) {
- return;
- }
- const monitor = this._register(new GlobalPointerMoveMonitor());
- const origin = dom.getDomNodePagePosition(this.domNode);
- this.domNode.classList.add('grabbing');
- if (e.target !== this.slider) {
- this.onDidChangeTop(e.offsetY);
- }
- monitor.startMonitoring(e.target, e.pointerId, e.buttons, event => this.onDidChangeTop(event.pageY - origin.top), () => null);
- const pointerUpListener = dom.addDisposableListener(document, dom.EventType.POINTER_UP, () => {
- this._onColorFlushed.fire();
- pointerUpListener.dispose();
- monitor.stopMonitoring(true);
- this.domNode.classList.remove('grabbing');
- }, true);
- }
- onDidChangeTop(top) {
- const value = Math.max(0, Math.min(1, 1 - (top / this.height)));
- this.updateSliderPosition(value);
- this._onDidChange.fire(value);
- }
- updateSliderPosition(value) {
- this.slider.style.top = `${(1 - value) * this.height}px`;
- }
- }
- class OpacityStrip extends Strip {
- constructor(container, model) {
- super(container, model);
- this.domNode.classList.add('opacity-strip');
- this._register(model.onDidChangeColor(this.onDidChangeColor, this));
- this.onDidChangeColor(this.model.color);
- }
- onDidChangeColor(color) {
- const { r, g, b } = color.rgba;
- const opaque = new Color(new RGBA(r, g, b, 1));
- const transparent = new Color(new RGBA(r, g, b, 0));
- this.overlay.style.background = `linear-gradient(to bottom, ${opaque} 0%, ${transparent} 100%)`;
- }
- getValue(color) {
- return color.hsva.a;
- }
- }
- class HueStrip extends Strip {
- constructor(container, model) {
- super(container, model);
- this.domNode.classList.add('hue-strip');
- }
- getValue(color) {
- return 1 - (color.hsva.h / 360);
- }
- }
- export class ColorPickerWidget extends Widget {
- constructor(container, model, pixelRatio, themeService) {
- super();
- this.model = model;
- this.pixelRatio = pixelRatio;
- this._register(PixelRatio.onDidChange(() => this.layout()));
- const element = $('.colorpicker-widget');
- container.appendChild(element);
- const header = new ColorPickerHeader(element, this.model, themeService);
- this.body = new ColorPickerBody(element, this.model, this.pixelRatio);
- this._register(header);
- this._register(this.body);
- }
- layout() {
- this.body.layout();
- }
- }
|