support specifying colors with RGB components
This commit is contained in:
parent
d80815b927
commit
56ab33c9d6
57
src/color.cc
57
src/color.cc
|
@ -2,37 +2,54 @@
|
||||||
|
|
||||||
#include "exception.hh"
|
#include "exception.hh"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
namespace Kakoune
|
namespace Kakoune
|
||||||
{
|
{
|
||||||
|
|
||||||
Color str_to_color(const String& color)
|
Color str_to_color(const String& color)
|
||||||
{
|
{
|
||||||
if (color == "default") return Color::Default;
|
if (color == "default") return Colors::Default;
|
||||||
if (color == "black") return Color::Black;
|
if (color == "black") return Colors::Black;
|
||||||
if (color == "red") return Color::Red;
|
if (color == "red") return Colors::Red;
|
||||||
if (color == "green") return Color::Green;
|
if (color == "green") return Colors::Green;
|
||||||
if (color == "yellow") return Color::Yellow;
|
if (color == "yellow") return Colors::Yellow;
|
||||||
if (color == "blue") return Color::Blue;
|
if (color == "blue") return Colors::Blue;
|
||||||
if (color == "magenta") return Color::Magenta;
|
if (color == "magenta") return Colors::Magenta;
|
||||||
if (color == "cyan") return Color::Cyan;
|
if (color == "cyan") return Colors::Cyan;
|
||||||
if (color == "white") return Color::White;
|
if (color == "white") return Colors::White;
|
||||||
|
|
||||||
|
static const Regex rgb_regex{"rgb:[0-9a-fA-F]{6}"};
|
||||||
|
if (boost::regex_match(color, rgb_regex))
|
||||||
|
{
|
||||||
|
long int l = strtol(color.c_str() + 4, NULL, 16);
|
||||||
|
return { (unsigned char)((l >> 16) & 0xFF),
|
||||||
|
(unsigned char)((l >> 8) & 0xFF),
|
||||||
|
(unsigned char)(l & 0xFF) };
|
||||||
|
}
|
||||||
throw runtime_error("Unable to parse color '" + color + "'");
|
throw runtime_error("Unable to parse color '" + color + "'");
|
||||||
return Color::Default;
|
return Colors::Default;
|
||||||
}
|
}
|
||||||
|
|
||||||
String color_to_str(const Color& color)
|
String color_to_str(const Color& color)
|
||||||
{
|
{
|
||||||
switch (color)
|
switch (color.color)
|
||||||
{
|
{
|
||||||
case Color::Default: return "default";
|
case Colors::Default: return "default";
|
||||||
case Color::Black: return "black";
|
case Colors::Black: return "black";
|
||||||
case Color::Red: return "red";
|
case Colors::Red: return "red";
|
||||||
case Color::Green: return "green";
|
case Colors::Green: return "green";
|
||||||
case Color::Yellow: return "yellow";
|
case Colors::Yellow: return "yellow";
|
||||||
case Color::Blue: return "blue";
|
case Colors::Blue: return "blue";
|
||||||
case Color::Magenta: return "magenta";
|
case Colors::Magenta: return "magenta";
|
||||||
case Color::Cyan: return "cyan";
|
case Colors::Cyan: return "cyan";
|
||||||
case Color::White: return "white";
|
case Colors::White: return "white";
|
||||||
|
case Colors::RGB:
|
||||||
|
{
|
||||||
|
char buffer[11];
|
||||||
|
sprintf(buffer, "rgb:%02x%02x%02x", color.r, color.g, color.b);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
kak_assert(false);
|
kak_assert(false);
|
||||||
return "default";
|
return "default";
|
||||||
|
|
23
src/color.hh
23
src/color.hh
|
@ -8,7 +8,7 @@ namespace Kakoune
|
||||||
|
|
||||||
class String;
|
class String;
|
||||||
|
|
||||||
enum class Color : char
|
enum class Colors : char
|
||||||
{
|
{
|
||||||
Default,
|
Default,
|
||||||
Black,
|
Black,
|
||||||
|
@ -18,7 +18,26 @@ enum class Color : char
|
||||||
Blue,
|
Blue,
|
||||||
Magenta,
|
Magenta,
|
||||||
Cyan,
|
Cyan,
|
||||||
White
|
White,
|
||||||
|
RGB,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Color
|
||||||
|
{
|
||||||
|
Colors color;
|
||||||
|
unsigned char r;
|
||||||
|
unsigned char g;
|
||||||
|
unsigned char b;
|
||||||
|
|
||||||
|
Color() : Color{Colors::Default} {}
|
||||||
|
Color(Colors c) : color{c}, r{0}, g{0}, b{0} {}
|
||||||
|
Color(unsigned char r, unsigned char g, unsigned char b)
|
||||||
|
: color{Colors::RGB}, r{r}, g{g}, b{b} {}
|
||||||
|
|
||||||
|
bool operator==(const Color& c) const
|
||||||
|
{ return color == c.color and r == c.r and g == c.g and b == c.b; }
|
||||||
|
bool operator!=(const Color& c) const
|
||||||
|
{ return color != c.color or r != c.r or g != c.g or b != c.b; }
|
||||||
};
|
};
|
||||||
|
|
||||||
using ColorPair = std::pair<Color, Color>;
|
using ColorPair = std::pair<Color, Color>;
|
||||||
|
|
|
@ -15,7 +15,7 @@ const ColorPair& ColorRegistry::operator[](const String& colordesc)
|
||||||
ColorPair colpair{ str_to_color(String(colordesc.begin(), it)),
|
ColorPair colpair{ str_to_color(String(colordesc.begin(), it)),
|
||||||
it != colordesc.end() ?
|
it != colordesc.end() ?
|
||||||
str_to_color(String(it+1, colordesc.end()))
|
str_to_color(String(it+1, colordesc.end()))
|
||||||
: Color::Default };
|
: Colors::Default };
|
||||||
|
|
||||||
return (m_aliases[colordesc] = colpair);
|
return (m_aliases[colordesc] = colpair);
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ void ColorRegistry::register_alias(const String& name, const String& colordesc,
|
||||||
|
|
||||||
auto it = std::find(colordesc.begin(), colordesc.end(), ',');
|
auto it = std::find(colordesc.begin(), colordesc.end(), ',');
|
||||||
auto fg = str_to_color(String(colordesc.begin(), it));
|
auto fg = str_to_color(String(colordesc.begin(), it));
|
||||||
auto bg = Color::Default;
|
auto bg = Color{Colors::Default};
|
||||||
if (it != colordesc.end())
|
if (it != colordesc.end())
|
||||||
bg = str_to_color(String(it+1, colordesc.end()));
|
bg = str_to_color(String(it+1, colordesc.end()));
|
||||||
|
|
||||||
|
@ -41,18 +41,18 @@ void ColorRegistry::register_alias(const String& name, const String& colordesc,
|
||||||
|
|
||||||
ColorRegistry::ColorRegistry()
|
ColorRegistry::ColorRegistry()
|
||||||
: m_aliases{
|
: m_aliases{
|
||||||
{ "PrimarySelection", { Color::Cyan, Color::Blue } },
|
{ "PrimarySelection", { Colors::Cyan, Colors::Blue } },
|
||||||
{ "SecondarySelection", { Color::Black, Color::Blue } },
|
{ "SecondarySelection", { Colors::Black, Colors::Blue } },
|
||||||
{ "PrimaryCursor", { Color::Black, Color::White } },
|
{ "PrimaryCursor", { Colors::Black, Colors::White } },
|
||||||
{ "SecondaryCursor", { Color::Black, Color::White } },
|
{ "SecondaryCursor", { Colors::Black, Colors::White } },
|
||||||
{ "LineNumbers", { Color::Black, Color::White } },
|
{ "LineNumbers", { Colors::Black, Colors::White } },
|
||||||
{ "MenuForeground", { Color::Blue, Color::Cyan } },
|
{ "MenuForeground", { Colors::Blue, Colors::Cyan } },
|
||||||
{ "MenuBackground", { Color::Cyan, Color::Blue } },
|
{ "MenuBackground", { Colors::Cyan, Colors::Blue } },
|
||||||
{ "Information", { Color::Black, Color::Yellow } },
|
{ "Information", { Colors::Black, Colors::Yellow } },
|
||||||
{ "Error", { Color::Black, Color::Red } },
|
{ "Error", { Colors::Black, Colors::Red } },
|
||||||
{ "StatusLine", { Color::Cyan, Color::Default } },
|
{ "StatusLine", { Colors::Cyan, Colors::Default } },
|
||||||
{ "StatusCursor", { Color::Black, Color::Cyan } },
|
{ "StatusCursor", { Colors::Black, Colors::Cyan } },
|
||||||
{ "Prompt", { Color::Yellow, Color::Default} },
|
{ "Prompt", { Colors::Yellow, Colors::Default} },
|
||||||
}
|
}
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
|
|
@ -115,7 +115,7 @@ struct DisplayAtom
|
||||||
AtomContent content;
|
AtomContent content;
|
||||||
|
|
||||||
DisplayAtom(AtomContent content,
|
DisplayAtom(AtomContent content,
|
||||||
ColorPair colors = {Color::Default, Color::Default},
|
ColorPair colors = {Colors::Default, Colors::Default},
|
||||||
Attribute attribute = Normal)
|
Attribute attribute = Normal)
|
||||||
: content{std::move(content)}, colors{colors}, attribute{attribute}
|
: content{std::move(content)}, colors{colors}, attribute{attribute}
|
||||||
{}
|
{}
|
||||||
|
|
|
@ -323,7 +323,7 @@ void expand_unprintable(DisplayBuffer& display_buffer)
|
||||||
if ((it+1).underlying_iterator() != atom_it->content.end())
|
if ((it+1).underlying_iterator() != atom_it->content.end())
|
||||||
atom_it = line.split(atom_it, (it+1).underlying_iterator());
|
atom_it = line.split(atom_it, (it+1).underlying_iterator());
|
||||||
atom_it->content.replace(str);
|
atom_it->content.replace(str);
|
||||||
atom_it->colors = { Color::Red, Color::Black };
|
atom_it->colors = { Colors::Red, Colors::Black };
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -361,7 +361,7 @@ public:
|
||||||
String content = it != lines.end() ? std::get<2>(*it) : empty;
|
String content = it != lines.end() ? std::get<2>(*it) : empty;
|
||||||
content += String(' ', width - content.char_length());
|
content += String(' ', width - content.char_length());
|
||||||
DisplayAtom atom{AtomContent(std::move(content))};
|
DisplayAtom atom{AtomContent(std::move(content))};
|
||||||
atom.colors = { it != lines.end() ? std::get<1>(*it) : Color::Default , m_bg };
|
atom.colors = { it != lines.end() ? std::get<1>(*it) : Colors::Default , m_bg };
|
||||||
line.insert(line.begin(), std::move(atom));
|
line.insert(line.begin(), std::move(atom));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,22 +26,73 @@ static void set_attribute(int attribute, bool on)
|
||||||
attroff(attribute);
|
attroff(attribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nc_color(Color color)
|
static bool operator<(const Color& lhs, const Color& rhs)
|
||||||
{
|
{
|
||||||
switch (color)
|
if (lhs.color == rhs.color and lhs.color == Colors::RGB)
|
||||||
{
|
return lhs.r == rhs.r ? (lhs.g == rhs.g ? lhs.b < rhs.b
|
||||||
case Color::Black: return COLOR_BLACK;
|
: lhs.g < rhs.g)
|
||||||
case Color::Red: return COLOR_RED;
|
: lhs.r < rhs.r;
|
||||||
case Color::Green: return COLOR_GREEN;
|
return lhs.color < rhs.color;
|
||||||
case Color::Yellow: return COLOR_YELLOW;
|
}
|
||||||
case Color::Blue: return COLOR_BLUE;
|
|
||||||
case Color::Magenta: return COLOR_MAGENTA;
|
|
||||||
case Color::Cyan: return COLOR_CYAN;
|
|
||||||
case Color::White: return COLOR_WHITE;
|
|
||||||
|
|
||||||
case Color::Default:
|
static int nc_color(const Color& color)
|
||||||
default:
|
{
|
||||||
return -1;
|
static std::map<Color, int> colors = {
|
||||||
|
{ Colors::Default, -1 },
|
||||||
|
{ Colors::Black, COLOR_BLACK },
|
||||||
|
{ Colors::Red, COLOR_RED },
|
||||||
|
{ Colors::Green, COLOR_GREEN },
|
||||||
|
{ Colors::Yellow, COLOR_YELLOW },
|
||||||
|
{ Colors::Blue, COLOR_BLUE },
|
||||||
|
{ Colors::Magenta, COLOR_MAGENTA },
|
||||||
|
{ Colors::Cyan, COLOR_CYAN },
|
||||||
|
{ Colors::White, COLOR_WHITE },
|
||||||
|
};
|
||||||
|
static int next_color = 8;
|
||||||
|
|
||||||
|
auto it = colors.find(color);
|
||||||
|
if (it != colors.end())
|
||||||
|
return it->second;
|
||||||
|
else if (can_change_color() and COLORS > 8)
|
||||||
|
{
|
||||||
|
kak_assert(color.color == Colors::RGB);
|
||||||
|
if (next_color > COLORS)
|
||||||
|
next_color = 8;
|
||||||
|
init_color(next_color,
|
||||||
|
color.r * 1000 / 255,
|
||||||
|
color.g * 1000 / 255,
|
||||||
|
color.b * 1000 / 255);
|
||||||
|
colors[color] = next_color;
|
||||||
|
return next_color++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
kak_assert(color.color == Colors::RGB);
|
||||||
|
// project to closest color.
|
||||||
|
struct BuiltinColor { int id; unsigned char r, g, b; };
|
||||||
|
static constexpr BuiltinColor builtins[] = {
|
||||||
|
{ COLOR_BLACK, 0, 0, 0 },
|
||||||
|
{ COLOR_RED, 255, 0, 0 },
|
||||||
|
{ COLOR_GREEN, 0, 255, 0 },
|
||||||
|
{ COLOR_YELLOW, 255, 255, 0 },
|
||||||
|
{ COLOR_BLUE, 0, 0, 255 },
|
||||||
|
{ COLOR_MAGENTA, 255, 0, 255 },
|
||||||
|
{ COLOR_CYAN, 0, 255, 255 },
|
||||||
|
{ COLOR_WHITE, 255, 255, 255 }
|
||||||
|
};
|
||||||
|
auto sq = [](int x) { return x * x; };
|
||||||
|
int lowestDist = INT_MAX;
|
||||||
|
int closestCol = -1;
|
||||||
|
for (auto& col : builtins)
|
||||||
|
{
|
||||||
|
int dist = sq(color.r - col.r) + sq(color.g - col.g) + sq(color.b - col.b);
|
||||||
|
if (dist < lowestDist)
|
||||||
|
{
|
||||||
|
lowestDist = dist;
|
||||||
|
closestCol = col.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return closestCol;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,7 +119,7 @@ static void set_color(WINDOW* window, const ColorPair colors)
|
||||||
if (current_pair != -1)
|
if (current_pair != -1)
|
||||||
wattroff(window, COLOR_PAIR(current_pair));
|
wattroff(window, COLOR_PAIR(current_pair));
|
||||||
|
|
||||||
if (colors.first != Color::Default or colors.second != Color::Default)
|
if (colors.first != Colors::Default or colors.second != Colors::Default)
|
||||||
{
|
{
|
||||||
current_pair = get_color_pair(colors);
|
current_pair = get_color_pair(colors);
|
||||||
wattron(window, COLOR_PAIR(current_pair));
|
wattron(window, COLOR_PAIR(current_pair));
|
||||||
|
@ -202,7 +253,7 @@ void NCursesUI::draw(const DisplayBuffer& display_buffer,
|
||||||
set_attribute(A_REVERSE, 0);
|
set_attribute(A_REVERSE, 0);
|
||||||
set_attribute(A_BLINK, 0);
|
set_attribute(A_BLINK, 0);
|
||||||
set_attribute(A_BOLD, 0);
|
set_attribute(A_BOLD, 0);
|
||||||
set_color(stdscr, { Color::Blue, Color::Default });
|
set_color(stdscr, { Colors::Blue, Colors::Default });
|
||||||
for (;line_index < m_dimensions.line; ++line_index)
|
for (;line_index < m_dimensions.line; ++line_index)
|
||||||
{
|
{
|
||||||
move((int)line_index, 0);
|
move((int)line_index, 0);
|
||||||
|
@ -321,7 +372,7 @@ void NCursesUI::draw_menu()
|
||||||
auto menu_fg = get_color_pair(m_menu_fg);
|
auto menu_fg = get_color_pair(m_menu_fg);
|
||||||
auto menu_bg = get_color_pair(m_menu_bg);
|
auto menu_bg = get_color_pair(m_menu_bg);
|
||||||
|
|
||||||
auto scroll_fg = get_color_pair({ Color::White, Color::White });
|
auto scroll_fg = get_color_pair({ Colors::White, Colors::White });
|
||||||
auto scroll_bg = get_color_pair(m_menu_bg);
|
auto scroll_bg = get_color_pair(m_menu_bg);
|
||||||
|
|
||||||
wattron(m_menu_win, COLOR_PAIR(menu_bg));
|
wattron(m_menu_win, COLOR_PAIR(menu_bg));
|
||||||
|
|
|
@ -75,6 +75,17 @@ public:
|
||||||
write(memoryview<T>(vec));
|
write(memoryview<T>(vec));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void write(const Color& color)
|
||||||
|
{
|
||||||
|
write(color.color);
|
||||||
|
if (color.color == Colors::RGB)
|
||||||
|
{
|
||||||
|
write(color.r);
|
||||||
|
write(color.g);
|
||||||
|
write(color.b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void write(const ColorPair& colors)
|
void write(const ColorPair& colors)
|
||||||
{
|
{
|
||||||
write(colors.first);
|
write(colors.first);
|
||||||
|
@ -149,6 +160,20 @@ std::vector<T> read_vector(int socket)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
Color read<Color>(int socket)
|
||||||
|
{
|
||||||
|
Color res;
|
||||||
|
res.color = read<Colors>(socket);
|
||||||
|
if (res.color == Colors::RGB)
|
||||||
|
{
|
||||||
|
res.r = read<unsigned char>(socket);
|
||||||
|
res.g = read<unsigned char>(socket);
|
||||||
|
res.b = read<unsigned char>(socket);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
ColorPair read<ColorPair>(int socket)
|
ColorPair read<ColorPair>(int socket)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue
Block a user