1 /** 2 Copyright: Copyright (c) 2013-2014 Andrey Penechko. 3 License: a$(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: Andrey Penechko. 5 */ 6 7 module anchovy.gui.skin.jsonguiskinparser; 8 9 import std.array; 10 import std.conv; 11 import std.json; 12 import std.stdio; 13 14 import anchovy.core.types; 15 16 import anchovy.gui; 17 18 19 //version = debug_parser; 20 21 class SkinParserException : Exception 22 { 23 this(string msg, string file = __FILE__, size_t line = __LINE__) 24 { 25 super(msg, file, line); 26 } 27 } 28 29 alias JSONValue[string] jsonObject; 30 alias JSONValue[] jsonArray; 31 32 static string[JSON_TYPE] jsonTypeNames; 33 static this() 34 { 35 jsonTypeNames = 36 [JSON_TYPE.STRING : "string", 37 JSON_TYPE.INTEGER : "int", 38 JSON_TYPE.UINTEGER : "uint", 39 JSON_TYPE.FLOAT : "float", 40 JSON_TYPE.OBJECT : "object", 41 JSON_TYPE.ARRAY : "array", 42 JSON_TYPE.TRUE : "true", 43 JSON_TYPE.FALSE : "false", 44 JSON_TYPE.NULL : "null",]; 45 } 46 47 static const string returnDefault = "if (value.type == JSON_TYPE.NULL) return defaultValue;"; 48 49 class JsonGuiSkinParser 50 { 51 string[] warnings; 52 string[] errors; 53 54 void warn(string message) 55 { 56 warnings ~= message; 57 } 58 59 GuiSkin parse(string skinData) 60 { 61 GuiSkin skin = new GuiSkin(); 62 63 try 64 { 65 auto jsonValue = parseJSON(skinData); 66 67 skin.name = getValue!string("name", jsonValue); 68 skin.textureFilename = getValue!string("image", jsonValue); 69 skin.fontInfos = parseFonts(getValue!jsonArray("fonts", jsonValue)); 70 71 foreach(i, ref value; getValue!jsonArray("styles", jsonValue)) 72 { 73 string styleName = getValue!string("name", value); 74 75 if (styleName == "") 76 { 77 warn("No style name was specified for style no " ~ to!string(i + 1) ~ " in skin " ~ skin.name); 78 continue; 79 } 80 81 skin.styles[styleName] = parseStyle(value, styleName); 82 } 83 84 } 85 catch(JSONException exception) 86 { 87 throw new SkinParserException(exception.msg); 88 } 89 90 if (!warnings.empty) 91 { 92 foreach(warning; warnings) 93 writeln("Warning: ", warning); 94 95 warnings = []; 96 } 97 98 return skin; 99 } 100 101 /** 102 * len Result 103 * 1 Поля будут установлены одновременно от каждого края элемента. 104 * 2 Первое значение устанавливает поля от верхнего и нижнего края, второе — от левого и правого. 105 * 3 Первое значение задает поле от верхнего края, второе — одновременно от левого и правого края, а третье — от нижнего края. 106 * 4 Поочередно устанавливается поля от верхнего, правого, нижнего и левого края. 107 */ 108 109 GuiStyle parseStyle(ref JSONValue value, string styleName) 110 { 111 expect(JSON_TYPE.OBJECT, value); 112 113 GuiStyle parsedStyle = new GuiStyle(); 114 GuiStyleState nullState; 115 116 parsedStyle.states["normal"] = parseStyleState(value, nullState); 117 parsedStyle.fontName = getValue!string("font", value, "normal"); 118 119 jsonArray states = getValue!jsonArray("states", value); 120 121 foreach(i, ref state; states) 122 { 123 string stateName = getValue!string("state", state); 124 125 if (stateName == "") 126 { 127 warn("No state name was specified for state no "~to!string(i+1)~" in style "~styleName); 128 continue; 129 } 130 131 version(debug_parser) writeln("stateName: ", stateName); 132 133 parsedStyle.states[stateName] = parseStyleState(state, parsedStyle.states["normal"]); 134 } 135 136 return parsedStyle; 137 } 138 139 GuiStyleState parseStyleState(ref JSONValue stateValue, ref GuiStyleState globalState) 140 { 141 GuiStyleState parsedStyleState; 142 expect(JSON_TYPE.OBJECT, stateValue); 143 144 jsonArray fixedBordersValue = getValue!jsonArray("fixedBorders", stateValue); 145 parsedStyleState.fixedBorders = RectOffset(parseRectOffset(fixedBordersValue, globalState.fixedBorders.arrayof)); 146 147 jsonArray contentPaddingValue = getValue!jsonArray("contentPadding", stateValue); 148 parsedStyleState.contentPadding = RectOffset(parseRectOffset(contentPaddingValue, globalState.contentPadding.arrayof)); 149 150 jsonArray rectValue = getValue!jsonArray("rect", stateValue); 151 parsedStyleState.atlasRect = Rect(parseRect(rectValue, globalState.atlasRect.arrayof)); 152 153 jsonArray outlineValue = getValue!jsonArray("outline", stateValue); 154 parsedStyleState.outline = RectOffset(parseRectOffset(outlineValue, globalState.outline.arrayof)); 155 156 jsonArray minSize = getValue!jsonArray("minSize", stateValue); 157 parsedStyleState.minSize = parseSize(minSize, ivec2(parsedStyleState.atlasRect.width, parsedStyleState.atlasRect.height)); 158 159 jsonArray maxSize = getValue!jsonArray("maxSize", stateValue); 160 parsedStyleState.maxSize = parseSize(maxSize, ivec2(0, 0)); 161 162 return parsedStyleState; 163 } 164 165 FontInfo[] parseFonts(jsonArray inArray) 166 { 167 FontInfo[] outFonts; 168 169 foreach(font; inArray) 170 { 171 outFonts ~= FontInfo(getValue!string("name", font), 172 getValue!string("file", font), 173 getValue!uint("size", font), 174 getValue!int("verticalOffset", font)); 175 } 176 177 version(debug_parser) writeln(outFonts); 178 179 return outFonts; 180 } 181 182 private: 183 184 int[4] parseRect(jsonArray inArray, int[4] defaultValue = [0, 0, 0, 0]) 185 { 186 if (inArray.length == 0) return defaultValue; 187 188 int[4] outArray; 189 190 foreach(i, ref element; inArray) 191 { 192 if (i > 3) 193 { 194 warn("Rect with more than 4 values found"); 195 break; 196 } 197 198 outArray[i] = getValue!int(element); 199 } 200 201 return outArray; 202 } 203 204 ivec2 parsePoint(jsonArray inArray, ivec2 defaultValue = ivec2(0, 0)) 205 { 206 if (inArray.length == 0) 207 return defaultValue; 208 209 if (inArray.length == 1) 210 return ivec2(getValue!int(inArray[0]), 0); 211 212 return ivec2(getValue!int(inArray[0]), getValue!int(inArray[1])); 213 } 214 215 ivec2 parseSize(jsonArray inArray, ivec2 defaultValue = ivec2(0, 0)) 216 { 217 if (inArray.length == 0) 218 return defaultValue; 219 220 if (inArray.length == 1) 221 { 222 uint size = getValue!uint(inArray[0]); 223 224 return ivec2(size, size); 225 } 226 227 return ivec2(getValue!uint(inArray[0]), getValue!uint(inArray[1])); 228 } 229 230 int[4] parseRectOffset(jsonArray inArray, int[4] defaultValue = [0, 0, 0, 0]) 231 { 232 if (inArray.length == 0) 233 { 234 return defaultValue; 235 } 236 else if (inArray.length == 1) 237 { 238 int val = getValue!int(inArray[0]); 239 240 return [val, val, val, val]; 241 } 242 else if (inArray.length == 2) 243 { 244 int vert = getValue!int(inArray[0]); 245 int hor = getValue!int(inArray[1]); 246 247 return [hor, hor, vert, vert]; 248 } 249 else if (inArray.length == 3) 250 { 251 int top = getValue!int(inArray[0]); 252 int hor = getValue!int(inArray[1]); 253 int bottom = getValue!int(inArray[2]); 254 255 return [hor, hor, top, bottom]; 256 } 257 else 258 { 259 int top = getValue!int(inArray[0]); 260 int right = getValue!int(inArray[1]); 261 int bottom = getValue!int(inArray[2]); 262 int left = getValue!int(inArray[3]); 263 264 if (inArray.length > 4) warn("Rect with more than 4 values found"); 265 266 return [left, right, top, bottom]; 267 } 268 } 269 270 void expect(JSON_TYPE type, ref JSONValue value, string file = __FILE__, size_t line = __LINE__) 271 { 272 if(value.type != type && value.type != JSON_TYPE.NULL) 273 { 274 throw new SkinParserException("Wrong JSON type. " ~ 275 jsonTypeNames[value.type] ~ 276 " found while " ~ 277 jsonTypeNames[type] ~ 278 " expected", file, line); 279 } 280 } 281 282 T getValue(T)(string key, ref JSONValue objectValue, T defaultValue = T.init) 283 { 284 JSONValue* targetValue = key in objectValue.object; 285 286 if (targetValue is null) 287 { 288 return defaultValue; 289 } 290 else 291 { 292 return getValue!T(*targetValue, defaultValue); 293 } 294 } 295 296 T getValue(T)(ref JSONValue value, T defaultValue = T.init) 297 { 298 static if (is(T : string)) 299 { 300 expect(JSON_TYPE.STRING, value); 301 mixin(returnDefault); 302 return value.str; 303 } 304 else static if (is(T : int)) 305 { 306 expect(JSON_TYPE.INTEGER, value); 307 mixin(returnDefault); 308 return cast(int)value.integer; 309 } 310 else static if (is(T : uint)) 311 { 312 expect(JSON_TYPE.UINTEGER, value); 313 mixin(returnDefault); 314 return cast(uint)value.uinteger; 315 } 316 else static if(is(T : float)) 317 { 318 expect(JSON_TYPE.FLOAT, value); 319 mixin(returnDefault); 320 return cast(float)value.floating; 321 } 322 else static if(is(T : jsonObject)) 323 { 324 expect(JSON_TYPE.OBJECT, value); 325 mixin(returnDefault); 326 return value.object; 327 } 328 else static if(is(T : jsonArray)) 329 { 330 expect(JSON_TYPE.ARRAY, value); 331 mixin(returnDefault); 332 return value.array; 333 } 334 else static if(is(T : bool)) 335 { 336 if(value.type == JSON_TYPE.TRUE) 337 return true; 338 else if (value.type == JSON_TYPE.FALSE) 339 return false; 340 else if (value.type == JSON_TYPE.NULL) 341 return defaultValue; 342 else throw new SkinParserException("Wrong JSON type. " ~ 343 jsonTypeNames[value.type] ~ 344 " found while boolean expected"); 345 } 346 } 347 }