From 6f3a5cba2ea08ea6f442e2336b74f32f4bbc0604 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 2 Jul 2017 21:48:10 -0400 Subject: [PATCH] A day's worth of hell... which is turning into heaven. --- ShiftOS.Frontend/Desktop/Desktop.cs | 85 ++++++ ShiftOS.Frontend/Desktop/WindowManager.cs | 180 +++++++++++- ShiftOS.Frontend/GUI/Button.cs | 50 ++++ ShiftOS.Frontend/GUI/Control.cs | 218 ++++++++++++++ ShiftOS.Frontend/GUI/ItemGroup.cs | 64 ++++ ShiftOS.Frontend/GUI/PictureBox.cs | 146 +++++++++ ShiftOS.Frontend/GUI/ProgressBar.cs | 57 ++++ ShiftOS.Frontend/GUI/TextControl.cs | 16 + ShiftOS.Frontend/GUI/TextInput.cs | 46 +++ .../GraphicsSubsystem/UIManager.cs | 277 +++++++++++++++++- ShiftOS.Frontend/Infobox.cs | 179 +++++++++++ .../Properties/Resources.Designer.cs | 10 + ShiftOS.Frontend/Properties/Resources.resx | 3 + ShiftOS.Frontend/Resources/justthes.png | Bin 0 -> 10030 bytes ShiftOS.Frontend/ShiftOS.Frontend.csproj | 11 + ShiftOS.Frontend/ShiftOS.cs | 95 ++++-- ShiftOS_TheReturn/Desktop.cs | 9 +- ShiftOS_TheReturn/Localization.cs | 113 +++---- 18 files changed, 1476 insertions(+), 83 deletions(-) create mode 100644 ShiftOS.Frontend/Desktop/Desktop.cs create mode 100644 ShiftOS.Frontend/GUI/Button.cs create mode 100644 ShiftOS.Frontend/GUI/ItemGroup.cs create mode 100644 ShiftOS.Frontend/GUI/PictureBox.cs create mode 100644 ShiftOS.Frontend/GUI/ProgressBar.cs create mode 100644 ShiftOS.Frontend/GUI/TextInput.cs create mode 100644 ShiftOS.Frontend/Infobox.cs create mode 100644 ShiftOS.Frontend/Resources/justthes.png diff --git a/ShiftOS.Frontend/Desktop/Desktop.cs b/ShiftOS.Frontend/Desktop/Desktop.cs new file mode 100644 index 0000000..5bcf3a9 --- /dev/null +++ b/ShiftOS.Frontend/Desktop/Desktop.cs @@ -0,0 +1,85 @@ +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.Desktop +{ + public class Desktop : GUI.Control, IDesktop + { + public string DesktopName + { + get + { + return "ShiftOS MonoGame Desktop"; + } + } + + public void Close() + { + throw new NotImplementedException(); + } + + public Size GetSize() + { + return new Size(Width, Height); + } + + public void HideAppLauncher() + { + + } + + public void InvokeOnWorkerThread(Action act) + { + act?.Invoke(); + } + + public void KillWindow(IWindowBorder border) + { + } + + public void MaximizeWindow(IWindowBorder brdr) + { + } + + public void MinimizeWindow(IWindowBorder brdr) + { + } + + public void OpenAppLauncher(Point loc) + { + } + + public void PopulateAppLauncher(LauncherItem[] items) + { + } + + public void PopulatePanelButtons() + { + } + + public void PushNotification(string app, string title, string message) + { + } + + public void RestoreWindow(IWindowBorder brdr) + { + } + + public void SetupDesktop() + { + } + + public void Show() + { + } + + public void ShowWindow(IWindowBorder border) + { + } + } +} diff --git a/ShiftOS.Frontend/Desktop/WindowManager.cs b/ShiftOS.Frontend/Desktop/WindowManager.cs index d17cd37..2450986 100644 --- a/ShiftOS.Frontend/Desktop/WindowManager.cs +++ b/ShiftOS.Frontend/Desktop/WindowManager.cs @@ -1,10 +1,12 @@ 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.Desktop { @@ -12,9 +14,16 @@ namespace ShiftOS.Frontend.Desktop { public override void Close(IShiftOSWindow win) { - + var brdr = RunningBorders.FirstOrDefault(x => x.ParentWindow == win); + if (brdr != null) + { + brdr.Close(); + win = null; + } } + private List RunningBorders = new List(); + public override void InvokeAction(Action act) { act?.Invoke(); @@ -32,17 +41,44 @@ namespace ShiftOS.Frontend.Desktop public override void SetTitle(IShiftOSWindow win, string title) { - throw new NotImplementedException(); + var brdr = RunningBorders.FirstOrDefault(x => x.ParentWindow == win); + if (brdr != null) + brdr.Text = title; } public override void SetupDialog(IShiftOSWindow win) { - throw new NotImplementedException(); + var wb = new WindowBorder(); + wb.Width = (win as GUI.Control).Width + LoadedSkin.LeftBorderWidth + LoadedSkin.RightBorderWidth; + wb.Height = (win as GUI.Control).Height + LoadedSkin.TitlebarHeight + LoadedSkin.BottomBorderWidth; + wb.ParentWindow = win; + wb.IsDialog = true; + UIManager.AddTopLevel(wb); + RunningBorders.Add(wb); + win.OnLoad(); + win.OnUpgrade(); + win.OnSkinLoad(); } public override void SetupWindow(IShiftOSWindow win) { - throw new NotImplementedException(); + if (!Shiftorium.UpgradeAttributesUnlocked(win.GetType())) + { + Console.WriteLine("Application not found on system."); + return; + } + var wb = new WindowBorder(); + wb.Width = (win as GUI.Control).Width + LoadedSkin.LeftBorderWidth + LoadedSkin.RightBorderWidth; + wb.Height = (win as GUI.Control).Height + LoadedSkin.TitlebarHeight + LoadedSkin.BottomBorderWidth; + wb.ParentWindow = win; + wb.IsDialog = true; + UIManager.AddTopLevel(wb); + AppearanceManager.OpenForms.Add(wb); + RunningBorders.Add(wb); + win.OnLoad(); + win.OnUpgrade(); + win.OnSkinLoad(); + } } @@ -61,9 +97,13 @@ namespace ShiftOS.Frontend.Desktop set { _hostedwindow = (GUI.Control)value; + ClearControls(); + AddControl(_hostedwindow); } } + public bool IsDialog { get; set; } + public string Text { get @@ -83,10 +123,138 @@ namespace ShiftOS.Frontend.Desktop UIManager.StopHandling(this); } - public override void MouseStateChanged() + public override void Paint(Graphics gfx) { - //todo: close, minimize, maximize, drag, resize + int titleheight = LoadedSkin.TitlebarHeight; + int leftborderwidth = LoadedSkin.LeftBorderWidth; + int rightborderwidth = LoadedSkin.RightBorderWidth; + int bottomborderwidth = LoadedSkin.BottomBorderWidth; + if (Shiftorium.UpgradeInstalled("wm_titlebar") || true) + { + var titlebarcolor = LoadedSkin.TitleBackgroundColor; + var titlefont = LoadedSkin.TitleFont; + var titletextcolor = LoadedSkin.TitleTextColor; + var titletextleft = LoadedSkin.TitleTextLeft; + bool titletextcentered = LoadedSkin.TitleTextCentered; + + var titlebarbg = GetImage("titlebar"); + var titlebarlayout = GetImageLayout("titlebar"); + + var drawcorners = LoadedSkin.ShowTitleCorners; + int titlebarleft = 0; + int titlebarwidth = Width; + if (drawcorners) + { + //set titleleft to the first corner width + titlebarleft = LoadedSkin.TitleLeftCornerWidth; + titlebarwidth -= titlebarleft; + titlebarwidth -= LoadedSkin.TitleRightCornerWidth; + + //Let's get the left and right images. + var leftimage = GetImage("titlebarleft"); + var rightimage = GetImage("titlebarright"); + //and the colors + var leftcolor = LoadedSkin.TitleLeftCornerBackground; + var rightcolor = LoadedSkin.TitleRightCornerBackground; + //and the layouts... + var leftlayout = GetImageLayout("titlebarleft"); + var rightlayout = GetImageLayout("titlebarright"); + //and the widths + var leftwidth = LoadedSkin.TitleLeftCornerWidth; + var rightwidth = LoadedSkin.TitleRightCornerWidth; + + //draw left corner + if(leftimage != null) + { + var resized = ResizeImage(leftimage, leftwidth, titleheight); + gfx.DrawImage(resized, 0, 0); + } + else + { + gfx.FillRectangle(new SolidBrush(leftcolor), new Rectangle(0, 0, leftwidth, titleheight)); + } + + //draw right corner + if (rightimage != null) + { + var resized = ResizeImage(rightimage, rightwidth, titleheight); + gfx.DrawImage(resized, titlebarleft+titlebarwidth, 0); + } + else + { + gfx.FillRectangle(new SolidBrush(rightcolor), new Rectangle(titlebarleft+titlebarwidth, 0, rightwidth, titleheight)); + } + } + + if (titlebarbg == null) + { + //draw the title bg + gfx.FillRectangle(new SolidBrush(titlebarcolor), new Rectangle(titlebarleft, 0, titlebarwidth, titleheight)); + + + } + else + { + var resized = ResizeImage(titlebarbg, titlebarwidth, titleheight); + gfx.DrawImage(resized, titlebarleft, 0); + } + //Now we draw the title text. + var textMeasure = gfx.MeasureString(_text, titlefont); + PointF textloc; + if (titletextcentered) + textloc = new PointF((titlebarwidth - textMeasure.Width) / 2, + titletextleft.Y); + else + textloc = new PointF(titlebarleft + titletextleft.X, titletextleft.Y); + + gfx.DrawString(_text, titlefont, new SolidBrush(titletextcolor), textloc); + + var tbuttonpos = LoadedSkin.TitleButtonPosition; + + //Draw close button + if(Shiftorium.UpgradeInstalled("close_button") || true) + { + var closebuttoncolor = LoadedSkin.CloseButtonColor; + var closebuttonsize = LoadedSkin.CloseButtonSize; + var closebuttonright = LoadedSkin.CloseButtonFromSide; + + gfx.FillRectangle(new SolidBrush(closebuttoncolor), new Rectangle(closebuttonright, closebuttonsize)); + + } + } + else + { + //Set the titleheight to 0. + titleheight = 0; + + } + + //So here's what we're gonna do now. + //Now that we have a titlebar and window borders... + //We're going to composite the hosted window + //and draw it to the remaining area. + + //First let's GET the window. + if(_hostedwindow != null) + { + var win = _hostedwindow; + //Now let's create a new bitmap to draw onto, the same size as the client area. + using(var bmp = new Bitmap(Width, Height - titleheight)) + { + //Now, let's create a graphics object. + using(var cgfx = Graphics.FromImage(bmp)) + { + //And composite... + win.Paint(cgfx); + + } + //Now draw the bitmap to our client area + gfx.DrawImage(bmp, 0, titleheight); + //We now have a full window. + } + } } + } } diff --git a/ShiftOS.Frontend/GUI/Button.cs b/ShiftOS.Frontend/GUI/Button.cs new file mode 100644 index 0000000..c2e41a3 --- /dev/null +++ b/ShiftOS.Frontend/GUI/Button.cs @@ -0,0 +1,50 @@ +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 + 4; + Height = borderwidth + (int)measure.Height + 8; + } + } + base.OnLayout(); + } + + public override void Paint(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.Paint(gfx); + + } + } +} diff --git a/ShiftOS.Frontend/GUI/Control.cs b/ShiftOS.Frontend/GUI/Control.cs index 975c69a..fcbd429 100644 --- a/ShiftOS.Frontend/GUI/Control.cs +++ b/ShiftOS.Frontend/GUI/Control.cs @@ -5,6 +5,11 @@ 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 { @@ -21,6 +26,77 @@ namespace ShiftOS.Frontend.GUI 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; + + public double Opacity + { + get + { + return _opacity; + } + set + { + _opacity = (double)MathHelper.Clamp((float)value, 0, 1); + } + } + + 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 { @@ -150,6 +226,11 @@ namespace ShiftOS.Frontend.GUI return parentCoords; } + public void ClearControls() + { + _children.Clear(); + } + public Point PointToLocal(int x, int y) { return new GUI.Point(x - _x, y - _y); @@ -168,13 +249,90 @@ namespace ShiftOS.Frontend.GUI using (var cBmp = new System.Drawing.Bitmap(child.Width, child.Height)) { child.Paint(System.Drawing.Graphics.FromImage(cBmp)); + cBmp.SetOpacity((float)child.Opacity); gfx.DrawImage(cBmp, new System.Drawing.Point(child.X, child.Y)); + } } } } } + public void Layout() + { + //Dock style + if(_parent != 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. @@ -230,6 +388,10 @@ namespace ShiftOS.Frontend.GUI { fire = true; } + if (_leftState == true && ld == false) + Click?.Invoke(); + if (_leftState == false && ld == true) + UIManager.FocusedControl = this; _leftState = ld; _middleState = md; _rightState = rd; @@ -240,6 +402,10 @@ namespace ShiftOS.Frontend.GUI } else { + _leftState = false; + _rightState = false; + _middleState = false; + MouseStateChanged(); //If the mouse was in local space before, fire MouseLeave if(_wasMouseInControl == true) { @@ -251,9 +417,22 @@ namespace ShiftOS.Frontend.GUI return false; } + protected virtual void OnKeyEvent(KeyEvent e) + { + + } + + public void ProcessKeyEvent(KeyEvent e) + { + OnKeyEvent(e); + KeyEvent?.Invoke(e); + } + public event Action MouseMove; public event Action MouseEnter; public event Action MouseLeave; + public event Action Click; + public event Action KeyEvent; } public struct Point @@ -268,4 +447,43 @@ namespace ShiftOS.Frontend.GUI 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; + } + } } 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..2447416 --- /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; + } + } + + public override void Paint(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..f0dd626 --- /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; + } + } + + public override void Paint(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 index c8eb56b..79da213 100644 --- a/ShiftOS.Frontend/GUI/TextControl.cs +++ b/ShiftOS.Frontend/GUI/TextControl.cs @@ -13,6 +13,22 @@ namespace ShiftOS.Frontend.GUI 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; } diff --git a/ShiftOS.Frontend/GUI/TextInput.cs b/ShiftOS.Frontend/GUI/TextInput.cs new file mode 100644 index 0000000..fc142a0 --- /dev/null +++ b/ShiftOS.Frontend/GUI/TextInput.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +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; + + 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.Insert(_index, e.KeyChar.ToString()); + base.OnKeyEvent(e); + } + + protected override void OnLayout() + { + base.OnLayout(); + } + + private int _textOffset = 0; + + public override void Paint(Graphics gfx) + { + gfx.Clear(LoadedSkin.ControlColor); + + } + } +} diff --git a/ShiftOS.Frontend/GraphicsSubsystem/UIManager.cs b/ShiftOS.Frontend/GraphicsSubsystem/UIManager.cs index fdd5f99..a688757 100644 --- a/ShiftOS.Frontend/GraphicsSubsystem/UIManager.cs +++ b/ShiftOS.Frontend/GraphicsSubsystem/UIManager.cs @@ -9,6 +9,7 @@ using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using ShiftOS.Engine; using ShiftOS.Frontend.Desktop; +using ShiftOS.Frontend.GUI; namespace ShiftOS.Frontend.GraphicsSubsystem { @@ -16,6 +17,30 @@ namespace ShiftOS.Frontend.GraphicsSubsystem { private static List topLevels = new List(); + public static GUI.Control FocusedControl = null; + + public static void LayoutUpdate() + { + foreach (var toplevel in topLevels) + toplevel.Layout(); + } + + public static void Animate(object owner, System.Reflection.PropertyInfo prop, double from, double to, int timeMs) + { + var t = new System.Threading.Thread(() => + { + for(int i = 0; i < timeMs; i++) + { + double value = ProgressBar.linear(i, 0, timeMs, from, to); + prop.SetValue(owner, value); + System.Threading.Thread.Sleep(1); + } + }); + t.IsBackground = true; + t.Start(); + } + + public static void DrawControls(GraphicsDevice graphics, SpriteBatch batch) { foreach (var ctrl in topLevels) @@ -24,11 +49,19 @@ namespace ShiftOS.Frontend.GraphicsSubsystem { var gfx = System.Drawing.Graphics.FromImage(bmp); ctrl.Paint(gfx); + bmp.SetOpacity((float)ctrl.Opacity); //get the bits of the bitmap var data = bmp.LockBits(new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); byte[] rgb = new byte[Math.Abs(data.Stride) * data.Height]; Marshal.Copy(data.Scan0, rgb, 0, rgb.Length); bmp.UnlockBits(data); + for(int i = 0; i < rgb.Length; i+=4) + { + byte r = rgb[i]; + byte b = rgb[i + 2]; + rgb[i] = b; + rgb[i + 2] = r; + } var tex2 = new Texture2D(graphics, bmp.Width, bmp.Height); tex2.SetData(rgb); batch.Draw(tex2, new Rectangle(ctrl.X, ctrl.Y, ctrl.Width, ctrl.Height), Color.White); @@ -40,18 +73,21 @@ namespace ShiftOS.Frontend.GraphicsSubsystem { if (!topLevels.Contains(ctrl)) topLevels.Add(ctrl); + ctrl.Layout(); } public static void ProcessMouseState(MouseState state) { foreach(var ctrl in topLevels) { - if (ctrl.ProcessMouseState(state) == true) - break; + ctrl.ProcessMouseState(state); } } - + public static void ProcessKeyEvent(KeyEvent e) + { + FocusedControl?.ProcessKeyEvent(e); + } public static void DrawBackgroundLayer(GraphicsDevice graphics, SpriteBatch batch, int width, int height) { @@ -79,4 +115,239 @@ namespace ShiftOS.Frontend.GraphicsSubsystem ctrl = null; } } + + public class KeyEvent + { + public KeyEvent(bool control, bool alt, bool shift, Keys key) + { + ControlDown = control; + AltDown = alt; + ShiftDown = shift; + Key = key; + KeyChar = key.ToCharacter(shift); + } + + public bool ControlDown { get; private set; } + public bool AltDown { get; private set; } + public bool ShiftDown { get; set; } + public Keys Key { get; private set; } + + public char KeyChar { get; private set; } + } + + public static class KeysExtensions + { + public static char ToCharacter(this Keys key, bool shift) + { + char c = ' '; + switch (key) + { + case Keys.Space: + c = ' '; + break; + case Keys.A: + c = 'a'; + break; + case Keys.B: + c = 'b'; + break; + case Keys.C: + c = 'c'; + break; + case Keys.D: + c = 'd'; + break; + case Keys.E: + c = 'e'; + break; + case Keys.F: + c = 'f'; + break; + case Keys.G: + c = 'g'; + break; + case Keys.H: + c = 'h'; + break; + case Keys.I: + c = 'i'; + break; + case Keys.J: + c = 'j'; + break; + case Keys.K: + c = 'k'; + break; + case Keys.L: + c = 'l'; + break; + case Keys.M: + c = 'm'; + break; + case Keys.N: + c = 'n'; + break; + case Keys.O: + c = 'o'; + break; + case Keys.P: + c = 'p'; + break; + case Keys.Q: + c = 'q'; + break; + case Keys.R: + c = 'r'; + break; + case Keys.S: + c = 's'; + break; + case Keys.T: + c = 't'; + break; + case Keys.U: + c = 'u'; + break; + case Keys.V: + c = 'v'; + break; + case Keys.W: + c = 'w'; + break; + case Keys.X: + c = 'x'; + break; + case Keys.Y: + c = 'y'; + break; + case Keys.Z: + c = 'z'; + break; + case Keys.D0: + if (shift) + c = ')'; + else + c = '0'; + break; + case Keys.D1: + if (shift) + c = '!'; + else + c = '1'; + break; + case Keys.D2: + if (shift) + c = '@'; + else + c = '2'; + break; + case Keys.D3: + if (shift) + c = '#'; + else + c = '3'; + break; + case Keys.D4: + if (shift) + c = '$'; + else + c = '4'; + break; + case Keys.D5: + if (shift) + c = '%'; + else + c = '5'; + break; + case Keys.D6: + if (shift) + c = '^'; + else + c = '6'; + break; + case Keys.D7: + if (shift) + c = '&'; + else + c = '7'; + break; + case Keys.D8: + if (shift) + c = '*'; + else + c = '8'; + break; + case Keys.D9: + if (shift) + c = '('; + else + c = '9'; + break; + case Keys.OemBackslash: + if (shift) + c = '|'; + else + c = '\\'; + break; + case Keys.OemCloseBrackets: + if (shift) + c = '}'; + else + c = ']'; + break; + case Keys.OemComma: + if (shift) + c = '<'; + else + c = ','; + break; + case Keys.OemPeriod: + if (shift) + c = '>'; + else + c = '.'; + break; + case Keys.OemQuestion: + if (shift) + c = '?'; + else + c = '/'; + break; + case Keys.OemSemicolon: + if (shift) + c = ':'; + else + c = ';'; + break; + case Keys.OemQuotes: + if (shift) + c = '"'; + else + c = '\''; + break; + case Keys.OemTilde: + if (shift) + c = '~'; + else + c = '`'; + break; + case Keys.OemMinus: + if (shift) + c = '_'; + else + c = '-'; + break; + case Keys.OemPlus: + if (shift) + c = '+'; + else + c = '='; + break; + } + if (char.IsLetter(c)) + if (shift) + c = char.ToUpper(c); + return c; + } + } } diff --git a/ShiftOS.Frontend/Infobox.cs b/ShiftOS.Frontend/Infobox.cs new file mode 100644 index 0000000..68c681d --- /dev/null +++ b/ShiftOS.Frontend/Infobox.cs @@ -0,0 +1,179 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ShiftOS.Engine; +using ShiftOS.Frontend.GUI; + +namespace ShiftOS.Frontend +{ + public class Infobox : IInfobox + { + public void Open(string title, string msg, Action callback = null) + { + var imsg = new InfoboxMessage(title, msg); + imsg.ShowPrompt(callback); + } + + public void PromptText(string title, string message, Action callback, bool isPassword) + { + var imsg = new InfoboxMessage(title, message); + imsg.ShowText(callback); + } + + public void PromptYesNo(string title, string message, Action callback) + { + var imsg = new InfoboxMessage(title, message); + imsg.ShowYesNo(callback); + + } + } + + public class InfoboxMessage : GUI.Control, IShiftOSWindow + { + public InfoboxMessage(string title, string message) + { + InitializeComponent(); + lbmessage.Text = Localization.Parse(message); + Title = title; + } + + public string Title { get; private set; } + + public void OnLoad() + { + AppearanceManager.SetWindowTitle(this, Title); + } + + public void ShowPrompt(Action callback) + { + AppearanceManager.SetupDialog(this); + flyesno.Visible = false; + txtinput.Visible = false; + btnok.Visible = true; + btnok.Click += callback; + } + + public void ShowYesNo(Action callback) + { + AppearanceManager.SetupDialog(this); + flyesno.Visible = true; + txtinput.Visible = false; + btnok.Visible = false; + btnyes.Click += () => + { + callback?.Invoke(true); + }; + btnno.Click += () => + { + callback?.Invoke(false); + }; + } + + public void ShowText(Action callback) + { + Title = "Not yet implemented."; + lbmessage.Text = "This feature hasn't yet been implemented."; + ShowPrompt(null); + + } + + public void OnSkinLoad() + { + } + + public bool OnUnload() + { + return true; + } + + public void OnUpgrade() + { + } + + //NOTE: The following code is ported over from Windows Forms. + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.txtinput = new TextInput(); + this.lbmessage = new TextControl(); + this.flyesno = new ItemGroup(); + this.btnyes = new Button(); + this.btnno = new Button(); + this.btnok = new Button(); + this.pbicon = new PictureBox(); + // + // txtinput + // + this.txtinput.X = 88; + this.txtinput.Y = 116; + this.txtinput.Width = 250; + this.txtinput.Height = 20; + // + // lbmessage + // + this.lbmessage.X = 85; + this.lbmessage.Y = 19; + this.lbmessage.Width = 253; + this.lbmessage.Height = 94; + this.lbmessage.Text = "label1"; + this.lbmessage.TextAlign = TextAlign.MiddleLeft; + // + // flyesno + // + this.flyesno.AutoSize = true; + this.flyesno.AddControl(this.btnyes); + this.flyesno.AddControl(this.btnno); + this.flyesno.X = 129; + this.flyesno.Y = 134; + // + // btnyes + // + this.btnyes.AutoSize = true; + this.btnyes.Text = Localization.Parse("{GEN_YES}"); + // + // btnno + // + this.btnno.AutoSize = true; + this.btnno.Text = Localization.Parse("{GEN_NO}"); + // + // btnok + // + this.btnok.AutoSize = true; + this.btnok.Text = Localization.Parse("{GEN_OK}"); + // + // pbicon + // + this.pbicon.X = 14; + this.pbicon.Y = 19; + this.pbicon.Width = 64; + this.pbicon.Height = 64; + // + // Dialog + // + this.Width = 341; + this.Height = 127; + this.AddControl(pbicon); + this.AddControl(btnok); + this.AddControl(flyesno); + this.AddControl(lbmessage); + + this.Layout(); + } + + + private Control panel1; + private TextControl lbmessage; + private ItemGroup flyesno; + private Button btnyes; + private Button btnno; + private Button btnok; + private PictureBox pbicon; + private TextInput txtinput; + + } +} diff --git a/ShiftOS.Frontend/Properties/Resources.Designer.cs b/ShiftOS.Frontend/Properties/Resources.Designer.cs index b3bb6d7..fc35525 100644 --- a/ShiftOS.Frontend/Properties/Resources.Designer.cs +++ b/ShiftOS.Frontend/Properties/Resources.Designer.cs @@ -69,5 +69,15 @@ namespace ShiftOS.Frontend.Properties { return ((System.Drawing.Bitmap)(obj)); } } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap justthes { + get { + object obj = ResourceManager.GetObject("justthes", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } } } diff --git a/ShiftOS.Frontend/Properties/Resources.resx b/ShiftOS.Frontend/Properties/Resources.resx index 9510e93..e309997 100644 --- a/ShiftOS.Frontend/Properties/Resources.resx +++ b/ShiftOS.Frontend/Properties/Resources.resx @@ -121,4 +121,7 @@ ..\Resources\cursor_9x_pointer.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\Resources\justthes.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + \ No newline at end of file diff --git a/ShiftOS.Frontend/Resources/justthes.png b/ShiftOS.Frontend/Resources/justthes.png new file mode 100644 index 0000000000000000000000000000000000000000..a85ba275e6cd652ac2d44320bbdff113c5fe5b21 GIT binary patch literal 10030 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 T?h6CuY-sAr+Di2bHlhCq&_gAd literal 0 HcmV?d00001 diff --git a/ShiftOS.Frontend/ShiftOS.Frontend.csproj b/ShiftOS.Frontend/ShiftOS.Frontend.csproj index 48748a9..61ff0c6 100644 --- a/ShiftOS.Frontend/ShiftOS.Frontend.csproj +++ b/ShiftOS.Frontend/ShiftOS.Frontend.csproj @@ -42,10 +42,17 @@ app.manifest + + + + + + + True True @@ -62,6 +69,7 @@ + @@ -133,6 +141,9 @@ + + +