1 module rpui.resources.icons;
2 
3 import std.path;
4 
5 import rpui.math;
6 import gapi.texture;
7 
8 import rpui.resources.images;
9 
10 import rpdl.tree;
11 import rpui.math;
12 import rpui.paths;
13 
14 struct Icon {
15     string group;
16     string name;
17     Texture2DCoords texCoord;
18 }
19 
20 struct IconsConfig {
21     string name;
22     string textureFileName;
23     bool themed;  /// If themed, then icon texture must placed in theme resources folder.
24     vec2 size;  /// Size of one icon.
25     vec2 start;  /// Start offset of all icons.
26     vec2 gaps;  /// Spacing beetwen icons.
27 }
28 
29 /**
30  * This class uses RPDL files for icons repository, for each icons have group
31  * where declared default config such as size of icon, gaps etc.
32  */
33 final class IconsRes {
34     const Paths paths;
35 
36     /**
37      * Create icons resources, this constructor get `imagesRes` as argument
38      * for getting textures for icons.
39      */
40     this(ImagesRes imagesRes) {
41         assert(imagesRes !is null);
42         this.imagesRes = imagesRes;
43         this.paths = createPathes();
44     }
45 
46     /// Get texture instance for particular group icons.
47     Texture2D getTextureForIcons(in string group) {
48         const config = iconsConfig[group];
49 
50         if (config.themed) {
51             return this.imagesRes.getTextureForUiTheme(buildPath("icons", config.textureFileName));
52         } else {
53             return this.imagesRes.getTexture(buildPath("icons", config.textureFileName));
54         }
55     }
56 
57     /// Get texture instance for particular icon.
58     Texture2D getTextureForIcons(in Icon icon) {
59         return getTextureForIcons(icon.group);
60     }
61 
62     /// Retrieve icon information from group by icon name.
63     Icon getIcon(in string group, in string name) {
64         auto texCoord = Texture2DCoords();
65 
66         const config = iconsConfig[group];
67 
68         // Minus vec2(1, 1) due to icons indexing starting from 1
69         const iconIndexes = iconsData[group].data.getVec2f("Icons." ~ name) - vec2(1, 1);
70         const offsetWithGaps = vec2(
71             iconIndexes.x * (config.size.x + config.gaps.x),
72             iconIndexes.y * (config.size.y + config.gaps.y)
73         );
74 
75         texCoord.offset = config.start + offsetWithGaps;
76         texCoord.size = config.size;
77         texCoord = normilizeTexture2DCoords(texCoord, getTextureForIcons(group));
78 
79         return Icon(group, name, texCoord);
80     }
81 
82     /// Retrieve icon config from group.
83     IconsConfig getIconsConfig(in string group) {
84         assert(group in iconsConfig, "Unknown icons group '" ~ group ~ "'");
85         return iconsConfig[group];
86     }
87 
88     /**
89      * Add new icons group, file with icons declarations must placed in
90      * res/ui/icons folder.
91      */
92     void addIcons(in string group, in string fileName) {
93         const path = buildPath(paths.resources, "ui", "icons");
94         iconsData[group] = new RpdlTree(path);
95         iconsData[group].load(fileName);
96         iconsConfig[group] = IconsConfig();
97 
98         with (iconsConfig[group]) {
99             auto groupData = iconsData[group].data;
100 
101             name = group;
102             textureFileName = groupData.getString("Config.icons.0");
103             themed = groupData.optBoolean("Config.themed.0", false);
104             size = groupData.getVec2f("Config.size");
105             start = groupData.getVec2f("Config.start");
106             gaps = groupData.getVec2f("Config.gaps");
107         }
108     }
109 
110 private:
111     ImagesRes imagesRes;
112     RpdlTree[string] iconsData;
113     IconsConfig[string] iconsConfig;
114 }