io commander

This commit is contained in:
meelstorm
2025-07-19 15:57:59 +02:00
parent 40d74d6da6
commit cd70088d9a
46 changed files with 2198 additions and 28 deletions

View File

@@ -1,4 +1,6 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Windows.Forms;
using Inspectron.Settings.Attributes;
@@ -36,7 +38,12 @@ public class DefaultControlFactory : IControlFactory
};
}
if (type == typeof(string) && name.EndsWith("Path", StringComparison.OrdinalIgnoreCase))
// Check for List<T> types
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>))
{
return CreateListControl(name, description, type, initialValue, valueChangedCallback, optionsWindow, settingDescription, previewLabel, getPreview);
}
else if (type == typeof(string) && name.EndsWith("Path", StringComparison.OrdinalIgnoreCase))
{
return CreatePathControl(name, description, initialValue, valueChangedCallback, optionsWindow, settingDescription, previewLabel, getPreview);
}
@@ -62,6 +69,63 @@ public class DefaultControlFactory : IControlFactory
}
}
private Control CreateListControl(string name, string description, Type listType, object initialValue,
Action<object> valueChangedCallback, OptionsWindow optionsWindow, string settingDescription,
Label previewLabel, Func<object, string> getPreview)
{
var elementType = listType.GetGenericArguments()[0];
var list = (IList)initialValue ?? (IList)Activator.CreateInstance(listType);
var label = new Label { Text = description, AutoSize = true, Padding = new Padding(0, 5, 0, 0) };
var textBox = new TextBox
{
Text = GetListDisplayText(list),
Width = 500,
Height = 60,
Multiline = true,
ReadOnly = true,
ScrollBars = ScrollBars.Vertical
};
var editButton = new Button { Text = "Edit", AutoSize = true };
editButton.Click += (s, e) =>
{
using (var listEditor = new ListEditorDialog(elementType, list, this, optionsWindow, name))
{
if (listEditor.ShowDialog(optionsWindow) == DialogResult.OK)
{
var newList = listEditor.GetEditedList();
list.Clear();
foreach (object o in newList)
{
list.Add(o);
}
textBox.Text = GetListDisplayText(list);
valueChangedCallback(list);
if (previewLabel != null && getPreview != null)
previewLabel.Text = getPreview(list);
}
}
};
return CreateOuterPanel(settingDescription, previewLabel, label, textBox, editButton);
}
private string GetListDisplayText(IList list)
{
if (list == null || list.Count == 0)
return "(empty list)";
var items = new List<string>();
foreach (var item in list)
{
items.Add(item?.ToString() ?? "(null)");
}
return string.Join(Environment.NewLine, items);
}
private Control CreatePathControl(string name, string description, object initialValue, Action<object> valueChangedCallback,
OptionsWindow optionsWindow, string settingDescription, Label previewLabel, Func<object, string> getPreview)
{
@@ -186,3 +250,9 @@ public class DefaultControlFactory : IControlFactory
return outerPanel;
}
}
// List Editor Dialog
// Item Editor Dialog
// Helper class for simple property info

View File

@@ -0,0 +1,163 @@
using System;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
namespace Inspectron.Settings.Windows.Configuration;
public partial class ItemEditorDialog : Form
{
private readonly Type _itemType;
private object _workingValue;
private readonly DefaultControlFactory _controlFactory;
private readonly OptionsWindow _parentOptionsWindow;
private FlowLayoutPanel _propertyPanel;
private Button _okButton;
private Button _cancelButton;
public ItemEditorDialog(Type itemType, object initialValue, DefaultControlFactory controlFactory, OptionsWindow parentOptionsWindow)
{
_itemType = itemType;
_workingValue = CloneObject(initialValue);
_controlFactory = controlFactory;
_parentOptionsWindow = parentOptionsWindow;
InitializeComponent();
CreatePropertyControls();
}
private void InitializeComponent()
{
this.Text = $"Edit {_itemType.Name}";
this.Size = new System.Drawing.Size(650, 420);
this.StartPosition = FormStartPosition.CenterParent;
this.FormBorderStyle = FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
_propertyPanel = new FlowLayoutPanel
{
Location = new System.Drawing.Point(12, 12),
Size = new System.Drawing.Size(620, 320),
Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom,
FlowDirection = FlowDirection.TopDown,
AutoScroll = true
};
_okButton = new Button
{
Text = "OK",
Location = new System.Drawing.Point(470, 340),
Size = new System.Drawing.Size(75, 23),
Anchor = AnchorStyles.Bottom | AnchorStyles.Right,
DialogResult = DialogResult.OK
};
_cancelButton = new Button
{
Text = "Cancel",
Location = new System.Drawing.Point(550, 340),
Size = new System.Drawing.Size(75, 23),
Anchor = AnchorStyles.Bottom | AnchorStyles.Right,
DialogResult = DialogResult.Cancel
};
this.Controls.AddRange(new Control[] { _propertyPanel, _okButton, _cancelButton });
}
private void CreatePropertyControls()
{
if (_itemType.IsValueType || _itemType == typeof(string))
{
// For simple types, create a single control
CreateSimpleValueControl();
}
else
{
// For complex types, create controls for each property
CreateComplexObjectControls();
}
}
private void CreateSimpleValueControl()
{
var dummyProperty = new SimplePropertyInfo(_itemType, "Value");
var control = _controlFactory.CreateControl("Value", "Value", dummyProperty, _workingValue,
newValue => _workingValue = newValue, _parentOptionsWindow);
_propertyPanel.Controls.Add(control);
}
private void CreateComplexObjectControls()
{
var properties = _itemType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => p.CanRead && p.CanWrite)
.ToArray();
foreach (var property in properties)
{
var currentValue = property.GetValue(_workingValue);
var control = _controlFactory.CreateControl(property.Name, property.Name, property, currentValue,
newValue => property.SetValue(_workingValue, newValue), _parentOptionsWindow);
_propertyPanel.Controls.Add(control);
}
}
public object GetEditedValue()
{
return _workingValue;
}
private object CloneObject(object obj)
{
if (obj == null) return GetDefaultValue(_itemType);
var type = obj.GetType();
if (type.IsValueType || type == typeof(string))
{
return obj;
}
try
{
var clone = Activator.CreateInstance(type);
foreach (var prop in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (prop.CanRead && prop.CanWrite)
{
prop.SetValue(clone, prop.GetValue(obj));
}
}
return clone;
}
catch
{
return GetDefaultValue(type);
}
}
private object GetDefaultValue(Type type)
{
if (type.IsValueType)
{
return Activator.CreateInstance(type);
}
else if (type == typeof(string))
{
return "";
}
else
{
try
{
return Activator.CreateInstance(type);
}
catch
{
return null;
}
}
}
}

View File

@@ -0,0 +1,235 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Windows.Forms;
namespace Inspectron.Settings.Windows.Configuration;
public partial class ListEditorDialog : Form
{
private readonly Type _elementType;
private readonly IList _originalList;
private readonly IList _workingList;
private readonly DefaultControlFactory _controlFactory;
private readonly OptionsWindow _parentOptionsWindow;
private readonly string _name;
private ListBox _listBox;
private Button _addButton;
private Button _editButton;
private Button _removeButton;
private Button _okButton;
private Button _cancelButton;
public ListEditorDialog(Type elementType, IList originalList, DefaultControlFactory controlFactory, OptionsWindow parentOptionsWindow, string name)
{
_elementType = elementType;
_originalList = originalList;
_controlFactory = controlFactory;
_parentOptionsWindow = parentOptionsWindow;
_name = name;
// Create a working copy of the list
var listType = typeof(List<>).MakeGenericType(elementType);
_workingList = (IList)Activator.CreateInstance(listType);
foreach (var item in originalList)
{
_workingList.Add(CloneObject(item));
}
InitializeComponent();
LoadListItems();
}
private void InitializeComponent()
{
this.Text = $"Edit {_name}";
this.Size = new System.Drawing.Size(600, 400);
this.StartPosition = FormStartPosition.CenterParent;
this.FormBorderStyle = FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
// Create controls
_listBox = new ListBox
{
Location = new System.Drawing.Point(12, 12),
Size = new System.Drawing.Size(460, 300),
Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom
};
_addButton = new Button
{
Text = "Add",
Location = new System.Drawing.Point(490, 12),
Size = new System.Drawing.Size(75, 23),
Anchor = AnchorStyles.Top | AnchorStyles.Right
};
_editButton = new Button
{
Text = "Edit",
Location = new System.Drawing.Point(490, 45),
Size = new System.Drawing.Size(75, 23),
Anchor = AnchorStyles.Top | AnchorStyles.Right,
Enabled = false
};
_removeButton = new Button
{
Text = "Remove",
Location = new System.Drawing.Point(490, 78),
Size = new System.Drawing.Size(75, 23),
Anchor = AnchorStyles.Top | AnchorStyles.Right,
Enabled = false
};
_okButton = new Button
{
Text = "OK",
Location = new System.Drawing.Point(410, 330),
Size = new System.Drawing.Size(75, 23),
Anchor = AnchorStyles.Bottom | AnchorStyles.Right,
DialogResult = DialogResult.OK
};
_cancelButton = new Button
{
Text = "Cancel",
Location = new System.Drawing.Point(490, 330),
Size = new System.Drawing.Size(75, 23),
Anchor = AnchorStyles.Bottom | AnchorStyles.Right,
DialogResult = DialogResult.Cancel
};
// Add event handlers
_listBox.SelectedIndexChanged += ListBox_SelectedIndexChanged;
_addButton.Click += AddButton_Click;
_editButton.Click += EditButton_Click;
_removeButton.Click += RemoveButton_Click;
// Add controls to form
this.Controls.AddRange(new Control[] { _listBox, _addButton, _editButton, _removeButton, _okButton, _cancelButton });
}
private void LoadListItems()
{
_listBox.Items.Clear();
foreach (var item in _workingList)
{
_listBox.Items.Add(item?.ToString() ?? "(null)");
}
}
private void ListBox_SelectedIndexChanged(object sender, EventArgs e)
{
bool hasSelection = _listBox.SelectedIndex >= 0;
_editButton.Enabled = hasSelection;
_removeButton.Enabled = hasSelection;
}
private void AddButton_Click(object sender, EventArgs e)
{
var defaultValue = GetDefaultValue(_elementType);
using (var itemEditor = new ItemEditorDialog(_elementType, defaultValue, _controlFactory, _parentOptionsWindow))
{
if (itemEditor.ShowDialog(this) == DialogResult.OK)
{
_workingList.Add(itemEditor.GetEditedValue());
LoadListItems();
_listBox.SelectedIndex = _listBox.Items.Count - 1;
}
}
}
private void EditButton_Click(object sender, EventArgs e)
{
if (_listBox.SelectedIndex >= 0)
{
var selectedItem = _workingList[_listBox.SelectedIndex];
using (var itemEditor = new ItemEditorDialog(_elementType, selectedItem, _controlFactory, _parentOptionsWindow))
{
if (itemEditor.ShowDialog(this) == DialogResult.OK)
{
_workingList[_listBox.SelectedIndex] = itemEditor.GetEditedValue();
LoadListItems();
}
}
}
}
private void RemoveButton_Click(object sender, EventArgs e)
{
if (_listBox.SelectedIndex >= 0)
{
int selectedIndex = _listBox.SelectedIndex;
_workingList.RemoveAt(selectedIndex);
LoadListItems();
// Maintain selection if possible
if (_listBox.Items.Count > 0)
{
_listBox.SelectedIndex = Math.Min(selectedIndex, _listBox.Items.Count - 1);
}
}
}
public IList GetEditedList()
{
return _workingList;
}
private object GetDefaultValue(Type type)
{
if (type.IsValueType)
{
return Activator.CreateInstance(type);
}
else if (type == typeof(string))
{
return "";
}
else
{
try
{
return Activator.CreateInstance(type);
}
catch
{
return null;
}
}
}
private object CloneObject(object obj)
{
if (obj == null) return null;
var type = obj.GetType();
if (type.IsValueType || type == typeof(string))
{
return obj;
}
// For complex objects, try to create a new instance and copy properties
try
{
var clone = Activator.CreateInstance(type);
foreach (var prop in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (prop.CanRead && prop.CanWrite)
{
prop.SetValue(clone, prop.GetValue(obj));
}
}
return clone;
}
catch
{
return obj; // Fallback to original object if cloning fails
}
}
}

View File

@@ -21,6 +21,7 @@ public partial class OptionsWindow
// treeViewCategories
//
treeViewCategories.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left;
treeViewCategories.HideSelection = false;
treeViewCategories.Location = new Point(12, 12);
treeViewCategories.Name = "treeViewCategories";
treeViewCategories.Size = new Size(200, 582);

View File

@@ -145,7 +145,10 @@ public partial class OptionsWindow : Form
setting.Description,
setting.Property,
setting.Value,
(newValue) => setting.Value = newValue,
(newValue) =>
{
setting.Value = newValue;
},
this);
flowLayoutPanelSettings.Controls.Add(control);

View File

@@ -0,0 +1,34 @@
using System;
using System.Reflection;
namespace Inspectron.Settings.Windows.Configuration;
internal class SimplePropertyInfo : PropertyInfo
{
private readonly Type _propertyType;
private readonly string _name;
public SimplePropertyInfo(Type propertyType, string name)
{
_propertyType = propertyType;
_name = name;
}
public override PropertyAttributes Attributes => PropertyAttributes.None;
public override bool CanRead => true;
public override bool CanWrite => true;
public override Type PropertyType => _propertyType;
public override string Name => _name;
public override Type DeclaringType => typeof(object);
public override Type ReflectedType => typeof(object);
public override MethodInfo[] GetAccessors(bool nonPublic) => new MethodInfo[0];
public override MethodInfo GetGetMethod(bool nonPublic) => null;
public override ParameterInfo[] GetIndexParameters() => new ParameterInfo[0];
public override MethodInfo GetSetMethod(bool nonPublic) => null;
public override object GetValue(object obj, BindingFlags invokeAttr, Binder binder, object[] index, System.Globalization.CultureInfo culture) => obj;
public override void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, object[] index, System.Globalization.CultureInfo culture) { }
public override object[] GetCustomAttributes(Type attributeType, bool inherit) => new object[0];
public override object[] GetCustomAttributes(bool inherit) => new object[0];
public override bool IsDefined(Type attributeType, bool inherit) => false;
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
@@ -755,6 +756,20 @@ namespace Inspectron.Settings
{
v = Enum.Parse(setting.PropertyDescriptor.PropertyType, setting.ValueString);
}
// if is list
else if (setting.PropertyDescriptor.PropertyType.IsGenericType &&
setting.PropertyDescriptor.PropertyType.GetGenericTypeDefinition() == typeof(List<>))
{
Type itemType = setting.PropertyDescriptor.PropertyType.GetGenericArguments()[0];
TypeConverter converter = TypeDescriptor.GetConverter(itemType);
string[] items = setting.ValueString.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
IList list = (IList)Activator.CreateInstance(setting.PropertyDescriptor.PropertyType);
foreach (string item in items)
{
list.Add(converter.ConvertFromInvariantString(item.Trim()));
}
v = list;
}
else
{
v = Convert.ChangeType(setting.ValueString, setting.PropertyDescriptor.PropertyType);
@@ -815,7 +830,7 @@ namespace Inspectron.Settings
}
}
ApplyStoredSettings();
//ApplyStoredSettings();
Reloaded.Raise(this, EventArgs.Empty);
}
catch (Exception ex)