1 // =================================
  2 // Copyright (c) 2021 Seppo Laakko
  3 // Distributed under the MIT license
  4 // =================================
  5 
  6 using System;
  7 using System.Collections;
  8 
  9 namespace System.Windows
 10 {
 11     public class StatusBar : Control
 12     {
 13         private enum Flags : sbyte
 14         {
 15             none = 0changed = 1 << 0
 16         }
 17 
 18         private const int initialHeight = 20;
 19         private const int topLineWidth = 1;
 20 
 21         public StatusBar(const Font& font_const Color& backgroundColorconst Point& locationconst Size& sizeDock dockAnchors anchors) : 
 22             base("System.Windows.StatusBar"DefaultWindowClassStyle()DefaultChildWindowStyle()DefaultExtendedWindowStyle()
 23             backgroundColor"statusBar"locationsizedockanchors)flags(Flags.none)font(font_)stringFormat()textColor(Color.Black())
 24             textBrush(textColor)
 25             topLineColor(215u215u215u)topLinePen(topLineColor1.0f)
 26             sunkenBorderOuterTopLeftColor(160u160u160u)sunkenBorderOuterTopLeftPen(sunkenBorderOuterTopLeftColor1.0f)
 27             sunkenBorderInnerTopLeftColor(105u105u105u)sunkenBorderInnerTopLeftPen(sunkenBorderInnerTopLeftColor1.0f)
 28             sunkenBorderOuterRightBottomColor(Color.White())sunkenBorderOuterRightBottomPen(sunkenBorderOuterRightBottomColor1.0f)
 29             sunkenBorderInnerRightBottomColor(227u227u227u)sunkenBorderInnerRightBottomPen(sunkenBorderInnerRightBottomColor1.0f)
 30             raisedBorderOuterTopLeftColor(227u227u227u)raisedBorderOuterTopLeftPen(raisedBorderOuterTopLeftColor1.0f)
 31             raisedBorderOuterRightBottomColor(105u105u105u)raisedBorderOuterRightBottomPen(raisedBorderOuterRightBottomColor1.0f)
 32             raisedBorderInnerTopLeftColor(Color.White())raisedBorderInnerTopLeftPen(raisedBorderInnerTopLeftColor1.0f)
 33             raisedBorderInnerRightBottomColor(160u160u160u)raisedBorderInnerRightBottomPen(raisedBorderInnerRightBottomColor1.0f)
 34         {
 35             SetChanged();
 36         }
 37         public StatusBar() : this(Font(FontFamily("Segoe UI")9.0f)DefaultControlBackgroundColor()Point(00)Size(0initialHeight)Dock.bottomAnchors.none)
 38         {
 39         }
 40         public void AddItem(StatusBarItem* statusBarItem)
 41         {
 42             statusBarItem->SetStatusBar(this);
 43             statusBarItems.Add(UniquePtr<StatusBarItem>(statusBarItem));
 44             SetChanged();
 45         }
 46         public inline nothrow const Color& TextColor() const
 47         {
 48             return textColor;
 49         }
 50         public void SetTextColor(const Color& textColor_)
 51         {
 52             if (textColor != textColor_)
 53             {
 54                 SetChanged();
 55                 textColor = textColor_;
 56                 textBrush = SolidBrush(textColor);
 57             }
 58         }
 59         internal inline nothrow const Brush& TextBrush() const
 60         {
 61             return textBrush;
 62         }
 63         internal inline nothrow const Font& TextFont() const
 64         {
 65             return font;
 66         }
 67         internal inline nothrow const Pen& TopLinePen() const
 68         {
 69             return topLinePen;
 70         }
 71         public inline nothrow const Color& TopLineColor() const
 72         {
 73             return topLineColor;
 74         }
 75         public void SetTopLineColor(const Color& topLineColor_)
 76         {
 77             if (topLineColor != topLineColor_)
 78             {
 79                 SetChanged();
 80                 topLineColor = topLineColor_;
 81                 topLinePen = Pen(topLineColor1.0f);
 82             }
 83         }
 84         public inline nothrow const Color& SunkenBorderOuterTopLeftColor() const
 85         {
 86             return sunkenBorderOuterTopLeftColor;
 87         }
 88         public void SetSunkenBorderOuterTopLeftColor(const Color& sunkenBorderOuterTopLeftColor_)
 89         {
 90             if (sunkenBorderOuterTopLeftColor != sunkenBorderOuterTopLeftColor_)
 91             {
 92                 SetChanged();
 93                 sunkenBorderOuterTopLeftColor = sunkenBorderOuterTopLeftColor_;
 94                 sunkenBorderOuterTopLeftPen = Pen(sunkenBorderOuterTopLeftColor1.0f);
 95             }
 96         }
 97         internal inline nothrow const Pen& SunkenBorderOuterTopLeftPen() const
 98         {
 99             return sunkenBorderOuterTopLeftPen;
100         }
101         public inline nothrow const Color& SunkenBorderInnerTopLeftColor() const
102         {
103             return sunkenBorderInnerTopLeftColor;
104         }
105         public void SetSunkenBorderInnerTopLeftColor(const Color& sunkenBorderInnerTopLeftColor_)
106         {
107             if (sunkenBorderInnerTopLeftColor != sunkenBorderInnerTopLeftColor_)
108             {
109                 SetChanged();
110                 sunkenBorderInnerTopLeftColor = sunkenBorderInnerTopLeftColor_;
111                 sunkenBorderInnerTopLeftPen = Pen(sunkenBorderInnerTopLeftColor1.0f);
112             }
113         }
114         internal inline nothrow const Pen& SunkenBorderInnerTopLeftPen() const
115         {
116             return sunkenBorderInnerTopLeftPen;
117         }
118         public inline nothrow const Color& SunkenBorderOuterRightBottomColor() const
119         {
120             return sunkenBorderOuterRightBottomColor;
121         }
122         public void SetSunkenBorderOuterRightBottomColor(const Color& sunkenBorderOuterRightBottomColor_)
123         {
124             if (sunkenBorderOuterRightBottomColor != sunkenBorderOuterRightBottomColor_)
125             {
126                 SetChanged();
127                 sunkenBorderOuterRightBottomColor = sunkenBorderOuterRightBottomColor_;
128                 sunkenBorderOuterRightBottomPen = Pen(sunkenBorderOuterRightBottomColor1.0f);
129             }
130         }
131         internal inline nothrow const Pen& SunkenBorderOuterRightBottomPen() const
132         {
133             return sunkenBorderOuterRightBottomPen;
134         }
135         public inline nothrow const Color& SunkenBorderInnerRightBottomColor() const
136         {
137             return sunkenBorderInnerRightBottomColor;
138         }
139         public void SetSunkenBorderInnerRightBottomColor(const Color& sunkenBorderInnerRightBottomColor_)
140         {
141             if (sunkenBorderInnerRightBottomColor != sunkenBorderInnerRightBottomColor_)
142             {
143                 SetChanged();
144                 sunkenBorderInnerRightBottomColor = sunkenBorderInnerRightBottomColor_;
145                 sunkenBorderInnerRightBottomPen = Pen(sunkenBorderInnerRightBottomColor1.0f);
146             }
147         }
148         internal inline nothrow const Pen& SunkenBorderInnerRightBottomPen() const
149         {
150             return sunkenBorderInnerRightBottomPen;
151         }
152         public inline nothrow const Color& RaisedBorderOuterTopLeftColor() const
153         {
154             return raisedBorderOuterTopLeftColor;
155         }
156         public void SetRaisedBorderOuterTopLeftColor(const Color& raisedBorderOuterTopLeftColor_)
157         {
158             if (raisedBorderOuterTopLeftColor != raisedBorderOuterTopLeftColor_)
159             {
160                 SetChanged();
161                 raisedBorderOuterTopLeftColor = raisedBorderOuterTopLeftColor_;
162                 raisedBorderOuterTopLeftPen = Pen(raisedBorderOuterTopLeftColor1.0f);
163             }
164         }
165         internal inline nothrow const Pen& RaisedBorderOuterTopLeftPen() const
166         {
167             return raisedBorderOuterTopLeftPen;
168         }
169         public inline nothrow const Color& RaisedBorderOuterRightBottomColor() const
170         {
171             return raisedBorderOuterRightBottomColor;
172         }
173         public void SetRaisedBorderRightBottomColor(const Color& raisedBorderOuterRightBottomColor_)
174         {
175             if (raisedBorderOuterRightBottomColor != raisedBorderOuterRightBottomColor_)
176             {
177                 SetChanged();
178                 raisedBorderOuterRightBottomColor = raisedBorderOuterRightBottomColor_;
179                 raisedBorderOuterRightBottomPen = Pen(raisedBorderOuterRightBottomColor1.0f);
180             }
181         }
182         internal inline nothrow const Pen& RaisedBorderOuterRightBottomPen() const
183         {
184             return raisedBorderOuterRightBottomPen;
185         }
186         public inline nothrow const Color& RaisedBorderInnerTopLeftColor() const
187         {
188             return raisedBorderInnerTopLeftColor;
189         }
190         public void SetRaisedBorderInnerTopLeftColor(const Color& raisedBorderInnerTopLeftColor_)
191         {
192             if (raisedBorderInnerTopLeftColor != raisedBorderInnerTopLeftColor_)
193             {
194                 SetChanged();
195                 raisedBorderInnerTopLeftColor = raisedBorderInnerTopLeftColor_;
196                 raisedBorderInnerTopLeftPen = Pen(raisedBorderInnerTopLeftColor1.0f);
197             }
198         }
199         internal inline nothrow const Pen& RaisedBorderInnerTopLeftPen() const
200         {
201             return raisedBorderInnerTopLeftPen;
202         }
203 
204         public inline nothrow const Color& RaisedBorderInnerRightBottomColor() const
205         {
206             return raisedBorderInnerRightBottomColor;
207         }
208         public void SetRaisedBorderInnerRightBottomColor(const Color& raisedBorderInnerRightBottomColor_)
209         {
210             if (raisedBorderInnerRightBottomColor != raisedBorderInnerRightBottomColor_)
211             {
212                 SetChanged();
213                 raisedBorderInnerRightBottomColor = raisedBorderInnerRightBottomColor_;
214                 raisedBorderInnerRightBottomPen = Pen(raisedBorderInnerRightBottomColor1.0f);
215             }
216         }
217         internal inline nothrow const Pen& RaisedBorderInnerRightBottomPen() const
218         {
219             return raisedBorderInnerRightBottomPen;
220         }
221         protected override nothrow Padding DefaultPadding() const
222         {
223             return Padding(4444);
224         }
225         protected override void OnPaint(PaintEventArgs& args)
226         {
227             if (Changed())
228             {
229                 ResetChanged();
230                 Measure(args.graphics);
231             }
232             args.graphics.Clear(BackgroundColor());
233             DrawTopLine(args.graphics);
234             DrawItems(args.graphics);
235             base->OnPaint(args);
236         }
237         private void DrawTopLine(Graphics& graphics)
238         {
239             Size size = GetSize();
240             Point start(00);
241             Point end(size.w - 10);
242             graphics.DrawLineChecked(topLinePenstartend);
243         }
244         private void DrawItems(Graphics& graphics)
245         {
246             long n = statusBarItems.Count();
247             for (long i = 0; i < n; ++i;)
248             {
249                 StatusBarItem* item = statusBarItems[i].Get();
250                 item->Draw(graphics);
251             }
252         }
253         private void Measure(Graphics& graphics)
254         {
255             LogView* logView = Application.GetLogView();
256             Padding padding = DefaultPadding();
257             int statusBarItemBorderWidth = StatusBarItem.BorderWidth();
258             int statusBarItemHorizontalPadding = StatusBarItem.HorizontalPadding();
259             int textHeight = 0;
260             long n = statusBarItems.Count();
261             for (long i = 0; i < n; ++i;)
262             {
263                 StatusBarItem* item = statusBarItems[i].Get();
264                 string s = item->Text();
265                 ustring u(ToUtf32(s));
266                 string z('0'u.Length());
267                 RectF rect = graphics.MeasureStringChecked(zfontPointF(00)stringFormat);
268                 textHeight = Max(textHeightcast<int>(rect.size.h));
269                 item->SetTextWidth(cast<int>(rect.size.w));
270             }
271             if (textHeight != 0)
272             {
273                 int height = topLineWidth + padding.Vertical() + 2 * statusBarItemBorderWidth + textHeight;
274                 Size size = GetSize();
275                 size.h = height;
276                 SetSize(size);
277                 DockWindow();
278                 Point leftOrigin(padding.lefttopLineWidth + padding.top);
279                 long springIndex = -1;
280                 for (long i = 0; i < n; ++i;)
281                 {
282                     StatusBarItem* item = statusBarItems[i].Get();
283                     if (item->Spring())
284                     {
285                         springIndex = i;
286                         break;
287                     }
288                     item->SetLocation(leftOrigin);
289                     item->SetSize(Size(2 * statusBarItemBorderWidth + statusBarItemHorizontalPadding + item->TextWidth()2 * statusBarItemBorderWidth + textHeight));
290                     leftOrigin.x = leftOrigin.x + item->GetSize().w;
291                 }
292                 Point rightOrigin(size.w - 1 - padding.righttopLineWidth + padding.top);
293                 if (springIndex != -1)
294                 {
295                     for (long i = n - 1; i > springIndex; --i;)
296                     {
297                         StatusBarItem* item = statusBarItems[i].Get();
298                         if (item->Spring())
299                         {
300                             break;
301                         }
302                         item->SetSize(Size(2 * statusBarItemBorderWidth + statusBarItemHorizontalPadding + item->TextWidth()2 * statusBarItemBorderWidth + textHeight));
303                         rightOrigin.x = rightOrigin.x - item->GetSize().w;
304                         item->SetLocation(rightOrigin);
305                     }
306                     StatusBarItem* springItem = statusBarItems[springIndex].Get();
307                     springItem->SetLocation(leftOrigin);
308                     springItem->SetSize(Size(rightOrigin.x - leftOrigin.x + 12 * statusBarItemBorderWidth + textHeight));
309                 }
310             }
311         }
312         private inline nothrow bool Changed() const
313         {
314             return (flags & Flags.changed) != Flags.none;
315         }
316         internal inline nothrow void SetChanged()
317         {
318             flags = cast<Flags>(flags | Flags.changed);
319         }
320         private inline nothrow void ResetChanged()
321         {
322             flags = cast<Flags>(flags & ~Flags.changed);
323         }
324         private Flags flags;
325         private Font font;
326         private StringFormat stringFormat;
327         private List<UniquePtr<StatusBarItem>> statusBarItems;
328         private Color textColor;
329         private SolidBrush textBrush;
330         private Color topLineColor;
331         private Pen topLinePen;
332         private Color sunkenBorderOuterTopLeftColor;
333         private Pen sunkenBorderOuterTopLeftPen;
334         private Color sunkenBorderInnerTopLeftColor;
335         private Pen sunkenBorderInnerTopLeftPen;
336         private Color sunkenBorderOuterRightBottomColor;
337         private Pen sunkenBorderOuterRightBottomPen;
338         private Color sunkenBorderInnerRightBottomColor;
339         private Pen sunkenBorderInnerRightBottomPen;
340         private Color raisedBorderOuterTopLeftColor;
341         private Pen raisedBorderOuterTopLeftPen;
342         private Color raisedBorderOuterRightBottomColor;
343         private Pen raisedBorderOuterRightBottomPen;
344         private Color raisedBorderInnerTopLeftColor;
345         private Pen raisedBorderInnerTopLeftPen;
346         private Color raisedBorderInnerRightBottomColor;
347         private Pen raisedBorderInnerRightBottomPen;
348     }
349 
350     public class StatusBarItem
351     {
352         public enum BorderStyle : sbyte
353         {
354             flatsunkenraised
355         }
356         public static nothrow int BorderWidth()
357         {
358             return 2;
359         }
360         public static nothrow int HorizontalPadding()
361         {
362             return 4;
363         }
364         public nothrow StatusBarItem(const string& text_BorderStyle borderStyle_bool spring_) : statusBar(null)text(text_)borderStyle(borderStyle_)spring(spring_)
365         {
366         }
367         public nothrow StatusBarItem(const string& textBorderStyle borderStyle) : this(textborderStylefalse)
368         {
369         }
370         public nothrow StatusBarItem(const string& text) : this(textBorderStyle.flat)
371         {
372         }
373         public nothrow StatusBarItem() : this(string())
374         {
375         }
376         public nothrow const Point& Location() const
377         {
378             return location;
379         }
380         public nothrow void SetLocation(const Point& location_)
381         {
382             location = location_;
383         }
384         public nothrow const Size& GetSize() const
385         {
386             return size;
387         }
388         public nothrow void SetSize(const Size& size_)
389         {
390             size = size_;
391         }
392         public inline nothrow const string& Text() const
393         {
394             return text;
395         }
396         public void SetText(const string& text_)
397         {
398             if (text != text_)
399             {
400                 text = text_;
401                 Invalidate();
402             }
403         }
404         public inline nothrow int TextWidth() const
405         {
406             return textWidth;
407         }
408         public inline nothrow void SetTextWidth(int textWidth_)
409         {
410             textWidth = textWidth_;
411         }
412         public inline nothrow BorderStyle GetBorderStyle() const
413         {
414             return borderStyle;
415         }
416         public void SetBorderStyle(BorderStyle borderStyle_)
417         {
418             if (borderStyle != borderStyle_)
419             {
420                 borderStyle = borderStyle_;
421                 Invalidate();
422             }
423         }
424         public inline nothrow bool Spring() const
425         {
426             return spring;
427         }
428         public void SetSpring(bool spring_)
429         {
430             if (spring != spring_)
431             {
432                 spring = spring_;
433                 Invalidate();
434             }
435         }
436         public virtual default ~StatusBarItem();
437         public void Draw(Graphics& graphics)
438         {
439             DrawBorder(graphics);
440             DrawText(graphics);
441         }
442         private void DrawText(Graphics& graphics)
443         {
444             const Brush& brush = statusBar->TextBrush();
445             const Font& font = statusBar->TextFont();
446             PointF origin(location.x + 2 + HorizontalPadding() / 2location.y + 2);
447             graphics.DrawStringChecked(textfontoriginbrush);
448         }
449         private void DrawBorder(Graphics& graphics)
450         {
451             switch (borderStyle)
452             {
453                 case BorderStyle.sunken:
454                 {
455                     DrawSunkenBorder(graphics);
456                     break;
457                 }
458                 case BorderStyle.raised:
459                 {
460                     DrawRaisedBorder(graphics);
461                     break;
462                 }
463             }
464         }
465         private void DrawSunkenBorder(Graphics& graphics)
466         {
467             const Pen& outerTopLeftPen = statusBar->SunkenBorderOuterTopLeftPen();
468             graphics.DrawLineChecked(outerTopLeftPenlocationPoint(location.x + size.w - 2location.y));
469             graphics.DrawLineChecked(outerTopLeftPenlocationPoint(location.xlocation.y + size.h - 2));
470             const Pen& outerRightBottomPen = statusBar->SunkenBorderOuterRightBottomPen();
471             graphics.DrawLineChecked(outerRightBottomPenPoint(location.x + size.w - 1location.y)Point(location.x + size.w - 1location.y + size.h - 1));
472             graphics.DrawLineChecked(outerRightBottomPenPoint(location.xlocation.y + size.h - 1)Point(location.x + size.w - 1location.y + size.h - 1));
473             const Pen& innerTopLeftPen = statusBar->SunkenBorderInnerTopLeftPen();
474             graphics.DrawLineChecked(innerTopLeftPenPoint(location.x + 1location.y + 1)Point(location.x + size.w - 3location.y + 1));
475             graphics.DrawLineChecked(innerTopLeftPenPoint(location.x + 1location.y + 1)Point(location.x + 1location.y + size.h - 3));
476             const Pen& innerRightBottomPen = statusBar->SunkenBorderInnerRightBottomPen();
477             graphics.DrawLineChecked(innerRightBottomPenPoint(location.x + size.w - 2location.y + 1)Point(location.x + size.w - 2location.y + size.h - 2));
478             graphics.DrawLineChecked(innerRightBottomPenPoint(location.x + 1location.y + size.h - 2)Point(location.x + size.w - 2location.y + size.h - 2));
479         }
480         private void DrawRaisedBorder(Graphics& graphics)
481         {
482             const Pen& outerTopLeftPen = statusBar->RaisedBorderOuterTopLeftPen();
483             graphics.DrawLineChecked(outerTopLeftPenlocationPoint(location.x + size.w - 2location.y));
484             graphics.DrawLineChecked(outerTopLeftPenlocationPoint(location.xlocation.y + size.h - 2));
485             const Pen& outerRightBottomPen = statusBar->RaisedBorderOuterRightBottomPen();
486             graphics.DrawLineChecked(outerRightBottomPenPoint(location.x + size.w - 1location.y)Point(location.x + size.w - 1location.y + size.h - 1));
487             graphics.DrawLineChecked(outerRightBottomPenPoint(location.xlocation.y + size.h - 1)Point(location.x + size.w - 1location.y + size.h - 1));
488             const Pen& innerTopLeftPen = statusBar->RaisedBorderInnerTopLeftPen();
489             graphics.DrawLineChecked(innerTopLeftPenPoint(location.x + 1location.y + 1)Point(location.x + size.w - 3location.y + 1));
490             graphics.DrawLineChecked(innerTopLeftPenPoint(location.x + 1location.y + 1)Point(location.x + 1location.y + size.h - 3));
491             const Pen& innerRightBottomPen = statusBar->RaisedBorderInnerRightBottomPen();
492             graphics.DrawLineChecked(innerRightBottomPenPoint(location.x + size.w - 2location.y + 1)Point(location.x + size.w - 2location.y + size.h - 2));
493             graphics.DrawLineChecked(innerRightBottomPenPoint(location.x + 1location.y + size.h - 2)Point(location.x + size.w - 2location.y + size.h - 2));
494         }
495         internal nothrow void SetStatusBar(StatusBar* statusBar_)
496         {
497             statusBar = statusBar_;
498         }
499         public void Invalidate()
500         {
501             if (statusBar != null)
502             {
503                 statusBar->SetChanged();
504                 statusBar->Invalidate();
505             }
506         }
507         private StatusBar* statusBar;
508         private Point location;
509         private Size size;
510         private int textWidth;
511         private string text;
512         private BorderStyle borderStyle;
513         private bool spring;
514     }
515 }