/// <summary> /// Set a custom style that overrides default one during graph rendering </summary> /// <param name="node">Graph node to use the custom style</param> /// <param name="style">Custom style</param> public void SetCustomStyle(object node, DiagramDrawingStyle style) { if (node != null) { m_customStyles[node] = style; } }
private void DrawAnnotation(IAnnotation annotation, DiagramDrawingStyle style, Graphics g) { Rectangle bounds = annotation.Bounds; if (bounds.Size.IsEmpty) { // calculate size of text block SizeF textSizeF = g.MeasureString(annotation.Text, m_theme.Font); Size textSize = new Size((int)Math.Ceiling(textSizeF.Width), (int)Math.Ceiling(textSizeF.Height)); bounds.Size = textSize; annotation.SetTextSize(textSize); } g.FillRectangle(SystemBrushes.Info, bounds); if ((style & DiagramDrawingStyle.Ghosted) == 0) { g.DrawString(annotation.Text, m_theme.Font, SystemBrushes.WindowText, bounds); Pen pen = null; if ((style & DiagramDrawingStyle.LastSelected) != 0) { pen = m_theme.LastHighlightPen; } else if ((style & DiagramDrawingStyle.Selected) != 0) { pen = m_theme.HighlightPen; } if (pen != null) { g.DrawRectangle(pen, bounds); } } }
private void control_Paint(object sender, PaintEventArgs e) { Matrix transform = m_transformAdapter.Transform; Matrix oldTransform = e.Graphics.Transform; Region oldClip = e.Graphics.Clip; e.Graphics.Transform = transform; Rectangle clip = GdiUtil.InverseTransform(e.Graphics.Transform, e.ClipRectangle); e.Graphics.SetClip(clip); e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; foreach (TNode node in m_graph.Nodes) { DiagramDrawingStyle style = GetStyle(node); m_renderer.Draw(node, style, e.Graphics); } foreach (TEdge edge in m_graph.Edges) { DiagramDrawingStyle style = GetStyle(edge); m_renderer.Draw(edge, style, e.Graphics); } e.Graphics.Transform = oldTransform; e.Graphics.Clip = oldClip; }
/// <summary> /// Gets the theme's pen for the given drawing style</summary> /// <param name="style">Drawing style</param> /// <returns>Pen for the given drawing style</returns> public Pen GetPen(DiagramDrawingStyle style) { switch (style) { case DiagramDrawingStyle.Normal: return(m_outlinePen); case DiagramDrawingStyle.Selected: return(m_highlightPen); case DiagramDrawingStyle.LastSelected: return(m_lastHighlightPen); case DiagramDrawingStyle.Hot: return(m_hotPen); case DiagramDrawingStyle.Ghosted: return(m_ghostPen); case DiagramDrawingStyle.Hidden: return(m_hiddenPen); case DiagramDrawingStyle.Error: default: return(m_errorPen); } }
/// <summary> /// Renders entire graph</summary> protected override void OnRender() { if (m_graph == s_emptyGraph) { return; } base.OnRender(); m_renderer.VisibleWorldBounds = GdiUtil.InverseTransform(m_transformAdapter.Transform, AdaptedControl.ClientRectangle); // render group pins var group = m_graph.Cast <ICircuitGroupType <TNode, TEdge, TEdgeRoute> >(); foreach (var pin in group.Inputs.Concat(group.Info.HiddenInputPins)) { var grpPin = pin.Cast <ICircuitGroupPin <TNode> >(); DiagramDrawingStyle style = GetStyle(grpPin); m_renderer.DrawFloatingGroupPin(grpPin, true, style, m_d2dGraphics); } foreach (var pin in group.Outputs.Concat(group.Info.HiddenOutputPins)) { var grpPin = pin.Cast <ICircuitGroupPin <TNode> >(); DiagramDrawingStyle style = GetStyle(grpPin); m_renderer.DrawFloatingGroupPin(grpPin, false, style, m_d2dGraphics); } }
/// <summary> /// Gets the theme's brush for the given drawing style</summary> /// <param name="style">Drawing style</param> /// <returns>Brush for the given drawing style</returns> public Brush GetBrush(DiagramDrawingStyle style) { switch (style) { case DiagramDrawingStyle.Normal: return(m_fillBrush); case DiagramDrawingStyle.Selected: return(m_highlightBrush); case DiagramDrawingStyle.LastSelected: return(m_lastHighlightBrush); case DiagramDrawingStyle.Hot: return(m_hotBrush); case DiagramDrawingStyle.Ghosted: return(m_ghostBrush); case DiagramDrawingStyle.Hidden: return(m_hiddenBrush); case DiagramDrawingStyle.Error: default: return(m_errorBrush); } }
/// <summary> /// Draws a graph edge</summary> /// <param name="edge">Edge to draw</param> /// <param name="style">Diagram drawing style</param> /// <param name="g">Graphics object</param> public override void Draw(TWire edge, DiagramDrawingStyle style, Graphics g) { TPin inputPin = edge.ToRoute; TPin outputPin = edge.FromRoute; Pen pen = (style == DiagramDrawingStyle.Normal) ? GetPen(inputPin) : m_theme.GetPen(style); DrawWire(edge.FromNode, outputPin, edge.ToNode, inputPin, g, pen); }
/// <summary> /// Draws a graph node</summary> /// <param name="element">Element to draw</param> /// <param name="style">Diagram drawing style</param> /// <param name="g">Graphics object</param> public override void Draw(ScriptNode element, DiagramDrawingStyle style, D2dGraphics g) { // Use the "disabled" theme when drawing disabled circuit elements. if (!((ScriptNodeElementInfo)element.ElementInfo).Enabled) { D2dDiagramTheme defaultTheme = Theme; Theme = m_disabledTheme; base.Draw(element, style, g); Theme = defaultTheme; return; } base.Draw(element, style, g); }
private void DrawEdge(TEdge edge, RectangleF clipBounds) { if (edge == m_hiddenEdge) { return; } RectangleF edgeBounds = m_renderer.GetBounds(edge, m_d2dGraphics); if (!clipBounds.IntersectsWith(edgeBounds)) { return; } DiagramDrawingStyle style = GetStyle(edge); m_renderer.Draw(edge, style, m_d2dGraphics); }
private void DrawAssociatedEdges(TNode node, RectangleF clipBounds) { var edges = m_nodeEdges.Find(node); foreach (var edge in edges) { int edgeVisit = m_edgeNodeEncounter[edge] + 1; m_edgeNodeEncounter[edge] = edgeVisit; if (edgeVisit == 2) { RectangleF bounds = m_renderer.GetBounds(edge, m_d2dGraphics); DiagramDrawingStyle style = GetStyle(edge); if (clipBounds.IntersectsWith(bounds) && style != DiagramDrawingStyle.Hidden) { m_renderer.Draw(edge, style, m_d2dGraphics); } } } }
/// <summary> /// Gets the theme's brush (pen) for the given drawing style</summary> /// <param name="style">Drawing style</param> /// <returns>Brush for the given drawing style</returns> public D2dBrush GetOutLineBrush(DiagramDrawingStyle style) { switch (style) { case DiagramDrawingStyle.Normal: return(m_outlineBrush); case DiagramDrawingStyle.Selected: return(m_highlightBrush); case DiagramDrawingStyle.LastSelected: return(m_lastHighlightBrush); case DiagramDrawingStyle.Hot: return(m_hotBrush); case DiagramDrawingStyle.DragSource: return(m_dragSourceBrush); case DiagramDrawingStyle.DropTarget: return(m_dropTargetBrush); case DiagramDrawingStyle.Ghosted: return(m_ghostBrush); case DiagramDrawingStyle.Hidden: return(m_hiddenBrush); case DiagramDrawingStyle.TemplatedInstance: return(m_templatedInstance); case DiagramDrawingStyle.CopyInstance: return(m_copyInstance); case DiagramDrawingStyle.Error: default: return(m_errorBrush); } }
/// <summary> /// Draws a graph node</summary> /// <param name="element">Element to draw</param> /// <param name="style">Diagram drawing style</param> /// <param name="g">Graphics object</param> public override void Draw(TElement element, DiagramDrawingStyle style, Graphics g) { switch (style) { case DiagramDrawingStyle.Normal: Draw(element, g); break; case DiagramDrawingStyle.Selected: case DiagramDrawingStyle.LastSelected: case DiagramDrawingStyle.Hot: default: Draw(element, g); DrawOutline(element, m_theme.GetPen(style), g); break; case DiagramDrawingStyle.Ghosted: case DiagramDrawingStyle.Hidden: DrawGhost(element, g); break; } }
/// <summary> /// Draws a graph, using an optional selection to determine which nodes should be /// highlighted</summary> /// <param name="graph">Graph to render</param> /// <param name="selection">Selection containing graph nodes and edges, or null</param> /// <param name="g">Graphics device</param> public void Draw(IGraph <TNode, TEdge, TEdgeRoute> graph, Selection <object> selection, Graphics g) { foreach (TNode node in graph.Nodes) { DiagramDrawingStyle style = DiagramDrawingStyle.Normal; if (selection != null && selection.Contains(node)) { if (selection.LastSelected.Equals(node)) { style = DiagramDrawingStyle.LastSelected; } else { style = DiagramDrawingStyle.Selected; } } Draw(node, style, g); } foreach (TEdge edge in graph.Edges) { DiagramDrawingStyle style = DiagramDrawingStyle.Normal; if (selection != null && selection.Contains(edge)) { if (selection.LastSelected.Equals(edge)) { style = DiagramDrawingStyle.LastSelected; } else { style = DiagramDrawingStyle.Selected; } } Draw(edge, style, g); } }
/// <summary> /// Renders entire graph</summary> protected override void OnRender() { D2dGraphics.AntialiasMode = D2dAntialiasMode.PerPrimitive; Matrix3x2F invMtrx = D2dGraphics.Transform; invMtrx.Invert(); RectangleF boundsGr = Matrix3x2F.Transform(invMtrx, this.AdaptedControl.ClientRectangle); // Draw normal nodes on top of edges foreach (StateBase node in Graph.Nodes) { RectangleF nodeBounds = Renderer.GetBounds(node, D2dGraphics); if (!boundsGr.IntersectsWith(nodeBounds)) { continue; } DiagramDrawingStyle style = GetStyle(node); Renderer.Draw(node, style, D2dGraphics); } // Draw edges last for now. Todo: draw in layers, with edges and then nodes for each layer foreach (Transition edge in Graph.Edges) { if (edge == HiddenEdge) { continue; } RectangleF edgeBounds = Renderer.GetBounds(edge, D2dGraphics); if (!boundsGr.IntersectsWith(edgeBounds)) { continue; } DiagramDrawingStyle style = GetStyle(edge); Renderer.Draw(edge, style, D2dGraphics); } }
/// <summary> /// Draws a graph node</summary> /// <param name="node">Node to draw</param> /// <param name="style">Diagram drawing style</param> /// <param name="g">Graphics object</param> public override void Draw(TNode node, DiagramDrawingStyle style, Graphics g) { switch (style) { case DiagramDrawingStyle.Normal: Draw(node, g); break; case DiagramDrawingStyle.Selected: case DiagramDrawingStyle.LastSelected: case DiagramDrawingStyle.Hot: case DiagramDrawingStyle.Error: Draw(node, g); DrawOutline(node, m_theme.GetPen(style), g); break; case DiagramDrawingStyle.Ghosted: case DiagramDrawingStyle.Hidden: default: DrawGhost(node, g); break; } }
/// <summary> /// Draws a graph edge</summary> /// <param name="edge">Edge to draw</param> /// <param name="style">Diagram drawing style</param> /// <param name="g">Graphics object</param> public abstract void Draw(TEdge edge, DiagramDrawingStyle style, Graphics g);
/// <summary> /// Draws a graph node</summary> /// <param name="node">Node to draw</param> /// <param name="style">Diagram drawing style</param> /// <param name="g">Graphics object</param> public abstract void Draw(TNode node, DiagramDrawingStyle style, Graphics g);
/// <summary> /// Sets the rendering style for a diagram item. This style will override the /// normal style used by the graph adapter.</summary> /// <param name="item">Rendered item</param> /// <param name="style">Diagram style to use for item</param> public void SetStyle(object item, DiagramDrawingStyle style) { m_styles[item] = style; }
private void DrawAnnotation(IAnnotation annotation, DiagramDrawingStyle style, Graphics g) { Rectangle bounds = annotation.Bounds; if (bounds.Size.IsEmpty) { // calculate size of text block SizeF textSizeF = g.MeasureString(annotation.Text, m_theme.Font); Size textSize = new Size((int)Math.Ceiling(textSizeF.Width), (int)Math.Ceiling(textSizeF.Height)); bounds.Size = textSize; annotation.SetTextSize(textSize); } g.FillRectangle(SystemBrushes.Info, bounds); if ((style & DiagramDrawingStyle.Ghosted) == 0) { g.DrawString(annotation.Text, m_theme.Font, SystemBrushes.WindowText, bounds); Pen pen = null; if ((style & DiagramDrawingStyle.LastSelected) != 0) pen = m_theme.LastHighlightPen; else if ((style & DiagramDrawingStyle.Selected) != 0) pen = m_theme.HighlightPen; if (pen != null) g.DrawRectangle(pen, bounds); } }
/// <summary> /// Gets the current rendering style for an item</summary> /// <param name="item">Rendered item</param> /// <returns>Rendering style set by SetStyle, Normal if no override is set</returns> public virtual DiagramDrawingStyle GetStyle(object item) { DiagramDrawingStyle result = DiagramDrawingStyle.Normal; // no override if (m_visibilityContext != null && !m_visibilityContext.IsVisible(item)) { result = DiagramDrawingStyle.Hidden; } else if (item == m_hoverObject || item == m_hoverSubObject) { if (CircuitUtil.IsGroupTemplateInstance(item)) { result = DiagramDrawingStyle.TemplatedInstance; } else if (item.Is <Group>()) { result = DiagramDrawingStyle.CopyInstance; } else { result = DiagramDrawingStyle.Hot; } } else if (m_selectionContext != null && m_selectionContext.SelectionContains(item)) { if (m_selectionContext.LastSelected.Equals(item)) { result = DiagramDrawingStyle.LastSelected; } else { result = DiagramDrawingStyle.Selected; } } else if (m_selectionPathProvider != null && m_selectionPathProvider.IncludedPath(item) != null) { if (CircuitUtil.IsGroupTemplateInstance(item)) { result = DiagramDrawingStyle.TemplatedInstance; } else if (item.Is <Group>()) { result = DiagramDrawingStyle.CopyInstance; } else { result = DiagramDrawingStyle.Hot; } } else if (m_renderer.RouteConnecting != null) { // connection context cue: highlight edges that connect to the starting node if (item.Is <TEdge>()) { var edge = item.Cast <TEdge>(); if (m_renderer.RouteConnecting.StartNode.Equals(edge.FromNode) || m_renderer.RouteConnecting.StartNode.Equals(edge.ToNode)) { result = DiagramDrawingStyle.Hot; } } } return(result); }
private void DrawAnnotation(IAnnotation annotation, DiagramDrawingStyle style, D2dGraphics g, bool drawText, RectangleF graphBound) { // fill background Rectangle bounds = annotation.Bounds; Color backColor = m_coloringContext == null ? SystemColors.Info : (Color) m_coloringContext.GetColor(ColoringTypes.BackColor, annotation); // keep the width of border in 1 pixel after transformation to avoid D2d antialiasing away the line float borderThickness = 1.0f/m_scaleX; g.FillRectangle(bounds, backColor); g.DrawRectangle(bounds, ControlPaint.Dark(backColor), borderThickness, null); // draw titlebar if (style == DiagramDrawingStyle.LastSelected || style == DiagramDrawingStyle.Selected) { var titleBarRect = new RectangleF(bounds.X, bounds.Y, bounds.Width, Margin.Top - 1); g.FillRectangle(titleBarRect, ControlPaint.Dark(backColor)); } // line seperate titlebar from text content g.DrawLine(bounds.X, bounds.Y + Margin.Top-1, bounds.X + bounds.Width, bounds.Y + Margin.Top-1, ControlPaint.Dark(backColor), borderThickness); // draw content if (drawText) { var contentBounds = new RectangleF(bounds.X + Margin.Left, bounds.Y + Margin.Top, bounds.Width - Margin.Size.Width, bounds.Height - Margin.Size.Height); contentBounds.Width = Math.Max(contentBounds.Width, MinimumWidth); contentBounds.Height = Math.Max(contentBounds.Height, MinimumHeight); var textBounds = contentBounds; int topLine = 0; D2dTextLayout textLayout = null; if (m_annotationEditors.ContainsKey(annotation)) { var annotationEditor = m_annotationEditors[annotation]; if (annotationEditor.TextLayout.Text != annotation.Text) // text content changed, for example, undo,redo { annotationEditor.ResetText(annotation.Text); } topLine = annotationEditor.TopLine; textLayout = m_annotationEditors[annotation].TextLayout; annotationEditor.VerticalScrollBarVisibe = textLayout.Height > textLayout.LayoutHeight; if (m_annotationEditors[annotation].VerticalScrollBarVisibe) textBounds.Width -= ScrollBarWidth + 2 * ScrollBarMargin; textLayout.LayoutWidth = textBounds.Width; textLayout.LayoutHeight = textBounds.Height; } if (textLayout == null) { // first assume no v-scroll bar needed textLayout = D2dFactory.CreateTextLayout(annotation.Text, m_theme.TextFormat, contentBounds.Width, contentBounds.Height); if (m_theme.TextFormat.Underlined) textLayout.SetUnderline(true, 0, annotation.Text.Length); if (m_theme.TextFormat.Strikeout) textLayout.SetStrikethrough(true, 0, annotation.Text.Length); if (textLayout.Height > textLayout.LayoutHeight) // need v-scroll bar { textLayout.LayoutWidth = contentBounds.Width - ScrollBarWidth - 2*ScrollBarMargin; } m_annotationEditors.Add(annotation, new TextEditor() { TextLayout = textLayout, TextFormat = m_theme.TextFormat, TopLine = topLine, VerticalScrollBarVisibe = textLayout.Height > textLayout.LayoutHeight }); } var annotationData = m_annotationEditors[annotation]; PointF origin = new PointF(contentBounds.Location.X, contentBounds.Location.Y - topLine * m_theme.TextFormat.FontHeight); g.PushAxisAlignedClip(contentBounds); // draw the selection range behind the text. if (annotationData.SelectionLength > 0) { var hitTestMetrics = textLayout.HitTestTextRange(annotationData.SelectionStart, annotationData.SelectionLength, 0, 0); for (int i = 0; i < hitTestMetrics.Length; ++i) { var highlightRect = new RectangleF(hitTestMetrics[i].Point.X, hitTestMetrics[i].Point.Y, hitTestMetrics[i].Width, hitTestMetrics[i].Height); highlightRect.Offset(origin); g.FillRectangle(highlightRect, m_theme.TextHighlightBrush); } } // draw caret if ( style == DiagramDrawingStyle.Selected || style == DiagramDrawingStyle.LastSelected) { textLayout = annotationData.TextLayout; var caretRect = m_annotationEditors[annotation].GetCaretRect(); caretRect.Offset(origin); //g.FillRectangle(caretRect, m_theme.HotBrush); // set Windows caret position if (m_editingText && contentBounds.IntersectsWith(caretRect) && graphBound.IntersectsWith(caretRect) && AdaptedControl.Focused) { var caretClientRect = GdiUtil.Transform(m_transformAdapter.Transform, caretRect); float ratio = m_scaleX*m_theme.TextFormat.FontHeight/CaretHeight; if (ratio > 1.1f || ratio < 0.9f) // adjust caret height { CaretHeight = (int)(m_scaleX*m_theme.TextFormat.FontHeight); User32.DestroyCaret(); User32.CreateCaret(AdaptedControl.Handle, IntPtr.Zero, CaretWidth, CaretHeight); } // align bottom User32.SetCaretPos((int) caretClientRect.X, (int)(caretClientRect.Y + caretClientRect.Height - CaretHeight)); if (!m_rmbPressed) AdaptedControl.HasKeyboardFocus = true; } else HideCaret(); } // draw text g.DrawTextLayout(origin, textLayout, m_theme.TextBrush); g.PopAxisAlignedClip(); // draw v-scroll bar if (contentBounds.Height < textLayout.Height) { float visibleLines = contentBounds.Height / m_theme.TextFormat.FontHeight; float vMin = topLine * textLayout.LayoutHeight / textLayout.LineCount; float vMax = (topLine + visibleLines-1) * textLayout.LayoutHeight / textLayout.LineCount; if (m_scrolling) { var trackBounds = new RectangleF(contentBounds.Right - ScrollBarMargin - ScrollBarWidth, contentBounds.Y, ScrollBarWidth, contentBounds.Height); g.FillRectangle(trackBounds, Color.Gainsboro); } var thumbBounds = new RectangleF(contentBounds.Right - ScrollBarMargin - ScrollBarWidth, contentBounds.Y + vMin, ScrollBarWidth, vMax - vMin); g.FillRectangle(thumbBounds, Color.DimGray); } } else HideCaret(); }
/// <summary> /// Draws a graph edge</summary> /// <param name="edge">Edge to draw</param> /// <param name="style">Diagram drawing style</param> /// <param name="g">Graphics object</param> public override void Draw(TEdge edge, DiagramDrawingStyle style, Graphics g) { Draw(edge, m_theme.GetPen(style), g); }
/// <summary> /// Draws floating group pin</summary> /// <param name="grpPin">Group pin</param> /// <param name="inputSide">True for input pin, false for output pin</param> /// <param name="style">DiagramDrawingStyle</param> /// <param name="g">Graphics object</param> public void DrawFloatingGroupPin(ICircuitGroupPin <TElement> grpPin, bool inputSide, DiagramDrawingStyle style, D2dGraphics g) { SizeF pinNameSize = g.MeasureText(grpPin.Name, Theme.TextFormat); PointF p; if (inputSide) { p = GetGroupPinLocation(grpPin, true); RectangleF pinRect = new RectangleF(p.X + CircuitGroupPinInfo.FloatingPinBoxWidth - Theme.PinSize, grpPin.Bounds.Location.Y + Theme.PinMargin + Theme.PinOffset, Theme.PinSize, Theme.PinSize); // draw output pin for input floating pins g.DrawRectangle(pinRect, m_subGraphPinPen); if (grpPin.Info.Pinned) { D2dUtil.DrawPin((int)(p.X + CircuitGroupPinInfo.FloatingPinBoxWidth), (int)p.Y, true, true, m_pinBrush, g); } else { D2dUtil.DrawPin((int)(p.X + CircuitGroupPinInfo.FloatingPinBoxWidth), (int)p.Y + Theme.PinSize / 2, false, true, m_pinBrush, g); } RectangleF bounds = new RectangleF(p.X, p.Y, CircuitGroupPinInfo.FloatingPinBoxWidth, CircuitGroupPinInfo.FloatingPinBoxHeight); RectangleF alignRect = new RectangleF( bounds.Left, bounds.Bottom + Theme.PinMargin, pinNameSize.Width, Theme.RowSpacing); var textAlignment = Theme.TextFormat.TextAlignment; Theme.TextFormat.TextAlignment = D2dTextAlignment.Leading; g.DrawText(grpPin.Name, Theme.TextFormat, alignRect.Location, Theme.TextBrush); Theme.TextFormat.TextAlignment = textAlignment; } else { // assume vertical scroll bar width = 16 p = GetGroupPinLocation(grpPin, false); RectangleF pinRect = new RectangleF(p.X + 1, grpPin.Bounds.Location.Y + Theme.PinMargin + Theme.PinOffset, Theme.PinSize, Theme.PinSize); // draw input pin for output floating pins g.DrawRectangle(pinRect, m_subGraphPinPen); // draw pin icon if (grpPin.Info.Pinned) { D2dUtil.DrawPin((int)p.X, (int)p.Y, true, false, m_pinBrush, g); } else { D2dUtil.DrawPin((int)p.X, (int)p.Y + Theme.PinSize / 2, false, false, m_pinBrush, g); } // draw label RectangleF bounds = new RectangleF(p.X, p.Y, CircuitGroupPinInfo.FloatingPinBoxWidth, CircuitGroupPinInfo.FloatingPinBoxHeight); RectangleF alignRectF = new RectangleF(bounds.Right - pinNameSize.Width, bounds.Bottom + Theme.PinMargin, pinNameSize.Width, Theme.RowSpacing); var textAlignment = Theme.TextFormat.TextAlignment; Theme.TextFormat.TextAlignment = D2dTextAlignment.Trailing; g.DrawText(grpPin.Name, Theme.TextFormat, alignRectF, Theme.TextBrush); Theme.TextFormat.TextAlignment = textAlignment; } // draw the fake pin node itself float savedStrokeWidth = Theme.StrokeWidth; Theme.StrokeWidth = 2.0f; if (style == DiagramDrawingStyle.Normal) { g.DrawRectangle(new RectangleF(p.X, p.Y, CircuitGroupPinInfo.FloatingPinBoxWidth, CircuitGroupPinInfo.FloatingPinBoxHeight), m_subGraphPinNodePen); } else { g.DrawRectangle(new RectangleF(p.X, p.Y, CircuitGroupPinInfo.FloatingPinBoxWidth, CircuitGroupPinInfo.FloatingPinBoxHeight), Theme.HotBrush); } Theme.StrokeWidth = savedStrokeWidth; if (!grpPin.Info.ExternalConnected) { RectangleF eyeRect = GetVisibilityCheckRect(grpPin, inputSide); g.DrawEyeIcon(eyeRect, grpPin.Info.Visible ? m_visiblePinBrush : m_hiddrenPinBrush, 1.0f); } // draw fake edge that connects group pin fake node DrawGroupPinNodeFakeEdge(grpPin, p, inputSide, style, g); }
/// <summary> /// Renders entire graph</summary> protected virtual void OnRender() { try { m_renderer.GetStyle = GetStyle; m_d2dGraphics.AntialiasMode = D2dAntialiasMode.PerPrimitive; Matrix3x2F invMtrx = m_d2dGraphics.Transform; invMtrx.Invert(); RectangleF boundsGr = Matrix3x2F.Transform(invMtrx, this.AdaptedControl.ClientRectangle); // Either draw (most) edges first or prepare multimaps for draw-as-we-go edges. if (EdgeRenderPolicy == DrawEdgePolicy.AllFirst) { foreach (var edge in m_graph.Edges) { if (edge == m_hiddenEdge) { continue; } RectangleF bounds = m_renderer.GetBounds(edge, m_d2dGraphics); if (!boundsGr.IntersectsWith(bounds)) { continue; } var group = edge.FromNode.As <ICircuitGroupType <TNode, TEdge, TEdgeRoute> >(); if (group == null || !group.Expanded) { group = edge.ToNode.As <ICircuitGroupType <TNode, TEdge, TEdgeRoute> >(); } if (group != null && group.Expanded) { m_edgesOnGroups.Add(edge); } else { DiagramDrawingStyle style = GetStyle(edge); m_renderer.Draw(edge, style, m_d2dGraphics); } } } else { // build node to edge maps foreach (TEdge edge in m_graph.Edges) { m_nodeEdges.Add(edge.FromNode, edge); m_nodeEdges.Add(edge.ToNode, edge); m_edgeNodeEncounter.Add(edge, 0); } } // Draw normal nodes first TNode containerOfSelectedNode = null; foreach (var node in m_graph.Nodes) { RectangleF nodeBounds = m_renderer.GetBounds(node, m_d2dGraphics); if (boundsGr.IntersectsWith(nodeBounds)) { DiagramDrawingStyle drawStyle = GetStyle(node); // Draw all dragged nodes (even expanded groups) last. if (drawStyle == DiagramDrawingStyle.DragSource) { m_draggingNodes.Add(node); } else { // Draw selected nodes after normal nodes. If the node // is hot, check if it's selected. if (drawStyle == DiagramDrawingStyle.Selected || drawStyle == DiagramDrawingStyle.LastSelected || (drawStyle == DiagramDrawingStyle.Hot && m_selectionContext != null && m_selectionContext.SelectionContains(node))) { m_selectedNodes.Add(node); } else { // Expanded groups are drawn after normal nodes. bool expandedGroup = false; var group = node.As <ICircuitGroupType <TNode, TEdge, TEdgeRoute> >(); if (group != null) { group.Info.PickingPriority = 0; if (group.Expanded) { if (node == ActiveContainer()) { containerOfSelectedNode = node; } else { m_expandedGroups.Add(node); } expandedGroup = true; } } // Draw normal nodes and collapsed groups. if (!expandedGroup) { m_renderer.Draw(node, drawStyle, m_d2dGraphics); if (EdgeRenderPolicy == DrawEdgePolicy.Associated) { DrawAssociatedEdges(node, boundsGr); } } } } } else if (EdgeRenderPolicy == DrawEdgePolicy.Associated) { DrawAssociatedEdges(node, boundsGr); } } // Draw expanded groups on top of normal sibling nodes, so that normal nodes that overlap // these groups don't appear as if they are in the groups. int pickPriority = 0; foreach (var node in m_expandedGroups) { var group = node.Cast <ICircuitGroupType <TNode, TEdge, TEdgeRoute> >(); group.Info.PickingPriority = pickPriority++; m_renderer.Draw(node, GetStyle(node), m_d2dGraphics); if (EdgeRenderPolicy == DrawEdgePolicy.Associated) { DrawAssociatedEdges(node, boundsGr); } } // Draw the expanded group that contains a selected or dragged child node, so that // if multiple expanded groups overlap, that the user can see the owning group. if (containerOfSelectedNode != null) { var group = containerOfSelectedNode.Cast <ICircuitGroupType <TNode, TEdge, TEdgeRoute> >(); group.Info.PickingPriority = pickPriority++; m_renderer.Draw(containerOfSelectedNode, GetStyle(containerOfSelectedNode), m_d2dGraphics); if (EdgeRenderPolicy == DrawEdgePolicy.Associated) { DrawAssociatedEdges(containerOfSelectedNode, boundsGr); } } // Draw selected nodes. foreach (var node in m_selectedNodes) { var group = node.As <ICircuitGroupType <TNode, TEdge, TEdgeRoute> >(); if (group != null) { group.Info.PickingPriority = pickPriority++; } m_renderer.Draw(node, GetStyle(node), m_d2dGraphics); if (EdgeRenderPolicy == DrawEdgePolicy.Associated) { DrawAssociatedEdges(node, boundsGr); } } // Draw dragging nodes last to ensure they are visible (necessary for container-crossing move operation) foreach (var node in m_draggingNodes) { m_renderer.Draw(node, DiagramDrawingStyle.DragSource, m_d2dGraphics); if (EdgeRenderPolicy == DrawEdgePolicy.Associated) { DrawAssociatedEdges(node, boundsGr); } } // Draw "all first" edges that connect to expanded groups. foreach (var edge in m_edgesOnGroups) { DiagramDrawingStyle style = GetStyle(edge); m_renderer.Draw(edge, style, m_d2dGraphics); } } finally { m_nodeEdges.Clear(); m_edgeNodeEncounter.Clear(); m_draggingNodes.Clear(); m_selectedNodes.Clear(); m_expandedGroups.Clear(); m_edgesOnGroups.Clear(); } }
private void control_Paint(object sender, PaintEventArgs e) { if (m_annotatedDiagram == null) { return; } Graphics g = e.Graphics; Matrix oldTransform = null; PointF delta = Delta; if (m_transformAdapter != null) { // modify the transform so it is only a translation and uniform scale oldTransform = e.Graphics.Transform; float[] m = m_transformAdapter.Transform.Elements; float scale = Math.Min(m[0], m[3]); Matrix newTransform = new Matrix(scale, m[1], m[2], scale, m[4], m[5]); g.Transform = newTransform; delta = GdiUtil.InverseTransformVector(newTransform, delta); } // draw all annotations in their current position, ghosting those which are dragging foreach (IAnnotation annotation in m_annotatedDiagram.Annotations) { DiagramDrawingStyle style = DiagramDrawingStyle.Normal; if (m_annotationSet != null && m_annotationSet.Contains(annotation)) { style = DiagramDrawingStyle.Ghosted; } else if (m_selectionContext.SelectionContains(annotation)) { style = DiagramDrawingStyle.Selected; if (m_selectionContext.GetLastSelected <IAnnotation>() == annotation) { style = DiagramDrawingStyle.LastSelected; } } DrawAnnotation(annotation, style, g); } if (m_draggingAnnotations != null) { // set dragged nodes' positions, offsetting by drag delta and applying layout constraints for (int i = 0; i < m_draggingAnnotations.Length; i++) { IAnnotation annotation = m_draggingAnnotations[i]; Rectangle oldBounds = GetBounds(annotation); Rectangle newBounds = oldBounds; newBounds.X += (int)delta.X; newBounds.Y += (int)delta.Y; m_newPositions[i] = newBounds.Location; m_layoutContext.SetBounds(annotation, newBounds, BoundsSpecified.Location); DrawAnnotation(annotation, DiagramDrawingStyle.Normal, g); m_layoutContext.SetBounds(annotation, oldBounds, BoundsSpecified.Location); } } if (m_transformAdapter != null) { g.Transform = oldTransform; } }
/// <summary> /// Draws a graph edge</summary> /// <param name="edge">Edge to draw</param> /// <param name="style">Diagram drawing style</param> /// <param name="g">Graphics object</param> public override void Draw(TEdge edge, DiagramDrawingStyle style, D2dGraphics g) { Draw(edge, m_theme.GetOutLineBrush(style), g); }
private void DrawGroupPinNodeFakeEdge(ICircuitGroupPin <TElement> grpPin, PointF grpPinPos, bool inputSide, DiagramDrawingStyle style, D2dGraphics g) { ElementTypeInfo info = GetElementTypeInfo(grpPin.InternalElement, g); if (inputSide) { PointF op = grpPinPos; float x1 = op.X + CircuitGroupPinInfo.FloatingPinBoxWidth; float y1 = op.Y + CircuitGroupPinInfo.FloatingPinBoxHeight / 2; Point ip = grpPin.InternalElement.Bounds.Location; float x2 = ip.X; float y2 = ip.Y + GetPinOffset(grpPin.InternalElement, grpPin.InternalPinIndex, true); DrawWire(g, m_fakeInputLinkPen, x1, y1, x2, y2, 1.0f, m_VirtualLinkStrokeStyle); } else { Point op = grpPin.InternalElement.Bounds.Location; float x1 = op.X + info.Size.Width; float y1 = op.Y + GetPinOffset(grpPin.InternalElement, grpPin.InternalPinIndex, false); PointF ip = grpPinPos; float x2 = ip.X; float y2 = ip.Y + CircuitGroupPinInfo.FloatingPinBoxHeight / 2; DrawWire(g, m_fakeOutputLinkPen, x1, y1, x2, y2, 1.0f, m_VirtualLinkStrokeStyle); } }
private void DrawAnnotation(IAnnotation annotation, DiagramDrawingStyle style, D2dGraphics g, bool drawText, RectangleF graphBound) { // fill background Rectangle bounds = annotation.Bounds; Color backColor = m_coloringContext == null ? SystemColors.Info : m_coloringContext.GetColor(ColoringTypes.BackColor, annotation); Color foreColor = m_coloringContext == null ? SystemColors.WindowText : m_coloringContext.GetColor(ColoringTypes.ForeColor, annotation); // keep the width of border in 2 pixel after transformation to avoid D2d antialiasing away the line float borderThickness = 2.0f/m_scaleX; g.FillRectangle(bounds, backColor); g.DrawRectangle(bounds, m_theme.GetOutLineBrush(style), borderThickness); //// draw titlebar //if (style == DiagramDrawingStyle.LastSelected || style == DiagramDrawingStyle.Selected) //{ // var titleBarRect = new RectangleF(bounds.X, bounds.Y, bounds.Width, Margin.Top - 1); // g.FillRectangle(titleBarRect, ControlPaint.Dark(backColor)); //} //// line seperate titlebar from text content //g.DrawLine(bounds.X, bounds.Y + Margin.Top-1, bounds.X + bounds.Width, bounds.Y + Margin.Top-1, ControlPaint.Dark(backColor), borderThickness); // draw content if (drawText) { var contentBounds = new RectangleF(bounds.X + Margin.Left, bounds.Y + Margin.Top, bounds.Width - Margin.Size.Width, bounds.Height - Margin.Size.Height); contentBounds.Width = Math.Max(contentBounds.Width, MinimumWidth); contentBounds.Height = Math.Max(contentBounds.Height, MinimumHeight); var textBounds = contentBounds; TextEditor textEditor; if (!m_annotationEditors.TryGetValue(annotation,out textEditor)) { // first assume no v-scroll bar needed var textLayout = D2dFactory.CreateTextLayout(annotation.Text, m_theme.TextFormat, contentBounds.Width, contentBounds.Height); if (m_theme.TextFormat.Underlined) textLayout.SetUnderline(true, 0, annotation.Text.Length); if (m_theme.TextFormat.Strikeout) textLayout.SetStrikethrough(true, 0, annotation.Text.Length); if (textLayout.Height > textLayout.LayoutHeight) // need v-scroll bar { textLayout.LayoutWidth = contentBounds.Width - ScrollBarWidth - 2 * ScrollBarMargin; } textEditor = new TextEditor { TextLayout = textLayout, TextFormat = m_theme.TextFormat, TopLine = 0, VerticalScrollBarVisibe = textLayout.Height > textLayout.LayoutHeight }; m_annotationEditors.Add(annotation, textEditor); } else if (textEditor.TextLayout.Text != annotation.Text) // text content changed, for example, undo,redo { textEditor.ResetText(annotation.Text); } int topLine = textEditor.TopLine; textEditor.VerticalScrollBarVisibe = textEditor.TextLayout.Height > textEditor.TextLayout.LayoutHeight; if (textEditor.VerticalScrollBarVisibe) textBounds.Width -= ScrollBarWidth + 2 * ScrollBarMargin; if (Math.Abs(textEditor.TextLayout.LayoutWidth - textBounds.Width) + Math.Abs(textEditor.TextLayout.LayoutHeight - textBounds.Height) > 1.0) { textEditor.TextLayout.LayoutWidth = textBounds.Width; // layout width and height can be updated textEditor.TextLayout.LayoutHeight = textBounds.Height; textEditor.Validate(); } float yOffset = textEditor.GetLineYOffset(topLine); PointF origin = new PointF(contentBounds.Location.X, contentBounds.Location.Y - yOffset); g.PushAxisAlignedClip(contentBounds); // adjust caret. // pull out this code to the caller. if ( annotation == m_editingAnnotation && m_caretCreated) { var caretRect = textEditor.GetCaretRect(); caretRect.Offset(origin); // set Windows caret position if (contentBounds.IntersectsWith(caretRect) && AdaptedControl.Focused) { Matrix3x2F xform = m_transformAdapter != null ? m_transformAdapter.Transform : g.Transform; var caretClientRect = Matrix3x2F.Transform(xform, caretRect); float ratio = m_scaleX*m_theme.TextFormat.FontHeight/CaretHeight; if (ratio > 1.1f || ratio < 0.9f) // adjust caret height { CaretHeight = (int)(m_scaleX*m_theme.TextFormat.FontHeight); User32.DestroyCaret(); User32.CreateCaret(AdaptedControl.Handle, IntPtr.Zero, CaretWidth, CaretHeight); } // align bottom User32.SetCaretPos((int) caretClientRect.X, (int)(caretClientRect.Y + caretClientRect.Height - CaretHeight)); if (!m_rmbPressed) AdaptedControl.HasKeyboardFocus = true; } else HideCaret(); } // draw the selection range above the text. if (textEditor.SelectionLength > 0) { D2dBrush hibrush = AdaptedControl.Focused ? m_theme.TextHighlightBrush : m_solidBrush; var hitTestMetrics = textEditor.TextLayout.HitTestTextRange(textEditor.SelectionStart, textEditor.SelectionLength, 0, 0); for (int i = 0; i < hitTestMetrics.Length; ++i) { var highlightRect = new RectangleF(hitTestMetrics[i].Point.X, hitTestMetrics[i].Point.Y, hitTestMetrics[i].Width, hitTestMetrics[i].Height); highlightRect.Offset(origin); g.FillRectangle(highlightRect, hibrush); } } // draw text g.DrawTextLayout(origin, textEditor.TextLayout, foreColor); g.PopAxisAlignedClip(); // draw v-scroll bar // if (contentBounds.Height < textEditor.TextLayout.Height) if(textEditor.VerticalScrollBarVisibe) { float visibleLines = textEditor.GetVisibleLines(); float vMin = topLine * contentBounds.Height / textEditor.TextLayout.LineCount; float vMax = (topLine + visibleLines - 1) * contentBounds.Height / textEditor.TextLayout.LineCount; // if (m_scrolling) // { var trackBounds = new RectangleF(contentBounds.Right - ScrollBarMargin - ScrollBarWidth, contentBounds.Y, ScrollBarWidth, contentBounds.Height); g.FillRectangle(trackBounds, Color.Gainsboro); // } var thumbBounds = new RectangleF(contentBounds.Right - ScrollBarMargin - ScrollBarWidth, contentBounds.Y + vMin, ScrollBarWidth, vMax - vMin); g.FillRectangle(thumbBounds, Color.DimGray); } } }