1
2
3
4
5
6 using System;
7 using System.Collections;
8
9 namespace System.Windows
10 {
11 public nothrow Color DefaultPathViewTextColor()
12 {
13 return Color.Black();
14 }
15
16 public nothrow Color DefaultPathViewTickColor()
17 {
18 return Color.Black();
19 }
20
21 public nothrow Color DefaultPathViewMouseOverColor()
22 {
23 return Color(230u, 243u, 255u);
24 }
25
26 public nothrow Color DefaultPathViewMouseClickColor()
27 {
28 return Color(204u, 232u, 255u);
29 }
30
31 public nothrow Padding DefaultPathViewPathComponentPadding()
32 {
33 return Padding(2, 6, 2, 6);
34 }
35
36 public nothrow Padding DefaultPathViewTickPadding()
37 {
38 return Padding(4, 6, 4, 6);
39 }
40
41 public nothrow string DefaultPathViewFontFamilyName()
42 {
43 return "Segoe UI";
44 }
45
46 public nothrow float DefaultPathViewFontSize()
47 {
48 return 9.0f;
49 }
50
51 public nothrow float DefaultPathViewTickSizePercent()
52 {
53 return 40.0f;
54 }
55
56 public class PathComponentEventArgs
57 {
58 public nothrow PathComponentEventArgs(PathComponent* pathComponent_) : pathComponent(pathComponent_)
59 {
60 }
61 public PathComponent* pathComponent;
62 }
63
64 public class delegate void PathComponentSelectedEventHandler(PathComponentEventArgs& args);
65
66 public nothrow ControlCreateParams& PathViewControlCreateParams(ControlCreateParams& controlCreateParams)
67 {
68 return controlCreateParams.SetWindowClassName("System.Windows.PathView").
69 SetWindowClassBackgroundColor(SystemColor.COLOR_WINDOW).
70 SetBackgroundColor(Color.White());
71 }
72
73 public class PathViewCreateParams
74 {
75 public nothrow PathViewCreateParams(ControlCreateParams& controlCreateParams_) :
76 controlCreateParams(controlCreateParams_),
77 fontFamilyName(DefaultPathViewFontFamilyName()),
78 fontSize(DefaultPathViewFontSize()),
79 textColor(DefaultPathViewTextColor()),
80 tickSizePercent(DefaultPathViewTickSizePercent()),
81 tickColor(DefaultPathViewTickColor()),
82 pathComponentPadding(DefaultPathViewPathComponentPadding()),
83 tickPadding(DefaultPathViewTickPadding()),
84 mouseOverColor(DefaultPathViewMouseOverColor()),
85 mouseClickColor(DefaultPathViewMouseClickColor())
86 {
87 }
88 public nothrow PathViewCreateParams& Defaults()
89 {
90 return *this;
91 }
92 public nothrow PathViewCreateParams& SetFontFamilyName(const string& fontFamilyName_)
93 {
94 fontFamilyName = fontFamilyName_;
95 return *this;
96 }
97 public nothrow PathViewCreateParams& SetFontSize(float fontSize_)
98 {
99 fontSize = fontSize_;
100 return *this;
101 }
102 public nothrow PathViewCreateParams& SetTextColor(const Color& textColor_)
103 {
104 textColor = textColor_;
105 return *this;
106 }
107 public nothrow PathViewCreateParams& SetTickSizePercent(float tickSizePercent_)
108 {
109 tickSizePercent = tickSizePercent_;
110 return *this;
111 }
112 public nothrow PathViewCreateParams& SetTickColor(const Color& tickColor_)
113 {
114 tickColor = tickColor_;
115 return *this;
116 }
117 public nothrow PathViewCreateParams& SetPathComponentPadding(const Padding& pathComponentPadding_)
118 {
119 pathComponentPadding = pathComponentPadding_;
120 return *this;
121 }
122 public nothrow PathViewCreateParams& SetTickPadding(const Padding& tickPadding_)
123 {
124 tickPadding = tickPadding_;
125 return *this;
126 }
127 public nothrow PathViewCreateParams& SetMouseOverColor(const Color& mouseOverColor_)
128 {
129 mouseOverColor = mouseOverColor_;
130 return *this;
131 }
132 public nothrow PathViewCreateParams& SetMouseClickColor(const Color& mouseClickColor_)
133 {
134 mouseClickColor = mouseClickColor_;
135 return *this;
136 }
137 public ControlCreateParams& controlCreateParams;
138 public string fontFamilyName;
139 public float fontSize;
140 public Color textColor;
141 public float tickSizePercent;
142 public Color tickColor;
143 public Padding pathComponentPadding;
144 public Padding tickPadding;
145 public Color mouseOverColor;
146 public Color mouseClickColor;
147 }
148
149 public class PathView : Control
150 {
151 public nothrow PathView(PathViewCreateParams& createParams) :
152 base(createParams.controlCreateParams),
153 pathComponents(this),
154 tickSizePercent(createParams.tickSizePercent),
155 textBrush(createParams.textColor),
156 tickBrush(createParams.tickColor),
157 mouseOverBrush(createParams.mouseOverColor),
158 mouseClickBrush(createParams.mouseClickColor),
159 pathComponentPadding(createParams.pathComponentPadding),
160 tickPadding(createParams.tickPadding),
161 textHeight(0),
162 tickHeight(0),
163 tickWidth(0),
164 sqrt3per2(0),
165 maxWidth(0),
166 mouseDownComponent(null),
167 mouseOverComponent(null)
168 {
169 SetFont(Font(FontFamily(createParams.fontFamilyName), createParams.fontSize));
170 sqrt3per2 = cast<float>(Sqrt(3.0) / 2);
171 SetSize(Size(10, 10));
172 Invalidate();
173 }
174 public nothrow void Clear()
175 {
176 mouseDownComponent = null;
177 mouseOverComponent = null;
178 if (!pathComponents.IsEmpty())
179 {
180 while (!pathComponents.IsEmpty())
181 {
182 pathComponents.RemoveChild(pathComponents.FirstChild());
183 }
184 Invalidate();
185 }
186 }
187 public nothrow void SetMaxWidth(int maxWidth_)
188 {
189 if (maxWidth != maxWidth_)
190 {
191 maxWidth = maxWidth_;
192 Size sz = GetSize();
193 sz.w = maxWidth;
194 SetSize(sz);
195 Invalidate();
196 }
197 }
198 public nothrow void AddPathComponent(const string& pathComponentName, void* data)
199 {
200 PathComponent* pathComponent = new PathComponent(this, pathComponentName, data);
201 pathComponents.AddChild(pathComponent);
202 Invalidate();
203 }
204 public nothrow void PushPathComponent(const string& pathComponentName, void* data)
205 {
206 PathComponent* pathComponent = new PathComponent(this, pathComponentName, data);
207 if (!pathComponents.IsEmpty())
208 {
209 pathComponents.InsertBefore(pathComponent, pathComponents.FirstChild());
210 }
211 else
212 {
213 pathComponents.AddChild(pathComponent);
214 }
215 Invalidate();
216 }
217 public inline nothrow const Padding& GetPathComponentPadding() const
218 {
219 return pathComponentPadding;
220 }
221 public inline nothrow const Brush& GetTextBrush() const
222 {
223 return textBrush;
224 }
225 public inline nothrow const Brush& MouseOverBrush() const
226 {
227 return mouseOverBrush;
228 }
229 public inline nothrow const Brush& MouseClickBrush() const
230 {
231 return mouseClickBrush;
232 }
233 public nothrow void SetTextHeight(float textHeight_)
234 {
235 textHeight = Max(textHeight, textHeight_);
236 }
237 public nothrow Event<PathComponentSelectedEventHandler, PathComponentEventArgs>& PathComponentSelectedEvent()
238 {
239 return pathComponentSelectedEvent;
240 }
241 protected override void OnPaint(PaintEventArgs& args)
242 {
243 Measure(args.graphics);
244 Component* startChild = null;
245 if (maxWidth > 0)
246 {
247 PointF origin(0, 0);
248 int width = 0;
249 Component* child = pathComponents.LastChild();
250 bool first = true;
251 while (child != null)
252 {
253 if (child is PathComponent*)
254 {
255 if (first)
256 {
257 first = false;
258 }
259 else
260 {
261 width = cast<int>(width + tickWidth + tickPadding.Horizontal());
262 }
263 PathComponent* pathComponent = cast<PathComponent*>(child);
264 Size sz = pathComponent->GetSize();
265 if (width + sz.w > maxWidth)
266 {
267 break;
268 }
269 width = width + sz.w;
270 startChild = child;
271 }
272 child = child->PrevSibling();
273 }
274 }
275 else
276 {
277 startChild = pathComponents.FirstChild();
278 }
279 args.graphics.Clear(BackgroundColor());
280 startChild = pathComponents.FirstChild();
281 PointF origin(0, 0);
282 Component* child = startChild;
283 bool first = true;
284 while (child != null)
285 {
286 if (child is PathComponent*)
287 {
288 if (first)
289 {
290 first = false;
291 }
292 else
293 {
294 DrawTick(args.graphics, origin);
295 }
296 PathComponent* pathComponent = cast<PathComponent*>(child);
297 pathComponent->SetLocation(Point(cast<int>(origin.x + 0.5f), cast<int>(origin.y + 0.5f)));
298 pathComponent->Draw(args.graphics);
299 Size sz = pathComponent->GetSize();
300 origin.x = origin.x + sz.w;
301 }
302 child = child->NextSibling();
303 }
304 }
305 protected override void OnMouseDown(MouseEventArgs& args)
306 {
307 PathComponent* pathComponent = PathComponentAt(args.location);
308 if (pathComponent != null)
309 {
310 mouseDownComponent = pathComponent;
311 mouseDownComponent->SetState(PathComponent.State.mouseClick);
312 }
313 else
314 {
315 mouseDownComponent = null;
316 }
317 }
318 protected override void OnMouseUp(MouseEventArgs& args)
319 {
320 PathComponent* pathComponent = PathComponentAt(args.location);
321 if (pathComponent != null)
322 {
323 pathComponent->SetState(PathComponent.State.idle);
324 if (pathComponent == mouseDownComponent)
325 {
326 PathComponentEventArgs args(pathComponent);
327 OnPathComponentSelected(args);
328 }
329 }
330 mouseDownComponent = null;
331 }
332 protected override void OnMouseMove(MouseEventArgs& args)
333 {
334 if (mouseOverComponent != null)
335 {
336 mouseOverComponent->SetState(PathComponent.State.idle);
337 mouseOverComponent = null;
338 }
339 PathComponent* pathComponent = PathComponentAt(args.location);
340 if (pathComponent != null)
341 {
342 pathComponent->SetState(PathComponent.State.mouseOver);
343 mouseOverComponent = pathComponent;
344 }
345 }
346 protected override void OnMouseEnter()
347 {
348 mouseDownComponent = null;
349 mouseOverComponent = null;
350 }
351 protected override void OnMouseLeave()
352 {
353 if (mouseDownComponent != null)
354 {
355 mouseDownComponent->SetState(PathComponent.State.idle);
356 mouseDownComponent = null;
357 }
358 if (mouseOverComponent != null)
359 {
360 mouseOverComponent->SetState(PathComponent.State.idle);
361 mouseOverComponent = null;
362 }
363 }
364 protected virtual void OnPathComponentSelected(PathComponentEventArgs& args)
365 {
366 pathComponentSelectedEvent.Fire(args);
367 }
368 private nothrow PathComponent* PathComponentAt(const Point& location)
369 {
370 Component* child = pathComponents.FirstChild();
371 while (child != null)
372 {
373 if (child is PathComponent*)
374 {
375 PathComponent* pathComponent = cast<PathComponent*>(child);
376 Rect r(pathComponent->Location(), pathComponent->GetSize());
377 if (r.Contains(location))
378 {
379 return pathComponent;
380 }
381 }
382 child = child->NextSibling();
383 }
384 return null;
385 }
386 private void Measure(Graphics& graphics)
387 {
388 Component* child = pathComponents.FirstChild();
389 while (child != null)
390 {
391 if (child is PathComponent*)
392 {
393 PathComponent* pathComponent = cast<PathComponent*>(child);
394 pathComponent->Measure(graphics);
395 }
396 child = child->NextSibling();
397 }
398 if (textHeight > 0)
399 {
400 tickHeight = (tickSizePercent / 100.0f) * textHeight;
401 tickWidth = sqrt3per2 * tickHeight;
402 }
403 }
404 private void DrawTick(Graphics& graphics, PointF& origin)
405 {
406 SmoothingMode prevSmoothingMode = graphics.GetSmoothingModeChecked();
407 graphics.SetSmoothingModeChecked(SmoothingMode.highQuality);
408 PointF p0(tickPadding.left + origin.x, tickPadding.top + origin.y + (textHeight - tickHeight) / 2.0f);
409 PointF p1(p0.x, p0.y + tickHeight);
410 PointF p2(p0.x + tickWidth, p0.y + tickHeight / 2.0f);
411 List<PointF> points;
412 points.Add(p0);
413 points.Add(p1);
414 points.Add(p2);
415 graphics.FillPolygonChecked(tickBrush, 3, points.CBegin().Ptr());
416 origin.x = origin.x + tickWidth + tickPadding.Horizontal();
417 graphics.SetSmoothingModeChecked(prevSmoothingMode);
418 }
419 private Container pathComponents;
420 private float tickSizePercent;
421 private SolidBrush textBrush;
422 private SolidBrush tickBrush;
423 private SolidBrush mouseOverBrush;
424 private SolidBrush mouseClickBrush;
425 private Padding pathComponentPadding;
426 private Padding tickPadding;
427 private float textHeight;
428 private float tickHeight;
429 private float tickWidth;
430 private float sqrt3per2;
431 private int maxWidth;
432 private Event<PathComponentSelectedEventHandler, PathComponentEventArgs> pathComponentSelectedEvent;
433 private PathComponent* mouseDownComponent;
434 private PathComponent* mouseOverComponent;
435 }
436
437 public class PathComponent : Component
438 {
439 public enum State : int
440 {
441 idle, mouseOver, mouseClick
442 }
443 public nothrow PathComponent(PathView* pathView_, const string& name_, void* data_) :
444 pathView(pathView_), name(name_), data(data_), state(State.idle), location(), size()
445 {
446 }
447 public inline nothrow State GetState() const
448 {
449 return state;
450 }
451 public nothrow void SetState(State state_)
452 {
453 if (state != state_)
454 {
455 state = state_;
456 pathView->Invalidate();
457 }
458 }
459 public inline nothrow void* Data() const
460 {
461 return data;
462 }
463 public void Draw(Graphics& graphics)
464 {
465 if (state == State.mouseOver)
466 {
467 Rect rect(location, size);
468 graphics.FillRectangleChecked(pathView->MouseOverBrush(), rect);
469 }
470 else if (state == State.mouseClick)
471 {
472 Rect rect(location, size);
473 graphics.FillRectangleChecked(pathView->MouseClickBrush(), rect);
474 }
475 Padding componentPadding = pathView->GetPathComponentPadding();
476 PointF origin(location.x, location.y);
477 origin.x = origin.x + componentPadding.left;
478 origin.y = origin.y + componentPadding.top;
479 graphics.DrawStringChecked(name, pathView->GetFont(), origin, pathView->GetTextBrush());
480 }
481 public void Measure(Graphics& graphics)
482 {
483 Padding componentPadding = pathView->GetPathComponentPadding();
484 PointF origin(0, 0);
485 StringFormat stringFormat;
486 RectF r = graphics.MeasureStringChecked(name, pathView->GetFont(), origin, stringFormat);
487 pathView->SetTextHeight(r.size.h);
488 size = Size(cast<int>(componentPadding.Horizontal() + r.size.w + 0.5f), cast<int>(componentPadding.Vertical() + r.size.h + 0.5f));
489 }
490 public inline nothrow const Point& Location() const
491 {
492 return location;
493 }
494 public nothrow void SetLocation(const Point& location_)
495 {
496 location = location_;
497 }
498 public inline nothrow const Size& GetSize() const
499 {
500 return size;
501 }
502 private PathView* pathView;
503 private string name;
504 private void* data;
505 private State state;
506 private Point location;
507 private Size size;
508 }
509 }