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 nothrow Color DefaultToolTipTextColor()
 12     {
 13         return Color.Black();
 14     }
 15     
 16     public nothrow Color DefaultToolTipFrameColor()
 17     {
 18         return Color.Black();
 19     }
 20 
 21     public nothrow string DefaultToolTipFontFamilyName()
 22     {
 23         return "Segoe UI";
 24     }
 25 
 26     public nothrow float DefaultToolTipFontSize()
 27     {
 28         return 9.0f;
 29     }
 30     
 31     public nothrow ControlCreateParams& ToolTipControlCreateParams(ControlCreateParams& controlCreateParams)
 32     {
 33         return controlCreateParams.SetWindowClassName("System.Windows.ToolTip").SetWindowStyle(HiddenChildWindowStyle()).
 34             SetWindowClassBackgroundColor(SystemColor.COLOR_WINDOW).SetBackgroundColor(Color.FloralWhite()).SetSize(Size(00));
 35     }
 36     
 37     public class ToolTipCreateParams
 38     {
 39         public nothrow ToolTipCreateParams(ControlCreateParams& controlCreateParams_) : 
 40             controlCreateParams(controlCreateParams_)
 41             textColor(DefaultToolTipTextColor())
 42             frameColor(DefaultToolTipFrameColor())
 43             fontFamilyName(DefaultToolTipFontFamilyName())
 44             fontSize(DefaultToolTipFontSize())
 45         {
 46         }
 47         public nothrow ToolTipCreateParams& Defaults()
 48         {
 49             return *this;
 50         }
 51         public nothrow ToolTipCreateParams& SetTextColor(const Color& textColor_)
 52         {
 53             textColor = textColor_;
 54             return *this;
 55         }
 56         public nothrow ToolTipCreateParams& SetFrameColor(const Color& frameColor_)
 57         {
 58             frameColor = frameColor_;
 59             return *this;
 60         }
 61         public nothrow ToolTipCreateParams& SetFontFamilyName(const string& fontFamilyName_)
 62         {
 63             fontFamilyName = fontFamilyName_;
 64             return *this;
 65         }
 66         public nothrow ToolTipCreateParams& SetFontSize(float fontSize_)
 67         {
 68             fontSize = fontSize_;
 69             return *this;
 70         }
 71         public ControlCreateParams& controlCreateParams;
 72         public Color textColor;
 73         public Color frameColor;
 74         public string fontFamilyName;
 75         public float fontSize;
 76     }
 77     
 78     public class ToolTip : Control
 79     {
 80         private enum Flags : sbyte
 81         {
 82             none = 0changed = 1 << 0
 83         }
 84         public ToolTip(const Color& backgroundColorconst Color& textColorconst Color& frameColorconst string& text_const Point& location_
 85             const Font& font_) : 
 86             base("System.Windows.ToolTip"DefaultWindowClassStyle()HiddenChildWindowStyle()DefaultExtendedWindowStyle()backgroundColor
 87             text_location_Size(00)Dock.noneAnchors.none)framePen(frameColor)font(font_)textBrush(textColor)
 88             format(StringAlignment.nearStringAlignment.near)textHeight(0)
 89         {
 90             SetChanged();
 91         }
 92         public ToolTip(const Color& backgroundColor) : 
 93             this(backgroundColorColor.Black()Color.Black()""Point(00)Font(FontFamily("Segoe UI")9.0f))
 94         {
 95         }
 96         public ToolTip() : this(Color.FloralWhite())
 97         {
 98         }
 99         public ToolTip(ToolTipCreateParams& createParams) : 
100             base(createParams.controlCreateParams)
101             framePen(createParams.frameColor)
102             font(FontFamily(createParams.fontFamilyName)createParams.fontSize)
103             textBrush(createParams.textColor)
104             format(StringAlignment.nearStringAlignment.near)textHeight(0)
105         {
106             SetChanged();
107         }
108         public void SetTextColor(const Color& textColor)
109         {
110             textBrush = SolidBrush(textColor);
111             Invalidate();
112         }
113         public void SetFrameColor(const Color& frameColor)
114         {
115             framePen = Pen(frameColor);
116             Invalidate();
117         }
118         public void SetFont(const Font& font_)
119         {
120             font = font_;
121             SetChanged();
122             Invalidate();
123         }
124         public void MeasureExtent()
125         {
126             Graphics graphics = Graphics.FromWindowHandle(Handle());
127             Measure(graphics);
128         }
129         protected override void OnTextChanged()
130         {
131             base->OnTextChanged();
132             lines = SplitIntoLines(Text());
133             SetChanged();
134             Invalidate();
135         }
136         protected override void OnPaint(PaintEventArgs& args)
137         {
138             if (Changed())
139             {
140                 ResetChanged();
141                 Measure(args.graphics);
142             }
143             Rect r(Point()GetSize());
144             r.size.w = r.size.w - 1;
145             r.size.h = r.size.h - 1;
146             args.graphics.Clear(BackgroundColor());
147             args.graphics.DrawRectangleChecked(framePenr);
148             PointF pt(11);
149             for (const string& line : lines)
150             {
151                 args.graphics.DrawStringChecked(linefontpttextBrush);
152                 pt.y = pt.y + textHeight;
153             }
154             base->OnPaint(args);
155         }
156         private void Measure(Graphics& graphics)
157         {
158             Size size;
159             for (const string& line : lines)
160             {
161                 RectF textRect = graphics.MeasureStringChecked(linefontPointF(00)format);
162                 textHeight = Max(textHeightcast<int>(textRect.size.h));
163                 size.w = Max(size.wcast<int>(textRect.size.w));
164             }
165             size.h = cast<int>(lines.Count()) * textHeight;
166             size.w = size.w + 1;
167             size.h = size.h + 1;
168             SetSize(size);
169         }
170         private inline nothrow bool Changed() const
171         {
172             return (flags & Flags.changed) != Flags.none;
173         }
174         private inline nothrow void SetChanged()
175         {
176             flags = cast<Flags>(flags | Flags.changed);
177         }
178         private inline nothrow void ResetChanged()
179         {
180             flags = cast<Flags>(flags & ~Flags.changed);
181         }
182         private Flags flags;
183         private Pen framePen;
184         private Font font;
185         private SolidBrush textBrush;
186         private StringFormat format;
187         private List<string> lines;
188         private int textHeight;
189     }
190 
191     public nothrow List<string> SplitIntoLines(const string& text)
192     {
193         List<string> lines;
194         string line;
195         int state = 0;
196         for (char c : text)
197         {
198             switch (state)
199             {
200                 case 0:
201                 {
202                     switch (c)
203                     {
204                         case '\n':
205                         {
206                             lines.Add(line);
207                             line.Clear();
208                             break;
209                         }
210                         case '\r':
211                         {
212                             state = 1;
213                             break;
214                         }
215                         default:
216                         {
217                             line.Append(c);
218                             break;
219                         }
220                     }
221                     break;
222                 }
223                 case 1:
224                 {
225                     switch (c)
226                     {
227                         case '\n':
228                         {
229                             lines.Add(line);
230                             line.Clear();
231                             state = 0;
232                             break;
233                         }
234                     }
235                     break;
236                 }
237             }
238         }
239         if (!line.IsEmpty())
240         {
241             lines.Add(line);
242         }
243         return lines;
244     }
245 }