From 97a5a97370bc8f4d721791457d551c6bd2b0b67c Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 5 Aug 2017 22:57:40 -0400 Subject: [PATCH] skin loader and ui tint --- ShiftOS.Frontend/Apps/SkinLoader.cs | 365 +++++++++++++++++- ShiftOS.Frontend/Commands.cs | 14 + .../GraphicsSubsystem/GraphicsContext.cs | 2 + .../GraphicsSubsystem/UIManager.cs | 12 +- ShiftOS.Frontend/ShiftOS.cs | 7 +- ShiftOS_TheReturn/AppLauncherDaemon.cs | 2 +- 6 files changed, 392 insertions(+), 10 deletions(-) diff --git a/ShiftOS.Frontend/Apps/SkinLoader.cs b/ShiftOS.Frontend/Apps/SkinLoader.cs index 42f7a47..ebd693f 100644 --- a/ShiftOS.Frontend/Apps/SkinLoader.cs +++ b/ShiftOS.Frontend/Apps/SkinLoader.cs @@ -1,22 +1,377 @@ 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 Newtonsoft.Json; using ShiftOS.Engine; +using ShiftOS.Frontend.GraphicsSubsystem; +using ShiftOS.Frontend.GUI; using ShiftOS.Objects.ShiftFS; namespace ShiftOS.Frontend.Apps { + [WinOpen("skinloader")] + [DefaultTitle("Skin Loader")] + [Launcher("Skin Loader", false, null, "Customization")] [FileHandler("Skin Loader", ".skn", "")] - public class SkinLoader : IFileHandler + public class SkinLoader : GUI.Control, IShiftOSWindow, IFileHandler { + private Skin _skin = null; + + private Button _close = null; + private Button _default = null; + private Button _load = null; + private Button _save = null; + private Button _apply = null; + private Dictionary SkinTextures = new Dictionary(); + + public SkinLoader() + { + _close = new GUI.Button(); + _default = new GUI.Button(); + _load = new GUI.Button(); + _save = new GUI.Button(); + _apply = new GUI.Button(); + _close.AutoSize = true; + _default.AutoSize = true; + _load.AutoSize = true; + _save.AutoSize = true; + _apply.AutoSize = true; + AddControl(_close); + AddControl(_default); + AddControl(_load); + AddControl(_save); + AddControl(_apply); + + _close.Click += ()=> AppearanceManager.Close(this); + _default.Click += () => + { + _skin = new Skin(); + ResetPreviewData(); + }; + _load.Click += () => + { + FileSkimmerBackend.GetFile(new[] { ".skn" }, FileOpenerStyle.Open, (path) => + { + _skin = JsonConvert.DeserializeObject(Utils.ReadAllText(path)); + ResetPreviewData(); + }); + }; + _save.Click += () => + { + FileSkimmerBackend.GetFile(new[] { ".skn" }, FileOpenerStyle.Save, (path) => + { + string json = JsonConvert.SerializeObject(_skin, Formatting.Indented); + Utils.WriteAllText(path, json); + }); + + }; + _apply.Click += () => + { + string json = JsonConvert.SerializeObject(_skin, Formatting.Indented); + Utils.WriteAllText(Paths.GetPath("skin.json"), json); + SkinEngine.LoadSkin(); + Engine.Infobox.Show("Skin applied!", "The operating system's UI has been shifted successfully. Enjoy the new look!"); + }; + } + + public void ResetSkinTextures() + { + var graphics = UIManager.GraphicsDevice; + SkinTextures.Clear(); + foreach (var byteArray in SkinEngine.LoadedSkin.GetType().GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance).Where(x => x.FieldType == typeof(byte[]))) + { + var imgAttrib = byteArray.GetCustomAttributes(false).FirstOrDefault(x => x is ImageAttribute) as ImageAttribute; + if (imgAttrib != null) + { + var img = SkinEngine.ImageFromBinary((byte[])byteArray.GetValue(_skin)); + if (img != null) + { + var bmp = (System.Drawing.Bitmap)img; + var lck = bmp.LockBits(new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); + var data = new byte[Math.Abs(lck.Stride) * lck.Height]; + Marshal.Copy(lck.Scan0, data, 0, data.Length); + bmp.UnlockBits(lck); + var tex2 = new Texture2D(graphics, bmp.Width, bmp.Height); + for (int i = 0; i < data.Length; i += 4) + { + byte r = data[i]; + byte b = data[i + 2]; + if (r == 1 && b == 1 && data[i + 1] == 1) + { + data[i + 3] = 0; + } + data[i] = b; + data[i + 2] = r; + } + tex2.SetData(data); + SkinTextures.Add(imgAttrib.Name, tex2); + } + } + } + + foreach (var colorfield in SkinEngine.LoadedSkin.GetType().GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance).Where(x => x.FieldType == typeof(System.Drawing.Color))) + { + var color = (System.Drawing.Color)colorfield.GetValue(_skin); + var tex2 = new Texture2D(graphics, 1, 1); + tex2.SetData(new[] { color.R, color.G, color.B, color.A }); + SkinTextures.Add(colorfield.Name, tex2); + } + + var pureWhite = new Texture2D(graphics, 1, 1); + pureWhite.SetData(new byte[] { 255, 255, 255, 255 }); + SkinTextures.Add("PureWhite", pureWhite); + + } + + + public void PaintDesktop(GraphicsContext gfx) + { + //Let's get data for the desktop panel. + + //We need the width and the height and the position. + + int dp_height = _skin.DesktopPanelHeight; + int dp_position = (_skin.DesktopPanelPosition == 0) ? _desktopystart : _desktopystart + (_previewheight - dp_height); + int dp_width = _previewxwidth; + + //Alright, now we need to know if we should draw using a texture or a color + if (SkinTextures.ContainsKey("desktoppanel")) + { + //Draw with the texture + gfx.DrawRectangle(_previewxstart, dp_position, dp_width, dp_height, SkinTextures["desktoppanel"]); + } + else + { + //draw with a color + var color = SkinTextures["DesktopPanelColor"]; + gfx.DrawRectangle(_previewxstart, dp_position, dp_width, dp_height, color); + } + + //Alright, now App Launcher. + var al_left = _skin.AppLauncherFromLeft; + var holderSize = _skin.AppLauncherHolderSize; + if (SkinTextures.ContainsKey("applauncher")) + { + gfx.DrawRectangle(al_left.X, dp_position + al_left.Y, holderSize.Width, holderSize.Height, SkinTextures["applauncher"]); + } + var altextmeasure = gfx.MeasureString(_skin.AppLauncherText, _skin.AppLauncherFont); + int altextx = _previewxstart + (holderSize.Width - (int)altextmeasure.X) / 2; + int altexty = _desktopystart + (holderSize.Height - (int)altextmeasure.Y) / 2; + gfx.DrawString(_skin.AppLauncherText, altextx, altexty, _skin.AppLauncherTextColor.ToMonoColor(), _skin.AppLauncherFont); + //Panel clock. + + var panelClockRight = _skin.DesktopPanelClockFromRight; + var panelClockTextColor = _skin.DesktopPanelClockColor.ToMonoColor(); + + string dateTimeString = "00:00:00 - localhost"; + var measure = gfx.MeasureString(dateTimeString, _skin.DesktopPanelClockFont); + + int panelclockleft = _previewxstart + (dp_width - (int)measure.X); + int panelclockwidth = (dp_width - panelclockleft); + + if (SkinTextures.ContainsKey("panelclockbg")) + { + //draw the background using panelclock texture + gfx.DrawRectangle(panelclockleft, dp_position, panelclockwidth, dp_height, SkinTextures["panelclockbg"]); + } + else + { + //draw using the bg color + var pcBGColor = SkinTextures["DesktopPanelClockBackgroundColor"]; + gfx.DrawRectangle(panelclockleft, dp_position, panelclockwidth, dp_height, pcBGColor); + } + + int text_left = (panelclockwidth - (int)measure.X) / 2; + int text_top = (dp_height - (int)measure.Y) / 2; + + //draw string + gfx.DrawString(dateTimeString, panelclockleft + text_left, dp_position + text_top, panelClockTextColor, _skin.DesktopPanelClockFont); + + int initialGap = _previewxstart + _skin.PanelButtonHolderFromLeft; + int offset = initialGap; + + foreach (var pbtn in PanelButtons.ToArray()) + { + offset += _skin.PanelButtonFromLeft.X; + + int pbtnfromtop = _skin.PanelButtonFromTop; + int pbtnwidth = _skin.PanelButtonSize.Width; + int pbtnheight = _skin.PanelButtonSize.Height; + + //Draw panel button background... + if (SkinTextures.ContainsKey("panelbutton")) + { + gfx.DrawRectangle(offset, dp_position + pbtnfromtop, pbtnwidth, pbtnheight, SkinTextures["panelbutton"]); + } + else + { + gfx.DrawRectangle(offset, dp_position + pbtnfromtop, pbtnwidth, pbtnheight, SkinTextures["PanelButtonColor"]); + } + + //now we draw the text + + gfx.DrawString(pbtn.Title, offset + 2, dp_position + pbtnfromtop + 2, _skin.PanelButtonTextColor.ToMonoColor(), _skin.PanelButtonFont); + + offset += _skin.PanelButtonSize.Width; + } + + int alX = _previewxstart; + int alY = _desktopystart + dp_height; + int height = (LauncherItems[0].Height * LauncherItems.Count) + 2; + if (_skin.DesktopPanelPosition == 1) + alY = _desktopystart + ((_previewheight - dp_height) - height); + int width = LauncherItems[0].Width + 2; + gfx.DrawRectangle(alX, alY, width, height, SkinTextures["Menu_MenuBorder"]); + gfx.DrawRectangle(alX + 1, alY + 1, width - 2, height - 2, SkinTextures["Menu_ToolStripDropDownBackground"]); + gfx.DrawRectangle(alX + 1, alY + 1, 18, height - 2, SkinTextures["Menu_ImageMarginGradientBegin"]); + + foreach (var item in LauncherItems) + { + if (LauncherItems.IndexOf(item) == alSelectedItem) + { + gfx.DrawRectangle(alX + 1, alY + item.Y + 1, item.Width - 2, item.Height, SkinTextures["Menu_MenuItemSelected"]); + } + gfx.DrawString(Localization.Parse(item.Data.DisplayData.Name), alX + 21, alY + item.Y + 1, _skin.Menu_TextColor.ToMonoColor(), _skin.MainFont); + } + + + } + + public void ResetPreviewData() + { + LauncherItems = new List(); + int font_height = _skin.MainFont.Height; + LauncherItems.Add(new Desktop.AppLauncherItem + { + Data = new LauncherItem + { + DisplayData = new LauncherAttribute("Regular", false, null) + }, + Height = font_height, + Width = 200, + X = 0, + Y = 0 + }); + LauncherItems.Add(new Desktop.AppLauncherItem + { + Data = new LauncherItem + { + DisplayData = new LauncherAttribute("Highlighted", false, null) + }, + Height = font_height, + Width = 200, + X = 0, + Y = font_height + }); + PanelButtons = new List(); + PanelButtons.Add(new Desktop.PanelButtonData + { + Title = "Panel button text" + }); + ResetSkinTextures(); + } + + private readonly int alSelectedItem = 1; + private List LauncherItems = null; + private List PanelButtons = null; + + public void OnLoad() + { + if (_skin == null) + { + _skin = SkinEngine.LoadedSkin; + } + ResetPreviewData(); + } + + public void OnSkinLoad() + { + } + + public bool OnUnload() + { + while(SkinTextures.Count > 0) + { + var key = SkinTextures.First().Key; + SkinTextures[key].Dispose(); + SkinTextures.Remove(key); + } + return true; + } + + public void OnUpgrade() + { + } + public void OpenFile(string file) { - string skn = Utils.ReadAllText(file); - Utils.WriteAllText(Paths.GetPath("skin.json"), skn); - SkinEngine.LoadSkin(); - + _skin = JsonConvert.DeserializeObject(Utils.ReadAllText(file)); + AppearanceManager.SetupWindow(this); + } + + private int _bottomheight = 0; + private int _bottomstart = 0; + + private int _previewxstart = 0; + private int _previewxwidth = 0; + private int _desktopystart = 0; + private int _windowystart = 0; + private int _previewheight = 0; + + protected override void OnLayout(GameTime gameTime) + { + base.OnLayout(gameTime); + + _close.Text = "Close"; + _load.Text = "Load"; + _save.Text = "Save"; + _apply.Text = "Apply"; + _default.Text = "Load default"; + + _bottomheight = (_close.Height + 20); + _bottomstart = Height - _bottomheight; + _close.X = 10; + _close.Y = _bottomstart + ((_bottomheight - _close.Height) / 2); + _default.X = _close.X + _close.Width + 10; + _default.Y = _bottomstart + ((_bottomheight - _close.Height) / 2); + _load.X = _default.X + _default.Width + 10; + _load.Y = _bottomstart + ((_bottomheight - _close.Height) / 2); + _save.X = _load.X + _load.Width + 10; + _save.Y = _bottomstart + ((_bottomheight - _close.Height) / 2); + _apply.X = Width - _apply.Width - 10; + _apply.Y = _bottomstart + ((_bottomheight - _close.Height) / 2); + + _previewxstart = 10; + _previewxwidth = Width - 20; + _previewheight = ((Height - _bottomheight) / 2) - 20; + _desktopystart = 10; + _windowystart = _desktopystart + _previewheight + 10; + Invalidate(); + } + + protected override void OnPaint(GraphicsContext gfx) + { + /*Paint the desktop rect*/ + + //First we gotta paint the desktop bg. + //I'll steal the code from UIManager.DrawBackground() + + gfx.DrawRectangle(_previewxstart, _desktopystart, _previewxwidth, _previewheight, SkinTextures["DesktopColor"]); + if (SkinTextures.ContainsKey("desktopbackground")) + { + gfx.DrawRectangle(_previewxstart, _desktopystart, _previewxwidth, _previewheight, SkinTextures["desktopbackground"]); + } + //Now we actually paint the ui + PaintDesktop(gfx); + + //Paint the window rect + gfx.DrawRectangle(_previewxstart, _windowystart, _previewxwidth, _previewheight, Color.Red); + } } } diff --git a/ShiftOS.Frontend/Commands.cs b/ShiftOS.Frontend/Commands.cs index 19d229c..29d8409 100644 --- a/ShiftOS.Frontend/Commands.cs +++ b/ShiftOS.Frontend/Commands.cs @@ -41,11 +41,25 @@ using ShiftOS.Objects; using ShiftOS.Engine.Scripting; using ShiftOS.Objects.ShiftFS; using ShiftOS.Engine; +using Microsoft.Xna.Framework; +using ShiftOS.Frontend.GraphicsSubsystem; namespace ShiftOS.Frontend { public static class FrontendDebugCommands { + [Command("set_ui_tint")] + [RequiresArgument("color")] + [ShellConstraint("shiftos_debug> ")] + public static void SetUITint (Dictionary args) + { + string[] split = args["color"].ToString().Split(';'); + int r = MathHelper.Clamp(Convert.ToInt32(split[0]), 0, 255); + int g = MathHelper.Clamp(Convert.ToInt32(split[1]), 0, 255); + int b = MathHelper.Clamp(Convert.ToInt32(split[2]), 0, 255); + UIManager.SetUITint(new Color(r, g, b, 255)); + } + [Command("drop_opener")] [ShellConstraint("shiftos_debug> ")] public static void DropOpener(Dictionary args) diff --git a/ShiftOS.Frontend/GraphicsSubsystem/GraphicsContext.cs b/ShiftOS.Frontend/GraphicsSubsystem/GraphicsContext.cs index 217eb33..2a33331 100644 --- a/ShiftOS.Frontend/GraphicsSubsystem/GraphicsContext.cs +++ b/ShiftOS.Frontend/GraphicsSubsystem/GraphicsContext.cs @@ -168,6 +168,8 @@ namespace ShiftOS.Frontend.GraphicsSubsystem gfx.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; gfx.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality; gfx.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit; + gfx.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver; + gfx.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality; gfx.DrawString(text, font, System.Drawing.Brushes.Black, new System.Drawing.RectangleF(0, 0, bmp.Width, bmp.Height), sFormat); } diff --git a/ShiftOS.Frontend/GraphicsSubsystem/UIManager.cs b/ShiftOS.Frontend/GraphicsSubsystem/UIManager.cs index 18e924a..dc47e93 100644 --- a/ShiftOS.Frontend/GraphicsSubsystem/UIManager.cs +++ b/ShiftOS.Frontend/GraphicsSubsystem/UIManager.cs @@ -92,7 +92,7 @@ namespace ShiftOS.Frontend.GraphicsSubsystem } } - batch.Draw(_target, new Rectangle(ctrl.X, ctrl.Y, ctrl.Width, ctrl.Height), Color.White); + batch.Draw(_target, new Rectangle(ctrl.X, ctrl.Y, ctrl.Width, ctrl.Height), _game.UITint); } } } @@ -240,6 +240,11 @@ namespace ShiftOS.Frontend.GraphicsSubsystem } + public static void SetUITint(Color color) + { + _game.UITint = color; + } + public static bool ExperimentalEffects = true; @@ -250,10 +255,13 @@ namespace ShiftOS.Frontend.GraphicsSubsystem { if (SkinEngine.LoadedSkin == null) SkinEngine.Init(); + + batch.Draw(SkinTextures["DesktopColor"], new Rectangle(0, 0, Viewport.Width, Viewport.Height), _game.UITint); + graphics.Clear(SkinEngine.LoadedSkin.DesktopColor.ToMonoColor()); if (SkinTextures.ContainsKey("desktopbackground")) { - batch.Draw(SkinTextures["desktopbackground"], new Rectangle(0, 0, Viewport.Width, Viewport.Height), Color.White); + batch.Draw(SkinTextures["desktopbackground"], new Rectangle(0, 0, Viewport.Width, Viewport.Height), _game.UITint); } } diff --git a/ShiftOS.Frontend/ShiftOS.cs b/ShiftOS.Frontend/ShiftOS.cs index 65ec7aa..7cc142e 100644 --- a/ShiftOS.Frontend/ShiftOS.cs +++ b/ShiftOS.Frontend/ShiftOS.cs @@ -29,6 +29,8 @@ namespace ShiftOS.Frontend private bool failEnded = false; private double failCharAddMS = 0; + public Color UITint = Color.White; + private bool DisplayDebugInfo = false; private KeyboardListener keyboardListener = new KeyboardListener (); @@ -59,7 +61,7 @@ namespace ShiftOS.Frontend ); Content.RootDirectory = "Content"; - + graphicsDevice.PreferMultiSampling = true; //Make window borderless Window.IsBorderless = false; @@ -327,9 +329,10 @@ namespace ShiftOS.Frontend var rasterizerState = new RasterizerState(); rasterizerState.CullMode = CullMode.None; rasterizerState.MultiSampleAntiAlias = true; + spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied, - SamplerState.LinearClamp, DepthStencilState.Default, + SamplerState.LinearWrap, DepthStencilState.Default, rasterizerState); //Draw the desktop BG. UIManager.DrawBackgroundLayer(GraphicsDevice, spriteBatch, 640, 480); diff --git a/ShiftOS_TheReturn/AppLauncherDaemon.cs b/ShiftOS_TheReturn/AppLauncherDaemon.cs index 2d21a8c..8793cde 100644 --- a/ShiftOS_TheReturn/AppLauncherDaemon.cs +++ b/ShiftOS_TheReturn/AppLauncherDaemon.cs @@ -101,7 +101,7 @@ namespace ShiftOS.Engine /// /// Display data including icons, names, and the category of the item. /// - public LauncherAttribute DisplayData { get; internal set; } + public LauncherAttribute DisplayData { get; set; } /// /// A .NET that is associated with this item. ///