colorpicker.ts 63 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517
  1. import { Observable } from "babylonjs/Misc/observable";
  2. import { Color3, Vector2 } from "babylonjs/Maths/math";
  3. import { Control } from "./control";
  4. import { Measure } from "../measure";
  5. import { InputText } from "./inputText";
  6. import { Rectangle } from "./rectangle";
  7. import { Button } from "./button";
  8. import { Grid } from "./grid";
  9. import { AdvancedDynamicTexture } from "../advancedDynamicTexture";
  10. import { TextBlock } from "../controls/textBlock";
  11. import { _TypeStore } from 'babylonjs/Misc/typeStore';
  12. /** Class used to create color pickers */
  13. export class ColorPicker extends Control {
  14. private static _Epsilon = 0.000001;
  15. private _colorWheelCanvas: HTMLCanvasElement;
  16. private _value: Color3 = Color3.Red();
  17. private _tmpColor = new Color3();
  18. private _pointerStartedOnSquare = false;
  19. private _pointerStartedOnWheel = false;
  20. private _squareLeft = 0;
  21. private _squareTop = 0;
  22. private _squareSize = 0;
  23. private _h = 360;
  24. private _s = 1;
  25. private _v = 1;
  26. private _lastPointerDownID = -1;
  27. /**
  28. * Observable raised when the value changes
  29. */
  30. public onValueChangedObservable = new Observable<Color3>();
  31. /** Gets or sets the color of the color picker */
  32. public get value(): Color3 {
  33. return this._value;
  34. }
  35. public set value(value: Color3) {
  36. if (this._value.equals(value)) {
  37. return;
  38. }
  39. this._value.copyFrom(value);
  40. this._value.toHSVToRef(this._tmpColor);
  41. this._h = this._tmpColor.r;
  42. this._s = Math.max(this._tmpColor.g, 0.00001);
  43. this._v = Math.max(this._tmpColor.b, 0.00001);
  44. this._markAsDirty();
  45. if (this._value.r <= ColorPicker._Epsilon) {
  46. this._value.r = 0;
  47. }
  48. if (this._value.g <= ColorPicker._Epsilon) {
  49. this._value.g = 0;
  50. }
  51. if (this._value.b <= ColorPicker._Epsilon) {
  52. this._value.b = 0;
  53. }
  54. if (this._value.r >= 1.0 - ColorPicker._Epsilon) {
  55. this._value.r = 1.0;
  56. }
  57. if (this._value.g >= 1.0 - ColorPicker._Epsilon) {
  58. this._value.g = 1.0;
  59. }
  60. if (this._value.b >= 1.0 - ColorPicker._Epsilon) {
  61. this._value.b = 1.0;
  62. }
  63. this.onValueChangedObservable.notifyObservers(this._value);
  64. }
  65. /**
  66. * Gets or sets control width
  67. * @see http://doc.babylonjs.com/how_to/gui#position-and-size
  68. */
  69. public get width(): string | number {
  70. return this._width.toString(this._host);
  71. }
  72. public set width(value: string | number) {
  73. if (this._width.toString(this._host) === value) {
  74. return;
  75. }
  76. if (this._width.fromString(value)) {
  77. this._height.fromString(value);
  78. this._markAsDirty();
  79. }
  80. }
  81. /**
  82. * Gets or sets control height
  83. * @see http://doc.babylonjs.com/how_to/gui#position-and-size
  84. */
  85. public get height(): string | number {
  86. return this._height.toString(this._host);
  87. }
  88. /** Gets or sets control height */
  89. public set height(value: string | number) {
  90. if (this._height.toString(this._host) === value) {
  91. return;
  92. }
  93. if (this._height.fromString(value)) {
  94. this._width.fromString(value);
  95. this._markAsDirty();
  96. }
  97. }
  98. /** Gets or sets control size */
  99. public get size(): string | number {
  100. return this.width;
  101. }
  102. public set size(value: string | number) {
  103. this.width = value;
  104. }
  105. /**
  106. * Creates a new ColorPicker
  107. * @param name defines the control name
  108. */
  109. constructor(public name?: string) {
  110. super(name);
  111. this.value = new Color3(.88, .1, .1);
  112. this.size = "200px";
  113. this.isPointerBlocker = true;
  114. }
  115. protected _getTypeName(): string {
  116. return "ColorPicker";
  117. }
  118. /** @hidden */
  119. protected _preMeasure(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
  120. if (parentMeasure.width < parentMeasure.height) {
  121. this._currentMeasure.height = parentMeasure.width;
  122. } else {
  123. this._currentMeasure.width = parentMeasure.height;
  124. }
  125. }
  126. private _updateSquareProps(): void {
  127. var radius = Math.min(this._currentMeasure.width, this._currentMeasure.height) * .5;
  128. var wheelThickness = radius * .2;
  129. var innerDiameter = (radius - wheelThickness) * 2;
  130. var squareSize = innerDiameter / (Math.sqrt(2));
  131. var offset = radius - squareSize * .5;
  132. this._squareLeft = this._currentMeasure.left + offset;
  133. this._squareTop = this._currentMeasure.top + offset;
  134. this._squareSize = squareSize;
  135. }
  136. private _drawGradientSquare(hueValue: number, left: number, top: number, width: number, height: number, context: CanvasRenderingContext2D) {
  137. var lgh = context.createLinearGradient(left, top, width + left, top);
  138. lgh.addColorStop(0, '#fff');
  139. lgh.addColorStop(1, 'hsl(' + hueValue + ', 100%, 50%)');
  140. context.fillStyle = lgh;
  141. context.fillRect(left, top, width, height);
  142. var lgv = context.createLinearGradient(left, top, left, height + top);
  143. lgv.addColorStop(0, 'rgba(0,0,0,0)');
  144. lgv.addColorStop(1, '#000');
  145. context.fillStyle = lgv;
  146. context.fillRect(left, top, width, height);
  147. }
  148. private _drawCircle(centerX: number, centerY: number, radius: number, context: CanvasRenderingContext2D) {
  149. context.beginPath();
  150. context.arc(centerX, centerY, radius + 1, 0, 2 * Math.PI, false);
  151. context.lineWidth = 3;
  152. context.strokeStyle = '#333333';
  153. context.stroke();
  154. context.beginPath();
  155. context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
  156. context.lineWidth = 3;
  157. context.strokeStyle = '#ffffff';
  158. context.stroke();
  159. }
  160. private _createColorWheelCanvas(radius: number, thickness: number): HTMLCanvasElement {
  161. var canvas = document.createElement("canvas");
  162. canvas.width = radius * 2;
  163. canvas.height = radius * 2;
  164. var context = <CanvasRenderingContext2D>canvas.getContext("2d");
  165. var image = context.getImageData(0, 0, radius * 2, radius * 2);
  166. var data = image.data;
  167. var color = this._tmpColor;
  168. var maxDistSq = radius * radius;
  169. var innerRadius = radius - thickness;
  170. var minDistSq = innerRadius * innerRadius;
  171. for (var x = -radius; x < radius; x++) {
  172. for (var y = -radius; y < radius; y++) {
  173. var distSq = x * x + y * y;
  174. if (distSq > maxDistSq || distSq < minDistSq) {
  175. continue;
  176. }
  177. var dist = Math.sqrt(distSq);
  178. var ang = Math.atan2(y, x);
  179. Color3.HSVtoRGBToRef(ang * 180 / Math.PI + 180, dist / radius, 1, color);
  180. var index = ((x + radius) + ((y + radius) * 2 * radius)) * 4;
  181. data[index] = color.r * 255;
  182. data[index + 1] = color.g * 255;
  183. data[index + 2] = color.b * 255;
  184. var alphaRatio = (dist - innerRadius) / (radius - innerRadius);
  185. //apply less alpha to bigger color pickers
  186. var alphaAmount = .2;
  187. var maxAlpha = .2;
  188. var minAlpha = .04;
  189. var lowerRadius = 50;
  190. var upperRadius = 150;
  191. if (radius < lowerRadius) {
  192. alphaAmount = maxAlpha;
  193. } else if (radius > upperRadius) {
  194. alphaAmount = minAlpha;
  195. } else {
  196. alphaAmount = (minAlpha - maxAlpha) * (radius - lowerRadius) / (upperRadius - lowerRadius) + maxAlpha;
  197. }
  198. var alphaRatio = (dist - innerRadius) / (radius - innerRadius);
  199. if (alphaRatio < alphaAmount) {
  200. data[index + 3] = 255 * (alphaRatio / alphaAmount);
  201. } else if (alphaRatio > 1 - alphaAmount) {
  202. data[index + 3] = 255 * (1.0 - ((alphaRatio - (1 - alphaAmount)) / alphaAmount));
  203. } else {
  204. data[index + 3] = 255;
  205. }
  206. }
  207. }
  208. context.putImageData(image, 0, 0);
  209. return canvas;
  210. }
  211. /** @hidden */
  212. public _draw(context: CanvasRenderingContext2D): void {
  213. context.save();
  214. this._applyStates(context);
  215. var radius = Math.min(this._currentMeasure.width, this._currentMeasure.height) * .5;
  216. var wheelThickness = radius * .2;
  217. var left = this._currentMeasure.left;
  218. var top = this._currentMeasure.top;
  219. if (!this._colorWheelCanvas || this._colorWheelCanvas.width != radius * 2) {
  220. this._colorWheelCanvas = this._createColorWheelCanvas(radius, wheelThickness);
  221. }
  222. this._updateSquareProps();
  223. if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
  224. context.shadowColor = this.shadowColor;
  225. context.shadowBlur = this.shadowBlur;
  226. context.shadowOffsetX = this.shadowOffsetX;
  227. context.shadowOffsetY = this.shadowOffsetY;
  228. context.fillRect(this._squareLeft, this._squareTop, this._squareSize, this._squareSize);
  229. }
  230. context.drawImage(this._colorWheelCanvas, left, top);
  231. if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
  232. context.shadowBlur = 0;
  233. context.shadowOffsetX = 0;
  234. context.shadowOffsetY = 0;
  235. }
  236. this._drawGradientSquare(this._h,
  237. this._squareLeft,
  238. this._squareTop,
  239. this._squareSize,
  240. this._squareSize,
  241. context);
  242. var cx = this._squareLeft + this._squareSize * this._s;
  243. var cy = this._squareTop + this._squareSize * (1 - this._v);
  244. this._drawCircle(cx, cy, radius * .04, context);
  245. var dist = radius - wheelThickness * .5;
  246. cx = left + radius + Math.cos((this._h - 180) * Math.PI / 180) * dist;
  247. cy = top + radius + Math.sin((this._h - 180) * Math.PI / 180) * dist;
  248. this._drawCircle(cx, cy, wheelThickness * .35, context);
  249. context.restore();
  250. }
  251. // Events
  252. private _pointerIsDown = false;
  253. private _updateValueFromPointer(x: number, y: number): void {
  254. if (this._pointerStartedOnWheel) {
  255. var radius = Math.min(this._currentMeasure.width, this._currentMeasure.height) * .5;
  256. var centerX = radius + this._currentMeasure.left;
  257. var centerY = radius + this._currentMeasure.top;
  258. this._h = Math.atan2(y - centerY, x - centerX) * 180 / Math.PI + 180;
  259. }
  260. else if (this._pointerStartedOnSquare) {
  261. this._updateSquareProps();
  262. this._s = (x - this._squareLeft) / this._squareSize;
  263. this._v = 1 - (y - this._squareTop) / this._squareSize;
  264. this._s = Math.min(this._s, 1);
  265. this._s = Math.max(this._s, ColorPicker._Epsilon);
  266. this._v = Math.min(this._v, 1);
  267. this._v = Math.max(this._v, ColorPicker._Epsilon);
  268. }
  269. Color3.HSVtoRGBToRef(this._h, this._s, this._v, this._tmpColor);
  270. this.value = this._tmpColor;
  271. }
  272. private _isPointOnSquare(x: number, y: number): boolean {
  273. this._updateSquareProps();
  274. var left = this._squareLeft;
  275. var top = this._squareTop;
  276. var size = this._squareSize;
  277. if (x >= left && x <= left + size &&
  278. y >= top && y <= top + size) {
  279. return true;
  280. }
  281. return false;
  282. }
  283. private _isPointOnWheel(x: number, y: number): boolean {
  284. var radius = Math.min(this._currentMeasure.width, this._currentMeasure.height) * .5;
  285. var centerX = radius + this._currentMeasure.left;
  286. var centerY = radius + this._currentMeasure.top;
  287. var wheelThickness = radius * .2;
  288. var innerRadius = radius - wheelThickness;
  289. var radiusSq = radius * radius;
  290. var innerRadiusSq = innerRadius * innerRadius;
  291. var dx = x - centerX;
  292. var dy = y - centerY;
  293. var distSq = dx * dx + dy * dy;
  294. if (distSq <= radiusSq && distSq >= innerRadiusSq) {
  295. return true;
  296. }
  297. return false;
  298. }
  299. public _onPointerDown(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number): boolean {
  300. if (!super._onPointerDown(target, coordinates, pointerId, buttonIndex)) {
  301. return false;
  302. }
  303. this._pointerIsDown = true;
  304. this._pointerStartedOnSquare = false;
  305. this._pointerStartedOnWheel = false;
  306. // Invert transform
  307. this._invertTransformMatrix.transformCoordinates(coordinates.x, coordinates.y, this._transformedPosition);
  308. let x = this._transformedPosition.x;
  309. let y = this._transformedPosition.y;
  310. if (this._isPointOnSquare(x, y)) {
  311. this._pointerStartedOnSquare = true;
  312. } else if (this._isPointOnWheel(x, y)) {
  313. this._pointerStartedOnWheel = true;
  314. }
  315. this._updateValueFromPointer(x, y);
  316. this._host._capturingControl[pointerId] = this;
  317. this._lastPointerDownID = pointerId;
  318. return true;
  319. }
  320. public _onPointerMove(target: Control, coordinates: Vector2, pointerId: number): void {
  321. // Only listen to pointer move events coming from the last pointer to click on the element (To support dual vr controller interaction)
  322. if (pointerId != this._lastPointerDownID) {
  323. return;
  324. }
  325. // Invert transform
  326. this._invertTransformMatrix.transformCoordinates(coordinates.x, coordinates.y, this._transformedPosition);
  327. let x = this._transformedPosition.x;
  328. let y = this._transformedPosition.y;
  329. if (this._pointerIsDown) {
  330. this._updateValueFromPointer(x, y);
  331. }
  332. super._onPointerMove(target, coordinates, pointerId);
  333. }
  334. public _onPointerUp(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number, notifyClick: boolean): void {
  335. this._pointerIsDown = false;
  336. delete this._host._capturingControl[pointerId];
  337. super._onPointerUp(target, coordinates, pointerId, buttonIndex, notifyClick);
  338. }
  339. /**
  340. * This function expands the color picker by creating a color picker dialog with manual
  341. * color value input and the ability to save colors into an array to be used later in
  342. * subsequent launches of the dialogue.
  343. * @param advancedTexture defines the AdvancedDynamicTexture the dialog is assigned to
  344. * @param options defines size for dialog and options for saved colors. Also accepts last color picked as hex string and saved colors array as hex strings.
  345. * @returns picked color as a hex string and the saved colors array as hex strings.
  346. */
  347. public static ShowPickerDialogAsync(advancedTexture: AdvancedDynamicTexture,
  348. options: {
  349. pickerWidth?: string,
  350. pickerHeight?: string,
  351. headerHeight?: string,
  352. lastColor?: string,
  353. swatchLimit?: number,
  354. numSwatchesPerLine?: number,
  355. savedColors?: Array<string>
  356. }
  357. ): Promise<{
  358. savedColors?: string[],
  359. pickedColor: string
  360. }> {
  361. return new Promise((resolve, reject) => {
  362. // Default options
  363. options.pickerWidth = options.pickerWidth || "640px";
  364. options.pickerHeight = options.pickerHeight || "400px";
  365. options.headerHeight = options.headerHeight || "35px";
  366. options.lastColor = options.lastColor || "#000000";
  367. options.swatchLimit = options.swatchLimit || 20;
  368. options.numSwatchesPerLine = options.numSwatchesPerLine || 10;
  369. // Window size settings
  370. var drawerMaxRows: number = options.swatchLimit / options.numSwatchesPerLine;
  371. var rawSwatchSize: number = parseFloat(<string>options.pickerWidth) / options.numSwatchesPerLine;
  372. var gutterSize: number = Math.floor(rawSwatchSize * 0.25);
  373. var colGutters: number = gutterSize * (options.numSwatchesPerLine + 1);
  374. var swatchSize: number = Math.floor((parseFloat(<string>options.pickerWidth) - colGutters) / options.numSwatchesPerLine);
  375. var drawerMaxSize: number = (swatchSize * drawerMaxRows) + (gutterSize * (drawerMaxRows + 1));
  376. var containerSize: string = (parseInt(options.pickerHeight) + drawerMaxSize + Math.floor(swatchSize * 0.25)).toString() + "px";
  377. // Button Colors
  378. var buttonColor: string = "#c0c0c0";
  379. var buttonBackgroundColor: string = "#535353";
  380. var buttonBackgroundHoverColor: string = "#414141";
  381. var buttonBackgroundClickColor: string = "515151";
  382. var buttonDisabledColor: string = "#555555";
  383. var buttonDisabledBackgroundColor: string = "#454545";
  384. var currentSwatchesOutlineColor: string = "#404040";
  385. var luminanceLimitColor: Color3 = Color3.FromHexString("#dddddd");
  386. var luminanceLimit: number = luminanceLimitColor.r + luminanceLimitColor.g + luminanceLimitColor.b;
  387. var iconColorDark: string = "#aaaaaa";
  388. var iconColorLight: string = "#ffffff";
  389. var closeIconColor: Color3;
  390. // Button settings
  391. var buttonFontSize: number;
  392. var butEdit: Button;
  393. var buttonWidth: string;
  394. var buttonHeight: string;
  395. // Input Text Colors
  396. var inputFieldLabels: string[] = ["R", "G", "B"];
  397. var inputTextBackgroundColor: string = "#454545";
  398. var inputTextColor: string = "#f0f0f0";
  399. // This is the current color as set by either the picker or by entering a value
  400. var currentColor: Color3;
  401. // This int is used for naming swatches and serves as the index for calling them from the list
  402. var swatchNumber: number;
  403. // Menu Panel options. We need to know if the swatchDrawer exists so we can create it if needed.
  404. var swatchDrawer: Grid;
  405. var editSwatchMode: boolean = false;
  406. // Color InputText fields that will be updated upon value change
  407. var picker: ColorPicker;
  408. var rValInt: InputText;
  409. var gValInt: InputText;
  410. var bValInt: InputText;
  411. var rValDec: InputText;
  412. var gValDec: InputText;
  413. var bValDec: InputText;
  414. var hexVal: InputText;
  415. var newSwatch: Rectangle;
  416. var lastVal: string;
  417. var activeField: string;
  418. /**
  419. * Will update all values for InputText and ColorPicker controls based on the BABYLON.Color3 passed to this function.
  420. * Each InputText control and the ColorPicker control will be tested to see if they are the activeField and if they
  421. * are will receive no update. This is to prevent the input from the user being overwritten.
  422. */
  423. function updateValues(value: Color3, inputField: string) {
  424. activeField = inputField;
  425. var pickedColor: string = value.toHexString();
  426. newSwatch.background = pickedColor;
  427. if (rValInt.name != activeField) {
  428. rValInt.text = Math.floor(value.r * 255).toString();
  429. }
  430. if (gValInt.name != activeField) {
  431. gValInt.text = Math.floor(value.g * 255).toString();
  432. }
  433. if (bValInt.name != activeField) {
  434. bValInt.text = Math.floor(value.b * 255).toString();
  435. }
  436. if (rValDec.name != activeField) {
  437. rValDec.text = value.r.toString();
  438. }
  439. if (gValDec.name != activeField) {
  440. gValDec.text = value.g.toString();
  441. }
  442. if (bValDec.name != activeField) {
  443. bValDec.text = value.b.toString();
  444. }
  445. if (hexVal.name != activeField) {
  446. var minusPound: string[] = pickedColor.split("#");
  447. hexVal.text = minusPound[1];
  448. }
  449. if (picker.name != activeField) {
  450. picker.value = value;
  451. }
  452. }
  453. // When the user enters an integer for R, G, or B we check to make sure it is a valid number and replace if not.
  454. function updateInt(field: InputText, channel: string) {
  455. var newValue: string = field.text;
  456. var checkVal: boolean = /[^0-9]/g.test(newValue);
  457. if (checkVal) {
  458. field.text = lastVal;
  459. return;
  460. }
  461. else {
  462. if (newValue != "") {
  463. if (Math.floor(parseInt(newValue)) < 0) {
  464. newValue = "0";
  465. }
  466. else if (Math.floor(parseInt(newValue)) > 255) {
  467. newValue = "255";
  468. }
  469. else if (isNaN(parseInt(newValue))) {
  470. newValue = "0";
  471. }
  472. }
  473. if (activeField == field.name) {
  474. lastVal = newValue;
  475. }
  476. }
  477. if (newValue != "") {
  478. newValue = parseInt(newValue).toString();
  479. field.text = newValue;
  480. var newSwatchRGB: Color3 = Color3.FromHexString(newSwatch.background);
  481. if (activeField == field.name) {
  482. if (channel == "r") {
  483. updateValues(new Color3((parseInt(newValue)) / 255, newSwatchRGB.g, newSwatchRGB.b), field.name);
  484. }
  485. else if (channel == "g") {
  486. updateValues(new Color3(newSwatchRGB.r, (parseInt(newValue)) / 255, newSwatchRGB.b), field.name);
  487. }
  488. else {
  489. updateValues(new Color3(newSwatchRGB.r, newSwatchRGB.g, (parseInt(newValue)) / 255), field.name);
  490. }
  491. }
  492. }
  493. }
  494. // When the user enters a float for R, G, or B we check to make sure it is a valid number and replace if not.
  495. function updateFloat(field: InputText, channel: string) {
  496. var newValue: string = field.text;
  497. var checkVal: boolean = /[^0-9\.]/g.test(newValue);
  498. if (checkVal) {
  499. field.text = lastVal;
  500. return;
  501. }
  502. else {
  503. if (newValue != "" && newValue != "." && parseFloat(newValue) != 0) {
  504. if (parseFloat(newValue) < 0.0) {
  505. newValue = "0.0";
  506. }
  507. else if (parseFloat(newValue) > 1.0) {
  508. newValue = "1.0";
  509. }
  510. else if (isNaN(parseFloat(newValue))) {
  511. newValue = "0.0";
  512. }
  513. }
  514. if (activeField == field.name) {
  515. lastVal = newValue;
  516. }
  517. }
  518. if (newValue != "" && newValue != "." && parseFloat(newValue) != 0) {
  519. newValue = parseFloat(newValue).toString();
  520. field.text = newValue;
  521. }
  522. else {
  523. newValue = "0.0";
  524. }
  525. var newSwatchRGB = Color3.FromHexString(newSwatch.background);
  526. if (activeField == field.name) {
  527. if (channel == "r") {
  528. updateValues(new Color3(parseFloat(newValue), newSwatchRGB.g, newSwatchRGB.b), field.name);
  529. }
  530. else if (channel == "g") {
  531. updateValues(new Color3(newSwatchRGB.r, parseFloat(newValue), newSwatchRGB.b), field.name);
  532. }
  533. else {
  534. updateValues(new Color3(newSwatchRGB.r, newSwatchRGB.g, parseFloat(newValue)), field.name);
  535. }
  536. }
  537. }
  538. // Removes the current index from the savedColors array. Drawer can then be regenerated.
  539. function deleteSwatch(index: number) {
  540. if (options.savedColors) {
  541. options.savedColors.splice(index, 1);
  542. }
  543. if (options.savedColors && options.savedColors.length == 0) {
  544. setEditButtonVisibility(false);
  545. editSwatchMode = false;
  546. }
  547. }
  548. // Creates and styles an individual swatch when updateSwatches is called.
  549. function createSwatch() {
  550. if (options.savedColors && options.savedColors[swatchNumber]) {
  551. if (editSwatchMode) {
  552. var icon: string = "b";
  553. }
  554. else {
  555. var icon: string = "";
  556. }
  557. var swatch: Button = Button.CreateSimpleButton("Swatch_" + swatchNumber, icon);
  558. swatch.fontFamily = "BabylonJSglyphs";
  559. var swatchColor: Color3 = Color3.FromHexString(options.savedColors[swatchNumber]);
  560. var swatchLuminence: number = swatchColor.r + swatchColor.g + swatchColor.b;
  561. // Set color of outline and textBlock based on luminance of the color swatch so feedback always visible
  562. if (swatchLuminence > luminanceLimit) {
  563. swatch.color = iconColorDark;
  564. }
  565. else {
  566. swatch.color = iconColorLight;
  567. }
  568. swatch.fontSize = Math.floor(swatchSize * 0.7);
  569. swatch.textBlock!.verticalAlignment = Control.VERTICAL_ALIGNMENT_CENTER;
  570. swatch.height = swatch.width = (swatchSize).toString() + "px";
  571. swatch.background = options.savedColors[swatchNumber];
  572. swatch.thickness = 2;
  573. let metadata = swatchNumber;
  574. swatch.pointerDownAnimation = () => {
  575. swatch.thickness = 4;
  576. };
  577. swatch.pointerUpAnimation = () => {
  578. swatch.thickness = 3;
  579. };
  580. swatch.pointerEnterAnimation = () => {
  581. swatch.thickness = 3;
  582. };
  583. swatch.pointerOutAnimation = () => {
  584. swatch.thickness = 2;
  585. };
  586. swatch.onPointerClickObservable.add(() => {
  587. if (!editSwatchMode) {
  588. if (options.savedColors) {
  589. updateValues(Color3.FromHexString(options.savedColors[metadata]), swatch.name!);
  590. }
  591. }
  592. else {
  593. deleteSwatch(metadata);
  594. updateSwatches("", butSave);
  595. }
  596. });
  597. return swatch;
  598. }
  599. else {
  600. return null;
  601. }
  602. }
  603. // Mode switch to render button text and close symbols on swatch controls
  604. function editSwatches(mode?: boolean) {
  605. if (mode !== undefined) {
  606. editSwatchMode = mode;
  607. }
  608. if (editSwatchMode) {
  609. for (var i = 0; i < swatchDrawer.children.length; i++) {
  610. var thisButton: Button = swatchDrawer.children[i] as Button;
  611. thisButton.textBlock!.text = "b";
  612. }
  613. if (butEdit !== undefined) {
  614. butEdit.textBlock!.text = "Done";
  615. }
  616. }
  617. else {
  618. for (var i = 0; i < swatchDrawer.children.length; i++) {
  619. var thisButton: Button = swatchDrawer.children[i] as Button;
  620. thisButton.textBlock!.text = "";
  621. }
  622. if (butEdit !== undefined) {
  623. butEdit.textBlock!.text = "Edit";
  624. }
  625. }
  626. }
  627. /**
  628. * When Save Color button is pressed this function will first create a swatch drawer if one is not already
  629. * made. Then all controls are removed from the drawer and we step through the savedColors array and
  630. * creates one swatch per color. It will also set the height of the drawer control based on how many
  631. * saved colors there are and how many can be stored per row.
  632. */
  633. function updateSwatches(color: string, button: Button) {
  634. if (options.savedColors) {
  635. if (color != "") {
  636. options.savedColors.push(color);
  637. }
  638. swatchNumber = 0;
  639. swatchDrawer.clearControls();
  640. var rowCount: number = Math.ceil(options.savedColors.length / options.numSwatchesPerLine!);
  641. if (rowCount == 0) {
  642. var gutterCount: number = 0;
  643. }
  644. else {
  645. var gutterCount: number = rowCount + 1;
  646. }
  647. if (swatchDrawer.rowCount != rowCount + gutterCount) {
  648. var currentRows: number = swatchDrawer.rowCount;
  649. for (var i = 0; i < currentRows; i++) {
  650. swatchDrawer.removeRowDefinition(0);
  651. }
  652. for (var i = 0; i < rowCount + gutterCount; i++) {
  653. if (i % 2) {
  654. swatchDrawer.addRowDefinition(swatchSize, true);
  655. }
  656. else {
  657. swatchDrawer.addRowDefinition(gutterSize, true);
  658. }
  659. }
  660. }
  661. swatchDrawer.height = ((swatchSize * rowCount) + (gutterCount * gutterSize)).toString() + "px";
  662. for (var y = 1, thisRow = 1; y < rowCount + gutterCount; y += 2, thisRow++) {
  663. // Determine number of buttons to create per row based on the button limit per row and number of saved colors
  664. if (options.savedColors.length > thisRow * options.numSwatchesPerLine!) {
  665. var totalButtonsThisRow = options.numSwatchesPerLine!;
  666. }
  667. else {
  668. var totalButtonsThisRow = options.savedColors.length - ((thisRow - 1) * options.numSwatchesPerLine!);
  669. }
  670. var buttonIterations: number = (Math.min(Math.max(totalButtonsThisRow, 0), options.numSwatchesPerLine!));
  671. for (var x = 0, w = 1; x < buttonIterations; x++) {
  672. if (x > options.numSwatchesPerLine!) {
  673. continue;
  674. }
  675. var swatch: Button | null = createSwatch();
  676. if (swatch != null) {
  677. swatchDrawer.addControl(swatch, y, w);
  678. w += 2;
  679. swatchNumber++;
  680. }
  681. else {
  682. continue;
  683. }
  684. }
  685. }
  686. if (options.savedColors.length >= options.swatchLimit!) {
  687. disableButton(button, true);
  688. }
  689. else {
  690. disableButton(button, false);
  691. }
  692. }
  693. }
  694. // Shows or hides edit swatches button depending on if there are saved swatches
  695. function setEditButtonVisibility(enableButton: boolean) {
  696. if (enableButton) {
  697. butEdit = Button.CreateSimpleButton("butEdit", "Edit");
  698. butEdit.width = buttonWidth;
  699. butEdit.height = buttonHeight;
  700. butEdit.left = (Math.floor(parseInt(buttonWidth) * 0.1)).toString() + "px";
  701. butEdit.top = (parseFloat(butEdit.left) * -1).toString() + "px";
  702. butEdit.verticalAlignment = Control.VERTICAL_ALIGNMENT_BOTTOM;
  703. butEdit.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_LEFT;
  704. butEdit.thickness = 2;
  705. butEdit.color = buttonColor;
  706. butEdit.fontSize = buttonFontSize;
  707. butEdit.background = buttonBackgroundColor;
  708. butEdit.onPointerEnterObservable.add(() => {
  709. butEdit.background = buttonBackgroundHoverColor;
  710. });
  711. butEdit.onPointerOutObservable.add(() => {
  712. butEdit.background = buttonBackgroundColor;
  713. });
  714. butEdit.pointerDownAnimation = () => {
  715. butEdit.background = buttonBackgroundClickColor;
  716. };
  717. butEdit.pointerUpAnimation = () => {
  718. butEdit.background = buttonBackgroundHoverColor;
  719. };
  720. butEdit.onPointerClickObservable.add(() => {
  721. if (editSwatchMode) {
  722. editSwatchMode = false;
  723. }
  724. else {
  725. editSwatchMode = true;
  726. }
  727. editSwatches();
  728. });
  729. pickerGrid.addControl(butEdit, 1, 0);
  730. }
  731. else {
  732. pickerGrid.removeControl(butEdit);
  733. }
  734. }
  735. // Called when the user hits the limit of saved colors in the drawer.
  736. function disableButton(button: Button, disabled: boolean) {
  737. if (disabled) {
  738. button.color = buttonDisabledColor;
  739. button.background = buttonDisabledBackgroundColor;
  740. }
  741. else {
  742. button.color = buttonColor;
  743. button.background = buttonBackgroundColor;
  744. }
  745. }
  746. // Passes last chosen color back to scene and kills dialog by removing from AdvancedDynamicTexture
  747. function closePicker(color: string) {
  748. if (options.savedColors && options.savedColors.length > 0) {
  749. resolve({
  750. savedColors: options.savedColors,
  751. pickedColor: color
  752. });
  753. }
  754. else {
  755. resolve({
  756. pickedColor: color
  757. });
  758. }
  759. advancedTexture.removeControl(dialogContainer);
  760. }
  761. // Dialogue menu container which will contain both the main dialogue window and the swatch drawer which opens once a color is saved.
  762. var dialogContainer: Grid = new Grid();
  763. dialogContainer.name = "Dialog Container";
  764. dialogContainer.width = options.pickerWidth;
  765. if (options.savedColors) {
  766. dialogContainer.height = containerSize;
  767. var topRow: number = parseInt(options.pickerHeight) / parseInt(containerSize);
  768. dialogContainer.addRowDefinition(topRow, false);
  769. dialogContainer.addRowDefinition(1.0 - topRow, false);
  770. }
  771. else {
  772. dialogContainer.height = options.pickerHeight;
  773. dialogContainer.addRowDefinition(1.0, false);
  774. }
  775. advancedTexture.addControl(dialogContainer);
  776. // Swatch drawer which contains all saved color buttons
  777. if (options.savedColors) {
  778. swatchDrawer = new Grid();
  779. swatchDrawer.name = "Swatch Drawer";
  780. swatchDrawer.verticalAlignment = Control.VERTICAL_ALIGNMENT_TOP;
  781. swatchDrawer.background = buttonBackgroundColor;
  782. swatchDrawer.width = options.pickerWidth!;
  783. var initialRows: number = options.savedColors.length / options.numSwatchesPerLine;
  784. if (initialRows == 0) {
  785. var gutterCount: number = 0;
  786. }
  787. else {
  788. var gutterCount: number = initialRows + 1;
  789. }
  790. swatchDrawer.height = ((swatchSize * initialRows) + (gutterCount * gutterSize)).toString() + "px";
  791. swatchDrawer.top = Math.floor(swatchSize * 0.25).toString() + "px";
  792. for (var i = 0; i < (Math.ceil(options.savedColors.length / options.numSwatchesPerLine) * 2) + 1; i++) {
  793. if (i % 2 != 0) {
  794. swatchDrawer.addRowDefinition(swatchSize, true);
  795. }
  796. else {
  797. swatchDrawer.addRowDefinition(gutterSize, true);
  798. }
  799. }
  800. for (var i = 0; i < options.numSwatchesPerLine! * 2 + 1; i++) {
  801. if (i % 2 != 0) {
  802. swatchDrawer.addColumnDefinition(swatchSize, true);
  803. }
  804. else {
  805. swatchDrawer.addColumnDefinition(gutterSize, true);
  806. }
  807. }
  808. dialogContainer.addControl(swatchDrawer, 1, 0);
  809. }
  810. // Picker container
  811. var pickerPanel: Grid = new Grid();
  812. pickerPanel.name = "Picker Panel";
  813. pickerPanel.height = options.pickerHeight;
  814. var panelHead: number = parseInt(options.headerHeight) / parseInt(options.pickerHeight);
  815. var pickerPanelRows: number[] = [panelHead, 1.0 - panelHead];
  816. pickerPanel.addRowDefinition(pickerPanelRows[0], false);
  817. pickerPanel.addRowDefinition(pickerPanelRows[1], false);
  818. dialogContainer.addControl(pickerPanel, 0, 0);
  819. // Picker container header
  820. var header: Rectangle = new Rectangle();
  821. header.name = "Dialogue Header Bar";
  822. header.background = "#cccccc";
  823. header.thickness = 0;
  824. pickerPanel.addControl(header, 0, 0);
  825. // Header close button
  826. var closeButton: Button = Button.CreateSimpleButton("closeButton", "a");
  827. closeButton.fontFamily = "BabylonJSglyphs";
  828. var headerColor3: Color3 = Color3.FromHexString(header.background);
  829. closeIconColor = new Color3(1.0 - headerColor3.r, 1.0 - headerColor3.g, 1.0 - headerColor3.b);
  830. closeButton.color = closeIconColor.toHexString();
  831. closeButton.fontSize = Math.floor(parseInt(options.headerHeight!) * 0.6);
  832. closeButton.textBlock!.textVerticalAlignment = Control.VERTICAL_ALIGNMENT_CENTER;
  833. closeButton.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_RIGHT;
  834. closeButton.height = closeButton.width = options.headerHeight;
  835. closeButton.background = header.background;
  836. closeButton.thickness = 0;
  837. closeButton.pointerDownAnimation = () => {
  838. };
  839. closeButton.pointerUpAnimation = () => {
  840. closeButton.background = header.background;
  841. };
  842. closeButton.pointerEnterAnimation = () => {
  843. closeButton.color = header.background;
  844. closeButton.background = "red";
  845. };
  846. closeButton.pointerOutAnimation = () => {
  847. closeButton.color = closeIconColor.toHexString();
  848. closeButton.background = header.background;
  849. };
  850. closeButton.onPointerClickObservable.add(() => {
  851. closePicker(currentSwatch.background);
  852. });
  853. pickerPanel.addControl(closeButton, 0, 0);
  854. // Dialog container body
  855. var dialogBody: Grid = new Grid();
  856. dialogBody.name = "Dialogue Body";
  857. dialogBody.background = buttonBackgroundColor;
  858. var dialogBodyCols: number[] = [0.4375, 0.5625];
  859. dialogBody.addRowDefinition(1.0, false);
  860. dialogBody.addColumnDefinition(dialogBodyCols[0], false);
  861. dialogBody.addColumnDefinition(dialogBodyCols[1], false);
  862. pickerPanel.addControl(dialogBody, 1, 0);
  863. // Picker grid
  864. var pickerGrid: Grid = new Grid();
  865. pickerGrid.name = "Picker Grid";
  866. pickerGrid.addRowDefinition(0.85, false);
  867. pickerGrid.addRowDefinition(0.15, false);
  868. dialogBody.addControl(pickerGrid, 0, 0);
  869. // Picker control
  870. picker = new ColorPicker();
  871. picker.name = "GUI Color Picker";
  872. if (options.pickerHeight < options.pickerWidth) {
  873. picker.width = 0.89;
  874. }
  875. else {
  876. picker.height = 0.89;
  877. }
  878. picker.value = Color3.FromHexString(options.lastColor);
  879. picker.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER;
  880. picker.verticalAlignment = Control.VERTICAL_ALIGNMENT_CENTER;
  881. picker.onPointerDownObservable.add(() => {
  882. activeField = picker.name!;
  883. lastVal = "";
  884. editSwatches(false);
  885. });
  886. picker.onValueChangedObservable.add(function(value) { // value is a color3
  887. if (activeField == picker.name) {
  888. updateValues(value, picker.name);
  889. }
  890. });
  891. pickerGrid.addControl(picker, 0, 0);
  892. // Picker body right quarant
  893. var pickerBodyRight: Grid = new Grid();
  894. pickerBodyRight.name = "Dialogue Right Half";
  895. pickerBodyRight.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_LEFT;
  896. var pickerBodyRightRows: number[] = [0.514, 0.486];
  897. pickerBodyRight.addRowDefinition(pickerBodyRightRows[0], false);
  898. pickerBodyRight.addRowDefinition(pickerBodyRightRows[1], false);
  899. dialogBody.addControl(pickerBodyRight, 1, 1);
  900. // Picker container swatches and buttons
  901. var pickerSwatchesButtons: Grid = new Grid();
  902. pickerSwatchesButtons.name = "Swatches and Buttons";
  903. var pickerButtonsCol: number[] = [0.417, 0.583];
  904. pickerSwatchesButtons.addRowDefinition(1.0, false);
  905. pickerSwatchesButtons.addColumnDefinition(pickerButtonsCol[0], false);
  906. pickerSwatchesButtons.addColumnDefinition(pickerButtonsCol[1], false);
  907. pickerBodyRight.addControl(pickerSwatchesButtons, 0, 0);
  908. // Picker Swatches quadrant
  909. var pickerSwatches: Grid = new Grid();
  910. pickerSwatches.name = "New and Current Swatches";
  911. var pickeSwatchesRows: number[] = [0.04, 0.16, 0.64, 0.16];
  912. pickerSwatches.addRowDefinition(pickeSwatchesRows[0], false);
  913. pickerSwatches.addRowDefinition(pickeSwatchesRows[1], false);
  914. pickerSwatches.addRowDefinition(pickeSwatchesRows[2], false);
  915. pickerSwatches.addRowDefinition(pickeSwatchesRows[3], false);
  916. pickerSwatchesButtons.addControl(pickerSwatches, 0, 0);
  917. // Active swatches
  918. var activeSwatches: Grid = new Grid();
  919. activeSwatches.name = "Active Swatches";
  920. activeSwatches.width = 0.67;
  921. activeSwatches.addRowDefinition(0.5, false);
  922. activeSwatches.addRowDefinition(0.5, false);
  923. pickerSwatches.addControl(activeSwatches, 2, 0);
  924. var labelWidth: number = (Math.floor(parseInt(options.pickerWidth) * dialogBodyCols[1] * pickerButtonsCol[0] * 0.11));
  925. var labelHeight: number = (Math.floor(parseInt(options.pickerHeight) * pickerPanelRows[1] * pickerBodyRightRows[0] * pickeSwatchesRows[1] * 0.5));
  926. if (options.pickerWidth > options.pickerHeight) {
  927. var labelTextSize: number = labelHeight;
  928. }
  929. else {
  930. var labelTextSize: number = labelWidth;
  931. }
  932. // New color swatch and previous color button
  933. var newText: TextBlock = new TextBlock();
  934. newText.text = "new";
  935. newText.name = "New Color Label";
  936. newText.color = buttonColor;
  937. newText.fontSize = labelTextSize;
  938. pickerSwatches.addControl(newText, 1, 0);
  939. newSwatch = new Rectangle();
  940. newSwatch.name = "New Color Swatch";
  941. newSwatch.background = options.lastColor;
  942. newSwatch.thickness = 0;
  943. activeSwatches.addControl(newSwatch, 0, 0);
  944. var currentSwatch: Button = Button.CreateSimpleButton("currentSwatch", "");
  945. currentSwatch.background = options.lastColor;
  946. currentSwatch.thickness = 0;
  947. currentSwatch.onPointerClickObservable.add(() => {
  948. var revertColor = Color3.FromHexString(currentSwatch.background);
  949. updateValues(revertColor, currentSwatch.name!);
  950. editSwatches(false);
  951. });
  952. currentSwatch.pointerDownAnimation = () => { };
  953. currentSwatch.pointerUpAnimation = () => { };
  954. currentSwatch.pointerEnterAnimation = () => { };
  955. currentSwatch.pointerOutAnimation = () => { };
  956. activeSwatches.addControl(currentSwatch, 1, 0);
  957. var swatchOutline: Rectangle = new Rectangle();
  958. swatchOutline.name = "Swatch Outline";
  959. swatchOutline.width = 0.67;
  960. swatchOutline.thickness = 2;
  961. swatchOutline.color = currentSwatchesOutlineColor;
  962. swatchOutline.isHitTestVisible = false;
  963. pickerSwatches.addControl(swatchOutline, 2, 0);
  964. var currentText: TextBlock = new TextBlock();
  965. currentText.name = "Current Color Label";
  966. currentText.text = "current";
  967. currentText.color = buttonColor;
  968. currentText.fontSize = labelTextSize;
  969. pickerSwatches.addControl(currentText, 3, 0);
  970. // Buttons grid
  971. var buttonGrid: Grid = new Grid();
  972. buttonGrid.name = "Button Grid";
  973. buttonGrid.height = 0.8;
  974. var buttonGridRows: number = 1 / 3;
  975. buttonGrid.addRowDefinition(buttonGridRows, false);
  976. buttonGrid.addRowDefinition(buttonGridRows, false);
  977. buttonGrid.addRowDefinition(buttonGridRows, false);
  978. pickerSwatchesButtons.addControl(buttonGrid, 0, 1);
  979. // Determine pixel width and height for all buttons from overall panel dimensions
  980. buttonWidth = (Math.floor(parseInt(options.pickerWidth) * dialogBodyCols[1] * pickerButtonsCol[1] * 0.67)).toString() + "px";
  981. buttonHeight = (Math.floor(parseInt(options.pickerHeight) * pickerPanelRows[1] * pickerBodyRightRows[0] * (parseFloat(buttonGrid.height.toString()) / 100) * buttonGridRows * 0.7)).toString() + "px";
  982. // Determine button type size
  983. if (parseFloat(buttonWidth) > parseFloat(buttonHeight)) {
  984. buttonFontSize = Math.floor(parseFloat(buttonHeight) * 0.45);
  985. }
  986. else {
  987. buttonFontSize = Math.floor(parseFloat(buttonWidth) * 0.11);
  988. }
  989. // Panel Buttons
  990. var butOK: Button = Button.CreateSimpleButton("butOK", "OK");
  991. butOK.width = buttonWidth;
  992. butOK.height = buttonHeight;
  993. butOK.verticalAlignment = Control.VERTICAL_ALIGNMENT_CENTER;
  994. butOK.thickness = 2;
  995. butOK.color = buttonColor;
  996. butOK.fontSize = buttonFontSize;
  997. butOK.background = buttonBackgroundColor;
  998. butOK.onPointerEnterObservable.add(() => { butOK.background = buttonBackgroundHoverColor; });
  999. butOK.onPointerOutObservable.add(() => { butOK.background = buttonBackgroundColor; });
  1000. butOK.pointerDownAnimation = () => {
  1001. butOK.background = buttonBackgroundClickColor;
  1002. };
  1003. butOK.pointerUpAnimation = () => {
  1004. butOK.background = buttonBackgroundHoverColor;
  1005. };
  1006. butOK.onPointerClickObservable.add(() => {
  1007. editSwatches(false);
  1008. closePicker(newSwatch.background);
  1009. });
  1010. buttonGrid.addControl(butOK, 0, 0);
  1011. var butCancel: Button = Button.CreateSimpleButton("butCancel", "Cancel");
  1012. butCancel.width = buttonWidth;
  1013. butCancel.height = buttonHeight;
  1014. butCancel.verticalAlignment = Control.VERTICAL_ALIGNMENT_CENTER;
  1015. butCancel.thickness = 2;
  1016. butCancel.color = buttonColor;
  1017. butCancel.fontSize = buttonFontSize;
  1018. butCancel.background = buttonBackgroundColor;
  1019. butCancel.onPointerEnterObservable.add(() => { butCancel.background = buttonBackgroundHoverColor; });
  1020. butCancel.onPointerOutObservable.add(() => { butCancel.background = buttonBackgroundColor; });
  1021. butCancel.pointerDownAnimation = () => {
  1022. butCancel.background = buttonBackgroundClickColor;
  1023. };
  1024. butCancel.pointerUpAnimation = () => {
  1025. butCancel.background = buttonBackgroundHoverColor;
  1026. };
  1027. butCancel.onPointerClickObservable.add(() => {
  1028. editSwatches(false);
  1029. closePicker(currentSwatch.background);
  1030. });
  1031. buttonGrid.addControl(butCancel, 1, 0);
  1032. if (options.savedColors) {
  1033. var butSave: Button = Button.CreateSimpleButton("butSave", "Save");
  1034. butSave.width = buttonWidth;
  1035. butSave.height = buttonHeight;
  1036. butSave.verticalAlignment = Control.VERTICAL_ALIGNMENT_CENTER;
  1037. butSave.thickness = 2;
  1038. butSave.fontSize = buttonFontSize;
  1039. if (options.savedColors.length < options.swatchLimit!) {
  1040. butSave.color = buttonColor;
  1041. butSave.background = buttonBackgroundColor;
  1042. }
  1043. else {
  1044. disableButton(butSave, true);
  1045. }
  1046. butSave.onPointerEnterObservable.add(() => {
  1047. if (options.savedColors) {
  1048. if (options.savedColors.length < options.swatchLimit!) {
  1049. butSave.background = buttonBackgroundHoverColor;
  1050. }
  1051. }
  1052. });
  1053. butSave.onPointerOutObservable.add(() => {
  1054. if (options.savedColors) {
  1055. if (options.savedColors.length < options.swatchLimit!) {
  1056. butSave.background = buttonBackgroundColor;
  1057. }
  1058. }
  1059. });
  1060. butSave.pointerDownAnimation = () => {
  1061. if (options.savedColors) {
  1062. if (options.savedColors.length < options.swatchLimit!) {
  1063. butSave.background = buttonBackgroundClickColor;
  1064. }
  1065. }
  1066. };
  1067. butSave.pointerUpAnimation = () => {
  1068. if (options.savedColors) {
  1069. if (options.savedColors.length < options.swatchLimit!) {
  1070. butSave.background = buttonBackgroundHoverColor;
  1071. }
  1072. }
  1073. };
  1074. butSave.onPointerClickObservable.add(() => {
  1075. if (options.savedColors) {
  1076. if (options.savedColors.length == 0) {
  1077. setEditButtonVisibility(true);
  1078. }
  1079. if (options.savedColors.length < options.swatchLimit!) {
  1080. updateSwatches(newSwatch.background, butSave);
  1081. }
  1082. editSwatches(false);
  1083. }
  1084. });
  1085. if (options.savedColors.length > 0) {
  1086. setEditButtonVisibility(true);
  1087. }
  1088. buttonGrid.addControl(butSave, 2, 0);
  1089. }
  1090. // Picker color values input
  1091. var pickerColorValues: Grid = new Grid();
  1092. pickerColorValues.name = "Dialog Lower Right";
  1093. pickerColorValues.addRowDefinition(0.02, false);
  1094. pickerColorValues.addRowDefinition(0.63, false);
  1095. pickerColorValues.addRowDefinition(0.21, false);
  1096. pickerColorValues.addRowDefinition(0.14, false);
  1097. pickerBodyRight.addControl(pickerColorValues, 1, 0);
  1098. // RGB values text boxes
  1099. currentColor = Color3.FromHexString(options.lastColor);
  1100. var rgbValuesQuadrant: Grid = new Grid();
  1101. rgbValuesQuadrant.name = "RGB Values";
  1102. rgbValuesQuadrant.width = 0.82;
  1103. rgbValuesQuadrant.verticalAlignment = Control.VERTICAL_ALIGNMENT_CENTER;
  1104. rgbValuesQuadrant.addRowDefinition(1 / 3, false);
  1105. rgbValuesQuadrant.addRowDefinition(1 / 3, false);
  1106. rgbValuesQuadrant.addRowDefinition(1 / 3, false);
  1107. rgbValuesQuadrant.addColumnDefinition(0.1, false);
  1108. rgbValuesQuadrant.addColumnDefinition(0.2, false);
  1109. rgbValuesQuadrant.addColumnDefinition(0.7, false);
  1110. pickerColorValues.addControl(rgbValuesQuadrant, 1, 0);
  1111. for (var i = 0; i < inputFieldLabels.length; i++) {
  1112. var labelText: TextBlock = new TextBlock();
  1113. labelText.text = inputFieldLabels[i];
  1114. labelText.color = buttonColor;
  1115. labelText.fontSize = buttonFontSize;
  1116. rgbValuesQuadrant.addControl(labelText, i, 0);
  1117. }
  1118. // Input fields for RGB values
  1119. rValInt = new InputText();
  1120. rValInt.width = 0.83;
  1121. rValInt.height = 0.72;
  1122. rValInt.name = "rIntField";
  1123. rValInt.fontSize = buttonFontSize;
  1124. rValInt.text = (currentColor.r * 255).toString();
  1125. rValInt.color = inputTextColor;
  1126. rValInt.background = inputTextBackgroundColor;
  1127. rValInt.onFocusObservable.add(() => {
  1128. activeField = rValInt.name!;
  1129. lastVal = rValInt.text;
  1130. editSwatches(false);
  1131. });
  1132. rValInt.onBlurObservable.add(() => {
  1133. if (rValInt.text == "") {
  1134. rValInt.text = "0";
  1135. }
  1136. updateInt(rValInt, "r");
  1137. if (activeField == rValInt.name) {
  1138. activeField = "";
  1139. }
  1140. });
  1141. rValInt.onTextChangedObservable.add(() => {
  1142. if (activeField == rValInt.name) {
  1143. updateInt(rValInt, "r");
  1144. }
  1145. });
  1146. rgbValuesQuadrant.addControl(rValInt, 0, 1);
  1147. gValInt = new InputText();
  1148. gValInt.width = 0.83;
  1149. gValInt.height = 0.72;
  1150. gValInt.name = "gIntField";
  1151. gValInt.fontSize = buttonFontSize;
  1152. gValInt.text = (currentColor.g * 255).toString();
  1153. gValInt.color = inputTextColor;
  1154. gValInt.background = inputTextBackgroundColor;
  1155. gValInt.onFocusObservable.add(() => {
  1156. activeField = gValInt.name!;
  1157. lastVal = gValInt.text;
  1158. editSwatches(false);
  1159. });
  1160. gValInt.onBlurObservable.add(() => {
  1161. if (gValInt.text == "") {
  1162. gValInt.text = "0";
  1163. }
  1164. updateInt(gValInt, "g");
  1165. if (activeField == gValInt.name) {
  1166. activeField = "";
  1167. }
  1168. });
  1169. gValInt.onTextChangedObservable.add(() => {
  1170. if (activeField == gValInt.name) {
  1171. updateInt(gValInt, "g");
  1172. }
  1173. });
  1174. rgbValuesQuadrant.addControl(gValInt, 1, 1);
  1175. bValInt = new InputText();
  1176. bValInt.width = 0.83;
  1177. bValInt.height = 0.72;
  1178. bValInt.name = "bIntField";
  1179. bValInt.fontSize = buttonFontSize;
  1180. bValInt.text = (currentColor.b * 255).toString();
  1181. bValInt.color = inputTextColor;
  1182. bValInt.background = inputTextBackgroundColor;
  1183. bValInt.onFocusObservable.add(() => {
  1184. activeField = bValInt.name!;
  1185. lastVal = bValInt.text;
  1186. editSwatches(false);
  1187. });
  1188. bValInt.onBlurObservable.add(() => {
  1189. if (bValInt.text == "") {
  1190. bValInt.text = "0";
  1191. }
  1192. updateInt(bValInt, "b");
  1193. if (activeField == bValInt.name) {
  1194. activeField = "";
  1195. }
  1196. });
  1197. bValInt.onTextChangedObservable.add(() => {
  1198. if (activeField == bValInt.name) {
  1199. updateInt(bValInt, "b");
  1200. }
  1201. });
  1202. rgbValuesQuadrant.addControl(bValInt, 2, 1);
  1203. rValDec = new InputText();
  1204. rValDec.width = 0.95;
  1205. rValDec.height = 0.72;
  1206. rValDec.name = "rDecField";
  1207. rValDec.fontSize = buttonFontSize;
  1208. rValDec.text = currentColor.r.toString();
  1209. rValDec.color = inputTextColor;
  1210. rValDec.background = inputTextBackgroundColor;
  1211. rValDec.onFocusObservable.add(() => {
  1212. activeField = rValDec.name!;
  1213. lastVal = rValDec.text;
  1214. editSwatches(false);
  1215. });
  1216. rValDec.onBlurObservable.add(() => {
  1217. if (parseFloat(rValDec.text) == 0 || rValDec.text == "") {
  1218. rValDec.text = "0";
  1219. updateFloat(rValDec, "r");
  1220. }
  1221. if (activeField == rValDec.name) {
  1222. activeField = "";
  1223. }
  1224. });
  1225. rValDec.onTextChangedObservable.add(() => {
  1226. if (activeField == rValDec.name) {
  1227. updateFloat(rValDec, "r");
  1228. }
  1229. });
  1230. rgbValuesQuadrant.addControl(rValDec, 0, 2);
  1231. gValDec = new InputText();
  1232. gValDec.width = 0.95;
  1233. gValDec.height = 0.72;
  1234. gValDec.name = "gDecField";
  1235. gValDec.fontSize = buttonFontSize;
  1236. gValDec.text = currentColor.g.toString();
  1237. gValDec.color = inputTextColor;
  1238. gValDec.background = inputTextBackgroundColor;
  1239. gValDec.onFocusObservable.add(() => {
  1240. activeField = gValDec.name!;
  1241. lastVal = gValDec.text;
  1242. editSwatches(false);
  1243. });
  1244. gValDec.onBlurObservable.add(() => {
  1245. if (parseFloat(gValDec.text) == 0 || gValDec.text == "") {
  1246. gValDec.text = "0";
  1247. updateFloat(gValDec, "g");
  1248. }
  1249. if (activeField == gValDec.name) {
  1250. activeField = "";
  1251. }
  1252. });
  1253. gValDec.onTextChangedObservable.add(() => {
  1254. if (activeField == gValDec.name) {
  1255. updateFloat(gValDec, "g");
  1256. }
  1257. });
  1258. rgbValuesQuadrant.addControl(gValDec, 1, 2);
  1259. bValDec = new InputText();
  1260. bValDec.width = 0.95;
  1261. bValDec.height = 0.72;
  1262. bValDec.name = "bDecField";
  1263. bValDec.fontSize = buttonFontSize;
  1264. bValDec.text = currentColor.b.toString();
  1265. bValDec.color = inputTextColor;
  1266. bValDec.background = inputTextBackgroundColor;
  1267. bValDec.onFocusObservable.add(() => {
  1268. activeField = bValDec.name!;
  1269. lastVal = bValDec.text;
  1270. editSwatches(false);
  1271. });
  1272. bValDec.onBlurObservable.add(() => {
  1273. if (parseFloat(bValDec.text) == 0 || bValDec.text == "") {
  1274. bValDec.text = "0";
  1275. updateFloat(bValDec, "b");
  1276. }
  1277. if (activeField == bValDec.name) {
  1278. activeField = "";
  1279. }
  1280. });
  1281. bValDec.onTextChangedObservable.add(() => {
  1282. if (activeField == bValDec.name) {
  1283. updateFloat(bValDec, "b");
  1284. }
  1285. });
  1286. rgbValuesQuadrant.addControl(bValDec, 2, 2);
  1287. // Hex value input
  1288. var hexValueQuadrant: Grid = new Grid();
  1289. hexValueQuadrant.name = "Hex Value";
  1290. hexValueQuadrant.width = 0.82;
  1291. hexValueQuadrant.addRowDefinition(1.0, false);
  1292. hexValueQuadrant.addColumnDefinition(0.1, false);
  1293. hexValueQuadrant.addColumnDefinition(0.9, false);
  1294. pickerColorValues.addControl(hexValueQuadrant, 2, 0);
  1295. var labelText: TextBlock = new TextBlock();
  1296. labelText.text = "#";
  1297. labelText.color = buttonColor;
  1298. labelText.fontSize = buttonFontSize;
  1299. hexValueQuadrant.addControl(labelText, 0, 0);
  1300. hexVal = new InputText();
  1301. hexVal.width = 0.96;
  1302. hexVal.height = 0.72;
  1303. hexVal.name = "hexField";
  1304. hexVal.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER;
  1305. hexVal.fontSize = buttonFontSize;
  1306. var minusPound = options.lastColor.split("#");
  1307. hexVal.text = minusPound[1];
  1308. hexVal.color = inputTextColor;
  1309. hexVal.background = inputTextBackgroundColor;
  1310. hexVal.onFocusObservable.add(() => {
  1311. activeField = hexVal.name!;
  1312. lastVal = hexVal.text;
  1313. editSwatches(false);
  1314. });
  1315. hexVal.onBlurObservable.add(() => {
  1316. if (hexVal.text.length == 3) {
  1317. var val = hexVal.text.split("");
  1318. hexVal.text = val[0] + val[0] + val[1] + val[1] + val[2] + val[2];
  1319. }
  1320. if (hexVal.text == "") {
  1321. hexVal.text = "000000";
  1322. updateValues(Color3.FromHexString(hexVal.text), "b");
  1323. }
  1324. if (activeField == hexVal.name) {
  1325. activeField = "";
  1326. }
  1327. });
  1328. hexVal.onTextChangedObservable.add(() => {
  1329. var newHexValue: string = hexVal.text;
  1330. var checkHex: boolean = /[^0-9A-F]/i.test(newHexValue);
  1331. if ((hexVal.text.length > 6 || checkHex) && activeField == hexVal.name) {
  1332. hexVal.text = lastVal;
  1333. }
  1334. else {
  1335. if (hexVal.text.length < 6) {
  1336. var leadingZero: Number = 6 - hexVal.text.length;
  1337. for (var i = 0; i < leadingZero; i++) {
  1338. newHexValue = "0" + newHexValue;
  1339. }
  1340. }
  1341. if (hexVal.text.length == 3) {
  1342. var val: string[] = hexVal.text.split("");
  1343. newHexValue = val[0] + val[0] + val[1] + val[1] + val[2] + val[2];
  1344. }
  1345. newHexValue = "#" + newHexValue;
  1346. if (activeField == hexVal.name) {
  1347. lastVal = hexVal.text;
  1348. updateValues(Color3.FromHexString(newHexValue), hexVal.name);
  1349. }
  1350. }
  1351. });
  1352. hexValueQuadrant.addControl(hexVal, 0, 1);
  1353. if (options.savedColors && options.savedColors.length > 0) {
  1354. updateSwatches("", butSave!);
  1355. }
  1356. });
  1357. }
  1358. }
  1359. _TypeStore.RegisteredTypes["BABYLON.GUI.ColorPicker"] = ColorPicker;