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 <stdlib.h>
|
||||
|
||||
namespace Kakoune
|
||||
{
|
||||
|
||||
Color str_to_color(const String& color)
|
||||
{
|
||||
if (color == "default") return Color::Default;
|
||||
if (color == "black") return Color::Black;
|
||||
if (color == "red") return Color::Red;
|
||||
if (color == "green") return Color::Green;
|
||||
if (color == "yellow") return Color::Yellow;
|
||||
if (color == "blue") return Color::Blue;
|
||||
if (color == "magenta") return Color::Magenta;
|
||||
if (color == "cyan") return Color::Cyan;
|
||||
if (color == "white") return Color::White;
|
||||
if (color == "default") return Colors::Default;
|
||||
if (color == "black") return Colors::Black;
|
||||
if (color == "red") return Colors::Red;
|
||||
if (color == "green") return Colors::Green;
|
||||
if (color == "yellow") return Colors::Yellow;
|
||||
if (color == "blue") return Colors::Blue;
|
||||
if (color == "magenta") return Colors::Magenta;
|
||||
if (color == "cyan") return Colors::Cyan;
|
||||
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 + "'");
|
||||
return Color::Default;
|
||||
return Colors::Default;
|
||||
}
|
||||
|
||||
String color_to_str(const Color& color)
|
||||
{
|
||||
switch (color)
|
||||
switch (color.color)
|
||||
{
|
||||
case Color::Default: return "default";
|
||||
case Color::Black: return "black";
|
||||
case Color::Red: return "red";
|
||||
case Color::Green: return "green";
|
||||
case Color::Yellow: return "yellow";
|
||||
case Color::Blue: return "blue";
|
||||
case Color::Magenta: return "magenta";
|
||||
case Color::Cyan: return "cyan";
|
||||
case Color::White: return "white";
|
||||
case Colors::Default: return "default";
|
||||
case Colors::Black: return "black";
|
||||
case Colors::Red: return "red";
|
||||
case Colors::Green: return "green";
|
||||
case Colors::Yellow: return "yellow";
|
||||
case Colors::Blue: return "blue";
|
||||
case Colors::Magenta: return "magenta";
|
||||
case Colors::Cyan: return "cyan";
|
||||
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);
|
||||
return "default";
|
||||
|
|
23
src/color.hh
23
src/color.hh
|
@ -8,7 +8,7 @@ namespace Kakoune
|
|||
|
||||
class String;
|
||||
|
||||
enum class Color : char
|
||||
enum class Colors : char
|
||||
{
|
||||
Default,
|
||||
Black,
|
||||
|
@ -18,7 +18,26 @@ enum class Color : char
|
|||
Blue,
|
||||
Magenta,
|
||||
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>;
|
||||
|
|
|
@ -15,7 +15,7 @@ const ColorPair& ColorRegistry::operator[](const String& colordesc)
|
|||
ColorPair colpair{ str_to_color(String(colordesc.begin(), it)),
|
||||
it != colordesc.end() ?
|
||||
str_to_color(String(it+1, colordesc.end()))
|
||||
: Color::Default };
|
||||
: Colors::Default };
|
||||
|
||||
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 fg = str_to_color(String(colordesc.begin(), it));
|
||||
auto bg = Color::Default;
|
||||
auto bg = Color{Colors::Default};
|
||||
if (it != 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()
|
||||
: m_aliases{
|
||||
{ "PrimarySelection", { Color::Cyan, Color::Blue } },
|
||||
{ "SecondarySelection", { Color::Black, Color::Blue } },
|
||||
{ "PrimaryCursor", { Color::Black, Color::White } },
|
||||
{ "SecondaryCursor", { Color::Black, Color::White } },
|
||||
{ "LineNumbers", { Color::Black, Color::White } },
|
||||
{ "MenuForeground", { Color::Blue, Color::Cyan } },
|
||||
{ "MenuBackground", { Color::Cyan, Color::Blue } },
|
||||
{ "Information", { Color::Black, Color::Yellow } },
|
||||
{ "Error", { Color::Black, Color::Red } },
|
||||
{ "StatusLine", { Color::Cyan, Color::Default } },
|
||||
{ "StatusCursor", { Color::Black, Color::Cyan } },
|
||||
{ "Prompt", { Color::Yellow, Color::Default} },
|
||||
{ "PrimarySelection", { Colors::Cyan, Colors::Blue } },
|
||||
{ "SecondarySelection", { Colors::Black, Colors::Blue } },
|
||||
{ "PrimaryCursor", { Colors::Black, Colors::White } },
|
||||
{ "SecondaryCursor", { Colors::Black, Colors::White } },
|
||||
{ "LineNumbers", { Colors::Black, Colors::White } },
|
||||
{ "MenuForeground", { Colors::Blue, Colors::Cyan } },
|
||||
{ "MenuBackground", { Colors::Cyan, Colors::Blue } },
|
||||
{ "Information", { Colors::Black, Colors::Yellow } },
|
||||
{ "Error", { Colors::Black, Colors::Red } },
|
||||
{ "StatusLine", { Colors::Cyan, Colors::Default } },
|
||||
{ "StatusCursor", { Colors::Black, Colors::Cyan } },
|
||||
{ "Prompt", { Colors::Yellow, Colors::Default} },
|
||||
}
|
||||
{}
|
||||
|
||||
|
|
|
@ -115,7 +115,7 @@ struct DisplayAtom
|
|||
AtomContent content;
|
||||
|
||||
DisplayAtom(AtomContent content,
|
||||
ColorPair colors = {Color::Default, Color::Default},
|
||||
ColorPair colors = {Colors::Default, Colors::Default},
|
||||
Attribute attribute = Normal)
|
||||
: 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())
|
||||
atom_it = line.split(atom_it, (it+1).underlying_iterator());
|
||||
atom_it->content.replace(str);
|
||||
atom_it->colors = { Color::Red, Color::Black };
|
||||
atom_it->colors = { Colors::Red, Colors::Black };
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -361,7 +361,7 @@ public:
|
|||
String content = it != lines.end() ? std::get<2>(*it) : empty;
|
||||
content += String(' ', width - content.char_length());
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,22 +26,73 @@ static void set_attribute(int attribute, bool on)
|
|||
attroff(attribute);
|
||||
}
|
||||
|
||||
static int nc_color(Color color)
|
||||
static bool operator<(const Color& lhs, const Color& rhs)
|
||||
{
|
||||
switch (color)
|
||||
{
|
||||
case Color::Black: return COLOR_BLACK;
|
||||
case Color::Red: return COLOR_RED;
|
||||
case Color::Green: return COLOR_GREEN;
|
||||
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;
|
||||
if (lhs.color == rhs.color and lhs.color == Colors::RGB)
|
||||
return lhs.r == rhs.r ? (lhs.g == rhs.g ? lhs.b < rhs.b
|
||||
: lhs.g < rhs.g)
|
||||
: lhs.r < rhs.r;
|
||||
return lhs.color < rhs.color;
|
||||
}
|
||||
|
||||
case Color::Default:
|
||||
default:
|
||||
return -1;
|
||||
static int nc_color(const Color& color)
|
||||
{
|
||||
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)
|
||||
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);
|
||||
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_BLINK, 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)
|
||||
{
|
||||
move((int)line_index, 0);
|
||||
|
@ -321,7 +372,7 @@ void NCursesUI::draw_menu()
|
|||
auto menu_fg = get_color_pair(m_menu_fg);
|
||||
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);
|
||||
|
||||
wattron(m_menu_win, COLOR_PAIR(menu_bg));
|
||||
|
|
|
@ -75,6 +75,17 @@ public:
|
|||
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)
|
||||
{
|
||||
write(colors.first);
|
||||
|
@ -149,6 +160,20 @@ std::vector<T> read_vector(int socket)
|
|||
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<>
|
||||
ColorPair read<ColorPair>(int socket)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue
Block a user