diff --git a/ShiftOS.Frontend/Apps/Terminal.cs b/ShiftOS.Frontend/Apps/Terminal.cs
new file mode 100644
index 0000000..22e0d12
--- /dev/null
+++ b/ShiftOS.Frontend/Apps/Terminal.cs
@@ -0,0 +1,274 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using ShiftOS.Engine;
+using ShiftOS.Frontend.GraphicsSubsystem;
+using static ShiftOS.Engine.SkinEngine;
+
+namespace ShiftOS.Frontend.Apps
+{
+ [FileHandler("Shell script", ".trm", "fileicontrm")]
+ [Launcher("{TITLE_TERMINAL}", false, null, "{AL_UTILITIES}")]
+ [WinOpen("{WO_TERMINAL}")]
+ [DefaultTitle("{TITLE_TERMINAL}")]
+ [DefaultIcon("iconTerminal")]
+ public class Terminal : GUI.Control, IShiftOSWindow
+ {
+ private TerminalControl _terminal = null;
+
+ public Terminal()
+ {
+ Width = 493;
+ Height = 295;
+ }
+
+ public void OnLoad()
+ {
+ _terminal = new Apps.TerminalControl();
+ _terminal.Dock = GUI.DockStyle.Fill;
+ AddControl(_terminal);
+ _terminal.Layout();
+ AppearanceManager.ConsoleOut = _terminal;
+ AppearanceManager.StartConsoleOut();
+ }
+
+ protected override void OnLayout()
+ {
+ if (ContainsFocusedControl || IsFocusedControl)
+ AppearanceManager.ConsoleOut = _terminal;
+ }
+
+ public void OnSkinLoad()
+ {
+
+ }
+
+ public bool OnUnload()
+ {
+ return true;
+ }
+
+ public void OnUpgrade()
+ {
+ }
+ }
+
+ public class TerminalControl : GUI.TextInput, ITerminalWidget
+ {
+ public string[] Lines
+ {
+ get
+ {
+ return Text.Split(new[] { "\n" }, StringSplitOptions.None);
+
+ }
+ }
+
+ public void Clear()
+ {
+ Text = "";
+ Index = 0;
+ Invalidate();
+ }
+
+ public void SelectBottom()
+ {
+ Index = Text.Length - 1;
+ RecalculateLayout();
+ InvalidateTopLevel();
+ }
+
+
+
+ public void Write(string text)
+ {
+ Engine.Desktop.InvokeOnWorkerThread(() =>
+ {
+ SelectBottom();
+ Text = Text.Insert(Index, text);
+ Index += text.Length;
+ RecalculateLayout();
+ InvalidateTopLevel();
+ });
+ }
+
+ public void WriteLine(string text)
+ {
+ Write(text + Environment.NewLine);
+ }
+
+
+ public int GetCurrentLine()
+ {
+ int line = 0;
+ for(int i = 0; i < Text.Length; i++)
+ {
+ if(Text[i]=='\n')
+ {
+ line++;
+ continue;
+ }
+ if (i == Index)
+ return line;
+ }
+ return 0;
+ }
+
+ float _vertOffset = 0.0f;
+
+ protected void RecalculateLayout()
+ {
+ if(!string.IsNullOrEmpty(Text))
+ using (var gfx = Graphics.FromImage(new Bitmap(1, 1)))
+ {
+ var cursorpos = GetPointAtIndex(gfx);
+ var caretSize = gfx.SmartMeasureString(Text.ToString(), LoadedSkin.TerminalFont, Width - 4);
+ float initial = ((caretSize.Height) - cursorpos.Y) - _vertOffset;
+ if (initial < 0)
+ {
+ float difference = initial - Height;
+ _vertOffset += initial + difference;
+ }
+ if (initial > Height)
+ {
+ float difference = initial - Height;
+ _vertOffset -= initial - difference;
+ }
+
+ }
+ }
+
+ ///
+ /// Gets the X and Y coordinates (in pixels) of the caret.
+ ///
+ /// A object used for font measurements
+ /// An absolute fucking mess. Seriously, can someone fix this method so it uhh WORKS PROPERLY?
+ public PointF GetPointAtIndex(Graphics gfx)
+ {
+ float vertMeasure = 2.0f;
+ float horizMeasure = 2.0f;
+ int lineindexes = 0;
+ for (int l = 0; l <= GetCurrentLine(); l++)
+ {
+ var measure = gfx.SmartMeasureString(Lines[l], LoadedSkin.TerminalFont, Width - 4);
+ vertMeasure += measure.Width;
+ if(l == GetCurrentLine())
+ {
+ string _linetext = Text.Substring(lineindexes, Index - lineindexes);
+ var lMeasure = gfx.SmartMeasureString(_linetext, LoadedSkin.TerminalFont);
+ horizMeasure = lMeasure.Width;
+ if (horizMeasure > Width - 4)
+ horizMeasure -= (Width-4);
+ }
+ else
+ {
+ lineindexes += Lines[l].Length;
+ }
+ }
+ return new PointF(horizMeasure, vertMeasure);
+ }
+
+ protected override void OnKeyEvent(KeyEvent e)
+ {
+ if (e.Key == Microsoft.Xna.Framework.Input.Keys.Enter)
+ {
+ Text = Text.Insert(Index, "\r\n");
+ Index++;
+ }
+ base.OnKeyEvent(e);
+ RecalculateLayout();
+ InvalidateTopLevel();
+ }
+
+ protected override void OnPaint(Graphics gfx)
+ {
+ RecalculateLayout();
+ gfx.Clear(LoadedSkin.TerminalBackColorCC.ToColor());
+ if (!string.IsNullOrEmpty(Text))
+ {
+ //Draw the caret.
+ var caretPos = GetPointAtIndex(gfx);
+ var caretSize = gfx.SmartMeasureString(Text[Index - 1].ToString(), LoadedSkin.TerminalFont);
+ if (IsFocusedControl)
+ {
+ gfx.FillRectangle(new SolidBrush(LoadedSkin.TerminalForeColorCC.ToColor()), new RectangleF(new PointF(caretPos.X, caretPos.Y - _vertOffset), new SizeF(2, caretSize.Height)));
+ }//Draw the text
+ var textMeasure = gfx.MeasureString(Text, LoadedSkin.TerminalFont, Width - 4);
+ gfx.DrawString(Text, LoadedSkin.TerminalFont, new SolidBrush(LoadedSkin.TerminalForeColorCC.ToColor()), 2, 2 - _vertOffset);
+
+
+ }
+ }
+
+ }
+
+ public static class ConsoleColorExtensions
+ {
+ public static Color ToColor(this ConsoleColor cc)
+ {
+ switch (cc)
+ {
+ case ConsoleColor.Black:
+ return Color.Black;
+ case ConsoleColor.Blue:
+ return Color.Blue;
+ case ConsoleColor.Cyan:
+ return Color.Cyan;
+ case ConsoleColor.DarkBlue:
+ return Color.DarkBlue;
+ case ConsoleColor.DarkCyan:
+ return Color.DarkCyan;
+ case ConsoleColor.DarkGray:
+ return Color.DarkGray;
+ case ConsoleColor.DarkGreen:
+ return Color.DarkGreen;
+ case ConsoleColor.DarkMagenta:
+ return Color.DarkMagenta;
+ case ConsoleColor.DarkRed:
+ return Color.DarkRed;
+ case ConsoleColor.DarkYellow:
+ return Color.Orange;
+ case ConsoleColor.Gray:
+ return Color.Gray;
+ case ConsoleColor.Green:
+ return Color.Green;
+ case ConsoleColor.Magenta:
+ return Color.Magenta;
+ case ConsoleColor.Red:
+ return Color.Red;
+ case ConsoleColor.White:
+ return Color.White;
+ case ConsoleColor.Yellow:
+ return Color.Yellow;
+ }
+ return Color.Empty;
+ }
+ }
+
+ public static class GraphicsExtensions
+ {
+ public static SizeF SmartMeasureString(this Graphics gfx, string s, Font font, int width)
+ {
+ if (string.IsNullOrEmpty(s))
+ s = " ";
+ var textformat = new StringFormat(StringFormat.GenericTypographic);
+ textformat.FormatFlags = StringFormatFlags.MeasureTrailingSpaces;
+ textformat.Trimming = StringTrimming.None;
+ return gfx.MeasureString(s, font, width, textformat);
+ }
+
+ public static SizeF SmartMeasureString(this Graphics gfx, string s, Font font)
+ {
+ if (string.IsNullOrEmpty(s))
+ s = " ";
+ var textformat = new StringFormat(StringFormat.GenericTypographic);
+ textformat.FormatFlags = StringFormatFlags.MeasureTrailingSpaces;
+ textformat.Trimming = StringTrimming.None;
+ return gfx.MeasureString(s, font, int.MaxValue, textformat);
+ }
+
+ }
+}
diff --git a/ShiftOS.Frontend/Desktop/Desktop.cs b/ShiftOS.Frontend/Desktop/Desktop.cs
index 5bcf3a9..ffb41e8 100644
--- a/ShiftOS.Frontend/Desktop/Desktop.cs
+++ b/ShiftOS.Frontend/Desktop/Desktop.cs
@@ -5,6 +5,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ShiftOS.Engine;
+using ShiftOS.Frontend.GraphicsSubsystem;
namespace ShiftOS.Frontend.Desktop
{
@@ -35,7 +36,7 @@ namespace ShiftOS.Frontend.Desktop
public void InvokeOnWorkerThread(Action act)
{
- act?.Invoke();
+ UIManager.CrossThreadOperations.Enqueue(act);
}
public void KillWindow(IWindowBorder border)
diff --git a/ShiftOS.Frontend/Desktop/WindowManager.cs b/ShiftOS.Frontend/Desktop/WindowManager.cs
index 8e22569..4f99a05 100644
--- a/ShiftOS.Frontend/Desktop/WindowManager.cs
+++ b/ShiftOS.Frontend/Desktop/WindowManager.cs
@@ -26,7 +26,7 @@ namespace ShiftOS.Frontend.Desktop
public override void InvokeAction(Action act)
{
- act?.Invoke();
+ UIManager.CrossThreadOperations.Enqueue(act);
}
public override void Maximize(IWindowBorder border)
diff --git a/ShiftOS.Frontend/GUI/TextControl.cs b/ShiftOS.Frontend/GUI/TextControl.cs
index 9bf240f..9bc70e8 100644
--- a/ShiftOS.Frontend/GUI/TextControl.cs
+++ b/ShiftOS.Frontend/GUI/TextControl.cs
@@ -92,7 +92,6 @@ namespace ShiftOS.Frontend.GUI
}
- base.OnPaint(gfx);
gfx.DrawString(_text, _font, new SolidBrush(Engine.SkinEngine.LoadedSkin.ControlTextColor), new RectangleF(loc.X, loc.Y, sMeasure.Width, sMeasure.Height));
}
}
diff --git a/ShiftOS.Frontend/GUI/TextInput.cs b/ShiftOS.Frontend/GUI/TextInput.cs
index e59e927..c4e5260 100644
--- a/ShiftOS.Frontend/GUI/TextInput.cs
+++ b/ShiftOS.Frontend/GUI/TextInput.cs
@@ -17,6 +17,48 @@ namespace ShiftOS.Frontend.GUI
private int _index = 0;
private Font _font = new Font("Tahoma", 9f);
+ public int Index
+ {
+ get
+ {
+ return _index;
+ }
+ set
+ {
+ if (_index == value)
+ return;
+ if(_text.Length == 0)
+ {
+ _index = 0;
+ return;
+ }
+ _index = MathHelper.Clamp(value, 0, _text.Length - 1);
+ if (_text[_index] == '\n')
+ _index++;
+ Invalidate();
+ }
+ }
+
+ public string Text
+ {
+ get
+ {
+ return _text;
+ }
+ set
+ {
+ if (_text == value)
+ return;
+
+ _text = value;
+ if(_index >= _text.Length)
+ {
+ _index = _text.Length - 1;
+ }
+ Invalidate();
+ }
+ }
+
protected override void OnKeyEvent(KeyEvent e)
{
if(e.Key == Microsoft.Xna.Framework.Input.Keys.Left)
diff --git a/ShiftOS.Frontend/GraphicsSubsystem/UIManager.cs b/ShiftOS.Frontend/GraphicsSubsystem/UIManager.cs
index 046e53c..bb62ea4 100644
--- a/ShiftOS.Frontend/GraphicsSubsystem/UIManager.cs
+++ b/ShiftOS.Frontend/GraphicsSubsystem/UIManager.cs
@@ -85,6 +85,8 @@ namespace ShiftOS.Frontend.GraphicsSubsystem
private static Texture2D DesktopBackground = null;
+ public static Queue CrossThreadOperations = new Queue();
+
public static void DrawBackgroundLayer(GraphicsDevice graphics, SpriteBatch batch, int width, int height)
{
if (SkinEngine.LoadedSkin == null)
diff --git a/ShiftOS.Frontend/Infobox.cs b/ShiftOS.Frontend/Infobox.cs
index 53b4857..3f47b54 100644
--- a/ShiftOS.Frontend/Infobox.cs
+++ b/ShiftOS.Frontend/Infobox.cs
@@ -79,10 +79,15 @@ namespace ShiftOS.Frontend
public void ShowText(Action callback)
{
- Title = "Not yet implemented.";
- lbmessage.Text = "This feature hasn't yet been implemented.";
- ShowPrompt(null);
-
+ AppearanceManager.SetupDialog(this);
+ flyesno.Visible = false;
+ btnok.Visible = true;
+ txtinput.Visible = true;
+ btnok.Click += () =>
+ {
+ callback?.Invoke(txtinput.Text);
+ AppearanceManager.Close(this);
+ };
}
public void OnSkinLoad()
diff --git a/ShiftOS.Frontend/ShiftOS.Frontend.csproj b/ShiftOS.Frontend/ShiftOS.Frontend.csproj
index 61ff0c6..99f2081 100644
--- a/ShiftOS.Frontend/ShiftOS.Frontend.csproj
+++ b/ShiftOS.Frontend/ShiftOS.Frontend.csproj
@@ -42,6 +42,7 @@
app.manifest
+
diff --git a/ShiftOS.Frontend/ShiftOS.cs b/ShiftOS.Frontend/ShiftOS.cs
index 833ee59..fd3d784 100644
--- a/ShiftOS.Frontend/ShiftOS.cs
+++ b/ShiftOS.Frontend/ShiftOS.cs
@@ -113,10 +113,15 @@ Reflection manager found {ReflectMan.Types.Count()} Common Language Runtime type
statslabel.Layout();
};
- //We'll use sandbox mode
- SaveSystem.IsSandbox = true;
+ TerminalBackend.TerminalRequested += () =>
+ {
+ AppearanceManager.SetupWindow(new Apps.Terminal());
+ };
- SaveSystem.Begin();
+ //We'll use sandbox mode
+ SaveSystem.IsSandbox = false;
+
+ SaveSystem.Begin(true);
var textinput = new GUI.TextInput();
textinput.Width = 250;
@@ -174,6 +179,12 @@ Reflection manager found {ReflectMan.Types.Count()} Common Language Runtime type
/// Provides a snapshot of timing values.
protected override void Update(GameTime gameTime)
{
+ if (UIManager.CrossThreadOperations.Count > 0)
+ {
+ var action = UIManager.CrossThreadOperations.Dequeue();
+ action?.Invoke();
+ }
+
//Let's get the mouse state
var mouseState = Mouse.GetState(this.Window);