colorpicker.ts 65 KB

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