From b52f2dc5d790b35a7ed9a235aa19d9d5323e4302 Mon Sep 17 00:00:00 2001 From: jhshen Date: Mon, 8 Sep 2014 13:26:03 -0700 Subject: [PATCH] Fix #21 Don't include label height for element size calculation if the element has no name. This fixed the problem that premature picking elements when moving the mouse from below. (Also integrated latest changes to D2dCircuitRenderer) --- .../Graphs/Circuit/CircuitEditingContext.cs | 12 +- .../Adaptable/Graphs/D2dCircuitRenderer.cs | 197 ++++++++++++++++-- Framework/Atf.Gui/Direct2D/D2dUtil.cs | 2 +- 3 files changed, 185 insertions(+), 26 deletions(-) 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;