diff --git a/package.json b/package.json index ff3bca935c40..579e591c04e6 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "npm": ">=8.0.0" }, "dependencies": { + "@blockly/field-colour": "^4.0.2", "@blockly/keyboard-navigation": "0.5.4", "@blockly/plugin-workspace-search": "8.0.9", "@crowdin/crowdin-api-client": "^1.33.0", diff --git a/pxtblocks/fields/fieldEditorRegistry.ts b/pxtblocks/fields/fieldEditorRegistry.ts index fe21f32984dd..4dda51273921 100644 --- a/pxtblocks/fields/fieldEditorRegistry.ts +++ b/pxtblocks/fields/fieldEditorRegistry.ts @@ -7,7 +7,7 @@ import { FieldTextInput } from "./field_textinput"; import { FieldCustom, FieldCustomConstructor } from "./field_utils"; import { FieldSpriteEditor } from "./field_sprite"; import { FieldGridPicker } from "./field_gridpicker"; -// import { FieldColorNumber } from "./field_colour"; +import { FieldColorNumber } from "./field_colour"; import { FieldImages } from "./field_images"; import { FieldTextDropdown } from "./field_textdropdown"; import { FieldNumberDropdown } from "./field_numberdropdown"; @@ -53,7 +53,7 @@ export function initFieldEditors() { registerFieldEditor('toggledownup', FieldToggleDownUp); registerFieldEditor('togglehighlow', FieldToggleHighLow); registerFieldEditor('togglewinlose', FieldToggleWinLose); - // registerFieldEditor('colornumber', FieldColorNumber); + registerFieldEditor('colornumber', FieldColorNumber); registerFieldEditor('images', FieldImages); registerFieldEditor('sprite', FieldSpriteEditor); registerFieldEditor('animation', FieldAnimationEditor); diff --git a/pxtblocks/fields/field_colour.ts b/pxtblocks/fields/field_colour.ts index 2194c9ae21b1..330830b171bc 100644 --- a/pxtblocks/fields/field_colour.ts +++ b/pxtblocks/fields/field_colour.ts @@ -3,6 +3,8 @@ import * as Blockly from "blockly"; import { FieldCustom, FieldCustomOptions } from "./field_utils"; +import { FieldColour } from "@blockly/field-colour"; + /** * The value modes: * hex - Outputs an HTML color string: "#ffffff" (with quotes) @@ -18,113 +20,113 @@ export interface FieldColourNumberOptions extends FieldCustomOptions { valueMode?: FieldColourValueMode; } -// export class FieldColorNumber extends Blockly.FieldColour implements FieldCustom { -// public isFieldCustom_ = true; - -// protected colour_: string; -// private valueMode_: FieldColourValueMode = "rgb"; -// protected colours_: string[]; - -// constructor(text: string, params: FieldColourNumberOptions, opt_validator?: Blockly.FieldValidator) { -// super(text, opt_validator); - -// if (params.colours) -// this.setColours(JSON.parse(params.colours)); -// else if (pxt.appTarget.runtime && pxt.appTarget.runtime.palette) { -// let p = pxt.Util.clone(pxt.appTarget.runtime.palette); -// p[0] = "#dedede"; -// let t; -// if (pxt.appTarget.runtime.paletteNames) { -// t = pxt.Util.clone(pxt.appTarget.runtime.paletteNames); -// t[0] = lf("transparent"); -// } -// this.setColours(p, t); -// } - -// // Set to first color in palette (for toolbox) -// this.setValue(this.getColours_()[0]); - -// if (params.columns) this.setColumns(parseInt(params.columns)); -// if (params.valueMode) this.valueMode_ = params.valueMode; -// } - -// setColours(colours: string[], titles?: string[]): Blockly.FieldColour { -// const s = super.setColours(colours, titles); -// this.colours_ = colours; -// return s; -// } - - -// doClassValidation_(colour: string) { -// return "string" != typeof colour ? null : parseColour(colour, this.getColours_()); -// } - -// /** -// * Return the current colour. -// * @param {boolean} opt_asHex optional field if the returned value should be a hex -// * @return {string} Current colour in '#rrggbb' format. -// */ -// getValue(opt_asHex?: boolean) { -// if (opt_asHex) return this.value_; -// switch (this.valueMode_) { -// case "hex": -// return `"${this.value_}"`; -// case "rgb": -// if (this.value_.indexOf('#') > -1) { -// return `0x${this.value_.replace(/^#/, '')}`; -// } -// else { -// return this.value_; -// } -// case "index": -// if (!this.value_) return "-1"; -// const allColours = this.getColours_(); -// for (let i = 0; i < allColours.length; i++) { -// if (this.value_.toUpperCase() === allColours[i].toUpperCase()) { -// return i + ""; -// } -// } -// } -// return this.value_; -// } - -// /** -// * Set the colour. -// * @param {string} colour The new colour in '#rrggbb' format. -// */ -// doValueUpdate_(colour: string) { -// super.doValueUpdate_(parseColour(colour, this.getColours_())) -// // this.applyColour(); -// } - -// getColours_(): string[] { -// return this.colours_; -// } - -// override applyColour() { -// const block = this.getSourceBlock() as Blockly.BlockSvg | null; -// if (!block) throw new Blockly.UnattachedFieldError(); - -// if (!this.fieldGroup_) return; - -// const borderRect = this.borderRect_; -// if (!borderRect) { -// throw new Error('The border rect has not been initialized'); -// } - -// if (!this.isFullBlockField()) { -// borderRect.style.display = 'block'; -// borderRect.style.fill = this.getValue() as string; -// } else { -// borderRect.style.display = 'none'; -// // In general, do *not* let fields control the color of blocks. Having the -// // field control the color is unexpected, and could have performance -// // impacts. -// block.pathObject.svgPath.setAttribute('fill', parseColour(this.getValue(), this.getColours_())); -// block.pathObject.svgPath.setAttribute('stroke', '#fff'); -// } -// } -// } +export class FieldColorNumber extends FieldColour implements FieldCustom { + public isFieldCustom_ = true; + + protected colour_: string; + private valueMode_: FieldColourValueMode = "rgb"; + protected colours_: string[]; + + constructor(text: string, params: FieldColourNumberOptions, opt_validator?: Blockly.FieldValidator) { + super(text, opt_validator); + + if (params.colours) + this.setColours(JSON.parse(params.colours)); + else if (pxt.appTarget.runtime && pxt.appTarget.runtime.palette) { + let p = pxt.Util.clone(pxt.appTarget.runtime.palette); + p[0] = "#dedede"; + let t; + if (pxt.appTarget.runtime.paletteNames) { + t = pxt.Util.clone(pxt.appTarget.runtime.paletteNames); + t[0] = lf("transparent"); + } + this.setColours(p, t); + } + + // Set to first color in palette (for toolbox) + this.setValue(this.getColours_()[0]); + + if (params.columns) this.setColumns(parseInt(params.columns)); + if (params.valueMode) this.valueMode_ = params.valueMode; + } + + setColours(colours: string[], titles?: string[]): FieldColour { + const s = super.setColours(colours, titles); + this.colours_ = colours; + return s; + } + + + doClassValidation_(colour: string) { + return "string" != typeof colour ? null : parseColour(colour, this.getColours_()); + } + + /** + * Return the current colour. + * @param {boolean} opt_asHex optional field if the returned value should be a hex + * @return {string} Current colour in '#rrggbb' format. + */ + getValue(opt_asHex?: boolean) { + if (opt_asHex) return this.value_; + switch (this.valueMode_) { + case "hex": + return `"${this.value_}"`; + case "rgb": + if (this.value_.indexOf('#') > -1) { + return `0x${this.value_.replace(/^#/, '')}`; + } + else { + return this.value_; + } + case "index": + if (!this.value_) return "-1"; + const allColours = this.getColours_(); + for (let i = 0; i < allColours.length; i++) { + if (this.value_.toUpperCase() === allColours[i].toUpperCase()) { + return i + ""; + } + } + } + return this.value_; + } + + /** + * Set the colour. + * @param {string} colour The new colour in '#rrggbb' format. + */ + doValueUpdate_(colour: string) { + super.doValueUpdate_(parseColour(colour, this.getColours_())) + // this.applyColour(); + } + + getColours_(): string[] { + return this.colours_; + } + + override applyColour() { + const block = this.getSourceBlock() as Blockly.BlockSvg | null; + if (!block) throw new Blockly.UnattachedFieldError(); + + if (!this.fieldGroup_) return; + + const borderRect = this.borderRect_; + if (!borderRect) { + throw new Error('The border rect has not been initialized'); + } + + if (!this.isFullBlockField()) { + borderRect.style.display = 'block'; + borderRect.style.fill = this.getValue() as string; + } else { + borderRect.style.display = 'none'; + // In general, do *not* let fields control the color of blocks. Having the + // field control the color is unexpected, and could have performance + // impacts. + block.pathObject.svgPath.setAttribute('fill', parseColour(this.getValue(), this.getColours_())); + block.pathObject.svgPath.setAttribute('stroke', '#fff'); + } + } +} function parseColour(colour: string, allColours: string[]) { if (colour) {