support specifying colors with RGB components

This commit is contained in:
Maxime Coste 2013-05-07 18:52:23 +02:00
parent d80815b927
commit 56ab33c9d6
7 changed files with 168 additions and 56 deletions

View File

@ -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";

View File

@ -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>;

View File

@ -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} },
} }
{} {}

View File

@ -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}
{} {}

View File

@ -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));
} }
} }

View File

@ -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));

View File

@ -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)
{ {