diff options
Diffstat (limited to 'ShiftOS.Frontend/GUI')
| -rw-r--r-- | ShiftOS.Frontend/GUI/Button.cs | 49 | ||||
| -rw-r--r-- | ShiftOS.Frontend/GUI/Control.cs | 642 | ||||
| -rw-r--r-- | ShiftOS.Frontend/GUI/ItemGroup.cs | 64 | ||||
| -rw-r--r-- | ShiftOS.Frontend/GUI/PictureBox.cs | 146 | ||||
| -rw-r--r-- | ShiftOS.Frontend/GUI/ProgressBar.cs | 57 | ||||
| -rw-r--r-- | ShiftOS.Frontend/GUI/TextControl.cs | 112 | ||||
| -rw-r--r-- | ShiftOS.Frontend/GUI/TextInput.cs | 89 |
7 files changed, 1159 insertions, 0 deletions
diff --git a/ShiftOS.Frontend/GUI/Button.cs b/ShiftOS.Frontend/GUI/Button.cs new file mode 100644 index 0000000..551d0d4 --- /dev/null +++ b/ShiftOS.Frontend/GUI/Button.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ShiftOS.Engine; + +namespace ShiftOS.Frontend.GUI +{ + public class Button : TextControl + { + public Button() + { + TextAlign = TextAlign.MiddleCenter; + Text = "Click me!"; + } + + protected override void OnLayout() + { + if(AutoSize == true) + { + int borderwidth = SkinEngine.LoadedSkin.ButtonBorderWidth * 2; + + using (var gfx = Graphics.FromImage(new Bitmap(1, 1))) + { + var measure = gfx.MeasureString(this.Text, this.Font); + Width = borderwidth + (int)measure.Width + 16; + Height = borderwidth + (int)measure.Height + 12; + } + } + } + + protected override void OnPaint(Graphics gfx) + { + Color bgCol = SkinEngine.LoadedSkin.ButtonBackgroundColor; + Color fgCol = SkinEngine.LoadedSkin.ControlTextColor; + if (ContainsMouse) + bgCol = SkinEngine.LoadedSkin.ButtonHoverColor; + if (MouseLeftDown) + bgCol = SkinEngine.LoadedSkin.ButtonPressedColor; + + gfx.Clear(bgCol); + gfx.DrawRectangle(new Pen(new SolidBrush(fgCol), SkinEngine.LoadedSkin.ButtonBorderWidth), new Rectangle(0, 0, Width, Height)); + base.OnPaint(gfx); + + } + } +} diff --git a/ShiftOS.Frontend/GUI/Control.cs b/ShiftOS.Frontend/GUI/Control.cs new file mode 100644 index 0000000..90e7ba5 --- /dev/null +++ b/ShiftOS.Frontend/GUI/Control.cs @@ -0,0 +1,642 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; +using System.Drawing; +using ShiftOS.Frontend.GraphicsSubsystem; +using System.Drawing.Imaging; +using System.Drawing.Drawing2D; +using Microsoft.Xna.Framework; + +namespace ShiftOS.Frontend.GUI +{ + public abstract class Control + { + private int _x = 0; + private int _y = 0; + private int _w = 0; + private int _h = 0; + private Control _parent = null; + private List<Control> _children = new List<Control>(); + private bool _wasMouseInControl = false; + private bool _leftState = false; + private bool _rightState = false; + private bool _middleState = false; + private bool _visible = true; + private DockStyle _dock = DockStyle.None; + private bool _focused = false; + private bool _autoSize = false; + private double _opacity = 1.0; + private bool _invalidated = true; + private Bitmap _texCache = null; + private Anchor _anchor = null; + private int _mouseX = 0; + private int _mouseY = 0; + private bool _captureMouse = false; + + public bool CaptureMouse + { + get + { + return _captureMouse; + } + set + { + _captureMouse = value; + } + } + + public int MouseX + { + get + { + return _mouseX; + } + } + + public int MouseY + { + get + { + return _mouseY; + } + } + + + public Anchor Anchor + { + get + { + return _anchor; + } + set + { + _anchor = value; + Invalidate(); + } + } + + public void Invalidate() + { + _invalidated = true; + foreach(var child in _children) + { + child.Invalidate(); + } + } + + public double Opacity + { + get + { + return _opacity; + } + set + { + _opacity = value; + Invalidate(); + } + } + + public bool AutoSize + { + get + { + return _autoSize; + } + set + { + _autoSize = value; + } + } + + //Thank you, StackOverflow. + public static Bitmap ResizeImage(Image image, int width, int height) + { + var destRect = new System.Drawing.Rectangle(0, 0, width, height); + var destImage = new Bitmap(width, height); + + destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution); + + using (var graphics = Graphics.FromImage(destImage)) + { + graphics.CompositingMode = CompositingMode.SourceCopy; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + + using (var wrapMode = new ImageAttributes()) + { + wrapMode.SetWrapMode(WrapMode.TileFlipXY); + graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode); + } + } + + return destImage; + } + + public DockStyle Dock + { + get + { + return _dock; + } + set + { + _dock = value; + } + } + + public bool ContainsMouse + { + get { return _wasMouseInControl; } + } + + public bool Visible + { + get + { + return _visible; + } + set + { + _visible = value; + Invalidate(); + } + } + + public void AddControl(Control ctrl) + { + if (!_children.Contains(ctrl)) + { + ctrl._parent = this; + _children.Add(ctrl); + Invalidate(); + } + } + + public bool MouseLeftDown + { + get + { + return _leftState; + } + } + + public bool MouseMiddleDown + { + get + { + return _middleState; + } + } + + public bool MouseRightDown + { + get + { + return _rightState; + } + } + + + + public int X + { + get + { + return _x; + } + set + { + _x = value; + Invalidate(); + } + } + + public int Y + { + get + { + return _y; + } + set + { + _y = value; + Invalidate(); + } + } + + public int Width + { + get + { + return _w; + } + set + { + _w = value; + Invalidate(); + } + } + + public int Height + { + get + { + return _h; + } + set + { + _h = value; + Invalidate(); + } + } + + public Control Parent + { + get + { + return _parent; + } + } + + public Control[] Children + { + get + { + return _children.ToArray(); + } + } + + public Point PointToParent(int x, int y) + { + return new Point(x + _x, y + _y); + } + + public Point PointToScreen(int x, int y) + { + var parentCoords = PointToParent(x, y); + Control parent = this._parent; + while(parent != null) + { + parentCoords = parent.PointToParent(parentCoords.X, parentCoords.Y); + parent = parent.Parent; + } + return parentCoords; + } + + public void ClearControls() + { + _children.Clear(); + Invalidate(); + } + + public Point PointToLocal(int x, int y) + { + return new GUI.Point(x - _x, y - _y); + } + + public virtual void MouseStateChanged() { } + + protected virtual void OnPaint(Graphics gfx) + { + + } + + public void InvalidateTopLevel() + { + var parent = this; + while (parent.Parent != null) + parent = parent.Parent; + parent.Invalidate(); + } + + public void Paint(System.Drawing.Graphics gfx) + { + if (_visible == true) + { + if (_invalidated) + { + _texCache = new Bitmap(Width, Height); + using (var cGfx = Graphics.FromImage(_texCache)) + { + OnPaint(cGfx); + } + _invalidated = false; + } + gfx.DrawImage(_texCache, 0, 0); + foreach (var child in _children) + { + if (child.Visible) + { + if (child._invalidated) + { + var cBmp = new Bitmap(child.Width, child.Height); + child.Paint(System.Drawing.Graphics.FromImage(cBmp)); + cBmp.SetOpacity((float)child.Opacity); + child._invalidated = false; + child._texCache = cBmp; + gfx.DrawImage(cBmp, new System.Drawing.Point(child.X, child.Y)); + + + + } + else + { + gfx.DrawImage(child._texCache, child.X, child.Y); + } + } + } + + } + } + + public void Layout() + { + //Dock style + if(_parent != null) + { + if(_anchor != null) + { + + } + + switch (_dock) + { + case DockStyle.Top: + X = 0; + Y = 0; + Width = _parent.Width; + break; + case DockStyle.Left: + X = 0; + Y = 0; + Height = _parent.Height; + break; + case DockStyle.Right: + Y = 0; + X = _parent.Width - Width; + Height = _parent.Height; + break; + case DockStyle.Bottom: + X = 0; + Y = _parent.Height - Height; + Width = _parent.Width; + break; + case DockStyle.Fill: + X = 0; + Y = 0; + Width = _parent.Width; + Height = _parent.Height; + break; + } + } + OnLayout(); + foreach (var child in _children) + child.Layout(); + } + + protected virtual void OnLayout() + { + //do nothing + } + + public bool IsFocusedControl + { + get + { + return UIManager.FocusedControl == this; + } + } + + public bool ContainsFocusedControl + { + get + { + if (UIManager.FocusedControl == null) + return false; + else + { + bool contains = false; + + var ctrl = UIManager.FocusedControl; + while(ctrl.Parent != null) + { + ctrl = ctrl.Parent; + if (ctrl == this) + contains = true; + } + return contains; + } + } + } + + public virtual bool ProcessMouseState(MouseState state) + { + //If we aren't rendering the control, we aren't accepting input. + if (_visible == false) + return false; + + + //Firstly, we get the mouse coordinates in the local space + var coords = PointToLocal(state.Position.X, state.Position.Y); + _mouseX = coords.X; + _mouseY = coords.Y; + //Now we check if the mouse is within the bounds of the control + if(coords.X >= 0 && coords.Y >= 0 && coords.X <= _w && coords.Y <= _h) + { + //We're in the local space. Let's fire the MouseMove event. + MouseMove?.Invoke(coords); + //Also, if the mouse hasn't been in the local space last time it moved, fire MouseEnter. + if(_wasMouseInControl == false) + { + _wasMouseInControl = true; + MouseEnter?.Invoke(); + Invalidate(); + } + + //Things are going to get a bit complicated. + //Firstly, we need to find out if we have any children. + bool _requiresMoreWork = true; + if(_children.Count > 0) + { + //We do. We're going to iterate through them all and process the mouse state. + foreach(var control in _children) + { + + //If the process method returns true, then we do not need to do anything else on our end. + + //We need to first create a new mousestate object with the new coordinates + + var nstate = new MouseState(coords.X, coords.Y, state.ScrollWheelValue, state.LeftButton, state.MiddleButton, state.RightButton, state.XButton1, state.XButton2); + //pass that state to the process method, and set the _requiresMoreWork value to the opposite of the return value + _requiresMoreWork = !control.ProcessMouseState(nstate); + //If it's false, break the loop. + if (_requiresMoreWork == false) + break; + } + } + + //If we need to do more work... + if(_requiresMoreWork == true) + { + bool fire = false; //so we know to fire a MouseStateChanged method + //Let's get the state values of each button + bool ld = state.LeftButton == ButtonState.Pressed; + bool md = state.MiddleButton == ButtonState.Pressed; + bool rd = state.RightButton == ButtonState.Pressed; + if(ld != _leftState || md != _middleState || rd != _rightState) + { + fire = true; + } + if (_leftState == true && ld == false) + { + Click?.Invoke(); + Invalidate(); + } + if (_leftState == false && ld == true) + { + var focused = UIManager.FocusedControl; + UIManager.FocusedControl = this; + focused?.InvalidateTopLevel(); + InvalidateTopLevel(); + } + _leftState = ld; + _middleState = md; + _rightState = rd; + if (fire) + MouseStateChanged(); + } + return true; + } + else + { + _leftState = false; + _rightState = false; + _middleState = false; + MouseStateChanged(); + //If the mouse was in local space before, fire MouseLeave + if (_wasMouseInControl == true) + { + if (CaptureMouse == true) + { + _wasMouseInControl = true; + int newX = MathHelper.Clamp(state.X, X, X + Width); + int newY = MathHelper.Clamp(state.Y, Y, Y + Height); + Mouse.SetPosition(newX, newY); + + } + else + { + _wasMouseInControl = false; + MouseLeave?.Invoke(); + Invalidate(); + } + } + } + if (CaptureMouse == true) + { + _mouseX = coords.X; + _mouseY = coords.Y; + Layout(); + _wasMouseInControl = true; + int newX = MathHelper.Clamp(state.X, X, X + Width); + int newY = MathHelper.Clamp(state.Y, Y, Y + Height); + Mouse.SetPosition(newX, newY); + return true; + } + + //Mouse is not in the local space, don't do anything. + return false; + } + + protected virtual void OnKeyEvent(KeyEvent e) + { + + } + + public void ProcessKeyEvent(KeyEvent e) + { + OnKeyEvent(e); + KeyEvent?.Invoke(e); + } + + public event Action<Point> MouseMove; + public event Action MouseEnter; + public event Action MouseLeave; + public event Action Click; + public event Action<KeyEvent> KeyEvent; + } + + public struct Point + { + public Point(int x, int y) + { + X = x; + Y = y; + } + + public int X { get; set; } + public int Y { get; set; } + } + + public enum DockStyle + { + None, + Top, + Bottom, + Left, + Right, + Fill + } + + //Thanks, StackOverflow. + public static class BitmapExtensions + { + public static Image SetOpacity(this Image image, float opacity) + { + var colorMatrix = new ColorMatrix(); + colorMatrix.Matrix33 = opacity; + var imageAttributes = new ImageAttributes(); + imageAttributes.SetColorMatrix( + colorMatrix, + ColorMatrixFlag.Default, + ColorAdjustType.Bitmap); + var output = new Bitmap(image.Width, image.Height); + using (var gfx = Graphics.FromImage(output)) + { + gfx.SmoothingMode = SmoothingMode.AntiAlias; + gfx.DrawImage( + image, + new System.Drawing.Rectangle(0, 0, image.Width, image.Height), + 0, + 0, + image.Width, + image.Height, + GraphicsUnit.Pixel, + imageAttributes); + } + return output; + } + } + + [Flags] + public enum AnchorStyle + { + Top, + Left, + Bottom, + Right + } + + public class Anchor + { + public AnchorStyle Style { get; set; } + public int Distance { get; set; } + } +} diff --git a/ShiftOS.Frontend/GUI/ItemGroup.cs b/ShiftOS.Frontend/GUI/ItemGroup.cs new file mode 100644 index 0000000..e52a17f --- /dev/null +++ b/ShiftOS.Frontend/GUI/ItemGroup.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ShiftOS.Frontend.GUI +{ + public class ItemGroup : Control + { + private int _gap = 3; + private FlowDirection _flowDir = FlowDirection.LeftToRight; + private int _initialgap = 2; + + protected override void OnLayout() + { + if (AutoSize) + { + int _highesty = _initialgap; + int _xx = _initialgap; + foreach(var ctrl in Children) + { + _xx += ctrl.Width + _gap; + if (_highesty < ctrl.Height + _initialgap + _gap) + _highesty = ctrl.Height + _initialgap + _gap; + } + Width = _xx; + Height = _highesty; + } + + int _x = _initialgap; + int _y = _initialgap; + int _maxYForRow = 0; + foreach (var ctrl in Children) + { + if (_x + ctrl.Width + _gap > Width) + { + _x = _initialgap; + _y = _maxYForRow; + _maxYForRow = 0; + if (_maxYForRow < ctrl.Height + _gap) + _maxYForRow = ctrl.Height + _gap; + } + ctrl.X = _x; + ctrl.Y = _y; + ctrl.Dock = DockStyle.None; + ctrl.Layout(); + _x += ctrl.Width + _gap; + + if (_maxYForRow < ctrl.Height + _gap) + _maxYForRow = ctrl.Height + _gap; + + } + } + } + + public enum FlowDirection + { + LeftToRight, + TopDown, + RightToLeft, + BottomUp + } +} diff --git a/ShiftOS.Frontend/GUI/PictureBox.cs b/ShiftOS.Frontend/GUI/PictureBox.cs new file mode 100644 index 0000000..9a234d4 --- /dev/null +++ b/ShiftOS.Frontend/GUI/PictureBox.cs @@ -0,0 +1,146 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ShiftOS.Engine; +using System.Drawing.Imaging; + +namespace ShiftOS.Frontend.GUI +{ + public class PictureBox : Control + { + private System.Drawing.Image img = null; + private ImageLayout _layout = ImageLayout.Fit; + + public ImageLayout ImageLayout + { + get + { + return _layout; + } + set + { + _layout = value; + } + } + + public System.Drawing.Image Image + { + get + { + return img; + } + set + { + if (img != null) + img.Dispose(); + img = value; + } + } + + protected override void OnLayout() + { + if (AutoSize) + { + Width = (img == null) ? 0 : img.Width; + Height = (img == null) ? 0 : img.Height; + } + } + + protected override void OnPaint(Graphics gfx) + { + if(img != null) + switch (_layout) + { + case ImageLayout.None: + //Just draw the image. + gfx.DrawImage(img, new PointF(0, 0)); + break; + case ImageLayout.Stretch: + //Stretch the image, with no regard for aspect ratio. + var stretched = ResizeImage(img, Width, Height); + gfx.DrawImage(stretched, 0, 0); + break; + case ImageLayout.Fit: + //Resize image to fit the control but keep aspect ratio. + var fitted = FixedSize(img, Width, Height); + gfx.DrawImage(fitted, 0, 0); + break; + case ImageLayout.Tile: + //Keep original size but tile the image. + + for(int x = 0; x < Width; x += img.Width) + { + for (int y = 0; y < Height; y += img.Height) + { + gfx.DrawImage(img, x, y); + } + } + + break; + } + } + + //Again, thanks StackOverflow + static Image FixedSize(Image imgPhoto, int Width, int Height) + { + int sourceWidth = imgPhoto.Width; + int sourceHeight = imgPhoto.Height; + int sourceX = 0; + int sourceY = 0; + int destX = 0; + int destY = 0; + + float nPercent = 0; + float nPercentW = 0; + float nPercentH = 0; + + nPercentW = ((float)Width / (float)sourceWidth); + nPercentH = ((float)Height / (float)sourceHeight); + if (nPercentH < nPercentW) + { + nPercent = nPercentH; + destX = System.Convert.ToInt16((Width - + (sourceWidth * nPercent)) / 2); + } + else + { + nPercent = nPercentW; + destY = System.Convert.ToInt16((Height - + (sourceHeight * nPercent)) / 2); + } + + int destWidth = (int)(sourceWidth * nPercent); + int destHeight = (int)(sourceHeight * nPercent); + + Bitmap bmPhoto = new Bitmap(Width, Height, + PixelFormat.Format24bppRgb); + bmPhoto.SetResolution(imgPhoto.HorizontalResolution, + imgPhoto.VerticalResolution); + + Graphics grPhoto = Graphics.FromImage(bmPhoto); + grPhoto.Clear(SkinEngine.LoadedSkin.ControlColor); + grPhoto.InterpolationMode = + InterpolationMode.HighQualityBicubic; + + grPhoto.DrawImage(imgPhoto, + new Rectangle(destX, destY, destWidth, destHeight), + new Rectangle(sourceX, sourceY, sourceWidth, sourceHeight), + GraphicsUnit.Pixel); + + grPhoto.Dispose(); + return bmPhoto; + } + } + + public enum ImageLayout + { + None, + Stretch, + Tile, + Fit, + } +} diff --git a/ShiftOS.Frontend/GUI/ProgressBar.cs b/ShiftOS.Frontend/GUI/ProgressBar.cs new file mode 100644 index 0000000..e35dc27 --- /dev/null +++ b/ShiftOS.Frontend/GUI/ProgressBar.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static ShiftOS.Engine.SkinEngine; + +namespace ShiftOS.Frontend.GUI +{ + public class ProgressBar : Control + { + private int _maximum = 100; + private int _value = 0; + + public int Maximum + { + get + { + return _maximum; + } + set + { + _maximum = value; + } + } + + public int Value + { + get + { + return _value; + } + set + { + _value = value; + } + } + + protected override void OnPaint(Graphics gfx) + { + gfx.Clear(LoadedSkin.ProgressBarBackgroundColor); + int w = (int)linear(_value, 0, _maximum, 0, Width); + gfx.FillRectangle(new SolidBrush(LoadedSkin.ProgressColor), new Rectangle(0, 0, w, Height)); + } + + static public double linear(double x, double x0, double x1, double y0, double y1) + { + if ((x1 - x0) == 0) + { + return (y0 + y1) / 2; + } + return y0 + (x - x0) * (y1 - y0) / (x1 - x0); + } + + } +} diff --git a/ShiftOS.Frontend/GUI/TextControl.cs b/ShiftOS.Frontend/GUI/TextControl.cs new file mode 100644 index 0000000..37b9ca7 --- /dev/null +++ b/ShiftOS.Frontend/GUI/TextControl.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ShiftOS.Frontend.GUI +{ + public class TextControl : Control + { + private string _text = "Text Control"; + private TextAlign _textAlign = TextAlign.TopLeft; + private Font _font = new Font("Tahoma", 9f); + + protected override void OnLayout() + { + if (AutoSize) + { + using (var bmp = new Bitmap(1, 1)) + { + using(var gfx = Graphics.FromImage(bmp)) + { + var measure = gfx.MeasureString(_text, _font); + Width = (int)measure.Width; + Height = (int)measure.Height; + } + } + } + } + + public string Text + { + get { return _text; } + set { _text = value; } + } + + public Font Font + { + get + { + return _font; + } + set + { + _font = value; + } + } + + public TextAlign TextAlign + { + get { return _textAlign; } + set { _textAlign = value; } + } + + protected override void OnPaint(Graphics gfx) + { + var sMeasure = gfx.MeasureString(_text, _font, Width); + PointF loc = new PointF(2, 2); + float centerH = (Width - sMeasure.Width) / 2; + float centerV = (Height - sMeasure.Height) / 2; + switch (_textAlign) + { + case TextAlign.TopCenter: + loc.X = centerH; + break; + case TextAlign.TopRight: + loc.X = Width - sMeasure.Width; + break; + case TextAlign.MiddleLeft: + loc.Y = centerV; + break; + case TextAlign.MiddleCenter: + loc.Y = centerV; + loc.X = centerH; + break; + case TextAlign.MiddleRight: + loc.Y = centerV; + loc.X = (Width - sMeasure.Width); + break; + case TextAlign.BottomLeft: + loc.Y = (Height - sMeasure.Height); + break; + case TextAlign.BottomCenter: + loc.Y = (Height - sMeasure.Height); + loc.X = centerH; + break; + case TextAlign.BottomRight: + loc.Y = (Height - sMeasure.Height); + loc.X = (Width - sMeasure.Width); + break; + + + } + + gfx.DrawString(_text, _font, new SolidBrush(Color.White), new RectangleF(loc.X, loc.Y, sMeasure.Width, sMeasure.Height)); + } + } + + public enum TextAlign + { + TopLeft, + TopCenter, + TopRight, + MiddleLeft, + MiddleCenter, + MiddleRight, + BottomLeft, + BottomCenter, + BottomRight + } +} diff --git a/ShiftOS.Frontend/GUI/TextInput.cs b/ShiftOS.Frontend/GUI/TextInput.cs new file mode 100644 index 0000000..7466cfd --- /dev/null +++ b/ShiftOS.Frontend/GUI/TextInput.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Xna.Framework; +using ShiftOS.Frontend.GraphicsSubsystem; +using static ShiftOS.Engine.SkinEngine; + +namespace ShiftOS.Frontend.GUI +{ + public class TextInput : Control + { + private string _label = "Type here!"; + private string _text = ""; + private int _index = 0; + private Font _font = new Font("Tahoma", 9f); + + protected override void OnKeyEvent(KeyEvent e) + { + if(e.Key == Microsoft.Xna.Framework.Input.Keys.Left) + { + if (_index > 0) + _index--; + + } + if (e.Key == Microsoft.Xna.Framework.Input.Keys.Right) + if (_index < _text.Length) + _index++; + if (e.KeyChar != '\0') { + _text = _text.Insert(_index, e.KeyChar.ToString()); + _index++; + } + CalculateVisibleText(); + Invalidate(); + base.OnKeyEvent(e); + } + + private int textInputOffset = 0; + private int maxCanFit = 5; + string visibleText = ""; + float caretPos = 2f; + + protected void CalculateVisibleText() + { + visibleText = ""; + caretPos = -1f; + using (var gfx = Graphics.FromImage(new Bitmap(1, 1))) + { + for (int i = textInputOffset; i < _text.Length; i++) + { + visibleText += _text[i]; + var measure = gfx.MeasureString(visibleText, _font); + if (measure.Width > Width) + { + maxCanFit = visibleText.Length; + if(_index < textInputOffset) + { + textInputOffset = MathHelper.Clamp(_index - (maxCanFit / 2), 0, _text.Length - 1); + + } + if(_index > textInputOffset + maxCanFit) + { + textInputOffset = MathHelper.Clamp(_index + (maxCanFit / 2), 0, _text.Length - 1) - maxCanFit; + } + break; + } + Height = (int)measure.Height + 4; + } + } + } + + + protected override void OnPaint(Graphics gfx) + { + gfx.Clear(LoadedSkin.ControlColor); + gfx.DrawString(visibleText, _font, new SolidBrush(LoadedSkin.ControlTextColor), 2, 2); + if (IsFocusedControl) + { + //Draw caret. + gfx.FillRectangle(new SolidBrush(LoadedSkin.ControlTextColor), new RectangleF(caretPos, 2, 2, Height - 4)); + } + gfx.DrawRectangle(new Pen(new SolidBrush(LoadedSkin.ControlTextColor), 1), new Rectangle(0, 0, Width - 1, Height - 1)); + + + } + } +} |
