From 21df4ebd0888b5e2385350f5c26dd981df19cd08 Mon Sep 17 00:00:00 2001 From: Ahmet Sait Date: Mon, 13 May 2024 18:12:23 +0300 Subject: [PATCH] Fix `FlagsEditorControl` unreadable text on Visual Studio dark theme --- Scintilla.NET/ColorSpace.cs | 97 ++++++++++++++++++++ Scintilla.NET/FlagsEditorControl.Designer.cs | 4 +- Scintilla.NET/FlagsEditorControl.cs | 15 ++- Scintilla.NET/Helpers.cs | 14 +++ 4 files changed, 126 insertions(+), 4 deletions(-) create mode 100644 Scintilla.NET/ColorSpace.cs diff --git a/Scintilla.NET/ColorSpace.cs b/Scintilla.NET/ColorSpace.cs new file mode 100644 index 0000000..3667e13 --- /dev/null +++ b/Scintilla.NET/ColorSpace.cs @@ -0,0 +1,97 @@ +using System; +using System.Drawing; + +namespace ScintillaNET; + +internal class ColorSpace +{ + public static float SrgbToLinearSrgb(float x) => + x >= 0.04045 ? (float)Math.Pow((x + 0.055f) / (1 + 0.055f), 2.4f) : x / 12.92f; + + public static float LinearSrgbToSrgb(float x) => + x >= 0.0031308 ? 1.055f * (float)Math.Pow(x, 1.0 / 2.4) - 0.055f : 12.92f * x; +} + +/// +/// OkLab color. +/// +/// Luminance (perceived lightness) in range [0.0, 1.0]. +/// How green/red the color is in range [-0.233887, +0.276216]. +/// How blue/yellow the color is in range [-0.311528, +0.198570]. +internal struct OkLab(float L, float a, float b) +{ + public float L = L; + public float a = a; + public float b = b; + + public readonly Srgb ToLinearSrgb() + { + float l_ = this.L + 0.3963377774f * this.a + 0.2158037573f * this.b; + float m_ = this.L - 0.1055613458f * this.a - 0.0638541728f * this.b; + float s_ = this.L - 0.0894841775f * this.a - 1.2914855480f * this.b; + + float l = l_ * l_ * l_; + float m = m_ * m_ * m_; + float s = s_ * s_ * s_; + + return new Srgb( + Helpers.Clamp(+4.0767416621f * l - 3.3077115913f * m + 0.2309699292f * s, 0, 1), + Helpers.Clamp(-1.2684380046f * l + 2.6097574011f * m - 0.3413193965f * s, 0, 1), + Helpers.Clamp(-0.0041960863f * l - 0.7034186147f * m + 1.7076147010f * s, 0, 1) + ); + } + + public override readonly string ToString() + { + return FormattableString.Invariant($"(L:{L}, a:{a}, b:{b})"); + } +} + +/// +/// sRGB color. +/// +/// Red component. +/// Green component. +/// Blue component. +internal struct Srgb(float R, float G, float B) +{ + public float R = R; + public float G = G; + public float B = B; + + public readonly OkLab ToOkLab() + { + float l = 0.4122214708f * this.R + 0.5363325363f * this.G + 0.0514459929f * this.B; + float m = 0.2119034982f * this.R + 0.6806995451f * this.G + 0.1073969566f * this.B; + float s = 0.0883024619f * this.R + 0.2817188376f * this.G + 0.6299787005f * this.B; + + float l_ = (float)Math.Pow(l, 1.0 / 3.0); + float m_ = (float)Math.Pow(m, 1.0 / 3.0); + float s_ = (float)Math.Pow(s, 1.0 / 3.0); + + return new OkLab( + 0.2104542553f * l_ + 0.7936177850f * m_ - 0.0040720468f * s_, + 1.9779984951f * l_ - 2.4285922050f * m_ + 0.4505937099f * s_, + 0.0259040371f * l_ + 0.7827717662f * m_ - 0.8086757660f * s_ + ); + } + + public static Srgb FromColor(Color c) => new(c.R / 255.0f, c.G / 255.0f, c.B / 255.0f); + + public readonly Color ToColor() => Color.FromArgb((byte)(this.R * 255), (byte)(this.G * 255), (byte)(this.B * 255)); + + public readonly Srgb ToLinearSrgb() => + new(Helpers.Clamp(ColorSpace.SrgbToLinearSrgb(R), 0f, 1f), + Helpers.Clamp(ColorSpace.SrgbToLinearSrgb(G), 0f, 1f), + Helpers.Clamp(ColorSpace.SrgbToLinearSrgb(B), 0f, 1f)); + + public readonly Srgb ToSrgb() => + new(Helpers.Clamp(ColorSpace.LinearSrgbToSrgb(R), 0f, 1f), + Helpers.Clamp(ColorSpace.LinearSrgbToSrgb(G), 0f, 1f), + Helpers.Clamp(ColorSpace.LinearSrgbToSrgb(B), 0f, 1f)); + + public override readonly string ToString() + { + return FormattableString.Invariant($"(R:{R}, G:{G}, B:{B})"); + } +} diff --git a/Scintilla.NET/FlagsEditorControl.Designer.cs b/Scintilla.NET/FlagsEditorControl.Designer.cs index 5b74b8a..e5cd15b 100644 --- a/Scintilla.NET/FlagsEditorControl.Designer.cs +++ b/Scintilla.NET/FlagsEditorControl.Designer.cs @@ -84,7 +84,7 @@ private void InitializeComponent() this.button_Ok.Size = new System.Drawing.Size(75, 26); this.button_Ok.TabIndex = 1; this.button_Ok.Text = "OK"; - this.button_Ok.UseVisualStyleBackColor = true; + this.button_Ok.UseVisualStyleBackColor = false; this.button_Ok.Click += new System.EventHandler(this.button_Ok_Click); // // button_Cancel @@ -100,7 +100,7 @@ private void InitializeComponent() this.button_Cancel.Size = new System.Drawing.Size(75, 26); this.button_Cancel.TabIndex = 1; this.button_Cancel.Text = "Cancel"; - this.button_Cancel.UseVisualStyleBackColor = true; + this.button_Cancel.UseVisualStyleBackColor = false; this.button_Cancel.Click += new System.EventHandler(this.button_Cancel_Click); // // FlagsEditorControl diff --git a/Scintilla.NET/FlagsEditorControl.cs b/Scintilla.NET/FlagsEditorControl.cs index 9610a29..598e6bf 100644 --- a/Scintilla.NET/FlagsEditorControl.cs +++ b/Scintilla.NET/FlagsEditorControl.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Drawing; using System.Linq; using System.Windows.Forms; using System.Windows.Forms.Design; @@ -51,7 +52,6 @@ public FlagsEditorControl(IWindowsFormsEditorService editorService, Enum value) Tag = item, Margin = new Padding(3, 0, 3, 0), Padding = Padding.Empty, - UseVisualStyleBackColor = true, }; checkBox.CheckStateChanged += checkBox_CheckStateChanged; this.flowLayoutPanel_CheckBoxList.Controls.Add(checkBox); @@ -67,7 +67,6 @@ public FlagsEditorControl(IWindowsFormsEditorService editorService, Enum value) Tag = (Enum)Enum.ToObject(this.enumType, allBits), Margin = new Padding(3, 0, 3, 0), Padding = Padding.Empty, - UseVisualStyleBackColor = true, }; checkBox.CheckStateChanged += checkBox_CheckStateChanged; this.flowLayoutPanel_CheckBoxList.Controls.Add(checkBox); @@ -81,6 +80,18 @@ public FlagsEditorControl(IWindowsFormsEditorService editorService, Enum value) } } + protected override void OnBackColorChanged(EventArgs e) + { + base.OnBackColorChanged(e); + Helpers.ApplyToControlTree(this, c => { + OkLab backColor = Srgb.FromColor(c.BackColor).ToLinearSrgb().ToOkLab(); + if (backColor.L < 0.5f) + c.ForeColor = Color.White; + else + c.ForeColor = Color.Black; + }); + } + private static ulong CalculateEnumAllValue(Type enumType) { ulong all = 0; diff --git a/Scintilla.NET/Helpers.cs b/Scintilla.NET/Helpers.cs index e558ff3..0ac4d06 100644 --- a/Scintilla.NET/Helpers.cs +++ b/Scintilla.NET/Helpers.cs @@ -106,6 +106,11 @@ public static unsafe byte[] CharToByteStyles(byte[] styles, byte* text, int leng return result; } + public static float Clamp(float f, float min, float max) + { + return f < min ? min : f > max ? max : f; + } + public static int Clamp(int value, int min, int max) { if (value < min) @@ -1260,6 +1265,15 @@ public static int MaxIndex(this IEnumerable source, Func action) + { + foreach (Control child in control.Controls) + { + ApplyToControlTree(child, action); + } + action(control); + } + #endregion Methods #region Types