A day's worth of hell... which is turning into heaven.

This commit is contained in:
Michael 2017-07-02 21:48:10 -04:00
parent 5d5f351138
commit 6f3a5cba2e
18 changed files with 1476 additions and 83 deletions

View file

@ -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)
{
}
}
}

View file

@ -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<WindowBorder> RunningBorders = new List<WindowBorder>();
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.
}
}
}
}
}

View file

@ -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);
}
}
}

View file

@ -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<Point> MouseMove;
public event Action MouseEnter;
public event Action MouseLeave;
public event Action Click;
public event Action<KeyEvent> 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;
}
}
}

View file

@ -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
}
}

View file

@ -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,
}
}

View file

@ -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);
}
}
}

View file

@ -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; }

View file

@ -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);
}
}
}

View file

@ -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<GUI.Control> topLevels = new List<GUI.Control>();
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<byte>(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;
}
}
}

179
ShiftOS.Frontend/Infobox.cs Normal file
View file

@ -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<string> callback, bool isPassword)
{
var imsg = new InfoboxMessage(title, message);
imsg.ShowText(callback);
}
public void PromptYesNo(string title, string message, Action<bool> 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<bool> 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<string> 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.
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
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;
}
}

View file

@ -69,5 +69,15 @@ namespace ShiftOS.Frontend.Properties {
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap justthes {
get {
object obj = ResourceManager.GetObject("justthes", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
}
}

View file

@ -121,4 +121,7 @@
<data name="cursor_9x_pointer" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\cursor_9x_pointer.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="justthes" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\justthes.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

View file

@ -42,10 +42,17 @@
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>
<ItemGroup>
<Compile Include="Desktop\Desktop.cs" />
<Compile Include="Desktop\WindowManager.cs" />
<Compile Include="GraphicsSubsystem\UIManager.cs" />
<Compile Include="GUI\Button.cs" />
<Compile Include="GUI\Control.cs" />
<Compile Include="GUI\ItemGroup.cs" />
<Compile Include="GUI\PictureBox.cs" />
<Compile Include="GUI\ProgressBar.cs" />
<Compile Include="GUI\TextControl.cs" />
<Compile Include="GUI\TextInput.cs" />
<Compile Include="Infobox.cs" />
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
@ -62,6 +69,7 @@
</Reference>
<Reference Include="System" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
@ -133,6 +141,9 @@
<ItemGroup>
<None Include="Resources\cursor_9x_pointer.png" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\justthes.png" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildExtensionsPath)\MonoGame\v3.0\MonoGame.Content.Builder.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.

View file

@ -1,8 +1,10 @@
using System;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using ShiftOS.Engine;
using ShiftOS.Frontend.GraphicsSubsystem;
namespace ShiftOS.Frontend
@ -18,15 +20,14 @@ namespace ShiftOS.Frontend
public ShiftOS()
{
GraphicsDevice = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
//Make the mouse cursor visible.
this.IsMouseVisible = true;
GraphicsDevice.PreferredBackBufferHeight = 1080;
GraphicsDevice.PreferredBackBufferWidth = 1920;
//Don't allow ALT+F4
this.Window.AllowAltF4 = false;
Content.RootDirectory = "Content";
//Make window borderless
Window.IsBorderless = true;
Window.IsBorderless = false;
//Set the title
Window.Title = "ShiftOS";
@ -34,10 +35,12 @@ namespace ShiftOS.Frontend
//Fullscreen
GraphicsDevice.IsFullScreen = true;
GraphicsDevice.IsFullScreen = false;
}
private GUI.TextControl _titleLabel = null;
/// <summary>
/// Allows the game to perform any initialization it needs to before starting to run.
/// This is where it can query for any required services and load any non-graphic
@ -46,21 +49,39 @@ namespace ShiftOS.Frontend
/// </summary>
protected override void Initialize()
{
//We'll start by initializing the BARE FUNDAMENTALS of the ShiftOS engine.
//This'll be enough to do skinning, fs, windowmanagement etc
//so that we can make the main menu and yeah
//Before we do ANYTHING, we've got to initiate the ShiftOS engine.
//Let's add a control to test something
var textControl = new GUI.TextControl();
textControl.Width = 640;
textControl.Height = 480;
UIManager.AddTopLevel(textControl);
//First things first, let's initiate the window manager.
AppearanceManager.Initiate(new Desktop.WindowManager());
//Cool. Now the engine's window management system talks to us.
UIManager.AddTopLevel(framerate);
framerate.Width = 640;
framerate.Height = 480;
framerate.TextAlign = GUI.TextAlign.BottomRight;
//Also initiate the desktop
Engine.Desktop.Init(new Desktop.Desktop());
//Now we can initiate the Infobox subsystem
Engine.Infobox.Init(new Infobox());
//Let's give it a try.
Engine.Infobox.Show("Welcome to ShiftOS!", "This is a test infobox. Clicking OK will dismiss it.");
//Let's set up the Main Menu UI.
var justthes = new GUI.PictureBox();
justthes.AutoSize = true;
justthes.ImageLayout = GUI.ImageLayout.Stretch;
justthes.Image = Properties.Resources.justthes;
justthes.X = 15;
justthes.Y = 15;
UIManager.AddTopLevel(justthes);
_titleLabel = new GUI.TextControl();
_titleLabel.Text = " - main menu - ";
_titleLabel.AutoSize = true;
_titleLabel.X = justthes.X;
_titleLabel.Y = justthes.Y + justthes.Height + 15;
_titleLabel.Font = SkinEngine.LoadedSkin.HeaderFont;
UIManager.AddTopLevel(_titleLabel);
base.Initialize();
@ -100,9 +121,43 @@ namespace ShiftOS.Frontend
//Now let's process it.
UIManager.ProcessMouseState(mouseState);
//Cause layout update on all elements
UIManager.LayoutUpdate();
//set framerate
framerate.Text = "ShiftOS 1.0 Beta 4\r\nCopyright (c) 2017 ShiftOS\r\nFPS: " + (1 / gameTime.ElapsedGameTime.TotalSeconds);
//So we have mouse input, and the UI layout system working...
//But an OS isn't useful without the keyboard!
//Let's see how keyboard input works.
//Hmmm... just like the mouse...
var keystate = Keyboard.GetState();
//Simple... just iterate through this list and generate some key events?
var keys = keystate.GetPressedKeys();
if (keys.Length > 0)
{
//Of course, we need modifier keys...
//First for Control.
bool controlDown = keys.Contains(Keys.LeftControl) || keys.Contains(Keys.RightControl);
//Now SHIFT.
bool shiftDown = keys.Contains(Keys.LeftShift) || keys.Contains(Keys.RightShift);
//And ALT.
bool altDown = keys.Contains(Keys.LeftAlt) || keys.Contains(Keys.RightAlt);
foreach(var key in keys)
{
//This'll make it so we skip the modifier keys.
if(key != Keys.LeftAlt && key != Keys.RightAlt && key != Keys.LeftControl && key != Keys.RightControl && key != Keys.LeftShift && key != Keys.RightShift)
{
var keyevent = new KeyEvent(controlDown, altDown, shiftDown, key);
UIManager.ProcessKeyEvent(keyevent);
}
}
}
base.Update(gameTime);
}

View file

@ -246,7 +246,14 @@ namespace ShiftOS.Engine
public static void InvokeOnWorkerThread(Action act)
{
_desktop.InvokeOnWorkerThread(act);
try
{
_desktop.InvokeOnWorkerThread(act);
}
catch
{
act?.Invoke();
}
}
public static void ResetPanelButtons()

View file

@ -99,63 +99,65 @@ namespace ShiftOS.Engine
public static string Parse(string original, Dictionary<string, string> replace)
{
Dictionary<string, string> localizationStrings = new Dictionary<string, string>();
try
{
localizationStrings = JsonConvert.DeserializeObject<Dictionary<string, string>>(_provider.GetCurrentTranscript());
}
catch
{
localizationStrings = JsonConvert.DeserializeObject<Dictionary<string, string>>(Utils.ReadAllText(Paths.GetPath("english.local"))); //if no provider fall back to english
}
Dictionary<string, string> localizationStrings = new Dictionary<string, string>();
foreach (var kv in localizationStrings.Where(x=>original.Contains(x.Key)))
{
original = original.Replace(kv.Key, kv.Value); // goes through and replaces all the localization blocks
}
//string original2 = Parse(original);
string usernameReplace = "";
string domainReplace = "";
// if the user has saved then store their username and systemname in these string variables please
if (SaveSystem.CurrentSave != null)
{
try
{
usernameReplace = SaveSystem.CurrentUser.Username;
localizationStrings = JsonConvert.DeserializeObject<Dictionary<string, string>>(_provider.GetCurrentTranscript());
}
catch
{
localizationStrings = JsonConvert.DeserializeObject<Dictionary<string, string>>(Utils.ReadAllText(Paths.GetPath("english.local"))); //if no provider fall back to english
}
foreach (var kv in localizationStrings.Where(x => original.Contains(x.Key)))
{
original = original.Replace(kv.Key, kv.Value); // goes through and replaces all the localization blocks
}
//string original2 = Parse(original);
string usernameReplace = "";
string domainReplace = "";
// if the user has saved then store their username and systemname in these string variables please
if (SaveSystem.CurrentSave != null)
{
try
{
usernameReplace = SaveSystem.CurrentUser.Username;
}
catch
{
usernameReplace = "user";
}
try
{
domainReplace = SaveSystem.CurrentSave.SystemName;
}
catch
{
domainReplace = "system";
}
}
try
string namespaceReplace = "";
string commandReplace = "";
// if the user did a command in the terminal and it had a period in it then split it up into the part before the period and the part after and then store them into these two string variables please
if (TerminalBackend.latestCommmand != "" && TerminalBackend.latestCommmand.IndexOf('.') > -1)
{
domainReplace = SaveSystem.CurrentSave.SystemName;
namespaceReplace = TerminalBackend.latestCommmand.Split('.')[0];
commandReplace = TerminalBackend.latestCommmand.Split('.')[1];
}
catch
{
domainReplace = "system";
}
}
string namespaceReplace = "";
string commandReplace = "";
// if the user did a command in the terminal and it had a period in it then split it up into the part before the period and the part after and then store them into these two string variables please
if (TerminalBackend.latestCommmand != "" && TerminalBackend.latestCommmand.IndexOf('.') > -1)
{
namespaceReplace = TerminalBackend.latestCommmand.Split('.')[0];
commandReplace = TerminalBackend.latestCommmand.Split('.')[1];
}
// if you see these then replace them with what you need to
Dictionary<string, string> defaultReplace = new Dictionary<string, string>() {
// if you see these then replace them with what you need to
Dictionary<string, string> defaultReplace = new Dictionary<string, string>() {
{"%username", usernameReplace},
{"%domain", domainReplace},
{"%ns", namespaceReplace},
@ -165,19 +167,24 @@ namespace ShiftOS.Engine
#endif
};
// actually do the replacement
foreach (KeyValuePair<string, string> replacement in replace.Where(x => original.Contains(x.Key)))
{
original = original.Replace(replacement.Key, Parse(replacement.Value));
}
// actually do the replacement
foreach (KeyValuePair<string, string> replacement in replace.Where(x => original.Contains(x.Key)))
{
original = original.Replace(replacement.Key, Parse(replacement.Value));
}
// do the replacement but default
foreach (KeyValuePair<string, string> replacement in defaultReplace.Where(x => original.Contains(x.Key)))
{
original = original.Replace(replacement.Key, replacement.Value);
}
// do the replacement but default
foreach (KeyValuePair<string, string> replacement in defaultReplace.Where(x => original.Contains(x.Key)))
{
original = original.Replace(replacement.Key, replacement.Value);
}
return original; // returns the now replaced string
return original; // returns the now replaced string
}
catch
{
return original;
}
}
// a few things are defined here