From dad09c9e7c1ff68a157836b636f13f25d27e050a Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 2 Jul 2017 13:31:39 -0400 Subject: Render text onscreen --- ShiftOS.Frontend/Content/Content.mgcb | 15 ++ ShiftOS.Frontend/Desktop/WindowManager.cs | 92 ++++++++ ShiftOS.Frontend/GUI/Control.cs | 271 ++++++++++++++++++++++++ ShiftOS.Frontend/GUI/TextControl.cs | 72 +++++++ ShiftOS.Frontend/GraphicsSubsystem/UIManager.cs | 82 +++++++ ShiftOS.Frontend/Icon.bmp | Bin 0 -> 262282 bytes ShiftOS.Frontend/Icon.ico | Bin 0 -> 147541 bytes ShiftOS.Frontend/Program.cs | 20 ++ ShiftOS.Frontend/Properties/AssemblyInfo.cs | 36 ++++ ShiftOS.Frontend/ShiftOS.Frontend.csproj | 134 ++++++++++++ ShiftOS.Frontend/ShiftOS.cs | 117 ++++++++++ ShiftOS.Frontend/Window.cs | 32 +++ ShiftOS.Frontend/app.manifest | 42 ++++ 13 files changed, 913 insertions(+) create mode 100644 ShiftOS.Frontend/Content/Content.mgcb create mode 100644 ShiftOS.Frontend/Desktop/WindowManager.cs create mode 100644 ShiftOS.Frontend/GUI/Control.cs create mode 100644 ShiftOS.Frontend/GUI/TextControl.cs create mode 100644 ShiftOS.Frontend/GraphicsSubsystem/UIManager.cs create mode 100644 ShiftOS.Frontend/Icon.bmp create mode 100644 ShiftOS.Frontend/Icon.ico create mode 100644 ShiftOS.Frontend/Program.cs create mode 100644 ShiftOS.Frontend/Properties/AssemblyInfo.cs create mode 100644 ShiftOS.Frontend/ShiftOS.Frontend.csproj create mode 100644 ShiftOS.Frontend/ShiftOS.cs create mode 100644 ShiftOS.Frontend/Window.cs create mode 100644 ShiftOS.Frontend/app.manifest (limited to 'ShiftOS.Frontend') diff --git a/ShiftOS.Frontend/Content/Content.mgcb b/ShiftOS.Frontend/Content/Content.mgcb new file mode 100644 index 0000000..ddc4c36 --- /dev/null +++ b/ShiftOS.Frontend/Content/Content.mgcb @@ -0,0 +1,15 @@ + +#----------------------------- Global Properties ----------------------------# + +/outputDir:bin/$(Platform) +/intermediateDir:obj/$(Platform) +/platform:DesktopGL +/config: +/profile:Reach +/compress:False + +#-------------------------------- References --------------------------------# + + +#---------------------------------- Content ---------------------------------# + diff --git a/ShiftOS.Frontend/Desktop/WindowManager.cs b/ShiftOS.Frontend/Desktop/WindowManager.cs new file mode 100644 index 0000000..d17cd37 --- /dev/null +++ b/ShiftOS.Frontend/Desktop/WindowManager.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ShiftOS.Engine; +using ShiftOS.Frontend.GraphicsSubsystem; + +namespace ShiftOS.Frontend.Desktop +{ + public class WindowManager : Engine.WindowManager + { + public override void Close(IShiftOSWindow win) + { + + } + + public override void InvokeAction(Action act) + { + act?.Invoke(); + } + + public override void Maximize(IWindowBorder border) + { + throw new NotImplementedException(); + } + + public override void Minimize(IWindowBorder border) + { + throw new NotImplementedException(); + } + + public override void SetTitle(IShiftOSWindow win, string title) + { + throw new NotImplementedException(); + } + + public override void SetupDialog(IShiftOSWindow win) + { + throw new NotImplementedException(); + } + + public override void SetupWindow(IShiftOSWindow win) + { + throw new NotImplementedException(); + } + } + + public class WindowBorder : GUI.Control, IWindowBorder + { + private string _text = "ShiftOS window"; + private GUI.Control _hostedwindow = null; + + public IShiftOSWindow ParentWindow + { + get + { + return (IShiftOSWindow)_hostedwindow; + } + + set + { + _hostedwindow = (GUI.Control)value; + } + } + + public string Text + { + get + { + return _text; + } + + set + { + _text = value; + } + } + + public void Close() + { + Visible = false; + UIManager.StopHandling(this); + } + + public override void MouseStateChanged() + { + //todo: close, minimize, maximize, drag, resize + + } + } +} diff --git a/ShiftOS.Frontend/GUI/Control.cs b/ShiftOS.Frontend/GUI/Control.cs new file mode 100644 index 0000000..975c69a --- /dev/null +++ b/ShiftOS.Frontend/GUI/Control.cs @@ -0,0 +1,271 @@ +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; + +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 _children = new List(); + private bool _wasMouseInControl = false; + private bool _leftState = false; + private bool _rightState = false; + private bool _middleState = false; + private bool _visible = true; + + public bool Visible + { + get + { + return _visible; + } + set + { + _visible = value; + } + } + + public void AddControl(Control ctrl) + { + if (!_children.Contains(ctrl)) + { + ctrl._parent = this; + _children.Add(ctrl); + } + } + + 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; + } + } + + public int Y + { + get + { + return _y; + } + set + { + _y = value; + } + } + + public int Width + { + get + { + return _w; + } + set + { + _w = value; + } + } + + public int Height + { + get + { + return _h; + } + set + { + _h = value; + } + } + + 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 Point PointToLocal(int x, int y) + { + return new GUI.Point(x - _x, y - _y); + } + + public virtual void MouseStateChanged() { } + + public virtual void Paint(System.Drawing.Graphics gfx) + { + if (_visible == true) + { + if (_children.Count > 0) + { + foreach (var child in _children) + { + using (var cBmp = new System.Drawing.Bitmap(child.Width, child.Height)) + { + child.Paint(System.Drawing.Graphics.FromImage(cBmp)); + gfx.DrawImage(cBmp, new System.Drawing.Point(child.X, child.Y)); + } + } + } + } + } + + 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); + //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(); + } + + //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; + } + _leftState = ld; + _middleState = md; + _rightState = rd; + if (fire) + MouseStateChanged(); + } + return true; + } + else + { + //If the mouse was in local space before, fire MouseLeave + if(_wasMouseInControl == true) + { + _wasMouseInControl = false; + MouseLeave?.Invoke(); + } + } + //Mouse is not in the local space, don't do anything. + return false; + } + + public event Action MouseMove; + public event Action MouseEnter; + public event Action MouseLeave; + } + + public struct Point + { + public Point(int x, int y) + { + X = x; + Y = y; + } + + public int X { get; set; } + public int Y { get; set; } + } + +} diff --git a/ShiftOS.Frontend/GUI/TextControl.cs b/ShiftOS.Frontend/GUI/TextControl.cs new file mode 100644 index 0000000..06d8233 --- /dev/null +++ b/ShiftOS.Frontend/GUI/TextControl.cs @@ -0,0 +1,72 @@ +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); + + public override void Paint(Graphics gfx) + { + var sMeasure = gfx.MeasureString(_text, _font); + 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/GraphicsSubsystem/UIManager.cs b/ShiftOS.Frontend/GraphicsSubsystem/UIManager.cs new file mode 100644 index 0000000..fdd5f99 --- /dev/null +++ b/ShiftOS.Frontend/GraphicsSubsystem/UIManager.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; +using ShiftOS.Engine; +using ShiftOS.Frontend.Desktop; + +namespace ShiftOS.Frontend.GraphicsSubsystem +{ + public static class UIManager + { + private static List topLevels = new List(); + + public static void DrawControls(GraphicsDevice graphics, SpriteBatch batch) + { + foreach (var ctrl in topLevels) + { + using(var bmp = new System.Drawing.Bitmap(ctrl.Width, ctrl.Height)) + { + var gfx = System.Drawing.Graphics.FromImage(bmp); + ctrl.Paint(gfx); + //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); + 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); + } + } + } + + public static void AddTopLevel(GUI.Control ctrl) + { + if (!topLevels.Contains(ctrl)) + topLevels.Add(ctrl); + } + + public static void ProcessMouseState(MouseState state) + { + foreach(var ctrl in topLevels) + { + if (ctrl.ProcessMouseState(state) == true) + break; + } + } + + + + public static void DrawBackgroundLayer(GraphicsDevice graphics, SpriteBatch batch, int width, int height) + { + if (SkinEngine.LoadedSkin == null) + SkinEngine.Init(); + graphics.Clear(SkinEngine.LoadedSkin.DesktopColor.ToMonoColor()); + var desktopbg = SkinEngine.GetImage("desktopbackground"); + if(desktopbg != null) + { + var tex2 = new Texture2D(graphics, desktopbg.Width, desktopbg.Height); + tex2.SetData(SkinEngine.LoadedSkin.DesktopBackgroundImage); + batch.Draw(tex2, new Rectangle(0, 0, width, height), Color.White); + } + } + + public static Color ToMonoColor(this System.Drawing.Color color) + { + return new Color(color.R, color.G, color.B, color.A); + } + + internal static void StopHandling(GUI.Control ctrl) + { + if (topLevels.Contains(ctrl)) + topLevels.Remove(ctrl); + ctrl = null; + } + } +} diff --git a/ShiftOS.Frontend/Icon.bmp b/ShiftOS.Frontend/Icon.bmp new file mode 100644 index 0000000..2b48165 Binary files /dev/null and b/ShiftOS.Frontend/Icon.bmp differ diff --git a/ShiftOS.Frontend/Icon.ico b/ShiftOS.Frontend/Icon.ico new file mode 100644 index 0000000..7d9dec1 Binary files /dev/null and b/ShiftOS.Frontend/Icon.ico differ diff --git a/ShiftOS.Frontend/Program.cs b/ShiftOS.Frontend/Program.cs new file mode 100644 index 0000000..031b2ab --- /dev/null +++ b/ShiftOS.Frontend/Program.cs @@ -0,0 +1,20 @@ +using System; + +namespace ShiftOS.Frontend +{ + /// + /// The main class. + /// + public static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + using (var game = new ShiftOS()) + game.Run(); + } + } +} diff --git a/ShiftOS.Frontend/Properties/AssemblyInfo.cs b/ShiftOS.Frontend/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..f25b1d7 --- /dev/null +++ b/ShiftOS.Frontend/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ShiftOS.Frontend")] +[assembly: AssemblyProduct("ShiftOS.Frontend")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("13e2a4c8-a6cc-405b-a4ec-5d39531993c2")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/ShiftOS.Frontend/ShiftOS.Frontend.csproj b/ShiftOS.Frontend/ShiftOS.Frontend.csproj new file mode 100644 index 0000000..672024b --- /dev/null +++ b/ShiftOS.Frontend/ShiftOS.Frontend.csproj @@ -0,0 +1,134 @@ + + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {4DFC3088-1B08-4A0E-A9F5-483A7B9811FD} + WinExe + Properties + ShiftOS.Frontend + ShiftOS.Frontend + 512 + DesktopGL + v4.5 + + + true + bin\$(MonoGamePlatform)\$(Platform)\$(Configuration)\ + DEBUG;TRACE;LINUX + full + AnyCPU + prompt + false + 4 + + + bin\$(MonoGamePlatform)\$(Platform)\$(Configuration)\ + TRACE;LINUX + true + pdbonly + AnyCPU + prompt + false + 4 + + + Icon.ico + + + app.manifest + + + + + + + + + + + + + + $(MonoGameInstallDirectory)\MonoGame\v3.0\Assemblies\DesktopGL\MonoGame.Framework.dll + + + + + + + + + + + + + x86\SDL2.dll + PreserveNewest + + + x64\SDL2.dll + PreserveNewest + + + x86\soft_oal.dll + PreserveNewest + + + x64\soft_oal.dll + PreserveNewest + + + x86\libSDL2-2.0.so.0 + PreserveNewest + + + x64\libSDL2-2.0.so.0 + PreserveNewest + + + x86\libopenal.so.1 + PreserveNewest + + + x64\libopenal.so.1 + PreserveNewest + + + libSDL2-2.0.0.dylib + PreserveNewest + + + libopenal.1.dylib + PreserveNewest + + + MonoGame.Framework.dll.config + PreserveNewest + + + + + + {a069089a-8962-4607-b2b2-4cf4a371066e} + ShiftOS.Objects + + + {7c979b07-0585-4033-a110-e5555b9d6651} + ShiftOS.Engine + + + + + + + \ No newline at end of file diff --git a/ShiftOS.Frontend/ShiftOS.cs b/ShiftOS.Frontend/ShiftOS.cs new file mode 100644 index 0000000..90ebb4b --- /dev/null +++ b/ShiftOS.Frontend/ShiftOS.cs @@ -0,0 +1,117 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; +using ShiftOS.Frontend.GraphicsSubsystem; + +namespace ShiftOS.Frontend +{ + /// + /// This is the main type for your game. + /// + public class ShiftOS : Game + { + GraphicsDeviceManager GraphicsDevice; + SpriteBatch spriteBatch; + + public ShiftOS() + { + GraphicsDevice = new GraphicsDeviceManager(this); + Content.RootDirectory = "Content"; + //Make the mouse cursor visible. + this.IsMouseVisible = true; + + //Don't allow ALT+F4 + this.Window.AllowAltF4 = false; + + //Make window borderless + Window.IsBorderless = true; + + //Set the title + Window.Title = "ShiftOS"; + + + + //Fullscreen + GraphicsDevice.IsFullScreen = true; + + } + + /// + /// 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 + /// related content. Calling base.Initialize will enumerate through any components + /// and initialize them as well. + /// + 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 + + //Let's add a control to test something + var textControl = new GUI.TextControl(); + textControl.Width = 640; + textControl.Height = 480; + UIManager.AddTopLevel(textControl); + + + base.Initialize(); + + } + + /// + /// LoadContent will be called once per game and is the place to load + /// all of your content. + /// + protected override void LoadContent() + { + // Create a new SpriteBatch, which can be used to draw textures. + this.spriteBatch = new SpriteBatch(base.GraphicsDevice); + + // TODO: use this.Content to load your game content here + } + + /// + /// UnloadContent will be called once per game and is the place to unload + /// game-specific content. + /// + protected override void UnloadContent() + { + // TODO: Unload any non ContentManager content here + } + + /// + /// Allows the game to run logic such as updating the world, + /// checking for collisions, gathering input, and playing audio. + /// + /// Provides a snapshot of timing values. + protected override void Update(GameTime gameTime) + { + //Let's get the mouse state + var mouseState = Mouse.GetState(this.Window); + + //Now let's process it. + UIManager.ProcessMouseState(mouseState); + + base.Update(gameTime); + } + + /// + /// This is called when the game should draw itself. + /// + /// Provides a snapshot of timing values. + protected override void Draw(GameTime gameTime) + { + this.spriteBatch.Begin(); + //Draw the desktop BG. + var graphics = GraphicsDevice.GraphicsDevice; + UIManager.DrawBackgroundLayer(graphics, spriteBatch, 640, 480); + + //The desktop is drawn, now we can draw the UI. + UIManager.DrawControls(graphics, spriteBatch); + + spriteBatch.End(); + base.Draw(gameTime); + } + } +} diff --git a/ShiftOS.Frontend/Window.cs b/ShiftOS.Frontend/Window.cs new file mode 100644 index 0000000..4fd08a5 --- /dev/null +++ b/ShiftOS.Frontend/Window.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ShiftOS.Engine; + +namespace ShiftOS.Frontend +{ + public abstract class Window : IShiftOSWindow + { + public void OnLoad() + { + throw new NotImplementedException(); + } + + public void OnSkinLoad() + { + throw new NotImplementedException(); + } + + public bool OnUnload() + { + throw new NotImplementedException(); + } + + public void OnUpgrade() + { + throw new NotImplementedException(); + } + } +} diff --git a/ShiftOS.Frontend/app.manifest b/ShiftOS.Frontend/app.manifest new file mode 100644 index 0000000..048d329 --- /dev/null +++ b/ShiftOS.Frontend/app.manifest @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true/pm + + + + -- cgit v1.2.3