From ed1204cea6f0f041fa534f0ecd89c76161667a99 Mon Sep 17 00:00:00 2001 From: jkriege2 Date: Fri, 21 Jul 2023 19:25:46 +0200 Subject: [PATCH] NEW: You can use additional syntax derived from CCS to define colors in style.ini.files (or when using jkqtp_String2QColor() ): You can use full CSS-color syntax with functions "rgb(R,G,B)", "rgba(...)", "hsl(...)", "hsv(...)", "gray(...)", "green(...)", "red(...)", "blue(...)" --- lib/jkqtcommon/jkqtpstringtools.cpp | 202 ++++++++++++++++++++++++++-- lib/jkqtcommon/jkqtpstringtools.h | 23 +++- 2 files changed, 208 insertions(+), 17 deletions(-) diff --git a/lib/jkqtcommon/jkqtpstringtools.cpp b/lib/jkqtcommon/jkqtpstringtools.cpp index fc0a73fbd8..eee2e77d03 100644 --- a/lib/jkqtcommon/jkqtpstringtools.cpp +++ b/lib/jkqtcommon/jkqtpstringtools.cpp @@ -363,6 +363,7 @@ std::string jkqtp_booltostr(bool data){ # undef rgb #endif #define rgb(r,g,b) (0xff000000 | (r << 16) | (g << 8) | b) +#define rgba(r,g,b,a) ((a << 24) | (r << 16) | (g << 8) | b) #define gray(g) rgb(g,g,g) #define gray_p(p) gray(static_cast(g/100.0*255.0)) @@ -423,6 +424,15 @@ static const struct RGBData { { "ghostwhite", rgb(248, 248, 255) }, { "gold", rgb(255, 215, 0) }, { "goldenrod", rgb(218, 165, 32) }, + { "gray10", gray(255*1/10) }, + { "gray20", gray(255*2/10) }, + { "gray30", gray(255*3/10) }, + { "gray40", gray(255*4/10) }, + { "gray50", gray(255*5/10) }, + { "gray60", gray(255*6/10) }, + { "gray70", gray(255*7/10) }, + { "gray80", gray(255*8/10) }, + { "gray90", gray(255*9/10) }, { "gray", rgb(128, 128, 128) }, { "green", rgb( 0, 128, 0) }, { "greenyellow", rgb(173, 255, 47) }, @@ -510,7 +520,7 @@ static const struct RGBData { { "teal", rgb( 0, 128, 128) }, { "thistle", rgb(216, 191, 216) }, { "tomato", rgb(255, 99, 71) }, - { "transparent", 0 }, + { "transparent", rgba(0,0,0,0) }, { "turquoise", rgb( 64, 224, 208) }, { "violet", rgb(238, 130, 238) }, { "wheat", rgb(245, 222, 179) }, @@ -540,6 +550,7 @@ QString jkqtp_rgbtostring(unsigned char r, unsigned char g, unsigned char b, uns return rgbTbl[i].name; } } + if (r==g && r==b) return QString("grey%1").arg(static_cast(static_cast(r)/255.0*100.0)); return QString("#%1%2%3").arg(static_cast(r), 2,16,QLatin1Char('0')).arg(static_cast(g), 2,16,QLatin1Char('0')).arg(static_cast(b), 2,16,QLatin1Char('0')); } // if we reach this, we have an unnamed transparent color @@ -562,7 +573,8 @@ QString jkqtp_QColor2String(QColor color, bool useSpecialTransparencySyntax) { } -QColor jkqtp_lookupQColorName(const QString &color) { +QColor jkqtp_lookupQColorName(const QString &color, bool namesOnly, bool *nameFound) { + if (nameFound) *nameFound=true; const QString col=color.toLower().trimmed(); if (col=="window") return QGuiApplication::palette().color(QPalette::Window); if (col=="windowtext") return QGuiApplication::palette().color(QPalette::WindowText); @@ -590,40 +602,203 @@ QColor jkqtp_lookupQColorName(const QString &color) { for (int i=0; i(0, QLocale::c().toDouble(v)/100.0*intMax, intMax); + } else if (unit=="deg") { + int vv=qBound(0, QLocale::c().toDouble(v), intMax);; + if (vv<0) vv=vv+static_cast(qCeil(static_cast(-vv)/360.0)*360.0); + return vv; + } + return qBound(0, QLocale::c().toDouble(v), intMax); + }; + static auto valUnitToAlphaInt=[](const QString& v, const QString& unit="", int intMax=255) { + if (v.isEmpty()) return -1; + if (unit=="%") { + return intMax-qBound(0, QLocale::c().toDouble(v)/100.0*intMax, intMax); + } else if (unit=="deg") { + int vv=qBound(0, QLocale::c().toDouble(v), intMax);; + if (vv<0) vv=vv+static_cast(qCeil(static_cast(-vv)/360.0)*360.0); + return vv; + } + return qBound(0, QLocale::c().toDouble(v), intMax); + }; + + // now we check for diverse special syntaxes + // P: "color,NN%" NN=TRANSPARENCY in percent + // AP: "color,aNN\%" NN=ALPHA in percent + // NP: "color,[a]NN\%" NN=ALPHA 0..255 + // Frac: "grey25" + // ColMod: "rgb(R,G,B)", "hsl(H,S,V)" ... (CSS-Syntax) #if (QT_VERSION>=QT_VERSION_CHECK(6, 0, 0)) - QRegularExpression rxP("(.+)\\s*,\\s*t?\\s*(\\d+\\.?\\d+)\\%"); - QRegularExpression rxAP("(.+)\\s*,\\s*a\\s*(\\d+\\.?\\d+)\\%"); - QRegularExpression rxNP("(.+)\\s*,\\s*a?\\s*([\\d]+)"); + static QRegularExpression rxP("(.+)\\s*,\\s*t?\\s*(\\d+\\.?\\d+)\\%"); + static QRegularExpression rxFrac("([a-zA-Z]{3,})(\\d{1,3})\\%?"); + static QRegularExpression rxColMod("\\s*(rgb|hsl|hsv|rgba|gray|grey|red|green|blue)\\(\\s*(\\d+\\.?\\d*)(%|deg)?(?:\\s+|\\s*,\\s*|\\s\\/\\s)?(\\d+\\.?\\d*)?(%)?(?:\\s+|\\s*,\\s*|\\s\\/\\s)?(\\d+\\.?\\d*)?(%)?(?:\\s+|\\s*,\\s*|\\s*\\/\\s*)?(\\d+\\.?\\d*)?(%)?\\s*\\)\\s*"); + static QRegularExpression rxAP("(.+)\\s*,\\s*a\\s*(\\d+\\.?\\d+)\\%"); + static QRegularExpression rxNP("(.+)\\s*,\\s*a?\\s*([\\d]+)"); const auto mP=rxP.match(color); + + const auto mColMod=rxColMod.match(color); + if (mColMod.hasMatch()) { + QColor col(Qt::black); + const QString name=mColMod.captured(1); + const int v1=valUnitToInt(mColMod.captured(2), mColMod.captured(3)); + const int h1=valUnitToInt(mColMod.captured(2), mColMod.captured(3), INT_MAX); + const int v2=valUnitToInt(mColMod.captured(4), mColMod.captured(5)); + const int a2=valUnitToAlphaInt(mColMod.captured(4), mColMod.captured(5)); + const int v3=valUnitToInt(mColMod.captured(6), mColMod.captured(7)); + const int a4=valUnitToAlphaInt(mColMod.captured(8), mColMod.captured(9)); + if (name=="gray"||name=="grey") { + if (v2<0) col.setRgb(v1,v1,v1); + else col.setRgb(v1,v1,v1,a2); + return col; + } + else if (name=="red") { + col.setRed(v1); + if (v2>=0) col.setAlpha(a2); + return col; + } + else if (name=="green") { + col.setGreen(v1); + if (v2>=0) col.setAlpha(a2); + return col; + } + else if (name=="blue") { + col.setBlue(v1); + if (v2>=0) col.setAlpha(a2); + return col; + } + else if (name=="rgb") { + if (a4<0) col.setRgb(v1,v2,v3); + else col.setRgb(v1,v2,v3,a2); + return col; + } + else if (name=="rgba") { + col.setRgb(v1,v2,v3,a2); + return col; + } + else if (name=="hsl") { + if (a4<0) col.setHsl(h1,v2,v3); + else col.setHsl(h1,v2,v3,a2); + return col; + } + else if (name=="hsv") { + if (a4<0) col.setHsv(h1,v2,v3); + else col.setHsv(h1,v2,v3,a2); + return col; + } else { + qDebug()<<"unrecognized CSS-color:'"<(QLocale::c().toInt(mFrac.captured(2)))/100.0; + if (mFrac.captured(1)=="grey"||mFrac.captured(1)=="gray") col.setRgbF(a,a,a); + else col.setRgbF(col.redF()*a, col.greenF()*a, col.blueF()*a); + return col; + } #else QRegExp rxP("(.+)\\s*,\\s*t?\\s*(\\d+\\.?\\d+)\\%"); QRegExp rxAP("(.+)\\s*,\\s*a\\s*(\\d+\\.?\\d+)\\%"); QRegExp rxNP("(.+)\\s*,\\s*a?\\s*([\\d]+)"); + QRegExp rxFrac("([a-zA-Z]{3,})(\\d{1,3})\\%?"); + QRegExp rxColMod("\\s*(rgb|hsl|hsv|rgba|gray|grey|red|green|blue)\\(\\s*(\\d+\\.?\\d*)(%|deg)?(?:\\s+|\\s*,\\s*|\\s\\/\\s)?(\\d+\\.?\\d*)?(%)?(?:\\s+|\\s*,\\s*|\\s\\/\\s)?(\\d+\\.?\\d*)?(%)?(?:\\s+|\\s*,\\s*|\\s*\\/\\s*)?(\\d+\\.?\\d*)?(%)?\\s*\\)\\s*"); + if (rxColMod.exactMatch(color)) { + QColor col(Qt::black); + const QString name=rxColMod.cap(1); + const int v1=valPercToInt(rxColMod.cap(2), rxColMod.cap(3)); + const int h1=valUnitToInt(mColMod.captured(2), mColMod.captured(3), INT_MAX); + const int v2=valPercToInt(rxColMod.cap(4), rxColMod.cap(5)); + const int a2=valPercToAInt(rxColMod.cap(4), rxColMod.cap(5)); + const int v3=valPercToInt(rxColMod.cap(6), rxColMod.cap(7)); + const int a4=valPercToAInt(rxColMod.cap(8), rxColMod.cap(9)); + if (name=="gray"||name=="grey") { + if (v2<0) col.setRgb(v1,v1,v1); + else col.setRgb(v1,v1,v1,a2); + return col; + } + else if (name=="red") { + col.setRed(v1); + if (v2>=0) col.setAlpha(a2); + return col; + } + else if (name=="green") { + col.setGreen(v1); + if (v2>=0) col.setAlpha(a2); + return col; + } + else if (name=="blue") { + col.setBlue(v1); + if (v2>=0) col.setAlpha(a2); + return col; + } + else if (name=="rgb") { + if (a4<0) col.setRgb(v1,v2,v3); + else col.setRgb(v1,v2,v3,a2); + return col; + } + else if (name=="rgba") { + col.setRgb(v1,v2,v3,a2); + return col; + } + else if (name=="hsl") { + if (a4<0) col.setHsl(h1,v2,v3); + else col.setHsl(h1,v2,v3,a2); + return col; + } + else if (name=="hsv") { + if (a4<0) col.setHsv(h1,v2,v3); + else col.setHsv(h1,v2,v3,a2); + return col; + } else { + qDebug()<<"unrecognized CSS-color:'"<(QLocale::c().toInt(rxFrac.cap(2)))/100.0; + if (rxFrac.cap(1)=="grey"||rxFrac.cap(1)=="gray") col.setRgbF(a,a,a); + else col.setRgbF(col.redF()*a, col.greenF()*a, col.blueF()*a); + return col; + } #endif return jkqtp_lookupQColorName(color); } diff --git a/lib/jkqtcommon/jkqtpstringtools.h b/lib/jkqtcommon/jkqtpstringtools.h index 8835a40f8c..4267a44ce7 100644 --- a/lib/jkqtcommon/jkqtpstringtools.h +++ b/lib/jkqtcommon/jkqtpstringtools.h @@ -209,20 +209,29 @@ JKQTCOMMON_LIB_EXPORT QString jkqtp_QColor2String(QColor color, bool useSpecialT * \ingroup jkqtptools_string * * This returns a QString which contains the name of named colors and the RGBA values in a QT readable form othertwise. + * + * \param color the color name to convert + * \param namesOnly if \c true , the function only compares against the list of CSS colors; otherwise it passes the string also on to QColor()-constructor, which interprets e.g. \c #AABBCC + * \param nameFound[out] optional return value that signifies whether a name was found */ -JKQTCOMMON_LIB_EXPORT QColor jkqtp_lookupQColorName(const QString& color); +JKQTCOMMON_LIB_EXPORT QColor jkqtp_lookupQColorName(const QString& color, bool namesOnly=false, bool* nameFound=nullptr); /** \brief converts a QString into a QColor, compatible with jkqtp_QColor2String(QColor color); * \ingroup jkqtptools_string * - * This returns a QString which contains the name of named colors and the RGBA values in a QT readable form othertwise. - * This function allows to add the alpha-value as \c "," as integer betwee 0 and 255 - * or as \c ",%" in the range of 0..100 % (i.e. (1-transparency_percent/100)*255). - * Also \c ",a%" in the range of 0..100 % (i.e. alpha_percent/100*255). + * This function converts a color name to a QColor. It extends the names by the following optional synatxes (basically the CSS-syntax with even more options): + * - This function allows to add the alpha-value as \c "," as integer betwee 0 and 255 + * or as \c ",%" in the range of 0..100 % (i.e. (1-transparency_percent/100)*255). + * - Also \c ",a%" in the range of 0..100 % (i.e. alpha_percent/100*255). + * - \c "gray" in the range of 0..100 generates a gray value with 0%=black and 100%=white. Also works for "blue"|"green"|"red"|... + * - You can use full CSS-color syntax with functions \c "rgb(R,G,B)" , \c "rgba(...)" , \c "hsl(...)" , \c "hsv(...)" , \c "gray(...)" , \c "green(...)" , \c "red(...)" , \c "blue(...)". + * The function also support %-values as parameters and whitespace, comma or slash as value separatos! + * - Finally the default Qt color definitions are supported, i.e. \c #RGB , \c #RRGGBB , \c #AARRGGBB , \c #RRRGGGBBB , \c #RRRRGGGGBBBB + * . + * * - * Finally the default Qt color definitions are supported, i.e. \c #RGB , \c #RRGGBB , \c #AARRGGBB , \c #RRRGGGBBB , \c #RRRRGGGGBBBB */ -JKQTCOMMON_LIB_EXPORT QColor jkqtp_String2QColor(const QString& color); +JKQTCOMMON_LIB_EXPORT QColor jkqtp_String2QColor(QString color); /** \brief clean a string to be usable as a variable name, e.g. in an expression parser, or a C++-expression * \ingroup jkqtptools_string