diff --git a/Framework/Atf.Gui.WinForms/Controls/Adaptable/Graphs/Circuit/CircuitEditingContext.cs b/Framework/Atf.Gui.WinForms/Controls/Adaptable/Graphs/Circuit/CircuitEditingContext.cs index 9200a228..529ccf69 100644 --- a/Framework/Atf.Gui.WinForms/Controls/Adaptable/Graphs/Circuit/CircuitEditingContext.cs +++ b/Framework/Atf.Gui.WinForms/Controls/Adaptable/Graphs/Circuit/CircuitEditingContext.cs @@ -998,10 +998,10 @@ bool IEditableGraphContainer.CanResize(object contai var layoutContext = m_viewingContext.Cast(); BoundsSpecified specified = layoutContext.CanSetBounds(group); if ((specified & BoundsSpecified.Width) != 0 || (specified & BoundsSpecified.Height) != 0) - return true; + return true; + } } } - } return false; } @@ -1012,12 +1012,14 @@ bool IEditableGraphContainer.CanResize(object contai /// New container height void IEditableGraphContainer.Resize(object container, int newWidth, int newHeight) { - // Subtract the label height because this isn't a part of the CircuitGroupInfo.MinimumSize or group.Bounds. - // The label height is added back in by D2dCircuitRenderer.GetBounds(). http://tracker.ship.scea.com/jira/browse/WWSATF-1504 var control = m_viewingContext.Cast(); - newHeight -= GetLabelHeight(control); var group = container.Cast(); + if (!string.IsNullOrEmpty(group.Name)) + // Subtract the label height because this isn't a part of the CircuitGroupInfo.MinimumSize or group.Bounds. + // The label height is added back in by D2dCircuitRenderer.GetBounds(). http://tracker.ship.scea.com/jira/browse/WWSATF-1504 + newHeight -= GetLabelHeight(control); + if (group.AutoSize) group.Info.MinimumSize = new Size(newWidth, newHeight); else diff --git a/Framework/Atf.Gui.WinForms/Controls/Adaptable/Graphs/D2dCircuitRenderer.cs b/Framework/Atf.Gui.WinForms/Controls/Adaptable/Graphs/D2dCircuitRenderer.cs index 3babbd63..3b7a8703 100644 --- a/Framework/Atf.Gui.WinForms/Controls/Adaptable/Graphs/D2dCircuitRenderer.cs +++ b/Framework/Atf.Gui.WinForms/Controls/Adaptable/Graphs/D2dCircuitRenderer.cs @@ -73,20 +73,31 @@ public PinStyle PinDrawStyle } /// - /// Gets the string to use on the title bar of the circuit element + /// Get the string to use on the title bar of the circuit element + /// Circuit element + /// String to use on title bar of circuit element protected virtual string GetElementTitle(TElement element) { return element.Type.Name; } /// - /// Gets the display name of the circuit element, which is drawn below and outside the circuit element. + /// Get the display name of the circuit element, which is drawn below and outside the circuit element. /// Returns null or the empty string to not draw anything in this place + /// Circuit element + /// Display name of the circuit element protected virtual string GetElementDisplayName(TElement element) { return element.Name; } + /// + /// Get element type cache + protected Dictionary ElementTypeCache + { + get { return m_elementTypeCache; } + } + /// /// Gets or sets a value indicating whether the background area of the title should be filled public bool TitleBackgroundFilled { get; set; } @@ -400,12 +411,14 @@ public override RectangleF GetBounds(TElement element, D2dGraphics g) { RectangleF result = GetElementBounds(element, g); - // Add in the label at bottom. Keep in sync with CircuitEditingContext.Resize(). - result.Height += LabelHeight; + if (!string.IsNullOrEmpty(element.Name)) + // Add in the label at bottom. Keep in sync with CircuitEditingContext.Resize(). + result.Height += LabelHeight; return result; } + /// /// Finds node and/or edge hit by the given rectangle /// Graph to test @@ -606,6 +619,18 @@ public override GraphHitRecord Pick( pickedOutputPos.Offset(ParentWorldOffset(subPick.First)); } + if (pickedWire != null && pickedElement != null && + pickedInput == null && pickedOutput == null && // favor picking pin over wire, need to check no pin is picked + pickedSubInput == null && pickedSubOutput == null) + { + // favor picking wire over element, but we don't want to select wires that are hidden by the element + // a rough check here probably should suffice most of the time: the bounds of the wire should not be enclosed by the picked node + var elementBounds = GetElementBounds(pickedElement, g); + var curveBounds = GetWireBounds(pickedWire, g); + if (!elementBounds.Contains(curveBounds)) + return new GraphHitRecord(null, pickedWire, null, null); + } + var result = new GraphHitRecord(pickedElement, pickedWire, pickedOutput, pickedInput); if (pickedElement != null && pickedWire == null && pickedOutput == null && pickedInput == null) result.Part = borderPart.Border == DiagramBorder.BorderType.None ? null : borderPart; @@ -615,6 +640,9 @@ public override GraphHitRecord Pick( result.ToRoutePos = pickedInputPos; result.FromRoutePos = pickedOutputPos; + if (subPick.Second.Is()) + result.SubItem = subPick.Second; // if a sub-edge is picked + result.HitPathInversed = subPick.First; return result; } @@ -624,20 +652,22 @@ public override GraphHitRecord Pick( /// Edge to test /// Point to test /// D2dGraphics object + /// optional x offset added to edge + /// optional y offset added to edge /// True iff edge hits point - protected virtual bool PickEdge(TWire edge, PointF p, D2dGraphics g) + protected virtual bool PickEdge(TWire edge, PointF p, D2dGraphics g, float xOffset = 0, float yOffset = 0) { ElementTypeInfo fromInfo = GetElementTypeInfo(edge.FromNode, g); TPin inputPin = edge.ToRoute; TPin outputPin = edge.FromRoute; Point p1 = edge.FromNode.Bounds.Location; - int x1 = p1.X + fromInfo.Size.Width; - int y1 = p1.Y + GetPinOffset(edge.FromNode, outputPin.Index, false); + float x1 = p1.X + fromInfo.Size.Width + xOffset; + float y1 = p1.Y + GetPinOffset(edge.FromNode, outputPin.Index, false) + yOffset; Point p2 = edge.ToNode.Bounds.Location; - int x2 = p2.X; - int y2 = p2.Y + GetPinOffset(edge.ToNode, inputPin.Index, true); + float x2 = p2.X + xOffset; ; + float y2 = p2.Y + GetPinOffset(edge.ToNode, inputPin.Index, true) + yOffset; float tanLen = GetTangentLength(x1, x2); @@ -749,7 +779,7 @@ private void Draw(TElement element, D2dGraphics g, bool outline) g.DrawRectangle(bounds, m_theme.OutlineBrush); } - if (!TitleBackgroundFilled && (info.Size.Height > 2* TitleHeight)) // draw the separate line between title and content + if (!TitleBackgroundFilled && (info.Size.Height > 2 * TitleHeight)) // draw the separate line between title and content g.DrawLine(p.X, p.Y + titleHeight, p.X + info.Size.Width, p.Y + titleHeight, m_theme.OutlineBrush); if (drawPins) @@ -921,7 +951,10 @@ protected void DrawExpandedGroup(TElement element, D2dGraphics g) // draw sub-edges after recursively draw sub-nodes foreach (var subEdge in group.SubEdges) - Draw(subEdge, DiagramDrawingStyle.Normal, g); + { + var style = GetStyle(subEdge); + Draw(subEdge, style, g); + } m_graphPath.Pop(); } @@ -1203,39 +1236,57 @@ private Pair, object> PickSubItem(ICircuitGroupType(); subgroup_stack.Push(pickedElement.Cast()); + TWire pickedWire = null; bool goDown; do { var current = stack.Peek(); - var group = current.Cast>(); + var group = current.Cast>(); // the stack top must be a group if we can go down goDown = false; + bool hitSubNode = false; + PointF offset = WorldOffset(subgroup_stack); foreach (var child in group.SubNodes) { // only push in the first child that hits the point RectangleF bounds = GetBounds(child, g); - bounds.Offset(WorldOffset(subgroup_stack)); + bounds.Offset(offset); bounds.Inflate(m_theme.PickTolerance, m_theme.PickTolerance); if (bounds.Contains(p.X, p.Y)) { stack.Push(child.Cast()); + hitSubNode = true; // sub-node is hit if (child.Is>()) { - subgroup_stack.Push(child.Cast()); + subgroup_stack.Push(child.Cast()); // update group hierarchy path // go down when the child is expanded goDown = child.Cast>().Expanded; break; } } - + } + if (!hitSubNode) // try picking sub-edges + { + foreach (var subEdge in group.SubEdges) + { + if (PickEdge(subEdge, p, g, offset.X, offset.Y)) + { + pickedWire = subEdge; + break; + } + } } } while (goDown); - // stack top is the subitem - if (stack.Peek() != pickedElement.Cast()) + if (pickedWire != null) { - var subItem = stack.Peek(); result.First = stack; + result.Second = pickedWire; + } + else if (stack.Peek() != pickedElement.Cast())// stack top is the picked subitem + { + var subItem = stack.Peek(); + result.First = stack; // get hit path // try pick subItem RectangleF bounds = GetElementBounds(subItem, g); @@ -1281,7 +1332,6 @@ private Pair, object> PickSubItem(ICircuitGroupType width) width = group.Info.MinimumSize.Width; @@ -1728,7 +1779,11 @@ private float GetTangentLength(float x1, float x2) return tanLen; } - private D2dBrush GetPen(TPin pin) + /// + /// Get pen for drawing pin + /// Pin to draw + /// Pen for drawing pin + protected virtual D2dBrush GetPen(TPin pin) { D2dBrush pen = m_theme.GetCustomBrush(pin.TypeName); if (pen == null) @@ -1799,6 +1854,108 @@ private RectangleF GetElementBounds(TElement element, D2dGraphics g) return new RectangleF(element.Bounds.Location, info.Size); } + + private RectangleF GetWireBounds(TWire wire, D2dGraphics g) + { + ElementTypeInfo info = GetElementTypeInfo(wire.FromNode, g); + RectangleF result = new RectangleF(); + if (wire.Is()) + { + bool firstTime = true; + foreach (var edgeInfo in wire.Cast().GetData(this, WorldOffset(m_graphPath), g)) + { + if (edgeInfo.ShapeType == EdgeStyleData.EdgeShape.Bezier) + { + var curve = edgeInfo.EdgeData.As(); + var cpts = new PointF[] { curve.P1, curve.P2, curve.P3, curve.P4 }; + if (firstTime) + { + result = GetPointsBounds(cpts); + firstTime = false; + } + else + result = RectangleF.Union(result, GetPointsBounds(cpts)); + } + else if (edgeInfo.ShapeType == EdgeStyleData.EdgeShape.Line) + { + var line = edgeInfo.EdgeData.As(); + if (line != null) + { + if (firstTime) + { + result = D2dUtil.MakeRectangle(line[0], line[1]); + firstTime = false; + } + else + result = RectangleF.Union(result, D2dUtil.MakeRectangle(line[0], line[1])); + } + + } + else if (edgeInfo.ShapeType == EdgeStyleData.EdgeShape.Polyline) + { + var lines = edgeInfo.EdgeData.As(); + if (lines != null) + { + if (firstTime) + { + result = GetPointsBounds(lines); + firstTime = false; + } + else + result = RectangleF.Union(result, GetPointsBounds(lines)); + } + } + else if (edgeInfo.ShapeType == EdgeStyleData.EdgeShape.BezierSpline) + { + var curves = edgeInfo.EdgeData.As>(); + + foreach (var curve in curves) + { + var cpts = new PointF[] { curve.P1, curve.P2, curve.P3, curve.P4 }; + if (firstTime) + { + result = GetPointsBounds(cpts); + firstTime = false; + } + else + result = RectangleF.Union(result, GetPointsBounds(cpts)); + } + } + + } + } + else + { + Point op = wire.FromNode.Bounds.Location; + op.Offset(WorldOffset(m_graphPath)); + int x1 = op.X + info.Size.Width; + if (PinDrawStyle == PinStyle.OnBorderFilled) + x1 += m_pinSize / 2; + int y1 = op.Y + GetPinOffset(wire.FromNode, wire.FromRoute.Index, false); + + Point ip = wire.ToNode.Bounds.Location; + ip.Offset(WorldOffset(m_graphPath)); + int x2 = ip.X; + if (PinDrawStyle == PinStyle.OnBorderFilled) + x2 -= m_pinSize / 2; + int y2 = ip.Y + GetPinOffset(wire.ToNode, wire.ToRoute.Index, true); + result = D2dUtil.MakeRectangle(new PointF(x1, y1), new PointF(x2, y2)); + } + + return result; + } + + private RectangleF GetPointsBounds(IEnumerable points) + { + var pts = points.ToArray(); + float minX = points.Min(p => p.X); + float minY = points.Min(p => p.Y); + float maxX = points.Max(p => p.X); + float maxY = points.Max(p => p.Y); + + return new RectangleF(new PointF(minX, minY), new SizeF(maxX - minX, maxY - minY)); + } + /// /// Gets expander rectangle /// Upper-left corner point of expander diff --git a/Framework/Atf.Gui/Direct2D/D2dUtil.cs b/Framework/Atf.Gui/Direct2D/D2dUtil.cs index 7249bd56..b29190fc 100644 --- a/Framework/Atf.Gui/Direct2D/D2dUtil.cs +++ b/Framework/Atf.Gui/Direct2D/D2dUtil.cs @@ -471,7 +471,7 @@ internal static System.Drawing.Color ToSystemColor(this Color4 color4) /// Start point /// End point /// A new instance of Rectangle that is specified by 2 points - private static RectangleF MakeRectangle(PointF p1, PointF p2) + public static RectangleF MakeRectangle(PointF p1, PointF p2) { float x = p1.X; float y = p1.Y;