using AddInPlugin;
using AddInPlugin.Entity;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using GongSolutions.Wpf.DragDrop;
using OpenTap;
using OpenTap.Plugins.BasicSteps;
using OpenTapEditor.UI;
using OpenTapEditor.Util;
using System.Collections.Concurrent;
using System.Collections.ObjectModel;
using System.IO;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using UILib;
namespace OpenTapEditor
{
///
/// StepTree.xaml 的交互逻辑
///
[ObservableObject]
public partial class StepTree : UserControl
{
public event RoutedPropertyChangedEventHandler SelectedItemChanged;
[ObservableProperty]
private TestStepList datasource;
partial void OnDatasourceChanged(TestStepList value)
{
DebugIds.CollectionChanged -= DebugIds_CollectionChanged;
DebugIds = value?.DebugIds ?? new ObservableCollection();
DebugIds.CollectionChanged += DebugIds_CollectionChanged;
}
private void DebugIds_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
OnPropertyChanged(nameof(DebugIds));
}
[ObservableProperty]
ObservableCollection debugIds = new ObservableCollection();
[ObservableProperty]
private Guid debugStepId = Guid.Empty;
[ObservableProperty]
private Guid runningStepId = Guid.Empty;
[ObservableProperty]
private ConcurrentDictionary complatedIds = new ConcurrentDictionary();
public void UpdateStepStatus()
{
OnPropertyChanged(nameof(ComplatedIds));
}
public TestPlan Plan { get; set; }
public IDropTarget DragHandler { get; set; } = new StepTreeDragHandler();
private ITestStep SelectedStep;
[ObservableProperty]
private TestStepList selectedSteps = new TestStepList();
public StepTree()
{
DataContext = this;
InitializeComponent();
}
[RelayCommand]
public void CopyStep()
{
//if (SelectedStep != null)
//{
// CopyStep(SelectedStep);
//}
CopyStep(null);
}
[RelayCommand]
public void PasteStep()
{
PasteStep(SelectedStep);
}
[RelayCommand]
public void DeleteStep()
{
if (SelectedStep != null)
{
DeleteStep(SelectedStep);
}
}
public List RefreshMenuItems(ITestStep selectedStep, bool addChild)
{
var allSteps = StepTypeLoader.Steps;
var StepMenuItems = new List(allSteps.Count);
Dictionary groupMap = new Dictionary();
Dictionary groupAdapMap = new Dictionary();
foreach (ITypeData stepType in allSteps)
{
var display = stepType.GetAttribute();
if (display == null)
{
continue;
}
MenuItem groupItem = null;
var groups = display.Group;
if (groups.Length > 0)
{
var group = groups[groups.Length - 1];
if (!string.IsNullOrEmpty(group))
{
if (!groupMap.ContainsKey(group))
{
groupMap[group] = new MenuItem() { Header = group };
StepMenuItems.Add(groupMap[group]);
}
groupItem = groupMap[group];
}
}
TypeData data = stepType as TypeData;
StepTypeMenuItem menuItem = new StepTypeMenuItem();
string des = string.IsNullOrEmpty(display.Description) ? "" : $" ({display.Description})";
menuItem.Header = $"{display.Name}{des}";
menuItem.Click += addChild ? Add_Child_Step_Click : Add_Generaic_Step_Click;
menuItem.TypeData = stepType;
menuItem.Tag = selectedStep;
if (!addChild)
{
var parent = selectedStep?.Parent?.GetType() ?? typeof(TestPlan);
menuItem.IsEnabled = parent == null || TestStepList.AllowChild(parent, data.Type);
}
else
{
menuItem.IsEnabled = (selectedStep != null && TestStepList.AllowChild(selectedStep.GetType(), data.Type));
}
if (groupItem != null)
{
groupItem.Items.Add(menuItem);
}
else
{
StepMenuItems.Add(menuItem);
}
}
if (StepTypeLoader.CustomSteps != null && StepTypeLoader.CustomSteps.Count > 0)
{
StepMenuItems.Add(new Separator { });
foreach (AdapterData stepType in StepTypeLoader.CustomSteps)
{
MenuItem groupItem = null;
if (!string.IsNullOrEmpty(stepType.GroupBy))
{
if (!groupAdapMap.ContainsKey(stepType.GroupBy))
{
groupAdapMap[stepType.GroupBy] = new MenuItem
{
Header = stepType.GroupBy
};
StepMenuItems.Add(groupAdapMap[stepType.GroupBy]);
}
groupItem = groupAdapMap[stepType.GroupBy];
}
StepTypeMenuItem menuItem = new StepTypeMenuItem();
menuItem.Header = stepType.Name;
menuItem.Click += addChild ? Add_Child_Step_Click : Add_Generaic_Step_Click;
menuItem.TypeData = stepType;
menuItem.Tag = selectedStep;
menuItem.IsEnabled = !addChild || (selectedStep != null && TestStepList.AllowChild(selectedStep?.GetType(), typeof(AdapterData)));
if (groupItem != null)
{
groupItem.Items.Add(menuItem);
}
else
{
StepMenuItems.Add(menuItem);
}
}
}
return StepMenuItems;
}
private void treeList_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs e)
{
}
///
/// 显示菜单
///
///
private void CreateContextMenu(ITestStep obj)
{
ContextMenu contextMenu = new ContextMenu();
MenuItem menuItem = new MenuItem();
menuItem.SetValue(MenuItem.HeaderProperty, new DynamicResourceExtension("插入Step").ProvideValue(null));
menuItem.Tag = obj;
foreach (var item in RefreshMenuItems(obj, false))
{
menuItem.Items.Add(item);
}
contextMenu.Items.Add(menuItem);
if (obj != null)
{
menuItem = new MenuItem();
menuItem.SetValue(MenuItem.HeaderProperty, new DynamicResourceExtension("插入子Step").ProvideValue(null));
menuItem.Tag = obj;
foreach (var item in RefreshMenuItems(obj, true))
{
menuItem.Items.Add(item);
}
contextMenu.Items.Add(menuItem);
}
if (obj != null)
{
menuItem = new MenuItem();
menuItem.SetValue(MenuItem.HeaderProperty, new DynamicResourceExtension("复制").ProvideValue(null));
menuItem.InputGestureText = "Ctrl + C";
menuItem.Click += Copy_Step_Click;
menuItem.Tag = obj;
contextMenu.Items.Add(menuItem);
}
menuItem = new MenuItem();
menuItem.SetValue(MenuItem.HeaderProperty, new DynamicResourceExtension("粘贴").ProvideValue(null));
menuItem.InputGestureText = "Ctrl + V";
menuItem.Click += Paste_Step_Click;
menuItem.Tag = obj;
contextMenu.Items.Add(menuItem);
if (obj != null)
{
menuItem = new MenuItem();
menuItem.SetValue(MenuItem.HeaderProperty, new DynamicResourceExtension("删除").ProvideValue(null));
menuItem.InputGestureText = "Delete";
menuItem.Click += Delete_Step_Click;
menuItem.Tag = obj;
contextMenu.Items.Add(menuItem);
}
this.treeList.ContextMenu = contextMenu;
}
private void AddStep2Plan(ITestStep newStep, ITestStep insertAfter, TestPlan plan)
{
if (insertAfter != null)
{
var parent = insertAfter.Parent;
if (parent is TestPlan pplan)
{
if (TestStepList.AllowChild(typeof(TestPlan), newStep.GetType()))
{
pplan.InsertStep(Datasource.SequenceName,
pplan.GetStepsByName(Datasource.SequenceName).IndexOf(insertAfter) + 1, newStep);
}
}
else if (parent != null && TestStepList.AllowChild(parent.GetType(), newStep.GetType()))
{
parent.InsertStep(Datasource.SequenceName, parent.ChildTestSteps.IndexOf(insertAfter) + 1, newStep);
}
}
else
{
if (TestStepList.AllowChild(typeof(TestPlan), newStep.GetType()))
{
plan.AddStep(Datasource.SequenceName, newStep);
}
}
}
#region add
///
/// 添加同级Step
///
///
///
private void Add_Generaic_Step_Click(object sender, RoutedEventArgs e)
{
ITestStep step = ((StepTypeMenuItem)sender).Tag as ITestStep;
ITestStep newStep = null;
if (((StepTypeMenuItem)sender).TypeData is ITypeData type)
{
newStep = type.CreateInstance() as ITestStep;
}
else if (((StepTypeMenuItem)sender).TypeData is AdapterData data)
{
newStep = new AdapterStep()
{
Name = data.Name,
XmlString = data.Xml
};
}
if (newStep != null)
{
AddStep2Plan(newStep, step, Plan);
}
}
///
/// 添加子Step
///
///
///
private void Add_Child_Step_Click(object sender, RoutedEventArgs e)
{
ITestStep step = ((StepTypeMenuItem)sender).Tag as ITestStep;
if (step == null)
{
return;
}
ITestStep newStep = null;
if (((StepTypeMenuItem)sender).TypeData is ITypeData type)
{
newStep = type.CreateInstance() as ITestStep;
}
else if (((StepTypeMenuItem)sender).TypeData is AdapterData data)
{
newStep = new AdapterStep()
{
Name = data.Name,
XmlString = data.Xml
};
}
if (newStep != null)
{
step.AddStep(Datasource.SequenceName, newStep);
}
}
private void DeleteStep(ITestStep step)
{
foreach (var child in SelectedSteps)
{
if (child.Parent is ITestStep)
{
child.Parent.ChildTestSteps.Remove(child);
}
else
{
Datasource.Remove(child);
}
}
SelectedSteps.Clear();
SelectedStep = null;
}
private void Delete_Step_Click(object sender, RoutedEventArgs e)
{
ITestStep step = ((MenuItem)sender).Tag as ITestStep;
DeleteStep(step);
}
private void CopyStep(ITestStep step)
{
try
{
var target = new TestStepList();
foreach (var child in Datasource.RecursivelyGetAllTestSteps(TestStepSearch.All))
{
if (SelectedSteps.Contains(child))
{
target.Add(child);
}
}
TapSerializer serializer = new TapSerializer();
using (var str = new MemoryStream())
{
serializer.Serialize(str, target);
Clipboard.SetText(Encoding.UTF8.GetString(str.ToArray()));
}
}
catch (Exception ex)
{
MessageBox.Show($"{ex.Message}");
}
}
private void Copy_Step_Click(object sender, RoutedEventArgs e)
{
ITestStep step = ((MenuItem)sender).Tag as ITestStep;
CopyStep(step);
}
private void PasteStep(ITestStep targetStep)
{
try
{
TapSerializer serializer = new TapSerializer();
TestStepList newSteps = (TestStepList)serializer.DeserializeFromString(Clipboard.GetText());
if (newSteps == null)
{
return;
}
var last = targetStep;
foreach (var child in newSteps.RecursivelyGetAllTestSteps(TestStepSearch.All))
{
child.Id = Guid.NewGuid();
}
foreach (var child in newSteps)
{
AddStep2Plan(child, last, Plan);
last = child;
}
}
catch (Exception ex)
{
MessageBox.Show($"粘贴错误: {ex.Message}", "错误");
}
}
private void Paste_Step_Click(object sender, RoutedEventArgs e)
{
ITestStep step = ((MenuItem)sender).Tag as ITestStep;
PasteStep(step);
}
#endregion
private void DebuggerBtn_HandleClick(object sender, EventArgs e)
{
DebuggerBtn btn = (DebuggerBtn)sender;
if (Datasource.DebugIds.Contains(btn.StepId))
{
Datasource.DebugIds.Remove(btn.StepId);
}
else
{
Datasource.DebugIds.Add(btn.StepId);
}
}
public bool NeedBreak(Guid id)
{
if (Datasource == null) return false;
return Datasource.DebugIds.Contains(id);
}
private void TreeViewItem_PreviewMouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
DependencyObject current = e.OriginalSource as DependencyObject;
while (current != null)
{
// 如果找到需要处理双击的控件(例如Button、TextBox等),则不阻止事件
if (current is EditBox eb && eb.CanEdit)
{
return; // 让事件继续传递
}
current = VisualTreeHelper.GetParent(current);
}
e.Handled = true;
}
private void treeList_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
TreeListViewItem item = FindControl(e.OriginalSource);
if (item == null) return;
ITestStep step = (ITestStep)item.DataContext;
if ((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
{
if (SelectedSteps.Contains(step))
{
SelectedSteps.Remove(step);
}
else
{
var parents = step.GetParents();
foreach (var parent in parents)
{
if (parent is ITestStep pStep)
{
SelectedSteps.Remove(pStep);
}
}
foreach (var child in step.RecursivelyGetChildSteps(TestStepSearch.All))
{
SelectedSteps.Remove(child);
}
SelectedSteps.Add(step);
}
return;
}
var old = SelectedStep;
SelectedStep = step;
SelectedItemChanged?.Invoke(sender, new RoutedPropertyChangedEventArgs(old, SelectedStep));
SelectedSteps = new TestStepList { SelectedStep };
}
private void treeList_PreviewMouseRightButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
TreeListViewItem item = FindControl(e.OriginalSource);
if (item != null)
{
item.Focus();
var step = item.DataContext as ITestStep;
SelectedStep = step;
if (!SelectedSteps.Contains(step))
{
SelectedSteps = new TestStepList { SelectedStep };
}
CreateContextMenu(step);
}
else
{
CreateContextMenu(null);
}
}
private T FindControl(object obj)
{
if (obj == null)
{
return default(T);
}
if (obj is T target)
{
return target;
}
return FindControl(VisualTreeHelper.GetParent((DependencyObject)obj));
}
public void Unload()
{
if (Datasource != null)
{
Datasource.DebugIds.CollectionChanged -= DebugIds_CollectionChanged;
}
Datasource = null;
}
}
}