editor.cpp (10273B)
1 #include "editor.h" 2 3 #include "uiObject.h" 4 5 #include "baseLib/filesystem.h" 6 7 namespace genericUI 8 { 9 Editor::Editor(EditorInterface& _interface) : m_interface(_interface) 10 { 11 } 12 13 void Editor::create(const std::string& _jsonFilename) 14 { 15 uint32_t jsonSize; 16 const auto jsonData = m_interface.getResourceByFilename(_jsonFilename, jsonSize); 17 18 juce::var json; 19 const auto error = juce::JSON::parse(juce::String(std::string(jsonData, jsonSize)), json); 20 21 if (error.failed()) 22 throw std::runtime_error("Failed to load json, " + error.getErrorMessage().toStdString()); 23 24 m_jsonFilename = _jsonFilename; 25 26 m_rootObject.reset(new UiObject(nullptr, json)); 27 28 std::set<std::string> textures; 29 m_rootObject->collectVariants(textures, "texture"); 30 31 for (const auto& texture : textures) 32 { 33 const auto dataName = texture + ".png"; 34 35 uint32_t dataSize; 36 const auto* data = m_interface.getResourceByFilename(dataName, dataSize); 37 if (!data) 38 throw std::runtime_error("Failed to find image named " + dataName); 39 auto drawable = juce::Drawable::createFromImageData(data, dataSize); 40 #ifdef _DEBUG 41 drawable->setName(dataName); 42 #endif 43 m_drawables.insert(std::make_pair(texture, std::move(drawable))); 44 } 45 46 std::set<std::string> fonts; 47 m_rootObject->collectVariants(fonts, "fontFile"); 48 49 for(const auto& fontFile : fonts) 50 { 51 const auto dataName = fontFile + ".ttf"; 52 53 uint32_t dataSize; 54 const auto* data = m_interface.getResourceByFilename(dataName, dataSize); 55 if (!data) 56 throw std::runtime_error("Failed to find font named " + dataName); 57 auto font = juce::Font(juce::Typeface::createSystemTypefaceFor(data, dataSize)); 58 m_fonts.insert(std::make_pair(fontFile, std::move(font))); 59 } 60 61 m_rootObject->createJuceTree(*this); 62 m_rootObject->createTabGroups(*this); 63 m_rootObject->createControllerLinks(*this); 64 m_rootObject->registerTemplates(*this); 65 66 m_scale = m_rootObject->getPropertyFloat("scale", 1.0f); 67 } 68 69 std::string Editor::exportToFolder(const std::string& _folder) const 70 { 71 if(!m_rootObject) 72 return "Nothing to export"; 73 74 baseLib::filesystem::createDirectory(_folder); 75 76 std::string subfolder = m_jsonFilename; 77 const auto dotIndex = m_jsonFilename.rfind('.'); 78 if(dotIndex != std::string::npos) 79 subfolder = subfolder.substr(0, dotIndex); 80 const auto underscoreIndex = m_jsonFilename.find('_'); 81 if(underscoreIndex != std::string::npos) 82 subfolder = subfolder.substr(underscoreIndex+1); 83 84 const auto folder = _folder + subfolder + '/'; 85 86 baseLib::filesystem::createDirectory(folder); 87 88 std::stringstream errors; 89 90 auto writeFile = [this, &folder, &errors](const std::string& _name) 91 { 92 uint32_t dataSize; 93 const auto data = m_interface.getResourceByFilename(_name, dataSize); 94 95 FILE* hFile = fopen((folder + _name).c_str(), "wb"); 96 97 if(!hFile) 98 { 99 errors << "Failed to create file " << folder << _name << '\n'; 100 } 101 else 102 { 103 const auto writtenCount = fwrite(data, 1, dataSize, hFile); 104 (void)fclose(hFile); 105 if(writtenCount != dataSize) 106 errors << "Failed to write " << dataSize << " bytes to " << folder << _name << '\n'; 107 } 108 }; 109 110 auto writeData = [this, writeFile](const std::set<std::string>& _names, const std::string& _ext) 111 { 112 for (const auto& name : _names) 113 { 114 const auto dataName = name + _ext; 115 writeFile(dataName); 116 } 117 }; 118 119 std::set<std::string> textures; 120 std::set<std::string> fonts; 121 122 m_rootObject->collectVariants(textures, "texture"); 123 m_rootObject->collectVariants(fonts, "fontFile"); 124 125 writeFile(m_jsonFilename); 126 127 writeData(textures, ".png"); 128 writeData(fonts, ".ttf"); 129 130 return errors.str(); 131 } 132 133 juce::Drawable* Editor::getImageDrawable(const std::string& _texture) 134 { 135 const auto it = m_drawables.find(_texture); 136 return it == m_drawables.end() ? nullptr : it->second.get(); 137 } 138 139 std::unique_ptr<juce::Drawable> Editor::createImageDrawable(const std::string& _texture) 140 { 141 const auto existing = getImageDrawable(_texture); 142 return existing ? existing->createCopy() : nullptr; 143 } 144 145 const juce::Font& Editor::getFont(const std::string& _fontFile) 146 { 147 const auto it = m_fonts.find(_fontFile); 148 if(it == m_fonts.end()) 149 throw std::runtime_error("Unable to find font named " + _fontFile); 150 return it->second; 151 } 152 153 void Editor::registerComponent(const std::string& _name, juce::Component* _component) 154 { 155 const auto itExisting = m_componentsByName.find(_name); 156 157 if(itExisting != m_componentsByName.end()) 158 { 159 itExisting->second.push_back(_component); 160 } 161 else 162 { 163 m_componentsByName.insert(std::make_pair(_name, std::vector{_component})); 164 } 165 166 const auto param = _component->getProperties()["parametername"].toString().toStdString(); 167 if(param.empty()) 168 return; 169 170 const auto itParamExisting = m_componentsByParameter.find(param); 171 172 if(itParamExisting != m_componentsByParameter.end()) 173 { 174 itParamExisting->second.push_back(_component); 175 } 176 else 177 { 178 m_componentsByParameter.insert(std::make_pair(param, std::vector{_component})); 179 } 180 } 181 182 void Editor::registerTabGroup(TabGroup* _group) 183 { 184 const auto& n = _group->getName(); 185 186 if(m_tabGroupsByName.find(n) != m_tabGroupsByName.end()) 187 throw std::runtime_error("tab group " + n + " is already defined"); 188 189 m_tabGroupsByName.insert(std::make_pair(n, _group)); 190 } 191 192 const std::vector<juce::Component*>& Editor::findComponents(const std::map<std::string, std::vector<juce::Component*>>& _components, const std::string& _name, const uint32_t _expectedCount, const std::string& _typename) 193 { 194 const auto it = _components.find(_name); 195 if(it != _components.end()) 196 { 197 if(_expectedCount && it->second.size() != _expectedCount) 198 { 199 std::stringstream ss; 200 ss << "Expected to find " << _expectedCount << " components with " << _typename << ' ' << _name << " but found " << it->second.size(); 201 throw std::runtime_error(ss.str()); 202 } 203 return it->second; 204 } 205 206 if(_expectedCount) 207 { 208 std::stringstream ss; 209 ss << "Unable to find component with " << _typename << ' ' << _name << ", expected to find " << _expectedCount << " components"; 210 throw std::runtime_error(ss.str()); 211 } 212 213 static std::vector<juce::Component*> empty; 214 return empty; 215 } 216 217 const std::vector<juce::Component*>& Editor::findComponents(const std::string& _name, const uint32_t _expectedCount/* = 0*/) const 218 { 219 return findComponents(m_componentsByName, _name, _expectedCount, "name"); 220 } 221 222 const std::vector<juce::Component*>& Editor::findComponentsByParam(const std::string& _name, const uint32_t _expectedCount) const 223 { 224 return findComponents(m_componentsByParameter, _name, _expectedCount, "parameter"); 225 } 226 227 juce::Component* Editor::findComponent(const std::string& _name, const bool _mustExist/* = true*/) const 228 { 229 const auto comps = findComponents(_name); 230 if(comps.size() > 1) 231 throw std::runtime_error("Failed to find unique component named " + _name + ", found more than one object with that name"); 232 if(_mustExist && comps.empty()) 233 throw std::runtime_error("Failed to find component named " + _name); 234 return comps.empty() ? nullptr : comps.front(); 235 } 236 237 juce::Component* Editor::findComponentByParam(const std::string& _param, const bool _mustExist) const 238 { 239 const auto comps = findComponentsByParam(_param); 240 if(comps.size() > 1) 241 throw std::runtime_error("Failed to find unique component with parameter " + _param + ", found more than one object with that parameter"); 242 if(_mustExist && comps.empty()) 243 throw std::runtime_error("Failed to find component with parameter " + _param); 244 return comps.empty() ? nullptr : comps.front(); 245 } 246 247 size_t Editor::getConditionCountRecursive() const 248 { 249 return m_rootObject ? m_rootObject->getConditionCountRecursive() : 0; 250 } 251 252 size_t Editor::getControllerLinkCountRecursive() const 253 { 254 return m_rootObject ? m_rootObject->getControllerLinkCountRecursive() : 0; 255 } 256 257 void Editor::registerTemplate(const std::shared_ptr<UiObject>& _value) 258 { 259 const auto name = _value->getName(); 260 261 if (name.empty()) 262 throw std::runtime_error("Every template needs to have a name"); 263 264 if (m_templates.find(name) != m_templates.end()) 265 throw std::runtime_error("Template with name '" + name + "' exists more than once"); 266 267 m_templates.insert({ name, _value }); 268 } 269 270 void Editor::setEnabled(juce::Component& _component, const bool _enable) 271 { 272 if(_component.getProperties().contains("disabledAlpha")) 273 { 274 const float a = _component.getProperties()["disabledAlpha"]; 275 276 _component.setAlpha(_enable ? 1.0f : a); 277 _component.setEnabled(_enable); 278 } 279 else 280 { 281 _component.setVisible(_enable); 282 } 283 } 284 285 void Editor::setCurrentPart(const uint8_t _part) 286 { 287 m_rootObject->setCurrentPart(*this, _part); 288 } 289 290 void Editor::updateKeyValueConditions(const std::string& _key, const std::string& _value) const 291 { 292 m_rootObject->updateKeyValueConditions(_key, _value); 293 } 294 295 std::shared_ptr<UiObject> Editor::getTemplate(const std::string& _name) const 296 { 297 const auto& it = m_templates.find(_name); 298 if (it == m_templates.end()) 299 return {}; 300 return it->second; 301 } 302 303 bool Editor::resizeDrawableImage(juce::DrawableImage& _drawable, const uint32_t _percent) 304 { 305 if(_percent == 100) 306 return true; 307 if(_percent < 1) 308 return false; 309 310 auto image = _drawable.getImage(); 311 const auto x = image.getBounds().getX(); 312 const auto y = image.getBounds().getY(); 313 const auto w = image.getWidth(); 314 const auto h = image.getHeight(); 315 316 const int percent = static_cast<int>(_percent); 317 318 image = image.rescaled(w * percent / 100, h * percent / 100); 319 320 _drawable.setImage(image); 321 auto b = image.getBounds(); 322 b.setSize(w, h); 323 b.setPosition(x, y); 324 _drawable.setBounds(b); 325 _drawable.setBoundingBox(b.toFloat()); 326 return true; 327 } 328 329 void Editor::paint(juce::Graphics& g) 330 { 331 #if JUCE_MAJOR_VERSION >= 8 && defined(_WIN32) 332 // This makes a huge difference on Windows, especially when the whole window is downscaled 333 g.getInternalContext().setInterpolationQuality(juce::Graphics::highResamplingQuality); 334 #endif 335 Component::paint(g); 336 } 337 338 bool Editor::selectTabWithComponent(const juce::Component* _component) const 339 { 340 for (const auto& it : m_tabGroupsByName) 341 { 342 auto* tabGroup = it.second; 343 if (tabGroup->selectTabWithComponent(_component)) 344 return true; 345 } 346 return false; 347 } 348 }