|
@@ -0,0 +1,293 @@
|
|
|
+using System;
|
|
|
+using System.Collections.Generic;
|
|
|
+using System.Linq;
|
|
|
+using System.Text;
|
|
|
+using System.Threading.Tasks;
|
|
|
+
|
|
|
+namespace ConFrames
|
|
|
+{
|
|
|
+ public class ConsoleWindow : Window
|
|
|
+ {
|
|
|
+ public ConsoleWindow(string title, Rect frameRectangle) : base(title, frameRectangle)
|
|
|
+ {
|
|
|
+ _buffer.Add(null);
|
|
|
+ ScrollPos = 0;
|
|
|
+ _prompt = ">";
|
|
|
+ CursorVisible = true;
|
|
|
+ CursorPosition = new Point(_prompt.Length, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ public string Prompt
|
|
|
+ {
|
|
|
+ get { return _prompt; }
|
|
|
+ set { _prompt = value; }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void Write(string str)
|
|
|
+ {
|
|
|
+ if (string.IsNullOrEmpty(str))
|
|
|
+ return;
|
|
|
+
|
|
|
+ str = str.Replace("\r\n", "\n").Replace('\r', '\n');
|
|
|
+ var lines = str.Split('\n');
|
|
|
+
|
|
|
+ bool trailingCR = str.EndsWith("\n");
|
|
|
+ int lineCount = trailingCR ? lines.Length - 1 : lines.Length;
|
|
|
+
|
|
|
+ int oldBufferSize = _buffer.Count;
|
|
|
+
|
|
|
+ for (int i=0; i < lineCount; i++)
|
|
|
+ {
|
|
|
+ int count = _buffer.Count;
|
|
|
+ if (_buffer[count - 1] == null)
|
|
|
+ _buffer[count - 1] = lines[i];
|
|
|
+ else
|
|
|
+ _buffer[count - 1] = _buffer[count - 1] + lines[i];
|
|
|
+
|
|
|
+ if (i < lineCount-1)
|
|
|
+ _buffer.Add(null);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (trailingCR)
|
|
|
+ _buffer.Add(null);
|
|
|
+
|
|
|
+ CursorY += _buffer.Count - oldBufferSize;
|
|
|
+
|
|
|
+ Invalidate();
|
|
|
+ ScrollToBottom();
|
|
|
+ }
|
|
|
+
|
|
|
+ public void WriteLine(string str)
|
|
|
+ {
|
|
|
+ Write(str);
|
|
|
+ Write("\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ public void WriteLine(string str, params object[] args)
|
|
|
+ {
|
|
|
+ WriteLine(string.Format(str, args));
|
|
|
+ }
|
|
|
+
|
|
|
+ public override void OnPaint(PaintContext ctx)
|
|
|
+ {
|
|
|
+ base.OnPaint(ctx);
|
|
|
+
|
|
|
+ var height = ClientSize.Height;
|
|
|
+ if (_prompt!=null)
|
|
|
+ height--;
|
|
|
+
|
|
|
+ int firstLine = ScrollPos;
|
|
|
+ int lastLine = ScrollPos + height + 1;
|
|
|
+
|
|
|
+ if (lastLine > TrimmedBufferLines)
|
|
|
+ lastLine = TrimmedBufferLines;
|
|
|
+
|
|
|
+ if (firstLine < _buffer.Count)
|
|
|
+ {
|
|
|
+ for (int i=firstLine; i<lastLine; i++)
|
|
|
+ {
|
|
|
+ var line = _buffer[i];
|
|
|
+ ctx.WriteLine(line == null ? "" : line);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (lastLine == TrimmedBufferLines && _prompt!= null)
|
|
|
+ {
|
|
|
+ ctx.Write(_prompt);
|
|
|
+ ctx.WriteLine(_inputBuffer);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ int _scrollPos;
|
|
|
+ public int ScrollPos
|
|
|
+ {
|
|
|
+ get
|
|
|
+ {
|
|
|
+ return _scrollPos;
|
|
|
+ }
|
|
|
+ set
|
|
|
+ {
|
|
|
+ if (value < 0)
|
|
|
+ value = 0;
|
|
|
+ if (value > TrimmedBufferLines)
|
|
|
+ value = TrimmedBufferLines;
|
|
|
+
|
|
|
+ if (_scrollPos == value)
|
|
|
+ return;
|
|
|
+
|
|
|
+ CursorY += _scrollPos - value;
|
|
|
+ _scrollPos = value;
|
|
|
+ Invalidate();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ List<string> _buffer = new List<string>();
|
|
|
+ string _prompt = null;
|
|
|
+
|
|
|
+ string _inputBuffer = "";
|
|
|
+
|
|
|
+ void RedrawPrompt()
|
|
|
+ {
|
|
|
+ var ctx = GetPaintContext();
|
|
|
+
|
|
|
+ ctx.ClearLineOnReturn = true;
|
|
|
+ ctx.Position = new Point(0, TrimmedBufferLines - ScrollPos);
|
|
|
+ ctx.Write(_prompt);
|
|
|
+ ctx.WriteLine(_inputBuffer);
|
|
|
+ }
|
|
|
+
|
|
|
+ int TrimmedBufferLines
|
|
|
+ {
|
|
|
+ get
|
|
|
+ {
|
|
|
+ int count = _buffer.Count;
|
|
|
+ if (_buffer[count - 1] == null)
|
|
|
+ count--;
|
|
|
+ return count;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ void ScrollToBottom()
|
|
|
+ {
|
|
|
+ ScrollPos = _buffer.Count - ClientSize.Height;
|
|
|
+ if (ScrollPos < 0)
|
|
|
+ ScrollPos = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ public override bool OnKey(ConsoleKeyInfo key)
|
|
|
+ {
|
|
|
+ int posInBuffer = CursorX - _prompt.Length;
|
|
|
+
|
|
|
+ switch (key.Key)
|
|
|
+ {
|
|
|
+ case ConsoleKey.LeftArrow:
|
|
|
+ if (posInBuffer > 0)
|
|
|
+ CursorX--;
|
|
|
+ ScrollToBottom();
|
|
|
+ return true;
|
|
|
+
|
|
|
+ case ConsoleKey.RightArrow:
|
|
|
+ if (posInBuffer < _inputBuffer.Length)
|
|
|
+ CursorX++;
|
|
|
+ ScrollToBottom();
|
|
|
+ return true;
|
|
|
+
|
|
|
+ case ConsoleKey.UpArrow:
|
|
|
+ if (key.Modifiers == ConsoleModifiers.Shift)
|
|
|
+ ScrollPos--;
|
|
|
+ else
|
|
|
+ OnCommandHistory(-1);
|
|
|
+ return true;
|
|
|
+
|
|
|
+ case ConsoleKey.DownArrow:
|
|
|
+ if (key.Modifiers == ConsoleModifiers.Shift)
|
|
|
+ ScrollPos++;
|
|
|
+ else
|
|
|
+ OnCommandHistory(1);
|
|
|
+ return true;
|
|
|
+
|
|
|
+ case ConsoleKey.Home:
|
|
|
+ CursorX = _prompt.Length;
|
|
|
+ ScrollToBottom();
|
|
|
+ return true;
|
|
|
+
|
|
|
+ case ConsoleKey.End:
|
|
|
+ CursorX = _prompt.Length + _inputBuffer.Length;
|
|
|
+ ScrollToBottom();
|
|
|
+ return true;
|
|
|
+
|
|
|
+ case ConsoleKey.Backspace:
|
|
|
+ if (posInBuffer > 0)
|
|
|
+ {
|
|
|
+ _inputBuffer = _inputBuffer.Substring(0, posInBuffer - 1) + _inputBuffer.Substring(posInBuffer);
|
|
|
+ RedrawPrompt();
|
|
|
+ CursorX--;
|
|
|
+ }
|
|
|
+ ScrollToBottom();
|
|
|
+ return true;
|
|
|
+
|
|
|
+ case ConsoleKey.Delete:
|
|
|
+ if (posInBuffer < _inputBuffer.Length)
|
|
|
+ {
|
|
|
+ _inputBuffer = _inputBuffer.Substring(0, posInBuffer) + _inputBuffer.Substring(posInBuffer + 1);
|
|
|
+ RedrawPrompt();
|
|
|
+ }
|
|
|
+ ScrollToBottom();
|
|
|
+ return true;
|
|
|
+
|
|
|
+ case ConsoleKey.Escape:
|
|
|
+ _inputBuffer = "";
|
|
|
+ RedrawPrompt();
|
|
|
+ CursorX = _prompt.Length;
|
|
|
+ ScrollToBottom();
|
|
|
+ return true;
|
|
|
+
|
|
|
+ case ConsoleKey.Enter:
|
|
|
+ {
|
|
|
+ WriteLine(_prompt + _inputBuffer);
|
|
|
+ string str = _inputBuffer;
|
|
|
+ _inputBuffer = "";
|
|
|
+ CursorX = _prompt.Length;
|
|
|
+ ScrollToBottom();
|
|
|
+ OnCommand(str);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (key.KeyChar!=0)
|
|
|
+ {
|
|
|
+ _inputBuffer = _inputBuffer.Substring(0, posInBuffer) + key.KeyChar + _inputBuffer.Substring(posInBuffer);
|
|
|
+ CursorX++;
|
|
|
+ ScrollToBottom();
|
|
|
+ RedrawPrompt();
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void SetInputBuffer(string str)
|
|
|
+ {
|
|
|
+ _inputBuffer = str;
|
|
|
+ RedrawPrompt();
|
|
|
+ CursorX = _prompt.Length + _inputBuffer.Length;
|
|
|
+ ScrollToBottom();
|
|
|
+ }
|
|
|
+
|
|
|
+ List<string> _commandHistory = new List<String>();
|
|
|
+ int _commandHistoryPos;
|
|
|
+
|
|
|
+ protected virtual void OnCommand(string command)
|
|
|
+ {
|
|
|
+ if (_commandHistory.Count>0)
|
|
|
+ {
|
|
|
+ if (_commandHistory[_commandHistory.Count - 1] == command)
|
|
|
+ {
|
|
|
+ _commandHistoryPos = _commandHistory.Count;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ _commandHistory.Add(command);
|
|
|
+ _commandHistoryPos = _commandHistory.Count;
|
|
|
+ }
|
|
|
+
|
|
|
+ void OnCommandHistory(int delta)
|
|
|
+ {
|
|
|
+ int newPos = _commandHistoryPos + delta;
|
|
|
+ if (newPos < 0)
|
|
|
+ return;
|
|
|
+ if (newPos >= _commandHistory.Count)
|
|
|
+ {
|
|
|
+ SetInputBuffer("");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ _commandHistoryPos = newPos;
|
|
|
+ SetInputBuffer(_commandHistory[newPos]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|