Skip to content

Commit

Permalink
Improve & use the new FlagsEditor for all flags
Browse files Browse the repository at this point in the history
Turns out Scintilla had a custom editor for flags called `FlagsEnumConverter` before. Unaware of the first one, I wrote a new one called `FlagsEditor` for `ChangeHistory` enum. I may be biased but it turned out pretty good so I think it would be cool to use it for all flags.
  • Loading branch information
ahmetsait committed May 11, 2024
1 parent ec5df98 commit 30d142e
Show file tree
Hide file tree
Showing 7 changed files with 503 additions and 140 deletions.
75 changes: 75 additions & 0 deletions Scintilla.NET/FlagsConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using System;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Text;

namespace ScintillaNET;

internal class FlagsConverter : TypeConverter
{
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return destinationType == typeof(string) || base.CanConvertTo(context, destinationType);
}

public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
}

public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (value is Enum valueEnum && destinationType == typeof(string))
{
Type enumType = valueEnum.GetType();
ulong valueBits = Convert.ToUInt64(valueEnum);
if (valueBits == 0)
{
return Enum.ToObject(enumType, 0).ToString();
}
ulong bits = 0;
StringBuilder sb = new StringBuilder();
void Add(Enum item, ulong itemBits)
{
if (itemBits != 0 && valueEnum.HasFlag(item))
{
bits |= itemBits;
if (sb.Length > 0)
sb.Append(" | ");
sb.Append(item);
}
}
foreach (Enum item in Enum.GetValues(enumType))
{
ulong itemBits = Convert.ToUInt64(item);
if (Helpers.PopCount(itemBits) == 1)
Add(item, itemBits);
}
foreach (Enum item in Enum.GetValues(enumType))
{
ulong itemBits = Convert.ToUInt64(item);
if (Helpers.PopCount(itemBits) > 1 && (bits & itemBits) != itemBits)
Add(item, itemBits);
}
return sb.ToString();
}
return base.ConvertTo(context, culture, value, destinationType);
}

public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string str)
{
Type t = context.PropertyDescriptor.PropertyType;
ulong bits = 0;
var nameList = str.Split('|').Select(x => x.Trim());
foreach (var name in nameList)
{
bits |= Convert.ToUInt64(Enum.Parse(t, name));
}
return Enum.ToObject(t, bits);
}
return base.ConvertFrom(context, culture, value);
}
}
136 changes: 3 additions & 133 deletions Scintilla.NET/FlagsEditor.cs
Original file line number Diff line number Diff line change
@@ -1,156 +1,26 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing.Design;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Forms.Design;

namespace ScintillaNET;

internal class FlagsConverter : TypeConverter
{
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return destinationType == typeof(string) || base.CanConvertTo(context, destinationType);
}

public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
}

public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (value is Enum e && destinationType == typeof(string))
{
if (Convert.ToUInt64(e) == 0)
{
return Enum.ToObject(e.GetType(), 0).ToString();
}
StringBuilder sb = new StringBuilder();
foreach (Enum item in Enum.GetValues(e.GetType()))
{
if (Convert.ToUInt64(item) != 0 && e.HasFlag(item))
{
if (sb.Length > 0)
sb.Append(" | ");
sb.Append(item);
}
}
return sb.ToString();
}
return base.ConvertTo(context, culture, value, destinationType);
}

public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string str)
{
Type t = context.PropertyDescriptor.PropertyType;
ulong bits = 0;
var nameList = str.Split('|').Select(x => x.Trim());
foreach (var name in nameList)
{
bits |= Convert.ToUInt64(Enum.Parse(t, name));
}
return Enum.ToObject(t, bits);
}
return base.ConvertFrom(context, culture, value);
}
}

internal class FlagsEditor : UITypeEditor
{
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) => UITypeEditorEditStyle.DropDown;

public override bool IsDropDownResizable => true;

private int inCheck = 0;

private static ulong CombineEnumList(IEnumerable checkedList)
{
ulong bits = 0;
foreach (var item in checkedList)
{
bits |= Convert.ToUInt64(item);
}
return bits;
}

public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
if (value is Enum e && context.PropertyDescriptor.Attributes.OfType<FlagsAttribute>().Any())
{
IWindowsFormsEditorService svc = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
Type enumType = e.GetType();

CheckedListBox checkedListBox = new() {
Dock = DockStyle.Fill,
CheckOnClick = true,
};
checkedListBox.ItemCheck += (object sender, ItemCheckEventArgs e) => {
if (inCheck > 0)
return;
inCheck++;
try
{
ulong bits = CombineEnumList(checkedListBox.CheckedItems);
ulong change = Convert.ToUInt64(checkedListBox.Items[e.Index]);
if (e.NewValue == CheckState.Checked)
bits |= change;
else if (e.NewValue == CheckState.Unchecked)
bits &= ~change;
Enum enumFinal = (Enum)Enum.ToObject(enumType, bits);
for (int i = 0; i < checkedListBox.Items.Count; i++)
{
Enum itemValue = (Enum)checkedListBox.Items[i];
checkedListBox.SetItemChecked(i, enumFinal.HasFlag(itemValue));
}
}
finally
{
inCheck--;
}
};
inCheck++;
try
{
foreach (Enum item in Enum.GetValues(enumType))
{
if (Convert.ToUInt64(item) != 0)
checkedListBox.Items.Add(item, e.HasFlag(item));
}
}
finally
{
inCheck--;
}
Button okBtton = new() {
Dock = DockStyle.Bottom,
AutoSize = true,
AutoSizeMode = AutoSizeMode.GrowAndShrink,
UseVisualStyleBackColor = true,
};
UserControl userControl = new();
userControl.Controls.Add(checkedListBox);
userControl.Controls.Add(okBtton);
okBtton.Text = NativeMethods.GetMessageBoxString(0); // OK
okBtton.Click += (object sender, EventArgs e) => {
ulong bits = 0;
foreach (Enum item in checkedListBox.CheckedItems)
{
bits |= Convert.ToUInt64(item);
}
value = Enum.ToObject(enumType, bits);
svc.CloseDropDown();
};
svc.DropDownControl(userControl);
return value;
var control = new FlagsEditorControl(svc, e);
svc.DropDownControl(control);
return control.Value;
}
else
return base.EditValue(context, provider, value);
Expand Down
126 changes: 126 additions & 0 deletions Scintilla.NET/FlagsEditorControl.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 30d142e

Please sign in to comment.