1 // Copyright 2001-2016 Crytek GmbH / Crytek Group. All rights reserved.
6 #include <CrySystem/ITimer.h>
8 #include "GdiplusUtils.h"
9 #include "GraphViewFormatter.h"
10 #include "PluginUtils.h"
12 #pragma warning(disable: 4355)
16 const float CDefaultGraphViewPainter::NODE_BEVEL
= 15.0f
;
17 const BYTE
CDefaultGraphViewPainter::NODE_HEADER_ALPHA
= 200;
18 const BYTE
CDefaultGraphViewPainter::NODE_HEADER_ALPHA_DISABLED
= 80;
19 const float CDefaultGraphViewPainter::NODE_HEADER_TEXT_BORDER_X
= 8.0f
;
20 const float CDefaultGraphViewPainter::NODE_HEADER_TEXT_BORDER_Y
= 2.0f
;
21 const Gdiplus::Color
CDefaultGraphViewPainter::NODE_HEADER_TEXT_COLOR
= Gdiplus::Color(60, 60, 60);
22 const Gdiplus::Color
CDefaultGraphViewPainter::NODE_ERROR_FILL_COLOR
= Gdiplus::Color(210, 66, 66);
23 const char* CDefaultGraphViewPainter::NODE_ERROR_TEXT
= "Error!";
24 const float CDefaultGraphViewPainter::NODE_ERROR_TEXT_BORDER_X
= 8.0f
;
25 const float CDefaultGraphViewPainter::NODE_ERROR_TEXT_BORDER_Y
= 2.0f
;
26 const Gdiplus::Color
CDefaultGraphViewPainter::NODE_ERROR_TEXT_COLOR
= Gdiplus::Color(60, 60, 60);
27 const Gdiplus::Color
CDefaultGraphViewPainter::NODE_WARNING_FILL_COLOR
= Gdiplus::Color(255, 255, 0);
28 const char* CDefaultGraphViewPainter::NODE_WARNING_TEXT
= "Warning!";
29 const float CDefaultGraphViewPainter::NODE_WARNING_TEXT_BORDER_X
= 8.0f
;
30 const float CDefaultGraphViewPainter::NODE_WARNING_TEXT_BORDER_Y
= 2.0f
;
31 const Gdiplus::Color
CDefaultGraphViewPainter::NODE_WARNING_TEXT_COLOR
= Gdiplus::Color(60, 60, 60);
32 const Gdiplus::Color
CDefaultGraphViewPainter::NODE_CONTENTS_FILL_COLOR
= Gdiplus::Color(200, 40, 40, 40);
33 const float CDefaultGraphViewPainter::NODE_CONTENTS_TEXT_BORDER_X
= 8.0f
;
34 const float CDefaultGraphViewPainter::NODE_CONTENTS_TEXT_BORDER_Y
= 2.0f
;
35 const float CDefaultGraphViewPainter::NODE_CONTENTS_TEXT_MAX_WIDTH
= 300.0f
;
36 const float CDefaultGraphViewPainter::NODE_CONTENTS_TEXT_MAX_HEIGHT
= 200.0f
;
37 const Gdiplus::Color
CDefaultGraphViewPainter::NODE_CONTENTS_TEXT_COLOR
= Gdiplus::Color(255, 255, 255);
38 const Gdiplus::Color
CDefaultGraphViewPainter::NODE_BODY_FILL_COLOR
= Gdiplus::Color(200, 40, 40, 40);
39 const Gdiplus::Color
CDefaultGraphViewPainter::NODE_BODY_FILL_COLOR_DISABLED
= Gdiplus::Color(80, 40, 40, 40);
40 const Gdiplus::Color
CDefaultGraphViewPainter::NODE_BODY_OUTLINE_COLOR
= Gdiplus::Color(200, 40, 40, 40);
41 const Gdiplus::Color
CDefaultGraphViewPainter::NODE_BODY_OUTLINE_COLOR_HIGHLIGHT
= Gdiplus::Color(250, 232, 12);
42 const float CDefaultGraphViewPainter::NODE_BODY_OUTLINE_WIDTH
= 1.0f
;
43 const float CDefaultGraphViewPainter::NODE_INPUT_OUPUT_HORZ_SPACING
= 40.0f
;
44 const float CDefaultGraphViewPainter::NODE_INPUT_OUPUT_VERT_SPACING
= 5.0f
;
45 const float CDefaultGraphViewPainter::NODE_INPUT_ICON_BORDER
= 5.0f
;
46 const float CDefaultGraphViewPainter::NODE_INPUT_ICON_WIDTH
= 11.0f
;
47 const float CDefaultGraphViewPainter::NODE_INPUT_ICON_HEIGHT
= 11.0f
;
48 const Gdiplus::Color
CDefaultGraphViewPainter::NODE_INPUT_ICON_OUTLINE_COLOR
= Gdiplus::Color(200, 20, 20, 20);
49 const float CDefaultGraphViewPainter::NODE_INPUT_ICON_OUTLINE_WIDTH
= 1.0f
;
50 const float CDefaultGraphViewPainter::NODE_INPUT_NAME_BORDER
= 2.0f
;
51 const float CDefaultGraphViewPainter::NODE_INPUT_NAME_WIDTH_MAX
= 400.0f
;
52 const Gdiplus::Color
CDefaultGraphViewPainter::NODE_INPUT_NAME_COLOR
= Gdiplus::Color(240, 240, 240);
53 const float CDefaultGraphViewPainter::NODE_OUTPUT_ICON_BORDER
= 5.0f
;
54 const float CDefaultGraphViewPainter::NODE_OUTPUT_ICON_WIDTH
= 11.0f
;
55 const float CDefaultGraphViewPainter::NODE_OUTPUT_ICON_HEIGHT
= 11.0f
;
56 const Gdiplus::Color
CDefaultGraphViewPainter::NODE_OUTPUT_ICON_OUTLINE_COLOR
= Gdiplus::Color(200, 20, 20, 20);
57 const float CDefaultGraphViewPainter::NODE_OUTPUT_ICON_OUTLINE_WIDTH
= 1.0f
;
58 const float CDefaultGraphViewPainter::NODE_OUTPUT_NAME_BORDER
= 2.0f
;
59 const float CDefaultGraphViewPainter::NODE_OUTPUT_NAME_WIDTH_MAX
= 400.0f
;
60 const Gdiplus::Color
CDefaultGraphViewPainter::NODE_OUTPUT_NAME_COLOR
= Gdiplus::Color(240, 240, 240);
61 const float CDefaultGraphViewPainter::NODE_OUTPUT_SPACER_OFFSET
= 3.0f
;
62 const float CDefaultGraphViewPainter::NODE_OUTPUT_SPACER_WIDTH
= 80.0f
;
63 const float CDefaultGraphViewPainter::NODE_OUTPUT_SPACER_LINE_WIDTH
= 1.0f
;
64 const Gdiplus::Color
CDefaultGraphViewPainter::NODE_OUTPUT_SPACER_COLOR
= Gdiplus::Color(140, 120, 120, 120);
65 const float CDefaultGraphViewPainter::LINK_WIDTH
= 2.0f
;
66 const float CDefaultGraphViewPainter::LINK_CURVE_OFFSET
= 45.0f
;
67 const float CDefaultGraphViewPainter::LINK_DODGE_X
= 15.0f
;
68 const float CDefaultGraphViewPainter::LINK_DODGE_Y
= 15.0f
;
69 const float CDefaultGraphViewPainter::ALPHA_HIGHLIGHT_MIN
= 0.3f
;
70 const float CDefaultGraphViewPainter::ALPHA_HIGHLIGHT_MAX
= 1.0f
;
71 const float CDefaultGraphViewPainter::ALPHA_HIGHLIGHT_SPEED
= 0.8f
;
72 const Gdiplus::Color
CDefaultGraphViewPainter::SELECTION_FILL_COLOR
= Gdiplus::Color(100, 0, 142, 212);
73 const Gdiplus::Color
CDefaultGraphViewPainter::SELECTION_OUTLINE_COLOR
= Gdiplus::Color(0, 162, 232);
75 const float CGraphView::MIN_ZOOM
= 0.28f
;
76 const float CGraphView::MAX_ZOOM
= 1.6f
;
77 const float CGraphView::DELTA_ZOOM
= 0.001f
;
78 const float CGraphView::DEFAULT_NODE_SPACING
= 40.0f
;
82 static const wchar_t* FONT_NAME
= L
"Tahoma";
83 static const float FONT_SIZE
= 8.0f
;
84 static const Gdiplus::FontStyle FONT_STYLE
= Gdiplus::FontStyleBold
;
86 //////////////////////////////////////////////////////////////////////////
87 static inline const Gdiplus::Font
& GetFont()
89 static const Gdiplus::Font
font(FONT_NAME
, FONT_SIZE
, FONT_STYLE
);
94 static const UINT UPDATE_TIMER_EVENT_ID
= 1;
96 //////////////////////////////////////////////////////////////////////////
97 SGraphViewFormatSettings::SGraphViewFormatSettings()
103 //////////////////////////////////////////////////////////////////////////
104 void SGraphViewFormatSettings::Serialize(Serialization::IArchive
& archive
)
106 archive(horzSpacing
, "horzSpacing", "Horizontal Spacing");
107 archive(vertSpacing
, "vertSpacing", "Vertical Spacing");
108 archive(bSnapToGrid
, "bSnapToGrid", "Snap To Grid");
111 //////////////////////////////////////////////////////////////////////////
112 void SGraphViewSettings::Serialize(Serialization::IArchive
& archive
)
114 archive(format
, "format", "Format");
117 //////////////////////////////////////////////////////////////////////////
118 CGraphViewNode::CGraphViewNode(const SGraphViewGrid
& grid
, Gdiplus::PointF pos
)
122 , m_statusFlags(EGraphViewNodeStatusFlags::None
)
123 , m_paintRect(grid
.SnapPos(pos
), Gdiplus::SizeF()) // #SchematycTODO : Might make more sense to default initialize this.
126 //////////////////////////////////////////////////////////////////////////
127 CGraphViewNode::~CGraphViewNode() {}
129 //////////////////////////////////////////////////////////////////////////
130 const SGraphViewGrid
& CGraphViewNode::GetGrid() const
135 //////////////////////////////////////////////////////////////////////////
136 void CGraphViewNode::SetPos(Gdiplus::PointF pos
, bool bSnapToGrid
)
140 pos
= m_grid
.SnapPos(pos
);
142 m_paintRect
.X
= pos
.X
;
143 m_paintRect
.Y
= pos
.Y
;
147 //////////////////////////////////////////////////////////////////////////
148 Gdiplus::PointF
CGraphViewNode::GetPos() const
150 return Gdiplus::PointF(m_paintRect
.X
, m_paintRect
.Y
);
153 //////////////////////////////////////////////////////////////////////////
154 void CGraphViewNode::SetPaintRect(Gdiplus::RectF paintRect
)
156 m_paintRect
= paintRect
;
160 //////////////////////////////////////////////////////////////////////////
161 Gdiplus::RectF
CGraphViewNode::GetPaintRect() const
166 //////////////////////////////////////////////////////////////////////////
167 void CGraphViewNode::Enable(bool enable
)
172 //////////////////////////////////////////////////////////////////////////
173 bool CGraphViewNode::IsEnabled() const
178 //////////////////////////////////////////////////////////////////////////
179 void CGraphViewNode::Select(bool select
)
184 //////////////////////////////////////////////////////////////////////////
185 bool CGraphViewNode::IsSelected() const
190 //////////////////////////////////////////////////////////////////////////
191 void CGraphViewNode::SetStatusFlags(EGraphViewNodeStatusFlags statusFlags
)
193 m_statusFlags
= statusFlags
;
196 //////////////////////////////////////////////////////////////////////////
197 EGraphViewNodeStatusFlags
CGraphViewNode::GetStatusFlags() const
199 return m_statusFlags
;
202 //////////////////////////////////////////////////////////////////////////
203 size_t CGraphViewNode::FindInput(Gdiplus::PointF pos
) const
205 // #SchematycTODO : Do we need to double check that the paint rects are valid? Maybe we should just reference the ports by name to be safe?
206 for(TRectVector::const_iterator iBeginInputPaintRect
= m_inputPaintRects
.begin(), iEndInputPaintRect
= m_inputPaintRects
.end(), iInputPaintRect
= iBeginInputPaintRect
; iInputPaintRect
!= iEndInputPaintRect
; ++ iInputPaintRect
)
208 if(iInputPaintRect
->Contains(pos
))
210 return iInputPaintRect
- iBeginInputPaintRect
;
213 return INVALID_INDEX
;
216 //////////////////////////////////////////////////////////////////////////
217 void CGraphViewNode::SetInputPaintRect(size_t iInput
, Gdiplus::RectF paintRect
)
219 if(iInput
>= m_inputPaintRects
.size())
221 m_inputPaintRects
.resize(iInput
+ 1);
223 m_inputPaintRects
[iInput
] = paintRect
;
226 //////////////////////////////////////////////////////////////////////////
227 Gdiplus::RectF
CGraphViewNode::GetInputPaintRect(size_t iInput
) const
229 return iInput
< m_inputPaintRects
.size() ? m_inputPaintRects
[iInput
] : Gdiplus::RectF();
232 //////////////////////////////////////////////////////////////////////////
233 Gdiplus::PointF
CGraphViewNode::GetInputLinkPoint(size_t iInput
) const
235 if(iInput
< m_inputPaintRects
.size())
237 const Gdiplus::RectF paintRect
= m_inputPaintRects
[iInput
];
238 return Gdiplus::PointF(paintRect
.GetLeft(), paintRect
.Y
+ (paintRect
.Height
/ 2.0f
));
242 return Gdiplus::PointF();
246 //////////////////////////////////////////////////////////////////////////
247 size_t CGraphViewNode::FindOutput(Gdiplus::PointF pos
) const
249 // #SchematycTODO : Do we need to double check that the paint rects are valid? Maybe we should just reference the ports by name to be safe?
250 for(TRectVector::const_iterator iBeginOutputPaintRect
= m_outputPaintRects
.begin(), iEndOutputPaintRect
= m_outputPaintRects
.end(), iOutputPaintRect
= iBeginOutputPaintRect
; iOutputPaintRect
!= iEndOutputPaintRect
; ++ iOutputPaintRect
)
252 if(iOutputPaintRect
->Contains(pos
))
254 return iOutputPaintRect
- iBeginOutputPaintRect
;
257 return INVALID_INDEX
;
260 //////////////////////////////////////////////////////////////////////////
261 void CGraphViewNode::SetOutputPaintRect(size_t iOutput
, Gdiplus::RectF paintRect
)
263 if(iOutput
>= m_outputPaintRects
.size())
265 m_outputPaintRects
.resize(iOutput
+ 1);
267 m_outputPaintRects
[iOutput
] = paintRect
;
270 //////////////////////////////////////////////////////////////////////////
271 Gdiplus::RectF
CGraphViewNode::GetOutputPaintRect(size_t iOutput
) const
273 return iOutput
< m_outputPaintRects
.size() ? m_outputPaintRects
[iOutput
] : Gdiplus::RectF();
276 //////////////////////////////////////////////////////////////////////////
277 Gdiplus::PointF
CGraphViewNode::GetOutputLinkPoint(size_t iOutput
) const
279 if(iOutput
< m_outputPaintRects
.size())
281 const Gdiplus::RectF paintRect
= m_outputPaintRects
[iOutput
];
282 return Gdiplus::PointF(paintRect
.GetRight(), paintRect
.Y
+ (paintRect
.Height
/ 2.0f
));
286 return Gdiplus::PointF();
290 //////////////////////////////////////////////////////////////////////////
291 const char* CGraphViewNode::GetName() const
296 //////////////////////////////////////////////////////////////////////////
297 const char* CGraphViewNode::GetContents() const
302 //////////////////////////////////////////////////////////////////////////
303 Gdiplus::Color
CGraphViewNode::GetHeaderColor() const
305 return Gdiplus::Color();
308 //////////////////////////////////////////////////////////////////////////
309 size_t CGraphViewNode::GetInputCount() const
314 //////////////////////////////////////////////////////////////////////////
315 size_t CGraphViewNode::FindInput(const char* name
) const
317 return INVALID_INDEX
;
320 //////////////////////////////////////////////////////////////////////////
321 const char* CGraphViewNode::GetInputName(size_t iInput
) const
326 //////////////////////////////////////////////////////////////////////////
327 EScriptGraphPortFlags
CGraphViewNode::GetInputFlags(size_t iInput
) const
329 return EScriptGraphPortFlags::None
;
332 //////////////////////////////////////////////////////////////////////////
333 Gdiplus::Color
CGraphViewNode::GetInputColor(size_t iInput
) const
335 return Gdiplus::Color();
338 //////////////////////////////////////////////////////////////////////////
339 size_t CGraphViewNode::GetOutputCount() const
344 //////////////////////////////////////////////////////////////////////////
345 size_t CGraphViewNode::FindOutput(const char* name
) const
347 return INVALID_INDEX
;
350 //////////////////////////////////////////////////////////////////////////
351 const char* CGraphViewNode::GetOutputName(size_t iOutput
) const
356 //////////////////////////////////////////////////////////////////////////
357 EScriptGraphPortFlags
CGraphViewNode::GetOutputFlags(size_t iOutput
) const
359 return EScriptGraphPortFlags::None
;
362 //////////////////////////////////////////////////////////////////////////
363 Gdiplus::Color
CGraphViewNode::GetOutputColor(size_t iInput
) const
365 return Gdiplus::Color();
368 //////////////////////////////////////////////////////////////////////////
369 void CGraphViewNode::OnMove(Gdiplus::RectF paintRect
) {}
371 //////////////////////////////////////////////////////////////////////////
372 SGraphViewLink::SGraphViewLink(CGraphView
* _pGraphView
, const CGraphViewNodeWeakPtr
& _pSrcNode
, const char* _srcOutputName
, const CGraphViewNodeWeakPtr
& _pDstNode
, const char* _dstInputName
)
373 : pGraphView(_pGraphView
)
374 , pSrcNode(_pSrcNode
)
375 , srcOutputName(_srcOutputName
)
376 , pDstNode(_pDstNode
)
377 , dstInputName(_dstInputName
)
381 //////////////////////////////////////////////////////////////////////////
382 SDefaultGraphViewPainterSettings::SDefaultGraphViewPainterSettings()
383 : backgroundColor(60, 60, 60, 255)
385 gridLineColors
[0] = ColorB(68, 68, 68, 255);
386 gridLineColors
[1] = ColorB(48, 48, 48, 255);
389 //////////////////////////////////////////////////////////////////////////
390 void SDefaultGraphViewPainterSettings::Serialize(Serialization::IArchive
& archive
)
392 archive(backgroundColor
, "backgroundColor", "Background Color");
393 archive(gridLineColors
, "gridLineColors", "Grid Line Colors");
396 //////////////////////////////////////////////////////////////////////////
397 Serialization::SStruct
CDefaultGraphViewPainter::GetSettings()
399 return Serialization::SStruct(m_settings
);
402 //////////////////////////////////////////////////////////////////////////
403 Gdiplus::Color
CDefaultGraphViewPainter::GetBackgroundColor() const
405 return GdiplusUtils::CreateColor(m_settings
.backgroundColor
);
408 //////////////////////////////////////////////////////////////////////////
409 void CDefaultGraphViewPainter::PaintGrid(Gdiplus::Graphics
& graphics
, const SGraphViewGrid
& grid
) const
411 const int left
= static_cast<int>(grid
.bounds
.X
);
412 const int right
= static_cast<int>(grid
.bounds
.X
+ grid
.bounds
.Width
);
413 const int top
= static_cast<int>(grid
.bounds
.Y
);
414 const int bottom
= static_cast<int>(grid
.bounds
.Y
+ grid
.bounds
.Height
);
415 const int horzSpacing
= static_cast<int>(grid
.spacing
.X
);
416 const int vertSpacing
= static_cast<int>(grid
.spacing
.Y
);
418 Gdiplus::Pen
pen0(GdiplusUtils::CreateColor(m_settings
.gridLineColors
[0]), 1.0f
);
419 Gdiplus::Pen
pen1(GdiplusUtils::CreateColor(m_settings
.gridLineColors
[1]), 1.0f
);
421 for(int x
= left
+ horzSpacing
; x
< right
; x
+= static_cast<int>(grid
.spacing
.X
))
423 if(x
% (horzSpacing
* 10))
425 graphics
.DrawLine(&pen0
, x
, top
, x
, bottom
);
429 graphics
.DrawLine(&pen1
, x
, top
, x
, bottom
);
433 for(int y
= top
+ vertSpacing
; y
< bottom
; y
+= static_cast<int>(grid
.spacing
.Y
))
435 if(y
% (vertSpacing
* 10))
437 graphics
.DrawLine(&pen0
, left
, y
, right
, y
);
441 graphics
.DrawLine(&pen1
, left
, y
, right
, y
);
446 //////////////////////////////////////////////////////////////////////////
447 void CDefaultGraphViewPainter::UpdateNodePaintRect(Gdiplus::Graphics
& graphics
, const SGraphViewGrid
& grid
, CGraphViewNode
& node
) const
449 // Calculate node header and status sizes.
450 const Gdiplus::PointF headerSize
= CalculateNodeHeaderSize(graphics
, node
);
451 const Gdiplus::PointF statusSize
= CalculateNodeStatusSize(graphics
, node
);
452 const Gdiplus::PointF
headerAndStatusSize(std::max(headerSize
.X
, statusSize
.X
), headerSize
.Y
+ statusSize
.Y
);
453 const Gdiplus::PointF contentsSize
= CalculateNodeContentsSize(graphics
, node
);
454 const Gdiplus::PointF
headerAndStatusAndContentsSize(std::max(headerAndStatusSize
.X
, contentsSize
.X
), headerAndStatusSize
.Y
+ contentsSize
.Y
);
455 // Calculate total size of all node inputs.
456 Gdiplus::PointF totalInputSize
;
457 const size_t inputCount
= node
.GetInputCount();
458 for(size_t inputIdx
= 0; inputIdx
< inputCount
; ++ inputIdx
)
460 const Gdiplus::PointF inputSize
= CalculateNodeInputSize(graphics
, node
.GetInputName(inputIdx
));
461 if(inputSize
.X
> totalInputSize
.X
)
463 totalInputSize
.X
= inputSize
.X
;
465 totalInputSize
.Y
+= inputSize
.Y
+ NODE_INPUT_OUPUT_VERT_SPACING
;
467 totalInputSize
.Y
+= NODE_INPUT_OUPUT_VERT_SPACING
;
468 // Calculate total size of all node outputs.
469 Gdiplus::PointF totalOutputSize
;
470 const size_t outputCount
= node
.GetOutputCount();
471 for(size_t outputIdx
= 0; outputIdx
< outputCount
; ++ outputIdx
)
473 const Gdiplus::PointF output
= CalculateNodeOutputSize(graphics
, node
.GetOutputName(outputIdx
));
474 if(output
.X
> totalOutputSize
.X
)
476 totalOutputSize
.X
= output
.X
;
478 totalOutputSize
.Y
+= output
.Y
+ NODE_INPUT_OUPUT_VERT_SPACING
;
480 totalOutputSize
.Y
+= NODE_INPUT_OUPUT_VERT_SPACING
;
481 // Update node paint rect.
482 const Gdiplus::SizeF
paintSize(std::max(headerAndStatusAndContentsSize
.X
, totalInputSize
.X
+ NODE_INPUT_OUPUT_HORZ_SPACING
+ totalOutputSize
.X
), headerAndStatusAndContentsSize
.Y
+ std::max(totalInputSize
.Y
, totalOutputSize
.Y
));
483 const Gdiplus::RectF
paintRect(node
.GetPos(), grid
.SnapSize(paintSize
));
484 node
.SetPaintRect(paintRect
);
485 // Update node input paint rects.
486 Gdiplus::PointF
inputPos(paintRect
.X
, paintRect
.Y
+ headerAndStatusAndContentsSize
.Y
+ NODE_INPUT_OUPUT_VERT_SPACING
);
487 for(size_t inputIdx
= 0; inputIdx
< inputCount
; ++ inputIdx
)
489 UpdateNodeInputPaintRect(graphics
, node
, inputIdx
, inputPos
, totalInputSize
.X
);
490 inputPos
.Y
+= node
.GetInputPaintRect(inputIdx
).Height
+ NODE_INPUT_OUPUT_VERT_SPACING
;
492 // Update node output paint rects.
493 Gdiplus::PointF
outputPos(paintRect
.X
+ paintRect
.Width
- totalOutputSize
.X
, paintRect
.Y
+ headerAndStatusAndContentsSize
.Y
+ NODE_INPUT_OUPUT_VERT_SPACING
);
494 for(size_t outputIdx
= 0; outputIdx
< outputCount
; ++ outputIdx
)
496 UpdateNodeOutputPaintRect(graphics
, node
, outputIdx
, outputPos
, totalOutputSize
.X
);
497 outputPos
.Y
+= node
.GetOutputPaintRect(outputIdx
).Height
+ NODE_INPUT_OUPUT_VERT_SPACING
;
501 //////////////////////////////////////////////////////////////////////////
502 void CDefaultGraphViewPainter::PaintNode(Gdiplus::Graphics
& graphics
, const SGraphViewGrid
& grid
, CGraphViewNode
& node
) const
504 // Paint node body, header and outline
505 const Gdiplus::RectF paintRect
= node
.GetPaintRect();
506 const Gdiplus::PointF headerSize
= CalculateNodeHeaderSize(graphics
, node
);
507 const Gdiplus::PointF statusSize
= CalculateNodeStatusSize(graphics
, node
);
508 const Gdiplus::PointF contentsSize
= CalculateNodeContentsSize(graphics
, node
);
509 PaintNodeBody(graphics
, node
, Gdiplus::RectF(paintRect
.X
, paintRect
.Y
+ headerSize
.Y
+ statusSize
.Y
+ contentsSize
.Y
, paintRect
.Width
, paintRect
.Height
- headerSize
.Y
- statusSize
.Y
- contentsSize
.Y
));
510 PaintNodeHeader(graphics
, node
, Gdiplus::RectF(paintRect
.X
, paintRect
.Y
, paintRect
.Width
, headerSize
.Y
));
511 PaintNodeContents(graphics
, node
, Gdiplus::RectF(paintRect
.X
, paintRect
.Y
+ headerSize
.Y
, paintRect
.Width
, contentsSize
.Y
));
512 PaintNodeStatus(graphics
, node
, Gdiplus::RectF(paintRect
.X
, paintRect
.Y
+ headerSize
.Y
+ contentsSize
.Y
, paintRect
.Width
, headerSize
.Y
));
513 PaintNodeOutline(graphics
, node
, paintRect
);
514 // Paint node inputs.
515 for(size_t inputIdx
= 0, inputCount
= node
.GetInputCount(); inputIdx
< inputCount
; ++ inputIdx
)
517 PaintNodeInput(graphics
, node
, inputIdx
);
519 // Paint node outputs.
520 for(size_t outputIdx
= 0, outputCount
= node
.GetOutputCount(); outputIdx
< outputCount
; ++ outputIdx
)
522 PaintNodeOutput(graphics
, node
, outputIdx
);
526 //////////////////////////////////////////////////////////////////////////
527 Gdiplus::RectF
CDefaultGraphViewPainter::PaintLink(Gdiplus::Graphics
& graphics
, Gdiplus::PointF startPoint
, Gdiplus::PointF endPoint
, Gdiplus::Color color
, bool highlight
, float scale
) const
529 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
530 // Part of temporary fix for slow graphs with lots of links, until we can optimize the intersection tests properly.
531 if((GetAsyncKeyState(VK_SHIFT
) & 0x8000) != 0)
535 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
539 const float time
= gEnv
->pTimer
->GetAsyncTime().GetSeconds();
540 const float sinA
= sin_tpl(time
* ALPHA_HIGHLIGHT_SPEED
* gf_PI2
);
541 const float alpha
= ALPHA_HIGHLIGHT_MIN
+ clamp_tpl(ALPHA_HIGHLIGHT_MIN
+ (sinA
* ((ALPHA_HIGHLIGHT_MAX
- ALPHA_HIGHLIGHT_MIN
) * 0.5f
)), 0.0f
, 1.0f
);
542 color
= Gdiplus::Color::MakeARGB(static_cast<BYTE
>(alpha
* 255.0f
), color
.GetRed(), color
.GetGreen(), color
.GetBlue());
544 // Calculate control points.
545 const bool leftToRight
= startPoint
.X
< endPoint
.X
;
546 const float verticalSign
= startPoint
.Y
< endPoint
.Y
? 1.0f
: -1.0f
;
547 Gdiplus::PointF
midPoint((startPoint
.X
+ endPoint
.X
) / 2.0f
, (startPoint
.Y
+ endPoint
.Y
) / 2.0f
);
548 const size_t controlPointCount
= 8;
549 Gdiplus::PointF controlPoints
[controlPointCount
];
550 controlPoints
[0] = startPoint
;
552 const float offset
= fabs_tpl(midPoint
.X
- startPoint
.X
) * 0.5f
;
553 controlPoints
[1].X
= startPoint
.X
+ offset
;
556 controlPoints
[1].Y
= startPoint
.Y
;
560 const float extent
= fabs_tpl(midPoint
.Y
- startPoint
.Y
) * 0.95f
;
561 controlPoints
[1].Y
= startPoint
.Y
+ clamp_tpl(offset
* 0.5f
* verticalSign
, -extent
, extent
);
564 controlPoints
[2].X
= Lerp(controlPoints
[1].X
, midPoint
.X
, 0.75f
);
565 controlPoints
[2].Y
= Lerp(controlPoints
[1].Y
, midPoint
.Y
, 0.75f
);
566 controlPoints
[3] = midPoint
;
567 controlPoints
[7] = endPoint
;
569 const float offset
= fabs_tpl(midPoint
.X
- endPoint
.X
) * 0.5f
;
570 controlPoints
[6].X
= endPoint
.X
- offset
;
573 controlPoints
[6].Y
= endPoint
.Y
;
577 const float extent
= fabs_tpl(midPoint
.Y
- endPoint
.Y
) * 0.95f
;
578 controlPoints
[6].Y
= endPoint
.Y
- clamp_tpl(offset
* 0.5f
* verticalSign
, -extent
, extent
);
581 controlPoints
[5].X
= Lerp(controlPoints
[6].X
, midPoint
.X
, 0.75f
);
582 controlPoints
[5].Y
= Lerp(controlPoints
[6].Y
, midPoint
.Y
, 0.75f
);
583 controlPoints
[4] = midPoint
;
584 // Catch weird glitch when control points are close to parallel on y axis.
585 for(size_t i
= 1; i
< controlPointCount
; ++ i
)
587 if(fabs_tpl(controlPoints
[i
].Y
- controlPoints
[0].Y
) <= 1.0f
)
589 controlPoints
[i
].Y
= controlPoints
[0].Y
;
593 const float width
= LINK_WIDTH
* scale
;
594 Gdiplus::Pen
pen(color
, width
);
595 graphics
.DrawBezier(&pen
, controlPoints
[0], controlPoints
[1], controlPoints
[2], controlPoints
[3]);
596 graphics
.DrawBezier(&pen
, controlPoints
[4], controlPoints
[5], controlPoints
[6], controlPoints
[7]);
598 // Debug draw control points.
599 for(size_t i
= 0; i
< controlPointCount
; ++ i
)
601 graphics
.DrawEllipse(&pen
, Gdiplus::RectF(controlPoints
[i
].X
- 2.0f
, controlPoints
[i
].Y
- 2.0f
, 4.0f
, 4.0f
));
604 // Calculate paint rectangle.
605 // N.B. Technically this not how you calculate the bounds of a bezier, but it's sufficient for our purposes.
606 Gdiplus::PointF pmin
= controlPoints
[0];
607 Gdiplus::PointF pmax
= controlPoints
[0];
608 for(size_t i
= 1; i
< controlPointCount
; ++ i
)
610 if(controlPoints
[i
].X
< pmin
.X
)
612 pmin
.X
= controlPoints
[i
].X
;
614 if(controlPoints
[i
].Y
< pmin
.Y
)
616 pmin
.Y
= controlPoints
[i
].Y
;
618 if(controlPoints
[i
].X
> pmax
.X
)
620 pmax
.X
= controlPoints
[i
].X
;
622 if(controlPoints
[i
].Y
> pmax
.Y
)
624 pmax
.Y
= controlPoints
[i
].Y
;
627 return Gdiplus::RectF(pmin
.X
- width
, pmin
.Y
- width
, (pmax
.X
- pmin
.X
) + (width
* 2.0f
), (pmax
.Y
- pmin
.Y
) + (width
* 2.0f
));
630 //////////////////////////////////////////////////////////////////////////
631 void CDefaultGraphViewPainter::PaintSelectionRect(Gdiplus::Graphics
& graphics
, Gdiplus::RectF rect
) const
633 Gdiplus::SolidBrush
brush(SELECTION_FILL_COLOR
);
634 graphics
.FillRectangle(&brush
, rect
);
636 Gdiplus::Pen
pen(SELECTION_OUTLINE_COLOR
, 1.0f
);
637 graphics
.DrawRectangle(&pen
, rect
);
640 //////////////////////////////////////////////////////////////////////////
641 Gdiplus::PointF
CDefaultGraphViewPainter::CalculateNodeHeaderSize(Gdiplus::Graphics
& graphics
, const CGraphViewNode
& node
) const
643 CStringW
headerText(node
.GetName());
644 if(headerText
.IsEmpty())
648 Gdiplus::RectF headerTextRect
;
649 graphics
.MeasureString(headerText
, headerText
.GetLength(), &Assets::GetFont(), Gdiplus::PointF(), &headerTextRect
);
650 return Gdiplus::PointF(NODE_HEADER_TEXT_BORDER_X
+ headerTextRect
.Width
+ NODE_HEADER_TEXT_BORDER_X
, NODE_HEADER_TEXT_BORDER_Y
+ headerTextRect
.Height
+ NODE_HEADER_TEXT_BORDER_Y
);
653 //////////////////////////////////////////////////////////////////////////
654 Gdiplus::PointF
CDefaultGraphViewPainter::CalculateNodeStatusSize(Gdiplus::Graphics
& graphics
, const CGraphViewNode
& node
) const
656 Gdiplus::RectF statusTextRect
;
657 const EGraphViewNodeStatusFlags statusFlags
= node
.GetStatusFlags();
658 if((statusFlags
& EGraphViewNodeStatusFlags::ContainsErrors
) != 0)
660 const CStringW
statusText(NODE_ERROR_TEXT
);
661 graphics
.MeasureString(statusText
, statusText
.GetLength(), &Assets::GetFont(), Gdiplus::PointF(), &statusTextRect
);
662 statusTextRect
.Width
+= NODE_ERROR_TEXT_BORDER_X
+ NODE_ERROR_TEXT_BORDER_X
;
663 statusTextRect
.Height
+= NODE_ERROR_TEXT_BORDER_Y
+ NODE_ERROR_TEXT_BORDER_Y
;
665 else if((statusFlags
& EGraphViewNodeStatusFlags::ContainsWarnings
) != 0)
667 const CStringW
statusText(NODE_WARNING_TEXT
);
668 graphics
.MeasureString(statusText
, statusText
.GetLength(), &Assets::GetFont(), Gdiplus::PointF(), &statusTextRect
);
669 statusTextRect
.Width
+= NODE_WARNING_TEXT_BORDER_X
+ NODE_WARNING_TEXT_BORDER_X
;
670 statusTextRect
.Height
+= NODE_WARNING_TEXT_BORDER_Y
+ NODE_WARNING_TEXT_BORDER_Y
;
672 return Gdiplus::PointF(statusTextRect
.Width
, statusTextRect
.Height
);
675 //////////////////////////////////////////////////////////////////////////
676 Gdiplus::PointF
CDefaultGraphViewPainter::CalculateNodeContentsSize(Gdiplus::Graphics
& graphics
, const CGraphViewNode
& node
) const
678 if (const char * const nodeContents
= node
.GetContents())
680 Gdiplus::RectF contentsTextRect
;
681 const CStringW
contentsText(nodeContents
);
682 const Gdiplus::RectF
layoutRect(0.0f
, 0.0f
, NODE_CONTENTS_TEXT_MAX_WIDTH
, NODE_CONTENTS_TEXT_MAX_HEIGHT
);
683 graphics
.MeasureString(contentsText
, contentsText
.GetLength(), &Assets::GetFont(), layoutRect
, Gdiplus::StringFormat::GenericTypographic(), &contentsTextRect
);
684 contentsTextRect
.Width
+= NODE_CONTENTS_TEXT_BORDER_X
+ NODE_CONTENTS_TEXT_BORDER_X
;
685 contentsTextRect
.Height
+= NODE_CONTENTS_TEXT_BORDER_Y
+ NODE_CONTENTS_TEXT_BORDER_Y
;
686 return Gdiplus::PointF(contentsTextRect
.Width
, contentsTextRect
.Height
);
688 return Gdiplus::PointF();
691 //////////////////////////////////////////////////////////////////////////
692 Gdiplus::PointF
CDefaultGraphViewPainter::CalculateNodeInputSize(Gdiplus::Graphics
& graphics
, const char* name
) const
694 const CStringW
stringW(name
!= NULL
? name
: "");
695 Gdiplus::RectF nameRect
;
696 graphics
.MeasureString(stringW
, stringW
.GetLength(), &Assets::GetFont(), Gdiplus::PointF(), &nameRect
);
698 const Gdiplus::PointF
iconSize(NODE_INPUT_ICON_BORDER
+ NODE_INPUT_ICON_WIDTH
+ NODE_INPUT_ICON_BORDER
, NODE_INPUT_ICON_BORDER
+ NODE_INPUT_ICON_HEIGHT
+ NODE_INPUT_ICON_BORDER
);
699 const Gdiplus::PointF
nameSize(NODE_INPUT_NAME_BORDER
+ nameRect
.Width
+ NODE_INPUT_NAME_BORDER
, NODE_INPUT_NAME_BORDER
+ nameRect
.Height
+ NODE_INPUT_NAME_BORDER
);
700 return Gdiplus::PointF(iconSize
.X
+ nameSize
.X
, std::max(iconSize
.Y
, nameSize
.Y
));
703 //////////////////////////////////////////////////////////////////////////
704 Gdiplus::PointF
CDefaultGraphViewPainter::CalculateNodeOutputSize(Gdiplus::Graphics
& graphics
, const char* name
) const
706 const CStringW
stringW(name
!= NULL
? name
: "");
707 Gdiplus::RectF nameRect
;
708 graphics
.MeasureString(stringW
, stringW
.GetLength(), &Assets::GetFont(), Gdiplus::PointF(), &nameRect
);
710 const Gdiplus::PointF
iconSize(NODE_OUTPUT_ICON_BORDER
+ NODE_OUTPUT_ICON_WIDTH
+ NODE_OUTPUT_ICON_BORDER
, NODE_OUTPUT_ICON_BORDER
+ NODE_OUTPUT_ICON_HEIGHT
+ NODE_OUTPUT_ICON_BORDER
);
711 const Gdiplus::PointF
nameSize(NODE_OUTPUT_NAME_BORDER
+ nameRect
.Width
+ NODE_OUTPUT_NAME_BORDER
, NODE_OUTPUT_NAME_BORDER
+ nameRect
.Height
+ NODE_OUTPUT_NAME_BORDER
);
712 return Gdiplus::PointF(iconSize
.X
+ nameSize
.X
, std::max(iconSize
.Y
, nameSize
.Y
));
715 //////////////////////////////////////////////////////////////////////////
716 void CDefaultGraphViewPainter::PaintNodeBody(Gdiplus::Graphics
& graphics
, CGraphViewNode
& node
, Gdiplus::RectF paintRect
) const
718 const float bevels
[4] = { 0.0f
, 0.0f
, NODE_BEVEL
, NODE_BEVEL
};
719 const Gdiplus::SolidBrush
brush(node
.IsEnabled() ? NODE_BODY_FILL_COLOR
: NODE_BODY_FILL_COLOR_DISABLED
);
720 GdiplusUtils::FillBeveledRect(graphics
, paintRect
, bevels
, brush
);
723 //////////////////////////////////////////////////////////////////////////
724 void CDefaultGraphViewPainter::PaintNodeHeader(Gdiplus::Graphics
& graphics
, CGraphViewNode
& node
, Gdiplus::RectF paintRect
) const
726 const Gdiplus::Color headerColor
= node
.GetHeaderColor();
727 const BYTE alpha
= node
.IsEnabled() ? NODE_HEADER_ALPHA
: NODE_HEADER_ALPHA_DISABLED
;
728 const Gdiplus::Color fillColorLeft
= Gdiplus::Color(alpha
, headerColor
.GetRed(), headerColor
.GetGreen(), headerColor
.GetBlue());
729 const Gdiplus::Color fillColorRight
= GdiplusUtils::LerpColor(fillColorLeft
, Gdiplus::Color(alpha
, 255, 255, 255), 0.6f
);
731 const float bevels
[4] = { NODE_BEVEL
, NODE_BEVEL
, 0.0f
, 0.0f
};
732 const Gdiplus::LinearGradientBrush
brush(paintRect
, fillColorLeft
, fillColorRight
, Gdiplus::LinearGradientModeForwardDiagonal
);
733 GdiplusUtils::FillBeveledRect(graphics
, paintRect
, bevels
, brush
);
735 const CStringW
headerText(node
.GetName());
736 const Gdiplus::SolidBrush
textBrush(NODE_HEADER_TEXT_COLOR
);
737 graphics
.DrawString(headerText
, headerText
.GetLength(), &Assets::GetFont(), Gdiplus::PointF(paintRect
.X
+ NODE_HEADER_TEXT_BORDER_X
, paintRect
.Y
+ NODE_HEADER_TEXT_BORDER_Y
), &textBrush
);
740 //////////////////////////////////////////////////////////////////////////
741 void CDefaultGraphViewPainter::PaintNodeContents(Gdiplus::Graphics
& graphics
, CGraphViewNode
& node
, Gdiplus::RectF paintRect
) const
743 if (const char * const nodeContents
= node
.GetContents())
745 Gdiplus::SolidBrush
brush(NODE_CONTENTS_FILL_COLOR
);
746 graphics
.FillRectangle(&brush
, paintRect
);
747 const CStringW
contentsText(nodeContents
);
748 const Gdiplus::SolidBrush
textBrush(NODE_CONTENTS_TEXT_COLOR
);
749 const Gdiplus::PointF
pos(paintRect
.X
+ NODE_CONTENTS_TEXT_BORDER_X
, paintRect
.Y
+ NODE_CONTENTS_TEXT_BORDER_Y
);
750 const Gdiplus::RectF
layoutRect(pos
.X
, pos
.Y
, NODE_CONTENTS_TEXT_MAX_WIDTH
, NODE_CONTENTS_TEXT_MAX_HEIGHT
);
751 graphics
.DrawString(contentsText
, contentsText
.GetLength(), &Assets::GetFont(), layoutRect
, Gdiplus::StringFormat::GenericTypographic(), &textBrush
);
755 //////////////////////////////////////////////////////////////////////////
756 void CDefaultGraphViewPainter::PaintNodeStatus(Gdiplus::Graphics
& graphics
, CGraphViewNode
& node
, Gdiplus::RectF paintRect
) const
758 const EGraphViewNodeStatusFlags statusFlags
= node
.GetStatusFlags();
759 if((statusFlags
& EGraphViewNodeStatusFlags::ContainsErrors
) != 0)
761 Gdiplus::SolidBrush
brush(NODE_ERROR_FILL_COLOR
);
762 graphics
.FillRectangle(&brush
, paintRect
);
764 const CStringW
statusText(NODE_ERROR_TEXT
);
765 const Gdiplus::SolidBrush
textBrush(NODE_ERROR_TEXT_COLOR
);
766 graphics
.DrawString(statusText
, statusText
.GetLength(), &Assets::GetFont(), Gdiplus::PointF(paintRect
.X
+ NODE_ERROR_TEXT_BORDER_X
, paintRect
.Y
+ NODE_ERROR_TEXT_BORDER_Y
), &textBrush
);
768 else if((statusFlags
& EGraphViewNodeStatusFlags::ContainsWarnings
) != 0)
770 Gdiplus::SolidBrush
brush(NODE_WARNING_FILL_COLOR
);
771 graphics
.FillRectangle(&brush
, paintRect
);
773 const CStringW
statusText(NODE_WARNING_TEXT
);
774 const Gdiplus::SolidBrush
textBrush(NODE_WARNING_TEXT_COLOR
);
775 graphics
.DrawString(statusText
, statusText
.GetLength(), &Assets::GetFont(), Gdiplus::PointF(paintRect
.X
+ NODE_WARNING_TEXT_BORDER_X
, paintRect
.Y
+ NODE_WARNING_TEXT_BORDER_Y
), &textBrush
);
779 //////////////////////////////////////////////////////////////////////////
780 void CDefaultGraphViewPainter::PaintNodeOutline(Gdiplus::Graphics
& graphics
, CGraphViewNode
& node
, Gdiplus::RectF paintRect
) const
782 Gdiplus::Color outlineColor
= NODE_BODY_OUTLINE_COLOR
;
783 if(node
.IsSelected())
785 const float time
= gEnv
->pTimer
->GetAsyncTime().GetSeconds();
786 const float sinA
= sin_tpl(time
* ALPHA_HIGHLIGHT_SPEED
* gf_PI2
);
787 const float alpha
= ALPHA_HIGHLIGHT_MIN
+ clamp_tpl(ALPHA_HIGHLIGHT_MIN
+ (sinA
* ((ALPHA_HIGHLIGHT_MAX
- ALPHA_HIGHLIGHT_MIN
) * 0.5f
)), 0.0f
, 1.0f
);
788 outlineColor
= Gdiplus::Color::MakeARGB(static_cast<BYTE
>(alpha
* 255.0f
), NODE_BODY_OUTLINE_COLOR_HIGHLIGHT
.GetRed(), NODE_BODY_OUTLINE_COLOR_HIGHLIGHT
.GetGreen(), NODE_BODY_OUTLINE_COLOR_HIGHLIGHT
.GetBlue());
791 const float bevels
[4] = { NODE_BEVEL
, NODE_BEVEL
, NODE_BEVEL
, NODE_BEVEL
};
792 const Gdiplus::Pen
pen(outlineColor
, NODE_BODY_OUTLINE_WIDTH
);
793 GdiplusUtils::PaintBeveledRect(graphics
, paintRect
, bevels
, pen
);
796 //////////////////////////////////////////////////////////////////////////
797 void CDefaultGraphViewPainter::UpdateNodeInputPaintRect(Gdiplus::Graphics
& graphics
, CGraphViewNode
& node
, size_t iInput
, Gdiplus::PointF pos
, float maxWidth
) const
799 const CStringW
stringW(node
.GetInputName(iInput
));
800 Gdiplus::RectF nameRect
;
801 graphics
.MeasureString(stringW
, stringW
.GetLength(), &Assets::GetFont(), Gdiplus::PointF(), &nameRect
);
803 const Gdiplus::PointF
iconSize(NODE_INPUT_ICON_BORDER
+ NODE_INPUT_ICON_WIDTH
+ NODE_INPUT_ICON_BORDER
, NODE_INPUT_ICON_BORDER
+ NODE_INPUT_ICON_HEIGHT
+ NODE_INPUT_ICON_BORDER
);
804 const Gdiplus::PointF
nameSize(NODE_INPUT_NAME_BORDER
+ nameRect
.Width
+ NODE_INPUT_NAME_BORDER
, NODE_INPUT_NAME_BORDER
+ nameRect
.Height
+ NODE_INPUT_NAME_BORDER
);
805 const Gdiplus::RectF
paintRect(pos
.X
, pos
.Y
, iconSize
.X
+ nameSize
.X
, std::max(iconSize
.Y
, nameSize
.Y
));
806 node
.SetInputPaintRect(iInput
, paintRect
);
809 //////////////////////////////////////////////////////////////////////////
810 void CDefaultGraphViewPainter::PaintNodeInput(Gdiplus::Graphics
& graphics
, CGraphViewNode
& node
, size_t iInput
) const
812 const Gdiplus::RectF paintRect
= node
.GetInputPaintRect(iInput
);
813 const Gdiplus::PointF
iconSize(NODE_INPUT_ICON_BORDER
+ NODE_INPUT_ICON_WIDTH
+ NODE_INPUT_ICON_BORDER
, NODE_INPUT_ICON_BORDER
+ NODE_INPUT_ICON_HEIGHT
+ NODE_INPUT_ICON_BORDER
);
814 Gdiplus::PointF
iconPos(paintRect
.X
+ NODE_INPUT_ICON_BORDER
, paintRect
.Y
+ NODE_INPUT_ICON_BORDER
);
815 if(iconSize
.Y
< paintRect
.Height
)
817 iconPos
.Y
+= (paintRect
.Height
- iconSize
.Y
) * 0.5f
;
819 PaintNodeInputIcon(graphics
, iconPos
, node
.GetInputColor(iInput
), (node
.GetInputFlags(iInput
) & EScriptGraphPortFlags::Execute
) != 0);
821 const CStringW
stringW(node
.GetInputName(iInput
));
822 const Gdiplus::RectF
alignedNameRect(paintRect
.X
+ iconSize
.X
, paintRect
.Y
, paintRect
.Width
- iconSize
.X
, paintRect
.Height
);
823 Gdiplus::StringFormat stringFormat
;
824 stringFormat
.SetAlignment(Gdiplus::StringAlignmentNear
);
825 stringFormat
.SetLineAlignment(Gdiplus::StringAlignmentCenter
);
826 const Gdiplus::SolidBrush
nameBrush(NODE_OUTPUT_NAME_COLOR
);
827 graphics
.DrawString(stringW
, stringW
.GetLength(), &Assets::GetFont(), alignedNameRect
, &stringFormat
, &nameBrush
);
830 //////////////////////////////////////////////////////////////////////////
831 void CDefaultGraphViewPainter::PaintNodeInputIcon(Gdiplus::Graphics
& graphics
, Gdiplus::PointF pos
, Gdiplus::Color color
, bool active
) const
833 const Gdiplus::SolidBrush
fillBrush(color
);
834 const Gdiplus::Pen
pen(NODE_INPUT_ICON_OUTLINE_COLOR
, NODE_INPUT_ICON_OUTLINE_WIDTH
);
837 const Gdiplus::PointF points
[] =
839 Gdiplus::PointF(pos
.X
, pos
.Y
),
840 Gdiplus::PointF(pos
.X
+ NODE_INPUT_ICON_WIDTH
, pos
.Y
+ (NODE_INPUT_ICON_HEIGHT
* 0.5f
)),
841 Gdiplus::PointF(pos
.X
, pos
.Y
+ NODE_INPUT_ICON_HEIGHT
)
843 graphics
.FillPolygon(&fillBrush
, points
, CRY_ARRAY_COUNT(points
));
847 graphics
.FillEllipse(&fillBrush
, pos
.X
, pos
.Y
, NODE_INPUT_ICON_WIDTH
, NODE_INPUT_ICON_HEIGHT
);
851 //////////////////////////////////////////////////////////////////////////
852 void CDefaultGraphViewPainter::UpdateNodeOutputPaintRect(Gdiplus::Graphics
& graphics
, CGraphViewNode
& node
, size_t iOutput
, Gdiplus::PointF pos
, float maxWidth
) const
854 const CStringW
stringW(node
.GetOutputName(iOutput
));
855 Gdiplus::RectF nameRect
;
856 graphics
.MeasureString(stringW
, stringW
.GetLength(), &Assets::GetFont(), Gdiplus::PointF(), &nameRect
);
858 const Gdiplus::PointF
iconSize(NODE_OUTPUT_ICON_BORDER
+ NODE_OUTPUT_ICON_WIDTH
+ NODE_OUTPUT_ICON_BORDER
, NODE_OUTPUT_ICON_BORDER
+ NODE_OUTPUT_ICON_HEIGHT
+ NODE_OUTPUT_ICON_BORDER
);
859 const Gdiplus::PointF
nameSize(NODE_OUTPUT_NAME_BORDER
+ nameRect
.Width
+ NODE_OUTPUT_NAME_BORDER
, NODE_OUTPUT_NAME_BORDER
+ nameRect
.Height
+ NODE_OUTPUT_NAME_BORDER
);
860 const Gdiplus::RectF
paintRect(pos
.X
+ maxWidth
- (iconSize
.X
+ nameSize
.X
), pos
.Y
, iconSize
.X
+ nameSize
.X
, std::max(iconSize
.Y
, nameSize
.Y
));
861 node
.SetOutputPaintRect(iOutput
, paintRect
);
864 //////////////////////////////////////////////////////////////////////////
865 void CDefaultGraphViewPainter::PaintNodeOutput(Gdiplus::Graphics
& graphics
, CGraphViewNode
& node
, size_t iOutput
) const
867 const Gdiplus::RectF paintRect
= node
.GetOutputPaintRect(iOutput
);
868 const Gdiplus::PointF
iconSize(NODE_OUTPUT_ICON_BORDER
+ NODE_OUTPUT_ICON_WIDTH
+ NODE_OUTPUT_ICON_BORDER
, NODE_OUTPUT_ICON_BORDER
+ NODE_OUTPUT_ICON_HEIGHT
+ NODE_OUTPUT_ICON_BORDER
);
869 Gdiplus::PointF
iconPos(paintRect
.X
+ paintRect
.Width
- NODE_OUTPUT_ICON_WIDTH
- NODE_OUTPUT_ICON_BORDER
, paintRect
.Y
+ NODE_OUTPUT_ICON_BORDER
);
870 if(iconSize
.Y
< paintRect
.Height
)
872 iconPos
.Y
+= (paintRect
.Height
- iconSize
.Y
) * 0.5f
;
874 PaintNodeOutputIcon(graphics
, iconPos
, node
.GetOutputColor(iOutput
), (node
.GetOutputFlags(iOutput
) & EScriptGraphPortFlags::Execute
) != 0);
876 const CStringW
stringW(node
.GetOutputName(iOutput
));
877 const Gdiplus::RectF
alignedNameRect(paintRect
.X
, paintRect
.Y
, paintRect
.Width
- iconSize
.X
, paintRect
.Height
);
878 Gdiplus::StringFormat stringFormat
;
879 stringFormat
.SetAlignment(Gdiplus::StringAlignmentFar
);
880 stringFormat
.SetLineAlignment(Gdiplus::StringAlignmentCenter
);
881 const Gdiplus::SolidBrush
nameBrush(NODE_OUTPUT_NAME_COLOR
);
882 graphics
.DrawString(stringW
, stringW
.GetLength(), &Assets::GetFont(), alignedNameRect
, &stringFormat
, &nameBrush
);
884 if(((node
.GetOutputFlags(iOutput
) & EScriptGraphPortFlags::SpacerAbove
) != 0) && (iOutput
> 0))
886 const Gdiplus::Pen
pen(NODE_OUTPUT_SPACER_COLOR
, NODE_OUTPUT_SPACER_LINE_WIDTH
);
887 const float right
= paintRect
.GetRight() - NODE_OUTPUT_SPACER_OFFSET
;
888 const float left
= right
- NODE_OUTPUT_SPACER_WIDTH
;
889 const float top
= paintRect
.GetTop() - (NODE_INPUT_OUPUT_VERT_SPACING
* 0.5f
);
890 graphics
.DrawLine(&pen
, Gdiplus::PointF(left
, top
), Gdiplus::PointF(right
, top
));
893 if(((node
.GetOutputFlags(iOutput
) & EScriptGraphPortFlags::SpacerBelow
) != 0) && (iOutput
< (node
.GetOutputCount() - 1)))
895 const Gdiplus::Pen
pen(NODE_OUTPUT_SPACER_COLOR
, NODE_OUTPUT_SPACER_LINE_WIDTH
);
896 const float right
= paintRect
.GetRight() - NODE_OUTPUT_SPACER_OFFSET
;
897 const float left
= right
- NODE_OUTPUT_SPACER_WIDTH
;
898 const float bottom
= paintRect
.GetBottom() + (NODE_INPUT_OUPUT_VERT_SPACING
* 0.5f
);
899 graphics
.DrawLine(&pen
, Gdiplus::PointF(left
, bottom
), Gdiplus::PointF(right
, bottom
));
903 //////////////////////////////////////////////////////////////////////////
904 void CDefaultGraphViewPainter::PaintNodeOutputIcon(Gdiplus::Graphics
& graphics
, Gdiplus::PointF pos
, Gdiplus::Color color
, bool active
) const
906 const Gdiplus::SolidBrush
fillBrush(color
);
907 const Gdiplus::Pen
pen(NODE_OUTPUT_ICON_OUTLINE_COLOR
, NODE_OUTPUT_ICON_OUTLINE_WIDTH
);
910 const Gdiplus::PointF points
[] =
912 Gdiplus::PointF(pos
.X
, pos
.Y
),
913 Gdiplus::PointF(pos
.X
+ NODE_OUTPUT_ICON_WIDTH
, pos
.Y
+ (NODE_OUTPUT_ICON_HEIGHT
* 0.5f
)),
914 Gdiplus::PointF(pos
.X
, pos
.Y
+ NODE_OUTPUT_ICON_HEIGHT
)
916 graphics
.FillPolygon(&fillBrush
, points
, CRY_ARRAY_COUNT(points
));
920 graphics
.FillEllipse(&fillBrush
, pos
.X
, pos
.Y
, NODE_OUTPUT_ICON_WIDTH
, NODE_OUTPUT_ICON_HEIGHT
);
924 //////////////////////////////////////////////////////////////////////////
925 BEGIN_MESSAGE_MAP(CGraphView
, CWnd
)
942 //////////////////////////////////////////////////////////////////////////
943 BOOL
CGraphView::Create(DWORD dwStyle
, const RECT
& rect
, CWnd
* pParentWnd
, UINT nID
)
948 LPCTSTR lpClassName
= AfxRegisterWndClass(CS_DBLCLKS
| CS_HREDRAW
| CS_VREDRAW
, AfxGetApp()->LoadStandardCursor(IDC_ARROW
), NULL
, NULL
);
949 VERIFY(CreateEx(NULL
, lpClassName
, "SchematycView", dwStyle
, CRect(0, 0, 100, 100), pParentWnd
, nID
));
953 CWnd::SetTimer(UPDATE_TIMER_EVENT_ID
, 1000 / 30, NULL
);
954 // Register drop target.
955 m_dropTarget
.Register(this);
962 //////////////////////////////////////////////////////////////////////////
963 void CGraphView::Refresh()
965 __super::Invalidate(TRUE
);
968 //////////////////////////////////////////////////////////////////////////
969 SGraphViewSettings
& CGraphView::GetSettings()
974 //////////////////////////////////////////////////////////////////////////
975 const SGraphViewGrid
& CGraphView::GetGrid() const
980 //////////////////////////////////////////////////////////////////////////
981 const IGraphViewPainterPtr
& CGraphView::GetPainter() const
986 //////////////////////////////////////////////////////////////////////////
987 TGraphViewLinkVector
& CGraphView::GetLinks()
992 //////////////////////////////////////////////////////////////////////////
993 const TGraphViewLinkVector
& CGraphView::GetLinks() const
998 //////////////////////////////////////////////////////////////////////////
999 void CGraphView::DoQuickSearch(const char* defaultFilter
, const CGraphViewNodePtr
& pNode
, size_t iNodeOutput
, const CPoint
* pPoint
)
1001 SET_LOCAL_RESOURCE_SCOPE
1003 GetCursorPos(&cursorPoint
);
1004 if(const IQuickSearchOptions
* pQuickSearchOptions
= GetQuickSearchOptions(cursorPoint
, pNode
, iNodeOutput
))
1006 CQuickSearchDlg
quickSearchDlg(this, CPoint(cursorPoint
.x
- 20, cursorPoint
.y
- 20), "Nodes", defaultFilter
, nullptr, *pQuickSearchOptions
);
1007 if(quickSearchDlg
.DoModal() == IDOK
)
1009 SetFocus(); // Ensure we regain focus from quick search dialog.
1010 OnQuickSearchResult(pPoint
? *pPoint
: cursorPoint
, pNode
, iNodeOutput
, quickSearchDlg
.GetResult());
1015 //////////////////////////////////////////////////////////////////////////
1016 CGraphView::CGraphView(const SGraphViewGrid
& grid
, const IGraphViewPainterPtr
& pPainter
)
1018 , m_pPainter(pPainter
? pPainter
: IGraphViewPainterPtr(new CDefaultGraphViewPainter()))
1020 , m_prevMousePoint(0, 0)
1022 , m_iSelectedLink(INVALID_INDEX
)
1023 , m_dropTarget(*this)
1026 //////////////////////////////////////////////////////////////////////////
1027 void CGraphView::Enable(bool enable
)
1029 if(enable
!= m_enabled
)
1036 //////////////////////////////////////////////////////////////////////////
1037 bool CGraphView::IsEnabled() const
1042 //////////////////////////////////////////////////////////////////////////
1043 void CGraphView::ClearSelection()
1045 ClearNodeSelection();
1046 ClearLinkSelection();
1047 OnSelection(CGraphViewNodePtr(), nullptr);
1048 __super::Invalidate(TRUE
);
1051 //////////////////////////////////////////////////////////////////////////
1052 void CGraphView::SetScrollOffset(Gdiplus::PointF scrollOffset
)
1054 m_scrollOffset
= scrollOffset
;
1055 OnMove(m_scrollOffset
);
1058 //////////////////////////////////////////////////////////////////////////
1059 void CGraphView::AddNode(const CGraphViewNodePtr
& pNode
, bool select
, bool scrollToFit
)
1064 m_nodes
.push_back(pNode
);
1067 ClearNodeSelection();
1072 CPaintDC
paintDC(this);
1073 Gdiplus::Graphics
mainGraphics(paintDC
.GetSafeHdc());
1074 m_pPainter
->UpdateNodePaintRect(mainGraphics
, m_grid
, *pNode
);
1075 ScrollToFit(pNode
->GetPaintRect());
1077 __super::Invalidate(TRUE
);
1081 //////////////////////////////////////////////////////////////////////////
1082 void CGraphView::RemoveNode(CGraphViewNodePtr pNode
)
1084 CRY_ASSERT(pNode
!= NULL
);
1087 // Make sure node is not selected.
1089 // Remove links connected to node.
1090 RemoveLinksConnectedToNode(pNode
);
1092 TGraphViewNodePtrVector::iterator iEndNode
= m_nodes
.end();
1093 TGraphViewNodePtrVector::iterator iEraseNode
= std::find(m_nodes
.begin(), iEndNode
, pNode
);
1094 CRY_ASSERT(iEraseNode
!= iEndNode
);
1095 if(iEraseNode
!= iEndNode
)
1097 m_nodes
.erase(iEraseNode
);
1098 OnNodeRemoved(*pNode
);
1100 __super::Invalidate(TRUE
);
1104 //////////////////////////////////////////////////////////////////////////
1105 CGraphViewNodePtr
CGraphView::FindNode(Gdiplus::PointF pos
)
1107 for(TGraphViewNodePtrVector::const_iterator iNode
= m_nodes
.begin(), iEndNode
= m_nodes
.end(); iNode
!= iEndNode
; ++ iNode
)
1109 const CGraphViewNodePtr
& pNode
= *iNode
;
1110 if(pNode
->GetPaintRect().Contains(pos
))
1115 return CGraphViewNodePtr();
1118 //////////////////////////////////////////////////////////////////////////
1119 TGraphViewNodePtrVector
& CGraphView::GetNodes()
1124 //////////////////////////////////////////////////////////////////////////
1125 const TGraphViewNodePtrVector
& CGraphView::GetNodes() const
1130 //////////////////////////////////////////////////////////////////////////
1131 void CGraphView::ClearNodeSelection()
1133 for(TGraphViewNodePtrVector::iterator iSelectedNode
= m_selectedNodes
.begin(), iEndSelectedNode
= m_selectedNodes
.end(); iSelectedNode
!= iEndSelectedNode
; ++ iSelectedNode
)
1135 (*iSelectedNode
)->Select(false);
1137 m_selectedNodes
.clear();
1140 //////////////////////////////////////////////////////////////////////////
1141 void CGraphView::SelectNode(const CGraphViewNodePtr
& pNode
)
1143 if((pNode
!= NULL
) && (pNode
->IsSelected() == false))
1145 ClearLinkSelection();
1146 m_selectedNodes
.push_back(pNode
);
1147 pNode
->Select(true);
1148 OnSelection(pNode
, nullptr);
1149 __super::Invalidate(TRUE
);
1153 //////////////////////////////////////////////////////////////////////////
1154 void CGraphView::SelectNodesInRect(const Gdiplus::RectF
& rect
)
1157 for(TGraphViewNodePtrVector::iterator iNode
= m_nodes
.begin(), iEndNode
= m_nodes
.end(); iNode
!= iEndNode
; ++ iNode
)
1159 CGraphViewNodePtr pNode
= *iNode
;
1160 if(rect
.IntersectsWith(pNode
->GetPaintRect()) != 0)
1167 //////////////////////////////////////////////////////////////////////////
1168 const TGraphViewNodePtrVector
& CGraphView::GetSelectedNodes() const
1170 return m_selectedNodes
;
1173 //////////////////////////////////////////////////////////////////////////
1174 void CGraphView::MoveSelectedNodes(Gdiplus::PointF transform
, bool bSnapToGrid
)
1176 for(TGraphViewNodePtrVector::iterator iNode
= m_selectedNodes
.begin(), iEndNode
= m_selectedNodes
.end(); iNode
!= iEndNode
; ++ iNode
)
1178 CGraphViewNodePtr pNode
= *iNode
;
1179 pNode
->SetPos(pNode
->GetPos() + transform
, m_settings
.format
.bSnapToGrid
&& bSnapToGrid
);
1183 //////////////////////////////////////////////////////////////////////////
1184 bool CGraphView::CreateLink(const CGraphViewNodePtr
& pSrcNode
, const char* srcOutputName
, const CGraphViewNodePtr
& pDstNode
, const char* dstInputName
)
1186 CRY_ASSERT((pSrcNode
!= NULL
) && (srcOutputName
!= NULL
) && (pDstNode
!= NULL
) && (dstInputName
!= NULL
));
1187 if((pSrcNode
!= NULL
) && (srcOutputName
!= NULL
) && (pDstNode
!= NULL
) && (dstInputName
!= NULL
))
1189 if(CanCreateLink(*pSrcNode
, srcOutputName
, *pDstNode
, dstInputName
) == true)
1191 m_links
.push_back(SGraphViewLink(this, pSrcNode
, srcOutputName
, pDstNode
, dstInputName
));
1192 OnLinkCreated(m_links
.back());
1193 __super::Invalidate(TRUE
);
1200 //////////////////////////////////////////////////////////////////////////
1201 void CGraphView::AddLink(const CGraphViewNodePtr
& pSrcNode
, const char* srcOutputName
, const CGraphViewNodePtr
& pDstNode
, const char* dstInputName
)
1203 CRY_ASSERT((pSrcNode
!= NULL
) && (srcOutputName
!= NULL
) && (pDstNode
!= NULL
) && (dstInputName
!= NULL
));
1204 if((pSrcNode
!= NULL
) && (srcOutputName
!= NULL
) && (pDstNode
!= NULL
) && (dstInputName
!= NULL
))
1206 m_links
.push_back(SGraphViewLink(this, pSrcNode
, srcOutputName
, pDstNode
, dstInputName
));
1207 __super::Invalidate(TRUE
);
1211 //////////////////////////////////////////////////////////////////////////
1212 void CGraphView::RemoveLink(size_t iLink
)
1214 const size_t linkCount
= m_links
.size();
1215 CRY_ASSERT(iLink
< linkCount
);
1216 if(iLink
< linkCount
)
1218 if(m_iSelectedLink
== iLink
)
1220 m_iSelectedLink
= INVALID_INDEX
;
1222 OnLinkRemoved(m_links
[iLink
]);
1223 m_links
.erase(m_links
.begin() + iLink
);
1224 __super::Invalidate(TRUE
);
1228 //////////////////////////////////////////////////////////////////////////
1229 void CGraphView::RemoveLinksConnectedToNode(const CGraphViewNodePtr
& pNode
)
1231 if(!m_links
.empty())
1233 TGraphViewLinkVector::iterator iEndLink
= m_links
.end();
1234 TGraphViewLinkVector::iterator iLastLink
= iEndLink
- 1;
1235 for(TGraphViewLinkVector::iterator iLink
= m_links
.begin(); iLink
<= iLastLink
; )
1237 SGraphViewLink
& link
= *iLink
;
1238 if((link
.pSrcNode
.lock() == pNode
) || (link
.pDstNode
.lock() == pNode
))
1240 OnLinkRemoved(link
);
1241 if(iLink
< iLastLink
)
1243 *iLink
= *iLastLink
;
1252 m_links
.erase(iLastLink
+ 1, iEndLink
);
1253 __super::Invalidate(TRUE
);
1257 //////////////////////////////////////////////////////////////////////////
1258 void CGraphView::RemoveLinksConnectedToNodeInput(const CGraphViewNodePtr
& pNode
, const char* inputName
)
1260 CRY_ASSERT(inputName
!= NULL
);
1261 if((inputName
!= NULL
) && (m_links
.empty() == false))
1263 TGraphViewLinkVector::iterator iEndLink
= m_links
.end();
1264 TGraphViewLinkVector::iterator iLastLink
= iEndLink
- 1;
1265 for(TGraphViewLinkVector::iterator iLink
= m_links
.begin(); iLink
<= iLastLink
; )
1267 SGraphViewLink
& link
= *iLink
;
1268 if((link
.pDstNode
.lock() == pNode
) && (strcmp(link
.dstInputName
.c_str(), inputName
) == 0))
1270 OnLinkRemoved(link
);
1271 if(iLink
< iLastLink
)
1273 *iLink
= *iLastLink
;
1282 m_links
.erase(iLastLink
+ 1, iEndLink
);
1283 __super::Invalidate(TRUE
);
1287 //////////////////////////////////////////////////////////////////////////
1288 void CGraphView::RemoveLinksConnectedToNodeOutput(const CGraphViewNodePtr
& pNode
, const char* outputName
)
1290 CRY_ASSERT(outputName
!= NULL
);
1291 if((outputName
!= NULL
) && (m_links
.empty() == false))
1293 TGraphViewLinkVector::iterator iEndLink
= m_links
.end();
1294 TGraphViewLinkVector::iterator iLastLink
= iEndLink
- 1;
1295 for(TGraphViewLinkVector::iterator iLink
= m_links
.begin(); iLink
<= iLastLink
; )
1297 SGraphViewLink
& link
= *iLink
;
1298 if((link
.pSrcNode
.lock() == pNode
) && (strcmp(link
.srcOutputName
.c_str(), outputName
) == 0))
1300 OnLinkRemoved(link
);
1301 if(iLink
< iLastLink
)
1303 *iLink
= *iLastLink
;
1312 m_links
.erase(iLastLink
+ 1, iEndLink
);
1313 __super::Invalidate(TRUE
);
1317 //////////////////////////////////////////////////////////////////////////
1318 void CGraphView::FindLinks(const CGraphViewNodePtr
& pDstNode
, const char* inputName
, TSizeTVector
& output
) const
1320 CRY_ASSERT(inputName
!= NULL
);
1321 if(inputName
!= NULL
)
1323 for(TGraphViewLinkVector::const_iterator iBeginLink
= m_links
.begin(), iEndLink
= m_links
.end(), iLink
= iBeginLink
; iLink
!= iEndLink
; ++ iLink
)
1325 const SGraphViewLink
& link
= *iLink
;
1326 if((link
.pDstNode
.lock() == pDstNode
) && (strcmp(link
.dstInputName
.c_str(), inputName
) == 0))
1328 output
.push_back(iLink
- iBeginLink
);
1334 //////////////////////////////////////////////////////////////////////////
1335 size_t CGraphView::FindLink(const CGraphViewNodePtr
& pSrcNode
, const char* srcOutputName
, const CGraphViewNodePtr
& pDstNode
, const char* dstInputName
)
1337 for(TGraphViewLinkVector::iterator iBeginLink
= m_links
.begin(), iEndLink
= m_links
.end(), iLink
= iBeginLink
; iLink
!= iEndLink
; ++ iLink
)
1339 SGraphViewLink
& link
= *iLink
;
1340 if((link
.pSrcNode
.lock() == pSrcNode
) && (strcmp(link
.srcOutputName
, srcOutputName
) == 0) && (link
.pDstNode
.lock() == pDstNode
) && (strcmp(link
.dstInputName
, dstInputName
) == 0))
1342 return iLink
- iBeginLink
;
1345 return INVALID_INDEX
;
1348 //////////////////////////////////////////////////////////////////////////
1349 size_t CGraphView::FindLink(Gdiplus::PointF point
)
1351 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1352 // Part of temporary fix for slow graphs with lots of links, until we can optimize the intersection tests properly.
1353 if((GetAsyncKeyState(VK_SHIFT
) & 0x8000) == 0)
1355 return INVALID_INDEX
;
1357 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1358 for(TGraphViewLinkVector::iterator iBeginLink
= m_links
.begin(), iEndLink
= m_links
.end(), iLink
= iBeginLink
; iLink
!= iEndLink
; ++ iLink
)
1360 SGraphViewLink
& link
= *iLink
;
1361 if(link
.paintRect
.Contains(point
))
1363 const CGraphViewNodePtr pSrcNode
= link
.pSrcNode
.lock();
1364 const CGraphViewNodePtr pDstNode
= link
.pDstNode
.lock();
1365 CRY_ASSERT(pSrcNode
&& pDstNode
);
1366 if(pSrcNode
&& pDstNode
)
1368 const size_t iSrcOutput
= pSrcNode
->FindOutput(link
.srcOutputName
);
1369 const size_t iDstInput
= pDstNode
->FindInput(link
.dstInputName
);
1370 CRY_ASSERT((iSrcOutput
!= INVALID_INDEX
) && (iDstInput
!= INVALID_INDEX
));
1371 if((iSrcOutput
!= INVALID_INDEX
) && (iDstInput
!= INVALID_INDEX
))
1373 // N.B. This hit test is far from optimal and it may be better to implement something along the lines of what is proposed here: https://msdn.microsoft.com/en-us/library/ms969920.aspx.
1374 Gdiplus::Bitmap
bitmap(static_cast<INT
>(link
.paintRect
.Width
), static_cast<INT
>(link
.paintRect
.Height
), PixelFormat4bppIndexed
);
1375 if(Gdiplus::Graphics
* pGraphics
= Gdiplus::Graphics::FromImage(&bitmap
))
1377 const Gdiplus::Color
backgroundColor(255, 0, 0, 0);
1378 pGraphics
->Clear(backgroundColor
);
1380 const Gdiplus::PointF
topLeft(link
.paintRect
.X
, link
.paintRect
.Y
);
1381 Gdiplus::PointF startPoint
= pSrcNode
->GetOutputLinkPoint(iSrcOutput
);
1382 Gdiplus::PointF endPoint
= pDstNode
->GetInputLinkPoint(iDstInput
);
1383 Gdiplus::PointF testPoint
= point
;
1384 startPoint
.X
-= topLeft
.X
;
1385 startPoint
.Y
-= topLeft
.Y
;
1386 endPoint
.X
-= topLeft
.X
;
1387 endPoint
.Y
-= topLeft
.Y
;
1388 testPoint
.X
-= topLeft
.X
;
1389 testPoint
.Y
-= topLeft
.Y
;
1391 m_pPainter
->PaintLink(*pGraphics
, startPoint
, endPoint
, pSrcNode
->GetOutputColor(iSrcOutput
), link
.selected
, 1.2f
);
1393 Gdiplus::Color testColor
= backgroundColor
;
1394 const Gdiplus::Status testStatus
= bitmap
.GetPixel(static_cast<INT
>(testPoint
.X
), static_cast<INT
>(testPoint
.Y
), &testColor
);
1398 if((testStatus
== Gdiplus::Ok
) && (testColor
.GetValue() != backgroundColor
.GetValue()))
1400 return iLink
- iBeginLink
;
1407 return INVALID_INDEX
;
1410 //////////////////////////////////////////////////////////////////////////
1411 void CGraphView::ClearLinkSelection()
1413 if(m_iSelectedLink
!= INVALID_INDEX
)
1415 m_links
[m_iSelectedLink
].selected
= false;
1416 m_iSelectedLink
= INVALID_INDEX
;
1420 //////////////////////////////////////////////////////////////////////////
1421 void CGraphView::SelectLink(size_t iLink
)
1423 CRY_ASSERT(iLink
!= INVALID_INDEX
);
1424 if((iLink
!= INVALID_INDEX
) && (iLink
!= m_iSelectedLink
))
1427 m_iSelectedLink
= iLink
;
1428 m_links
[m_iSelectedLink
].selected
= true;
1429 OnSelection(CGraphViewNodePtr(), &m_links
[m_iSelectedLink
]);
1430 __super::Invalidate(TRUE
);
1434 //////////////////////////////////////////////////////////////////////////
1435 size_t CGraphView::GetSelectedLink() const
1437 return m_iSelectedLink
;
1440 //////////////////////////////////////////////////////////////////////////
1441 void CGraphView::Clear()
1447 //////////////////////////////////////////////////////////////////////////
1448 Gdiplus::PointF
CGraphView::ClientToGraph(LONG x
, LONG y
) const
1450 return Gdiplus::PointF((x
+ m_scrollOffset
.X
) / m_zoom
, (y
+ m_scrollOffset
.Y
) / m_zoom
);
1452 //////////////////////////////////////////////////////////////////////////
1453 Gdiplus::PointF
CGraphView::ClientToGraph(Gdiplus::PointF pos
) const
1455 return Gdiplus::PointF((pos
.X
+ m_scrollOffset
.X
) / m_zoom
, (pos
.Y
+ m_scrollOffset
.Y
) / m_zoom
);
1458 //////////////////////////////////////////////////////////////////////////
1459 Gdiplus::PointF
CGraphView::ClientToGraphTransform(Gdiplus::PointF transform
) const
1461 return Gdiplus::PointF(transform
.X
/ m_zoom
, transform
.Y
/ m_zoom
);
1464 //////////////////////////////////////////////////////////////////////////
1465 Gdiplus::RectF
CGraphView::ClientToGraph(Gdiplus::RectF rect
) const
1467 return Gdiplus::RectF((rect
.X
+ m_scrollOffset
.X
) / m_zoom
, (rect
.Y
+ m_scrollOffset
.Y
) / m_zoom
, rect
.Width
/ m_zoom
, rect
.Height
/ m_zoom
);
1470 //////////////////////////////////////////////////////////////////////////
1471 Gdiplus::PointF
CGraphView::GraphToClient(Gdiplus::PointF pos
) const
1473 return Gdiplus::PointF((pos
.X
* m_zoom
) - m_scrollOffset
.X
, (pos
.Y
* m_zoom
) - m_scrollOffset
.Y
);
1476 //////////////////////////////////////////////////////////////////////////
1477 Gdiplus::PointF
CGraphView::GraphToClientTransform(Gdiplus::PointF transform
) const
1479 return Gdiplus::PointF(transform
.X
* m_zoom
, transform
.Y
* m_zoom
);
1482 //////////////////////////////////////////////////////////////////////////
1483 Gdiplus::RectF
CGraphView::GraphToClient(Gdiplus::RectF rect
) const
1485 return Gdiplus::RectF((rect
.X
* m_zoom
) - m_scrollOffset
.X
, (rect
.Y
* m_zoom
) - m_scrollOffset
.Y
, rect
.Width
* m_zoom
, rect
.Height
* m_zoom
);
1488 //////////////////////////////////////////////////////////////////////////
1489 void CGraphView::ScrollToFit(Gdiplus::RectF rect
)
1492 GetClientRect(&clientRect
);
1493 Gdiplus::RectF graphViewRect
= ClientToGraph(Gdiplus::RectF(static_cast<Gdiplus::REAL
>(clientRect
.left
), static_cast<Gdiplus::REAL
>(clientRect
.top
),
1494 static_cast<Gdiplus::REAL
>(clientRect
.Width()), static_cast<Gdiplus::REAL
>(clientRect
.Height())));
1495 const float dx
= graphViewRect
.GetRight() - rect
.GetRight();
1498 m_scrollOffset
.X
+= (-dx
+ DEFAULT_NODE_SPACING
) * m_zoom
;
1499 OnMove(m_scrollOffset
);
1501 // #SchematycTODO : Check left, top and bottom of view too?
1504 //////////////////////////////////////////////////////////////////////////
1505 void CGraphView::CenterPosition(Gdiplus::PointF point
)
1508 GetClientRect(&clientRect
);
1509 Gdiplus::RectF graphViewRect
= ClientToGraph(Gdiplus::RectF(static_cast<Gdiplus::REAL
>(clientRect
.left
), static_cast<Gdiplus::REAL
>(clientRect
.top
),
1510 static_cast<Gdiplus::REAL
>(clientRect
.Width()), static_cast<Gdiplus::REAL
>(clientRect
.Height())));
1511 const float dx
= graphViewRect
.X
- point
.X
+ (graphViewRect
.Width
/2);
1512 const float dy
= graphViewRect
.Y
- point
.Y
+ (graphViewRect
.Height
/2);
1514 m_scrollOffset
.X
+= (-dx
+ DEFAULT_NODE_SPACING
) * m_zoom
;
1515 m_scrollOffset
.Y
+= (-dy
+ DEFAULT_NODE_SPACING
) * m_zoom
;
1517 OnMove(m_scrollOffset
);
1520 //////////////////////////////////////////////////////////////////////////
1521 BOOL
CGraphView::PreTranslateMessage(MSG
* pMsg
)
1523 if((pMsg
->message
== WM_KEYDOWN
) && (GetFocus() == this))
1525 OnKeyDown((UINT
)pMsg
->wParam
, LOWORD(pMsg
->lParam
), HIWORD(pMsg
->lParam
));
1530 return __super::PreTranslateMessage(pMsg
);
1534 //////////////////////////////////////////////////////////////////////////
1535 void CGraphView::OnDraw(CDC
* pDC
) {}
1537 //////////////////////////////////////////////////////////////////////////
1538 void CGraphView::OnKillFocus(CWnd
* pNewWnd
)
1543 //////////////////////////////////////////////////////////////////////////
1544 void CGraphView::OnPaint()
1548 CPaintDC
paintDC(this);
1549 const CRect paintRect
= paintDC
.m_ps
.rcPaint
;
1550 Gdiplus::Bitmap
backBuffer(paintRect
.Width(), paintRect
.Height());
1551 if(Gdiplus::Graphics
* pBackBufferGraphics
= Gdiplus::Graphics::FromImage(&backBuffer
))
1553 pBackBufferGraphics
->TranslateTransform(-m_scrollOffset
.X
, -m_scrollOffset
.Y
);
1554 pBackBufferGraphics
->ScaleTransform(m_zoom
, m_zoom
);
1555 pBackBufferGraphics
->SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias
);
1556 pBackBufferGraphics
->SetTextRenderingHint(Gdiplus::TextRenderingHintAntiAliasGridFit
);
1557 pBackBufferGraphics
->Clear(m_pPainter
->GetBackgroundColor());
1561 m_pPainter
->PaintGrid(*pBackBufferGraphics
, m_grid
);
1563 for(TGraphViewNodePtrVector::iterator iNode
= m_nodes
.begin(), iEndNode
= m_nodes
.end(); iNode
!= iEndNode
; ++ iNode
)
1565 m_pPainter
->UpdateNodePaintRect(*pBackBufferGraphics
, m_grid
, *(*iNode
));
1568 for(TGraphViewLinkVector::iterator iLink
= m_links
.begin(), iEndLink
= m_links
.end(); iLink
!= iEndLink
; ++ iLink
)
1570 SGraphViewLink
& link
= *iLink
;
1571 const CGraphViewNodePtr pSrcNode
= link
.pSrcNode
.lock();
1572 const CGraphViewNodePtr pDstNode
= link
.pDstNode
.lock();
1573 if(pSrcNode
&& pDstNode
)
1575 const size_t srcOutputIdx
= pSrcNode
->FindOutput(link
.srcOutputName
);
1576 const size_t dstInputIdx
= pDstNode
->FindInput(link
.dstInputName
);
1577 if((srcOutputIdx
!= INVALID_INDEX
) && (dstInputIdx
!= INVALID_INDEX
))
1579 link
.paintRect
= m_pPainter
->PaintLink(*pBackBufferGraphics
, pSrcNode
->GetOutputLinkPoint(srcOutputIdx
), pDstNode
->GetInputLinkPoint(dstInputIdx
), pSrcNode
->GetOutputColor(srcOutputIdx
), link
.selected
, 1.0f
);
1584 if(m_pDragState
!= NULL
)
1586 m_pDragState
->Paint(*this, *pBackBufferGraphics
, *m_pPainter
);
1589 for(TGraphViewNodePtrVector::reverse_iterator iNode
= m_nodes
.rbegin(), iEndNode
= m_nodes
.rend(); iNode
!= iEndNode
; ++ iNode
)
1591 m_pPainter
->PaintNode(*pBackBufferGraphics
, m_grid
, *(*iNode
));
1595 Gdiplus::Graphics
mainGraphics(paintDC
.GetSafeHdc());
1596 mainGraphics
.DrawImage(&backBuffer
, paintRect
.left
, paintRect
.top
, paintRect
.Width(), paintRect
.Height());
1598 delete pBackBufferGraphics
;
1603 //////////////////////////////////////////////////////////////////////////
1604 void CGraphView::OnMouseMove(UINT nFlags
, CPoint point
)
1606 if(m_enabled
&& (GetFocus() == this))
1610 m_pDragState
->Update(*this, Gdiplus::PointF(static_cast<float>(point
.x
), static_cast<float>(point
.y
)));
1611 __super::Invalidate(TRUE
);
1613 else if((point
.x
!= m_prevMousePoint
.x
) || (point
.y
!= m_prevMousePoint
.y
))
1615 if((nFlags
& MK_LBUTTON
) != 0)
1617 OnLeftMouseButtonDrag(m_prevMousePoint
);
1619 else if((nFlags
& MK_RBUTTON
) != 0)
1621 OnRightMouseButtonDrag(m_prevMousePoint
);
1623 else if((nFlags
& MK_MBUTTON
) != 0)
1625 OnMiddleMouseButtonDrag(m_prevMousePoint
);
1629 m_prevMousePoint
= point
;
1630 __super::OnMouseMove(nFlags
, point
);
1633 //////////////////////////////////////////////////////////////////////////
1634 BOOL
CGraphView::OnMouseWheel(UINT nFlags
, short zDelta
, CPoint point
)
1636 // Store previous focus.
1638 GetCursorPos(&cursorPoint
);
1639 ScreenToClient(&cursorPoint
);
1640 const Gdiplus::PointF
prevClientFocus((Gdiplus::REAL
)cursorPoint
.x
, (Gdiplus::REAL
)cursorPoint
.y
);
1641 const Gdiplus::PointF prevGraphFocus
= ClientToGraph(prevClientFocus
);
1643 m_zoom
= clamp_tpl(m_zoom
+ (DELTA_ZOOM
* zDelta
), MIN_ZOOM
, MAX_ZOOM
);
1644 // Scroll to previous focus point.
1645 const Gdiplus::PointF newClientFocus
= GraphToClient(prevGraphFocus
);
1646 m_scrollOffset
.X
+= newClientFocus
.X
- prevClientFocus
.X
;
1647 m_scrollOffset
.Y
+= newClientFocus
.Y
- prevClientFocus
.Y
;
1648 OnMove(m_scrollOffset
);
1650 __super::Invalidate(TRUE
);
1654 //////////////////////////////////////////////////////////////////////////
1655 void CGraphView::OnLButtonDown(UINT nFlags
, CPoint point
)
1660 //////////////////////////////////////////////////////////////////////////
1661 void CGraphView::OnLButtonUp(UINT nFlags
, CPoint point
)
1663 if(m_pDragState
!= NULL
)
1670 const Gdiplus::PointF graphPos
= ClientToGraph(point
.x
, point
.y
);
1671 CGraphViewNodePtr pNode
= FindNode(graphPos
);
1678 const size_t iLink
= FindLink(graphPos
);
1679 if(iLink
!= INVALID_INDEX
)
1687 //////////////////////////////////////////////////////////////////////////
1688 afx_msg
void CGraphView::OnRButtonDown(UINT nFlags
, CPoint point
)
1693 //////////////////////////////////////////////////////////////////////////
1694 afx_msg
void CGraphView::OnRButtonUp(UINT nFlags
, CPoint point
)
1696 if(m_pDragState
!= NULL
)
1703 popupMenu
.CreatePopupMenu();
1704 const Gdiplus::PointF graphPos
= ClientToGraph(point
.x
, point
.y
);
1705 CGraphViewNodePtr pNode
= FindNode(graphPos
);
1706 const size_t iNodeInput
= pNode
? pNode
->FindInput(graphPos
) : INVALID_INDEX
;
1707 const size_t iNodeOutput
= pNode
? pNode
->FindOutput(graphPos
) : INVALID_INDEX
;
1708 ClientToScreen(&point
);
1709 GetPopupMenuItems(popupMenu
, pNode
, iNodeInput
, iNodeOutput
, point
);
1710 const int popupMenuItemCount
= popupMenu
.GetMenuItemCount();
1711 if(popupMenuItemCount
== 1)
1713 OnPopupMenuResult(popupMenu
.GetMenuItemID(0), pNode
, iNodeInput
, iNodeOutput
, point
);
1715 else if(popupMenuItemCount
> 0)
1717 int popupMenuItem
= popupMenu
.TrackPopupMenuEx(TPM_RETURNCMD
, point
.x
, point
.y
, this, NULL
);
1718 OnPopupMenuResult(popupMenuItem
, pNode
, iNodeInput
, iNodeOutput
, point
);
1723 //////////////////////////////////////////////////////////////////////////
1724 afx_msg
void CGraphView::OnMButtonDown(UINT nFlags
, CPoint point
)
1729 //////////////////////////////////////////////////////////////////////////
1730 afx_msg
void CGraphView::OnMButtonUp(UINT nFlags
, CPoint point
)
1732 if(m_pDragState
!= NULL
)
1738 //////////////////////////////////////////////////////////////////////////
1739 void CGraphView::OnKeyDown(UINT nChar
, UINT nRepCnt
, UINT nFlags
)
1741 if((GetFocus() == this) && m_enabled
&& (nRepCnt
== 1))
1743 const bool bCtrlDown
= (GetAsyncKeyState(VK_CONTROL
) & 0x8000) != 0;
1744 const bool bShiftDown
= (GetAsyncKeyState(VK_SHIFT
) & 0x8000) != 0;
1751 FormatSelectedNodes();
1756 else if (bShiftDown
)
1767 OnDownArrowPressed();
1776 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1777 // Part of temporary fix for slow graphs with lots of links, until we can optimize the intersection tests properly.
1783 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1796 OnDownArrowPressed();
1800 if((nChar
>= 'A') && (nChar
<= 'Z'))
1802 const char defaultFilter
[2] = { static_cast<char>(nChar
), '\0' };
1803 if(m_selectedNodes
.size() == 1)
1805 CGraphViewNodePtr pSelectedNode
= m_selectedNodes
[0];
1806 Gdiplus::RectF paintRect
= pSelectedNode
->GetPaintRect();
1807 Gdiplus::PointF nextPos
= GraphToClient(Gdiplus::PointF(paintRect
.GetRight() + DEFAULT_NODE_SPACING
, paintRect
.GetTop()));
1808 CPoint
screenPoint(static_cast<int>(nextPos
.X
), static_cast<int>(nextPos
.Y
));
1809 ClientToScreen(&screenPoint
);
1810 DoQuickSearch(defaultFilter
, pSelectedNode
, 0, &screenPoint
); // Rather than passing zero it would be nice to pass the index of the first/selected execution output.
1814 DoQuickSearch(defaultFilter
);
1820 __super::OnKeyDown(nChar
, nRepCnt
, nFlags
);
1823 //////////////////////////////////////////////////////////////////////////
1824 void CGraphView::OnKeyUp(UINT nChar
, UINT nRepCnt
, UINT nFlags
)
1826 if((GetFocus() == this) && m_enabled
&& (nRepCnt
== 1))
1830 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1831 // Part of temporary fix for slow graphs with lots of links, until we can optimize the intersection tests properly.
1834 SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW
));
1837 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1840 __super::OnKeyUp(nChar
, nRepCnt
, nFlags
);
1843 //////////////////////////////////////////////////////////////////////////
1844 BOOL
CGraphView::OnSetCursor(CWnd
* pWnd
, UINT nHitTest
, UINT message
)
1850 return __super::OnSetCursor(pWnd
, nHitTest
, message
);
1853 //////////////////////////////////////////////////////////////////////////
1854 void CGraphView::OnMove(Gdiplus::PointF scrollOffset
) {}
1856 //////////////////////////////////////////////////////////////////////////
1857 void CGraphView::OnSelection(const CGraphViewNodePtr
& pSelectedNode
, SGraphViewLink
* pSelectedLink
) {}
1859 //////////////////////////////////////////////////////////////////////////
1860 void CGraphView::OnLinkModify(const SGraphViewLink
& link
) {}
1862 //////////////////////////////////////////////////////////////////////////
1863 void CGraphView::OnNodeRemoved(const CGraphViewNode
& node
) {}
1865 //////////////////////////////////////////////////////////////////////////
1866 bool CGraphView::CanCreateLink(const CGraphViewNode
& srcNode
, const char* srcOutputName
, const CGraphViewNode
& dstNode
, const char* dstInputName
) const
1871 //////////////////////////////////////////////////////////////////////////
1872 void CGraphView::OnLinkCreated(const SGraphViewLink
& link
) {}
1874 //////////////////////////////////////////////////////////////////////////
1875 void CGraphView::OnLinkRemoved(const SGraphViewLink
& link
) {}
1877 //////////////////////////////////////////////////////////////////////////
1878 DROPEFFECT
CGraphView::OnDragOver(CWnd
* pWnd
, COleDataObject
* pDataObject
, DWORD dwKeyState
, CPoint point
)
1880 return DROPEFFECT_NONE
;
1883 //////////////////////////////////////////////////////////////////////////
1884 BOOL
CGraphView::OnDrop(CWnd
* pWnd
, COleDataObject
* pDataObject
, DROPEFFECT dropEffect
, CPoint point
)
1889 //////////////////////////////////////////////////////////////////////////
1890 void CGraphView::GetPopupMenuItems(CMenu
& popupMenu
, const CGraphViewNodePtr
& pNode
, size_t iNodeInput
, size_t iNodeOutput
, CPoint point
)
1894 popupMenu
.AppendMenu(MF_STRING
, EPopupMenuItem::REMOVE_NODE
, "Remove Node");
1898 popupMenu
.AppendMenu(MF_STRING
, EPopupMenuItem::QUICK_SEARCH
, "Quick Search");
1902 //////////////////////////////////////////////////////////////////////////
1903 void CGraphView::OnPopupMenuResult(BOOL popupMenuItem
, const CGraphViewNodePtr
& pNode
, size_t iNodeInput
, size_t iNodeOutput
, CPoint point
)
1905 switch(popupMenuItem
)
1907 case EPopupMenuItem::QUICK_SEARCH
:
1912 case EPopupMenuItem::REMOVE_NODE
:
1914 CRY_ASSERT(pNode
!= NULL
);
1921 //////////////////////////////////////////////////////////////////////////
1922 const IQuickSearchOptions
* CGraphView::GetQuickSearchOptions(CPoint point
, const CGraphViewNodePtr
& pNode
, size_t iNodeOutput
)
1927 //////////////////////////////////////////////////////////////////////////
1928 void CGraphView::OnQuickSearchResult(CPoint point
, const CGraphViewNodePtr
& pNode
, size_t iNodeOutput
, size_t iSelectedOption
) {}
1930 //////////////////////////////////////////////////////////////////////////
1931 bool CGraphView::ShouldUpdate() const
1933 return GetFocus() == this;
1936 //////////////////////////////////////////////////////////////////////////
1937 void CGraphView::OnUpdate() {}
1939 //////////////////////////////////////////////////////////////////////////
1940 CGraphView::CScrollDragState::CScrollDragState(Gdiplus::PointF pos
)
1944 //////////////////////////////////////////////////////////////////////////
1945 void CGraphView::CScrollDragState::Update(CGraphView
& graphView
, Gdiplus::PointF pos
)
1947 if((pos
.X
!= m_prevPos
.X
) || (pos
.Y
!= m_prevPos
.Y
))
1949 graphView
.Scroll(pos
- m_prevPos
);
1954 //////////////////////////////////////////////////////////////////////////
1955 void CGraphView::CScrollDragState::Paint(CGraphView
& graphView
, Gdiplus::Graphics
& graphics
, IGraphViewPainter
& painter
) {}
1957 //////////////////////////////////////////////////////////////////////////
1958 void CGraphView::CScrollDragState::Exit(CGraphView
& graphView
) {}
1960 //////////////////////////////////////////////////////////////////////////
1961 CGraphView::CMoveDragState::CMoveDragState(Gdiplus::PointF pos
)
1965 //////////////////////////////////////////////////////////////////////////
1966 void CGraphView::CMoveDragState::Update(CGraphView
& graphView
, Gdiplus::PointF pos
)
1968 graphView
.MoveSelectedNodes(graphView
.ClientToGraphTransform(pos
- m_prevPos
), false);
1972 //////////////////////////////////////////////////////////////////////////
1973 void CGraphView::CMoveDragState::Paint(CGraphView
& graphView
, Gdiplus::Graphics
& graphics
, IGraphViewPainter
& painter
) {}
1975 //////////////////////////////////////////////////////////////////////////
1976 void CGraphView::CMoveDragState::Exit(CGraphView
& graphView
)
1978 graphView
.MoveSelectedNodes(Gdiplus::PointF(), true);
1981 //////////////////////////////////////////////////////////////////////////
1982 CGraphView::CLinkDragState::CLinkDragState(const CGraphViewNodeWeakPtr
& pSrcNode
, const char* srcOutputName
, Gdiplus::PointF start
, Gdiplus::PointF end
, Gdiplus::Color color
, bool newLink
)
1983 : m_pSrcNode(pSrcNode
)
1984 , m_srcOutputName(srcOutputName
)
1988 , m_newLink(newLink
)
1991 //////////////////////////////////////////////////////////////////////////
1992 void CGraphView::CLinkDragState::Update(CGraphView
& graphView
, Gdiplus::PointF pos
)
1997 //////////////////////////////////////////////////////////////////////////
1998 void CGraphView::CLinkDragState::Paint(CGraphView
& graphView
, Gdiplus::Graphics
& graphics
, IGraphViewPainter
& painter
)
2000 if(CGraphViewNodePtr pSrcNode
= m_pSrcNode
.lock())
2002 painter
.PaintLink(graphics
, m_start
, graphView
.ClientToGraph(m_end
), m_color
, true, 1.0f
);
2006 //////////////////////////////////////////////////////////////////////////
2007 void CGraphView::CLinkDragState::Exit(CGraphView
& graphView
)
2009 const Gdiplus::PointF graphPos
= graphView
.ClientToGraph(m_end
);
2010 if(CGraphViewNodePtr pDstNode
= graphView
.FindNode(graphPos
))
2012 const size_t iDstInput
= pDstNode
->FindInput(graphPos
);
2013 if(iDstInput
!= INVALID_INDEX
)
2015 graphView
.CreateLink(m_pSrcNode
.lock(), m_srcOutputName
.c_str(), pDstNode
, pDstNode
->GetInputName(iDstInput
));
2020 CGraphViewNodePtr pSrcNode
= m_pSrcNode
.lock();
2021 CRY_ASSERT(pSrcNode
);
2024 graphView
.DoQuickSearch(NULL
, pSrcNode
, pSrcNode
->FindOutput(m_srcOutputName
.c_str()));
2029 //////////////////////////////////////////////////////////////////////////
2030 CGraphView::CSelectDragState::CSelectDragState(Gdiplus::PointF pos
)
2035 //////////////////////////////////////////////////////////////////////////
2036 void CGraphView::CSelectDragState::Update(CGraphView
& graphView
, Gdiplus::PointF pos
)
2039 graphView
.SelectNodesInRect(graphView
.ClientToGraph(GetSelectionRect()));
2042 //////////////////////////////////////////////////////////////////////////
2043 void CGraphView::CSelectDragState::Paint(CGraphView
& graphView
, Gdiplus::Graphics
& graphics
, IGraphViewPainter
& painter
)
2045 painter
.PaintSelectionRect(graphics
, graphView
.ClientToGraph(GetSelectionRect()));
2048 //////////////////////////////////////////////////////////////////////////
2049 void CGraphView::CSelectDragState::Exit(CGraphView
& graphView
) {}
2051 //////////////////////////////////////////////////////////////////////////
2052 Gdiplus::RectF
CGraphView::CSelectDragState::GetSelectionRect() const
2054 Gdiplus::RectF selectionRect
;
2055 selectionRect
.X
= std::min(m_startPos
.X
, m_endPos
.X
);
2056 selectionRect
.Y
= std::min(m_startPos
.Y
, m_endPos
.Y
);
2057 selectionRect
.Width
= fabs_tpl(m_endPos
.X
- m_startPos
.X
);
2058 selectionRect
.Height
= fabs_tpl(m_endPos
.Y
- m_startPos
.Y
);
2059 return selectionRect
;
2062 //////////////////////////////////////////////////////////////////////////
2063 CGraphView::CDropTarget::CDropTarget(CGraphView
& graphView
)
2064 : m_graphView(graphView
)
2067 //////////////////////////////////////////////////////////////////////////
2068 DROPEFFECT
CGraphView::CDropTarget::OnDragOver(CWnd
* pWnd
, COleDataObject
* pDataObject
, DWORD dwKeyState
, CPoint point
)
2070 return m_graphView
.OnDragOver(pWnd
, pDataObject
, dwKeyState
, point
);
2073 //////////////////////////////////////////////////////////////////////////
2074 BOOL
CGraphView::CDropTarget::OnDrop(CWnd
* pWnd
, COleDataObject
* pDataObject
, DROPEFFECT dropEffect
, CPoint point
)
2076 return m_graphView
.OnDrop(pWnd
, pDataObject
, dropEffect
, point
);
2079 //////////////////////////////////////////////////////////////////////////
2080 void CGraphView::OnTimer(UINT_PTR nIDEvent
)
2082 if(nIDEvent
== UPDATE_TIMER_EVENT_ID
)
2087 //if(!m_selectedNodes.empty() || (m_iSelectedLink != INVALID_INDEX)) // Removed as part of temporary fix for slow graphs with lots of links, until we can optimize the intersection tests properly.
2089 __super::Invalidate(TRUE
);
2095 //////////////////////////////////////////////////////////////////////////
2096 void CGraphView::OnLeftMouseButtonDrag(CPoint point
)
2098 const Gdiplus::PointF
clientPos(static_cast<float>(point
.x
), static_cast<float>(point
.y
));
2099 const Gdiplus::PointF graphPos
= ClientToGraph(clientPos
);
2100 if(CGraphViewNodePtr pNode
= FindNode(graphPos
))
2102 if(m_selectedNodes
.size() < 2)
2107 const size_t iSrcOutput
= pNode
->FindOutput(graphPos
);
2108 if(iSrcOutput
!= INVALID_INDEX
)
2110 m_pDragState
= IGraphViewDragStatePtr(new CLinkDragState(pNode
, pNode
->GetOutputName(iSrcOutput
), pNode
->GetOutputLinkPoint(iSrcOutput
), clientPos
, pNode
->GetOutputColor(iSrcOutput
), true));
2114 const size_t iDstInput
= pNode
->FindInput(graphPos
);
2115 if(iDstInput
!= INVALID_INDEX
)
2117 const char* dstInputName
= pNode
->GetInputName(iDstInput
);
2119 FindLinks(pNode
, dstInputName
, links
);
2120 if(links
.size() == 1)
2122 const size_t iLink
= links
.front();
2123 const SGraphViewLink
& link
= m_links
[iLink
];
2124 if(CGraphViewNodePtr pSrcNode
= link
.pSrcNode
.lock())
2126 const size_t iLinkSrcOutput
= pSrcNode
->FindOutput(link
.srcOutputName
);
2127 CRY_ASSERT(iLinkSrcOutput
!= INVALID_INDEX
);
2128 if(iLinkSrcOutput
!= INVALID_INDEX
)
2130 m_pDragState
= IGraphViewDragStatePtr(new CLinkDragState(link
.pSrcNode
, link
.srcOutputName
, pSrcNode
->GetOutputLinkPoint(iLinkSrcOutput
), clientPos
, pSrcNode
->GetOutputColor(iLinkSrcOutput
), false));
2138 const Gdiplus::RectF nodePaintRect
= pNode
->GetPaintRect();
2139 m_pDragState
= IGraphViewDragStatePtr(new CMoveDragState(clientPos
));
2145 m_pDragState
= IGraphViewDragStatePtr(new CSelectDragState(clientPos
));
2149 //////////////////////////////////////////////////////////////////////////
2150 void CGraphView::OnRightMouseButtonDrag(CPoint point
)
2152 m_pDragState
= IGraphViewDragStatePtr(new CScrollDragState(Gdiplus::PointF(static_cast<float>(point
.x
), static_cast<float>(point
.y
))));
2155 //////////////////////////////////////////////////////////////////////////
2156 void CGraphView::OnMiddleMouseButtonDrag(CPoint point
)
2158 m_pDragState
= IGraphViewDragStatePtr(new CScrollDragState(Gdiplus::PointF(static_cast<float>(point
.x
), static_cast<float>(point
.y
))));
2161 //////////////////////////////////////////////////////////////////////////
2162 void CGraphView::Scroll(Gdiplus::PointF delta
)
2164 m_scrollOffset
.X
= clamp_tpl(m_scrollOffset
.X
- delta
.X
, m_grid
.bounds
.X
, m_grid
.bounds
.GetRight());
2165 m_scrollOffset
.Y
= clamp_tpl(m_scrollOffset
.Y
- delta
.Y
, m_grid
.bounds
.Y
, m_grid
.bounds
.GetBottom());
2166 OnMove(m_scrollOffset
);
2169 //////////////////////////////////////////////////////////////////////////
2170 void CGraphView::ExitDragState()
2172 if(m_pDragState
!= NULL
)
2174 IGraphViewDragStatePtr pDragState
= m_pDragState
;
2175 m_pDragState
.reset();
2176 pDragState
->Exit(*this);
2177 __super::Invalidate(TRUE
);
2181 //////////////////////////////////////////////////////////////////////////
2182 void CGraphView::OnDelete()
2184 if(m_selectedNodes
.empty() == false)
2186 // TODO: Switch to CryMessageBox when porting to Qt, we cannot use it right now due to a MFC / Qt conflict.
2187 if (::MessageBoxA(CView::GetSafeHwnd(), "Remove selected node(s)?", "Remove Node(s)", 1) == IDOK
)
2189 TGraphViewNodePtrVector removeNodes
= m_selectedNodes
;
2190 for(TGraphViewNodePtrVector::iterator iRemoveNode
= removeNodes
.begin(), iEndRemoveNode
= removeNodes
.end(); iRemoveNode
!= iEndRemoveNode
; ++ iRemoveNode
)
2192 RemoveNode(*iRemoveNode
);
2196 else if(m_iSelectedLink
)
2198 // TODO: Switch to CryMessageBox when porting to Qt, we cannot use it right now due to a MFC / Qt conflict.
2199 if (::MessageBoxA(CView::GetSafeHwnd(), "Remove selected link?", "Remove Link", 1) == IDOK
)
2201 RemoveLink(m_iSelectedLink
);
2206 //////////////////////////////////////////////////////////////////////////
2207 bool CGraphView::UpdateCursor()
2212 GetCursorPos(&cursorPoint
);
2213 ScreenToClient(&cursorPoint
);
2214 const Gdiplus::PointF graphPos
= ClientToGraph(Gdiplus::PointF(static_cast<float>(cursorPoint
.x
), static_cast<float>(cursorPoint
.y
)));
2215 if(FindNode(graphPos
) || (FindLink(graphPos
) != INVALID_INDEX
))
2217 SetCursor(AfxGetApp()->LoadStandardCursor(IDC_HAND
));
2224 //////////////////////////////////////////////////////////////////////////
2225 void CGraphView::FormatSelectedNodes()
2227 if(GetSchematyc()->IsExperimentalFeatureEnabled("GraphNodeFormat"))
2229 CGraphViewFormatter
formatter(*this, m_settings
.format
);
2230 formatter
.FormatNodes(m_selectedNodes
);