A pixel art painter geared specifically at NES pixel art. Includes export for .chr binary file as well as palette and namespace data.
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

329 行
8.0KB

  1. import GlobalEvents from "/app/js/common/EventCaller.js";
  2. import Utils from "/app/js/common/Utils.js";
  3. import JSONSchema from "/app/js/common/JSONSchema.js";
  4. import EditableText from "/app/js/ui/EditableText.js";
  5. import NESPalette from "/app/js/models/NESPalette.js";
  6. const PLI_TEMPLATE = "palette-list-item-template";
  7. const PLI_TITLE = "title";
  8. const PLI_SELECTED = "list-item-selected";
  9. const PLI_BG_COLOR = "pal-bg-color";
  10. const PLI_FG_BASE = "pal-fg-";
  11. const PLI_BG_BASE = "pal-bg-";
  12. const PLI_COLOR_BASE = "nes-color-bg-";
  13. var Palettes = [];
  14. var CurrentPaletteIndex = 0;
  15. var BlockEmits = false;
  16. const SCHEMA_ID="http://nespaint/PalettesStoreSchema.json";
  17. JSONSchema.add({
  18. "$schema": "http://json-schema.org/draft-07/schema#",
  19. "$id": SCHEMA_ID,
  20. "type": "array",
  21. "items": {
  22. "type": "object",
  23. "properties": {
  24. "name":{
  25. "type":"string",
  26. "minLength":1
  27. },
  28. "palette":{
  29. "$ref":"http://nespaint/NESPaletteSchema.json"
  30. }
  31. },
  32. "required":["name","palette"]
  33. }
  34. });
  35. function HANDLE_PaletteClick(e){
  36. if (!this.hasAttribute("palname")){return;}
  37. var pname = this.getAttribute("palname");
  38. if (Palettes.length > 0){
  39. if (Palettes[CurrentPaletteIndex][0].value !== pname || !Palettes[CurrentPaletteIndex][2].classList.contains(PLI_SELECTED)){
  40. var oel = Palettes[CurrentPaletteIndex][2];
  41. oel.classList.remove(PLI_SELECTED);
  42. for (let i=0; i < Palettes.length; i++){
  43. if (Palettes[i][0].value === pname){
  44. Palettes[i][2].classList.add(PLI_SELECTED);
  45. CurrentPaletteIndex = i;
  46. GlobalEvents.emit("set_app_palette", Palettes[i][1]);
  47. break;
  48. }
  49. }
  50. }
  51. }
  52. }
  53. function SetElPaletteName(el, pname){
  54. el.setAttribute("palname", pname);
  55. var et = new EditableText(el, "title");
  56. et.value = pname;
  57. et.listen("value_change", (v) => {el.setAttribute("palname", v);});
  58. return et;
  59. /*var sel = el.querySelectorAll("." + PLI_TITLE);
  60. if (sel.length === 1){
  61. sel = sel[0];
  62. sel.innerHTML = pname;
  63. }*/
  64. }
  65. function SetElToColor(el, mode, pi, ci, hex){
  66. var cel = null;
  67. if (ci === 0){
  68. cel = el.querySelectorAll("." + PLI_BG_COLOR);
  69. } else {
  70. cel = el.querySelectorAll("." + ((mode == 0) ? PLI_FG_BASE : PLI_BG_BASE) + pi + "-" + ci);
  71. }
  72. if (cel !== null && cel.length === 1){
  73. cel = cel[0];
  74. var clist = cel.getAttribute("class").split(" ");
  75. for (let i=0; i < clist.length; i++){
  76. if (clist[i].startsWith(PLI_COLOR_BASE)){
  77. cel.classList.remove(clist[i]);
  78. break;
  79. }
  80. }
  81. cel.classList.add(PLI_COLOR_BASE + hex.toUpperCase());
  82. }
  83. }
  84. function ColorElementToPalette(el, palette){
  85. SetElToColor(
  86. el, 0,
  87. 0, 0,
  88. palette.get_palette_syscolor_index(0,0,true)
  89. );
  90. for (let p=0; p < 8; p++){
  91. for (let c=1; c < 4; c++){
  92. SetElToColor(
  93. el, (p >= 4) ? 0 : 1,
  94. p%4, c,
  95. palette.get_palette_syscolor_index(p,c,true)
  96. );
  97. }
  98. }
  99. }
  100. function ConnectElementToPalette(el, palette){
  101. palette.listen("palettes_changed", (e) => {
  102. if (e.type == "ALL"){
  103. if (e.hasOwnProperty("cindex")){
  104. SetElToColor(
  105. el, 0,
  106. 0, 0,
  107. palette.get_palette_syscolor_index(0,0,true)
  108. );
  109. } else {
  110. ColorElementToPalette(el, palette);
  111. }
  112. } else if (e.type == "SPRITE"){
  113. SetElToColor(
  114. el, 0,
  115. e.pindex%4, e.cindex,
  116. palette.get_palette_syscolor_index(e.pindex, e.cindex, true)
  117. );
  118. } else if (e.type == "TILE"){
  119. SetElToColor(
  120. el, 1,
  121. e.pindex, e.cindex,
  122. palette.get_palette_syscolor_index(e.pindex, e.cindex, true)
  123. );
  124. }
  125. });
  126. }
  127. function CreatePaletteDOMEntry(pname, palette){
  128. var oel = document.querySelectorAll("." + PLI_TEMPLATE);
  129. if (oel.length == 1){
  130. var el = oel[0].cloneNode(true);
  131. el.classList.remove(PLI_TEMPLATE);
  132. el.classList.remove("hidden");
  133. ConnectElementToPalette(el, palette);
  134. ColorElementToPalette(el, palette);
  135. //SetElPaletteName(el, pname);
  136. el.addEventListener("click", HANDLE_PaletteClick);
  137. oel[0].parentNode.appendChild(el);
  138. return el;
  139. } else {
  140. console.log("WARNING: Multiple templates found. Ambigous state.");
  141. }
  142. return null;
  143. }
  144. class CTRLPalettesStore{
  145. constructor(){
  146. GlobalEvents.listen("palstore-add", (function(ev){
  147. GlobalEvents.emit("modal-close");
  148. var e = document.querySelector(".palettes-store-add");
  149. if (e){
  150. var eform = e.querySelector("form");
  151. var einput = e.querySelector('input[name="storeitemname"]');
  152. if (eform && einput){
  153. var name = einput.value;
  154. eform.reset();
  155. this.createPalette(name);
  156. this.activatePalette(name);
  157. }
  158. }
  159. }).bind(this));
  160. GlobalEvents.listen("palstore-remove", (function(e){
  161. this.removePalette(Palettes[CurrentPaletteIndex][0]);
  162. }).bind(this));
  163. }
  164. get obj(){
  165. var d = [];
  166. for (let i=0; i < Palettes.length; i++){
  167. d.push({name:Palettes[i][0].value, palette:Palettes[i][1].obj});
  168. }
  169. return d;
  170. }
  171. set obj(d){
  172. var validator = JSONSchema.getValidator(SCHEMA_ID);
  173. if (validator !== null && validator(d)){
  174. this.clear();
  175. for (let i=0; i < d.length; i++){
  176. this.createPalette(d[i].name, JSON.stringify(d[i].palette));
  177. }
  178. } else {
  179. throw new Error("Object failed to validate against PalettesStoreSchema.");
  180. }
  181. }
  182. get json(){
  183. return JSON.stringify(this.obj);
  184. }
  185. set json(j){
  186. try {
  187. this.obj = JSON.parse(j);
  188. } catch (e) {
  189. throw e;
  190. }
  191. }
  192. initialize(){
  193. if (Palettes.length <= 0)
  194. this.createPalette("Palette");
  195. return this;
  196. }
  197. paletteIndexFromName(name){
  198. for (let i=1; i < Palettes.length; i++){
  199. if (Palettes[i][0].value == name){
  200. return i;
  201. }
  202. }
  203. return -1;
  204. }
  205. getPalette(name){
  206. var i = this.paletteIndexFromName(name);
  207. return (i >= 0) ? Palettes[i][1] : null;
  208. }
  209. createPalette(name, pjson){
  210. var palette = this.getPalette(name);
  211. if (palette === null){
  212. palette = new NESPalette();
  213. if (typeof(pjson) === "string"){
  214. try {
  215. palette.json = pjson;
  216. } catch (e) {
  217. console.log("Failed to create palette.", e.toString());
  218. palette = null;
  219. }
  220. } else {
  221. palette.set_palette([
  222. "0F",
  223. "05","06","07",
  224. "09","0A","0B",
  225. "01","02","03",
  226. "0D","00","20",
  227. "15","16","17",
  228. "19","1A","1B",
  229. "11","21","31",
  230. "1D","10","30"
  231. ]);
  232. }
  233. if (palette !== null){
  234. var el = CreatePaletteDOMEntry(name, palette);
  235. var eltitle = SetElPaletteName(el, name);
  236. Palettes.push([eltitle, palette, el]);
  237. if (Palettes.length <= 1 && !BlockEmits){
  238. el.click();
  239. }
  240. }
  241. }
  242. return this;
  243. }
  244. removePalette(name){
  245. for (let i=0; i < Palettes.length; i++){
  246. if (Palettes[i][0].value === name){
  247. if (CurrentPaletteIndex === i){
  248. CurrentPaletteIndex = 0;
  249. this.activatePalette(Palettes[0][0].value);
  250. }
  251. Palettes[i][2].parentNode.removeChild(Palettes[i][2]);
  252. Palettes.splice(i, 1);
  253. }
  254. }
  255. if (Palettes.length <= 0){
  256. this.createPalette("Palette");
  257. } else {
  258. Palettes[CurrentPaletteIndex][2].click();
  259. }
  260. return this;
  261. }
  262. renamePalette(oldname, newname){
  263. var i = this.paletteIndexFromName(oldname);
  264. if (i < 0)
  265. throw new ValueError("Failed to find palette named '" + oldname +"'. Cannot rename.");
  266. Palettes[i][0].value = newname;
  267. //SetElPaletteName(Palettes[i][2], newname);
  268. return this;
  269. }
  270. activatePalette(name){
  271. var i = this.paletteIndexFromName(name);
  272. if (i >= 0 && CurrentPaletteIndex !== i){
  273. Palettes[CurrentPaletteIndex][2].click();
  274. }
  275. return this;
  276. }
  277. clear(){
  278. for (let i=0; i < Palettes.length; i++){
  279. Palettes[i][2].parentNode.removeChild(Palettes[i][2]);
  280. }
  281. Palettes = [];
  282. CurrentPaletteIndex = 0;
  283. }
  284. }
  285. const instance = new CTRLPalettesStore();
  286. export default instance;