1 // =================================
  2 // Copyright (c) 2021 Seppo Laakko
  3 // Distributed under the MIT license
  4 // =================================
  5 
  6 using System;
  7 
  8 namespace System.Windows
  9 {
 10     public class ColorCount
 11     {
 12         public nothrow ColorCount() : color(System.Color.Constant.black)count(0)
 13         {
 14         }
 15         public nothrow ColorCount(System.Color.Constant color_int count_) : color(color_)count(count_)
 16         {
 17         }
 18         public inline nothrow void IncrementCount()
 19         {
 20             ++count;
 21         }
 22         public inline nothrow void DecrementCount()
 23         {
 24             --count;
 25         }
 26         public System.Color.Constant color;
 27         public int count;
 28     }
 29 
 30     public Padding DefaultConsolePadding()
 31     {
 32         return Padding(4444);
 33     }
 34 
 35     public class delegate void ConsoleInputReadyEventHandler();
 36 
 37     public class Console : TextView
 38     {
 39         public Console(const FontFamily& fontFamilyfloat fontSizeSystem.Color.Constant backColorSystem.Color.Constant textColor
 40             const Point& locationconst Size& sizeDock dockAnchors anchors) : 
 41             base(fontFamilyfontSizeGetColor(backColor)GetColor(textColor)locationsizedockanchors)
 42             defaultBackColor(backColor)defaultTextColor(textColor)inputLine()eof(false)
 43         {
 44             SetReadOnly();
 45             SetPadding(DefaultConsolePadding());
 46             AnsiEngine.Out().SetDefaultBackColor(defaultBackColor);
 47             AnsiEngine.Error().SetDefaultBackColor(defaultBackColor);
 48             AnsiEngine.Out().SetDefaultTextColor(defaultTextColor);
 49             AnsiEngine.Error().SetDefaultTextColor(defaultTextColor);
 50         }
 51         public Console(System.Color.Constant backColorSystem.Color.Constant textColorconst Point& locationconst Size& sizeDock dockAnchors anchors) : 
 52             this(FontFamily("Cascadia Code")12.0fbackColortextColorlocationsizedockanchors)
 53         {
 54         }
 55         public Console(const Point& locationconst Size& sizeDock dockAnchors anchors) : 
 56             this(System.Color.Constant.blackSystem.Color.Constant.graylocationsizedockanchors)
 57         {
 58         }
 59         public void Write(int handleconst string& text)
 60         {
 61             if (text.IsEmpty()) return;
 62             ColorCharOutputMethod outputCharMethod = OutputChar;
 63             AnsiProcess(handletextoutputCharMethod);
 64             const List<ustring>& lines = Lines();
 65             if (!lines.IsEmpty())
 66             {
 67                 int line = cast<int>(lines.Count());
 68                 int col = cast<int>(lines.Back().Length() + 1);
 69                 SetCaretLineCol(linecol);
 70                 SetTextExtent();
 71                 ScrollToCaret();
 72                 SetChanged();
 73                 Invalidate();
 74             }
 75         }
 76         public void StartReadLine()
 77         {
 78             inputLine.Clear();
 79             eof = false;
 80             startInputCol = CaretColumn();
 81         }
 82         protected override void OnKeyDown(KeyEventArgs& args)
 83         {
 84             int caretCol = CaretColumn();
 85             int caretLine = CaretLine();
 86             switch (args.key)
 87             {
 88                 case cast<Keys>(Keys.controlModifier | Keys.z):
 89                 {
 90                     eof = true;
 91                     Write(1"^Z\n");
 92                     OnConsoleInputReady();
 93                     args.handled = true;
 94                     break;
 95                 }
 96                 case Keys.enter:
 97                 {
 98                     eof = false;
 99                     Write(1"\n");
100                     OnConsoleInputReady();
101                     args.handled = true;
102                     break;
103                 }
104                 case Keys.delete_:
105                 {
106                     int index = caretCol - startInputCol;
107                     if (index < inputLine.Length())
108                     {
109                         DeleteChar();
110                     }
111                     args.handled = true;
112                     break;
113                 }
114                 case Keys.back:
115                 {
116                     if (caretCol > startInputCol)
117                     {
118                         --caretCol;
119                         SetCaretLineCol(caretLinecaretCol);
120                         DeleteChar();
121                     }
122                     args.handled = true;
123                     break;
124                 }
125                 case Keys.left:
126                 {
127                     if (caretCol > startInputCol)
128                     {
129                         --caretCol;
130                         SetCaretLineCol(caretLinecaretCol);
131                         Invalidate();
132                     }
133                     args.handled = true;
134                     break;
135                 }
136                 case Keys.right:
137                 {
138                     int index = caretCol - startInputCol;
139                     if (index < inputLine.Length())
140                     {
141                         ++caretCol;
142                         SetCaretLineCol(caretLinecaretCol);
143                         Invalidate();
144                     }
145                     args.handled = true;
146                     break;
147                 }
148                 case Keys.home:
149                 {
150                     caretCol = startInputCol;
151                     SetCaretLineCol(caretLinecaretCol);
152                     Invalidate();
153                     args.handled = true;
154                     break;
155                 }
156                 case Keys.end:
157                 {
158                     caretCol = startInputCol + cast<int>(inputLine.Length());
159                     SetCaretLineCol(caretLinecaretCol);
160                     Invalidate();
161                     args.handled = true;
162                     break;
163                 }
164             }
165         }
166         protected override void OnKeyPress(KeyPressEventArgs& keyPressEventArgs)
167         {
168             if (eof) return;
169             base->OnKeyPress(keyPressEventArgs);
170             if (!keyPressEventArgs.handled)
171             {
172                 uchar ch = keyPressEventArgs.keyChar;
173                 InsertChar(ch);
174                 keyPressEventArgs.handled = true;
175             }
176         }
177         private nothrow void InsertChar(uchar ch)
178         {
179             int caretCol = CaretColumn();
180             int caretLine = CaretLine();
181             int index = caretCol - startInputCol;
182             if (index < inputLine.Length())
183             {
184                 inputLine = inputLine.Substring(0index) + ustring(ch) + inputLine.Substring(index);
185             }
186             else
187             {
188                 inputLine.Append(ch);
189             }
190             List<ustring>& lines = Lines();
191             ustring line = lines[caretLine - 1];
192             if (caretCol < line.Length())
193             {
194                 line = line.Substring(0caretCol - 1) + ustring(ch) + line.Substring(caretCol - 1);
195             }
196             else
197             {
198                 line.Append(ch);
199             }
200             lines[caretLine - 1] = line;
201             IncrementCaretColorCount();
202             ++caretCol;
203             SetCaretLineCol(caretLinecaretCol);
204             SetTextExtent();
205             Invalidate();
206         }
207         private nothrow void DeleteChar()
208         {
209             int caretCol = CaretColumn();
210             int caretLine = CaretLine();
211             int index = caretCol - startInputCol;
212             inputLine = inputLine.Substring(0index) + inputLine.Substring(index + 1);
213             List<ustring>& lines = Lines();
214             ustring line = lines[caretLine - 1];
215             line = line.Substring(0caretCol - 1) + line.Substring(caretCol);
216             lines[caretLine - 1] = line;
217             DecrementCaretColorCount();
218             Invalidate();
219         }
220         public override void Clear()
221         {
222             base->Clear();
223             textColorLines.Clear();
224             backColorLines.Clear();
225         }
226         protected override void PaintContent(Graphics& graphicsconst Rect& clipRect)
227         {
228             TextRenderingHint prevRenderingHint = graphics.GetTextRenderingHint();
229             graphics.SetTextRenderingHintChecked(TextRenderingHint.clearTypeGridFit);
230             if (Changed())
231             {
232                 ResetChanged();
233                 SetMaxLineLength();
234                 Measure(graphics);
235             }
236             graphics.ClearChecked(BackgroundColor());
237             const List<ustring>& lines = Lines();
238             int n = cast<int>(lines.Count());
239             Padding padding = GetPadding();
240             PointF origin(padding.leftpadding.top);
241             for (int i = 0; i < n; ++i;)
242             {
243                 if (IsLinePartiallyVisible(i + 1))
244                 {
245                     List<ColorCount>& backColorLine = backColorLines[i];
246                     PaintLineBackground(graphicsbackColorLineorigin);
247                     List<ColorCount>& textColorLine = textColorLines[i];
248                     const ustring& line = lines[i];
249                     DrawLineText(graphicslinetextColorLineorigin);
250                 }
251                 origin.y = origin.y + CharHeight();
252             }
253             graphics.SetTextRenderingHintChecked(prevRenderingHint);
254         }
255         private void PaintLineBackground(Graphics& graphicsconst List<ColorCount>& backColorLineconst PointF& origin)
256         {
257             int n = cast<int>(backColorLine.Count());
258             if (n == 1 && backColorLine[0].color == defaultBackColor) return;
259             PointF loc = origin;
260             for (int i = 0; i < n; ++i;)
261             {
262                 const ColorCount& colorCount = backColorLine[i];
263                 SizeF size(colorCount.count * CharWidth()CharHeight());
264                 if (colorCount.color != defaultBackColor)
265                 {
266                     SolidBrush* brush = GetOrInsertBrush(colorCount.color);
267                     RectF rect(locsize);
268                     graphics.FillRectangleChecked(*brushrect);
269                 }
270                 loc.x = loc.x + size.w;
271             }
272         }
273         private void DrawLineText(Graphics& graphicsconst ustring& lineconst List<ColorCount>& textColorLineconst PointF& origin)
274         {
275             PointF loc = origin;
276             long start = 0;
277             int n = cast<int>(textColorLine.Count());
278             for (int i = 0; i < n; ++i;)
279             {
280                 const ColorCount& colorCount = textColorLine[i];
281                 SolidBrush* brush = GetOrInsertBrush(colorCount.color);
282                 long length = colorCount.count;
283                 ustring s = line.Substring(startlength);
284                 graphics.DrawStringChecked(ToUtf8(s)*Fonts()[0]loc*brush);
285                 loc.x = loc.x + length * CharWidth();
286                 start = start + length;
287             }
288         }
289         private nothrow void IncrementCaretColorCount()
290         {
291             int caretCol = CaretColumn();
292             int caretLine = CaretLine();
293             List<ColorCount>& textColorLine = textColorLines[caretLine - 1];
294             IncrementColorCount(textColorLinecaretCol);
295             List<ColorCount>& backColorLine = backColorLines[caretLine - 1];
296             IncrementColorCount(backColorLinecaretCol);
297         }
298         private nothrow void IncrementColorCount(List<ColorCount>& colorLineint caretCol)
299         {
300             int count = 0;
301             bool incremented = false;
302             for (ColorCount& colorCount : colorLine)
303             {
304                 if (caretCol >= count && caretCol < count + colorCount.count)
305                 {
306                     colorCount.IncrementCount();
307                     incremented = true;
308                     break;
309                 }
310                 count = count + colorCount.count;
311             }
312             if (!incremented)
313             {
314                 colorLine.Back().IncrementCount();
315             }
316         }
317         private nothrow void DecrementCaretColorCount()
318         {
319             int caretCol = CaretColumn();
320             int caretLine = CaretLine();
321             List<ColorCount>& textColorLine = textColorLines[caretLine - 1];
322             DecrementColorCount(textColorLinecaretCol);
323             List<ColorCount>& backColorLine = backColorLines[caretLine - 1];
324             DecrementColorCount(backColorLinecaretCol);
325         }
326         private nothrow void DecrementColorCount(List<ColorCount>& colorLineint caretCol)
327         {
328             int count = 0;
329             bool decremented = false;
330             for (ColorCount& colorCount : colorLine)
331             {
332                 if (caretCol >= count && caretCol < count + colorCount.count)
333                 {
334                     colorCount.DecrementCount();
335                     decremented = true;
336                     break;
337                 }
338                 count = count + colorCount.count;
339             }
340             if (!decremented)
341             {
342                 colorLine.Back().DecrementCount();
343             }
344         }
345         private void OutputChar(System.Color.Constant textColorSystem.Color.Constant backColorint handleuchar c)
346         {
347             List<ustring>& lines = Lines();
348             if (lines.IsEmpty() || c == '\n')
349             {
350                 ustring line;
351                 AddLine(line);
352                 List<ColorCount> colorLine;
353                 textColorLines.Add(colorLine);
354                 backColorLines.Add(colorLine);
355             }
356             if (c != '\n')
357             {
358                 ustring& lastLine = lines.Back();
359                 lastLine.Append(c);
360                 AddColor(textColortextColorLines.Back());
361                 AddColor(backColorbackColorLines.Back());
362                 if (lastLine.Length() > MaxLineLength())
363                 {
364                     SetMaxLineLength();
365                 }
366             }
367         }
368         private nothrow void AddColor(System.Color.Constant colorList<ColorCount>& colorLine)
369         {
370             if (colorLine.IsEmpty() || color != colorLine.Back().color)
371             {
372                 ColorCount colorCount(color1);
373                 colorLine.Add(colorCount);
374             }
375             else
376             {
377                 colorLine.Back().IncrementCount();
378             }
379         }
380         private SolidBrush* GetOrInsertBrush(System.Color.Constant color)
381         {
382             HashMap<sbyteSolidBrush*>.ConstIterator it = brushMap.CFind(cast<sbyte>(color));
383             if (it != brushMap.CEnd())
384             {
385                 return it->second;
386             }
387             else
388             {
389                 SolidBrush* solidBrush = new SolidBrush(GetColor(color));
390                 brushes.Add(UniquePtr<SolidBrush>(solidBrush));
391                 brushMap[cast<sbyte>(color)] = solidBrush;
392                 return solidBrush;
393             }
394         }
395         public inline nothrow System.Color.Constant DefaultBackColor() const
396         {
397             return defaultBackColor;
398         }
399         public nothrow void SetDefaultBackColor(System.Color.Constant color)
400         {
401             if (defaultBackColor != color)
402             {
403                 defaultBackColor = color;
404                 AnsiEngine.Out().SetDefaultBackColor(defaultBackColor);
405                 AnsiEngine.Error().SetDefaultBackColor(defaultBackColor);
406                 SetBackgroundColor(GetColor(defaultBackColor));
407                 Invalidate();
408             }
409         }
410         public inline nothrow System.Color.Constant DefaultTextColor() const
411         {
412             return defaultTextColor;
413         }
414         public nothrow void SetDefaultTextColor(System.Color.Constant color)
415         {
416             if (defaultTextColor != color)
417             {
418                 defaultTextColor = color;
419                 AnsiEngine.Out().SetDefaultTextColor(defaultTextColor);
420                 AnsiEngine.Error().SetDefaultTextColor(defaultTextColor);
421                 Invalidate();
422             }
423         }
424         public nothrow const ustring& InputLine() const
425         {
426             return inputLine;
427         }
428         public nothrow bool Eof() const
429         {
430             return eof;
431         }
432         public void SetEof()
433         {
434             eof = true;
435             OnConsoleInputReady();
436         }
437         protected virtual void OnConsoleInputReady()
438         {
439             consoleInputReadyEvent.Fire();
440         }
441         public nothrow Event<ConsoleInputReadyEventHandler>& ConsoleInputReadyEvent()
442         {
443             return consoleInputReadyEvent;
444         }
445         private System.Color.Constant defaultBackColor;
446         private System.Color.Constant defaultTextColor;
447         private List<List<ColorCount>> textColorLines;
448         private List<List<ColorCount>> backColorLines;
449         private HashMap<sbyteSolidBrush*> brushMap;
450         private List<UniquePtr<SolidBrush>> brushes;
451         private Event<ConsoleInputReadyEventHandler> consoleInputReadyEvent;
452         private ustring inputLine;
453         private bool eof;
454         private int startInputCol;
455     }
456 }