1 module windows.addtiles; 2 3 import PixelPerfectEngine.concrete.window; 4 import PixelPerfectEngine.concrete.dialogs.filedialog; 5 import dimage; 6 7 import editor; 8 9 import std.conv : to; 10 11 public class AddTiles : Window { 12 public enum Mode { 13 Regular, 14 RestrictTo4Bit, 15 RestrictTo8Bit, 16 RestrictTo16Bit, 17 RestrictTo32Bit, 18 } 19 Label label0; 20 Button button_Source; 21 TextBox textBox_Source; 22 Label label1; 23 TextBox textBox_palShift; 24 CheckBox checkBox_palImport; 25 CheckBox checkBox_embDat; 26 Label label2; 27 TextBox textBox_palOffset; 28 Button button_Ok; 29 Label label3; 30 TextBox textBox_name; 31 Label label4; 32 TextBox textBox_numStyle; 33 Editor editor; 34 Image source; 35 int sizeX, sizeY; 36 public this(Editor editor, int sizeX, int sizeY, Mode mode = Mode.Regular) { 37 super(Coordinate(0, 0, 165, 230), "Add Tiles"d); 38 label0 = new Label("Source:"d, "label0", Coordinate(5, 21, 88, 40)); 39 button_Source = new Button("Browse"d, "button_Source", Coordinate(90, 20, 160, 40)); 40 textBox_Source = new TextBox(""d, "textBox_Source", Coordinate(5, 42, 160, 62)); 41 label1 = new Label("paletteShift:"d, "label1", Coordinate(5, 64, 88, 82)); 42 textBox_palShift = new TextBox("8"d, "textBox_palShift", Coordinate(100, 64, 160, 84)); 43 checkBox_palImport = new CheckBox("Import palette"d, "CheckBox0", Coordinate(5, 156, 160, 172)); 44 checkBox_embDat = new CheckBox("Use embedded mat data"d, "CheckBox1", Coordinate(5, 173, 160, 189)); 45 label2 = new Label("paletteOffset:"d, "label2", Coordinate(5, 88, 99, 105)); 46 textBox_palOffset = new TextBox("0"d, "textBox_palOffset", Coordinate(100, 86, 160, 106)); 47 button_Ok = new Button("Ok"d, "button_Ok", Coordinate(91, 206, 161, 226)); 48 label3 = new Label("name:"d, "label3", Coordinate(5, 110, 79, 126)); 49 textBox_name = new TextBox(""d, "textBox_name", Coordinate(45, 108, 160, 128)); 50 label4 = new Label("Num style and from:"d, "label4", Coordinate(5, 130, 120, 150)); 51 textBox_numStyle = new TextBox("h0000"d, "textBox_numStyle", Coordinate(120, 130, 160, 150)); 52 this.sizeX = sizeX; 53 this.sizeY = sizeY; 54 addElement(label0); 55 addElement(button_Source); 56 button_Source.onMouseLClick = &button_Source_onClick; 57 addElement(textBox_Source); 58 textBox_Source.onTextInput = &textBox_Source_onTextInput; 59 addElement(label1); 60 addElement(textBox_palShift); 61 addElement(checkBox_palImport); 62 addElement(checkBox_embDat); 63 addElement(label2); 64 addElement(textBox_palOffset); 65 addElement(button_Ok); 66 button_Ok.onMouseLClick = &button_Ok_onClick; 67 addElement(label3); 68 addElement(textBox_name); 69 addElement(label4); 70 addElement(textBox_numStyle); 71 this.editor = editor; 72 } 73 private void button_Source_onClick(Event e) { 74 handler.addWindow(new FileDialog("Import Tile Source"d, "fileDialog_TSBrowse", &fileDialog_TSBrowse_event, 75 [FileDialog.FileAssociationDescriptor("All supported formats", ["*.tga", "*.png", "*.bmp"]), 76 /+FileDialog.FileAssociationDescriptor("PPE Extendible Map file", ["*.xmf"]),+/ 77 FileDialog.FileAssociationDescriptor("Targa Graphics File", ["*.tga"]), 78 FileDialog.FileAssociationDescriptor("Windows Bitmap File", ["*.bmp"]), 79 FileDialog.FileAssociationDescriptor("Portable Network Graphics File", ["*.png"]),], "./")); 80 } 81 private void textBox_palShift_onTextInput(Event e) { 82 //validate input type 83 import PixelPerfectEngine.system.etc : isInteger; 84 if (!isInteger(textBox_palShift.getText.text)) { 85 handler.message("Bad format!"d, "Inputted value must be integer!"d); 86 } 87 const int value = to!int(textBox_palShift.getText.text); 88 if (value < 1 || value > 8) { 89 handler.message("Bad value!"d, "Inputted value must be between 1 and 8!"d); 90 } 91 } 92 private void fileDialog_TSBrowse_event(Event ev) { 93 FileEvent e = cast(FileEvent)ev; 94 if (!loadFile(e.getFullPath)) { 95 textBox_Source.setText(to!dstring(e.getFullPath)); 96 } 97 } 98 private void textBox_Source_onTextInput(Event e) { 99 if (loadFile(to!string(textBox_Source.getText.text))) { 100 textBox_Source.setText(""d); 101 } 102 103 } 104 private int loadFile(string path) { 105 import std.path : extension; 106 import std.stdio : File; 107 import PixelPerfectEngine.system.etc : nextPow2; 108 File f = File(path); 109 try { 110 switch (path.extension) { 111 case ".xmp"://TO DO: enable importing material data from other map files 112 break; 113 case ".png": 114 source = PNG.load(f); 115 break; 116 case ".tga": 117 source = TGA.load!(File, true, true)(f); 118 break; 119 case ".bmp": 120 source = BMP.load(f); 121 break; 122 default: 123 handler.message("Unsupported file format!"d, "The specified file format is not supported!"d); 124 return -1; 125 } 126 } catch (Exception ex) { 127 import std.conv : to; 128 handler.message("Error!"d, to!dstring(ex.msg)); 129 return -1; 130 } 131 if (source.width % sizeX || source.height % sizeY) { 132 handler.message("Tile size Mismatch!"d, "Supplied bitmap file is unsuitable for this layer as a tile source!"d); 133 source = null; 134 return -1; 135 } 136 if(source.isIndexed) { 137 checkBox_palImport.check(); 138 const int paletteLengthPOw2 = cast(int)nextPow2(source.palette.length); 139 switch (paletteLengthPOw2) { 140 case 2: 141 textBox_palShift.setText("1"); 142 break; 143 case 4: 144 textBox_palShift.setText("2"); 145 break; 146 case 8: 147 textBox_palShift.setText("3"); 148 break; 149 case 16: 150 textBox_palShift.setText("4"); 151 break; 152 case 32: 153 textBox_palShift.setText("5"); 154 break; 155 case 64: 156 textBox_palShift.setText("6"); 157 break; 158 case 128: 159 textBox_palShift.setText("7"); 160 break; 161 case 256: 162 textBox_palShift.setText("8"); 163 break; 164 default: break; 165 } 166 } 167 return 0; 168 } 169 private void button_Ok_onClick(Event e) { 170 import PixelPerfectEngine.system.etc : parseHex, parseOct, parseDec; 171 import editorevents : AddTileSheetEvent; 172 //detect numbering style 173 uint numStyle0; 174 int numFrom; 175 dstring numStyle = textBox_numStyle.getText.text; 176 if (numStyle.length) { 177 if (numStyle[0] == 'h' || numStyle[$-1] == 'h') { 178 numStyle0 = 1; 179 numStyle0 |= cast(uint)((numStyle.length - 1) << 8); 180 } 181 else if (numStyle[0] == 'o' || numStyle[$-1] == 'o') { 182 numStyle0 = 2; 183 numStyle0 |= cast(uint)((numStyle.length - 1) << 8); 184 } 185 else if (numStyle.length >= 2) { 186 if (numStyle[0..2] == "0x") { 187 numStyle0 = 1; 188 numStyle0 |= cast(uint)((numStyle.length - 2) << 8); 189 } 190 else if (numStyle[0..2] == "0o") { 191 numStyle0 = 2; 192 numStyle0 |= cast(uint)((numStyle.length - 2) << 8); 193 } 194 } else numStyle0 = cast(uint)(numStyle.length << 8); 195 switch (numStyle0) { 196 case 1: numFrom = parseHex(numStyle); break; 197 case 2: numFrom = parseOct(numStyle); break; 198 default: numFrom = parseDec(numStyle); break; 199 } 200 string name0 = to!string(textBox_name.getText.text); 201 string[3] name; 202 if (name0.length) { 203 for (size_t i ; i < name0.length ; i++) { 204 if (name0[i] == '#') { 205 name[0] = name0[0..i]; 206 if (i + 2 < name0.length) { 207 name[1] = name0[i+2..$]; 208 } 209 } 210 } 211 } 212 if(!name[0].length) name[0] = name0; 213 name[2] = to!string(textBox_Source.getText.text); 214 const int paletteShift = checkBox_palImport.isChecked ? to!int(textBox_palShift.getText.text) : -1; 215 const int paletteOffset = to!int(textBox_palOffset.getText.text); 216 editor.selDoc.events.addToTop(new AddTileSheetEvent(source, editor.selDoc, editor.selDoc.selectedLayer, paletteOffset, 217 paletteShift, name, numFrom, numStyle0)); 218 editor.selDoc.updateMaterialList; 219 this.close; 220 } else { 221 handler.message("Error!"d, "Numbering style must be specified in this case!"); 222 } 223 } 224 }