feat(storage): persist user data with iKv
This commit is contained in:
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -10,3 +10,6 @@
|
||||
[submodule "vendor/iZo"]
|
||||
path = vendor/iZo
|
||||
url = https://dock-it.dev/Idea-Studios/iZo
|
||||
[submodule "vendor/iKv"]
|
||||
path = vendor/iKv
|
||||
url = https://dock-it.dev/Idea-Studios/iKv
|
||||
|
||||
@@ -46,6 +46,12 @@ add_subdirectory(vendor/libgit2 EXCLUDE_FROM_ALL)
|
||||
set(IZO_BUILD_EXAMPLE OFF CACHE BOOL "" FORCE)
|
||||
add_subdirectory(vendor/iZo EXCLUDE_FROM_ALL)
|
||||
|
||||
# Persistent application settings and session data.
|
||||
set(IKV_BUILD_DEMOS OFF CACHE BOOL "" FORCE)
|
||||
set(IKV_BUILD_TESTS OFF CACHE BOOL "" FORCE)
|
||||
set(IKV_INSTALL OFF CACHE BOOL "" FORCE)
|
||||
add_subdirectory(vendor/iKv EXCLUDE_FROM_ALL)
|
||||
|
||||
find_package(OpenGL REQUIRED)
|
||||
|
||||
add_executable(gitree WIN32
|
||||
@@ -65,7 +71,7 @@ add_executable(gitree WIN32
|
||||
src/models/repository.h
|
||||
)
|
||||
target_include_directories(gitree PRIVATE src vendor/libgit2/include vendor/icons)
|
||||
target_link_libraries(gitree PRIVATE imgui libgit2package iZo::izo OpenGL::GL)
|
||||
target_link_libraries(gitree PRIVATE imgui libgit2package iZo::izo ikv::ikv OpenGL::GL)
|
||||
target_compile_definitions(gitree PRIVATE
|
||||
GITREE_VERSION="${PROJECT_VERSION}"
|
||||
GITREE_ASSET_DIR="${CMAKE_CURRENT_SOURCE_DIR}/vendor/fonts"
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
#include "user_data.h"
|
||||
|
||||
extern "C" {
|
||||
#include <ikv.h>
|
||||
}
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
@@ -14,6 +18,12 @@ std::filesystem::path roaming_directory() {
|
||||
if (const char* home = std::getenv("HOME")) return std::filesystem::path(home) / ".config";
|
||||
return std::filesystem::temp_directory_path();
|
||||
}
|
||||
|
||||
|
||||
const ikv_node_t* object_value(const ikv_node_t* object, const char* key, ikv_type_t type) {
|
||||
const ikv_node_t* value = object ? ikv_object_get(object, key) : nullptr;
|
||||
return value && ikv_node_type(value) == type ? value : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
UserData::UserData() {
|
||||
@@ -28,6 +38,47 @@ UserData::~UserData() {
|
||||
}
|
||||
|
||||
void UserData::load() {
|
||||
const std::filesystem::path data_path = directory_ / "user_data.ikv";
|
||||
if (ikv_node_t* root = ikv_parse_file(data_path.string().c_str())) {
|
||||
if (const ikv_node_t* settings = object_value(root, "settings", IKV_OBJECT)) {
|
||||
if (const ikv_node_t* value = object_value(settings, "sidebar_width", IKV_FLOAT))
|
||||
sidebar_width_ = static_cast<float>(ikv_as_float(value));
|
||||
if (const ikv_node_t* value = object_value(settings, "pull_mode", IKV_INT))
|
||||
pull_mode_ = static_cast<int>(ikv_as_int(value));
|
||||
if (const ikv_node_t* heights = object_value(settings, "sidebar_sections", IKV_ARRAY)) {
|
||||
const uint32_t count = std::min<uint32_t>(
|
||||
ikv_array_size(heights), static_cast<uint32_t>(sidebar_section_heights_.size()));
|
||||
for (uint32_t index = 0; index < count; ++index)
|
||||
sidebar_section_heights_[index] = static_cast<float>(ikv_as_float(ikv_array_get(heights, index)));
|
||||
}
|
||||
}
|
||||
if (const ikv_node_t* history = object_value(root, "recently_closed", IKV_ARRAY)) {
|
||||
const uint32_t count = std::min<uint32_t>(ikv_array_size(history), 12);
|
||||
for (uint32_t index = 0; index < count; ++index) {
|
||||
const char* path = ikv_as_string(ikv_array_get(history, index));
|
||||
if (path && *path) recently_closed_.emplace_back(path);
|
||||
}
|
||||
}
|
||||
if (const ikv_node_t* session = object_value(root, "session", IKV_OBJECT)) {
|
||||
if (const ikv_node_t* active = object_value(session, "active_tab", IKV_INT))
|
||||
active_repository_ = static_cast<size_t>(std::max<int64_t>(0, ikv_as_int(active)));
|
||||
if (const ikv_node_t* tabs = object_value(session, "tabs", IKV_ARRAY)) {
|
||||
for (uint32_t index = 0; index < ikv_array_size(tabs); ++index) {
|
||||
const char* path = ikv_as_string(ikv_array_get(tabs, index));
|
||||
open_repositories_.emplace_back(path ? path : "");
|
||||
}
|
||||
}
|
||||
}
|
||||
ikv_free(root);
|
||||
sidebar_width_ = std::clamp(sidebar_width_, 180.0f, 520.0f);
|
||||
pull_mode_ = std::clamp(pull_mode_, 0, 3);
|
||||
for (float& height : sidebar_section_heights_) height = std::clamp(height, 42.0f, 500.0f);
|
||||
if (open_repositories_.empty()) active_repository_ = 0;
|
||||
else active_repository_ = std::min(active_repository_, open_repositories_.size() - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
// Import the original files once when upgrading an existing installation.
|
||||
std::ifstream settings(directory_ / "settings.ini");
|
||||
std::string key;
|
||||
while (settings >> key) {
|
||||
@@ -76,16 +127,23 @@ void UserData::setRepositorySession(std::vector<std::string> paths, size_t activ
|
||||
|
||||
void UserData::save() const {
|
||||
std::filesystem::create_directories(directory_);
|
||||
std::ofstream settings(directory_ / "settings.ini", std::ios::trunc);
|
||||
settings << "sidebar_width " << sidebar_width_ << '\n';
|
||||
settings << "pull_mode " << pull_mode_ << '\n';
|
||||
for (size_t index = 0; index < sidebar_section_heights_.size(); ++index)
|
||||
settings << "sidebar_section_" << index << ' ' << sidebar_section_heights_[index] << '\n';
|
||||
ikv_node_t* root = ikv_create_object("gitree");
|
||||
if (!root) return;
|
||||
|
||||
std::ofstream history(directory_ / "history.txt", std::ios::trunc);
|
||||
for (const auto& path : recently_closed_) history << std::quoted(path) << '\n';
|
||||
ikv_node_t* settings = ikv_object_add_object(root, "settings");
|
||||
ikv_object_set_float(settings, "sidebar_width", sidebar_width_);
|
||||
ikv_object_set_int(settings, "pull_mode", pull_mode_);
|
||||
ikv_node_t* heights = ikv_object_add_array(settings, "sidebar_sections", IKV_FLOAT);
|
||||
for (const float height : sidebar_section_heights_) ikv_array_add_float(heights, height);
|
||||
|
||||
std::ofstream session(directory_ / "session.txt", std::ios::trunc);
|
||||
session << active_repository_ << '\n';
|
||||
for (const auto& path : open_repositories_) session << std::quoted(path) << '\n';
|
||||
ikv_node_t* history = ikv_object_add_array(root, "recently_closed", IKV_STRING);
|
||||
for (const auto& path : recently_closed_) ikv_array_add_string(history, path.c_str());
|
||||
|
||||
ikv_node_t* session = ikv_object_add_object(root, "session");
|
||||
ikv_object_set_int(session, "active_tab", static_cast<int64_t>(active_repository_));
|
||||
ikv_node_t* tabs = ikv_object_add_array(session, "tabs", IKV_STRING);
|
||||
for (const auto& path : open_repositories_) ikv_array_add_string(tabs, path.c_str());
|
||||
|
||||
ikv_write_file((directory_ / "user_data.ikv").string().c_str(), root);
|
||||
ikv_free(root);
|
||||
}
|
||||
|
||||
@@ -826,6 +826,7 @@ void draw_licenses_popup() {
|
||||
{"Dear ImGui", "MIT License", "https://github.com/ocornut/imgui"},
|
||||
{"GLFW", "zlib/libpng License", "https://github.com/glfw/glfw"},
|
||||
{"iZo", "MIT License", "https://dock-it.dev/Idea-Studios/iZo"},
|
||||
{"iKv", "CC BY-SA 4.0", "https://dock-it.dev/Idea-Studios/iKv"},
|
||||
{"Inter", "SIL Open Font License 1.1", "https://github.com/rsms/inter"},
|
||||
{"Font Awesome Free", "SIL Open Font License 1.1", "https://github.com/FortAwesome/Font-Awesome"},
|
||||
{"Tabler Icons", "MIT License", "https://github.com/tabler/tabler-icons"},
|
||||
|
||||
1
vendor/iKv
vendored
Submodule
1
vendor/iKv
vendored
Submodule
Submodule vendor/iKv added at d0b02f4735
Reference in New Issue
Block a user