Add TabColorConverter to fix Color to String conversion issue

Co-authored-by: Kvarkas <3611964+Kvarkas@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2025-10-07 20:15:32 +00:00
parent 5d623d80eb
commit 347546ee0e
3 changed files with 275 additions and 1 deletions

View File

@@ -158,7 +158,7 @@ namespace mRemoteNG.Connection
[LocalizedAttributes.LocalizedCategory(nameof(Language.Display)),
LocalizedAttributes.LocalizedDisplayName(nameof(Language.TabColor)),
LocalizedAttributes.LocalizedDescription(nameof(Language.PropertyDescriptionTabColor)),
TypeConverter(typeof(ColorConverter))]
TypeConverter(typeof(MiscTools.TabColorConverter))]
public virtual string TabColor
{
get => GetPropertyValue("TabColor", _tabColor);

View File

@@ -268,5 +268,131 @@ namespace mRemoteNG.Tools
return svc;
}
}
public class TabColorConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
{
return sourceType == typeof(string) || sourceType == typeof(Color) || base.CanConvertFrom(context, sourceType);
}
public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType)
{
return destinationType == typeof(string) || destinationType == typeof(Color) || base.CanConvertTo(context, destinationType);
}
public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object? value)
{
if (value == null || (value is string str && string.IsNullOrWhiteSpace(str)))
{
return string.Empty;
}
if (value is string stringValue)
{
return stringValue;
}
if (value is Color colorValue)
{
// Convert Color to string representation
// Use named color if it's a known color, otherwise use hex format
if (colorValue.IsNamedColor)
{
return colorValue.Name;
}
else
{
// Return hex format without alpha if fully opaque, otherwise include alpha
if (colorValue.A == 255)
{
return $"#{colorValue.R:X2}{colorValue.G:X2}{colorValue.B:X2}";
}
else
{
return $"#{colorValue.A:X2}{colorValue.R:X2}{colorValue.G:X2}{colorValue.B:X2}";
}
}
}
return base.ConvertFrom(context, culture, value);
}
public override object ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType)
{
if (destinationType == typeof(string))
{
if (value == null || (value is string str && string.IsNullOrWhiteSpace(str)))
{
return string.Empty;
}
return value.ToString() ?? string.Empty;
}
if (destinationType == typeof(Color))
{
if (value == null || (value is string str && string.IsNullOrWhiteSpace(str)))
{
return Color.Empty;
}
if (value is string stringValue)
{
try
{
ColorConverter converter = new ColorConverter();
return converter.ConvertFromString(stringValue) ?? Color.Empty;
}
catch
{
return Color.Empty;
}
}
}
return base.ConvertTo(context, culture, value, destinationType) ?? throw new InvalidOperationException("Base conversion returned null.");
}
public override bool GetStandardValuesSupported(ITypeDescriptorContext? context)
{
return true;
}
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext? context)
{
// Provide a list of common colors for the dropdown
Color[] colors =
[
Color.Red,
Color.Orange,
Color.Yellow,
Color.Green,
Color.Blue,
Color.Purple,
Color.Pink,
Color.Brown,
Color.Black,
Color.White,
Color.Gray,
Color.LightGray,
Color.DarkGray,
Color.Cyan,
Color.Magenta,
Color.Lime,
Color.Navy,
Color.Teal,
Color.Maroon,
Color.Olive
];
return new StandardValuesCollection(colors);
}
public override bool GetStandardValuesExclusive(ITypeDescriptorContext? context)
{
// Return false to allow custom values (hex codes or other color names)
return false;
}
}
}
}

View File

@@ -0,0 +1,148 @@
using System.Drawing;
using mRemoteNG.Tools;
using NUnit.Framework;
namespace mRemoteNGTests.Tools
{
public class TabColorConverterTests
{
private MiscTools.TabColorConverter _converter;
[SetUp]
public void Setup()
{
_converter = new MiscTools.TabColorConverter();
}
[TestCase(typeof(string), true)]
[TestCase(typeof(Color), true)]
public void CanConvertFrom(Type typeToConvertFrom, bool expectedOutcome)
{
var actualOutcome = _converter.CanConvertFrom(typeToConvertFrom);
Assert.That(actualOutcome, Is.EqualTo(expectedOutcome));
}
[TestCase(typeof(string), true)]
[TestCase(typeof(Color), true)]
public void CanConvertTo(Type typeToConvertTo, bool expectedOutcome)
{
var actualOutcome = _converter.CanConvertTo(typeToConvertTo);
Assert.That(actualOutcome, Is.EqualTo(expectedOutcome));
}
[Test]
public void ConvertFromColorToStringNamedColor()
{
var color = Color.Red;
var result = _converter.ConvertFrom(color);
Assert.That(result, Is.EqualTo("Red"));
}
[Test]
public void ConvertFromColorToStringCustomColor()
{
var color = Color.FromArgb(255, 128, 64, 32);
var result = _converter.ConvertFrom(color);
Assert.That(result, Is.EqualTo("#80401F"));
}
[Test]
public void ConvertFromColorToStringCustomColorWithAlpha()
{
var color = Color.FromArgb(128, 255, 0, 0);
var result = _converter.ConvertFrom(color);
Assert.That(result, Is.EqualTo("#80FF0000"));
}
[Test]
public void ConvertFromStringReturnsString()
{
var colorString = "Blue";
var result = _converter.ConvertFrom(colorString);
Assert.That(result, Is.EqualTo("Blue"));
}
[Test]
public void ConvertFromHexStringReturnsString()
{
var colorString = "#FF0000";
var result = _converter.ConvertFrom(colorString);
Assert.That(result, Is.EqualTo("#FF0000"));
}
[Test]
public void ConvertFromNullReturnsEmptyString()
{
var result = _converter.ConvertFrom(null);
Assert.That(result, Is.EqualTo(string.Empty));
}
[Test]
public void ConvertFromEmptyStringReturnsEmptyString()
{
var result = _converter.ConvertFrom("");
Assert.That(result, Is.EqualTo(string.Empty));
}
[Test]
public void ConvertToStringFromString()
{
var colorString = "Green";
var result = _converter.ConvertTo(colorString, typeof(string));
Assert.That(result, Is.EqualTo("Green"));
}
[Test]
public void ConvertToColorFromNamedString()
{
var colorString = "Red";
var result = _converter.ConvertTo(colorString, typeof(Color));
Assert.That(result, Is.EqualTo(Color.Red));
}
[Test]
public void ConvertToColorFromHexString()
{
var colorString = "#FF0000";
var result = _converter.ConvertTo(colorString, typeof(Color));
var expectedColor = Color.FromArgb(255, 255, 0, 0);
Assert.That(result, Is.EqualTo(expectedColor));
}
[Test]
public void ConvertToColorFromEmptyStringReturnsEmpty()
{
var result = _converter.ConvertTo("", typeof(Color));
Assert.That(result, Is.EqualTo(Color.Empty));
}
[Test]
public void ConvertToColorFromNullReturnsEmpty()
{
var result = _converter.ConvertTo(null, typeof(Color));
Assert.That(result, Is.EqualTo(Color.Empty));
}
[Test]
public void GetStandardValuesSupportedReturnsTrue()
{
var result = _converter.GetStandardValuesSupported(null);
Assert.That(result, Is.True);
}
[Test]
public void GetStandardValuesReturnsColorList()
{
var result = _converter.GetStandardValues(null);
Assert.That(result, Is.Not.Null);
Assert.That(result.Count, Is.GreaterThan(0));
}
[Test]
public void GetStandardValuesExclusiveReturnsFalse()
{
var result = _converter.GetStandardValuesExclusive(null);
Assert.That(result, Is.False);
}
}
}