浏览代码

Extend color swatch formats + fix DeleteObject (#868)

* Extended to include named colors, a missed hsl, plus others

Also, removed a redundant call to DeleteObject.

* add oklch
dcog989 10 月之前
父节点
当前提交
dad0d5c133
共有 1 个文件被更改,包括 404 次插入314 次删除
  1. 404 314
      src/QListCtrl.cpp

+ 404 - 314
src/QListCtrl.cpp

@@ -34,6 +34,165 @@ static char THIS_FILE[] = __FILE__;
 
 #define VALID_TOOLTIP (m_pToolTip && ::IsWindow(m_pToolTip->m_hWnd))
 
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+// Static map to hold W3C color names and their RGB values
+static std::map<CString, COLORREF> g_colorNameMap;
+static CMutex g_colorNameMapMutex;
+
+// Initializes the color map on first use
+void InitializeColorNameMap()
+{
+	CSingleLock lock(&g_colorNameMapMutex, TRUE);
+	if (g_colorNameMap.empty())
+	{
+        // Populate the map with W3C named colors
+        // A comprehensive list of 148 colors
+        g_colorNameMap[_T("black")] = RGB(0, 0, 0);
+        g_colorNameMap[_T("silver")] = RGB(192, 192, 192);
+        g_colorNameMap[_T("gray")] = RGB(128, 128, 128);
+        g_colorNameMap[_T("white")] = RGB(255, 255, 255);
+        g_colorNameMap[_T("maroon")] = RGB(128, 0, 0);
+        g_colorNameMap[_T("red")] = RGB(255, 0, 0);
+        g_colorNameMap[_T("purple")] = RGB(128, 0, 128);
+        g_colorNameMap[_T("fuchsia")] = RGB(255, 0, 255);
+        g_colorNameMap[_T("green")] = RGB(0, 128, 0);
+        g_colorNameMap[_T("lime")] = RGB(0, 255, 0);
+        g_colorNameMap[_T("olive")] = RGB(128, 128, 0);
+        g_colorNameMap[_T("yellow")] = RGB(255, 255, 0);
+        g_colorNameMap[_T("navy")] = RGB(0, 0, 128);
+        g_colorNameMap[_T("blue")] = RGB(0, 0, 255);
+        g_colorNameMap[_T("teal")] = RGB(0, 128, 128);
+        g_colorNameMap[_T("aqua")] = RGB(0, 255, 255);
+        g_colorNameMap[_T("aliceblue")] = RGB(240, 248, 255);
+        g_colorNameMap[_T("antiquewhite")] = RGB(250, 235, 215);
+        g_colorNameMap[_T("aquamarine")] = RGB(127, 255, 212);
+        g_colorNameMap[_T("azure")] = RGB(240, 255, 255);
+        g_colorNameMap[_T("beige")] = RGB(245, 245, 220);
+        g_colorNameMap[_T("bisque")] = RGB(255, 228, 196);
+        g_colorNameMap[_T("blanchedalmond")] = RGB(255, 235, 205);
+        g_colorNameMap[_T("blueviolet")] = RGB(138, 43, 226);
+        g_colorNameMap[_T("brown")] = RGB(165, 42, 42);
+        g_colorNameMap[_T("burlywood")] = RGB(222, 184, 135);
+        g_colorNameMap[_T("cadetblue")] = RGB(95, 158, 160);
+        g_colorNameMap[_T("chartreuse")] = RGB(127, 255, 0);
+        g_colorNameMap[_T("chocolate")] = RGB(210, 105, 30);
+        g_colorNameMap[_T("coral")] = RGB(255, 127, 80);
+        g_colorNameMap[_T("cornflowerblue")] = RGB(100, 149, 237);
+        g_colorNameMap[_T("cornsilk")] = RGB(255, 248, 220);
+        g_colorNameMap[_T("crimson")] = RGB(220, 20, 60);
+        g_colorNameMap[_T("cyan")] = RGB(0, 255, 255);
+        g_colorNameMap[_T("darkblue")] = RGB(0, 0, 139);
+        g_colorNameMap[_T("darkcyan")] = RGB(0, 139, 139);
+        g_colorNameMap[_T("darkgoldenrod")] = RGB(184, 134, 11);
+        g_colorNameMap[_T("darkgray")] = RGB(169, 169, 169);
+        g_colorNameMap[_T("darkgreen")] = RGB(0, 100, 0);
+        g_colorNameMap[_T("darkkhaki")] = RGB(189, 183, 107);
+        g_colorNameMap[_T("darkmagenta")] = RGB(139, 0, 139);
+        g_colorNameMap[_T("darkolivegreen")] = RGB(85, 107, 47);
+        g_colorNameMap[_T("darkorange")] = RGB(255, 140, 0);
+        g_colorNameMap[_T("darkorchid")] = RGB(153, 50, 204);
+        g_colorNameMap[_T("darkred")] = RGB(139, 0, 0);
+        g_colorNameMap[_T("darksalmon")] = RGB(233, 150, 122);
+        g_colorNameMap[_T("darkseagreen")] = RGB(143, 188, 143);
+        g_colorNameMap[_T("darkslateblue")] = RGB(72, 61, 139);
+        g_colorNameMap[_T("darkslategray")] = RGB(47, 79, 79);
+        g_colorNameMap[_T("darkturquoise")] = RGB(0, 206, 209);
+        g_colorNameMap[_T("darkviolet")] = RGB(148, 0, 211);
+        g_colorNameMap[_T("deeppink")] = RGB(255, 20, 147);
+        g_colorNameMap[_T("deepskyblue")] = RGB(0, 191, 255);
+        g_colorNameMap[_T("dimgray")] = RGB(105, 105, 105);
+        g_colorNameMap[_T("dodgerblue")] = RGB(30, 144, 255);
+        g_colorNameMap[_T("firebrick")] = RGB(178, 34, 34);
+        g_colorNameMap[_T("floralwhite")] = RGB(255, 250, 240);
+        g_colorNameMap[_T("forestgreen")] = RGB(34, 139, 34);
+        g_colorNameMap[_T("gainsboro")] = RGB(220, 220, 220);
+        g_colorNameMap[_T("ghostwhite")] = RGB(248, 248, 255);
+        g_colorNameMap[_T("gold")] = RGB(255, 215, 0);
+        g_colorNameMap[_T("goldenrod")] = RGB(218, 165, 32);
+        g_colorNameMap[_T("greenyellow")] = RGB(173, 255, 47);
+        g_colorNameMap[_T("honeydew")] = RGB(240, 255, 240);
+        g_colorNameMap[_T("hotpink")] = RGB(255, 105, 180);
+        g_colorNameMap[_T("indianred")] = RGB(205, 92, 92);
+        g_colorNameMap[_T("indigo")] = RGB(75, 0, 130);
+        g_colorNameMap[_T("ivory")] = RGB(255, 255, 240);
+        g_colorNameMap[_T("khaki")] = RGB(240, 230, 140);
+        g_colorNameMap[_T("lavender")] = RGB(230, 230, 250);
+        g_colorNameMap[_T("lavenderblush")] = RGB(255, 240, 245);
+        g_colorNameMap[_T("lawngreen")] = RGB(124, 252, 0);
+        g_colorNameMap[_T("lemonchiffon")] = RGB(255, 250, 205);
+        g_colorNameMap[_T("lightblue")] = RGB(173, 216, 230);
+        g_colorNameMap[_T("lightcoral")] = RGB(240, 128, 128);
+        g_colorNameMap[_T("lightcyan")] = RGB(224, 255, 255);
+        g_colorNameMap[_T("lightgoldenrodyellow")] = RGB(250, 250, 210);
+        g_colorNameMap[_T("lightgray")] = RGB(211, 211, 211);
+        g_colorNameMap[_T("lightgreen")] = RGB(144, 238, 144);
+        g_colorNameMap[_T("lightpink")] = RGB(255, 182, 193);
+        g_colorNameMap[_T("lightsalmon")] = RGB(255, 160, 122);
+        g_colorNameMap[_T("lightseagreen")] = RGB(32, 178, 170);
+        g_colorNameMap[_T("lightskyblue")] = RGB(135, 206, 250);
+        g_colorNameMap[_T("lightslategray")] = RGB(119, 136, 153);
+        g_colorNameMap[_T("lightsteelblue")] = RGB(176, 196, 222);
+        g_colorNameMap[_T("lightyellow")] = RGB(255, 255, 224);
+        g_colorNameMap[_T("limegreen")] = RGB(50, 205, 50);
+        g_colorNameMap[_T("linen")] = RGB(250, 240, 230);
+        g_colorNameMap[_T("magenta")] = RGB(255, 0, 255);
+        g_colorNameMap[_T("mediumaquamarine")] = RGB(102, 205, 170);
+        g_colorNameMap[_T("mediumblue")] = RGB(0, 0, 205);
+        g_colorNameMap[_T("mediumorchid")] = RGB(186, 85, 211);
+        g_colorNameMap[_T("mediumpurple")] = RGB(147, 112, 219);
+        g_colorNameMap[_T("mediumseagreen")] = RGB(60, 179, 113);
+        g_colorNameMap[_T("mediumslateblue")] = RGB(123, 104, 238);
+        g_colorNameMap[_T("mediumspringgreen")] = RGB(0, 250, 154);
+        g_colorNameMap[_T("mediumturquoise")] = RGB(72, 209, 204);
+        g_colorNameMap[_T("mediumvioletred")] = RGB(199, 21, 133);
+        g_colorNameMap[_T("midnightblue")] = RGB(25, 25, 112);
+        g_colorNameMap[_T("mintcream")] = RGB(245, 255, 250);
+        g_colorNameMap[_T("mistyrose")] = RGB(255, 228, 225);
+        g_colorNameMap[_T("moccasin")] = RGB(255, 228, 181);
+        g_colorNameMap[_T("navajowhite")] = RGB(255, 222, 173);
+        g_colorNameMap[_T("oldlace")] = RGB(253, 245, 230);
+        g_colorNameMap[_T("olivedrab")] = RGB(107, 142, 35);
+        g_colorNameMap[_T("orange")] = RGB(255, 165, 0);
+        g_colorNameMap[_T("orangered")] = RGB(255, 69, 0);
+        g_colorNameMap[_T("orchid")] = RGB(218, 112, 214);
+        g_colorNameMap[_T("palegoldenrod")] = RGB(238, 232, 170);
+        g_colorNameMap[_T("palegreen")] = RGB(152, 251, 152);
+        g_colorNameMap[_T("paleturquoise")] = RGB(175, 238, 238);
+        g_colorNameMap[_T("palevioletred")] = RGB(219, 112, 147);
+        g_colorNameMap[_T("papayawhip")] = RGB(255, 239, 213);
+        g_colorNameMap[_T("peachpuff")] = RGB(255, 218, 185);
+        g_colorNameMap[_T("peru")] = RGB(205, 133, 63);
+        g_colorNameMap[_T("pink")] = RGB(255, 192, 203);
+        g_colorNameMap[_T("plum")] = RGB(221, 160, 221);
+        g_colorNameMap[_T("powderblue")] = RGB(176, 224, 230);
+        g_colorNameMap[_T("rebeccapurple")] = RGB(102, 51, 153);
+        g_colorNameMap[_T("rosybrown")] = RGB(188, 143, 143);
+        g_colorNameMap[_T("royalblue")] = RGB(65, 105, 225);
+        g_colorNameMap[_T("saddlebrown")] = RGB(139, 69, 19);
+        g_colorNameMap[_T("salmon")] = RGB(250, 128, 114);
+        g_colorNameMap[_T("sandybrown")] = RGB(244, 164, 96);
+        g_colorNameMap[_T("seagreen")] = RGB(46, 139, 87);
+        g_colorNameMap[_T("seashell")] = RGB(255, 245, 238);
+        g_colorNameMap[_T("sienna")] = RGB(160, 82, 45);
+        g_colorNameMap[_T("skyblue")] = RGB(135, 206, 235);
+        g_colorNameMap[_T("slateblue")] = RGB(106, 90, 205);
+        g_colorNameMap[_T("slategray")] = RGB(112, 128, 144);
+        g_colorNameMap[_T("snow")] = RGB(255, 250, 250);
+        g_colorNameMap[_T("springgreen")] = RGB(0, 255, 127);
+        g_colorNameMap[_T("steelblue")] = RGB(70, 130, 180);
+        g_colorNameMap[_T("tan")] = RGB(210, 180, 140);
+        g_colorNameMap[_T("thistle")] = RGB(216, 191, 216);
+        g_colorNameMap[_T("tomato")] = RGB(255, 99, 71);
+        g_colorNameMap[_T("turquoise")] = RGB(64, 224, 208);
+        g_colorNameMap[_T("violet")] = RGB(238, 130, 238);
+        g_colorNameMap[_T("wheat")] = RGB(245, 222, 179);
+        g_colorNameMap[_T("whitesmoke")] = RGB(245, 245, 245);
+        g_colorNameMap[_T("yellowgreen")] = RGB(154, 205, 50);
+	}
+}
 
 /////////////////////////////////////////////////////////////////////////////
 // CQListCtrl
@@ -76,7 +235,6 @@ CQListCtrl::~CQListCtrl()
 		m_pFormatter = NULL;
 	}
 
-	DeleteObject(m_SmallFont);
 }
 
 // returns the position 1-10 if the index is in the FirstTen block else -1
@@ -561,380 +719,312 @@ COLORREF CQListCtrl::HslToRgb(double h, double s, double l)
 }
 
 
+// Helper function to parse a CSS numeric value which can be a percentage or a number
+static bool ParseCssValue(const CString& token, double& value)
+{
+	CString localToken = token;
+	localToken.Trim();
+	if (localToken.IsEmpty()) return false;
+
+	bool isPercent = (localToken.Right(1) == _T('%'));
+	if (isPercent)
+	{
+		localToken.TrimRight(_T('%'));
+	}
+
+	if (_stscanf(localToken, _T("%lf"), &value) == 1)
+	{
+		return true;
+	}
+	return false;
+}
+
+
+// Converts a color from OKLCH color space to sRGB.
+// l: 0-1, c: 0-0.4 (theoretically unbounded, but practically small), h: 0-360
+static COLORREF OklchToRgb(double l, double c, double h)
+{
+	// 1. Convert OKLCH to OKLAB
+	double h_rad = h * M_PI / 180.0;
+	double a = c * cos(h_rad);
+	double b = c * sin(h_rad);
+
+	// 2. Convert OKLAB to XYZ
+	// First, convert to an intermediate non-linear LMS-like space
+	double l_ = l + 0.3963377774 * a + 0.2158037573 * b;
+	double m_ = l - 0.1055613458 * a - 0.0638541728 * b;
+	double s_ = l - 0.0894841775 * a - 1.2914855480 * b;
+
+	// Then, convert to linear LMS by undoing the cube-root non-linearity
+	double l_linear = l_ * l_ * l_;
+	double m_linear = m_ * m_ * m_;
+	double s_linear = s_ * s_ * s_;
+
+	// Finally, convert linear LMS to XYZ color space
+	double x = +1.2270138511 * l_linear - 0.5577999807 * m_linear + 0.2812561490 * s_linear;
+	double y = -0.0405801784 * l_linear + 1.1122568696 * m_linear - 0.0716766787 * s_linear;
+	double z = -0.0763812845 * l_linear - 0.4214819784 * m_linear + 1.5861632204 * s_linear;
+
+	// 3. Convert XYZ to linear sRGB
+	double r_linear = +3.2404542 * x - 1.5371385 * y - 0.4985314 * z;
+	double g_linear = -0.9692660 * x + 1.8760108 * y + 0.0415560 * z;
+	double b_linear = +0.0556434 * x - 0.2040259 * y + 1.0572252 * z;
+
+	// 4. Convert linear sRGB to gamma-corrected sRGB
+	auto ToSrgb = [](double val) {
+		if (val <= 0.0031308) {
+			return 12.92 * val;
+		}
+		return 1.055 * pow(val, 1.0 / 2.4) - 0.055;
+	};
+
+	double r_srgb = ToSrgb(r_linear);
+	double g_srgb = ToSrgb(g_linear);
+	double b_srgb = ToSrgb(b_linear);
+
+	// 5. Scale to 0-255 and clamp (for out-of-gamut colors)
+	int R = static_cast<int>(std::round(r_srgb * 255.0));
+	int G = static_cast<int>(std::round(g_srgb * 255.0));
+	int B = static_cast<int>(std::round(b_srgb * 255.0));
+
+	R = max(0, min(255, R));
+	G = max(0, min(255, G));
+	B = max(0, min(255, B));
+
+	return RGB(R, G, B);
+}
+
+
 void CQListCtrl::DrawCopiedColorCode(CString& csText, CRect& rcText, CDC* pDC)
 {
 	if (CGetSetOptions::m_bDrawCopiedColorCode == FALSE || csText.IsEmpty())
 		return;
 
-	// 1. Initial Cleaning (Remove specific surrounding chars and leading/trailing whitespace)
+	// 1. Initial Cleaning and Prep
 	CString cleanedText = csText;
-	cleanedText.Trim(_T("»")); // Remove potential ditto mark first
-	cleanedText.Trim();        // Trim leading/trailing whitespace
-	cleanedText.TrimRight(_T(";")); // Remove trailing semicolon
-	cleanedText.Trim();        // Trim again in case semicolon had spaces after it
+	cleanedText.Trim(_T("»"));
+	cleanedText.Trim();
+	cleanedText.TrimRight(_T(";"));
+	cleanedText.Trim();
+
+	if (cleanedText.IsEmpty())
+		return;
 
-	// Store original case potentially for display, but use lower for parsing
 	CString originalCleanedText = cleanedText;
 	CString parseText = cleanedText;
-	parseText.MakeLower(); // Use lower case for keyword matching ('rgb', 'hsl', '0x', '#')
+	parseText.MakeLower();
 
-	// 2. Helper function to draw the color box
+	// 2. Helper lambda to draw the color box
 	auto DrawColorBox = [&](COLORREF color)
-		{
-			CRect pastedRect(rcText);
-			int boxSize = rcText.Height();
-			// Prevent overly large boxes if text rect somehow becomes huge
-			//boxSize = min(boxSize, m_windowDpi->Scale(20));
-			pastedRect.right = pastedRect.left + boxSize;
-			pastedRect.bottom = pastedRect.top + boxSize; // Make it square
-
-			pDC->FillSolidRect(pastedRect, color);
-
-			// Adjust text rect to be after the color box + padding
-			rcText.left += boxSize;
-			rcText.left += m_windowDpi->Scale(ROW_LEFT_BORDER);
-			// Restore original (potentially mixed-case) text for drawing
-			csText = originalCleanedText;
-		};
-
-	// 3. --- Hex Color Parsing ---
-	CString hexString;
-	bool isHex = false;
-	bool is0xPrefix = false; // Flag to track prefix type ('0x' vs '#')
-	int originalHexLength = 0;
-
-	if (parseText.GetLength() >= 2 && parseText.Left(2) == _T("0x"))
-	{
-		hexString = parseText.Mid(2); // Get part after 0x
-		originalHexLength = hexString.GetLength();
-		// Supported lengths for 0x: 3 (RGB -> RRGGBB), 6 (RRGGBB), 8 (AARRGGBB)
-		// Exclude length 4 (0xRGBA) as it's ambiguous/non-standard
-		if ((originalHexLength == 3 || originalHexLength == 6 || originalHexLength == 8) && IsHexString(hexString))
-		{
-			isHex = true;
-			is0xPrefix = true;
-			// Expand 0xRGB -> RRGGBB (will assign Alpha later)
-			if (originalHexLength == 3)
-			{
-				hexString.Format(_T("%c%c%c%c%c%c"), hexString[0], hexString[0], hexString[1], hexString[1], hexString[2], hexString[2]);
-				// Length becomes 6
-			}
-			// No expansion needed for 6 (0xRRGGBB) or 8 (0xAARRGGBB)
-		}
-	}
-	else if (parseText.GetLength() >= 1 && parseText.Left(1) == _T("#"))
 	{
-		hexString = parseText.Mid(1); // Get part after #
-		originalHexLength = hexString.GetLength();
-		// Supported lengths for #: 3 (RGB), 4 (RGBA), 6 (RRGGBB), 8 (RRGGBBAA)
-		if ((originalHexLength == 3 || originalHexLength == 4 || originalHexLength == 6 || originalHexLength == 8) && IsHexString(hexString))
-		{
-			isHex = true;
-			is0xPrefix = false; // It's a # prefix
-			// Expand #RGB -> RRGGBB
-			if (originalHexLength == 3)
-			{
-				hexString.Format(_T("%c%c%c%c%c%c"), hexString[0], hexString[0], hexString[1], hexString[1], hexString[2], hexString[2]);
-				// Length becomes 6
-			}
-			// Expand #RGBA -> RRGGBBAA
-			else if (originalHexLength == 4)
-			{
-				hexString.Format(_T("%c%c%c%c%c%c%c%c"), hexString[0], hexString[0], hexString[1], hexString[1], hexString[2], hexString[2], hexString[3], hexString[3]);
-				// Length becomes 8
-			}
-			// No expansion needed for 6 (#RRGGBB) or 8 (#RRGGBBAA)
-		}
+		CRect pastedRect(rcText);
+		int boxSize = rcText.Height();
+		pastedRect.right = pastedRect.left + boxSize;
+		pastedRect.bottom = pastedRect.top + boxSize;
+
+		pDC->FillSolidRect(pastedRect, color);
+
+		rcText.left += boxSize + m_windowDpi->Scale(ROW_LEFT_BORDER);
+		csText = originalCleanedText;
+	};
+
+	// 3. Check for formats with unique signatures first
+
+	// Check for W3C Named Colors
+	InitializeColorNameMap();
+	auto it = g_colorNameMap.find(parseText);
+	if (it != g_colorNameMap.end())
+	{
+		DrawColorBox(it->second);
+		return;
 	}
 
-	if (isHex)
+	// Check for Hex Colors with # or 0x prefix
+	if (parseText.Left(1) == _T('#') || parseText.Left(2) == _T("0x"))
 	{
-		int r = 0, g = 0, b = 0, a = 255; // Default alpha to fully opaque
-		int scanRet = 0;
-		unsigned int hexValue = 0;
-		int finalHexLength = hexString.GetLength(); // Length after potential expansion
+		CString hexString = parseText;
+		if (hexString.Left(1) == _T('#')) hexString.Delete(0, 1);
+		else if (hexString.Left(2) == _T("0x")) hexString.Delete(0, 2);
 
-		// At this point, hexString should contain 6 or 8 hex digits if isHex is true
-		if (finalHexLength != 6 && finalHexLength != 8) {
-			// This case should not be reached if the logic above is correct, but acts as a safeguard
-			isHex = false;
-		}
-		else {
-			// Use swscanf to parse the final 6 or 8 digit hex string
-			scanRet = swscanf(hexString, _T("%x"), &hexValue);
+		int len = hexString.GetLength();
+		if (len == 3 || len == 4) // Expand shorthand
+		{
+			CString expanded;
+			for (int i = 0; i < len; ++i) { expanded += hexString[i]; expanded += hexString[i]; }
+			hexString = expanded;
 		}
 
-		if (isHex && scanRet == 1)
+		if (IsHexString(hexString) && (hexString.GetLength() == 6 || hexString.GetLength() == 8))
 		{
-			if (finalHexLength == 8) // 8 digits: AARRGGBB (for 0x) or RRGGBBAA (for #)
+			unsigned int r = 0, g = 0, b = 0;
+			if (swscanf(hexString, _T("%02x%02x%02x"), &r, &g, &b) == 3)
 			{
-				if (is0xPrefix) { // Handle 0xAARRGGBB
-					a = (hexValue >> 24) & 0xFF;
-					r = (hexValue >> 16) & 0xFF;
-					g = (hexValue >> 8) & 0xFF;
-					b = hexValue & 0xFF;
-				} else { // Handle #RRGGBBAA (or expanded #RGBA)
-					r = (hexValue >> 24) & 0xFF;
-					g = (hexValue >> 16) & 0xFF;
-					b = (hexValue >> 8) & 0xFF;
-					a = hexValue & 0xFF;
-				}
-			}
-			else // 6 digits (RRGGBB - from #RGB, 0xRGB, #RRGGBB, 0xRRGGBB)
-			{
-				r = (hexValue >> 16) & 0xFF;
-				g = (hexValue >> 8) & 0xFF;
-				b = hexValue & 0xFF;
-				a = 255; // Explicitly set full opacity for all 6-digit formats
-			}
-
-			// Basic validation on parsed values (should be redundant if IsHexString worked)
-			if (r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255 && a >= 0 && a <= 255) {
-				DrawColorBox(RGB(r, g, b)); // Draw using RGB, ignore alpha for the solid box
-				return; // Valid hex format found and drawn
+				DrawColorBox(RGB(r, g, b));
+				return;
 			}
 		}
 	}
 
-
-	// 4. --- RGB Color Parsing ---
-	// Formats: rgb(R, G, B), rgba(R, G, B, A), rgb(R G B), rgb(R G B / A)
-	// R, G, B are 0-255. A is 0.0-1.0
-	// Percentage formats like rgb(100%, 0%, 0%) are NOT handled by this swscanf logic.
-	if (parseText.Left(3) == _T("rgb") && parseText.Right(1) == _T(")"))
-	{
-		CString content;
-		bool isRgba = parseText.Left(4) == _T("rgba");
-		int prefixLen = isRgba ? 5 : 4; // Length of "rgba(" or "rgb("
-		content = parseText.Mid(prefixLen, parseText.GetLength() - prefixLen - 1); // Extract content within ()
-		content.Trim(); // Trim spaces within parentheses
-
-		int r = -1, g = -1, b = -1;
-		double a_double = 1.0; // Default alpha
-		int scanRet = 0;
-		bool parsed = false;
-
-		// Try parsing comma-separated format first (most common)
-		// Check if it contains commas before trying swscanf
-		if (content.Find(',') != -1) {
-			scanRet = swscanf(content, _T("%d , %d ,%d , %lf"), &r, &g, &b, &a_double); // rgba(R, G, B, A)
-			if (scanRet >= 3) { // Need at least R, G, B
-				parsed = true;
-				if (isRgba && scanRet != 4) parsed = false; // rgba() must have 4 values
-				if (!isRgba && scanRet == 4) parsed = false; // rgb() must not have 4 values
+	// Check for W3C notations: rgb(...), hsl(...), and oklch(...)
+	if (parseText.Right(1) == _T(")"))
+	{
+		if (parseText.Left(3) == _T("rgb"))
+		{
+			CString content;
+			int prefixLen = (parseText.Left(4) == _T("rgba")) ? 5 : 4;
+			content = parseText.Mid(prefixLen, parseText.GetLength() - prefixLen - 1);
+			content.Replace(_T(','), _T(' '));
+			content.Replace(_T('/'), _T(' '));
+
+			std::vector<CString> tokens;
+			int curPos = 0;
+			CString token;
+			while (!(token = content.Tokenize(_T(" "), curPos)).IsEmpty())
+			{
+				tokens.push_back(token);
 			}
 
-			if (!parsed) {
-				scanRet = swscanf(content, _T("%d %% , %d %% , %d %% , %lf %%"), &r, &g, &b, &a_double);
-				if (scanRet >= 3) {
-					r = ((double)r / 100) * 255;
-					g = ((double)g / 100) * 255;
-					b = ((double)b / 100) * 255;
-					a_double = (a_double / 100) * 1;
-					parsed = true;
-					if (isRgba && scanRet != 4) parsed = false; // rgba() must have 4 values
-					if (!isRgba && scanRet == 4) parsed = false; // rgb() must not have 4 values
-				}
-			}
-		}
-
-		// Try parsing space-separated format if comma parse failed or wasn't attempted
-		// Check for slash for alpha separation
-		if (!parsed && content.Find('/') != -1) {
-			scanRet = swscanf(content, _T("%d %d %d / %lf"), &r, &g, &b, &a_double); // rgb(R G B / A)
-			if (scanRet == 4) { // Must have exactly R G B / A
-			    // This format is valid for both rgb() and rgba() in modern CSS, check values
-				parsed = true;
-			}
-		}
-		else if (!parsed) { // Try space separated without alpha
-			scanRet = swscanf(content, _T("%d %d %d"), &r, &g, &b); // rgb(R G B)
-			if (scanRet == 3) { // Must have exactly R G B
-				// Check for extraneous characters after the numbers
-				CString check_str;
-				check_str.Format(_T("%d %d %d"), r, g, b);
-				if (content == check_str) // Ensure the whole string was consumed
+			if (tokens.size() >= 3)
+			{
+				double r_val, g_val, b_val;
+				if (ParseCssValue(tokens[0], r_val) && ParseCssValue(tokens[1], g_val) && ParseCssValue(tokens[2], b_val))
 				{
-					parsed = true;
-					a_double = 1.0; // Set default alpha
+					if (tokens[0].Find('%') != -1)
+					{
+						r_val = std::round(r_val * 2.55);
+						g_val = std::round(g_val * 2.55);
+						b_val = std::round(b_val * 2.55);
+					}
+					if (r_val >= 0 && r_val <= 255 && g_val >= 0 && g_val <= 255 && b_val >= 0 && b_val <= 255)
+					{
+						DrawColorBox(RGB((int)r_val, (int)g_val, (int)b_val));
+						return;
+					}
 				}
 			}
 		}
-
-		if (parsed)
+		else if (parseText.Left(3) == _T("hsl"))
 		{
-			// Validate ranges: R, G, B (0-255), A (0.0-1.0)
-			if (r >= 0 && r <= 255 &&
-				g >= 0 && g <= 255 &&
-				b >= 0 && b <= 255 &&
-				a_double >= 0.0 && a_double <= 1.0)
+			CString content;
+			int prefixLen = (parseText.Left(4) == _T("hsla")) ? 5 : 4;
+			content = parseText.Mid(prefixLen, parseText.GetLength() - prefixLen - 1);
+			content.Replace(_T(','), _T(' '));
+			content.Replace(_T('/'), _T(' '));
+			content.Replace(_T("deg"), _T(""));
+
+			std::vector<CString> tokens;
+			int curPos = 0;
+			CString token;
+			while (!(token = content.Tokenize(_T(" "), curPos)).IsEmpty())
 			{
-				DrawColorBox(RGB(r, g, b)); // Draw using RGB part
-				return; // Valid RGB/RGBA format found and drawn
+				tokens.push_back(token);
 			}
-		}
-	}
 
-	//draw hex color copied like #FF0000, FF0000, FF0000 other text with a space before other text
-	if (parseText.GetLength() == 6 ||
-		parseText.Find(' ') == 6)
-	{
-		int r, g, b;
-		int scanRet = swscanf(parseText, _T("%02x%02x%02x"), &r, &g, &b);
-		if (scanRet == 3)
-		{
-			DrawColorBox(RGB(r, g, b)); // Draw using RGB part
-			return; // Valid RGB/RGBA format found and drawn
-		}
-	}
-
-	// 5. --- HSL Color Parsing ---
-	// Formats: hsl(H, S%, L%), hsla(H, S%, L%, A), hsl(H S% L%), hsl(H S% L% / A)
-	// Also supports 'deg' suffix for Hue.
-	// H is 0-360, S/L are 0-100%, A is 0.0-1.0
-	if (parseText.Left(3) == _T("hsl") && parseText.Right(1) == _T(")"))
-	{
-		CString content;
-		bool isHsla = parseText.Left(4) == _T("hsla");
-		int prefixLen = isHsla ? 5 : 4; // Length of "hsla(" or "hsl("
-		content = parseText.Mid(prefixLen, parseText.GetLength() - prefixLen - 1); // Extract content within ()
-		content.Trim(); // Trim spaces within parentheses
-
-		int h = -1;
-		double s = -1.0, l = -1.0, a_double = 1.0; // Default alpha
-		int scanRet = 0;
-		bool parsed = false;
-		wchar_t degSuffix[5] = { 0 }; // To capture 'deg' if present
-		wchar_t sPercent = 0, lPercent = 0; // To capture '%' signs
-
-		// --- Try parsing comma-separated formats ---
-		if (!parsed && content.Find(',') != -1) {
-			// hsla(Hdeg, S%, L%, A)
-			scanRet = swscanf(content, _T("%ddeg , %lf %% , %lf %% , %lf"), &h, &s, &l, &a_double);
-			if (scanRet >= 3) { // H, S, L required
-				parsed = true;
-				if (isHsla && scanRet != 4) parsed = false; // hsla requires 4
-				if (!isHsla && scanRet == 4) parsed = false; // hsl requires 3
-			}
-
-			// hsla(H, S%, L%, A) - No 'deg'
-			if (!parsed) {
-				scanRet = swscanf(content, _T("%d , %lf %% , %lf %% , %lf"), &h, &s, &l, &a_double);
-				if (scanRet >= 3) {
-					parsed = true;
-					if (isHsla && scanRet != 4) parsed = false;
-					if (!isHsla && scanRet == 4) parsed = false;
+			if (tokens.size() >= 3)
+			{
+				double h_val, s_val, l_val;
+				if (ParseCssValue(tokens[0], h_val) && ParseCssValue(tokens[1], s_val) && ParseCssValue(tokens[2], l_val))
+				{
+					if (s_val >= 0 && s_val <= 100 && l_val >= 0 && l_val <= 100)
+					{
+						h_val = fmod(h_val, 360.0);
+						if (h_val < 0) h_val += 360.0;
+						DrawColorBox(HslToRgb(h_val, s_val / 100.0, l_val / 100.0));
+						return;
+					}
 				}
 			}
 		}
-
-		// --- Try parsing space-separated formats ---
-		if (!parsed && content.Find(',') == -1) { // Only if no commas found
-			// hsl(Hdeg S% L% / A)
-			scanRet = swscanf(content, _T("%ddeg %lf %% %lf %% / %lf"), &h, &s, &l, &a_double);
-			if (scanRet == 4) { // Must match exactly 4 components
-				parsed = true;
+		else if (parseText.Left(5) == _T("oklch"))
+		{
+			CString content;
+			content = parseText.Mid(6, parseText.GetLength() - 7);
+			content.Replace(_T(','), _T(' '));
+			content.Replace(_T('/'), _T(' '));
+
+			std::vector<CString> tokens;
+			int curPos = 0;
+			CString token;
+			while (!(token = content.Tokenize(_T(" "), curPos)).IsEmpty())
+			{
+				tokens.push_back(token);
 			}
 
-			// hsl(H S% L% / A) - No 'deg'
-			if (!parsed) {
-				scanRet = swscanf(content, _T("%d %lf %% %lf %% / %lf"), &h, &s, &l, &a_double);
-				if (scanRet == 4) {
-					parsed = true;
-				}
-			}
+			if (tokens.size() >= 3)
+			{
+				double l_val, c_val, h_val;
+				if (ParseCssValue(tokens[0], l_val) && ParseCssValue(tokens[1], c_val) && ParseCssValue(tokens[2], h_val))
+				{
+					bool l_is_percent = tokens[0].Find('%') != -1;
 
-			// hsl(Hdeg S% L%) - No alpha
-			if (!parsed) {
-				// Need to ensure the *entire* string is consumed to avoid matching prefix of alpha version
-				// We store the scanned values and reconstruct to compare
-				scanRet = swscanf(content, _T("%ddeg %lf %% %lf %%"), &h, &s, &l);
-				if (scanRet == 3) {
-					CString check_str;
-					// Format considering potential floating point variations slightly
-					// However, simple comparison might be sufficient if input is clean
-					check_str.Format(_T("%ddeg %.0f%% %.0f%%"), h, s, l);
-					// Crude check - might fail if input has different spacing or precision
-					// A better check involves checking remaining chars after scan, but swscanf makes this hard.
-					// Let's try a simpler length/content check for now.
-					CString temp_content = content;
-					temp_content.Remove(' '); // Remove spaces for comparison
-					CString temp_check = check_str;
-					temp_check.Remove(' ');
-					if (temp_content == temp_check) { // Basic check if format matches structure
-						parsed = true;
-						a_double = 1.0; // Set default alpha
+					double l_normalized = l_val;
+					if (l_is_percent)
+					{
+						l_normalized = l_val / 100.0;
 					}
-				}
-			}
 
-			// hsl(H S% L%) - No 'deg', No alpha
-			if (!parsed) {
-				scanRet = swscanf(content, _T("%d %lf %% %lf %%"), &h, &s, &l);
-				if (scanRet == 3) {
-					CString check_str;
-					check_str.Format(_T("%d %.0f%% %.0f%%"), h, s, l);
-					CString temp_content = content;
-					temp_content.Remove(' ');
-					CString temp_check = check_str;
-					temp_check.Remove(' ');
-					if (temp_content == temp_check) {
-						parsed = true;
-						a_double = 1.0;
+					if (l_normalized >= 0 && l_normalized <= 1.0 && c_val >= 0)
+					{
+						DrawColorBox(OklchToRgb(l_normalized, c_val, h_val));
+						return;
 					}
 				}
 			}
 		}
+	}
+
+	// 4. --- Non-W3C Format Parsing ---
+	int r, g, b, chars_consumed = 0;
+
+	// Check for parenthesized RGB: "(255, 128, 0)"
+	if (parseText.Left(1) == _T("(") && parseText.Right(1) == _T(")"))
+	{
+		CString content = parseText.Mid(1, parseText.GetLength() - 2);
+		content.Trim(); // Trim whitespace inside parentheses
 
-		if (parsed)
+		if (swscanf(content, _T("%d , %d , %d %n"), &r, &g, &b, &chars_consumed) == 3 && chars_consumed == content.GetLength())
 		{
-			// Validate ranges: H (any number, will be normalized), S/L (0-100), A (0.0-1.0)
-			// Note: Hue can be negative or > 360, it will be normalized later.
-			if (s >= 0.0 && s <= 100.0 &&
-				l >= 0.0 && l <= 100.0 &&
-				a_double >= 0.0 && a_double <= 1.0)
+			if (r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255)
 			{
-				// Normalize HSL values for conversion
-				h = h % 360; // Hue: Normalize to 0-359
-				if (h < 0) {
-					h += 360;
-				}
-				double s_norm = s / 100.0; // Saturation: 0.0 - 1.0
-				double l_norm = l / 100.0; // Lightness: 0.0 - 1.0
-
-				COLORREF rgbColor = HslToRgb(static_cast<double>(h), s_norm, l_norm);
-				DrawColorBox(rgbColor);
-				return; // Valid HSL/HSLA format found and drawn
+				DrawColorBox(RGB(r, g, b));
+				return;
 			}
 		}
 	}
 
-	// draw rgb color copied like 255,0,0
-	int firstCommaPos = parseText.Find(',');
-	if (firstCommaPos >= 0)
+	// Check for comma-separated RGB: "255, 0, 0"
+	if (swscanf(parseText, _T("%d , %d , %d %n"), &r, &g, &b, &chars_consumed) == 3 && chars_consumed == parseText.GetLength())
 	{
-		int secondCommaPos = parseText.Find(',', firstCommaPos + 1);
-		if (secondCommaPos)
+		if (r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255)
 		{
-			int noThirdCommaPos = parseText.Find(',', secondCommaPos + 1);
+			DrawColorBox(RGB(r, g, b));
+			return;
+		}
+	}
 
-			if (noThirdCommaPos < 0)
-			{
-				int r, g, b;
-				int scanRet = swscanf(parseText, _T("%d,%d,%d"), &r, &g, &b);
-				if (scanRet == 3)
-				{
-					DrawColorBox(RGB(r, g, b)); // Draw using RGB part
-					return; // Valid RGB/RGBA format found and drawn
-				}
-			}
+	// Check for space-separated RGB: "255 128 0"
+	if (swscanf(parseText, _T("%d %d %d %n"), &r, &g, &b, &chars_consumed) == 3 && chars_consumed == parseText.GetLength())
+	{
+		if (r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255)
+		{
+			DrawColorBox(RGB(r, g, b));
+			return;
 		}
 	}
 
-	// If we reach here, no valid format was detected or parsed correctly.
-	// The original csText remains unchanged, and no color box is drawn.
+	// Check for 6-digit hex: "FF00CC"
+	if (parseText.GetLength() == 6 && IsHexString(parseText))
+	{
+		// Use %n here as well for consistency, though length check is sufficient.
+		if (swscanf(parseText, _T("%02x%02x%02x%n"), &r, &g, &b, &chars_consumed) == 3 && chars_consumed == 6)
+		{
+			DrawColorBox(RGB(r, g, b));
+			return;
+		}
+	}
 }
 
+
 BOOL CQListCtrl::DrawRtfText(int nItem, CRect& crRect, CDC* pDC)
 {
 	if (CGetSetOptions::m_bDrawRTF == FALSE)