Add TOML support and playback error handling.
This commit is contained in:
parent
7edd8bbc87
commit
893cd8f9d4
12 changed files with 18097 additions and 203 deletions
|
@ -1,8 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectModuleManager">
|
|
||||||
<modules>
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/neko-player.iml" filepath="$PROJECT_DIR$/.idea/neko-player.iml" />
|
|
||||||
</modules>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
|
@ -1,8 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<module type="CPP_MODULE" version="4">
|
|
||||||
<component name="NewModuleRootManager">
|
|
||||||
<content url="file://$MODULE_DIR$" />
|
|
||||||
<orderEntry type="inheritedJdk" />
|
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
|
||||||
</component>
|
|
||||||
</module>
|
|
|
@ -6,5 +6,8 @@
|
||||||
<mapping directory="$PROJECT_DIR$/imgui" vcs="Git" />
|
<mapping directory="$PROJECT_DIR$/imgui" vcs="Git" />
|
||||||
<mapping directory="$PROJECT_DIR$/subprojects/SDL-Mixer-X" vcs="Git" />
|
<mapping directory="$PROJECT_DIR$/subprojects/SDL-Mixer-X" vcs="Git" />
|
||||||
<mapping directory="$PROJECT_DIR$/subprojects/jsoncpp" vcs="Git" />
|
<mapping directory="$PROJECT_DIR$/subprojects/jsoncpp" vcs="Git" />
|
||||||
|
<mapping directory="$PROJECT_DIR$/subprojects/vgmstream" vcs="Git" />
|
||||||
|
<mapping directory="$PROJECT_DIR$/subprojects/vgmstream/dependencies/LibAtrac9" vcs="Git" />
|
||||||
|
<mapping directory="$PROJECT_DIR$/subprojects/vgmstream/dependencies/libg719_decode" vcs="Git" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
16
assets/licenses/TomlPlusPlus.txt
Normal file
16
assets/licenses/TomlPlusPlus.txt
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
||||||
|
documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
|
||||||
|
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
|
||||||
|
Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||||
|
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -35,6 +35,7 @@ add_basic 'licenses/libportal.txt' 'libportal_license'
|
||||||
add_basic 'licenses/ForkAwesome.txt' 'forkawesome_license'
|
add_basic 'licenses/ForkAwesome.txt' 'forkawesome_license'
|
||||||
add_basic 'licenses/libintl.txt' 'libintl_license'
|
add_basic 'licenses/libintl.txt' 'libintl_license'
|
||||||
add_basic 'licenses/cli11.txt' 'cli11_license'
|
add_basic 'licenses/cli11.txt' 'cli11_license'
|
||||||
|
add_basic 'licenses/TomlPlusPlus.txt' 'tomlplusplus_license'
|
||||||
add_basic '../IconFontCppHeaders/licence.txt' 'icnfntcpphdrs_license'
|
add_basic '../IconFontCppHeaders/licence.txt' 'icnfntcpphdrs_license'
|
||||||
echo '#pragma once' > 'assets.h'
|
echo '#pragma once' > 'assets.h'
|
||||||
for i in "${ASSETS[@]}"; do
|
for i in "${ASSETS[@]}"; do
|
||||||
|
|
4
main.cpp
4
main.cpp
|
@ -335,7 +335,8 @@ void MainLoop::GuiFunction() {
|
||||||
#endif
|
#endif
|
||||||
LicenseData(_TR_CTX("Library name", "Noto Sans"), "OFL-1.1-RFN"),
|
LicenseData(_TR_CTX("Library name", "Noto Sans"), "OFL-1.1-RFN"),
|
||||||
LicenseData(_TR_CTX("Library name", "Fork Awesome"), "OFL-1.1-RFN"),
|
LicenseData(_TR_CTX("Library name", "Fork Awesome"), "OFL-1.1-RFN"),
|
||||||
LicenseData(_TR_CTX("Library name", "IconFontCppHeaders"), "Zlib")
|
LicenseData(_TR_CTX("Library name", "IconFontCppHeaders"), "Zlib"),
|
||||||
|
LicenseData(_TR_CTX("Library name", "TOML++"), "MIT")
|
||||||
};
|
};
|
||||||
// Do this in an inner scope so that 'i' isn't accidentally used outside it,
|
// Do this in an inner scope so that 'i' isn't accidentally used outside it,
|
||||||
// and so that 'i' can refer to another variable such as in a for loop.
|
// and so that 'i' can refer to another variable such as in a for loop.
|
||||||
|
@ -356,6 +357,7 @@ void MainLoop::GuiFunction() {
|
||||||
LOAD_LICENSE(projects[i], notosans); i++;
|
LOAD_LICENSE(projects[i], notosans); i++;
|
||||||
LOAD_LICENSE(projects[i], forkawesome); i++;
|
LOAD_LICENSE(projects[i], forkawesome); i++;
|
||||||
LOAD_LICENSE(projects[i], icnfntcpphdrs); i++;
|
LOAD_LICENSE(projects[i], icnfntcpphdrs); i++;
|
||||||
|
LOAD_LICENSE(projects[i], tomlplusplus); i++;
|
||||||
}
|
}
|
||||||
// Left
|
// Left
|
||||||
static LicenseData selected = projects[0];
|
static LicenseData selected = projects[0];
|
||||||
|
|
|
@ -62,6 +62,7 @@ if get_option('portals') and target_machine.system() == 'linux'
|
||||||
endif
|
endif
|
||||||
|
|
||||||
srcs = [
|
srcs = [
|
||||||
|
'IconSet.cpp',
|
||||||
'main.cpp',
|
'main.cpp',
|
||||||
'RendererBackend.cpp',
|
'RendererBackend.cpp',
|
||||||
'playback.cpp',
|
'playback.cpp',
|
||||||
|
|
117
playback.cpp
117
playback.cpp
|
@ -17,25 +17,26 @@ void Playback::SDLCallbackInner(Uint8 *stream, int len) {
|
||||||
if (!playback_ready.load()) {
|
if (!playback_ready.load()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
size_t i = 0;
|
if (st == nullptr) {
|
||||||
size_t max = 0;
|
return;
|
||||||
size_t unit = sizeof(SAMPLETYPE) * spec.channels;
|
|
||||||
size_t bytes_per_iter = std::min(((bufsize / unit)) * unit, (size_t)fakespec.size);
|
|
||||||
while (st->numSamples() <= (uint)len) {
|
|
||||||
if (general_mixer == nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
general_mixer(NULL, buf + i, bytes_per_iter);
|
|
||||||
i += bytes_per_iter;
|
|
||||||
max = i + bytes_per_iter;
|
|
||||||
if (i + bytes_per_iter >= bufsize) {
|
|
||||||
st->putSamples((SAMPLETYPE*)buf, i / unit);
|
|
||||||
max = 0;
|
|
||||||
i = 0;
|
|
||||||
SDL_memset((void*)buf, 0, bufsize);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
st->putSamples((SAMPLETYPE*)buf, max / unit);
|
size_t i = 0;
|
||||||
|
size_t max = 0;
|
||||||
|
size_t unit = sizeof(SAMPLETYPE) * spec.channels;
|
||||||
|
size_t bytes_per_iter = std::min(((bufsize / unit)) * unit, (size_t)fakespec.size);
|
||||||
|
while (st->numSamples() < (size_t)len) {
|
||||||
|
if (general_mixer == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
general_mixer(nullptr, buf + i, (int)bytes_per_iter);
|
||||||
|
i += bytes_per_iter;
|
||||||
|
max = i + bytes_per_iter;
|
||||||
|
if (max >= bufsize) {
|
||||||
|
st->putSamples((SAMPLETYPE*)buf, i/unit);
|
||||||
|
i = 0;
|
||||||
|
max = i + bytes_per_iter;
|
||||||
|
}
|
||||||
|
}
|
||||||
st->receiveSamples((SAMPLETYPE*)stream, len / unit);
|
st->receiveSamples((SAMPLETYPE*)stream, len / unit);
|
||||||
}
|
}
|
||||||
void Playback::SDLCallback(void *userdata, Uint8 *stream, int len) {
|
void Playback::SDLCallback(void *userdata, Uint8 *stream, int len) {
|
||||||
|
@ -43,9 +44,12 @@ void Playback::SDLCallback(void *userdata, Uint8 *stream, int len) {
|
||||||
}
|
}
|
||||||
Mix_Music *Playback::Load(const char *file) {
|
Mix_Music *Playback::Load(const char *file) {
|
||||||
Mix_Music *output = Mix_LoadMUS(file);
|
Mix_Music *output = Mix_LoadMUS(file);
|
||||||
if (!output) {
|
if (output == nullptr) {
|
||||||
printf("Error loading music '%s': %s\n", file, Mix_GetError());
|
printf("Error loading music '%s': %s\n", file, Mix_GetError());
|
||||||
throw std::exception();
|
error_mutex.lock();
|
||||||
|
errors.emplace("Error loading music!");
|
||||||
|
error_mutex.unlock();
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
Mix_PlayMusicStream(output, -1);
|
Mix_PlayMusicStream(output, -1);
|
||||||
length = Mix_MusicDuration(output);
|
length = Mix_MusicDuration(output);
|
||||||
|
@ -57,18 +61,22 @@ void Playback::Unload(Mix_Music *music) {
|
||||||
Mix_FreeMusic(music);
|
Mix_FreeMusic(music);
|
||||||
}
|
}
|
||||||
void Playback::UpdateST() {
|
void Playback::UpdateST() {
|
||||||
|
|
||||||
if (speed > 0.0f) {
|
if (speed > 0.0f) {
|
||||||
|
pitch = std::max(std::min(speed, MaxSpeed), MinSpeed);
|
||||||
st->setRate(speed);
|
st->setRate(speed);
|
||||||
}
|
}
|
||||||
if (tempo > 0.0f) {
|
if (tempo > 0.0f) {
|
||||||
|
pitch = std::max(std::min(tempo, MaxTempo), MinTempo);
|
||||||
st->setTempo(tempo);
|
st->setTempo(tempo);
|
||||||
}
|
}
|
||||||
if (pitch > 0.0f) {
|
if (pitch > 0.0f) {
|
||||||
|
pitch = std::max(std::min(pitch, MaxPitch), MinPitch);
|
||||||
st->setPitch(pitch);
|
st->setPitch(pitch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
double Playback::GetMaxSeconds() {
|
double Playback::GetMaxSeconds() {
|
||||||
return std::max(MaxSpeed * MaxTempo, st->getInputOutputSampleRatio());
|
return std::max((double)(MaxSpeed * MaxTempo), st->getInputOutputSampleRatio());
|
||||||
}
|
}
|
||||||
void Playback::ThreadFunc() {
|
void Playback::ThreadFunc() {
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
|
@ -77,13 +85,13 @@ void Playback::ThreadFunc() {
|
||||||
bool reload = false;
|
bool reload = false;
|
||||||
while (running) {
|
while (running) {
|
||||||
playback_ready.store(false);
|
playback_ready.store(false);
|
||||||
if (reload) {
|
|
||||||
printf("Resuming playback...\n");
|
|
||||||
}
|
|
||||||
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
||||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
|
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
|
||||||
printf("Error initializing SDL: '%s'\n", SDL_GetError());
|
printf("Error initializing SDL: '%s'\n", SDL_GetError());
|
||||||
throw std::exception();
|
error_mutex.lock();
|
||||||
|
errors.emplace("Failed to initialize SDL!");
|
||||||
|
error_mutex.unlock();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SDL_AudioSpec obtained;
|
SDL_AudioSpec obtained;
|
||||||
|
@ -101,9 +109,13 @@ void Playback::ThreadFunc() {
|
||||||
desired.userdata = this;
|
desired.userdata = this;
|
||||||
st = new SoundTouch();
|
st = new SoundTouch();
|
||||||
Mix_Init(MIX_INIT_FLAC|MIX_INIT_MID|MIX_INIT_MOD|MIX_INIT_MP3|MIX_INIT_OGG|MIX_INIT_OPUS|MIX_INIT_WAVPACK);
|
Mix_Init(MIX_INIT_FLAC|MIX_INIT_MID|MIX_INIT_MOD|MIX_INIT_MP3|MIX_INIT_OGG|MIX_INIT_OPUS|MIX_INIT_WAVPACK);
|
||||||
if ((device = SDL_OpenAudioDevice(NULL, 0, &desired, &obtained, SDL_AUDIO_ALLOW_CHANNELS_CHANGE|SDL_AUDIO_ALLOW_FREQUENCY_CHANGE|SDL_AUDIO_ALLOW_SAMPLES_CHANGE)) == 0) {
|
if ((device = SDL_OpenAudioDevice(nullptr, 0, &desired, &obtained, SDL_AUDIO_ALLOW_CHANNELS_CHANGE|SDL_AUDIO_ALLOW_FREQUENCY_CHANGE|SDL_AUDIO_ALLOW_SAMPLES_CHANGE)) == 0) {
|
||||||
printf("Error opening audio device: '%s'\n", SDL_GetError());
|
printf("Error opening audio device: '%s'\n", SDL_GetError());
|
||||||
throw std::exception();
|
error_mutex.lock();
|
||||||
|
errors.emplace("Failed to open audio device!");
|
||||||
|
error_mutex.unlock();
|
||||||
|
running = false;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
spec = obtained;
|
spec = obtained;
|
||||||
st->setSampleRate(spec.freq);
|
st->setSampleRate(spec.freq);
|
||||||
|
@ -116,23 +128,33 @@ void Playback::ThreadFunc() {
|
||||||
fakespec.samples *= maxSeconds;
|
fakespec.samples *= maxSeconds;
|
||||||
size_t new_bufsize = CalculateBufSize(&spec, GetMaxSeconds(), MaxSeconds);
|
size_t new_bufsize = CalculateBufSize(&spec, GetMaxSeconds(), MaxSeconds);
|
||||||
buf = (Uint8*)malloc(new_bufsize);
|
buf = (Uint8*)malloc(new_bufsize);
|
||||||
if (buf == NULL) {
|
if (buf == nullptr) {
|
||||||
throw std::exception();
|
error_mutex.lock();
|
||||||
|
errors.emplace("Failed to allocate memory for playback!");
|
||||||
|
error_mutex.unlock();
|
||||||
|
running = false;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
bufsize = new_bufsize;
|
bufsize = new_bufsize;
|
||||||
general_mixer = Mix_GetGeneralMixer();
|
general_mixer = Mix_GetGeneralMixer();
|
||||||
Mix_InitMixer(&fakespec, SDL_TRUE);
|
Mix_InitMixer(&fakespec, SDL_FALSE);
|
||||||
SDL_PauseAudioDevice(device, 0);
|
SDL_PauseAudioDevice(device, 0);
|
||||||
Mix_Music *music = Load(filePath.c_str());
|
Mix_Music *music = Load(filePath.c_str());
|
||||||
if (reload) {
|
|
||||||
Seek(position);
|
|
||||||
}
|
|
||||||
reload = false;
|
reload = false;
|
||||||
playback_ready.store(true);
|
if (music) {
|
||||||
while (running && !reload) {
|
playback_ready.store(true);
|
||||||
|
} else {
|
||||||
|
playback_ready.store(false);
|
||||||
|
}
|
||||||
|
while (running) {
|
||||||
if (file_changed.exchange(false)) {
|
if (file_changed.exchange(false)) {
|
||||||
Unload(music);
|
Unload(music);
|
||||||
music = Load(filePath.c_str());
|
music = Load(filePath.c_str());
|
||||||
|
if (music) {
|
||||||
|
playback_ready.store(true);
|
||||||
|
} else {
|
||||||
|
playback_ready.store(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (flag_mutex.try_lock()) {
|
if (flag_mutex.try_lock()) {
|
||||||
if (seeking.exchange(false)) {
|
if (seeking.exchange(false)) {
|
||||||
|
@ -144,7 +166,7 @@ void Playback::ThreadFunc() {
|
||||||
Mix_ResumeMusicStream(music);
|
Mix_ResumeMusicStream(music);
|
||||||
}
|
}
|
||||||
if (update.exchange(false)) {
|
if (update.exchange(false)) {
|
||||||
Mix_VolumeMusicStream(music, (volume / 100.0 * MIX_MAX_VOLUME));
|
Mix_VolumeMusicStream(music, (int)(volume / 100.0 * MIX_MAX_VOLUME));
|
||||||
SDL_LockAudioDevice(device);
|
SDL_LockAudioDevice(device);
|
||||||
UpdateST();
|
UpdateST();
|
||||||
size_t correct_buf_size = CalculateBufSize(&spec, GetMaxSeconds(), MaxSeconds);
|
size_t correct_buf_size = CalculateBufSize(&spec, GetMaxSeconds(), MaxSeconds);
|
||||||
|
@ -161,7 +183,11 @@ void Playback::ThreadFunc() {
|
||||||
general_mixer = nullptr;
|
general_mixer = nullptr;
|
||||||
bufsize = 0;
|
bufsize = 0;
|
||||||
buf = (Uint8*)realloc((void*)buf, correct_buf_size);
|
buf = (Uint8*)realloc((void*)buf, correct_buf_size);
|
||||||
if (buf == NULL) {
|
if (buf == nullptr) {
|
||||||
|
error_mutex.lock();
|
||||||
|
errors.emplace("Failed to allocate memory for playback!");
|
||||||
|
error_mutex.unlock();
|
||||||
|
running = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
bufsize = correct_buf_size;
|
bufsize = correct_buf_size;
|
||||||
|
@ -251,3 +277,20 @@ void Playback::Update() {
|
||||||
bool Playback::IsStopped() {
|
bool Playback::IsStopped() {
|
||||||
return !running;
|
return !running;
|
||||||
}
|
}
|
||||||
|
optional<std::string> Playback::GetError() {
|
||||||
|
if (ErrorExists()) {
|
||||||
|
error_mutex.lock();
|
||||||
|
std::string error = errors.back();
|
||||||
|
errors.pop();
|
||||||
|
error_mutex.unlock();
|
||||||
|
return error;
|
||||||
|
} else {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool Playback::ErrorExists() {
|
||||||
|
error_mutex.lock();
|
||||||
|
bool output = !errors.empty();
|
||||||
|
error_mutex.unlock();
|
||||||
|
return output;
|
||||||
|
}
|
22
playback.h
22
playback.h
|
@ -10,9 +10,13 @@
|
||||||
#include <SoundTouch.h>
|
#include <SoundTouch.h>
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <vector>
|
||||||
|
#include <queue>
|
||||||
using namespace soundtouch;
|
using namespace soundtouch;
|
||||||
using std::span;
|
using std::span;
|
||||||
using std::optional;
|
using std::optional;
|
||||||
|
using std::vector;
|
||||||
|
using std::queue;
|
||||||
class Playback {
|
class Playback {
|
||||||
private:
|
private:
|
||||||
std::string filePath;
|
std::string filePath;
|
||||||
|
@ -23,6 +27,7 @@ private:
|
||||||
std::atomic_bool restart;
|
std::atomic_bool restart;
|
||||||
std::atomic_bool playback_ready;
|
std::atomic_bool playback_ready;
|
||||||
std::mutex flag_mutex;
|
std::mutex flag_mutex;
|
||||||
|
std::mutex error_mutex;
|
||||||
std::thread thread;
|
std::thread thread;
|
||||||
double position;
|
double position;
|
||||||
double length;
|
double length;
|
||||||
|
@ -42,6 +47,7 @@ private:
|
||||||
void ThreadFunc();
|
void ThreadFunc();
|
||||||
void UpdateST();
|
void UpdateST();
|
||||||
double GetMaxSeconds();
|
double GetMaxSeconds();
|
||||||
|
queue<std::string> errors;
|
||||||
public:
|
public:
|
||||||
Playback();
|
Playback();
|
||||||
~Playback();
|
~Playback();
|
||||||
|
@ -58,11 +64,13 @@ public:
|
||||||
float speed;
|
float speed;
|
||||||
float tempo;
|
float tempo;
|
||||||
float pitch;
|
float pitch;
|
||||||
double MaxSeconds = 100.0;
|
float MaxSeconds = 100.0;
|
||||||
double MaxSpeed = 4.0;
|
float MaxSpeed = 4.0;
|
||||||
double MaxPitch = 4.0;
|
float MaxPitch = 4.0;
|
||||||
double MaxTempo = 4.0;
|
float MaxTempo = 4.0;
|
||||||
double MinSpeed = 0.25;
|
float MinSpeed = 0.25;
|
||||||
double MinPitch = 0.25;
|
float MinPitch = 0.25;
|
||||||
double MinTempo = 0.25;
|
float MinTempo = 0.25;
|
||||||
|
optional<std::string> GetError();
|
||||||
|
bool ErrorExists();
|
||||||
};
|
};
|
358
theme.cpp
358
theme.cpp
|
@ -1,6 +1,7 @@
|
||||||
#include "theme.h"
|
#include "theme.h"
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
#include "json/value.h"
|
#include "json/value.h"
|
||||||
|
#include "thirdparty/toml.hpp"
|
||||||
#include "translation.h"
|
#include "translation.h"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
|
@ -303,7 +304,7 @@ bool Theme::ShowEditor(bool* open, Theme* &theme, ImGuiID dockid, int window_wid
|
||||||
selectedThemeName = "";
|
selectedThemeName = "";
|
||||||
filter = "";
|
filter = "";
|
||||||
saveAsOpen = false;
|
saveAsOpen = false;
|
||||||
theme->Save(Theme::themeDir / selectedThemePath.replace_extension(".json"));
|
theme->Save(Theme::themeDir / selectedThemePath.replace_extension(".toml"));
|
||||||
theme->file_path = selectedThemePath.generic_string();
|
theme->file_path = selectedThemePath.generic_string();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -324,25 +325,18 @@ AccentColorizer::AccentColorizer() {
|
||||||
Value = false;
|
Value = false;
|
||||||
Alpha = false;
|
Alpha = false;
|
||||||
}
|
}
|
||||||
AccentColorizer::AccentColorizer(Json::Value json) : AccentColorizer() {
|
AccentColorizer::AccentColorizer(toml::table table) : AccentColorizer() {
|
||||||
if (json.isBool()) {
|
Hue = **table["hue"].as_boolean();
|
||||||
Hue = json.asBool();
|
Saturation = **table["saturation"].as_boolean();
|
||||||
Saturation = Hue;
|
Value = **table["value"].as_boolean();
|
||||||
Value = Hue;
|
Alpha = **table["alpha"].as_boolean();
|
||||||
Alpha = Hue;
|
|
||||||
} else {
|
|
||||||
Hue = json["hue"].asBool();
|
|
||||||
Saturation = json["saturation"].asBool();
|
|
||||||
Value = json["value"].asBool();
|
|
||||||
Alpha = json["alpha"].asBool();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Json::Value AccentColorizer::Serialize() {
|
toml::table AccentColorizer::Serialize() {
|
||||||
Json::Value output;
|
toml::table output;
|
||||||
output["hue"] = Hue;
|
output.insert("hue", Hue);
|
||||||
output["saturation"] = Saturation;
|
output.insert("saturation", Saturation);
|
||||||
output["value"] = Value;
|
output.insert("value", Value);
|
||||||
output["alpha"] = Alpha;
|
output.insert("alpha", Alpha);
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
void AccentColorizer::Colorize(ImVec4 accent, ImVec4 &color) {
|
void AccentColorizer::Colorize(ImVec4 accent, ImVec4 &color) {
|
||||||
|
@ -375,79 +369,78 @@ void Theme::Apply(ImVec4 accent) {
|
||||||
void Theme::Save(string path) {
|
void Theme::Save(string path) {
|
||||||
printf("Saving theme to %s...\n", path.c_str());
|
printf("Saving theme to %s...\n", path.c_str());
|
||||||
{
|
{
|
||||||
Json::Value config;
|
toml::table config;
|
||||||
std::ofstream stream;
|
std::ofstream stream;
|
||||||
stream.open(path);
|
stream.open(path);
|
||||||
{
|
{
|
||||||
Json::Value metadata;
|
toml::table metadata;
|
||||||
metadata["SchemaVersion"] = 2;
|
metadata.insert("SchemaVersion", 3);
|
||||||
{
|
{
|
||||||
Json::Value stringsList;
|
toml::table stringsList;
|
||||||
for (auto kv : strings) {
|
for (auto kv : strings) {
|
||||||
Json::Value stringsEntryJson;
|
toml::table stringsEntryJson;
|
||||||
string language = kv.first;
|
string language = kv.first;
|
||||||
ThemeStrings stringsEntry = kv.second;
|
ThemeStrings stringsEntry = kv.second;
|
||||||
stringsEntryJson["Name"] = stringsEntry.name;
|
stringsEntryJson.insert("name", stringsEntry.name);
|
||||||
stringsEntryJson["Description"] = stringsEntry.description;
|
stringsEntryJson.insert("desc", stringsEntry.description);
|
||||||
stringsList[language] = stringsEntryJson;
|
stringsList.insert(language, stringsEntryJson);
|
||||||
}
|
}
|
||||||
metadata["Strings"] = stringsList;
|
metadata.insert("Strings", stringsList);
|
||||||
}
|
}
|
||||||
config["meta"] = metadata;
|
config.insert("meta", metadata);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
Json::Value rounding;
|
toml::table rounding;
|
||||||
rounding["Frame"] = style.FrameRounding;
|
rounding.insert("Frame", style.FrameRounding);
|
||||||
rounding["Window"] = style.WindowRounding;
|
rounding.insert("Window", style.WindowRounding);
|
||||||
rounding["Child"] = style.ChildRounding;
|
rounding.insert("Child", style.ChildRounding);
|
||||||
rounding["Popup"] = style.PopupRounding;
|
rounding.insert("Popup", style.PopupRounding);
|
||||||
rounding["Scrollbar"] = style.ScrollbarRounding;
|
rounding.insert("Scrollbar", style.ScrollbarRounding);
|
||||||
rounding["Grab"] = style.GrabRounding;
|
rounding.insert("Grab", style.GrabRounding);
|
||||||
rounding["Tab"] = style.TabRounding;
|
rounding.insert("Tab", style.TabRounding);
|
||||||
config["rounding"] = rounding;
|
config.insert("rounding", rounding);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
Json::Value sizing;
|
toml::table sizing;
|
||||||
sizing["FrameX"] = style.FramePadding.x;
|
sizing.insert("FrameX", style.FramePadding.x);
|
||||||
sizing["FrameY"] = style.FramePadding.y;
|
sizing.insert("FrameY", style.FramePadding.y);
|
||||||
sizing["WindowX"] = style.WindowPadding.x;
|
sizing.insert("WindowX", style.WindowPadding.x);
|
||||||
sizing["WindowY"] = style.WindowPadding.y;
|
sizing.insert("WindowY", style.WindowPadding.y);
|
||||||
sizing["CellX"] = style.CellPadding.x;
|
sizing.insert("CellX", style.CellPadding.x);
|
||||||
sizing["CellY"] = style.CellPadding.y;
|
sizing.insert("CellY", style.CellPadding.y);
|
||||||
sizing["SeparatorTextX"] = style.SeparatorTextPadding.x;
|
sizing.insert("SeparatorTextX", style.SeparatorTextPadding.x);
|
||||||
sizing["SeparatorTextY"] = style.SeparatorTextPadding.y;
|
sizing.insert("SeparatorTextY", style.SeparatorTextPadding.y);
|
||||||
sizing["ItemSpacingX"] = style.ItemSpacing.x;
|
sizing.insert("ItemSpacingX", style.ItemSpacing.x);
|
||||||
sizing["ItemSpacingY"] = style.ItemSpacing.y;
|
sizing.insert("ItemSpacingY", style.ItemSpacing.y);
|
||||||
sizing["Scrollbar"] = style.ScrollbarSize;
|
sizing.insert("Scrollbar", style.ScrollbarSize);
|
||||||
sizing["Grab"] = style.GrabMinSize;
|
sizing.insert("Grab", style.GrabMinSize);
|
||||||
config["sizing"] = sizing;
|
config.insert("sizing", sizing);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
Json::Value borders;
|
toml::table borders;
|
||||||
borders["Frame"] = style.FrameBorderSize;
|
borders.insert("Frame", style.FrameBorderSize);
|
||||||
borders["Window"] = style.WindowBorderSize;
|
borders.insert("Window", style.WindowBorderSize);
|
||||||
borders["Child"] = style.ChildBorderSize;
|
borders.insert("Child", style.ChildBorderSize);
|
||||||
borders["Popup"] = style.PopupBorderSize;
|
borders.insert("Popup", style.PopupBorderSize);
|
||||||
borders["Tab"] = style.TabBorderSize;
|
borders.insert("Tab", style.TabBorderSize);
|
||||||
borders["Tab"] = style.TabBorderSize;
|
borders.insert("SeparatorText", style.SeparatorTextBorderSize);
|
||||||
borders["SeparatorText"] = style.SeparatorTextBorderSize;
|
config.insert("borders", borders);
|
||||||
config["borders"] = borders;
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
Json::Value colors;
|
toml::table colors;
|
||||||
for (int i = 0; i < ImGuiCol_COUNT; i++)
|
for (int i = 0; i < ImGuiCol_COUNT; i++)
|
||||||
{
|
{
|
||||||
const char* name = ImGui::GetStyleColorName(i);
|
const char* name = ImGui::GetStyleColorName(i);
|
||||||
ImVec4 color = style.Colors[i];
|
ImVec4 color = style.Colors[i];
|
||||||
Json::Value colorValue;
|
toml::table colorValue;
|
||||||
colorValue["r"] = color.x;
|
colorValue.insert("r", color.x);
|
||||||
colorValue["g"] = color.y;
|
colorValue.insert("g", color.y);
|
||||||
colorValue["b"] = color.z;
|
colorValue.insert("b", color.z);
|
||||||
colorValue["a"] = color.w;
|
colorValue.insert("a", color.w);
|
||||||
colorValue["ConvertToAccent"] = AccentColorizers[i].Serialize();
|
colorValue.insert("accent", AccentColorizers[i].Serialize());
|
||||||
colors[name] = colorValue;
|
colors.insert(name, colorValue);
|
||||||
}
|
}
|
||||||
config["colors"] = colors;
|
config.insert("colors", colors);
|
||||||
}
|
}
|
||||||
stream << config;
|
stream << config;
|
||||||
stream.close();
|
stream.close();
|
||||||
|
@ -465,15 +458,16 @@ void Theme::updateAvailableThemes() {
|
||||||
for (auto const& dir_entry : directory_iterator(themeDir)) {
|
for (auto const& dir_entry : directory_iterator(themeDir)) {
|
||||||
if (dir_entry.is_regular_file()) {
|
if (dir_entry.is_regular_file()) {
|
||||||
if (dir_entry.path().extension().string() == ".json") {
|
if (dir_entry.path().extension().string() == ".json") {
|
||||||
path path = dir_entry.path();
|
string curpath = Migrate(dir_entry.path().string());
|
||||||
availableThemes.insert(path);
|
std::filesystem::remove(dir_entry.path());
|
||||||
Json::Value config;
|
availableThemes.insert(curpath);
|
||||||
std::ifstream stream;
|
toml::table config = toml::parse_file(curpath);
|
||||||
stream.open(path);
|
themeStrings[curpath] = ThemeStrings(config);
|
||||||
if (stream.is_open()) {
|
} else if (dir_entry.path().extension().string() == ".toml") {
|
||||||
stream >> config;
|
string curpath = dir_entry.path().string();
|
||||||
themeStrings[path] = ThemeStrings(config);
|
availableThemes.insert(curpath);
|
||||||
}
|
toml::table config = toml::parse_file(curpath);
|
||||||
|
themeStrings[curpath] = ThemeStrings(config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -524,108 +518,200 @@ ThemeStrings Theme::GetStrings() {
|
||||||
return strings["fallback"];
|
return strings["fallback"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ThemeStrings::ThemeStrings(Json::Value config) : ThemeStrings() {
|
ThemeStrings::ThemeStrings(toml::table config) : ThemeStrings() {
|
||||||
char *language_c = CURRENT_LANGUAGE;
|
char *language_c = CURRENT_LANGUAGE;
|
||||||
string language = language_c;
|
string language = language_c;
|
||||||
if (config.isMember("meta")) {
|
if (config.contains("meta")) {
|
||||||
Json::Value metadata = config["meta"];
|
toml::table metadata = *config["meta"].as_table();
|
||||||
//metadata["SchemaVersion"] = 1;
|
//metadata["SchemaVersion"] = 1;
|
||||||
if (metadata.isMember("Strings")) {
|
if (metadata.contains("Strings")) {
|
||||||
Json::Value stringsList = metadata["Strings"];
|
toml::table stringsList = *metadata["Strings"].as_table();
|
||||||
if (stringsList.isMember(language)) {
|
if (stringsList.contains(language)) {
|
||||||
Json::Value stringsEntryJson = stringsList[language];
|
toml::table stringsEntryJson = *stringsList[language].as_table();
|
||||||
if (stringsEntryJson.isMember("Name")) {
|
if (stringsEntryJson.contains("name")) {
|
||||||
name = stringsEntryJson["Name"].asString();
|
name = **stringsEntryJson["name"].as_string();
|
||||||
}
|
}
|
||||||
if (stringsEntryJson.isMember("Description")) {
|
if (stringsEntryJson.contains("desc")) {
|
||||||
description = stringsEntryJson["Description"].asString();
|
description = **stringsEntryJson["desc"].as_string();
|
||||||
}
|
}
|
||||||
} else if (metadata.isMember("fallback")) {
|
} else if (metadata.contains("fallback")) {
|
||||||
Json::Value stringsEntryJson = stringsList["fallback"];
|
toml::table stringsEntryJson = *stringsList["fallback"].as_table();
|
||||||
if (stringsEntryJson.isMember("Name")) {
|
if (stringsEntryJson.contains("name")) {
|
||||||
name = stringsEntryJson["Name"].asString();
|
name = **stringsEntryJson["name"].as_string();
|
||||||
}
|
}
|
||||||
if (stringsEntryJson.isMember("Description")) {
|
if (stringsEntryJson.contains("desc")) {
|
||||||
description = stringsEntryJson["Description"].asString();
|
description = **stringsEntryJson["desc"].as_string();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Theme::Theme(string path) : Theme() {
|
|
||||||
Json::Value config;
|
std::string Theme::Migrate(std::string path) {
|
||||||
std::ifstream stream;
|
if (path.ends_with(".json")) {
|
||||||
stream.open(path);
|
std::ifstream stream(path);
|
||||||
if (stream.is_open()) {
|
Json::Value config;
|
||||||
stream >> config;
|
stream >> config;
|
||||||
|
toml::table newConfig;
|
||||||
if (config.isMember("meta")) {
|
if (config.isMember("meta")) {
|
||||||
Json::Value metadata = config["meta"];
|
Json::Value metadata = config["meta"];
|
||||||
//metadata["SchemaVersion"] = 1;
|
toml::table newMeta;
|
||||||
if (metadata.isMember("Strings")) {
|
if (metadata.isMember("Strings")) {
|
||||||
Json::Value stringsList = metadata["Strings"];
|
Json::Value stringsList = metadata["Strings"];
|
||||||
|
toml::table newStringsList;
|
||||||
for (string language : stringsList.getMemberNames()) {
|
for (string language : stringsList.getMemberNames()) {
|
||||||
Json::Value stringsEntryJson = stringsList[language];
|
Json::Value stringsEntryJson = stringsList[language];
|
||||||
ThemeStrings stringsEntry;
|
toml::table newStringsEntry;
|
||||||
if (stringsEntryJson.isMember("Name")) {
|
if (stringsEntryJson.isMember("Name")) {
|
||||||
stringsEntry.name = stringsEntryJson["Name"].asString();
|
string value = stringsEntryJson["Name"].asString();
|
||||||
|
newStringsEntry.insert("name", value);
|
||||||
}
|
}
|
||||||
if (stringsEntryJson.isMember("Description")) {
|
if (stringsEntryJson.isMember("Description")) {
|
||||||
stringsEntry.description = stringsEntryJson["Description"].asString();
|
string value = stringsEntryJson["Description"].asString();
|
||||||
|
newStringsEntry.insert("desc", value);
|
||||||
}
|
}
|
||||||
strings[language] = stringsEntry;
|
newStringsList.insert(language, newStringsEntry);
|
||||||
}
|
}
|
||||||
|
newMeta.insert("Strings", newStringsList);
|
||||||
}
|
}
|
||||||
|
newConfig.insert("meta", newMeta);
|
||||||
}
|
}
|
||||||
if (config.isMember("rounding")) {
|
if (config.isMember("rounding")) {
|
||||||
Json::Value rounding = config["rounding"];
|
Json::Value rounding = config["rounding"];
|
||||||
style.FrameRounding = rounding["Frame"].asFloat();
|
toml::table newRounding;
|
||||||
style.WindowRounding = rounding["Window"].asFloat();
|
for (string key : rounding.getMemberNames()) {
|
||||||
style.ChildRounding = rounding["Child"].asFloat();
|
newRounding.insert(key, rounding[key].asFloat());
|
||||||
style.PopupRounding = rounding["Popup"].asFloat();
|
}
|
||||||
style.ScrollbarRounding = rounding["Scrollbar"].asFloat();
|
newConfig.insert("rounding", newRounding);
|
||||||
style.GrabRounding = rounding["Grab"].asFloat();
|
|
||||||
style.TabRounding = rounding["Tab"].asFloat();
|
|
||||||
}
|
}
|
||||||
if (config.isMember("sizing")) {
|
if (config.isMember("sizing")) {
|
||||||
Json::Value sizing = config["sizing"];
|
Json::Value rounding = config["sizing"];
|
||||||
style.FramePadding.x = sizing["FrameX"].asFloat();
|
toml::table newRounding;
|
||||||
style.FramePadding.y = sizing["FrameY"].asFloat();
|
for (string key : rounding.getMemberNames()) {
|
||||||
style.WindowPadding.x = sizing["WindowX"].asFloat();
|
newRounding.insert(key, rounding[key].asFloat());
|
||||||
style.WindowPadding.y = sizing["WindowY"].asFloat();
|
}
|
||||||
style.CellPadding.x = sizing["CellX"].asFloat();
|
newConfig.insert("sizing", newRounding);
|
||||||
style.CellPadding.y = sizing["CellY"].asFloat();
|
|
||||||
style.SeparatorTextPadding.x = sizing["SeparatorTextX"].asFloat();
|
|
||||||
style.SeparatorTextPadding.y = sizing["SeparatorTextY"].asFloat();
|
|
||||||
style.ItemSpacing.x = sizing["ItemSpacingX"].asFloat();
|
|
||||||
style.ItemSpacing.y = sizing["ItemSpacingY"].asFloat();
|
|
||||||
style.ScrollbarSize = sizing["Scrollbar"].asFloat();
|
|
||||||
style.GrabMinSize = sizing["Grab"].asFloat();
|
|
||||||
}
|
}
|
||||||
if (config.isMember("borders")) {
|
if (config.isMember("borders")) {
|
||||||
Json::Value borders = config["borders"];
|
Json::Value rounding = config["borders"];
|
||||||
style.FrameBorderSize = borders["Frame"].asFloat();
|
toml::table newRounding;
|
||||||
style.WindowBorderSize = borders["Window"].asFloat();
|
for (string key : rounding.getMemberNames()) {
|
||||||
style.ChildBorderSize = borders["Child"].asFloat();
|
newRounding.insert(key, rounding[key].asFloat());
|
||||||
style.PopupBorderSize = borders["Popup"].asFloat();
|
|
||||||
style.TabBorderSize = borders["Tab"].asFloat();
|
|
||||||
if (borders.isMember("SeparatorText")) {
|
|
||||||
style.SeparatorTextBorderSize = borders["SeparatorText"].asFloat();
|
|
||||||
}
|
}
|
||||||
|
newConfig.insert("borders", newRounding);
|
||||||
}
|
}
|
||||||
if (config.isMember("colors")) {
|
if (config.isMember("colors")) {
|
||||||
Json::Value colors = config["colors"];
|
Json::Value colors = config["colors"];
|
||||||
|
toml::table newColors;
|
||||||
for (int i = 0; i < ImGuiCol_COUNT; i++)
|
for (int i = 0; i < ImGuiCol_COUNT; i++)
|
||||||
{
|
{
|
||||||
|
toml::table newColor;
|
||||||
const char* name = ImGui::GetStyleColorName(i);
|
const char* name = ImGui::GetStyleColorName(i);
|
||||||
if (colors.isMember(name)) {
|
if (colors.isMember(name)) {
|
||||||
Json::Value colorValue = colors[name];
|
Json::Value colorValue = colors[name];
|
||||||
ImVec4 color = ImVec4(colorValue["r"].asFloat(), colorValue["g"].asFloat(), colorValue["b"].asFloat(), colorValue["a"].asFloat());
|
ImVec4 color = ImVec4(colorValue["r"].asFloat(), colorValue["g"].asFloat(), colorValue["b"].asFloat(), colorValue["a"].asFloat());
|
||||||
AccentColorizers[i] = AccentColorizer(colorValue["ConvertToAccent"]);
|
newColor.insert("r", colorValue["r"].asFloat());
|
||||||
style.Colors[i] = color;
|
newColor.insert("g", colorValue["g"].asFloat());
|
||||||
|
newColor.insert("b", colorValue["b"].asFloat());
|
||||||
|
newColor.insert("a", colorValue["a"].asFloat());
|
||||||
|
toml::table newAccentOptions;
|
||||||
|
if (colorValue["ConvertToAccent"].isBool()) {
|
||||||
|
newAccentOptions.insert("hue", colorValue["ConvertToAccent"].asBool());
|
||||||
|
newAccentOptions.insert("saturation", false);
|
||||||
|
newAccentOptions.insert("value", false);
|
||||||
|
newAccentOptions.insert("alpha", false);
|
||||||
|
} else {
|
||||||
|
Json::Value accentOptions = colorValue["ConvertToAccent"];
|
||||||
|
newAccentOptions.insert("hue", accentOptions["hue"].asBool());
|
||||||
|
newAccentOptions.insert("saturation", accentOptions["saturation"].asBool());
|
||||||
|
newAccentOptions.insert("value", accentOptions["value"].asBool());
|
||||||
|
newAccentOptions.insert("alpha", accentOptions["alpha"].asBool());
|
||||||
|
}
|
||||||
|
newColor.insert("accent", newAccentOptions);
|
||||||
|
newColors.insert(name, newColor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stream.close();
|
stream.close();
|
||||||
|
std::string newPath = path.replace(path.size() - 4, 4, "toml");
|
||||||
|
std::ofstream outStream(newPath);
|
||||||
|
outStream << newConfig;
|
||||||
|
outStream.close();
|
||||||
|
return newPath;
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
Theme::Theme(string path) : Theme() {
|
||||||
|
path = Migrate(path);
|
||||||
|
toml::table config;
|
||||||
|
config = toml::parse_file(path);
|
||||||
|
if (config.contains("meta")) {
|
||||||
|
toml::table metadata = *config["meta"].as_table();
|
||||||
|
//metadata["SchemaVersion"] = 1;
|
||||||
|
if (metadata.contains("Strings")) {
|
||||||
|
toml::table stringsList = *metadata["Strings"].as_table();
|
||||||
|
for (auto kv : stringsList) {
|
||||||
|
string language = string(kv.first.str());
|
||||||
|
toml::table stringEntryToml = *kv.second.as_table();
|
||||||
|
ThemeStrings stringsEntry;
|
||||||
|
if (stringEntryToml.contains("name")) {
|
||||||
|
stringsEntry.name = stringEntryToml["name"].as_string()->value_or("Unknown");
|
||||||
|
}
|
||||||
|
if (stringEntryToml.contains("desc")) {
|
||||||
|
stringsEntry.description = stringEntryToml["desc"].as_string()->value_or("No description.");
|
||||||
|
}
|
||||||
|
strings[language] = stringsEntry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (config.contains("rounding")) {
|
||||||
|
toml::table rounding = *config["rounding"].as_table();
|
||||||
|
style.FrameRounding = (float)(**(rounding["Frame"].as_floating_point()));
|
||||||
|
style.WindowRounding = (float)(**(rounding["Window"].as_floating_point()));
|
||||||
|
style.ChildRounding = (float)(**(rounding["Child"].as_floating_point()));
|
||||||
|
style.PopupRounding = (float)(**(rounding["Popup"].as_floating_point()));
|
||||||
|
style.ScrollbarRounding = (float)(**(rounding["Scrollbar"].as_floating_point()));
|
||||||
|
style.GrabRounding = (float)(**(rounding["Grab"].as_floating_point()));
|
||||||
|
style.TabRounding = (float)(**(rounding["Tab"].as_floating_point()));
|
||||||
|
}
|
||||||
|
if (config.contains("sizing")) {
|
||||||
|
toml::table sizing = *config["sizing"].as_table();
|
||||||
|
style.FramePadding.x = (float)(**sizing["FrameX"].as_floating_point());
|
||||||
|
style.FramePadding.y = (float)(**sizing["FrameY"].as_floating_point());
|
||||||
|
style.WindowPadding.x = (float)(**sizing["WindowX"].as_floating_point());
|
||||||
|
style.WindowPadding.y = (float)(**sizing["WindowY"].as_floating_point());
|
||||||
|
style.CellPadding.x = (float)(**sizing["CellX"].as_floating_point());
|
||||||
|
style.CellPadding.y = (float)(**sizing["CellY"].as_floating_point());
|
||||||
|
style.SeparatorTextPadding.x = (float)(**sizing["SeparatorTextX"].as_floating_point());
|
||||||
|
style.SeparatorTextPadding.y = (float)(**sizing["SeparatorTextY"].as_floating_point());
|
||||||
|
style.ItemSpacing.x = (float)(**sizing["ItemSpacingX"].as_floating_point());
|
||||||
|
style.ItemSpacing.y = (float)(**sizing["ItemSpacingY"].as_floating_point());
|
||||||
|
style.ScrollbarSize = (float)(**sizing["Scrollbar"].as_floating_point());
|
||||||
|
style.GrabMinSize = (float)(**sizing["Grab"].as_floating_point());
|
||||||
|
}
|
||||||
|
if (config.contains("borders")) {
|
||||||
|
toml::table borders = *config["borders"].as_table();
|
||||||
|
style.FrameBorderSize = (float)(**borders["Frame"].as_floating_point());
|
||||||
|
style.WindowBorderSize = (float)(**borders["Window"].as_floating_point());
|
||||||
|
style.ChildBorderSize = (float)(**borders["Child"].as_floating_point());
|
||||||
|
style.PopupBorderSize = (float)(**borders["Popup"].as_floating_point());
|
||||||
|
style.TabBorderSize = (float)(**borders["Tab"].as_floating_point());
|
||||||
|
if (borders.contains("SeparatorText")) {
|
||||||
|
style.SeparatorTextBorderSize = (float)(**borders["SeparatorText"].as_floating_point());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (config.contains("colors")) {
|
||||||
|
toml::table colors = *config["colors"].as_table();
|
||||||
|
for (int i = 0; i < ImGuiCol_COUNT; i++)
|
||||||
|
{
|
||||||
|
const char* name = ImGui::GetStyleColorName(i);
|
||||||
|
if (colors.contains(name)) {
|
||||||
|
toml::table colorValue = *colors[name].as_table();
|
||||||
|
ImVec4 color = ImVec4((float)**colorValue["r"].as_floating_point(), (float)**colorValue["g"].as_floating_point(), (float)**colorValue["b"].as_floating_point(), (float)**colorValue["a"].as_floating_point());
|
||||||
|
AccentColorizers[i] = AccentColorizer(*colorValue["accent"].as_table());
|
||||||
|
style.Colors[i] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
file_path = path;
|
file_path = path;
|
||||||
}
|
}
|
||||||
|
|
14
theme.h
14
theme.h
|
@ -7,6 +7,7 @@
|
||||||
#include "file_browser.h"
|
#include "file_browser.h"
|
||||||
#include <json/json.h>
|
#include <json/json.h>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include "thirdparty/toml.hpp"
|
||||||
using std::string;
|
using std::string;
|
||||||
using namespace std::filesystem;
|
using namespace std::filesystem;
|
||||||
|
|
||||||
|
@ -14,7 +15,7 @@ struct ThemeStrings {
|
||||||
string name;
|
string name;
|
||||||
string description;
|
string description;
|
||||||
ThemeStrings();
|
ThemeStrings();
|
||||||
ThemeStrings(Json::Value config);
|
ThemeStrings(toml::table config);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AccentColorizer {
|
struct AccentColorizer {
|
||||||
|
@ -29,18 +30,19 @@ struct AccentColorizer {
|
||||||
/// @brief Colorizes a color stored as an ImVec4 according to preferences.
|
/// @brief Colorizes a color stored as an ImVec4 according to preferences.
|
||||||
void Colorize(ImVec4 accent, ImVec4 &color);
|
void Colorize(ImVec4 accent, ImVec4 &color);
|
||||||
/// @brief Serialize the settings to json.
|
/// @brief Serialize the settings to json.
|
||||||
/// @returns The serialized JSON, as a Json::Value.
|
/// @returns The serialized TOML, as a toml::table.
|
||||||
Json::Value Serialize();
|
toml::table Serialize();
|
||||||
/// @brief Create a default accent colorizer
|
/// @brief Create a default accent colorizer
|
||||||
AccentColorizer();
|
AccentColorizer();
|
||||||
/// @brief Deserialize the settings from JSON and construct.
|
/// @brief Deserialize the settings from TOML and construct.
|
||||||
/// @param json The JSON to deserialize from.
|
/// @param table The TOML to deserialize from.
|
||||||
AccentColorizer(Json::Value json);
|
AccentColorizer(toml::table table);
|
||||||
};
|
};
|
||||||
|
|
||||||
class Theme {
|
class Theme {
|
||||||
ImGuiStyle style;
|
ImGuiStyle style;
|
||||||
|
|
||||||
|
static std::string Migrate(string path);
|
||||||
public:
|
public:
|
||||||
static std::set<path> availableThemes;
|
static std::set<path> availableThemes;
|
||||||
static std::map<path, ThemeStrings> themeStrings;
|
static std::map<path, ThemeStrings> themeStrings;
|
||||||
|
|
17748
thirdparty/toml.hpp
vendored
Normal file
17748
thirdparty/toml.hpp
vendored
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue