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个字符

337 行
8.2KB

  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 currentPalette(){
  165. return (Palettes.length > 0) ? Palettes[CurrentPaletteIndex][1] : null;
  166. }
  167. get currentPaletteName(){
  168. return (Palettes.length > 0) ? Palettes[CurrentPaletteIndex][0].value : "";
  169. }
  170. get obj(){
  171. var d = [];
  172. for (let i=0; i < Palettes.length; i++){
  173. d.push({name:Palettes[i][0].value, palette:Palettes[i][1].obj});
  174. }
  175. return d;
  176. }
  177. set obj(d){
  178. var validator = JSONSchema.getValidator(SCHEMA_ID);
  179. if (validator !== null && validator(d)){
  180. this.clear();
  181. for (let i=0; i < d.length; i++){
  182. this.createPalette(d[i].name, JSON.stringify(d[i].palette));
  183. }
  184. } else {
  185. throw new Error("Object failed to validate against PalettesStoreSchema.");
  186. }
  187. }
  188. get json(){
  189. return JSON.stringify(this.obj);
  190. }
  191. set json(j){
  192. try {
  193. this.obj = JSON.parse(j);
  194. } catch (e) {
  195. throw e;
  196. }
  197. }
  198. initialize(){
  199. if (Palettes.length <= 0)
  200. this.createPalette("Palette");
  201. return this;
  202. }
  203. paletteIndexFromName(name){
  204. for (let i=1; i < Palettes.length; i++){
  205. if (Palettes[i][0].value == name){
  206. return i;
  207. }
  208. }
  209. return -1;
  210. }
  211. getPalette(name){
  212. var i = this.paletteIndexFromName(name);
  213. return (i >= 0) ? Palettes[i][1] : null;
  214. }
  215. createPalette(name, pjson){
  216. var palette = this.getPalette(name);
  217. if (palette === null){
  218. palette = new NESPalette();
  219. if (typeof(pjson) === "string"){
  220. try {
  221. palette.json = pjson;
  222. } catch (e) {
  223. console.log("Failed to create palette.", e.toString());
  224. palette = null;
  225. }
  226. } else {
  227. palette.set_palette([
  228. "0F",
  229. "05","06","07",
  230. "09","0A","0B",
  231. "01","02","03",
  232. "0D","00","20",
  233. "15","16","17",
  234. "19","1A","1B",
  235. "11","21","31",
  236. "1D","10","30"
  237. ]);
  238. }
  239. if (palette !== null){
  240. var el = CreatePaletteDOMEntry(name, palette);
  241. var eltitle = SetElPaletteName(el, name);
  242. Palettes.push([eltitle, palette, el]);
  243. if (Palettes.length <= 1 && !BlockEmits){
  244. el.click();
  245. }
  246. }
  247. }
  248. return this;
  249. }
  250. removePalette(name){
  251. for (let i=0; i < Palettes.length; i++){
  252. if (Palettes[i][0].value === name){
  253. if (CurrentPaletteIndex === i){
  254. CurrentPaletteIndex = 0;
  255. this.activatePalette(Palettes[0][0].value);
  256. }
  257. Palettes[i][2].parentNode.removeChild(Palettes[i][2]);
  258. Palettes.splice(i, 1);
  259. }
  260. }
  261. if (Palettes.length <= 0){
  262. this.createPalette("Palette");
  263. } else {
  264. Palettes[CurrentPaletteIndex][2].click();
  265. }
  266. return this;
  267. }
  268. renamePalette(oldname, newname){
  269. var i = this.paletteIndexFromName(oldname);
  270. if (i < 0)
  271. throw new ValueError("Failed to find palette named '" + oldname +"'. Cannot rename.");
  272. Palettes[i][0].value = newname;
  273. //SetElPaletteName(Palettes[i][2], newname);
  274. return this;
  275. }
  276. activatePalette(name){
  277. var i = this.paletteIndexFromName(name);
  278. if (i >= 0 && CurrentPaletteIndex !== i){
  279. Palettes[CurrentPaletteIndex][2].click();
  280. }
  281. return this;
  282. }
  283. clear(){
  284. for (let i=0; i < Palettes.length; i++){
  285. Palettes[i][2].parentNode.removeChild(Palettes[i][2]);
  286. }
  287. Palettes = [];
  288. CurrentPaletteIndex = 0;
  289. }
  290. }
  291. const instance = new CTRLPalettesStore();
  292. export default instance;