Add support CSS color text for class nana::color

This commit is contained in:
cnjinhao 2015-02-06 01:45:17 +08:00
parent fd5ff571a7
commit 81a50fd84f
6 changed files with 267 additions and 19 deletions

View File

@ -306,8 +306,16 @@ namespace nana
color(unsigned red, unsigned green, unsigned blue);
color(unsigned red, unsigned green, unsigned blue, double alpha);
/// Initializes the color with a CSS-like rgb string.
color(std::string css_rgb);
color& alpha(double); ///< Sets alpha channel
color& from_rgb(unsigned red, unsigned green, unsigned blue); ///< immutable alpha channel
/// Sets color with a HSL value.
/// @param hue in range of [0, 360]
/// @param saturation in range of [0, 1]
/// @param lightness in range of [0, 1]
color& from_hsl(double hue, double saturation, double lightness); ///< immutable alpha channel
color blend(const color& bgcolor, bool ignore_bgcolor_alpha) const;

View File

@ -42,6 +42,14 @@ namespace nana
std::size_t strlen(const char_t* str);
double strtod(const char_t* str, char_t** endptr);
char_t* strcpy(char_t* dest, const char_t* source);
//Workaround for no implemenation of std::stoi in MinGW.
int stoi(const std::string&, std::size_t * pos = nullptr, int base = 10);
int stoi(const std::wstring&, std::size_t* pos = nullptr, int base = 10);
//Workaround for no implemenation of std::stod in MinGW.
double stod(const std::string&, std::size_t * pos = nullptr);
double stod(const std::wstring&, std::size_t* pos = nullptr);
}
#if defined(NANA_WINDOWS)

View File

@ -11,6 +11,7 @@
*/
#include <nana/basic_types.hpp>
#include <regex>
namespace nana
{
@ -58,6 +59,168 @@ namespace nana
a_ = 1.0;
}
//Initializes the color with a CSS-like string
//contributor: BigDave(mortis2007 at hotmail co uk)
//date: February 3, 2015
//maintainor: Jinhao, extended the support of CSS-spec
color::color(std::string css_color)
: a_(1.0)
{
const char * excpt_what = "color: invalid rgb format";
auto pos = css_color.find_first_not_of(' ');
if (pos == css_color.npos)
throw std::invalid_argument(excpt_what);
if ('#' == css_color[pos])
{
if (css_color.size() < pos + 4)
throw std::invalid_argument(excpt_what);
auto endpos = css_color.find_first_not_of("0123456789abcdefABCDEF", pos + 1);
if (endpos == css_color.npos)
endpos = static_cast<decltype(endpos)>(css_color.size());
if ((endpos - pos != 4) && (endpos - pos != 7))
throw std::invalid_argument(excpt_what);
auto n = ::nana::stoi(css_color.substr(pos + 1, endpos - pos - 1), nullptr, 16);
if (endpos - pos == 4)
{
r_ = ((0xF00 & n) >> 4) | ((0xF00 & n) >> 8);
g_ = (0xF0 & n) | ((0xF0 & n) >> 4);
b_ = (0xF & n) | ((0xF & n) << 4);
}
else
{
r_ = (0xFF0000 & n) >> 16;
g_ = (0xFF00 & n) >> 8;
b_ = (0xFF & n);
}
return;
}
std::transform(css_color.begin(), css_color.end(), css_color.begin(), std::tolower);
auto endpos = css_color.find(' ', pos + 1);
if (endpos == css_color.npos)
endpos = css_color.size();
if ((endpos - pos == 11) && (css_color.substr(pos, 11) == "transparent"))
{
r_ = 0;
g_ = 0;
b_ = 0;
a_ = 0;
return;
}
auto type_end = css_color.find_first_of(" (", pos + 1);
if (type_end == css_color.npos || ((type_end - pos != 3) && (type_end - pos != 4))) //rgb/hsl = 3, rgba/hsla = 4
throw std::invalid_argument(excpt_what);
bool has_alpha = false;
if (type_end - pos == 4) //maybe rgba/hsla
{
if (css_color[pos + 3] != 'a')
throw std::invalid_argument(excpt_what);
has_alpha = true;
}
std::regex pat;
std::regex_iterator<std::string::iterator> i, end;
auto type_name = css_color.substr(pos, 3);
if ("rgb" == type_name)
{
pat.assign("(\\d*\\.)?\\d+\\%?");
i = std::regex_iterator<std::string::iterator>(css_color.begin() + pos, css_color.end(), pat);
if (i == end)
throw std::invalid_argument(excpt_what);
std::vector<std::string> rgb;
rgb.emplace_back(i->str());
bool is_real;
if (is_real = (rgb.back().back() == '%'))
pat.assign("(\\d*\\.)?\\d+\\%");
else
pat.assign("\\d+");
for (++i; i != end; ++i)
{
rgb.emplace_back(i->str());
if (rgb.size() == 3)
break;
}
if (rgb.size() != 3)
throw std::invalid_argument(excpt_what);
if (is_real)
{
auto pr = ::nana::stod(rgb[0].substr(0, rgb[0].size() - 1));
r_ = (pr > 100 ? 255.0 : 2.55 * pr);
pr = ::nana::stod(rgb[1].substr(0, rgb[1].size() - 1));
g_ = (pr > 100 ? 255.0 : 2.55 * pr);
pr = ::nana::stod(rgb[2].substr(0, rgb[2].size() - 1));
b_ = (pr > 100 ? 255.0 : 2.55 * pr);
}
else
{
r_ = ::nana::stod(rgb[0]);
if (r_ > 255.0) r_ = 255;
g_ = ::nana::stod(rgb[1]);
if (g_ > 255.0) g_ = 255;
b_ = ::nana::stod(rgb[2]);
if (b_ > 255.0) b_ = 255;
}
}
else if ("hsl" == type_name)
{
pat.assign("(\\d*\\.)?\\d+");
i = std::regex_iterator<std::string::iterator>(css_color.begin() + pos, css_color.end(), pat);
if (i == end)
throw std::invalid_argument(excpt_what);
auto h = ::nana::stod(i->str());
pat.assign("(\\d*\\.)?\\d+\\%");
if (++i == end)
throw std::invalid_argument(excpt_what);
auto str = i->str();
auto s = ::nana::stod(str.substr(0, str.size() - 1));
if (++i == end)
throw std::invalid_argument(excpt_what);
str = i->str();
auto l = ::nana::stod(str.substr(0, str.size() - 1));
from_hsl(h, s / 100, l / 100);
}
else
throw std::invalid_argument(excpt_what); //invalid color type
if (has_alpha)
{
pat.assign("(\\d*\\.)?\\d+");
if (++i == end)
throw std::invalid_argument(excpt_what); //invalid alpha value
a_ = ::nana::stod(i->str());
}
}
color& color::from_rgb(unsigned red, unsigned green, unsigned blue)
{
r_ = red;
@ -97,6 +260,7 @@ namespace nana
double var1 = 2.0 * lightness - var2;
hue /= 360;
r_ = 255.0 * rgb_from_hue(var1, var2, hue + 0.33333);
g_ = 255.0 * rgb_from_hue(var1, var2, hue);
b_ = 255.0 * rgb_from_hue(var1, var2, hue - 0.33333);

View File

@ -12,7 +12,8 @@
*/
#include <nana/deploy.hpp>
#include <cstdlib>
#include <exception>
#if defined(NANA_WINDOWS)
#include <windows.h>
#elif defined(NANA_LINUX)
@ -49,6 +50,88 @@ namespace nana
#endif
}
int stoi(const std::string& str, std::size_t * pos, int base)
{
#if defined(NANA_MINGW)
auto sptr = str.c_str();
char *end;
errno = 0;
auto result = std::strtol(sptr, &end, base);
if (sptr == end)
throw std::invalid_argument("invalid stoi argument");
if (errno == ERANGE)
throw std::out_of_range("stoi argument out of range");
if (pos)
*pos = (std::size_t)(end - sptr);
return ((int)result);
#else
return std::stoi(str, pos, base);
#endif
}
int stoi(const std::wstring& str, std::size_t* pos, int base)
{
#if defined(NANA_MINGW)
auto sptr = str.data();
wchar_t *end;
errno = 0;
auto result = std::wcstol(sptr, &end, base);
if (sptr == end)
throw std::invalid_argument("invalid stoi argument");
if (errno == ERANGE)
throw std::out_of_range("stoi argument out of range");
if (pos)
*pos = (std::size_t)(end - sptr);
return ((int)result);
#else
return std::stoi(str, pos, base);
#endif
}
double stod(const std::string& str, std::size_t * pos)
{
#ifdef NANA_MINGW
auto *ptr = str.data();
errno = 0;
char *end;
auto result = std::strtod(ptr, &end);
if (ptr == end)
throw std::invalid_argument("invalid stod argument");
if (errno == ERANGE)
throw std::out_of_range("stod argument out of range");
if (pos)
*pos = (std::size_t)(end - ptr);
return result;
#else
return std::stod(str, pos);
#endif
}
double stod(const std::wstring& str, std::size_t* pos)
{
#ifdef NANA_MINGW
auto *ptr = str.data();
errno = 0;
wchar_t *end;
auto result = std::wcstod(ptr, &end);
if (ptr == end)
throw std::invalid_argument("invalid stod argument");
if (errno == ERANGE)
throw std::out_of_range("stod argument out of range");
if (pos)
*pos = (std::size_t)(end - ptr);
return result;
#else
return std::stod(str, pos);
#endif
}
bool is_incomplete(const nana::string& str, unsigned pos)
{
#ifndef NANA_UNICODE

View File

@ -611,22 +611,12 @@ namespace nana
int spinbox::to_int() const
{
//std::stoi is not defined by MinGW
std::wstringstream ss;
ss << value();
int n = 0;
ss >> n;
return n;
return ::nana::stoi(value());
}
double spinbox::to_double() const
{
//std::stod is not defined by MinGW
std::wstringstream ss;
ss << value();
double d = 0;
ss >> d;
return d;
return ::nana::stod(value());
}
void spinbox::set_accept(std::function<bool(::nana::char_t)> pred)

View File

@ -343,12 +343,7 @@ namespace nana
erase_n = str.size() - offset;
//If there is not a parameter for %argNNN, the %argNNN will be erased.
//a workaround, MinGW does not provide std::stoi
std::wstringstream ss;
std::size_t arg;
ss<<str.substr(offset + 4, arg_n);
ss>>arg;
std::size_t arg = static_cast<std::size_t>(::nana::stoi(str.substr(offset + 4, arg_n)));
if (arg_strs && arg < arg_strs->size())
str.replace(offset, erase_n, (*arg_strs)[arg]);