diff --git a/AIStudio.Wpf.Mind/Themes/MindNode.xaml b/AIStudio.Wpf.Mind/Themes/MindNode.xaml index 445caea..b2eef5f 100644 --- a/AIStudio.Wpf.Mind/Themes/MindNode.xaml +++ b/AIStudio.Wpf.Mind/Themes/MindNode.xaml @@ -135,7 +135,7 @@ - + diff --git a/AIStudio.Wpf.Mind/ViewModels/MindNode.cs b/AIStudio.Wpf.Mind/ViewModels/MindNode.cs index a2c1847..5c574b5 100644 --- a/AIStudio.Wpf.Mind/ViewModels/MindNode.cs +++ b/AIStudio.Wpf.Mind/ViewModels/MindNode.cs @@ -5,6 +5,7 @@ using System.ComponentModel; using System.IO; using System.Linq; using System.Text; +using System.Windows.Controls; using System.Windows.Media; using AIStudio.Wpf.DiagramDesigner; using AIStudio.Wpf.DiagramDesigner.Algorithms; @@ -42,7 +43,7 @@ namespace AIStudio.Wpf.Mind.ViewModels AddChildCommand = new SimpleCommand(Command_Enable, ExecuteAddChildCommand); AddParentCommand = new SimpleCommand(Level_Enable, ExecuteAddParentCommand); AddPeerCommand = new SimpleCommand(Level_Enable, ExecuteAddPeerCommand); - DeleteCommand = new SimpleCommand(Command_Enable, ExecuteDeleteCommand); + DeleteCommand = new SimpleCommand(Level_Enable, ExecuteDeleteCommand); MoveForwardCommand = new SimpleCommand(Command_Enable, ExecuteMoveForwardCommand); MoveBackCommand = new SimpleCommand(Command_Enable, ExecuteMoveBackCommand); BuildMenuOptions(); @@ -50,7 +51,7 @@ namespace AIStudio.Wpf.Mind.ViewModels this.PropertyChanged += this.Item_PropertyChanged; } - private void LevelInit(bool init =false) + private void LevelInit(bool init = false) { switch (NodeLevel) { @@ -108,8 +109,6 @@ namespace AIStudio.Wpf.Mind.ViewModels } } - - private bool Level_Enable(object obj) { if (Command_Enable(obj) == false) return false; @@ -117,10 +116,19 @@ namespace AIStudio.Wpf.Mind.ViewModels return NodeLevel != NodeLevel.Level1; } + #region 属性 [Browsable(false)] + private NodeLevel _nodeLevel; public NodeLevel NodeLevel { - get; set; + get + { + return _nodeLevel; + } + set + { + SetProperty(ref _nodeLevel, value); + } } private double _cornerRadius = 3; @@ -147,12 +155,7 @@ namespace AIStudio.Wpf.Mind.ViewModels { SetProperty(ref _isExpanded, value); } - } - - public bool LayoutUpdating - { - get; set; - } + } public SizeBase Spacing { @@ -187,6 +190,25 @@ namespace AIStudio.Wpf.Mind.ViewModels get; set; } + private MindType _mindType; + public MindType MindType + { + get + { + return _mindType; + } + set + { + SetProperty(ref _mindType, value); + } + } + + private bool _layoutUpdating; + + private bool _isRightLayout = true; + #endregion + + #region 命令 public SimpleCommand AddParentCommand { get; private set; @@ -216,7 +238,9 @@ namespace AIStudio.Wpf.Mind.ViewModels { get; private set; } + #endregion + #region 菜单 private void BuildMenuOptions() { menuOptions = new ObservableCollection(); @@ -228,10 +252,10 @@ namespace AIStudio.Wpf.Mind.ViewModels menuItem.Text = "同级"; menuItem.Command = AddPeerCommand; menuOptions.Add(menuItem); - //menuItem = new CinchMenuItem(); - //menuItem.Text = "上级"; - //menuItem.Command = AddParentCommand; - //menuOptions.Add(menuItem); + menuItem = new CinchMenuItem(); + menuItem.Text = "上级"; + menuItem.Command = AddParentCommand; + menuOptions.Add(menuItem); menuItem = new CinchMenuItem(); menuItem.Text = "前移"; menuItem.Command = MoveForwardCommand; @@ -245,7 +269,9 @@ namespace AIStudio.Wpf.Mind.ViewModels menuItem.Command = DeleteCommand; menuOptions.Add(menuItem); } + #endregion + #region 操作 public void ExecuteAddChildCommand(object obj) { if (obj is MindNode node) @@ -285,6 +311,7 @@ namespace AIStudio.Wpf.Mind.ViewModels parent.RemoveChild(this); int index = parent.Children.IndexOf(this); parent.AddChild(node, index + 1); + node.AddChild(this); LayoutUpdated(); @@ -348,13 +375,21 @@ namespace AIStudio.Wpf.Mind.ViewModels { if (Parent is MindNode parent) { - parent.RemoveChild(this); + parent.RemoveChild(this, true); LayoutUpdated(); } } public void AddChild(MindNode item, int index = -1) { + if (this.NodeLevel == NodeLevel.Level1) + { + item.NodeLevel = NodeLevel.Level2; + } + else + { + item.NodeLevel = NodeLevel.Level3; + } if (index >= 0) { this.Children.Insert(index, item); @@ -364,7 +399,6 @@ namespace AIStudio.Wpf.Mind.ViewModels this.Children.Add(item); } item.Parent = this; - //item.ParentId = this.Id; Root?.DirectAddItemCommand.Execute(item); ConnectionViewModel connector = new ConnectionViewModel(Root, this.Connectors.FirstOrDefault(), item.Connectors.FirstOrDefault()); @@ -375,13 +409,12 @@ namespace AIStudio.Wpf.Mind.ViewModels connector.ShapeViewModel.SinkMarker.SizeStyle = this.ShapeViewModel.SinkMarker.SizeStyle; Root?.DirectAddItemCommand.Execute(connector); + Root?.ClearSelectedItemsCommand.Execute(new SelectableDesignerItemViewModelBase[] { connector }); Root?.BringForwardCommand.Execute(new DesignerItemViewModelBase[] { item }); - - Root?.ClearSelectedItemsCommand.Execute(new SelectableDesignerItemViewModelBase[] { connector }); } - public void RemoveChild(MindNode item) + public void RemoveChild(MindNode item, bool removeall = false) { item.PropertyChanged -= Item_PropertyChanged; @@ -391,30 +424,28 @@ namespace AIStudio.Wpf.Mind.ViewModels Root?.DirectRemoveItemCommand.Execute(item); Root?.DirectRemoveItemCommand.Execute(connectors); - if (item.Children?.Count > 0) + + if (removeall) { - foreach (var child in item.Children.ToList()) + if (item.Children?.Count > 0) { - item.RemoveChild(child); + foreach (var child in item.Children.ToList()) + { + item.RemoveChild(child); + } } } } - - public MindNode GetLevel1Node() - { - var node = this; - while (node.Parent is MindNode mindNode) - { - node = mindNode; - } - return node; - } + #endregion private void Item_PropertyChanged(object sender, PropertyChangedEventArgs e) { switch (e.PropertyName) { case nameof(IsExpanded): + case nameof(ItemWidth): + case nameof(ItemHeight): + case nameof(MindType): GetLevel1Node()?.LayoutUpdated(); break; case nameof(NodeLevel): @@ -422,7 +453,6 @@ namespace AIStudio.Wpf.Mind.ViewModels break; case nameof(Left): { - if (e is ValuePropertyChangedEventArgs valuePropertyChangedEventArgs) { if (NodeLevel == NodeLevel.Level1) @@ -456,16 +486,37 @@ namespace AIStudio.Wpf.Mind.ViewModels } } - public void UpdateOffsetX(double oldvalue, double newvalue) + #region 布局相关 + protected MindNode GetLevel1Node() { - if (GetLevel1Node()?.LayoutUpdating == true) return; + var node = this; + while (node.Parent is MindNode mindNode) + { + node = mindNode; + } + return node; + } + + protected MindNode GetLevel2Node() + { + var node = this; + while (node.Parent is MindNode mindNode && mindNode.NodeLevel == NodeLevel.Level2) + { + node = mindNode; + } + return node; + } + + protected void UpdateOffsetX(double oldvalue, double newvalue) + { + if (GetLevel1Node()?._layoutUpdating == true) return; Offset += new VectorBase(newvalue - oldvalue, 0); } - public void UpdateOffsetY(double oldvalue, double newvalue) + protected void UpdateOffsetY(double oldvalue, double newvalue) { - if (GetLevel1Node()?.LayoutUpdating == true) return; + if (GetLevel1Node()?._layoutUpdating == true) return; Offset += new VectorBase(0, newvalue - oldvalue); } @@ -477,48 +528,180 @@ namespace AIStudio.Wpf.Mind.ViewModels protected void Level1LayoutUpdated() { - LayoutUpdating = true; + _layoutUpdating = true; var size = MeasureOverride(); ArrangeOverride(); Root.BringToFrontCommand.Execute(new SelectableDesignerItemViewModelBase[] { this }); Root?.ReconnectLinksToClosestPorts(); - LayoutUpdating = false; + _layoutUpdating = false; } protected SizeBase MeasureOverride(bool isExpanded = true) { - var sizewithSpacing = SizeWithSpacing; - if (Children?.Count > 0) + switch (MindType) { - var childrensizes = Children.Select(p => p.MeasureOverride(IsExpanded && isExpanded)).ToArray(); - sizewithSpacing = new SizeBase(sizewithSpacing.Width + childrensizes.Max(p => p.Width), Math.Max(sizewithSpacing.Height, childrensizes.Sum(p => p.Height))); + case MindType.Mind: + { + var sizewithSpacing = SizeWithSpacing; + if (Children?.Count > 0) + { + if (NodeLevel == NodeLevel.Level1) + { + var rights = Children.Where((p, index) => index % 2 == 0).ToList(); + rights.ForEach(p => p._isRightLayout = true); + var rightsizes = rights.Select(p => p.MeasureOverride(IsExpanded && isExpanded)).ToArray(); + + var lefts = Children.Where((p, index) => index % 2 == 1).ToList(); + lefts.ForEach(p => p._isRightLayout = false); + var leftsizes = lefts.Select(p => p.MeasureOverride(IsExpanded && isExpanded)).ToArray(); + sizewithSpacing = new SizeBase(sizewithSpacing.Width + rightsizes.Max(p => p.Width) + +leftsizes.Max(p => p.Width), Math.Max(sizewithSpacing.Height, Math.Max(rightsizes.Sum(p => p.Height), leftsizes.Sum(p => p.Height)))); + } + else + { + var childrensizes = Children.Select(p => p.MeasureOverride(IsExpanded && isExpanded)).ToArray(); + sizewithSpacing = new SizeBase(sizewithSpacing.Width + childrensizes.Max(p => p.Width), Math.Max(sizewithSpacing.Height, childrensizes.Sum(p => p.Height))); + } + } + DesiredSize = isExpanded ? sizewithSpacing : new SizeBase(0, 0); + Visible = isExpanded; + var connectors = Root?.Items.OfType().Where(p => p.SinkConnectorInfoFully?.DataItem == this).ToList(); + connectors?.ForEach(p => p.Visible = Visible); + break; + } + case MindType.Logical: + { + var sizewithSpacing = SizeWithSpacing; + if (Children?.Count > 0) + { + var childrensizes = Children.Select(p => p.MeasureOverride(IsExpanded && isExpanded)).ToArray(); + sizewithSpacing = new SizeBase(sizewithSpacing.Width + childrensizes.Max(p => p.Width), Math.Max(sizewithSpacing.Height, childrensizes.Sum(p => p.Height))); + } + DesiredSize = isExpanded ? sizewithSpacing : new SizeBase(0, 0); + Visible = isExpanded; + var connectors = Root?.Items.OfType().Where(p => p.SinkConnectorInfoFully?.DataItem == this).ToList(); + connectors?.ForEach(p => p.Visible = Visible); + break; + } + default: + { + var sizewithSpacing = SizeWithSpacing; + if (Children?.Count > 0) + { + var childrensizes = Children.Select(p => p.MeasureOverride(IsExpanded && isExpanded)).ToArray(); + sizewithSpacing = new SizeBase(sizewithSpacing.Width + childrensizes.Max(p => p.Width), Math.Max(sizewithSpacing.Height, childrensizes.Sum(p => p.Height))); + } + DesiredSize = isExpanded ? sizewithSpacing : new SizeBase(0, 0); + Visible = isExpanded; + var connectors = Root?.Items.OfType().Where(p => p.SinkConnectorInfoFully?.DataItem == this).ToList(); + connectors?.ForEach(p => p.Visible = Visible); + break; + } } - DesiredSize = isExpanded ? sizewithSpacing : new SizeBase(0, 0); - Visible = isExpanded; - var connectors = Root?.Items.OfType().Where(p => p.SinkConnectorInfoFully?.DataItem == this).ToList(); - connectors?.ForEach(p => p.Visible = Visible); return DesiredSize; } protected void ArrangeOverride() { - double left = MiddlePosition.X + ItemWidth / 2 + Spacing.Width; - double top = MiddlePosition.Y - DesiredSize.Height / 2; - if (Children?.Count > 0) + switch (MindType) { - foreach (var child in Children) - { + case MindType.Mind: + { + if (NodeLevel == NodeLevel.Level1) + { + if (Children?.Count > 0) + { + var rights = Children.Where(p => p._isRightLayout == true).ToList(); + double left = MiddlePosition.X + ItemWidth / 2 + Spacing.Width; + double lefttop = MiddlePosition.Y - Math.Min(DesiredSize.Height, rights.Sum(p => p.DesiredSize.Height)) / 2; + foreach (var child in rights) + { + child.Left = left + child.Spacing.Width + child.Offset.X; + child.Top = lefttop + child.DesiredSize.Height / 2 - child.ItemHeight / 2 + child.Offset.Y; + child.DesiredPosition = child.Position; + lefttop += child.DesiredSize.Height; - child.Left = left + child.Spacing.Width + child.Offset.X; - child.Top = top + child.DesiredSize.Height / 2 - child.ItemHeight / 2 + child.Offset.Y; - child.DesiredPosition = child.Position; - top += child.DesiredSize.Height; + child.ArrangeOverride(); + } - child.ArrangeOverride(); - } + var lefts = Children.Where(p => p._isRightLayout == false).ToList(); + double right = MiddlePosition.X - ItemWidth / 2 - Spacing.Width; + double righttop = MiddlePosition.Y - Math.Min(DesiredSize.Height, lefts.Sum(p => p.DesiredSize.Height)) / 2; + foreach (var child in lefts) + { + child.Left = right - child.Spacing.Width - child.ItemWidth + child.Offset.X ; + child.Top = righttop + child.DesiredSize.Height / 2 - child.ItemHeight / 2 + child.Offset.Y; + child.DesiredPosition = child.Position; + righttop += child.DesiredSize.Height; + + child.ArrangeOverride(); + } + } + } + else + { + if (GetLevel2Node()._isRightLayout) + { + double left = MiddlePosition.X + ItemWidth / 2 + Spacing.Width; + double top = MiddlePosition.Y - Math.Min(DesiredSize.Height, Children.Sum(p => p.DesiredSize.Height)) / 2; + if (Children?.Count > 0) + { + foreach (var child in Children) + { + child.Left = left + child.Spacing.Width + child.Offset.X; + child.Top = top + child.DesiredSize.Height / 2 - child.ItemHeight / 2 + child.Offset.Y; + child.DesiredPosition = child.Position; + top += child.DesiredSize.Height; + + child.ArrangeOverride(); + } + } + } + else + { + double right = MiddlePosition.X - ItemWidth / 2 - Spacing.Width; + double top = MiddlePosition.Y - Math.Min(DesiredSize.Height, Children.Sum(p => p.DesiredSize.Height)) / 2; + if (Children?.Count > 0) + { + foreach (var child in Children) + { + child.Left = right - child.Spacing.Width - child.ItemWidth + child.Offset.X; + child.Top = top + child.DesiredSize.Height / 2 - child.ItemHeight / 2 + child.Offset.Y; + child.DesiredPosition = child.Position; + top += child.DesiredSize.Height; + + child.ArrangeOverride(); + } + } + } + } + break; + } + case MindType.Logical: + { + double left = MiddlePosition.X + ItemWidth / 2 + Spacing.Width; + double top = MiddlePosition.Y - Math.Min(DesiredSize.Height, Children.Sum(p => p.DesiredSize.Height)) / 2; + if (Children?.Count > 0) + { + foreach (var child in Children) + { + child.Left = left + child.Spacing.Width + child.Offset.X; + child.Top = top + child.DesiredSize.Height / 2 - child.ItemHeight / 2 + child.Offset.Y; + child.DesiredPosition = child.Position; + top += child.DesiredSize.Height; + + child.ArrangeOverride(); + } + } + break; + } + default: + { + break; + } } } + #endregion } }