23#include <nlohmann/json.hpp>
37using json = nlohmann::json;
63 LOG_INFO(
"Config file not found, using defaults");
67 std::ifstream file(m_impl->configPath);
68 if (!file.is_open()) {
69 LOG_ERROR(
"Failed to open config file: {}", m_impl->configPath.string());
73 m_impl->config = json::parse(file);
74 LOG_INFO(
"Configuration loaded from: {}", m_impl->configPath.string());
76 }
catch (
const json::exception& e) {
77 LOG_ERROR(
"Failed to parse config file: {}", e.what());
80 }
catch (
const std::exception& e) {
81 LOG_ERROR(
"Failed to load config: {}", e.what());
88 if (!EnsureConfigDirectoryExists()) {
89 LOG_ERROR(
"Failed to create config directory");
93 std::ofstream file(m_impl->configPath);
94 if (!file.is_open()) {
95 LOG_ERROR(
"Failed to open config file for writing: {}", m_impl->configPath.string());
99 file << m_impl->config.dump(2);
100 LOG_INFO(
"Configuration saved to: {}", m_impl->configPath.string());
102 }
catch (
const std::exception& e) {
103 LOG_ERROR(
"Failed to save config: {}", e.what());
109 m_impl->config = json::object();
114 m_impl->config[
"window"][
"maximized"] =
false;
116 m_impl->config[
"recentFiles"] = json::array();
117 m_impl->config[
"settings"] = json::object();
121 return std::filesystem::exists(m_impl->configPath);
125 return GetConfigDirectory() /
"config.json";
131 m_impl->config[
"window"][
"x"] = x;
132 m_impl->config[
"window"][
"y"] = y;
136 m_impl->config[
"window"][
"width"] = width;
137 m_impl->config[
"window"][
"height"] = height;
142 if (m_impl->config.contains(
"window") && m_impl->config[
"window"].contains(
"x") &&
143 m_impl->config[
"window"].contains(
"y")) {
144 return std::make_pair(m_impl->config[
"window"][
"x"].get<
int>(), m_impl->config[
"window"][
"y"].get<
int>());
146 }
catch (
const json::exception& e) {
147 LOG_WARNING(
"Failed to get window position from config: {}", e.what());
154 if (m_impl->config.contains(
"window") && m_impl->config[
"window"].contains(
"width") &&
155 m_impl->config[
"window"].contains(
"height")) {
156 return std::make_pair(m_impl->config[
"window"][
"width"].get<
int>(),
157 m_impl->config[
"window"][
"height"].get<
int>());
159 }
catch (
const json::exception& e) {
160 LOG_WARNING(
"Failed to get window size from config: {}", e.what());
166 m_impl->config[
"window"][
"maximized"] = maximized;
171 if (m_impl->config.contains(
"window") && m_impl->config[
"window"].contains(
"maximized")) {
172 return m_impl->config[
"window"][
"maximized"].get<
bool>();
174 }
catch (
const json::exception& e) {
175 LOG_WARNING(
"Failed to get window maximized state from config: {}", e.what());
183 m_impl->config[
"theme"] = theme;
188 if (m_impl->config.contains(
"theme")) {
189 return m_impl->config[
"theme"].get<std::string>();
191 }
catch (
const json::exception& e) {
192 LOG_WARNING(
"Failed to get theme from config: {}", e.what());
200 if (!m_impl->config.contains(
"recentFiles")) {
201 m_impl->config[
"recentFiles"] = json::array();
204 auto& recentFiles = m_impl->config[
"recentFiles"];
207 for (
auto it = recentFiles.begin(); it != recentFiles.end(); ++it) {
208 if (*it == filepath) {
209 recentFiles.erase(it);
215 recentFiles.insert(recentFiles.begin(), filepath);
218 while (recentFiles.size() > m_impl->maxRecentFiles) {
219 recentFiles.erase(recentFiles.end() - 1);
224 std::vector<std::string> result;
226 if (m_impl->config.contains(
"recentFiles")) {
227 for (
const auto& file : m_impl->config[
"recentFiles"]) {
228 result.push_back(file.get<std::string>());
231 }
catch (
const json::exception& e) {
232 LOG_WARNING(
"Failed to get recent files from config: {}", e.what());
238 m_impl->config[
"recentFiles"] = json::array();
242 m_impl->maxRecentFiles = max;
248 if (!m_impl->config.contains(
"settings")) {
249 m_impl->config[
"settings"] = json::object();
251 m_impl->config[
"settings"][key] = value;
256 if (m_impl->config.contains(
"settings") && m_impl->config[
"settings"].contains(key)) {
257 return m_impl->config[
"settings"][key].get<std::string>();
259 }
catch (
const json::exception& e) {
260 LOG_WARNING(
"Failed to get string '{}' from config: {}", key, e.what());
266 if (!m_impl->config.contains(
"settings")) {
267 m_impl->config[
"settings"] = json::object();
269 m_impl->config[
"settings"][key] = value;
274 if (m_impl->config.contains(
"settings") && m_impl->config[
"settings"].contains(key)) {
275 return m_impl->config[
"settings"][key].get<
int>();
277 }
catch (
const json::exception& e) {
278 LOG_WARNING(
"Failed to get int '{}' from config: {}", key, e.what());
284 if (!m_impl->config.contains(
"settings")) {
285 m_impl->config[
"settings"] = json::object();
287 m_impl->config[
"settings"][key] = value;
292 if (m_impl->config.contains(
"settings") && m_impl->config[
"settings"].contains(key)) {
293 return m_impl->config[
"settings"][key].get<
bool>();
295 }
catch (
const json::exception& e) {
296 LOG_WARNING(
"Failed to get bool '{}' from config: {}", key, e.what());
302 if (!m_impl->config.contains(
"settings")) {
303 m_impl->config[
"settings"] = json::object();
305 m_impl->config[
"settings"][key] = value;
310 if (m_impl->config.contains(
"settings") && m_impl->config[
"settings"].contains(key)) {
311 return m_impl->config[
"settings"][key].get<
float>();
313 }
catch (
const json::exception& e) {
314 LOG_WARNING(
"Failed to get float '{}' from config: {}", key, e.what());
320 return m_impl->config.contains(
"settings") && m_impl->config[
"settings"].contains(key);
324 if (m_impl->config.contains(
"settings")) {
325 m_impl->config[
"settings"].erase(key);
330 std::vector<std::string> keys;
332 if (m_impl->config.contains(
"settings")) {
333 for (
auto it = m_impl->config[
"settings"].begin(); it != m_impl->config[
"settings"].end(); ++it) {
334 keys.push_back(it.key());
337 }
catch (
const json::exception& e) {
338 LOG_WARNING(
"Failed to get all keys from config: {}", e.what());
345std::filesystem::path ConfigManager::GetConfigDirectory() {
349 PWSTR path =
nullptr;
350 if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, NULL, &path))) {
351 std::filesystem::path result(path);
353 return result /
"MetaImGUI";
355 return std::filesystem::path(
"./config");
356#elif defined(__APPLE__)
359 const char* home = getenv(
"HOME");
361 return std::filesystem::path(home) /
"Library" /
"Application Support" /
"MetaImGUI";
363 return std::filesystem::path(
"./config");
367 const char* xdgConfig = getenv(
"XDG_CONFIG_HOME");
368 if (xdgConfig !=
nullptr) {
369 return std::filesystem::path(xdgConfig) /
"MetaImGUI";
373 const char* home = getenv(
"HOME");
374 if (home !=
nullptr) {
375 return std::filesystem::path(home) /
".config" /
"MetaImGUI";
382bool ConfigManager::EnsureConfigDirectoryExists() {
384 const std::filesystem::path dir = GetConfigDirectory();
385 if (!std::filesystem::exists(dir)) {
386 return std::filesystem::create_directories(dir);
389 }
catch (
const std::filesystem::filesystem_error& e) {
390 LOG_ERROR(
"Failed to create config directory: {}", e.what());