wxTimelineCtrlWidget
Loading...
Searching...
No Matches
TimelineArtProvider.h
Go to the documentation of this file.
1#ifndef _TIMELINEARTPROVIDER_H
2#define _TIMELINEARTPROVIDER_H
3
4#include <wx/wx.h>
5#include <wx/dcgraph.h>
6#include "TimelineItem.h"
7
8#if defined(__LINUX__)
9#include <wx/dc.h>
10#endif
11
12class wxGraphicsGradientStops;
13
15{
16 wxDECLARE_NO_COPY_CLASS(TimelineArtProvider);
17
18public:
20 : m_BackgroundColour(*wxWHITE),
21 m_LimitTimeColour(*wxBLUE),
22 m_Radius(5),
23 m_MinScaleStep(50.0)
24 {
25 m_TimeScaleFont.Create(8, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, "Arial");
26 m_ItemNameFont.Create(9, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, "Arial");
27 m_PenTimelineMain = wxPen(wxColour(255, 255, 255, 90));
28 m_PenScrollerMain = wxPen(wxColour(255, 255, 255, 120));
29 m_PenVisibleFrameNormal = wxPen(wxColour(255, 255, 255, 96));
30 m_PenVisibleFrameHover = wxPen(wxColour(255, 255, 255, 128));
31 m_PenVisibleFramePressed = wxPen(wxColour(255, 255, 255, 160));
32 m_PenGap = wxPen(*wxRED, 1, wxPENSTYLE_SHORT_DASH);
33
35 }
37
38 virtual void DrawBackground(wxDC& dc, const wxRect& rect)
39 {
40 dc.GradientFillLinear(rect, wxColour(83, 83, 83), wxColour(163, 163, 163), wxDOWN);
41 }
42 virtual void DrawTimelineBackground(wxDC& dc, const wxRect& rect) { wxUnusedVar(dc); wxUnusedVar(rect); }
43 virtual void DrawTimelineTrack(wxDC& dc, const wxRect& rect)
44 {
45 dc.SetPen(m_PenTimelineMain);
46 DrawGradientRoundedRect(dc, rect, m_Radius, wxGraphicsGradientStops(wxColour(55, 55, 55), wxColour(93, 93, 93)), wxDOWN);
47 }
48 virtual void DrawScrollerBackground(wxDC& dc, const wxRect& rect) { wxUnusedVar(dc); wxUnusedVar(rect); }
49 virtual void DrawScrollerTrack(wxDC& dc, const wxRect& rect)
50 {
51 dc.SetPen(m_PenScrollerMain);
52 DrawGradientRoundedRect(dc, rect, m_Radius, wxGraphicsGradientStops(wxColour(55, 55, 55), wxColour(93, 93, 93)), wxDOWN);
53 }
54 virtual void DrawGap(wxDC& dc, const wxRect& rTimeline, const wxRect& rVisibleFrame) {
55 wxUnusedVar(dc);
56 wxUnusedVar(rTimeline);
57 wxUnusedVar(rVisibleFrame);
58 }
59
60 virtual void DrawLeftArrow(wxDC& dc, const wxRect& rect, TimelineElementState state)
61 {
62 wxPoint t = rect.GetTopRight();
63 wxPoint b = rect.GetBottomRight();
64 wxPoint c = rect.GetTopLeft();
65 c.y += rect.height / 2;
66
67 DrawArrow(dc, t, b, c, state);
68 }
69 virtual void DrawRightArrow(wxDC& dc, const wxRect& rect, TimelineElementState state)
70 {
71 wxPoint t = rect.GetTopLeft();
72 wxPoint b = rect.GetBottomLeft();
73 wxPoint c = rect.GetTopRight();
74 c.y += rect.height / 2;
75
76 DrawArrow(dc, t, b, c, state);
77 }
78 virtual void DrawVisibleFrame(wxDC& dc, const wxRect& rect, TimelineElementState state)
79 {
80 double koef =
81 state == TimelineElementState::Normal ? 0.3 :
82 state == TimelineElementState::Hover ? 0.5 :
83 0.7;
84
85 int desiredRadius = 11;
86
87 int maxPossibleRadiusForRect = (wxMin(rect.width, rect.height) - 2) / 2;
88 int actualRadius = wxClip(desiredRadius, 0, maxPossibleRadiusForRect);
89
90 dc.SetPen(wxColour(255, 255, 255, 255 * (koef + 0.1)));
91
92 wxGraphicsGradientStops stops(wxColour(255, 255, 255, 192 * koef), wxColour(255, 255, 255, 128 * koef));
93 stops.Add(wxColour(255, 255, 255, 128 * koef), 0.6f);
94 stops.Add(wxColour(255, 255, 255, 76 * koef), 0.61f);
95 DrawGradientRoundedRect(dc, rect, actualRadius, stops, wxDOWN);
96 }
97
98 void DrawTimeScale(wxDC& dc, const wxRect& rect,
99 const wxDateTime& startTime, const wxDateTime& endTime)
100 {
101 if (rect.width <= 0 || rect.height <= 0)
102 return;
103
104 wxTimeSpan duration = endTime - startTime;
105 long totalSeconds = duration.GetSeconds().ToLong();
106
107 if (totalSeconds <= 0)
108 return;
109
110 dc.SetTextForeground(wxColour(80, 80, 80));
111 dc.SetFont(m_TimeScaleFont);
112 dc.SetPen(wxPen(wxColour(80, 80, 80), 1));
113
114 wxString sampleText = "00:00:00";
115 wxSize textSize = dc.GetTextExtent(sampleText);
116 int textWidth = textSize.GetWidth();
117 int textHeight = textSize.GetHeight();
118
119 int minSpacing = textWidth + 20;
120
121 int maxLabels = wxMax(2, rect.width / minSpacing);
122
123 int interval = CalculateNiceInterval(totalSeconds, maxLabels);
124
125 int numMarks = (totalSeconds / interval) + 1;
126
127 for (int i = 0; i <= numMarks && i * interval <= totalSeconds; ++i)
128 {
129 long offsetSeconds = i * interval;
130 if (offsetSeconds > totalSeconds)
131 offsetSeconds = totalSeconds;
132
133 double progress = double(offsetSeconds) / double(totalSeconds);
134 int x = rect.x + int(progress * rect.width);
135
136 wxDateTime markTime = startTime + wxTimeSpan::Seconds(offsetSeconds);
137 wxString timeText = FormatTimeForScale(markTime);
138
139 int tickHeight = 6;
140 int tickX = x;
141 dc.DrawLine(tickX, rect.y, tickX, rect.y + tickHeight);
142
143 wxSize currentTextSize = dc.GetTextExtent(timeText);
144 int textX, textY;
145
146 if (i == 0)
147 {
148 textX = x + 2;
149 }
150 else if (offsetSeconds >= totalSeconds)
151 {
152 textX = x - currentTextSize.GetWidth() - 2;
153 }
154 else
155 {
156 textX = x - currentTextSize.GetWidth() / 2;
157 }
158
159 textY = rect.y + (rect.height - textHeight) / 2;
160
161 textX = wxMax(rect.x, wxMin(textX, rect.GetRight() - currentTextSize.GetWidth()));
162
163 dc.SetClippingRegion(rect);
164 dc.DrawText(timeText, textX, textY);
165 dc.DestroyClippingRegion();
166 }
167 }
168
169 int CalculateNiceInterval(long totalSeconds, int maxLabels)
170 {
171 if (maxLabels <= 1)
172 return totalSeconds;
173
174 double roughInterval = double(totalSeconds) / double(maxLabels - 1);
175
176 const int niceIntervals[] = {
177 1, 2, 5, 10, 15, 30,
178 60, 120, 300, 600, 900, 1800,
179 3600, 7200, 10800, 21600, 43200,
180 86400, 172800, 432000, 864000, 2592000
181 };
182 const int numIntervals = sizeof(niceIntervals) / sizeof(niceIntervals[0]);
183
184 for (int i = 0; i < numIntervals; ++i)
185 {
186 if (niceIntervals[i] >= roughInterval)
187 {
188 return niceIntervals[i];
189 }
190 }
191
192 int largestNice = niceIntervals[numIntervals - 1];
193 int multiplier = (int(roughInterval) + largestNice - 1) / largestNice;
194 return largestNice * multiplier;
195 }
196
197 wxString FormatTimeForScale(const wxDateTime& time)
198 {
199 return time.Format("%H:%M:%S");
200 }
201
202 template<typename T>
203 void DrawItem(wxDC& dc, const wxRect& rect, const wxRect& parentRect, const TimelineItem<T>& item, bool isScrollerContext = false, bool makeTransparentDueToOverlap = false);
204
205 void SetBackgroundColour(const wxColour& colour) { m_BackgroundColour = colour; }
206 wxColour GetBackgroundColour() const { return m_BackgroundColour; }
207
208protected:
210 {
211 m_BmpRound = wxBitmap(m_Radius * 2, m_Radius * 2, 1);
212 {
213 wxMemoryDC dc(m_BmpRound);
214 dc.SetBackground(*wxWHITE_BRUSH);
215 dc.Clear();
216 dc.SetPen(*wxBLACK_PEN);
217 dc.SetBrush(*wxBLACK_BRUSH);
218 dc.DrawRoundedRectangle(0, 0, m_Radius * 2, m_Radius * 2, m_Radius);
219 }
221 m_RoundRegionTR.Offset(-m_Radius, 0);
222 m_RoundRegionBL.Offset(0, -m_Radius);
224 m_RoundRegionTL.Intersect(0, 0, m_Radius, m_Radius);
225 m_RoundRegionTR.Intersect(0, 0, m_Radius, m_Radius);
226 m_RoundRegionBL.Intersect(0, 0, m_Radius, m_Radius);
227 m_RoundRegionBR.Intersect(0, 0, m_Radius, m_Radius);
228 }
229
230 wxRegion GetRoundRegion(const wxRect& rect)
231 {
232 wxRegion region(rect);
233 wxRegion round;
234 round = m_RoundRegionTL;
235 round.Offset(rect.x, rect.y);
236 region.Subtract(round);
237
238 round = m_RoundRegionBL;
239 round.Offset(rect.x, rect.y + rect.height - m_Radius);
240 region.Subtract(round);
241
242 round = m_RoundRegionTR;
243 round.Offset(rect.x + rect.width - m_Radius, rect.y);
244 region.Subtract(round);
245
246 round = m_RoundRegionBR;
247 round.Offset(rect.x + rect.width - m_Radius, rect.y + rect.height - m_Radius);
248 region.Subtract(round);
249
250 return region;
251 }
252
253 void DrawGradientRoundedRect(wxDC& dc, const wxRect& rect, double radius, const wxGraphicsGradientStops& stops, wxDirection direction = wxEAST)
254 {
255 wxGCDC* gdc = wxDynamicCast(&dc, wxGCDC);
256 if (!gdc)
257 {
258 dc.SetDeviceClippingRegion(GetRoundRegion(rect));
259 dc.GradientFillLinear(rect, stops.GetStartColour(), stops.GetEndColour(), direction);
260 dc.SetBrush(*wxTRANSPARENT_BRUSH);
261 dc.DrawRoundedRectangle(rect, radius);
262 dc.DestroyClippingRegion();
263 return;
264 }
265
266 int w = rect.width;
267 int h = rect.height;
268
269 if (w == 0 || h == 0)
270 return;
271
272 if (radius < 0.0)
273 radius = -radius * ((w < h) ? w : h);
274
275 wxPoint start;
276 wxPoint end;
277 switch (direction)
278 {
279 case wxWEST:
280 start = rect.GetRightBottom();
281 start.x++;
282 end = rect.GetLeftBottom();
283 break;
284 case wxEAST:
285 start = rect.GetLeftBottom();
286 end = rect.GetRightBottom();
287 end.x++;
288 break;
289 case wxNORTH:
290 start = rect.GetLeftBottom();
291 start.y++;
292 end = rect.GetLeftTop();
293 break;
294 case wxSOUTH:
295 start = rect.GetLeftTop();
296 end = rect.GetLeftBottom();
297 end.y++;
298 break;
299 default:
300 break;
301 }
302
303 wxGraphicsContext* gc = gdc->GetGraphicsContext();
304
305 if (gc->ShouldOffset())
306 {
307 w -= 1;
308 h -= 1;
309 }
310
311 gc->SetBrush(gc->CreateLinearGradientBrush(start.x, start.y, end.x, end.y, stops));
312 gc->DrawRoundedRectangle(rect.x, rect.y, w, h, radius);
313 }
314
315 void DrawArrow(wxDC& dc, wxPoint& top, wxPoint& bottom, wxPoint& center, TimelineElementState state)
316 {
317 wxColour penColor, brushColor, shadowColor;
318
319 switch (state)
320 {
322 penColor = wxColour(60, 60, 60);
323 brushColor = wxColour(180, 180, 180, 220);
324 shadowColor = wxColour(0, 0, 0, 80);
325 break;
327 penColor = wxColour(40, 40, 40);
328 brushColor = wxColour(240, 240, 240, 240);
329 shadowColor = wxColour(0, 0, 0, 120);
330 break;
332 penColor = wxColour(20, 20, 20);
333 brushColor = wxColour(255, 255, 255, 255);
334 shadowColor = wxColour(0, 0, 0, 150);
335 break;
337 penColor = wxColour(120, 120, 120);
338 brushColor = wxColour(160, 160, 160, 160);
339 shadowColor = wxColour(0, 0, 0, 40);
340 break;
341 default:
342 penColor = wxColour(60, 60, 60);
343 brushColor = wxColour(180, 180, 180, 220);
344 shadowColor = wxColour(0, 0, 0, 80);
345 break;
346 }
347
348 wxPoint expandedTop = top;
349 wxPoint expandedBottom = bottom;
350 wxPoint expandedCenter = center;
351
352 int deltaY = (bottom.y - top.y) / 6;
353 expandedTop.y -= deltaY;
354 expandedBottom.y += deltaY;
355
356 int deltaX = abs(center.x - top.x) / 4;
357 if (center.x > top.x)
358 expandedCenter.x += deltaX;
359 else
360 expandedCenter.x -= deltaX;
361
362 wxPoint arrowPoints[] = { expandedTop, expandedCenter, expandedBottom };
363
365 {
366 wxPoint shadowPoints[] = {
367 wxPoint(expandedTop.x + 1, expandedTop.y + 1),
368 wxPoint(expandedCenter.x + 1, expandedCenter.y + 1),
369 wxPoint(expandedBottom.x + 1, expandedBottom.y + 1)
370 };
371
372 dc.SetBrush(wxBrush(shadowColor));
373 dc.SetPen(wxPen(shadowColor));
374 dc.DrawPolygon(WXSIZEOF(shadowPoints), shadowPoints);
375 }
376
377 dc.SetBrush(wxBrush(brushColor));
378 dc.SetPen(wxPen(brushColor));
379 dc.DrawPolygon(WXSIZEOF(arrowPoints), arrowPoints);
380
381 dc.SetPen(wxPen(penColor, state == TimelineElementState::Pressed ? 2 : 1));
382 dc.DrawLines(WXSIZEOF(arrowPoints), arrowPoints);
383
385 {
386 wxColour highlightColor = wxColour(255, 255, 255, 100);
387 dc.SetPen(wxPen(highlightColor, 1));
388
389 if (center.x > top.x)
390 {
391 dc.DrawLine(expandedTop.x - 1, expandedTop.y + 1,
392 expandedCenter.x - 1, expandedCenter.y);
393 }
394 else
395 {
396 dc.DrawLine(expandedTop.x + 1, expandedTop.y + 1,
397 expandedCenter.x + 1, expandedCenter.y);
398 }
399 }
400 }
401
402protected:
405
408
414 wxPen m_PenGap;
415
416 wxBitmap m_BmpRound;
418
421};
422
423template<typename T>
424void TimelineArtProvider::DrawItem(wxDC& dc, const wxRect& rect, const wxRect& parentRect, const TimelineItem<T>& item, bool isScrollerContext, bool makeTransparentDueToOverlap)
425{
426 if (!item.Data)
427 return;
428
429 wxRect r(rect.Intersect(parentRect));
430 if (r.IsEmpty())
431 return;
432
433 wxColour currentItemColor = item.Colour;
434
435 switch (item.State)
436 {
438 currentItemColor = currentItemColor.ChangeLightness(130);
439 break;
441 currentItemColor = currentItemColor.ChangeLightness(150);
442 break;
444 currentItemColor = currentItemColor.ChangeLightness(90);
445 break;
446 default:
447 break;
448 }
449
450 unsigned char finalAlpha = currentItemColor.Alpha();
451 if (makeTransparentDueToOverlap)
452 {
453 if (isScrollerContext)
454 {
455 finalAlpha = 100;
456 }
457 else
458 {
459 finalAlpha = 180;
460 }
461 }
462
463 wxColour baseColor(currentItemColor.Red(), currentItemColor.Green(), currentItemColor.Blue(), finalAlpha);
464
465 wxColour gradColor = baseColor.ChangeLightness(133);
466 gradColor.Set(gradColor.Red(), gradColor.Green(), gradColor.Blue(), finalAlpha);
467
468 int radius = wxClip(m_Radius, 0, (r.width - 2) / 2);
469
470 wxGCDC* gdc = wxDynamicCast(&dc, wxGCDC);
471 if (gdc)
472 {
473 wxGraphicsContext* gc = gdc->GetGraphicsContext();
474 if (gc)
475 {
476 wxGraphicsGradientStops stops(baseColor, gradColor);
477 wxGraphicsBrush brush = gc->CreateLinearGradientBrush(
478 r.x, r.GetBottom(), r.GetRight(), r.GetBottom(), stops);
479 gc->SetBrush(brush);
480 gc->DrawRoundedRectangle(r.x, r.y, r.width, r.height, radius);
481 }
482 }
483 else
484 {
485 dc.SetDeviceClippingRegion(GetRoundRegion(r));
486 dc.GradientFillLinear(r, gradColor, baseColor, wxDOWN);
487 dc.SetBrush(*wxTRANSPARENT_BRUSH);
488 dc.DrawRoundedRectangle(r, radius);
489 dc.DestroyClippingRegion();
490 }
491
492 wxString name = item.GetItemName();
493 if (!name.IsEmpty())
494 {
495 wxRect textRect = r;
496 textRect.Deflate(isScrollerContext ? 2 : 5, 0);
497
498 wxFont fontToUse = m_ItemNameFont;
499 if (isScrollerContext)
500 {
501 fontToUse = wxFont(wxFontInfo(9).Family(wxFONTFAMILY_SWISS).Weight(wxFONTWEIGHT_NORMAL));
502 }
503 else
504 {
505 fontToUse = wxFont(wxFontInfo(12)
506 .Family(m_ItemNameFont.GetFamily())
507 .Weight(m_ItemNameFont.GetWeight())
508 .Style(m_ItemNameFont.GetStyle())
509 .Underlined(m_ItemNameFont.GetUnderlined())
510 .FaceName(m_ItemNameFont.GetFaceName()));
511 }
512 dc.SetFont(fontToUse);
513
514 dc.SetTextForeground(*wxBLACK);
515
516 wxSize textSize = dc.GetTextExtent(name);
517
518 bool canDrawText = (textRect.width >= textSize.x && textRect.height >= textSize.y);
519 if (isScrollerContext && r.width < 10)
520 {
521 canDrawText = false;
522 }
523
524 if (canDrawText)
525 {
526 textRect = textRect.Intersect(r);
527 if (!textRect.IsEmpty())
528 {
529 wxDCClipper clip(dc, textRect);
530 dc.DrawLabel(name, textRect, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
531 }
532 }
533 }
534}
535
536#endif // _TIMELINEARTPROVIDER_H
TimelineElementState
Definition TimelineItem.h:12
Definition TimelineArtProvider.h:15
wxFont m_ItemNameFont
Definition TimelineArtProvider.h:407
wxRegion m_RoundRegionTR
Definition TimelineArtProvider.h:417
virtual void DrawTimelineTrack(wxDC &dc, const wxRect &rect)
Definition TimelineArtProvider.h:43
virtual ~TimelineArtProvider()
Definition TimelineArtProvider.h:36
wxPen m_PenVisibleFramePressed
Definition TimelineArtProvider.h:413
virtual void DrawScrollerTrack(wxDC &dc, const wxRect &rect)
Definition TimelineArtProvider.h:49
wxPen m_PenScrollerMain
Definition TimelineArtProvider.h:410
double m_MinScaleStep
Definition TimelineArtProvider.h:420
virtual void DrawGap(wxDC &dc, const wxRect &rTimeline, const wxRect &rVisibleFrame)
Definition TimelineArtProvider.h:54
wxPen m_PenGap
Definition TimelineArtProvider.h:414
virtual void DrawScrollerBackground(wxDC &dc, const wxRect &rect)
Definition TimelineArtProvider.h:48
virtual void DrawRightArrow(wxDC &dc, const wxRect &rect, TimelineElementState state)
Definition TimelineArtProvider.h:69
int CalculateNiceInterval(long totalSeconds, int maxLabels)
Definition TimelineArtProvider.h:169
void DrawArrow(wxDC &dc, wxPoint &top, wxPoint &bottom, wxPoint &center, TimelineElementState state)
Definition TimelineArtProvider.h:315
wxRegion m_RoundRegionBR
Definition TimelineArtProvider.h:417
wxRegion m_RoundRegionBL
Definition TimelineArtProvider.h:417
wxRegion m_RoundRegionTL
Definition TimelineArtProvider.h:417
virtual void DrawVisibleFrame(wxDC &dc, const wxRect &rect, TimelineElementState state)
Definition TimelineArtProvider.h:78
wxPen m_PenTimelineMain
Definition TimelineArtProvider.h:409
virtual void DrawTimelineBackground(wxDC &dc, const wxRect &rect)
Definition TimelineArtProvider.h:42
virtual void DrawBackground(wxDC &dc, const wxRect &rect)
Definition TimelineArtProvider.h:38
wxString FormatTimeForScale(const wxDateTime &time)
Definition TimelineArtProvider.h:197
wxPen m_PenVisibleFrameNormal
Definition TimelineArtProvider.h:411
wxFont m_TimeScaleFont
Definition TimelineArtProvider.h:406
void SetBackgroundColour(const wxColour &colour)
Definition TimelineArtProvider.h:205
int m_Radius
Definition TimelineArtProvider.h:419
void DrawGradientRoundedRect(wxDC &dc, const wxRect &rect, double radius, const wxGraphicsGradientStops &stops, wxDirection direction=wxEAST)
Definition TimelineArtProvider.h:253
void CreateRoundRegions()
Definition TimelineArtProvider.h:209
wxPen m_PenVisibleFrameHover
Definition TimelineArtProvider.h:412
virtual void DrawLeftArrow(wxDC &dc, const wxRect &rect, TimelineElementState state)
Definition TimelineArtProvider.h:60
void DrawItem(wxDC &dc, const wxRect &rect, const wxRect &parentRect, const TimelineItem< T > &item, bool isScrollerContext=false, bool makeTransparentDueToOverlap=false)
Definition TimelineArtProvider.h:424
void DrawTimeScale(wxDC &dc, const wxRect &rect, const wxDateTime &startTime, const wxDateTime &endTime)
Definition TimelineArtProvider.h:98
wxColour m_LimitTimeColour
Definition TimelineArtProvider.h:404
wxColour GetBackgroundColour() const
Definition TimelineArtProvider.h:206
TimelineArtProvider()
Definition TimelineArtProvider.h:19
wxBitmap m_BmpRound
Definition TimelineArtProvider.h:416
wxRegion GetRoundRegion(const wxRect &rect)
Definition TimelineArtProvider.h:230
wxColour m_BackgroundColour
Definition TimelineArtProvider.h:403
Definition TimelineItem.h:22
wxColour Colour
Definition TimelineItem.h:57
wxString GetItemName() const
Definition TimelineItem.h:47
T * Data
Definition TimelineItem.h:55
TimelineElementState State
Definition TimelineItem.h:56
Tval wxClip(Tval value, Tval min_val, Tval max_val)
Definition wxTimelineCtrl.h:18