2021-06-20 00:57:27 +12:00
|
|
|
/* Copyright 2021 Aristocratos (jakob@qvantnet.com)
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
|
|
|
|
indent = tab
|
|
|
|
tab-size = 4
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include <cmath>
|
|
|
|
#include <vector>
|
|
|
|
#include <unordered_map>
|
|
|
|
#include <ranges>
|
|
|
|
#include <algorithm>
|
2021-06-26 09:58:19 +12:00
|
|
|
#include <fstream>
|
2021-06-20 00:57:27 +12:00
|
|
|
|
2021-06-20 08:48:31 +12:00
|
|
|
#include <btop_tools.hpp>
|
|
|
|
#include <btop_config.hpp>
|
|
|
|
#include <btop_theme.hpp>
|
2021-06-20 00:57:27 +12:00
|
|
|
|
|
|
|
using std::round, std::vector, robin_hood::unordered_flat_map, std::stoi, std::views::iota, std::array,
|
|
|
|
std::clamp, std::max, std::min, std::ceil, std::to_string;
|
|
|
|
using namespace Tools;
|
|
|
|
namespace rng = std::ranges;
|
|
|
|
namespace fs = std::filesystem;
|
|
|
|
|
|
|
|
namespace Theme {
|
|
|
|
|
|
|
|
fs::path theme_dir;
|
|
|
|
fs::path user_theme_dir;
|
2021-06-21 08:07:04 +12:00
|
|
|
vector<string> themes;
|
2021-06-20 00:57:27 +12:00
|
|
|
|
|
|
|
const unordered_flat_map<string, string> Default_theme = {
|
|
|
|
{ "main_bg", "#00" },
|
|
|
|
{ "main_fg", "#cc" },
|
|
|
|
{ "title", "#ee" },
|
|
|
|
{ "hi_fg", "#969696" },
|
|
|
|
{ "selected_bg", "#7e2626" },
|
|
|
|
{ "selected_fg", "#ee" },
|
|
|
|
{ "inactive_fg", "#40" },
|
|
|
|
{ "graph_text", "#60" },
|
|
|
|
{ "meter_bg", "#40" },
|
|
|
|
{ "proc_misc", "#0de756" },
|
|
|
|
{ "cpu_box", "#3d7b46" },
|
|
|
|
{ "mem_box", "#8a882e" },
|
|
|
|
{ "net_box", "#423ba5" },
|
|
|
|
{ "proc_box", "#923535" },
|
|
|
|
{ "div_line", "#30" },
|
|
|
|
{ "temp_start", "#4897d4" },
|
|
|
|
{ "temp_mid", "#5474e8" },
|
|
|
|
{ "temp_end", "#ff40b6" },
|
|
|
|
{ "cpu_start", "#50f095" },
|
|
|
|
{ "cpu_mid", "#f2e266" },
|
|
|
|
{ "cpu_end", "#fa1e1e" },
|
|
|
|
{ "free_start", "#223014" },
|
|
|
|
{ "free_mid", "#b5e685" },
|
|
|
|
{ "free_end", "#dcff85" },
|
|
|
|
{ "cached_start", "#0b1a29" },
|
|
|
|
{ "cached_mid", "#74e6fc" },
|
|
|
|
{ "cached_end", "#26c5ff" },
|
|
|
|
{ "available_start", "#292107" },
|
|
|
|
{ "available_mid", "#ffd77a" },
|
|
|
|
{ "available_end", "#ffb814" },
|
|
|
|
{ "used_start", "#3b1f1c" },
|
|
|
|
{ "used_mid", "#d9626d" },
|
|
|
|
{ "used_end", "#ff4769" },
|
|
|
|
{ "download_start", "#231a63" },
|
|
|
|
{ "download_mid", "#4f43a3" },
|
|
|
|
{ "download_end", "#b0a9de" },
|
|
|
|
{ "upload_start", "#510554" },
|
|
|
|
{ "upload_mid", "#7d4180" },
|
|
|
|
{ "upload_end", "#dcafde" },
|
|
|
|
{ "process_start", "#80d0a3" },
|
|
|
|
{ "process_mid", "#dcd179" },
|
|
|
|
{ "process_end", "#d45454" }
|
|
|
|
};
|
|
|
|
|
2021-06-21 08:07:04 +12:00
|
|
|
const unordered_flat_map<string, string> TTY_theme = {
|
2021-06-22 08:52:55 +12:00
|
|
|
{ "main_bg", "\x1b[0;40m" },
|
2021-06-21 08:07:04 +12:00
|
|
|
{ "main_fg", "\x1b[37m" },
|
|
|
|
{ "title", "\x1b[97m" },
|
|
|
|
{ "hi_fg", "\x1b[31m" },
|
|
|
|
{ "selected_bg", "\x1b[41m" },
|
|
|
|
{ "selected_fg", "\x1b[97m" },
|
|
|
|
{ "inactive_fg", "\x1b[90m" },
|
|
|
|
{ "graph_text", "\x1b[37m" },
|
|
|
|
{ "meter_bg", "\x1b[90m" },
|
|
|
|
{ "proc_misc", "\x1b[92m" },
|
|
|
|
{ "cpu_box", "\x1b[32m" },
|
|
|
|
{ "mem_box", "\x1b[33m" },
|
|
|
|
{ "net_box", "\x1b[35m" },
|
|
|
|
{ "proc_box", "\x1b[31m" },
|
|
|
|
{ "div_line", "\x1b[90m" },
|
|
|
|
{ "temp_start", "\x1b[94m" },
|
|
|
|
{ "temp_mid", "\x1b[96m" },
|
|
|
|
{ "temp_end", "\x1b[95m" },
|
|
|
|
{ "cpu_start", "\x1b[92m" },
|
|
|
|
{ "cpu_mid", "\x1b[93m" },
|
|
|
|
{ "cpu_end", "\x1b[91m" },
|
|
|
|
{ "free_start", "\x1b[32m" },
|
|
|
|
{ "free_mid", "" },
|
|
|
|
{ "free_end", "\x1b[92m" },
|
|
|
|
{ "cached_start", "\x1b[36m" },
|
|
|
|
{ "cached_mid", "" },
|
|
|
|
{ "cached_end", "\x1b[96m" },
|
|
|
|
{ "available_start", "\x1b[33m" },
|
|
|
|
{ "available_mid", "" },
|
|
|
|
{ "available_end", "\x1b[93m" },
|
|
|
|
{ "used_start", "\x1b[31m" },
|
|
|
|
{ "used_mid", "" },
|
|
|
|
{ "used_end", "\x1b[91m" },
|
|
|
|
{ "download_start", "\x1b[34m" },
|
|
|
|
{ "download_mid", "" },
|
|
|
|
{ "download_end", "\x1b[94m" },
|
|
|
|
{ "upload_start", "\x1b[35m" },
|
|
|
|
{ "upload_mid", "" },
|
|
|
|
{ "upload_end", "\x1b[95m" },
|
|
|
|
{ "process_start", "\x1b[32m" },
|
|
|
|
{ "process_mid", "\x1b[33m" },
|
|
|
|
{ "process_end", "\x1b[31m" }
|
|
|
|
};
|
|
|
|
|
2021-06-20 00:57:27 +12:00
|
|
|
namespace {
|
|
|
|
//* Convert 24-bit colors to 256 colors using 6x6x6 color cube
|
|
|
|
int truecolor_to_256(int r, int g, int b){
|
2021-06-22 08:52:55 +12:00
|
|
|
if (round((double)r / 11) == round((double)g / 11) and round((double)g / 11) == round((double)b / 11)) {
|
2021-06-20 00:57:27 +12:00
|
|
|
return 232 + round((double)r / 11);
|
|
|
|
} else {
|
|
|
|
return round((double)r / 51) * 36 + round((double)g / 51) * 6 + round((double)b / 51) + 16;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
string hex_to_color(string hexa, bool t_to_256, string depth){
|
|
|
|
if (hexa.size() > 1){
|
|
|
|
hexa.erase(0, 1);
|
2021-06-22 08:52:55 +12:00
|
|
|
for (auto& c : hexa) if (not isxdigit(c)) {
|
2021-06-20 00:57:27 +12:00
|
|
|
Logger::error("Invalid hex value: " + hexa);
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
string pre = Fx::e + (depth == "fg" ? "38" : "48") + ";" + (t_to_256 ? "5;" : "2;");
|
|
|
|
|
|
|
|
if (hexa.size() == 2){
|
|
|
|
int h_int = stoi(hexa, 0, 16);
|
|
|
|
if (t_to_256){
|
|
|
|
return pre + to_string(truecolor_to_256(h_int, h_int, h_int)) + "m";
|
|
|
|
} else {
|
|
|
|
string h_str = to_string(h_int);
|
|
|
|
return pre + h_str + ";" + h_str + ";" + h_str + "m";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (hexa.size() == 6){
|
|
|
|
if (t_to_256){
|
|
|
|
return pre + to_string(truecolor_to_256(
|
|
|
|
stoi(hexa.substr(0, 2), 0, 16),
|
|
|
|
stoi(hexa.substr(2, 2), 0, 16),
|
|
|
|
stoi(hexa.substr(4, 2), 0, 16))) + "m";
|
|
|
|
} else {
|
|
|
|
return pre +
|
|
|
|
to_string(stoi(hexa.substr(0, 2), 0, 16)) + ";" +
|
|
|
|
to_string(stoi(hexa.substr(2, 2), 0, 16)) + ";" +
|
|
|
|
to_string(stoi(hexa.substr(4, 2), 0, 16)) + "m";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else Logger::error("Invalid size of hex value: " + hexa);
|
|
|
|
}
|
|
|
|
else Logger::error("Hex value missing: " + hexa);
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
string dec_to_color(int r, int g, int b, bool t_to_256, string depth){
|
|
|
|
string pre = Fx::e + (depth == "fg" ? "38" : "48") + ";" + (t_to_256 ? "5;" : "2;");
|
|
|
|
r = std::clamp(r, 0, 255);
|
|
|
|
g = std::clamp(g, 0, 255);
|
|
|
|
b = std::clamp(b, 0, 255);
|
|
|
|
if (t_to_256) return pre + to_string(truecolor_to_256(r, g, b)) + "m";
|
|
|
|
else return pre + to_string(r) + ";" + to_string(g) + ";" + to_string(b) + "m";
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
unordered_flat_map<string, string> colors;
|
|
|
|
unordered_flat_map<string, array<int, 3>> rgbs;
|
|
|
|
unordered_flat_map<string, array<string, 101>> gradients;
|
|
|
|
|
|
|
|
//* Convert hex color to a array of decimals
|
|
|
|
array<int, 3> hex_to_dec(string hexa){
|
|
|
|
if (hexa.size() > 1){
|
|
|
|
hexa.erase(0, 1);
|
2021-06-22 08:52:55 +12:00
|
|
|
for (auto& c : hexa) if (not isxdigit(c)) return array<int, 3>{-1, -1, -1};
|
2021-06-20 00:57:27 +12:00
|
|
|
|
|
|
|
if (hexa.size() == 2){
|
|
|
|
int h_int = stoi(hexa, 0, 16);
|
|
|
|
return array<int, 3>{h_int, h_int, h_int};
|
|
|
|
}
|
|
|
|
else if (hexa.size() == 6){
|
|
|
|
return array<int, 3>{
|
|
|
|
stoi(hexa.substr(0, 2), 0, 16),
|
|
|
|
stoi(hexa.substr(2, 2), 0, 16),
|
|
|
|
stoi(hexa.substr(4, 2), 0, 16)
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return {-1 ,-1 ,-1};
|
|
|
|
}
|
|
|
|
|
|
|
|
//* Generate colors and rgb decimal vectors for the theme
|
2021-06-21 08:07:04 +12:00
|
|
|
void generateColors(unordered_flat_map<string, string> source){
|
2021-06-20 00:57:27 +12:00
|
|
|
vector<string> t_rgb;
|
|
|
|
string depth;
|
2021-06-22 08:52:55 +12:00
|
|
|
bool t_to_256 = Config::getB("lowcolor");
|
2021-06-20 00:57:27 +12:00
|
|
|
colors.clear(); rgbs.clear();
|
|
|
|
for (auto& [name, color] : Default_theme) {
|
2021-06-26 09:58:19 +12:00
|
|
|
if (name == "main_bg" and not Config::getB("theme_background")) {
|
|
|
|
colors[name] = "\x1b[49m";
|
|
|
|
rgbs[name] = {-1, -1, -1};
|
|
|
|
continue;
|
|
|
|
}
|
2021-06-22 08:52:55 +12:00
|
|
|
depth = (name.ends_with("bg") and name != "meter_bg") ? "bg" : "fg";
|
2021-06-20 00:57:27 +12:00
|
|
|
if (source.contains(name)) {
|
2021-06-26 09:58:19 +12:00
|
|
|
if (name == "main_bg" and source.at(name).empty()) {
|
|
|
|
colors[name] = "\x1b[49m";
|
|
|
|
rgbs[name] = {-1, -1, -1};
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (source.at(name).empty() and (name.ends_with("_mid") or name.ends_with("_end"))) {
|
|
|
|
colors[name] = "";
|
|
|
|
rgbs[name] = {-1, -1, -1};
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (source.at(name).starts_with('#')) {
|
2021-06-20 00:57:27 +12:00
|
|
|
colors[name] = hex_to_color(source.at(name), t_to_256, depth);
|
|
|
|
rgbs[name] = hex_to_dec(source.at(name));
|
|
|
|
}
|
2021-06-26 09:58:19 +12:00
|
|
|
else if (not source.at(name).empty()) {
|
2021-06-20 00:57:27 +12:00
|
|
|
t_rgb = ssplit(source.at(name));
|
|
|
|
if (t_rgb.size() != 3)
|
|
|
|
Logger::error("Invalid RGB decimal value: \"" + source.at(name) + "\"");
|
|
|
|
else {
|
|
|
|
colors[name] = dec_to_color(stoi(t_rgb[0]), stoi(t_rgb[1]), stoi(t_rgb[2]), t_to_256, depth);
|
|
|
|
rgbs[name] = array<int, 3>{stoi(t_rgb[0]), stoi(t_rgb[1]), stoi(t_rgb[2])};
|
2021-06-26 09:58:19 +12:00
|
|
|
|
2021-06-20 00:57:27 +12:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (colors[name].empty()) {
|
2021-06-26 09:58:19 +12:00
|
|
|
Logger::debug("Missing color value for \"" + name + "\". Using value from default.");
|
2021-06-20 00:57:27 +12:00
|
|
|
colors[name] = hex_to_color(color, t_to_256, depth);
|
|
|
|
rgbs[name] = array<int, 3>{-1, -1, -1};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//* Generate color gradients from two or three colors, 101 values indexed 0-100
|
|
|
|
void generateGradients(){
|
|
|
|
gradients.clear();
|
|
|
|
array<string, 101> c_gradient;
|
2021-06-22 08:52:55 +12:00
|
|
|
bool t_to_256 = Config::getB("lowcolor");
|
2021-06-20 00:57:27 +12:00
|
|
|
for (auto& [name, source_arr] : rgbs) {
|
2021-06-22 08:52:55 +12:00
|
|
|
if (not name.ends_with("_start")) continue;
|
2021-06-20 00:57:27 +12:00
|
|
|
array<array<int, 3>, 101> dec_arr;
|
|
|
|
dec_arr[0][0] = -1;
|
|
|
|
string wname = rtrim(name, "_start");
|
2021-06-26 09:58:19 +12:00
|
|
|
array<array<int, 3>, 3> rgb_array = {source_arr, rgbs[wname + "_mid"], rgbs[wname + "_end"]};
|
2021-06-20 00:57:27 +12:00
|
|
|
|
|
|
|
//? Only start iteration if gradient has a _end color value defined
|
2021-06-26 09:58:19 +12:00
|
|
|
if (rgb_array[2][0] >= 0) {
|
2021-06-20 00:57:27 +12:00
|
|
|
|
|
|
|
//? Split iteration in two passes of 50 + 51 instead of 101 if gradient has _start, _mid and _end values defined
|
2021-06-26 09:58:19 +12:00
|
|
|
int cur_range = (rgb_array[1][0] >= 0) ? 50 : 100;
|
2021-06-20 00:57:27 +12:00
|
|
|
for (int rgb : iota(0, 3)){
|
2021-06-26 09:58:19 +12:00
|
|
|
int start = 0, offset = 0;
|
|
|
|
int end = (cur_range == 50) ? 1 : 2;
|
2021-06-20 00:57:27 +12:00
|
|
|
for (int i : iota(0, 101)) {
|
2021-06-26 09:58:19 +12:00
|
|
|
dec_arr[i][rgb] = rgb_array[start][rgb] + (i - offset) * (rgb_array[end][rgb] - rgb_array[start][rgb]) / cur_range;
|
2021-06-20 00:57:27 +12:00
|
|
|
|
|
|
|
//? Switch source arrays from _start/_mid to _mid/_end at 50 passes if _mid is defined
|
2021-06-26 09:58:19 +12:00
|
|
|
if (i == cur_range) { ++start; ++end; offset = 50;}
|
2021-06-20 00:57:27 +12:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (dec_arr[0][0] != -1) {
|
|
|
|
int y = 0;
|
|
|
|
for (auto& arr : dec_arr) c_gradient[y++] = dec_to_color(arr[0], arr[1], arr[2], t_to_256);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
//? If only _start was defined fill array with _start color
|
|
|
|
c_gradient.fill(colors[name]);
|
|
|
|
}
|
|
|
|
gradients[wname].swap(c_gradient);
|
|
|
|
}
|
|
|
|
}
|
2021-06-21 08:07:04 +12:00
|
|
|
|
2021-06-22 08:52:55 +12:00
|
|
|
//* Set colors and generate gradients for the TTY theme
|
2021-06-21 08:07:04 +12:00
|
|
|
void generateTTYColors(){
|
|
|
|
rgbs.clear();
|
|
|
|
gradients.clear();
|
2021-06-26 09:58:19 +12:00
|
|
|
colors = TTY_theme;
|
2021-06-21 08:07:04 +12:00
|
|
|
|
|
|
|
for (auto& c : colors) {
|
2021-06-22 08:52:55 +12:00
|
|
|
if (not c.first.ends_with("_start")) continue;
|
2021-06-21 08:07:04 +12:00
|
|
|
string base_name = rtrim(c.first, "_start");
|
|
|
|
string section = "_start";
|
|
|
|
int split = colors.at(base_name + "_mid").empty() ? 50 : 33;
|
|
|
|
for (int i : iota(0, 101)) {
|
|
|
|
gradients[base_name][i] = colors.at(base_name + section);
|
|
|
|
if (i == split) {
|
2021-06-22 08:52:55 +12:00
|
|
|
section = (split == 33) ? "_mid" : "_end";
|
|
|
|
split *= 2;
|
2021-06-21 08:07:04 +12:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-26 09:58:19 +12:00
|
|
|
//* Load a .theme file from disk
|
2021-06-21 08:07:04 +12:00
|
|
|
auto loadFile(string filename){
|
2021-06-26 09:58:19 +12:00
|
|
|
unordered_flat_map<string, string> theme_out;
|
|
|
|
fs::path filepath = filename;
|
|
|
|
if (not fs::exists(filepath))
|
|
|
|
return Default_theme;
|
|
|
|
|
|
|
|
std::ifstream themefile(filepath);
|
|
|
|
if (themefile.good()) {
|
|
|
|
Logger::debug("Loading theme file: " + filename);
|
|
|
|
while (not themefile.bad()) {
|
|
|
|
themefile.ignore(SSmax, '[');
|
|
|
|
if (themefile.eof()) break;
|
|
|
|
string name, value;
|
|
|
|
getline(themefile, name, ']');
|
|
|
|
if (not Default_theme.contains(name)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
themefile.ignore(SSmax, '=');
|
|
|
|
themefile >> std::ws;
|
|
|
|
if (themefile.eof()) break;
|
|
|
|
if (themefile.peek() == '"') {
|
|
|
|
themefile.ignore(1);
|
|
|
|
getline(themefile, value, '"');
|
|
|
|
}
|
|
|
|
else getline(themefile, value, '\n');
|
2021-06-21 08:07:04 +12:00
|
|
|
|
2021-06-26 09:58:19 +12:00
|
|
|
theme_out[name] = value;
|
|
|
|
}
|
|
|
|
themefile.close();
|
|
|
|
return theme_out;
|
|
|
|
}
|
|
|
|
return Default_theme;
|
2021-06-21 08:07:04 +12:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void updateThemes(){
|
2021-06-22 08:52:55 +12:00
|
|
|
themes.clear();
|
|
|
|
themes.push_back("Default");
|
|
|
|
themes.push_back("TTY");
|
2021-06-21 08:07:04 +12:00
|
|
|
|
2021-06-26 09:58:19 +12:00
|
|
|
for (const auto& path : { theme_dir, user_theme_dir } ) {
|
2021-07-04 11:18:48 +12:00
|
|
|
if (path.empty()) continue;
|
2021-06-26 09:58:19 +12:00
|
|
|
for (auto& file : fs::directory_iterator(path)){
|
|
|
|
if (file.path().extension() == ".theme" and access(file.path().c_str(), R_OK) != -1) {
|
|
|
|
themes.push_back(file.path().c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-06-20 00:57:27 +12:00
|
|
|
|
2021-06-26 09:58:19 +12:00
|
|
|
}
|
2021-06-20 00:57:27 +12:00
|
|
|
|
2021-06-26 09:58:19 +12:00
|
|
|
void setTheme(){
|
|
|
|
string theme = Config::getS("color_theme");
|
2021-06-22 08:52:55 +12:00
|
|
|
if (theme == "TTY" or Config::getB("tty_mode"))
|
2021-06-21 08:07:04 +12:00
|
|
|
generateTTYColors();
|
|
|
|
else {
|
|
|
|
generateColors((theme == "Default" ? Default_theme : loadFile(theme)));
|
|
|
|
generateGradients();
|
|
|
|
}
|
2021-06-20 00:57:27 +12:00
|
|
|
Term::fg = colors.at("main_fg");
|
|
|
|
Term::bg = colors.at("main_bg");
|
|
|
|
Fx::reset = Fx::reset_base + Term::fg + Term::bg;
|
|
|
|
}
|
|
|
|
|
|
|
|
//* Return escape code for color <name>
|
|
|
|
const string& c(string name){
|
|
|
|
return colors.at(name);
|
|
|
|
}
|
|
|
|
|
|
|
|
//* Return array of escape codes for color gradient <name>
|
|
|
|
const array<string, 101>& g(string name){
|
|
|
|
return gradients.at(name);
|
|
|
|
}
|
|
|
|
|
|
|
|
//* Return array of red, green and blue in decimal for color <name>
|
|
|
|
const std::array<int, 3>& dec(string name){
|
|
|
|
return rgbs.at(name);
|
|
|
|
}
|
|
|
|
|
2021-06-26 09:58:19 +12:00
|
|
|
unordered_flat_map<string, string>& test_colors(){
|
2021-06-20 00:57:27 +12:00
|
|
|
return colors;
|
|
|
|
}
|
2021-06-26 09:58:19 +12:00
|
|
|
unordered_flat_map<string, std::array<string, 101>>& test_gradients(){
|
2021-06-20 00:57:27 +12:00
|
|
|
return gradients;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|