diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index aa0a72087..41efbcbda 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -6,7 +6,15 @@ "version": "2.2.1", "commands": [ "InheritDoc" - ] + ], + "rollForward": false + }, + "dotnet-mgcb": { + "version": "3.8.1.303", + "commands": [ + "mgcb" + ], + "rollForward": false } } } \ No newline at end of file diff --git a/MSBuild/Common.props b/MSBuild/Common.props index b351871ee..763fb3bbb 100644 --- a/MSBuild/Common.props +++ b/MSBuild/Common.props @@ -4,13 +4,13 @@ $(AssemblyName).xml - 10.4.0 + 10.4.1 10.4.0 - 10.4.0 - 10.4.0 - 10.4.0 - 10.4.0 - 10.4.0 + 10.4.1 + 10.4.1 + 10.4.1 + 10.4.1 + 10.4.1 Thraka diff --git a/MiscCodeFragments/Layout/LayoutScreen.cs b/MiscCodeFragments/Layout/LayoutScreen.cs new file mode 100644 index 000000000..c6b24dfb4 --- /dev/null +++ b/MiscCodeFragments/Layout/LayoutScreen.cs @@ -0,0 +1,151 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Gum; +using Gum.Wireframe; +using RenderingLibrary; +using RenderingLibrary.Graphics; + +namespace SadConsole.Layout; + +public class LayoutScreen: ScreenObject +{ + public LayoutScreen(GraphicalUiElement root) + { + + } +} + +public enum ObjectType +{ + Surface, + AnimatedSurface, + Console, + ControlsConsole +} + +public struct LayoutSettings +{ + public string Name { get; init; } + public ObjectType ObjectType { get; init; } +} + +public class LayoutElement :GraphicalUiElement +{ + +} + +public class LayoutRenderable : IVisible, IRenderableIpso, IRenderable, IPositionedSizedObject, ISetClipsChildren +{ + private ObservableCollection children = new ObservableCollection(); + + private float height; + + private IRenderableIpso mParent; + + public bool AbsoluteVisible + { + get + { + if (((IVisible)this).Parent == null) + { + return Visible; + } + + return Visible && ((IVisible)this).Parent.AbsoluteVisible; + } + } + + public BlendState BlendState => Gum.BlendState.NonPremultiplied; + + public ObservableCollection Children => children; + + ColorOperation IRenderableIpso.ColorOperation => ColorOperation.Modulate; + + public bool ClipsChildren { get; set; } + + public float Height + { + get + { + return height; + } + set + { + if (float.IsPositiveInfinity(value)) + { + throw new ArgumentException(); + } + + height = value; + } + } + + public string Name { get; set; } + + public IRenderableIpso Parent + { + get + { + return mParent; + } + set + { + if (mParent != value) + { + if (mParent != null) + { + mParent.Children.Remove(this); + } + + mParent = value; + if (mParent != null) + { + mParent.Children.Add(this); + } + } + } + } + + public float Rotation { get; set; } + + public object Tag { get; set; } + + public bool Visible { get; set; } = true; + + + public float Width { get; set; } + + public bool Wrap => false; + + public float X { get; set; } + + public float Y { get; set; } + + public float Z { get; set; } + + public bool FlipHorizontal { get; set; } + + IVisible IVisible.Parent => Parent; + + public void PreRender() + { + } + + public void Render(ISystemManagers managers) + { + } + + void IRenderableIpso.SetParentDirect(IRenderableIpso parent) + { + mParent = parent; + } + + public override string ToString() + { + return Name; + } +} diff --git a/SadConsole.Debug.MonoGame/Debugger.cs b/SadConsole.Debug.MonoGame/Debugger.cs index 5e41c27fd..96913b9df 100644 --- a/SadConsole.Debug.MonoGame/Debugger.cs +++ b/SadConsole.Debug.MonoGame/Debugger.cs @@ -29,12 +29,12 @@ public static bool IsOpened /// public static void BasicInit() { - _imGui = new ImGuiMonoGameComponent(SadConsole.Host.Global.GraphicsDeviceManager, Game.Instance.MonoGameInstance, true); + _imGui = new ImGuiMonoGameComponent(SadConsole.Host.Global.GraphicsDeviceManager, (Microsoft.Xna.Framework.Game)Game.Instance.MonoGameInstance, true); //_imGui.Font = "Roboto-Regular.ttf"; //_imGui.fontSize = 14f; Game.Instance.MonoGameInstance.Components.Add(_imGui); - SadConsole.Game.Instance.MonoGameInstance.SadConsoleComponent.Enabled = false; + Host.Global.SadConsoleComponent.Enabled = false; } @@ -45,19 +45,19 @@ public static void Start() _imGui.Visible = true; _imGui.Enabled = true; - SadConsole.Game.Instance.MonoGameInstance.SadConsoleComponent.Enabled = false; + Host.Global.SadConsoleComponent.Enabled = false; SadConsole.Settings.DoFinalDraw = GuiState.ShowSadConsoleRendering; return; } - SadConsole.Game.Instance.MonoGameInstance.SadConsoleComponent.Enabled = false; + Host.Global.SadConsoleComponent.Enabled = false; SadConsole.Settings.DoFinalDraw = GuiState.ShowSadConsoleRendering; //SadConsole.Game.Instance.MonoGameInstance.ClearScreenComponent.Visible = false; //SadConsole.Game.Instance.MonoGameInstance.ClearScreenComponent.Enabled = false; - _imGui = new ImGuiMonoGameComponent(SadConsole.Host.Global.GraphicsDeviceManager, Game.Instance.MonoGameInstance, true); + _imGui = new ImGuiMonoGameComponent(SadConsole.Host.Global.GraphicsDeviceManager, (Microsoft.Xna.Framework.Game)Game.Instance.MonoGameInstance, true); //_imGui.Font = "Roboto-Regular.ttf"; //_imGui.fontSize = 14f; //ImGui.Theme = coolTheme; @@ -91,8 +91,8 @@ private static void _imGui_HostClosed(object sender, EventArgs e) => public static void Stop() { - SadConsole.Game.Instance.MonoGameInstance.SadConsoleComponent.Visible = true; - SadConsole.Game.Instance.MonoGameInstance.SadConsoleComponent.Enabled = true; + Host.Global.SadConsoleComponent.Visible = true; + Host.Global.SadConsoleComponent.Enabled = true; SadConsole.Settings.DoFinalDraw = true; //SadConsole.Game.Instance.MonoGameInstance.ClearScreenComponent.Visible = true; diff --git a/SadConsole.Debug.MonoGame/ImGuiSystem/ImGuiWindow.cs b/SadConsole.Debug.MonoGame/ImGuiSystem/ImGuiWindow.cs index ac3c853bd..379a3b759 100644 --- a/SadConsole.Debug.MonoGame/ImGuiSystem/ImGuiWindow.cs +++ b/SadConsole.Debug.MonoGame/ImGuiSystem/ImGuiWindow.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Text; +using ImGuiNET; namespace SadConsole.ImGuiSystem { @@ -16,5 +17,30 @@ public abstract class ImGuiWindow : ImGuiObjectBase protected void OnClosed() => Closed?.Invoke(this, EventArgs.Empty); + + + public static bool DrawButtons(out bool result, bool acceptDisabled = false) + { + bool buttonClicked = false; + result = false; + + ImGui.Separator(); + + if (ImGui.Button("Cancel")) { buttonClicked = true; } + + // Right-align button + float pos = ImGui.GetItemRectSize().X + ImGui.GetStyle().ItemSpacing.X; + ImGui.SameLine(ImGui.GetWindowWidth() - pos); + + ImGui.BeginDisabled(acceptDisabled); + if (ImGui.Button("Accept")) + { + buttonClicked = true; + result = true; + } + ImGui.EndDisabled(); + + return buttonClicked; + } } } diff --git a/SadConsole.Debug.MonoGame/ImGuiTypes/ImGuiGroupPanel.cs b/SadConsole.Debug.MonoGame/ImGuiTypes/ImGuiGroupPanel.cs index 65c07ab07..e0f95ee4a 100644 --- a/SadConsole.Debug.MonoGame/ImGuiTypes/ImGuiGroupPanel.cs +++ b/SadConsole.Debug.MonoGame/ImGuiTypes/ImGuiGroupPanel.cs @@ -18,7 +18,7 @@ public static void BeginGroupPanel(string name) => public static void BeginGroupPanel(string name, Vector2 size) { ImGui.BeginGroup(); - + ImGui.PushID($"panel_{name}"); var cursorPos = ImGui.GetCursorScreenPos(); var itemSpacing = ImGui.GetStyle().ItemSpacing; var framePadding = ImGui.GetStyle().FramePadding; @@ -129,6 +129,7 @@ public unsafe static void EndGroupPanel() ImGui.Dummy(new Vector2(0.0f, 0.0f)); + ImGui.PopID(); ImGui.EndGroup(); } } diff --git a/SadConsole.Host.FNA/SadConsole.Host.FNA.csproj b/SadConsole.Host.FNA/SadConsole.Host.FNA.csproj index 11de8cf3f..9616c0e5b 100644 --- a/SadConsole.Host.FNA/SadConsole.Host.FNA.csproj +++ b/SadConsole.Host.FNA/SadConsole.Host.FNA.csproj @@ -10,9 +10,7 @@ SadConsole.Host.FNA sadconsole;fna;roguelike;cli;xna;game;development;console;ansi;ascii;textmode;dotnet -- Reversioned to follow new versioning scheme. -- Configuration namespace was moved to SadConsole directly. Only library specific config options are here now. -- Package contains all dependencies now. +Cursor rendering has changed to draw on the hosting surface. Previously it was rendered on top as an entire separate process. diff --git a/SadConsole.Host.FNA/SadConsole.Host.FNA.xml b/SadConsole.Host.FNA/SadConsole.Host.FNA.xml index 93aa3a80e..e3f591d6e 100644 --- a/SadConsole.Host.FNA/SadConsole.Host.FNA.xml +++ b/SadConsole.Host.FNA/SadConsole.Host.FNA.xml @@ -151,10 +151,11 @@ Helps manage the while draw calls are drawing. - + Resumes rendering to with SadConsole's default settings. + When true, skips assinging as the render target. @@ -171,6 +172,11 @@ The image to draw. + + + The shader to use when drawing the texture. + + Where on the to draw the texture. @@ -181,13 +187,14 @@ A color tint to apply when drawn. - + Creates a new instance of this draw call. The image to draw. The position on the to draw the image. A color tint to apply to the drawn image. + A shader to apply to the texture being drawn. Thrown when is null. @@ -368,9 +375,9 @@ The settings used in creating the game. - + - Method called by the class for initializing SadConsole specifics. Called prior to . + Method called by the class for initializing SadConsole specifics. Called prior to . The game instance. @@ -546,41 +553,34 @@ - - - A MonoGame instance that runs SadConsole. - - - + A MonoGame component that clears the screen with the color. - + + + + - + A component to draw how many frames per second the engine is performing at. - + - + - + - - - The game component to control SadConsole updates, input, and rendering. - - - + - The game component that clears the render output before each frame draw. + A MonoGame instance that runs SadConsole. @@ -606,7 +606,12 @@ - + + + Global variables used by the MonoGame host. + + + Resizes the by the specified font size. @@ -616,68 +621,66 @@ Additional pixel width to add to the resize. Additional pixel height to add to the resize. - + Regenerates the if the desired size doesn't match the current size. The width of the render output. The height of the render output. - + - Resets the target and determines the appropriate and based on the window or fullscreen state. + When , prevents the keyboard and mouse logic from running. - + - A game component that handles updating, input, and rendering of SadConsole. + The graphics device created by MonoGame. - + - Draws the SadConsole frame through draw calls when is true. + A sprite batch used by all of SadConsole to render objects. - Time between drawing frames. - + - Updates the SadConsole game objects and handles input. Only runs when is true. + The output texture. After each screen in SadConsole is drawn, they're then drawn on this output texture to compose the final scene. - - + - Global variables used by the MonoGame host. + Reference to the game timer used in the MonoGame update loop. - + - When , prevents the keyboard and mouse logic from running. + Reference to the game timer used in the MonoGame render loop. - + - The graphics device created by MonoGame. + Regenerates the if the desired size doesn't match the current size. - + - A sprite batch used by all of SadConsole to render objects. + Resizes the by the specified font size. - + - The output texture. After each screen in SadConsole is drawn, they're then drawn on this output texture to compose the final scene. + Resets the target and determines the appropriate and based on the window or fullscreen state. - + - Reference to the game timer used in the MonoGame update loop. + The game component to control SadConsole updates, input, and rendering. - + - Reference to the game timer used in the MonoGame render loop. + The game component that clears the render output before each frame draw. @@ -690,6 +693,48 @@ Sets the render target to , targeting the app window. + + + Resizes the by the specified font size. + + The size of the font to base the final values on. + The count of glyphs along the X-axis. + The count of glyphs along the Y-axis. + Additional pixel width to add to the resize. + Additional pixel height to add to the resize. + + + + Regenerates the if the desired size doesn't match the current size. + + The width of the render output. + The height of the render output. + + + + Resets the target and determines the appropriate and based on the window or fullscreen state. + + + + + A game component that handles updating, input, and rendering of SadConsole. + + + + + + + + Draws the SadConsole frame through draw calls when is true. + + Time between drawing frames. + + + + Updates the SadConsole game objects and handles input. Only runs when is true. + + + A settings class usually used when creating the host object. @@ -1016,7 +1061,7 @@ - + @@ -1152,6 +1197,11 @@ + + + The shader to use when drawing the surface. + + Not used. @@ -1370,7 +1420,7 @@ The builder object that composes the game startup. The configuration builder. - + The method is called by the MonoGame constructor. Some MonoGame specific settings may only be settable via the constructor. @@ -1378,7 +1428,7 @@ A method. The configuration object. - + Internal only. Called by the MonoGame game to finish configuring SadConsole. @@ -1386,6 +1436,13 @@ A method. The configuration object. + + + When called, tells the game host not to create the monogame game instance at . + + The builder object that composes the game startup. + The configuration object. + Extensions for the type. diff --git a/SadConsole.Host.MonoGame/DrawCalls/DrawCallManager.cs b/SadConsole.Host.MonoGame/DrawCalls/DrawCallManager.cs index 19539da07..49cfa5822 100644 --- a/SadConsole.Host.MonoGame/DrawCalls/DrawCallManager.cs +++ b/SadConsole.Host.MonoGame/DrawCalls/DrawCallManager.cs @@ -12,10 +12,13 @@ public static class DrawCallManager /// /// Resumes rendering to with SadConsole's default settings. /// + /// When true, skips assinging as the render target. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ResumeBatch() + public static void ResumeBatch(bool skipSetRenderTarget = false) { - Global.GraphicsDevice.SetRenderTarget(Global.RenderOutput); + if (!skipSetRenderTarget) + Global.GraphicsDevice.SetRenderTarget(Global.RenderOutput); + Global.SharedSpriteBatch.Begin(SpriteSortMode.Deferred, SadConsole.Host.Settings.MonoGameScreenBlendState, SamplerState.PointClamp, DepthStencilState.DepthRead, RasterizerState.CullNone); } diff --git a/SadConsole.Host.MonoGame/DrawCalls/DrawCallTexture.cs b/SadConsole.Host.MonoGame/DrawCalls/DrawCallTexture.cs index 47f7eb697..6aca720e7 100644 --- a/SadConsole.Host.MonoGame/DrawCalls/DrawCallTexture.cs +++ b/SadConsole.Host.MonoGame/DrawCalls/DrawCallTexture.cs @@ -13,6 +13,11 @@ public class DrawCallTexture : IDrawCall /// public Texture2D Texture; + /// + /// The shader to use when drawing the texture. + /// + public Effect ShaderEffect; + /// /// Where on the to draw the texture. /// @@ -29,12 +34,11 @@ public class DrawCallTexture : IDrawCall /// The image to draw. /// The position on the to draw the image. /// A color tint to apply to the drawn image. + /// A shader to apply to the texture being drawn. /// Thrown when is null. - public DrawCallTexture(Texture2D texture, Vector2 position, Color? tint = null) + public DrawCallTexture(Texture2D texture, Vector2 position, Color? tint = null, Effect effect = null) { - if (texture == null) throw new System.NullReferenceException($"{nameof(texture)} cannot be null."); - - Texture = texture; + Texture = texture ?? throw new System.NullReferenceException($"{nameof(texture)} cannot be null."); Position = position; if (tint.HasValue) @@ -42,9 +46,26 @@ public DrawCallTexture(Texture2D texture, Vector2 position, Color? tint = null) else Tint = Color.White; + ShaderEffect = effect; + } /// - public void Draw() => - Host.Global.SharedSpriteBatch.Draw(Texture, Position, Tint); + public void Draw() + { + if (ShaderEffect == null) + { + Host.Global.SharedSpriteBatch.Draw(Texture, Position, Tint); + } + else + { + DrawCallManager.InterruptBatch(); + + Host.Global.SharedSpriteBatch.Begin(SpriteSortMode.Deferred, Host.Settings.MonoGameScreenBlendState, SamplerState.PointClamp, DepthStencilState.DepthRead, RasterizerState.CullNone, ShaderEffect); + Host.Global.SharedSpriteBatch.Draw(Texture, Position, Tint); + Host.Global.SharedSpriteBatch.End(); + + DrawCallManager.ResumeBatch(true); + } + } } diff --git a/SadConsole.Host.MonoGame/Game.Mono.cs b/SadConsole.Host.MonoGame/Game.Mono.cs index f2890c021..4c19a3a69 100644 --- a/SadConsole.Host.MonoGame/Game.Mono.cs +++ b/SadConsole.Host.MonoGame/Game.Mono.cs @@ -39,7 +39,7 @@ public sealed partial class Game : GameHost /// /// The instance. /// - public Host.Game MonoGameInstance { get; private set; } + public Microsoft.Xna.Framework.Game MonoGameInstance { get; set; } /// /// Strongly typed version of . @@ -115,7 +115,9 @@ public static void Create(Builder configuration) var game = new Game(); // Make sure the MonoGame Game instance calls back to SadConsole config after it's initialized. - configuration.WithMonoGameInit(game.MonoGameInit); + MonoGameCallbackConfig config = configuration.GetOrCreateConfig(); + if (config.MonoGameInitCallback == null) + configuration.WithMonoGameInit(game.MonoGameInit); game._configuration = configuration; @@ -128,14 +130,15 @@ public static void Create(Builder configuration) // When the user then calls this object's .Run method, monogame initializes itself // and after it's done, calls back to game.MonoGameInit to finish SadConsole // init. - game.MonoGameInstance = new Host.Game(); + if (!config.SkipMonoGameGameCreation) + game.MonoGameInstance = new Host.Game(); } /// /// Method called by the class for initializing SadConsole specifics. Called prior to . /// /// The game instance. - private void MonoGameInit(Host.Game game) + private void MonoGameInit(Microsoft.Xna.Framework.Game game) { if (_configuration == null) throw new Exception("Configuration must be set."); @@ -158,7 +161,7 @@ private void MonoGameInit(Host.Game game) ScreenCellsX = startupData.ScreenCellsX; ScreenCellsY = startupData.ScreenCellsY; - MonoGameInstance.ResizeGraphicsDeviceManager(DefaultFont.GetFontSize(DefaultFontSize).ToMonoPoint(), ScreenCellsX, ScreenCellsY, 0, 0); + SadConsole.Host.Global.ResizeGraphicsDeviceManager(DefaultFont.GetFontSize(DefaultFontSize).ToMonoPoint(), ScreenCellsX, ScreenCellsY, 0, 0); // Setup renderers SetRenderer(Renderers.Constants.RendererNames.Default, typeof(Renderers.ScreenSurfaceRenderer)); @@ -183,7 +186,7 @@ private void MonoGameInit(Host.Game game) // Load special FPS visual FpsConfig? fpsConfig = _configuration.Configs.OfType().FirstOrDefault(); if (fpsConfig != null && fpsConfig.ShowFPSVisual) - Instance.MonoGameInstance.Components.Add(new Host.Game.FPSCounterComponent(Instance.MonoGameInstance)); + Instance.MonoGameInstance.Components.Add(new Host.FPSCounterComponent((Microsoft.Xna.Framework.Game)Instance.MonoGameInstance)); // Run all startup config objects _configuration.Run(this); @@ -286,7 +289,7 @@ public void ToggleFullScreen() } } - Instance.MonoGameInstance.ResetRendering(); + SadConsole.Host.Global.ResetRendering(); } /// @@ -302,7 +305,7 @@ public override void ResizeWindow(int width, int height, bool resizeOutputSurfac SadConsole.Settings.Rendering.RenderHeight = Host.Global.GraphicsDeviceManager.PreferredBackBufferHeight; } - Instance.MonoGameInstance.ResetRendering(); + SadConsole.Host.Global.ResetRendering(); } internal void InvokeFrameDraw() => diff --git a/SadConsole.Host.MonoGame/Game.Wpf.cs b/SadConsole.Host.MonoGame/Game.Wpf.cs index 4a43a6384..2a0605280 100644 --- a/SadConsole.Host.MonoGame/Game.Wpf.cs +++ b/SadConsole.Host.MonoGame/Game.Wpf.cs @@ -2,6 +2,7 @@ using System; using System.IO; using System.Linq; +using MonoGame.Framework.WpfInterop; using SadConsole.Configuration; using SadRogue.Primitives; @@ -115,7 +116,7 @@ public static void Create(Builder configuration) /// Method called by the class for initializing SadConsole specifics. Called prior to . /// /// The game instance. - internal void MonoGameInit(Host.Game game) + internal void MonoGameInit(WpfGame game) { if (_configuration == null) throw new Exception("Configuration must be set."); diff --git a/SadConsole.Host.MonoGame/MonoGame/ClearScreenGameComponent.Mono.cs b/SadConsole.Host.MonoGame/MonoGame/ClearScreenGameComponent.Mono.cs index c90e4e363..bff307344 100644 --- a/SadConsole.Host.MonoGame/MonoGame/ClearScreenGameComponent.Mono.cs +++ b/SadConsole.Host.MonoGame/MonoGame/ClearScreenGameComponent.Mono.cs @@ -3,20 +3,18 @@ namespace SadConsole.Host; -public partial class Game +/// +/// A MonoGame component that clears the screen with the color. +/// +public class ClearScreenGameComponent : DrawableGameComponent { - /// - /// A MonoGame component that clears the screen with the color. - /// - public class ClearScreenGameComponent : DrawableGameComponent - { - internal ClearScreenGameComponent(Game game) : base(game) => DrawOrder = 0; + /// + public ClearScreenGameComponent(Microsoft.Xna.Framework.Game game) : base(game) => DrawOrder = 0; - /// - public override void Draw(GameTime gameTime) - { - Game.GraphicsDevice.SetRenderTarget(null); - Game.GraphicsDevice.Clear(SadConsole.Settings.ClearColor.ToMonoColor()); - } + /// + public override void Draw(GameTime gameTime) + { + Game.GraphicsDevice.SetRenderTarget(null); + Game.GraphicsDevice.Clear(SadConsole.Settings.ClearColor.ToMonoColor()); } } diff --git a/SadConsole.Host.MonoGame/MonoGame/ClearScreenGameComponent.Wpf.cs b/SadConsole.Host.MonoGame/MonoGame/ClearScreenGameComponent.Wpf.cs index 64cce6013..c963bd6cf 100644 --- a/SadConsole.Host.MonoGame/MonoGame/ClearScreenGameComponent.Wpf.cs +++ b/SadConsole.Host.MonoGame/MonoGame/ClearScreenGameComponent.Wpf.cs @@ -5,20 +5,17 @@ namespace SadConsole.Host; -public partial class Game +/// +/// A MonoGame component that clears the screen with the color. +/// +public class ClearScreenGameComponent : WpfDrawableGameComponent { - /// - /// A MonoGame component that clears the screen with the color. - /// - public class ClearScreenGameComponent : WpfDrawableGameComponent - { - internal ClearScreenGameComponent(Game game) : base(game) => DrawOrder = 0; + internal ClearScreenGameComponent(Game game) : base(game) => DrawOrder = 0; - /// - public override void Draw(GameTime gameTime) - { - Global.GraphicsDeviceWpfControl = (RenderTarget2D)GraphicsDevice.GetRenderTargets()[0].RenderTarget; - Game.GraphicsDevice.Clear(SadConsole.Settings.ClearColor.ToMonoColor()); - } + /// + public override void Draw(GameTime gameTime) + { + Global.GraphicsDeviceWpfControl = (RenderTarget2D)GraphicsDevice.GetRenderTargets()[0].RenderTarget; + Game.GraphicsDevice.Clear(SadConsole.Settings.ClearColor.ToMonoColor()); } } diff --git a/SadConsole.Host.MonoGame/MonoGame/FPSCounterComponent.Mono.cs b/SadConsole.Host.MonoGame/MonoGame/FPSCounterComponent.Mono.cs index 4ae85d3b5..55a2d0292 100644 --- a/SadConsole.Host.MonoGame/MonoGame/FPSCounterComponent.Mono.cs +++ b/SadConsole.Host.MonoGame/MonoGame/FPSCounterComponent.Mono.cs @@ -6,54 +6,51 @@ namespace SadConsole.Host; -public partial class Game +/// +/// A component to draw how many frames per second the engine is performing at. +/// +public class FPSCounterComponent : DrawableGameComponent { - /// - /// A component to draw how many frames per second the engine is performing at. - /// - public class FPSCounterComponent : DrawableGameComponent + private readonly Console surface; + private int frameRate = 0; + private int frameCounter = 0; + private TimeSpan delta = TimeSpan.Zero; + + /// + public FPSCounterComponent(Microsoft.Xna.Framework.Game game) + : base(game) { - private readonly Console surface; - private int frameRate = 0; - private int frameCounter = 0; - private TimeSpan delta = TimeSpan.Zero; + surface = new Console(30, 1); + surface.Surface.DefaultBackground = Color.Black; + surface.Clear(); + DrawOrder = 8; + Global.GraphicsDevice.PresentationParameters.RenderTargetUsage = RenderTargetUsage.PreserveContents; + } - /// - public FPSCounterComponent(Microsoft.Xna.Framework.Game game) - : base(game) - { - surface = new Console(30, 1); - surface.Surface.DefaultBackground = Color.Black; - surface.Clear(); - DrawOrder = 8; - Global.GraphicsDevice.PresentationParameters.RenderTargetUsage = RenderTargetUsage.PreserveContents; - } + /// + public override void Update(GameTime gameTime) + { + delta += gameTime.ElapsedGameTime; - /// - public override void Update(GameTime gameTime) + if (delta > TimeSpan.FromSeconds(1)) { - delta += gameTime.ElapsedGameTime; - - if (delta > TimeSpan.FromSeconds(1)) - { - delta -= TimeSpan.FromSeconds(1); - frameRate = frameCounter; - frameCounter = 0; - } + delta -= TimeSpan.FromSeconds(1); + frameRate = frameCounter; + frameCounter = 0; } + } - /// - public override void Draw(GameTime gameTime) - { - frameCounter++; - surface.Clear(); - surface.Print(0, 0, $"fps: {frameRate}", Color.White, Color.Black); - surface.Render(gameTime.ElapsedGameTime); - - Game.GraphicsDevice.SetRenderTarget(null); - Global.SharedSpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.PointClamp, DepthStencilState.None, RasterizerState.CullNone); - Global.SharedSpriteBatch.Draw(((GameTexture)surface.Renderer.Output).Texture, Vector2.Zero, XnaColor.White); - Global.SharedSpriteBatch.End(); - } + /// + public override void Draw(GameTime gameTime) + { + frameCounter++; + surface.Clear(); + surface.Print(0, 0, $"fps: {frameRate}", Color.White, Color.Black); + surface.Render(gameTime.ElapsedGameTime); + + Game.GraphicsDevice.SetRenderTarget(null); + Global.SharedSpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.PointClamp, DepthStencilState.None, RasterizerState.CullNone); + Global.SharedSpriteBatch.Draw(((GameTexture)surface.Renderer.Output).Texture, Vector2.Zero, XnaColor.White); + Global.SharedSpriteBatch.End(); } } diff --git a/SadConsole.Host.MonoGame/MonoGame/Game.Mono.cs b/SadConsole.Host.MonoGame/MonoGame/Game.Mono.cs index b34378d21..c15de3fb8 100644 --- a/SadConsole.Host.MonoGame/MonoGame/Game.Mono.cs +++ b/SadConsole.Host.MonoGame/MonoGame/Game.Mono.cs @@ -3,6 +3,7 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using SadConsole.Configuration; +using SadConsole.Host; namespace SadConsole.Host; @@ -14,16 +15,6 @@ public partial class Game : Microsoft.Xna.Framework.Game internal bool _resizeBusy = false; internal Action _initCallback; - /// - /// The game component to control SadConsole updates, input, and rendering. - /// - public SadConsoleGameComponent SadConsoleComponent; - - /// - /// The game component that clears the render output before each frame draw. - /// - public ClearScreenGameComponent ClearScreenComponent; - /// /// The current game window width. /// @@ -42,7 +33,7 @@ public partial class Game : Microsoft.Xna.Framework.Game /// /// Creates the new MonoGame game object. /// - internal Game() + public Game() { Global.GraphicsDeviceManager = new GraphicsDeviceManager(this) { @@ -81,22 +72,7 @@ private void Window_ClientSizeChanged(object sender, EventArgs e) _resizeBusy = false; } - //if (!resizeBusy && Settings.IsExitingFullscreen) - //{ - // GraphicsDeviceManager.PreferredBackBufferWidth = Global.WindowWidth; - // GraphicsDeviceManager.PreferredBackBufferHeight = Global.WindowHeight; - - // resizeBusy = true; - // GraphicsDeviceManager.ApplyChanges(); - // resizeBusy = false; - // Settings.IsExitingFullscreen = false; - //} - - //Global.WindowWidth = GraphicsDeviceManager.PreferredBackBufferWidth; - //Global.WindowHeight = GraphicsDeviceManager.PreferredBackBufferHeight; - //Global.WindowWidth = Global.RenderWidth = GraphicsDeviceManager.PreferredBackBufferWidth; - //Global.WindowHeight = Global.RenderHeight = GraphicsDeviceManager.PreferredBackBufferHeight; - ResetRendering(); + Global.ResetRendering(); if (!_resizeBusy) { @@ -107,17 +83,6 @@ private void Window_ClientSizeChanged(object sender, EventArgs e) /// protected override void Initialize() { - Global.GraphicsDevice = GraphicsDevice; - Global.GraphicsDevice.PresentationParameters.RenderTargetUsage = RenderTargetUsage.PreserveContents; - - // Unlimited FPS setting - FpsConfig config = SadConsole.Game.Instance._configuration.Configs.OfType().FirstOrDefault(); - if (config != null && config.UnlimitedFPS) - { - Global.GraphicsDeviceManager.SynchronizeWithVerticalRetrace = false; - IsFixedTimeStep = false; - } - // Window title Window.Title = SadConsole.Settings.WindowTitle; @@ -125,149 +90,20 @@ protected override void Initialize() IsMouseVisible = true; // Initialize the SadConsole engine with a font, and a screen size that mirrors MS-DOS. - SadConsoleComponent = new SadConsoleGameComponent(this); - ClearScreenComponent = new ClearScreenGameComponent(this); + Global.SadConsoleComponent = new SadConsoleGameComponent(this); + Global.ClearScreenComponent = new ClearScreenGameComponent(this); - Components.Add(SadConsoleComponent); - Components.Add(ClearScreenComponent); + Components.Add(Global.SadConsoleComponent); + Components.Add(Global.ClearScreenComponent); - // Call the default initialize of the base class. + // Initializes the components and loads content base.Initialize(); // Hook window change for resolution fixes Window.ClientSizeChanged += Window_ClientSizeChanged; Window.AllowUserResizing = SadConsole.Settings.AllowWindowResize; - Global.SharedSpriteBatch = new SpriteBatch(GraphicsDevice); - - // Invoke the monogame init callback - MonoGameCallbackConfig monoGameConfig = SadConsole.Game.Instance._configuration.Configs.OfType().FirstOrDefault(); - monoGameConfig?.MonoGameInitCallback?.Invoke(this); - - ResetRendering(); - // After we've init, clear the graphics device so everything is ready to start GraphicsDevice.SetRenderTarget(null); } - - /// - /// Resizes the by the specified font size. - /// - /// The size of the font to base the final values on. - /// The count of glyphs along the X-axis. - /// The count of glyphs along the Y-axis. - /// Additional pixel width to add to the resize. - /// Additional pixel height to add to the resize. - public void ResizeGraphicsDeviceManager(Point fontSize, int width, int height, int additionalWidth, int additionalHeight) - { - Global.GraphicsDeviceManager.PreferredBackBufferWidth = (fontSize.X * width) + additionalWidth; - Global.GraphicsDeviceManager.PreferredBackBufferHeight = (fontSize.Y * height) + additionalHeight; - - SadConsole.Settings.Rendering.RenderWidth = Global.GraphicsDeviceManager.PreferredBackBufferWidth; - SadConsole.Settings.Rendering.RenderHeight = Global.GraphicsDeviceManager.PreferredBackBufferHeight; - - Global.GraphicsDeviceManager.ApplyChanges(); - } - - /// - /// Regenerates the if the desired size doesn't match the current size. - /// - /// The width of the render output. - /// The height of the render output. - protected virtual void RecreateRenderOutput(int width, int height) - { - if (Global.RenderOutput == null || Global.RenderOutput.Width != width || Global.RenderOutput.Height != height) - { - Global.RenderOutput?.Dispose(); - Global.RenderOutput = new RenderTarget2D(GraphicsDevice, width, height); - } - } - - /// - /// Resets the target and determines the appropriate and based on the window or fullscreen state. - /// - public virtual void ResetRendering() - { - if (SadConsole.Settings.ResizeMode == SadConsole.Settings.WindowResizeOptions.Center) - { - RecreateRenderOutput(SadConsole.Settings.Rendering.RenderWidth, SadConsole.Settings.Rendering.RenderHeight); - SadConsole.Settings.Rendering.RenderRect = new Rectangle( - Math.Max(0, (GraphicsDevice.PresentationParameters.BackBufferWidth - SadConsole.Settings.Rendering.RenderWidth) / 2), - Math.Max(0, (GraphicsDevice.PresentationParameters.BackBufferHeight - SadConsole.Settings.Rendering.RenderHeight) / 2), - SadConsole.Settings.Rendering.RenderWidth, - SadConsole.Settings.Rendering.RenderHeight).ToRectangle(); - - SadConsole.Settings.Rendering.RenderScale = (1, 1); - } - else if (SadConsole.Settings.ResizeMode == SadConsole.Settings.WindowResizeOptions.Scale) - { - RecreateRenderOutput(SadConsole.Settings.Rendering.RenderWidth, SadConsole.Settings.Rendering.RenderHeight); - int multiple = 2; - - // Find the bounds - while (true) - { - if (SadConsole.Settings.Rendering.RenderWidth * multiple > GraphicsDevice.PresentationParameters.BackBufferWidth || SadConsole.Settings.Rendering.RenderHeight * multiple > GraphicsDevice.PresentationParameters.BackBufferHeight) - { - multiple--; - break; - } - - multiple++; - } - - SadConsole.Settings.Rendering.RenderRect = new Rectangle(Math.Max(0, (GraphicsDevice.PresentationParameters.BackBufferWidth - (SadConsole.Settings.Rendering.RenderWidth * multiple)) / 2), - Math.Max(0, (GraphicsDevice.PresentationParameters.BackBufferHeight - (SadConsole.Settings.Rendering.RenderHeight * multiple)) / 2), - SadConsole.Settings.Rendering.RenderWidth * multiple, - SadConsole.Settings.Rendering.RenderHeight * multiple).ToRectangle(); - - SadConsole.Settings.Rendering.RenderScale = (SadConsole.Settings.Rendering.RenderWidth / ((float)SadConsole.Settings.Rendering.RenderWidth * multiple), SadConsole.Settings.Rendering.RenderHeight / (float)(SadConsole.Settings.Rendering.RenderHeight * multiple)); - } - else if (SadConsole.Settings.ResizeMode == SadConsole.Settings.WindowResizeOptions.Fit) - { - RecreateRenderOutput(SadConsole.Settings.Rendering.RenderWidth, SadConsole.Settings.Rendering.RenderHeight); - float heightRatio = GraphicsDevice.PresentationParameters.BackBufferHeight / (float)SadConsole.Settings.Rendering.RenderHeight; - float widthRatio = GraphicsDevice.PresentationParameters.BackBufferWidth / (float)SadConsole.Settings.Rendering.RenderWidth; - - float fitHeight = SadConsole.Settings.Rendering.RenderHeight * widthRatio; - float fitWidth = SadConsole.Settings.Rendering.RenderWidth * heightRatio; - - if (fitHeight <= GraphicsDevice.PresentationParameters.BackBufferHeight) - { - // Render width = window width, pad top and bottom - - SadConsole.Settings.Rendering.RenderRect = new Rectangle(0, - Math.Max(0, (int)((GraphicsDevice.PresentationParameters.BackBufferHeight - fitHeight) / 2)), - GraphicsDevice.PresentationParameters.BackBufferWidth, - (int)fitHeight).ToRectangle(); - - SadConsole.Settings.Rendering.RenderScale = (SadConsole.Settings.Rendering.RenderWidth / (float)GraphicsDevice.PresentationParameters.BackBufferWidth, SadConsole.Settings.Rendering.RenderHeight / fitHeight); - } - else - { - // Render height = window height, pad left and right - - SadConsole.Settings.Rendering.RenderRect = new Rectangle(Math.Max(0, (int)((GraphicsDevice.PresentationParameters.BackBufferWidth - fitWidth) / 2)), - 0, - (int)fitWidth, - GraphicsDevice.PresentationParameters.BackBufferHeight).ToRectangle(); - - SadConsole.Settings.Rendering.RenderScale = (SadConsole.Settings.Rendering.RenderWidth / fitWidth, SadConsole.Settings.Rendering.RenderHeight / (float)GraphicsDevice.PresentationParameters.BackBufferHeight); - } - } - else if (SadConsole.Settings.ResizeMode == SadConsole.Settings.WindowResizeOptions.None) - { - SadConsole.Settings.Rendering.RenderWidth = GraphicsDevice.PresentationParameters.BackBufferWidth; - SadConsole.Settings.Rendering.RenderHeight = GraphicsDevice.PresentationParameters.BackBufferHeight; - RecreateRenderOutput(SadConsole.Settings.Rendering.RenderWidth, SadConsole.Settings.Rendering.RenderHeight); - SadConsole.Settings.Rendering.RenderRect = GraphicsDevice.Viewport.Bounds.ToRectangle(); - SadConsole.Settings.Rendering.RenderScale = (1, 1); - } - else // Stretch - { - RecreateRenderOutput(SadConsole.Settings.Rendering.RenderWidth, SadConsole.Settings.Rendering.RenderHeight); - SadConsole.Settings.Rendering.RenderRect = GraphicsDevice.Viewport.Bounds.ToRectangle(); - SadConsole.Settings.Rendering.RenderScale = (SadConsole.Settings.Rendering.RenderWidth / (float)GraphicsDevice.PresentationParameters.BackBufferWidth, SadConsole.Settings.Rendering.RenderHeight / (float)GraphicsDevice.PresentationParameters.BackBufferHeight); - } - } } diff --git a/SadConsole.Host.MonoGame/MonoGame/Global.cs b/SadConsole.Host.MonoGame/MonoGame/Global.cs index c913585da..e3e01aa98 100644 --- a/SadConsole.Host.MonoGame/MonoGame/Global.cs +++ b/SadConsole.Host.MonoGame/MonoGame/Global.cs @@ -14,6 +14,23 @@ namespace SadConsole.Host; /// public static class Global { + /// + /// Resizes the by the specified font size. + /// + /// The size of the font to base the final values on. + /// The count of glyphs along the X-axis. + /// The count of glyphs along the Y-axis. + /// Additional pixel width to add to the resize. + /// Additional pixel height to add to the resize. + public delegate void ResizeGraphicsDeviceManagerDelegate(Point fontSize, int width, int height, int additionalWidth, int additionalHeight); + + /// + /// Regenerates the if the desired size doesn't match the current size. + /// + /// The width of the render output. + /// The height of the render output. + public delegate void RecreateRenderOutputDelegate(int width, int height); + /// /// When , prevents the keyboard and mouse logic from running. /// @@ -44,6 +61,31 @@ public static class Global /// public static GameTime RenderLoopGameTime { get; internal set; } + /// + /// Regenerates the if the desired size doesn't match the current size. + /// + public static RecreateRenderOutputDelegate RecreateRenderOutput { get; set; } = RecreateRenderOutputHandler; + + /// + /// Resizes the by the specified font size. + /// + public static ResizeGraphicsDeviceManagerDelegate ResizeGraphicsDeviceManager { get; set; } = ResizeGraphicsDeviceManagerHandler; + + /// + /// Resets the target and determines the appropriate and based on the window or fullscreen state. + /// + public static Action ResetRendering { get; set; } = ResetRenderingHandler; + + /// + /// The game component to control SadConsole updates, input, and rendering. + /// + public static SadConsoleGameComponent SadConsoleComponent { get; set; } + + /// + /// The game component that clears the render output before each frame draw. + /// + public static ClearScreenGameComponent ClearScreenComponent { get; set; } + #if WPF /// @@ -75,4 +117,125 @@ public static void ResetGraphicsDevice() => GraphicsDevice.SetRenderTarget(null); #endif + /// + /// Resizes the by the specified font size. + /// + /// The size of the font to base the final values on. + /// The count of glyphs along the X-axis. + /// The count of glyphs along the Y-axis. + /// Additional pixel width to add to the resize. + /// Additional pixel height to add to the resize. + public static void ResizeGraphicsDeviceManagerHandler(Point fontSize, int width, int height, int additionalWidth, int additionalHeight) + { +#if !WPF + GraphicsDeviceManager.PreferredBackBufferWidth = (fontSize.X * width) + additionalWidth; + GraphicsDeviceManager.PreferredBackBufferHeight = (fontSize.Y * height) + additionalHeight; +#endif + SadConsole.Settings.Rendering.RenderWidth = Global.GraphicsDeviceManager.PreferredBackBufferWidth; + SadConsole.Settings.Rendering.RenderHeight = Global.GraphicsDeviceManager.PreferredBackBufferHeight; + + Global.GraphicsDeviceManager.ApplyChanges(); + } + /// + /// Regenerates the if the desired size doesn't match the current size. + /// + /// The width of the render output. + /// The height of the render output. + + public static void RecreateRenderOutputHandler(int width, int height) + { + if (Global.RenderOutput == null || Global.RenderOutput.Width != width || Global.RenderOutput.Height != height) + { + Global.RenderOutput?.Dispose(); + Global.RenderOutput = new RenderTarget2D(GraphicsDevice, width, height); + } + } + + /// + /// Resets the target and determines the appropriate and based on the window or fullscreen state. + /// + public static void ResetRenderingHandler() + { + if (SadConsole.Settings.ResizeMode == SadConsole.Settings.WindowResizeOptions.Center) + { + RecreateRenderOutput(SadConsole.Settings.Rendering.RenderWidth, SadConsole.Settings.Rendering.RenderHeight); + SadConsole.Settings.Rendering.RenderRect = new Rectangle( + Math.Max(0, (GraphicsDevice.PresentationParameters.BackBufferWidth - SadConsole.Settings.Rendering.RenderWidth) / 2), + Math.Max(0, (GraphicsDevice.PresentationParameters.BackBufferHeight - SadConsole.Settings.Rendering.RenderHeight) / 2), + SadConsole.Settings.Rendering.RenderWidth, + SadConsole.Settings.Rendering.RenderHeight).ToRectangle(); + + SadConsole.Settings.Rendering.RenderScale = (1, 1); + } + else if (SadConsole.Settings.ResizeMode == SadConsole.Settings.WindowResizeOptions.Scale) + { + RecreateRenderOutput(SadConsole.Settings.Rendering.RenderWidth, SadConsole.Settings.Rendering.RenderHeight); + int multiple = 2; + + // Find the bounds + while (true) + { + if (SadConsole.Settings.Rendering.RenderWidth * multiple > GraphicsDevice.PresentationParameters.BackBufferWidth || SadConsole.Settings.Rendering.RenderHeight * multiple > GraphicsDevice.PresentationParameters.BackBufferHeight) + { + multiple--; + break; + } + + multiple++; + } + + SadConsole.Settings.Rendering.RenderRect = new Rectangle(Math.Max(0, (GraphicsDevice.PresentationParameters.BackBufferWidth - (SadConsole.Settings.Rendering.RenderWidth * multiple)) / 2), + Math.Max(0, (GraphicsDevice.PresentationParameters.BackBufferHeight - (SadConsole.Settings.Rendering.RenderHeight * multiple)) / 2), + SadConsole.Settings.Rendering.RenderWidth * multiple, + SadConsole.Settings.Rendering.RenderHeight * multiple).ToRectangle(); + + SadConsole.Settings.Rendering.RenderScale = (SadConsole.Settings.Rendering.RenderWidth / ((float)SadConsole.Settings.Rendering.RenderWidth * multiple), SadConsole.Settings.Rendering.RenderHeight / (float)(SadConsole.Settings.Rendering.RenderHeight * multiple)); + } + else if (SadConsole.Settings.ResizeMode == SadConsole.Settings.WindowResizeOptions.Fit) + { + RecreateRenderOutput(SadConsole.Settings.Rendering.RenderWidth, SadConsole.Settings.Rendering.RenderHeight); + float heightRatio = GraphicsDevice.PresentationParameters.BackBufferHeight / (float)SadConsole.Settings.Rendering.RenderHeight; + float widthRatio = GraphicsDevice.PresentationParameters.BackBufferWidth / (float)SadConsole.Settings.Rendering.RenderWidth; + + float fitHeight = SadConsole.Settings.Rendering.RenderHeight * widthRatio; + float fitWidth = SadConsole.Settings.Rendering.RenderWidth * heightRatio; + + if (fitHeight <= GraphicsDevice.PresentationParameters.BackBufferHeight) + { + // Render width = window width, pad top and bottom + + SadConsole.Settings.Rendering.RenderRect = new Rectangle(0, + Math.Max(0, (int)((GraphicsDevice.PresentationParameters.BackBufferHeight - fitHeight) / 2)), + GraphicsDevice.PresentationParameters.BackBufferWidth, + (int)fitHeight).ToRectangle(); + + SadConsole.Settings.Rendering.RenderScale = (SadConsole.Settings.Rendering.RenderWidth / (float)GraphicsDevice.PresentationParameters.BackBufferWidth, SadConsole.Settings.Rendering.RenderHeight / fitHeight); + } + else + { + // Render height = window height, pad left and right + + SadConsole.Settings.Rendering.RenderRect = new Rectangle(Math.Max(0, (int)((GraphicsDevice.PresentationParameters.BackBufferWidth - fitWidth) / 2)), + 0, + (int)fitWidth, + GraphicsDevice.PresentationParameters.BackBufferHeight).ToRectangle(); + + SadConsole.Settings.Rendering.RenderScale = (SadConsole.Settings.Rendering.RenderWidth / fitWidth, SadConsole.Settings.Rendering.RenderHeight / (float)GraphicsDevice.PresentationParameters.BackBufferHeight); + } + } + else if (SadConsole.Settings.ResizeMode == SadConsole.Settings.WindowResizeOptions.None) + { + SadConsole.Settings.Rendering.RenderWidth = GraphicsDevice.PresentationParameters.BackBufferWidth; + SadConsole.Settings.Rendering.RenderHeight = GraphicsDevice.PresentationParameters.BackBufferHeight; + RecreateRenderOutput(SadConsole.Settings.Rendering.RenderWidth, SadConsole.Settings.Rendering.RenderHeight); + SadConsole.Settings.Rendering.RenderRect = GraphicsDevice.Viewport.Bounds.ToRectangle(); + SadConsole.Settings.Rendering.RenderScale = (1, 1); + } + else // Stretch + { + RecreateRenderOutput(SadConsole.Settings.Rendering.RenderWidth, SadConsole.Settings.Rendering.RenderHeight); + SadConsole.Settings.Rendering.RenderRect = GraphicsDevice.Viewport.Bounds.ToRectangle(); + SadConsole.Settings.Rendering.RenderScale = (SadConsole.Settings.Rendering.RenderWidth / (float)GraphicsDevice.PresentationParameters.BackBufferWidth, SadConsole.Settings.Rendering.RenderHeight / (float)GraphicsDevice.PresentationParameters.BackBufferHeight); + } + } } diff --git a/SadConsole.Host.MonoGame/MonoGame/SadConsoleGameComponent.Mono.cs b/SadConsole.Host.MonoGame/MonoGame/SadConsoleGameComponent.Mono.cs index 777b03896..f5229c9f4 100644 --- a/SadConsole.Host.MonoGame/MonoGame/SadConsoleGameComponent.Mono.cs +++ b/SadConsole.Host.MonoGame/MonoGame/SadConsoleGameComponent.Mono.cs @@ -1,142 +1,160 @@ -using Microsoft.Xna.Framework; +using System.Linq; +using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; +using SadConsole.Configuration; namespace SadConsole.Host; -public partial class Game +/// +/// A game component that handles updating, input, and rendering of SadConsole. +/// +public class SadConsoleGameComponent : DrawableGameComponent { - /// - /// A game component that handles updating, input, and rendering of SadConsole. - /// - public class SadConsoleGameComponent : DrawableGameComponent + /// + public SadConsoleGameComponent(Microsoft.Xna.Framework.Game game) : base(game) { - internal SadConsoleGameComponent(Game game) : base(game) - { - DrawOrder = 5; - UpdateOrder = 5; - } + DrawOrder = 5; + UpdateOrder = 5; + } #if NOESIS - protected override void LoadContent() - { - NoesisManager.CreateNoesisGUI(); - } + protected override void LoadContent() + { + NoesisManager.CreateNoesisGUI(); + } #endif - /// - /// Draws the SadConsole frame through draw calls when is true. - /// - /// Time between drawing frames. - public override void Draw(GameTime gameTime) + public override void Initialize() + { + base.Initialize(); + + Global.GraphicsDevice = GraphicsDevice; + + // Unlimited FPS setting + FpsConfig config = SadConsole.Game.Instance._configuration.Configs.OfType().FirstOrDefault(); + if (config != null && config.UnlimitedFPS) { - SadConsole.GameHost.Instance.DrawFrameDelta = gameTime.ElapsedGameTime; - Global.RenderLoopGameTime = gameTime; + Global.GraphicsDeviceManager.SynchronizeWithVerticalRetrace = false; + Game.IsFixedTimeStep = false; + } - if (SadConsole.Settings.DoDraw) - { + Global.SharedSpriteBatch = new SpriteBatch(GraphicsDevice); + + // Invoke the monogame init callback + MonoGameCallbackConfig monoGameConfig = SadConsole.Game.Instance._configuration.Configs.OfType().FirstOrDefault(); + monoGameConfig?.MonoGameInitCallback?.Invoke(Game); + + Global.ResetRendering(); + } + + /// + /// Draws the SadConsole frame through draw calls when is true. + /// + /// Time between drawing frames. + public override void Draw(GameTime gameTime) + { + SadConsole.GameHost.Instance.DrawFrameDelta = gameTime.ElapsedGameTime; + Global.RenderLoopGameTime = gameTime; + + if (SadConsole.Settings.DoDraw) + { #if NOESIS - // GUI - NoesisManager.noesisGUIWrapper.PreRender(); + // GUI + NoesisManager.noesisGUIWrapper.PreRender(); #endif - Host.Game game = (Host.Game)Game; + // Clear draw calls for next run + SadConsole.Game.Instance.DrawCalls.Clear(); - // Clear draw calls for next run - SadConsole.Game.Instance.DrawCalls.Clear(); + // Make sure all items in the screen are drawn. (Build a list of draw calls) + SadConsole.GameHost.Instance.Screen?.Render(SadConsole.GameHost.Instance.DrawFrameDelta); - // Make sure all items in the screen are drawn. (Build a list of draw calls) - SadConsole.GameHost.Instance.Screen?.Render(SadConsole.GameHost.Instance.DrawFrameDelta); + ((SadConsole.Game)SadConsole.Game.Instance).InvokeFrameDraw(); - ((SadConsole.Game)SadConsole.Game.Instance).InvokeFrameDraw(); + // Render to the global output texture + GraphicsDevice.SetRenderTarget(Global.RenderOutput); + GraphicsDevice.Clear(SadRogue.Primitives.SadRogueColorExtensions.ToMonoColor(SadConsole.Settings.ClearColor)); - // Render to the global output texture - GraphicsDevice.SetRenderTarget(Global.RenderOutput); - GraphicsDevice.Clear(SadRogue.Primitives.SadRogueColorExtensions.ToMonoColor(SadConsole.Settings.ClearColor)); + // Render each draw call + Global.SharedSpriteBatch.Begin(SpriteSortMode.Deferred, SadConsole.Host.Settings.MonoGameScreenBlendState, SamplerState.PointClamp, DepthStencilState.DepthRead, RasterizerState.CullNone); - // Render each draw call - Global.SharedSpriteBatch.Begin(SpriteSortMode.Deferred, SadConsole.Host.Settings.MonoGameScreenBlendState, SamplerState.PointClamp, DepthStencilState.DepthRead, RasterizerState.CullNone); + foreach (DrawCalls.IDrawCall call in SadConsole.Game.Instance.DrawCalls) + call.Draw(); - foreach (DrawCalls.IDrawCall call in SadConsole.Game.Instance.DrawCalls) - call.Draw(); + Global.SharedSpriteBatch.End(); + GraphicsDevice.SetRenderTarget(null); + // If we're going to draw to the screen, do it. + if (SadConsole.Settings.DoFinalDraw) + { + Global.SharedSpriteBatch.Begin(SpriteSortMode.Deferred, SadConsole.Host.Settings.MonoGameScreenBlendState, SamplerState.PointClamp, DepthStencilState.DepthRead, RasterizerState.CullNone); + Global.SharedSpriteBatch.Draw(Global.RenderOutput, SadRogue.Primitives.SadRogueRectangleExtensions.ToMonoRectangle(SadConsole.Settings.Rendering.RenderRect), Color.White); Global.SharedSpriteBatch.End(); - GraphicsDevice.SetRenderTarget(null); - - // If we're going to draw to the screen, do it. - if (SadConsole.Settings.DoFinalDraw) - { - Global.SharedSpriteBatch.Begin(SpriteSortMode.Deferred, SadConsole.Host.Settings.MonoGameScreenBlendState, SamplerState.PointClamp, DepthStencilState.DepthRead, RasterizerState.CullNone); - Global.SharedSpriteBatch.Draw(Global.RenderOutput, SadRogue.Primitives.SadRogueRectangleExtensions.ToMonoRectangle(SadConsole.Settings.Rendering.RenderRect), Color.White); - Global.SharedSpriteBatch.End(); - } + } #if NOESIS - // GUI TODO: Should I move this before rendertarget NULL? - NoesisManager.noesisGUIWrapper.Render(); + // GUI TODO: Should I move this before rendertarget NULL? + NoesisManager.noesisGUIWrapper.Render(); #endif - } } + } - /// - /// Updates the SadConsole game objects and handles input. Only runs when is true. - /// - /// - public override void Update(GameTime gameTime) - { - SadConsole.GameHost.Instance.UpdateFrameDelta = gameTime.ElapsedGameTime; - Global.UpdateLoopGameTime = gameTime; - - if (SadConsole.Settings.DoUpdate) - { - // Process any pre-Screen logic components - foreach (SadConsole.Components.RootComponent item in SadConsole.Game.Instance.RootComponents) - item.Run(GameHost.Instance.UpdateFrameDelta); - - var game = (Game)Game; + /// + /// Updates the SadConsole game objects and handles input. Only runs when is true. + /// + /// + public override void Update(GameTime gameTime) + { + SadConsole.GameHost.Instance.UpdateFrameDelta = gameTime.ElapsedGameTime; + Global.UpdateLoopGameTime = gameTime; + if (SadConsole.Settings.DoUpdate) + { + // Process any pre-Screen logic components + foreach (SadConsole.Components.RootComponent item in SadConsole.Game.Instance.RootComponents) + item.Run(GameHost.Instance.UpdateFrameDelta); #if NOESIS - bool blockInput = false; - NoesisManager.noesisGUIWrapper.UpdateInput(gameTime, isWindowActive: this.Game.IsActive); + bool blockInput = false; + NoesisManager.noesisGUIWrapper.UpdateInput(gameTime, isWindowActive: this.Game.IsActive); - blockInput = Global.BlockSadConsoleInput - || NoesisManager.noesisGUIWrapper.Input.ConsumedKeyboardKeys.Count != 0 - || NoesisManager.noesisGUIWrapper.Input.ConsumedMouseButtons.Count != 0 - || NoesisManager.noesisGUIWrapper.Input.ConsumedMouseDeltaWheel != 0; + blockInput = Global.BlockSadConsoleInput + || NoesisManager.noesisGUIWrapper.Input.ConsumedKeyboardKeys.Count != 0 + || NoesisManager.noesisGUIWrapper.Input.ConsumedMouseButtons.Count != 0 + || NoesisManager.noesisGUIWrapper.Input.ConsumedMouseDeltaWheel != 0; - if (Game.IsActive && !blockInput) - { + if (Game.IsActive && !blockInput) + { #else - if (Game.IsActive && !Global.BlockSadConsoleInput) - { + if (Game.IsActive && !Global.BlockSadConsoleInput) + { #endif - if (SadConsole.Settings.Input.DoKeyboard) - { - SadConsole.GameHost.Instance.Keyboard.Update(SadConsole.GameHost.Instance.UpdateFrameDelta); - - if (SadConsole.GameHost.Instance.FocusedScreenObjects.ScreenObject != null && SadConsole.GameHost.Instance.FocusedScreenObjects.ScreenObject.UseKeyboard) - { - SadConsole.GameHost.Instance.FocusedScreenObjects.ScreenObject.ProcessKeyboard(SadConsole.GameHost.Instance.Keyboard); - } - - } + if (SadConsole.Settings.Input.DoKeyboard) + { + SadConsole.GameHost.Instance.Keyboard.Update(SadConsole.GameHost.Instance.UpdateFrameDelta); - if (SadConsole.Settings.Input.DoMouse) + if (SadConsole.GameHost.Instance.FocusedScreenObjects.ScreenObject != null && SadConsole.GameHost.Instance.FocusedScreenObjects.ScreenObject.UseKeyboard) { - SadConsole.GameHost.Instance.Mouse.Update(SadConsole.GameHost.Instance.UpdateFrameDelta); - SadConsole.GameHost.Instance.Mouse.Process(); + SadConsole.GameHost.Instance.FocusedScreenObjects.ScreenObject.ProcessKeyboard(SadConsole.GameHost.Instance.Keyboard); } + + } + + if (SadConsole.Settings.Input.DoMouse) + { + SadConsole.GameHost.Instance.Mouse.Update(SadConsole.GameHost.Instance.UpdateFrameDelta); + SadConsole.GameHost.Instance.Mouse.Process(); } + } - SadConsole.GameHost.Instance.Screen?.Update(SadConsole.GameHost.Instance.UpdateFrameDelta); + SadConsole.GameHost.Instance.Screen?.Update(SadConsole.GameHost.Instance.UpdateFrameDelta); - ((SadConsole.Game)SadConsole.Game.Instance).InvokeFrameUpdate(); + ((SadConsole.Game)SadConsole.Game.Instance).InvokeFrameUpdate(); #if NOESIS - // GUI - NoesisManager.noesisGUIWrapper.Update(gameTime); + // GUI + NoesisManager.noesisGUIWrapper.Update(gameTime); #endif - } } } } diff --git a/SadConsole.Host.MonoGame/MonoGame/SadConsoleGameComponent.Wpf.cs b/SadConsole.Host.MonoGame/MonoGame/SadConsoleGameComponent.Wpf.cs index 784320619..dd598c103 100644 --- a/SadConsole.Host.MonoGame/MonoGame/SadConsoleGameComponent.Wpf.cs +++ b/SadConsole.Host.MonoGame/MonoGame/SadConsoleGameComponent.Wpf.cs @@ -4,111 +4,108 @@ namespace SadConsole.Host; -public partial class Game +/// +/// A game component that handles updating, input, and rendering of SadConsole. +/// +public class SadConsoleGameComponent : WpfDrawableGameComponent { + internal SadConsoleGameComponent(WpfGame game) : base(game) + { + DrawOrder = 5; + UpdateOrder = 5; + } + /// - /// A game component that handles updating, input, and rendering of SadConsole. + /// Draws the SadConsole frame through draw calls when is true. /// - public class SadConsoleGameComponent : WpfDrawableGameComponent + /// Time between drawing frames. + public override void Draw(GameTime gameTime) { - internal SadConsoleGameComponent(WpfGame game) : base(game) + if (((SadConsole.Host.Game)Game).ResetRenderingNextFrame) { - DrawOrder = 5; - UpdateOrder = 5; + ((SadConsole.Host.Game)Game).ResetRenderingNextFrame = false; + ((SadConsole.Host.Game)Game).ResetRendering(); } - /// - /// Draws the SadConsole frame through draw calls when is true. - /// - /// Time between drawing frames. - public override void Draw(GameTime gameTime) + SadConsole.GameHost.Instance.DrawFrameDelta = gameTime.ElapsedGameTime; + Global.RenderLoopGameTime = gameTime; + + if (SadConsole.Settings.DoDraw) { - if (((SadConsole.Host.Game)Game).ResetRenderingNextFrame) - { - ((SadConsole.Host.Game)Game).ResetRenderingNextFrame = false; - ((SadConsole.Host.Game)Game).ResetRendering(); - } + Host.Game game = (Host.Game)Game; - SadConsole.GameHost.Instance.DrawFrameDelta = gameTime.ElapsedGameTime; - Global.RenderLoopGameTime = gameTime; + // Clear draw calls for next run + SadConsole.Game.Instance.DrawCalls.Clear(); - if (SadConsole.Settings.DoDraw) - { - Host.Game game = (Host.Game)Game; + // Make sure all items in the screen are drawn. (Build a list of draw calls) + SadConsole.GameHost.Instance.Screen?.Render(SadConsole.GameHost.Instance.DrawFrameDelta); - // Clear draw calls for next run - SadConsole.Game.Instance.DrawCalls.Clear(); + ((SadConsole.Game)SadConsole.Game.Instance).InvokeFrameDraw(); - // Make sure all items in the screen are drawn. (Build a list of draw calls) - SadConsole.GameHost.Instance.Screen?.Render(SadConsole.GameHost.Instance.DrawFrameDelta); + // Render to the global output texture + GraphicsDevice.SetRenderTarget(Global.RenderOutput); + GraphicsDevice.Clear(SadRogue.Primitives.SadRogueColorExtensions.ToMonoColor(SadConsole.Settings.ClearColor)); - ((SadConsole.Game)SadConsole.Game.Instance).InvokeFrameDraw(); + // Render each draw call + Global.SharedSpriteBatch.Begin(SpriteSortMode.Deferred, SadConsole.Host.Settings.MonoGameScreenBlendState, SamplerState.PointClamp, DepthStencilState.DepthRead, RasterizerState.CullNone); + foreach (DrawCalls.IDrawCall call in SadConsole.Game.Instance.DrawCalls) + { + call.Draw(); + } - // Render to the global output texture - GraphicsDevice.SetRenderTarget(Global.RenderOutput); - GraphicsDevice.Clear(SadRogue.Primitives.SadRogueColorExtensions.ToMonoColor(SadConsole.Settings.ClearColor)); + Global.SharedSpriteBatch.End(); + Global.ResetGraphicsDevice(); - // Render each draw call + // If we're going to draw to the screen, do it. + if (SadConsole.Settings.DoFinalDraw) + { Global.SharedSpriteBatch.Begin(SpriteSortMode.Deferred, SadConsole.Host.Settings.MonoGameScreenBlendState, SamplerState.PointClamp, DepthStencilState.DepthRead, RasterizerState.CullNone); - foreach (DrawCalls.IDrawCall call in SadConsole.Game.Instance.DrawCalls) - { - call.Draw(); - } - + Global.SharedSpriteBatch.Draw(Global.RenderOutput, SadRogue.Primitives.SadRogueRectangleExtensions.ToMonoRectangle(SadConsole.Settings.Rendering.RenderRect), Color.White); Global.SharedSpriteBatch.End(); - Global.ResetGraphicsDevice(); - - // If we're going to draw to the screen, do it. - if (SadConsole.Settings.DoFinalDraw) - { - Global.SharedSpriteBatch.Begin(SpriteSortMode.Deferred, SadConsole.Host.Settings.MonoGameScreenBlendState, SamplerState.PointClamp, DepthStencilState.DepthRead, RasterizerState.CullNone); - Global.SharedSpriteBatch.Draw(Global.RenderOutput, SadRogue.Primitives.SadRogueRectangleExtensions.ToMonoRectangle(SadConsole.Settings.Rendering.RenderRect), Color.White); - Global.SharedSpriteBatch.End(); - } } } + } - /// - /// Updates the SadConsole game objects and handles input. Only runs when is true. - /// - /// - public override void Update(GameTime gameTime) - { - SadConsole.GameHost.Instance.UpdateFrameDelta = gameTime.ElapsedGameTime; - Global.UpdateLoopGameTime = gameTime; + /// + /// Updates the SadConsole game objects and handles input. Only runs when is true. + /// + /// + public override void Update(GameTime gameTime) + { + SadConsole.GameHost.Instance.UpdateFrameDelta = gameTime.ElapsedGameTime; + Global.UpdateLoopGameTime = gameTime; - if (SadConsole.Settings.DoUpdate) - { - // Process any pre-Screen logic components - foreach (SadConsole.Components.RootComponent item in SadConsole.Game.Instance.RootComponents) - item.Run(GameHost.Instance.UpdateFrameDelta); + if (SadConsole.Settings.DoUpdate) + { + // Process any pre-Screen logic components + foreach (SadConsole.Components.RootComponent item in SadConsole.Game.Instance.RootComponents) + item.Run(GameHost.Instance.UpdateFrameDelta); - var game = (Game)Game; + var game = (Game)Game; - if (Game.IsActive) + if (Game.IsActive) + { + if (SadConsole.Settings.Input.DoKeyboard) { - if (SadConsole.Settings.Input.DoKeyboard) - { - SadConsole.GameHost.Instance.Keyboard.Update(SadConsole.GameHost.Instance.UpdateFrameDelta); - - if (SadConsole.GameHost.Instance.FocusedScreenObjects.ScreenObject != null && SadConsole.GameHost.Instance.FocusedScreenObjects.ScreenObject.UseKeyboard) - { - SadConsole.GameHost.Instance.FocusedScreenObjects.ScreenObject.ProcessKeyboard(SadConsole.GameHost.Instance.Keyboard); - } + SadConsole.GameHost.Instance.Keyboard.Update(SadConsole.GameHost.Instance.UpdateFrameDelta); - } - - if (SadConsole.Settings.Input.DoMouse) + if (SadConsole.GameHost.Instance.FocusedScreenObjects.ScreenObject != null && SadConsole.GameHost.Instance.FocusedScreenObjects.ScreenObject.UseKeyboard) { - SadConsole.GameHost.Instance.Mouse.Update(SadConsole.GameHost.Instance.UpdateFrameDelta); - SadConsole.GameHost.Instance.Mouse.Process(); + SadConsole.GameHost.Instance.FocusedScreenObjects.ScreenObject.ProcessKeyboard(SadConsole.GameHost.Instance.Keyboard); } - } - SadConsole.GameHost.Instance.Screen?.Update(SadConsole.GameHost.Instance.UpdateFrameDelta); + } - ((SadConsole.Game)SadConsole.Game.Instance).InvokeFrameUpdate(); + if (SadConsole.Settings.Input.DoMouse) + { + SadConsole.GameHost.Instance.Mouse.Update(SadConsole.GameHost.Instance.UpdateFrameDelta); + SadConsole.GameHost.Instance.Mouse.Process(); + } } + + SadConsole.GameHost.Instance.Screen?.Update(SadConsole.GameHost.Instance.UpdateFrameDelta); + + ((SadConsole.Game)SadConsole.Game.Instance).InvokeFrameUpdate(); } } } diff --git a/SadConsole.Host.MonoGame/Renderers/Steps/ControlHostRenderStep.cs b/SadConsole.Host.MonoGame/Renderers/Steps/ControlHostRenderStep.cs index fcaccf2e7..5b9ffa9fc 100644 --- a/SadConsole.Host.MonoGame/Renderers/Steps/ControlHostRenderStep.cs +++ b/SadConsole.Host.MonoGame/Renderers/Steps/ControlHostRenderStep.cs @@ -65,7 +65,7 @@ public bool Refresh(IRenderer renderer, IScreenSurface screenObject, bool backin BackingTexture?.Dispose(); _cachedTexture?.Dispose(); - BackingTexture = new RenderTarget2D(Host.Global.GraphicsDevice, screenObject.AbsoluteArea.Width, screenObject.AbsoluteArea.Height, false, Host.Global.GraphicsDevice.DisplayMode.Format, DepthFormat.Depth24); + BackingTexture = new RenderTarget2D(Host.Global.GraphicsDevice, screenObject.AbsoluteArea.Width, screenObject.AbsoluteArea.Height, false, Host.Global.GraphicsDevice.DisplayMode.Format, DepthFormat.Depth24, 0, RenderTargetUsage.DiscardContents); _cachedTexture = new Host.GameTexture(BackingTexture); result = true; } diff --git a/SadConsole.Host.MonoGame/Renderers/Steps/CursorRenderStep.cs b/SadConsole.Host.MonoGame/Renderers/Steps/CursorRenderStep.cs index d86808f71..df45a9be7 100644 --- a/SadConsole.Host.MonoGame/Renderers/Steps/CursorRenderStep.cs +++ b/SadConsole.Host.MonoGame/Renderers/Steps/CursorRenderStep.cs @@ -2,6 +2,7 @@ using SadRogue.Primitives; using XnaRectangle = Microsoft.Xna.Framework.Rectangle; using XnaPoint = Microsoft.Xna.Framework.Point; +using Microsoft.Xna.Framework.Graphics; namespace SadConsole.Renderers; @@ -16,7 +17,7 @@ public class CursorRenderStep : IRenderStep /// public string Name => Constants.RenderStepNames.Cursor; - /// + /// public uint SortOrder { get; set; } = Constants.RenderStepSortValues.Cursor; /// @@ -38,39 +39,39 @@ public void Reset() } /// - public bool Refresh(IRenderer renderer, IScreenSurface screenObject, bool backingTextureChanged, bool isForced) => - false; + public bool Refresh(IRenderer renderer, IScreenSurface screenObject, bool backingTextureChanged, bool isForced) + { + foreach (Components.Cursor cursor in screenObject.GetSadComponents()) + { + if (cursor.CursorRenderCellActiveState.IsDirty) + return true; + } - /// - public void Composing(IRenderer renderer, IScreenSurface screenObject) { } + return false; + } /// - public void Render(IRenderer renderer, IScreenSurface screenObject) + public void Composing(IRenderer renderer, IScreenSurface screenObject) { - // If the tint isn't covering everything - if (screenObject.Tint.A != 255) + // Draw any cursors + foreach (Components.Cursor cursor in screenObject.GetSadComponents()) { - // Draw any cursors - foreach (Components.Cursor cursor in screenObject.GetSadComponents()) + if (cursor.IsVisible && screenObject.Surface.IsValidCell(cursor.Position.X, cursor.Position.Y) && screenObject.Surface.View.Contains(cursor.Position)) { - if (cursor.IsVisible && screenObject.Surface.IsValidCell(cursor.Position.X, cursor.Position.Y) && screenObject.Surface.View.Contains(cursor.Position)) - { - XnaPoint position = screenObject.Font.GetRenderRect(cursor.Position.X - screenObject.Surface.ViewPosition.X, - cursor.Position.Y - screenObject.Surface.ViewPosition.Y, - screenObject.FontSize).Translate(screenObject.AbsolutePosition).Position.ToMonoPoint(); + XnaRectangle rect = screenObject.Font.GetRenderRect(cursor.Position.X - screenObject.Surface.ViewPosition.X, + cursor.Position.Y - screenObject.Surface.ViewPosition.Y, + screenObject.FontSize).ToMonoRectangle(); - GameHost.Instance.DrawCalls.Enqueue( - new DrawCalls.DrawCallGlyph(cursor.CursorRenderCellActiveState, - new XnaRectangle(position.X, position.Y, screenObject.FontSize.X, screenObject.FontSize.Y), - screenObject.Font, - true - ) - ); - } + Host.Global.SharedSpriteBatch.Draw(((Host.GameTexture)screenObject.Font.Image).Texture, rect, + screenObject.Font.GetGlyphSourceRectangle(cursor.CursorRenderCellActiveState.Glyph).ToMonoRectangle(), + cursor.CursorRenderCellActiveState.Foreground.ToMonoColor(), + 0f, Microsoft.Xna.Framework.Vector2.Zero, SpriteEffects.None, 0.2f); } } } + /// + public void Render(IRenderer renderer, IScreenSurface screenObject) { } /// public void Dispose() => diff --git a/SadConsole.Host.MonoGame/Renderers/Steps/EntityRenderStep.cs b/SadConsole.Host.MonoGame/Renderers/Steps/EntityRenderStep.cs index a5f3485d5..8427e327c 100644 --- a/SadConsole.Host.MonoGame/Renderers/Steps/EntityRenderStep.cs +++ b/SadConsole.Host.MonoGame/Renderers/Steps/EntityRenderStep.cs @@ -63,7 +63,7 @@ public bool Refresh(IRenderer renderer, IScreenSurface screenObject, bool backin if (backingTextureChanged || BackingTexture == null || screenObject.AbsoluteArea.Width != BackingTexture.Width || screenObject.AbsoluteArea.Height != BackingTexture.Height) { BackingTexture?.Dispose(); - BackingTexture = new RenderTarget2D(Host.Global.GraphicsDevice, screenObject.AbsoluteArea.Width, screenObject.AbsoluteArea.Height, false, Host.Global.GraphicsDevice.DisplayMode.Format, DepthFormat.Depth24); + BackingTexture = new RenderTarget2D(Host.Global.GraphicsDevice, screenObject.AbsoluteArea.Width, screenObject.AbsoluteArea.Height, false, Host.Global.GraphicsDevice.DisplayMode.Format, DepthFormat.Depth24, 0, RenderTargetUsage.DiscardContents); _cachedTexture?.Dispose(); _cachedTexture = new Host.GameTexture(BackingTexture); result = true; diff --git a/SadConsole.Host.MonoGame/Renderers/Steps/LayeredSurfaceRenderStep.cs b/SadConsole.Host.MonoGame/Renderers/Steps/LayeredSurfaceRenderStep.cs index 8b86a1169..d026542fd 100644 --- a/SadConsole.Host.MonoGame/Renderers/Steps/LayeredSurfaceRenderStep.cs +++ b/SadConsole.Host.MonoGame/Renderers/Steps/LayeredSurfaceRenderStep.cs @@ -62,7 +62,7 @@ public bool Refresh(IRenderer renderer, IScreenSurface screenObject, bool backin if (backingTextureChanged || BackingTexture == null || screenObject.AbsoluteArea.Width != BackingTexture.Width || screenObject.AbsoluteArea.Height != BackingTexture.Height) { BackingTexture?.Dispose(); - BackingTexture = new RenderTarget2D(Host.Global.GraphicsDevice, screenObject.AbsoluteArea.Width, screenObject.AbsoluteArea.Height, false, Host.Global.GraphicsDevice.DisplayMode.Format, DepthFormat.Depth24); + BackingTexture = new RenderTarget2D(Host.Global.GraphicsDevice, screenObject.AbsoluteArea.Width, screenObject.AbsoluteArea.Height, false, Host.Global.GraphicsDevice.DisplayMode.Format, DepthFormat.Depth24, 0, RenderTargetUsage.DiscardContents); _cachedTexture?.Dispose(); _cachedTexture = new Host.GameTexture(BackingTexture); result = true; diff --git a/SadConsole.Host.MonoGame/Renderers/Steps/OutputSurfaceRenderStep.cs b/SadConsole.Host.MonoGame/Renderers/Steps/OutputSurfaceRenderStep.cs index cbe1e8f29..fed555676 100644 --- a/SadConsole.Host.MonoGame/Renderers/Steps/OutputSurfaceRenderStep.cs +++ b/SadConsole.Host.MonoGame/Renderers/Steps/OutputSurfaceRenderStep.cs @@ -1,4 +1,5 @@ using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; namespace SadConsole.Renderers; @@ -14,6 +15,11 @@ public class OutputSurfaceRenderStep : IRenderStep /// public uint SortOrder { get; set; } = Constants.RenderStepSortValues.Output; + /// + /// The shader to use when drawing the surface. + /// + public Effect ShaderEffect { get; set; } + /// /// Not used. /// @@ -33,7 +39,7 @@ public void Composing(IRenderer renderer, IScreenSurface screenObject) { } public void Render(IRenderer renderer, IScreenSurface screenObject) { var monoRenderer = (ScreenSurfaceRenderer)renderer; - GameHost.Instance.DrawCalls.Enqueue(new DrawCalls.DrawCallTexture(monoRenderer._backingTexture, new Vector2(screenObject.AbsoluteArea.Position.X, screenObject.AbsoluteArea.Position.Y), monoRenderer._finalDrawColor)); + GameHost.Instance.DrawCalls.Enqueue(new DrawCalls.DrawCallTexture(monoRenderer._backingTexture, new Vector2(screenObject.AbsoluteArea.Position.X, screenObject.AbsoluteArea.Position.Y), monoRenderer._finalDrawColor, ShaderEffect)); } /// diff --git a/SadConsole.Host.MonoGame/Renderers/Steps/SurfaceRenderStep.cs b/SadConsole.Host.MonoGame/Renderers/Steps/SurfaceRenderStep.cs index 83242c305..e891d1a9e 100644 --- a/SadConsole.Host.MonoGame/Renderers/Steps/SurfaceRenderStep.cs +++ b/SadConsole.Host.MonoGame/Renderers/Steps/SurfaceRenderStep.cs @@ -65,7 +65,7 @@ public bool Refresh(IRenderer renderer, IScreenSurface screenObject, bool backin if (backingTextureChanged || BackingTexture == null || screenObject.AbsoluteArea.Width != BackingTexture.Width || screenObject.AbsoluteArea.Height != BackingTexture.Height) { BackingTexture?.Dispose(); - BackingTexture = new RenderTarget2D(Host.Global.GraphicsDevice, screenObject.AbsoluteArea.Width, screenObject.AbsoluteArea.Height, false, Host.Global.GraphicsDevice.DisplayMode.Format, DepthFormat.Depth24); + BackingTexture = new RenderTarget2D(Host.Global.GraphicsDevice, screenObject.AbsoluteArea.Width, screenObject.AbsoluteArea.Height, false, Host.Global.GraphicsDevice.DisplayMode.Format, DepthFormat.Depth24, 0, RenderTargetUsage.DiscardContents); _cachedTexture?.Dispose(); _cachedTexture = new Host.GameTexture(BackingTexture); result = true; diff --git a/SadConsole.Host.MonoGame/SadConsole.Host.MonoGame.csproj b/SadConsole.Host.MonoGame/SadConsole.Host.MonoGame.csproj index 509938b23..1d1eaf58c 100644 --- a/SadConsole.Host.MonoGame/SadConsole.Host.MonoGame.csproj +++ b/SadConsole.Host.MonoGame/SadConsole.Host.MonoGame.csproj @@ -10,8 +10,8 @@ SadConsole.Host.MonoGame sadconsole;monogame;roguelike;cli;xna;game;development;console;ansi;ascii;textmode;dotnet -- Reversioned to follow new versioning scheme. -- Configuration namespace was moved to SadConsole directly. Only library specific config options are here now. +- Cursor rendering has changed to draw on the hosting surface. Previously it was rendered on top as an entire separate process. +- Change host to be a lot more friendly towards running from an existing MonoGame Game instead of SadConsole's game. diff --git a/SadConsole.Host.MonoGame/SadConsole.Host.MonoGame.xml b/SadConsole.Host.MonoGame/SadConsole.Host.MonoGame.xml index f9f8de14f..1a3138cbf 100644 --- a/SadConsole.Host.MonoGame/SadConsole.Host.MonoGame.xml +++ b/SadConsole.Host.MonoGame/SadConsole.Host.MonoGame.xml @@ -151,10 +151,11 @@ Helps manage the while draw calls are drawing. - + Resumes rendering to with SadConsole's default settings. + When true, skips assinging as the render target. @@ -171,6 +172,11 @@ The image to draw. + + + The shader to use when drawing the texture. + + Where on the to draw the texture. @@ -181,13 +187,14 @@ A color tint to apply when drawn. - + Creates a new instance of this draw call. The image to draw. The position on the to draw the image. A color tint to apply to the drawn image. + A shader to apply to the texture being drawn. Thrown when is null. @@ -368,9 +375,9 @@ The settings used in creating the game. - + - Method called by the class for initializing SadConsole specifics. Called prior to . + Method called by the class for initializing SadConsole specifics. Called prior to . The game instance. @@ -546,41 +553,34 @@ - - - A MonoGame instance that runs SadConsole. - - - + A MonoGame component that clears the screen with the color. - + + + + - + A component to draw how many frames per second the engine is performing at. - + - + - + - - - The game component to control SadConsole updates, input, and rendering. - - - + - The game component that clears the render output before each frame draw. + A MonoGame instance that runs SadConsole. @@ -606,7 +606,12 @@ - + + + Global variables used by the MonoGame host. + + + Resizes the by the specified font size. @@ -616,68 +621,66 @@ Additional pixel width to add to the resize. Additional pixel height to add to the resize. - + Regenerates the if the desired size doesn't match the current size. The width of the render output. The height of the render output. - + - Resets the target and determines the appropriate and based on the window or fullscreen state. + When , prevents the keyboard and mouse logic from running. - + - A game component that handles updating, input, and rendering of SadConsole. + The graphics device created by MonoGame. - + - Draws the SadConsole frame through draw calls when is true. + A sprite batch used by all of SadConsole to render objects. - Time between drawing frames. - + - Updates the SadConsole game objects and handles input. Only runs when is true. + The output texture. After each screen in SadConsole is drawn, they're then drawn on this output texture to compose the final scene. - - + - Global variables used by the MonoGame host. + Reference to the game timer used in the MonoGame update loop. - + - When , prevents the keyboard and mouse logic from running. + Reference to the game timer used in the MonoGame render loop. - + - The graphics device created by MonoGame. + Regenerates the if the desired size doesn't match the current size. - + - A sprite batch used by all of SadConsole to render objects. + Resizes the by the specified font size. - + - The output texture. After each screen in SadConsole is drawn, they're then drawn on this output texture to compose the final scene. + Resets the target and determines the appropriate and based on the window or fullscreen state. - + - Reference to the game timer used in the MonoGame update loop. + The game component to control SadConsole updates, input, and rendering. - + - Reference to the game timer used in the MonoGame render loop. + The game component that clears the render output before each frame draw. @@ -690,6 +693,48 @@ Sets the render target to , targeting the app window. + + + Resizes the by the specified font size. + + The size of the font to base the final values on. + The count of glyphs along the X-axis. + The count of glyphs along the Y-axis. + Additional pixel width to add to the resize. + Additional pixel height to add to the resize. + + + + Regenerates the if the desired size doesn't match the current size. + + The width of the render output. + The height of the render output. + + + + Resets the target and determines the appropriate and based on the window or fullscreen state. + + + + + A game component that handles updating, input, and rendering of SadConsole. + + + + + + + + Draws the SadConsole frame through draw calls when is true. + + Time between drawing frames. + + + + Updates the SadConsole game objects and handles input. Only runs when is true. + + + A settings class usually used when creating the host object. @@ -1016,7 +1061,7 @@ - + @@ -1152,6 +1197,11 @@ + + + The shader to use when drawing the surface. + + Not used. @@ -1370,7 +1420,7 @@ The builder object that composes the game startup. The configuration builder. - + The method is called by the MonoGame constructor. Some MonoGame specific settings may only be settable via the constructor. @@ -1378,7 +1428,7 @@ A method. The configuration object. - + Internal only. Called by the MonoGame game to finish configuring SadConsole. @@ -1386,6 +1436,13 @@ A method. The configuration object. + + + When called, tells the game host not to create the monogame game instance at . + + The builder object that composes the game startup. + The configuration object. + Extensions for the type. diff --git a/SadConsole.Host.MonoGameWPF/SadConsole.Host.MonoGameWPF.csproj b/SadConsole.Host.MonoGameWPF/SadConsole.Host.MonoGameWPF.csproj index 3cbc36252..e3aeb469d 100644 --- a/SadConsole.Host.MonoGameWPF/SadConsole.Host.MonoGameWPF.csproj +++ b/SadConsole.Host.MonoGameWPF/SadConsole.Host.MonoGameWPF.csproj @@ -11,8 +11,8 @@ SadConsole.Host.MonoGameWPF sadconsole;monogame;roguelike;cli;xna;game;development;console;ansi;ascii;textmode;dotnet;wpf -- Reversioned to follow new versioning scheme. -- Configuration namespace was moved to SadConsole directly. Only library specific config options are here now. +- Cursor rendering has changed to draw on the hosting surface. Previously it was rendered on top as an entire separate process. +- Change host to be a lot more friendly towards running from an existing MonoGame Game instead of SadConsole's game. diff --git a/SadConsole.Host.MonoGameWPF/SadConsole.Host.MonoGameWPF.xml b/SadConsole.Host.MonoGameWPF/SadConsole.Host.MonoGameWPF.xml index 49ba348a8..940109dc2 100644 --- a/SadConsole.Host.MonoGameWPF/SadConsole.Host.MonoGameWPF.xml +++ b/SadConsole.Host.MonoGameWPF/SadConsole.Host.MonoGameWPF.xml @@ -151,10 +151,11 @@ Helps manage the while draw calls are drawing. - + Resumes rendering to with SadConsole's default settings. + When true, skips assinging as the render target. @@ -171,6 +172,11 @@ The image to draw. + + + The shader to use when drawing the texture. + + Where on the to draw the texture. @@ -181,13 +187,14 @@ A color tint to apply when drawn. - + Creates a new instance of this draw call. The image to draw. The position on the to draw the image. A color tint to apply to the drawn image. + A shader to apply to the texture being drawn. Thrown when is null. @@ -353,7 +360,7 @@ The settings used in creating the game. - + Method called by the class for initializing SadConsole specifics. Called prior to . @@ -520,19 +527,19 @@ - - - A MonoGame instance that runs SadConsole. - - - + A MonoGame component that clears the screen with the color. - + + + + A MonoGame instance that runs SadConsole. + + This event handler is similar to the startup area of a normal SadConsole app, where you usually call Game.Instance.OnStart = ... @@ -572,27 +579,27 @@ Resets the target and determines the appropriate and based on the window or fullscreen state. - + - A game component that handles updating, input, and rendering of SadConsole. + Global variables used by the MonoGame host. - + - Draws the SadConsole frame through draw calls when is true. + Resizes the by the specified font size. - Time between drawing frames. + The size of the font to base the final values on. + The count of glyphs along the X-axis. + The count of glyphs along the Y-axis. + Additional pixel width to add to the resize. + Additional pixel height to add to the resize. - + - Updates the SadConsole game objects and handles input. Only runs when is true. - - - - - - Global variables used by the MonoGame host. + Regenerates the if the desired size doesn't match the current size. + The width of the render output. + The height of the render output. @@ -624,6 +631,31 @@ Reference to the game timer used in the MonoGame render loop. + + + Regenerates the if the desired size doesn't match the current size. + + + + + Resizes the by the specified font size. + + + + + Resets the target and determines the appropriate and based on the window or fullscreen state. + + + + + The game component to control SadConsole updates, input, and rendering. + + + + + The game component that clears the render output before each frame draw. + + The WPF control used in drawing the screen. @@ -639,6 +671,45 @@ Sets the render target to the control. + + + Resizes the by the specified font size. + + The size of the font to base the final values on. + The count of glyphs along the X-axis. + The count of glyphs along the Y-axis. + Additional pixel width to add to the resize. + Additional pixel height to add to the resize. + + + + Regenerates the if the desired size doesn't match the current size. + + The width of the render output. + The height of the render output. + + + + Resets the target and determines the appropriate and based on the window or fullscreen state. + + + + + A game component that handles updating, input, and rendering of SadConsole. + + + + + Draws the SadConsole frame through draw calls when is true. + + Time between drawing frames. + + + + Updates the SadConsole game objects and handles input. Only runs when is true. + + + A settings class usually used when creating the host object. @@ -965,7 +1036,7 @@ - + @@ -1101,6 +1172,11 @@ + + + The shader to use when drawing the surface. + + Not used. @@ -1328,7 +1404,7 @@ The total pixel height of the control. The configuration builder. - + The method is called by the MonoGame constructor. Some MonoGame specific settings may only be settable via the constructor. @@ -1336,7 +1412,7 @@ A method. The configuration object. - + Internal only. Called by the MonoGame game to finish configuring SadConsole. @@ -1344,6 +1420,13 @@ A method. The configuration object. + + + When called, tells the game host not to create the monogame game instance at . + + The builder object that composes the game startup. + The configuration object. + Extensions for the type. diff --git a/SadConsole.Host.SFML/Renderers/Steps/CursorRenderStep.cs b/SadConsole.Host.SFML/Renderers/Steps/CursorRenderStep.cs index be841993a..6427e2c8f 100644 --- a/SadConsole.Host.SFML/Renderers/Steps/CursorRenderStep.cs +++ b/SadConsole.Host.SFML/Renderers/Steps/CursorRenderStep.cs @@ -1,5 +1,6 @@ using System; using SadRogue.Primitives; +using SFML.Graphics; namespace SadConsole.Renderers; @@ -36,37 +37,40 @@ public void Reset() } /// - public bool Refresh(IRenderer renderer, IScreenSurface screenObject, bool backingTextureChanged, bool isForced) => - false; + public bool Refresh(IRenderer renderer, IScreenSurface screenObject, bool backingTextureChanged, bool isForced) + { + foreach (Components.Cursor cursor in screenObject.GetSadComponents()) + { + if (cursor.CursorRenderCellActiveState.IsDirty) + return true; + } - /// - public void Composing(IRenderer renderer, IScreenSurface screenObject) { } + return false; + } /// - public void Render(IRenderer renderer, IScreenSurface screenObject) + public void Composing(IRenderer renderer, IScreenSurface screenObject) { - // If the tint isn't covering everything - if (screenObject.Tint.A != 255) + // Draw any cursors + foreach (Components.Cursor cursor in screenObject.GetSadComponents()) { - // Draw any cursors - foreach (Components.Cursor cursor in screenObject.GetSadComponents()) + if (cursor.IsVisible && screenObject.Surface.IsValidCell(cursor.Position.X, cursor.Position.Y) && screenObject.Surface.View.Contains(cursor.Position)) { - if (cursor.IsVisible && screenObject.Surface.IsValidCell(cursor.Position.X, cursor.Position.Y) && screenObject.Surface.View.Contains(cursor.Position)) - { - Point cursorPosition = screenObject.AbsoluteArea.Position + screenObject.Font.GetRenderRect(cursor.Position.X - screenObject.Surface.ViewPosition.X, cursor.Position.Y - screenObject.Surface.ViewPosition.Y, screenObject.FontSize).Position; + IntRect rect = screenObject.Font.GetRenderRect(cursor.Position.X - screenObject.Surface.ViewPosition.X, + cursor.Position.Y - screenObject.Surface.ViewPosition.Y, + screenObject.FontSize).ToIntRect(); - GameHost.Instance.DrawCalls.Enqueue( - new DrawCalls.DrawCallGlyph(cursor.CursorRenderCellActiveState, - new Rectangle(cursorPosition.X, cursorPosition.Y, screenObject.FontSize.X, screenObject.FontSize.Y).ToIntRect(), - screenObject.Font, - true - ) - ); - } + Host.Global.SharedSpriteBatch.DrawCell(cursor.CursorRenderCellActiveState, rect, + cursor.CursorRenderCellActiveState.Background != SadRogue.Primitives.Color.Transparent + && cursor.CursorRenderCellActiveState.Background != screenObject.Surface.DefaultBackground, + screenObject.Font); } } } + /// + public void Render(IRenderer renderer, IScreenSurface screenObject) { } + /// public void Dispose() => Reset(); diff --git a/SadConsole.Host.SFML/SadConsole.Host.SFML.csproj b/SadConsole.Host.SFML/SadConsole.Host.SFML.csproj index 61362c29d..9ae35c817 100644 --- a/SadConsole.Host.SFML/SadConsole.Host.SFML.csproj +++ b/SadConsole.Host.SFML/SadConsole.Host.SFML.csproj @@ -10,8 +10,7 @@ SadConsole.Host.SFML sadconsole;sfml;roguelike;cli;game;development;console;ansi;ascii;textmode;dotnet -- Reversioned to follow new versioning scheme. -- Configuration namespace was moved to SadConsole directly. Only library specific config options are here now. +Cursor rendering has changed to draw on the hosting surface. Previously it was rendered on top as an entire separate process. diff --git a/SadConsole.Host.Shared/Configuration/MonoGameCallbackConfig.cs b/SadConsole.Host.Shared/Configuration/MonoGameCallbackConfig.cs index 7255de0e0..967c35d07 100644 --- a/SadConsole.Host.Shared/Configuration/MonoGameCallbackConfig.cs +++ b/SadConsole.Host.Shared/Configuration/MonoGameCallbackConfig.cs @@ -3,6 +3,12 @@ using System; +#if WPF +using MonoGameGame = MonoGame.Framework.WpfInterop.WpfGame; +#else +using MonoGameGame = Microsoft.Xna.Framework.Game; +#endif + namespace SadConsole.Configuration; public static partial class ExtensionsHost @@ -13,7 +19,7 @@ public static partial class ExtensionsHost /// The builder object that composes the game startup. /// A method. /// The configuration object. - public static Builder WithMonoGameCtor(this Builder configBuilder, Action monogameCtorCallback) + public static Builder WithMonoGameCtor(this Builder configBuilder, Action monogameCtorCallback) { MonoGameCallbackConfig config = configBuilder.GetOrCreateConfig(); @@ -28,7 +34,7 @@ public static Builder WithMonoGameCtor(this Builder configBuilder, ActionThe builder object that composes the game startup. /// A method. /// The configuration object. - internal static Builder WithMonoGameInit(this Builder configBuilder, Action monogameInitCallback) + public static Builder WithMonoGameInit(this Builder configBuilder, Action monogameInitCallback) { MonoGameCallbackConfig config = configBuilder.GetOrCreateConfig(); @@ -36,12 +42,28 @@ internal static Builder WithMonoGameInit(this Builder configBuilder, Action + /// When called, tells the game host not to create the monogame game instance at . + /// + /// The builder object that composes the game startup. + /// The configuration object. + public static Builder SkipMonoGameGameCreation(this Builder configBuilder) + { + MonoGameCallbackConfig config = configBuilder.GetOrCreateConfig(); + + config.SkipMonoGameGameCreation = true; + + return configBuilder; + } } public class MonoGameCallbackConfig : IConfigurator { - public Action? MonoGameCtorCallback { get; set; } - public Action? MonoGameInitCallback { get; set; } + public Action? MonoGameCtorCallback { get; set; } + public Action? MonoGameInitCallback { get; set; } + + public bool SkipMonoGameGameCreation { get; set; } public void Run(Builder config, GameHost game) { diff --git a/SadConsole.sln b/SadConsole.sln index 7bc4752fd..e715a0b9b 100644 --- a/SadConsole.sln +++ b/SadConsole.sln @@ -56,6 +56,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MSBuild", "MSBuild", "{EFF6 MSBuild\Common.props = MSBuild\Common.props EndProjectSection EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoGameFirst", "Samples\MonoGameFirst\MonoGameFirst.csproj", "{F7FF382F-BE4F-4233-BF02-0BBB4DB9AB00}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -184,6 +186,14 @@ Global {7A784A5A-F311-4180-B1C7-DF3A950ED0CF}.Release|Any CPU.Build.0 = Release|Any CPU {7A784A5A-F311-4180-B1C7-DF3A950ED0CF}.Release|x64.ActiveCfg = Release|Any CPU {7A784A5A-F311-4180-B1C7-DF3A950ED0CF}.Release|x64.Build.0 = Release|Any CPU + {F7FF382F-BE4F-4233-BF02-0BBB4DB9AB00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F7FF382F-BE4F-4233-BF02-0BBB4DB9AB00}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F7FF382F-BE4F-4233-BF02-0BBB4DB9AB00}.Debug|x64.ActiveCfg = Debug|Any CPU + {F7FF382F-BE4F-4233-BF02-0BBB4DB9AB00}.Debug|x64.Build.0 = Debug|Any CPU + {F7FF382F-BE4F-4233-BF02-0BBB4DB9AB00}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F7FF382F-BE4F-4233-BF02-0BBB4DB9AB00}.Release|Any CPU.Build.0 = Release|Any CPU + {F7FF382F-BE4F-4233-BF02-0BBB4DB9AB00}.Release|x64.ActiveCfg = Release|Any CPU + {F7FF382F-BE4F-4233-BF02-0BBB4DB9AB00}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -204,6 +214,7 @@ Global {DCA1D25E-2FFA-4904-AB66-36015C2E9817} = {E0F64D5F-67B3-4882-9F49-FB71BCF91325} {7A784A5A-F311-4180-B1C7-DF3A950ED0CF} = {C08D04C0-B57A-448B-A312-2A9A8E3CA435} {EFF675F4-080A-406C-9FBC-F0FC0E05B94E} = {897D0FB7-4163-4C23-A938-31C06F8E84FF} + {F7FF382F-BE4F-4233-BF02-0BBB4DB9AB00} = {E0F64D5F-67B3-4882-9F49-FB71BCF91325} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {55A32531-B44C-4843-9FC5-052AAE7525A3} diff --git a/SadConsole/Components/Cursor.cs b/SadConsole/Components/Cursor.cs index 6b62e2084..0635a41d5 100644 --- a/SadConsole/Components/Cursor.cs +++ b/SadConsole/Components/Cursor.cs @@ -1231,6 +1231,7 @@ void IComponent.OnRemoved(IScreenObject host) if (_cursorRenderStep != null) { ((IScreenSurface)host).Renderer?.Steps.Remove(_cursorRenderStep); + ((IScreenSurface)host).IsDirty = true; _cursorRenderStep.Dispose(); _cursorRenderStep = null; } diff --git a/SadConsole/Components/Overlay.cs b/SadConsole/Components/Overlay.cs index 2cd4675fa..7d1569f04 100644 --- a/SadConsole/Components/Overlay.cs +++ b/SadConsole/Components/Overlay.cs @@ -69,8 +69,11 @@ public override void OnRemoved(IScreenObject host) } /// - public override void Update(IScreenObject host, TimeSpan delta) => + public override void Update(IScreenObject host, TimeSpan delta) + { MatchSurface((IScreenSurface)host); + Surface.Update(delta); + } private void MatchSurface(IScreenSurface host) { diff --git a/SadConsole/Entities/EntityManager.cs b/SadConsole/Entities/EntityManager.cs index fd5ea3606..99068f9a6 100644 --- a/SadConsole/Entities/EntityManager.cs +++ b/SadConsole/Entities/EntityManager.cs @@ -277,7 +277,7 @@ public override void OnRemoved(IScreenObject host) RenderStep.Dispose(); RenderStep = null; } - + _screen = null; _screenCachedFont = null; _screenCachedFontSize = Point.None; diff --git a/SadConsole/ICellSurface.Editor.cs b/SadConsole/ICellSurface.Editor.cs index 02e3c22fa..bc4c63758 100644 --- a/SadConsole/ICellSurface.Editor.cs +++ b/SadConsole/ICellSurface.Editor.cs @@ -2357,7 +2357,7 @@ public static void DrawBox(this ISurface obj, Rectangle area, ShapeParameters pa { Rectangle fillRect = area.Expand(-1, -1); - if (parameters.HasFill && parameters.FillGlyph != null) + if (parameters.HasFill && parameters.FillGlyph != null && fillRect.Area > 0) { obj.Surface.Fill(fillRect, parameters.IgnoreFillForeground ? (Color?)null : parameters.FillGlyph.Foreground, @@ -2380,19 +2380,23 @@ public static void DrawBox(this ISurface obj, Rectangle area, ShapeParameters pa ColoredGlyphBase border = parameters.BorderGlyph ?? throw new NullReferenceException("Shape parameters is missing the border glyph."); // Draw the major sides - DrawLine(obj, area.Position, area.Position + new Point(area.Width - 1, 0), connectedLineStyle[(int)ICellSurface.ConnectedLineIndex.Top], + DrawLine(obj, area.Position, area.Position + new Point(area.Width - 1, 0), + parameters.IgnoreBorderGlyph ? (int?)null : connectedLineStyle[(int)ICellSurface.ConnectedLineIndex.Top], parameters.IgnoreBorderForeground ? (Color?)null : border.Foreground, parameters.IgnoreBorderBackground ? (Color?)null : border.Background, parameters.IgnoreBorderMirror ? (Mirror?)null : border.Mirror); - DrawLine(obj, area.Position + new Point(0, area.Height - 1), area.Position + new Point(area.Width - 1, area.Height - 1), connectedLineStyle[(int)ICellSurface.ConnectedLineIndex.Bottom], + DrawLine(obj, area.Position + new Point(0, area.Height - 1), area.Position + new Point(area.Width - 1, area.Height - 1), + parameters.IgnoreBorderGlyph ? (int?)null : connectedLineStyle[(int)ICellSurface.ConnectedLineIndex.Bottom], parameters.IgnoreBorderForeground ? (Color?)null : border.Foreground, parameters.IgnoreBorderBackground ? (Color?)null : border.Background, parameters.IgnoreBorderMirror ? (Mirror?)null : border.Mirror); - DrawLine(obj, area.Position, area.Position + new Point(0, area.Height - 1), connectedLineStyle[(int)ICellSurface.ConnectedLineIndex.Left], + DrawLine(obj, area.Position, area.Position + new Point(0, area.Height - 1), + parameters.IgnoreBorderGlyph ? (int?)null : connectedLineStyle[(int)ICellSurface.ConnectedLineIndex.Left], parameters.IgnoreBorderForeground ? (Color?)null : border.Foreground, parameters.IgnoreBorderBackground ? (Color?)null : border.Background, parameters.IgnoreBorderMirror ? (Mirror?)null : border.Mirror); - DrawLine(obj, area.Position + new Point(area.Width - 1, 0), area.Position + new Point(area.Width - 1, area.Height - 1), connectedLineStyle[(int)ICellSurface.ConnectedLineIndex.Right], + DrawLine(obj, area.Position + new Point(area.Width - 1, 0), area.Position + new Point(area.Width - 1, area.Height - 1), + parameters.IgnoreBorderGlyph ? (int?)null : connectedLineStyle[(int)ICellSurface.ConnectedLineIndex.Right], parameters.IgnoreBorderForeground ? (Color?)null : border.Foreground, parameters.IgnoreBorderBackground ? (Color?)null : border.Background, parameters.IgnoreBorderMirror ? (Mirror?)null : border.Mirror); @@ -2413,22 +2417,26 @@ public static void DrawBox(this ISurface obj, Rectangle area, ShapeParameters pa // Draw the major sides ColoredGlyphBase border = connectedLineStyle[(int)ICellSurface.ConnectedLineIndex.Top]; - DrawLine(obj, area.Position, area.Position + new Point(area.Width - 1, 0), border.Glyph, + DrawLine(obj, area.Position, area.Position + new Point(area.Width - 1, 0), + parameters.IgnoreBorderGlyph ? (int?)null : border.Glyph, parameters.IgnoreBorderForeground ? (Color?)null : border.Foreground, parameters.IgnoreBorderBackground ? (Color?)null : border.Background, parameters.IgnoreBorderMirror ? (Mirror?)null : border.Mirror); border = connectedLineStyle[(int)ICellSurface.ConnectedLineIndex.Bottom]; - DrawLine(obj, area.Position + new Point(0, area.Height - 1), area.Position + new Point(area.Width - 1, area.Height - 1), border.Glyph, + DrawLine(obj, area.Position + new Point(0, area.Height - 1), area.Position + new Point(area.Width - 1, area.Height - 1), + parameters.IgnoreBorderGlyph ? (int?)null : border.Glyph, parameters.IgnoreBorderForeground ? (Color?)null : border.Foreground, parameters.IgnoreBorderBackground ? (Color?)null : border.Background, parameters.IgnoreBorderMirror ? (Mirror?)null : border.Mirror); border = connectedLineStyle[(int)ICellSurface.ConnectedLineIndex.Left]; - DrawLine(obj, area.Position, area.Position + new Point(0, area.Height - 1), border.Glyph, + DrawLine(obj, area.Position, area.Position + new Point(0, area.Height - 1), + parameters.IgnoreBorderGlyph ? (int?)null : border.Glyph, parameters.IgnoreBorderForeground ? (Color?)null : border.Foreground, parameters.IgnoreBorderBackground ? (Color?)null : border.Background, parameters.IgnoreBorderMirror ? (Mirror?)null : border.Mirror); border = connectedLineStyle[(int)ICellSurface.ConnectedLineIndex.Right]; - DrawLine(obj, area.Position + new Point(area.Width - 1, 0), area.Position + new Point(area.Width - 1, area.Height - 1), border.Glyph, + DrawLine(obj, area.Position + new Point(area.Width - 1, 0), area.Position + new Point(area.Width - 1, area.Height - 1), + parameters.IgnoreBorderGlyph ? (int?)null : border.Glyph, parameters.IgnoreBorderForeground ? (Color?)null : border.Foreground, parameters.IgnoreBorderBackground ? (Color?)null : border.Background, parameters.IgnoreBorderMirror ? (Mirror?)null : border.Mirror); @@ -2444,19 +2452,23 @@ public static void DrawBox(this ISurface obj, Rectangle area, ShapeParameters pa { // Draw the major sides ColoredGlyphBase border = parameters.BorderGlyph ?? throw new NullReferenceException("Shape parameters is missing the border glyph."); - DrawLine(obj, area.Position, area.Position + new Point(area.Width - 1, 0), border.Glyph, + DrawLine(obj, area.Position, area.Position + new Point(area.Width - 1, 0), + parameters.IgnoreBorderGlyph ? (int?)null : border.Glyph, parameters.IgnoreBorderForeground ? (Color?)null : border.Foreground, parameters.IgnoreBorderBackground ? (Color?)null : border.Background, parameters.IgnoreBorderMirror ? (Mirror?)null : border.Mirror); - DrawLine(obj, area.Position + new Point(0, area.Height - 1), area.Position + new Point(area.Width - 1, area.Height - 1), border.Glyph, + DrawLine(obj, area.Position + new Point(0, area.Height - 1), area.Position + new Point(area.Width - 1, area.Height - 1), + parameters.IgnoreBorderGlyph ? (int?)null : border.Glyph, parameters.IgnoreBorderForeground ? (Color?)null : border.Foreground, parameters.IgnoreBorderBackground ? (Color?)null : border.Background, parameters.IgnoreBorderMirror ? (Mirror?)null : border.Mirror); - DrawLine(obj, area.Position, area.Position + new Point(0, area.Height - 1), border.Glyph, + DrawLine(obj, area.Position, area.Position + new Point(0, area.Height - 1), + parameters.IgnoreBorderGlyph ? (int?)null : border.Glyph, parameters.IgnoreBorderForeground ? (Color?)null : border.Foreground, parameters.IgnoreBorderBackground ? (Color?)null : border.Background, parameters.IgnoreBorderMirror ? (Mirror?)null : border.Mirror); - DrawLine(obj, area.Position + new Point(area.Width - 1, 0), area.Position + new Point(area.Width - 1, area.Height - 1), border.Glyph, + DrawLine(obj, area.Position + new Point(area.Width - 1, 0), area.Position + new Point(area.Width - 1, area.Height - 1), + parameters.IgnoreBorderGlyph ? (int?)null : border.Glyph, parameters.IgnoreBorderForeground ? (Color?)null : border.Foreground, parameters.IgnoreBorderBackground ? (Color?)null : border.Background, parameters.IgnoreBorderMirror ? (Mirror?)null : border.Mirror); @@ -2476,7 +2488,7 @@ public static void DrawCircle(this ISurface obj, Rectangle area, ShapeParameters if (parameters.BorderGlyph == null) throw new NullReferenceException("Shape parameters is missing the border glyph."); - foreach (var location in Shapes.GetEllipse(area.Position, area.MaxExtent)) + foreach (Point location in Shapes.GetEllipse(area.Position, area.MaxExtent)) { if (parameters.HasBorder && obj.Surface.IsValidCell(location.X, location.Y)) { @@ -2540,6 +2552,8 @@ public static void DrawCircle(this ISurface obj, Rectangle area, ShapeParameters Algorithms.FloodFill(area.Center.ToIndex(obj.Surface.Width), isTargetCell, fillCell, getConnectedCells); } + + obj.Surface.IsDirty = true; } /// diff --git a/SadConsole/SadConsole.csproj b/SadConsole/SadConsole.csproj index 6bb84fe85..657401d76 100644 --- a/SadConsole/SadConsole.csproj +++ b/SadConsole/SadConsole.csproj @@ -10,15 +10,10 @@ SadConsole sadconsole;roguelike;cli;game;development;console;ansi;ascii;textmode;dotnet -- Reversioned to follow new versioning scheme. -- Configuration namespace and related types was moved here from the hosts. -- Added Components.Overlay which displays a surface on top of an existing console/surface object as a top layer. -- Bug: Font.OnFontChanged wasn't actually being called. It is now. -- Added Coroutine NuGet library. This is included now with SadConsole. -- Added CoroutineHandlerComponent which is a manager that you can add to an object and coroutines to. -- Cursor.PrintCoroutine which prints as a coroutine, pausing at each character. -- Instructions.DrawString updated to use Cursor.PrintCoroutine which lets it pretty print word breaks. -- XML docs didn't genereate correctly for the last build. +- When a Cursor is removed from a host object, the object is set to dirty now. +- Overlay component now calls update on the attached visual. +- Fix bug with DrawCircle not setting dirty. +- Fix bug with DrawBox not respecting ignore glyph setting. diff --git a/SadConsole/SadConsole.xml b/SadConsole/SadConsole.xml index 5b26b2e52..c824b3362 100644 --- a/SadConsole/SadConsole.xml +++ b/SadConsole/SadConsole.xml @@ -2710,23 +2710,6 @@ The string "Console". - - - A debugging screen that takes the place of the active and displays information about SadConsole. - - - - - Displays the debugger. - - The font to use the debugging screen. - The size of the font. - - - - Shows the debug screen with the default font and size. - - A draw call that invokes an delegate. @@ -10990,16 +10973,6 @@ A message to provide to the result if the validation fails. The validation result. - - - Pattern:
- [a-zA-Z]
- Explanation:
- - ○ Match a character in the set [A-Za-z].
-
-
-
A color that can be adjusted by brightness and mapped to a color. @@ -15000,6 +14973,14 @@ The text to use for the colored string. A new colored string object. + + +.ColoredString"/> object using the current gradient. + + The gradient to work with. + The text to use for the colored string. + A new colored string object. + Custom -derived type for the LettersOnly method. diff --git a/SadConsoleEditor.sln b/SadConsoleEditor.sln index 8e447d083..5d8a95ae8 100644 --- a/SadConsoleEditor.sln +++ b/SadConsoleEditor.sln @@ -13,6 +13,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SadConsole.Debug.MonoGame", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Editor", "Samples\Editor\Editor.csproj", "{2972BDC7-1337-4BDC-8C37-99227A7861FD}" EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "SadConsole.Host.Shared", "SadConsole.Host.Shared\SadConsole.Host.Shared.shproj", "{595434C5-2192-495B-903C-7922D7E8B149}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -46,4 +48,8 @@ Global GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {55A32531-B44C-4843-9FC5-052AAE7525A3} EndGlobalSection + GlobalSection(SharedMSBuildProjectFiles) = preSolution + SadConsole.Host.Shared\SadConsole.Host.Shared.projitems*{595434c5-2192-495b-903c-7922d7e8b149}*SharedItemsImports = 13 + SadConsole.Host.Shared\SadConsole.Host.Shared.projitems*{f02a2b3b-706e-4277-9ce2-b47733ccb22f}*SharedItemsImports = 5 + EndGlobalSection EndGlobal diff --git a/Samples/Demo/Content/Assets.mgcb b/Samples/Demo/Content/Assets.mgcb new file mode 100644 index 000000000..946fcdc72 --- /dev/null +++ b/Samples/Demo/Content/Assets.mgcb @@ -0,0 +1,26 @@ + +#----------------------------- Global Properties ----------------------------# + +/outputDir:bin/$(Platform) +/intermediateDir:obj/$(Platform) +/platform:DesktopGL +/config: +/profile:Reach +/compress:False + +#-------------------------------- References --------------------------------# + + +#---------------------------------- Content ---------------------------------# + +#begin FinalDraw.fx +/importer:EffectImporter +/processor:EffectProcessor +/processorParam:DebugMode=Auto +/build:FinalDraw.fx + +#begin crt-lottes-mg.fx +/importer:EffectImporter +/processor:EffectProcessor +/processorParam:DebugMode=Auto +/build:crt-lottes-mg.fx diff --git a/Samples/Demo/Content/FinalDraw.fx b/Samples/Demo/Content/FinalDraw.fx new file mode 100644 index 000000000..a4b3878c1 --- /dev/null +++ b/Samples/Demo/Content/FinalDraw.fx @@ -0,0 +1,38 @@ +#if OPENGL + #define SV_POSITION POSITION + #define VS_SHADERMODEL vs_3_0 + #define PS_SHADERMODEL ps_3_0 +#else + #define VS_SHADERMODEL vs_4_0_level_9_1 + #define PS_SHADERMODEL ps_4_0_level_9_1 +#endif + +Texture2D SpriteTexture; +sampler s0; + +sampler2D SpriteTextureSampler = sampler_state +{ + Texture = ; +}; + +struct VertexShaderOutput +{ + float4 Position : SV_POSITION; + float4 Color : COLOR0; + float2 TextureCoordinates : TEXCOORD0; +}; + +float4 MainPS(VertexShaderOutput input) : COLOR +{ + float4 color = tex2D(SpriteTextureSampler,input.TextureCoordinates) * input.Color; + color.gb = color.r; + return color; +} + +technique SpriteDrawing +{ + pass P0 + { + PixelShader = compile PS_SHADERMODEL MainPS(); + } +}; \ No newline at end of file diff --git a/Samples/Demo/Content/crt-lottes-mg.fx b/Samples/Demo/Content/crt-lottes-mg.fx new file mode 100644 index 000000000..3aba04124 --- /dev/null +++ b/Samples/Demo/Content/crt-lottes-mg.fx @@ -0,0 +1,317 @@ +#if OPENGL + #define SV_POSITION POSITION + #define VS_SHADERMODEL vs_3_0 + #define PS_SHADERMODEL ps_3_0 +#else + #define VS_SHADERMODEL vs_4_0_level_9_1 + #define PS_SHADERMODEL ps_4_0_level_9_1 + #define HLSL_4 +#endif + +struct VertexShaderOutput +{ + float4 Position : SV_POSITION; + float4 Color : COLOR0; + float2 texCoord : TEXCOORD0; +}; + +// +// PUBLIC DOMAIN CRT STYLED SCAN-LINE SHADER +// +// by Timothy Lottes +// +// This is more along the style of a really good CGA arcade monitor. +// With RGB inputs instead of NTSC. +// The shadow mask example has the mask rotated 90 degrees for less chromatic aberration. +// +// Left it unoptimized to show the theory behind the algorithm. +// +// It is an example what I personally would want as a display option for pixel art games. +// Please take and use, change, or whatever. +// + +// -- config -- // +// #pragma parameter hardScan "hardScan" -8.0 -20.0 0.0 1.0 // default, minimum, maximum, optional step +// #pragma parameter hardPix "hardPix" -3.0 -20.0 0.0 1.0 +// #pragma parameter warpX "warpX" 0.031 0.0 0.125 0.01 +// #pragma parameter warpY "warpY" 0.041 0.0 0.125 0.01 +// #pragma parameter maskDark "maskDark" 0.5 0.0 2.0 0.1 +// #pragma parameter maskLight "maskLight" 1.5 0.0 2.0 0.1 +// #pragma parameter scaleInLinearGamma "scaleInLinearGamma" 1.0 0.0 1.0 1.0 +// #pragma parameter shadowMask "shadowMask" 3.0 0.0 4.0 1.0 +// #pragma parameter brightboost "brightness" 1.0 0.0 2.0 0.05 +// #pragma parameter hardBloomPix "bloom-x soft" -1.5 -2.0 -0.5 0.1 +// #pragma parameter hardBloomScan "bloom-y soft" -2.0 -4.0 -1.0 0.1 +// #pragma parameter bloomAmount "bloom amt" 0.15 0.0 1.0 0.05 +// #pragma parameter shape "filter kernel shape" 2.0 0.0 10.0 0.05 + +float hardScan; +float hardPix; +float warpX; +float warpY; +float maskDark; +float maskLight; +float scaleInLinearGamma; +float shadowMask; +float brightboost; +float hardBloomScan; +float hardBloomPix; +float bloomAmount; +float shape; + +float2 textureSize; +float2 videoSize; +float2 outputSize; + +//Uncomment to reduce instructions with simpler linearization +//(fixes HD3000 Sandy Bridge IGP) +//#define SIMPLE_LINEAR_GAMMA +#define DO_BLOOM 1 + +// ------------- // + +Texture2D decal; +sampler2D DecalSampler = sampler_state +{ + Texture = ; +}; + +float4x4 modelViewProj; + +#define warp float2(warpX,warpY) + +//------------------------------------------------------------------------ + +// sRGB to Linear. +// Assuing using sRGB typed textures this should not be needed. +#ifdef SIMPLE_LINEAR_GAMMA +float ToLinear1(float c) +{ + return c; +} +float3 ToLinear(float3 c) +{ + return c; +} + +float3 ToSrgb(float3 c) +{ + return pow(c, 1.0 / 2.2); +} +#else +float ToLinear1(float c) +{ + if (scaleInLinearGamma==0) return c; + return(c<=0.04045)?c/12.92:pow((c+0.055)/1.055,2.4); +} +float3 ToLinear(float3 c) +{ + if (scaleInLinearGamma==0) return c; + return float3(ToLinear1(c.r),ToLinear1(c.g),ToLinear1(c.b)); +} + +// Linear to sRGB. +// Assuming using sRGB typed textures this should not be needed. +float ToSrgb1(float c) +{ + if (scaleInLinearGamma==0) return c; + return(c<0.0031308?c*12.92:1.055*pow(c,0.41666)-0.055); +} + +float3 ToSrgb(float3 c) +{ + if (scaleInLinearGamma==0) return c; + return float3(ToSrgb1(c.r),ToSrgb1(c.g),ToSrgb1(c.b)); +} +#endif + +// Nearest emulated sample given floating point position and texel offset. +// Also zero's off screen. +float3 Fetch(float2 pos, float2 off, float2 texture_size){ + pos=(floor(pos*texture_size.xy+off)+float2(0.5,0.5))/texture_size.xy; +#ifdef SIMPLE_LINEAR_GAMMA + return ToLinear(brightboost * pow(tex2D(DecalSampler,pos.xy).rgb, 2.2)); +#else + return ToLinear(brightboost * tex2D(DecalSampler,pos.xy).rgb); +#endif +} + +// Distance in emulated pixels to nearest texel. +float2 Dist(float2 pos, float2 texture_size){pos=pos*texture_size.xy;return -(frac(pos)-float2(0.5, 0.5));} + +// 1D Gaussian. +float Gaus(float pos,float scale){return exp2(scale*pow(abs(pos),shape));} + +// 3-tap Gaussian filter along horz line. +float3 Horz3(float2 pos, float off, float2 texture_size){ + float3 b=Fetch(pos,float2(-1.0,off),texture_size); + float3 c=Fetch(pos,float2( 0.0,off),texture_size); + float3 d=Fetch(pos,float2( 1.0,off),texture_size); + float dst=Dist(pos, texture_size).x; + // Convert distance to weight. + float scale=hardPix; + float wb=Gaus(dst-1.0,scale); + float wc=Gaus(dst+0.0,scale); + float wd=Gaus(dst+1.0,scale); + // Return filtered sample. + return (b*wb+c*wc+d*wd)/(wb+wc+wd);} + +// 5-tap Gaussian filter along horz line. +float3 Horz5(float2 pos, float off, float2 texture_size){ + float3 a=Fetch(pos,float2(-2.0,off),texture_size); + float3 b=Fetch(pos,float2(-1.0,off),texture_size); + float3 c=Fetch(pos,float2( 0.0,off),texture_size); + float3 d=Fetch(pos,float2( 1.0,off),texture_size); + float3 e=Fetch(pos,float2( 2.0,off),texture_size); + float dst=Dist(pos, texture_size).x; + // Convert distance to weight. + float scale=hardPix; + float wa=Gaus(dst-2.0,scale); + float wb=Gaus(dst-1.0,scale); + float wc=Gaus(dst+0.0,scale); + float wd=Gaus(dst+1.0,scale); + float we=Gaus(dst+2.0,scale); + // Return filtered sample. + return (a*wa+b*wb+c*wc+d*wd+e*we)/(wa+wb+wc+wd+we);} + +// 7-tap Gaussian filter along horz line. +float3 Horz7(float2 pos, float off, float2 texture_size){ + float3 a=Fetch(pos,float2(-3.0,off),texture_size); + float3 b=Fetch(pos,float2(-2.0,off),texture_size); + float3 c=Fetch(pos,float2(-1.0,off),texture_size); + float3 d=Fetch(pos,float2( 0.0,off),texture_size); + float3 e=Fetch(pos,float2( 1.0,off),texture_size); + float3 f=Fetch(pos,float2( 2.0,off),texture_size); + float3 g=Fetch(pos,float2( 3.0,off),texture_size); + float dst=Dist(pos, texture_size).x; + // Convert distance to weight. + float scale=hardBloomPix; + float wa=Gaus(dst-3.0,scale); + float wb=Gaus(dst-2.0,scale); + float wc=Gaus(dst-1.0,scale); + float wd=Gaus(dst+0.0,scale); + float we=Gaus(dst+1.0,scale); + float wf=Gaus(dst+2.0,scale); + float wg=Gaus(dst+3.0,scale); + // Return filtered sample. + return (a*wa+b*wb+c*wc+d*wd+e*we+f*wf+g*wg)/(wa+wb+wc+wd+we+wf+wg);} + +// Return scanline weight. +float Scan(float2 pos,float off, float2 texture_size){ + float dst=Dist(pos, texture_size).y; + return Gaus(dst+off,hardScan);} + + // Return scanline weight for bloom. +float BloomScan(float2 pos,float off, float2 texture_size){ + float dst=Dist(pos, texture_size).y; + return Gaus(dst+off,hardBloomScan);} + +// Allow nearest three lines to effect pixel. +float3 Tri(float2 pos, float2 texture_size){ + float3 a=Horz3(pos,-1.0, texture_size); + float3 b=Horz5(pos, 0.0, texture_size); + float3 c=Horz3(pos, 1.0, texture_size); + float wa=Scan(pos,-1.0, texture_size); + float wb=Scan(pos, 0.0, texture_size); + float wc=Scan(pos, 1.0, texture_size); + return a*wa+b*wb+c*wc;} + +// Small bloom. +float3 Bloom(float2 pos, float2 texture_size){ + float3 a=Horz5(pos,-2.0, texture_size); + float3 b=Horz7(pos,-1.0, texture_size); + float3 c=Horz7(pos, 0.0, texture_size); + float3 d=Horz7(pos, 1.0, texture_size); + float3 e=Horz5(pos, 2.0, texture_size); + float wa=BloomScan(pos,-2.0, texture_size); + float wb=BloomScan(pos,-1.0, texture_size); + float wc=BloomScan(pos, 0.0, texture_size); + float wd=BloomScan(pos, 1.0, texture_size); + float we=BloomScan(pos, 2.0, texture_size); + return a*wa+b*wb+c*wc+d*wd+e*we;} + +// Distortion of scanlines, and end of screen alpha. +float2 Warp(float2 pos){ + pos=pos*2.0-1.0; + pos*=float2(1.0+(pos.y*pos.y)*warp.x,1.0+(pos.x*pos.x)*warp.y); + return pos*0.5+0.5;} + +// Shadow mask +float3 Mask(float2 pos){ + float3 mask=float3(maskDark,maskDark,maskDark); + + // Very compressed TV style shadow mask. + if (shadowMask == 1) { + float mask_line = maskLight; + float odd=0.0; + if(frac(pos.x/6.0)<0.5) odd = 1.0; + if(frac((pos.y+odd)/2.0)<0.5) mask_line = maskDark; + pos.x=frac(pos.x/3.0); + + if(pos.x<0.333)mask.r=maskLight; + else if(pos.x<0.666)mask.g=maskLight; + else mask.b=maskLight; + mask *= mask_line; + } + + // Aperture-grille. + else if (shadowMask == 2) { + pos.x=frac(pos.x/3.0); + + if(pos.x<0.333)mask.r=maskLight; + else if(pos.x<0.666)mask.g=maskLight; + else mask.b=maskLight; + } + + // Stretched VGA style shadow mask (same as prior shaders). + else if (shadowMask == 3) { + pos.x+=pos.y*3.0; + pos.x=frac(pos.x/6.0); + + if(pos.x<0.333)mask.r=maskLight; + else if(pos.x<0.666)mask.g=maskLight; + else mask.b=maskLight; + } + + // VGA style shadow mask. + else if (shadowMask == 4) { + pos.xy=floor(pos.xy*float2(1.0,0.5)); + pos.x+=pos.y*3.0; + pos.x=frac(pos.x/6.0); + + if(pos.x<0.333)mask.r=maskLight; + else if(pos.x<0.666)mask.g=maskLight; + else mask.b=maskLight; + } + + return mask; +} + +float4 crt_lottes(float2 texture_size, float2 video_size, float2 output_size, float2 tex, sampler2D s0) +{ + float2 pos=Warp(tex.xy*(texture_size.xy/video_size.xy))*(video_size.xy/texture_size.xy); + float3 outColor = Tri(pos, texture_size); + +#ifdef DO_BLOOM + //Add Bloom + outColor.rgb+=Bloom(pos, texture_size)*bloomAmount; +#endif + + if(shadowMask) + outColor.rgb*=Mask(floor(tex.xy*(texture_size.xy/video_size.xy)*output_size.xy)+float2(0.5,0.5)); + + return float4(ToSrgb(outColor.rgb),1.0); +} + +float4 main_fragment(VertexShaderOutput VOUT) : COLOR0 +{ + return crt_lottes(textureSize, videoSize, outputSize, VOUT.texCoord, DecalSampler); +} + +technique +{ + pass + { + PixelShader = compile PS_SHADERMODEL main_fragment(); + } +} diff --git a/Samples/Demo/Demo.csproj b/Samples/Demo/Demo.csproj index f202cdf8e..deb368242 100644 --- a/Samples/Demo/Demo.csproj +++ b/Samples/Demo/Demo.csproj @@ -17,11 +17,12 @@ - sfml + fna $(DefineConstants);MONOGAME $(DefineConstants);SFML $(DefineConstants);FNA;MONOGAME + @@ -37,6 +38,12 @@ ..\..\SadConsole.Host.FNA\FNA\FNA.dll + + + + + + @@ -45,6 +52,13 @@ + + + + + + + diff --git a/Samples/Editor/CallbackRenderStep.cs b/Samples/Editor/CallbackRenderStep.cs new file mode 100644 index 000000000..b72702ba1 --- /dev/null +++ b/Samples/Editor/CallbackRenderStep.cs @@ -0,0 +1,36 @@ +namespace SadConsole.Renderers; + +public class CallbackRenderStep : IRenderStep +{ + private Action _callback; + + /// + public string Name => "callback"; + + /// + public uint SortOrder { get; set; } = 80; // before tint step + + /// + public void SetData(object data) { } + + /// + public void Reset() { } + + public CallbackRenderStep(Action callback) => + _callback = callback; + + /// + public bool Refresh(IRenderer renderer, IScreenSurface screenObject, bool backingTextureChanged, bool isForced) => + true; // Return true to make sure Composing will be called + + /// + public void Composing(IRenderer renderer, IScreenSurface screenObject) => + _callback((SadConsole.Renderers.ScreenSurfaceRenderer)renderer!, screenObject); + + /// + public void Render(IRenderer renderer, IScreenSurface screenObject) { } + + /// + public void Dispose() => + Reset(); +} diff --git a/Samples/Editor/Editor.csproj b/Samples/Editor/Editor.csproj index 04a391b01..73b40c103 100644 --- a/Samples/Editor/Editor.csproj +++ b/Samples/Editor/Editor.csproj @@ -2,27 +2,35 @@ WinExe - net6.0 - app.manifest + net8.0 + enable + enable SadConsole.Editor true + app.manifest + + + + + + true - + - + - + @@ -35,6 +43,25 @@ + + + + + + + + Always + Always diff --git a/Samples/Editor/FileHandlers/IFileHandler.cs b/Samples/Editor/FileHandlers/IFileHandler.cs new file mode 100644 index 000000000..88f1bb6f2 --- /dev/null +++ b/Samples/Editor/FileHandlers/IFileHandler.cs @@ -0,0 +1,13 @@ +namespace SadConsole.Editor.FileHandlers; + +public interface IFileHandler +{ + bool SupportsLoad { get; } + bool SupportsSave { get; } + string FriendlyName { get; } + string[] ExtensionsLoading { get; } + string[] ExtensionsSaving { get; } + object Load(string file); + bool Save(object instance, string file); + string HelpInformation { get; } +} diff --git a/Samples/Editor/FileHandlers/SurfaceFile.cs b/Samples/Editor/FileHandlers/SurfaceFile.cs new file mode 100644 index 000000000..3d4030e95 --- /dev/null +++ b/Samples/Editor/FileHandlers/SurfaceFile.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SadConsole.Editor.FileHandlers; +internal class SurfaceFile : IFileHandler +{ + public bool SupportsLoad => true; + + public bool SupportsSave => true; + + public string FriendlyName => "Surface"; + + public string[] ExtensionsLoading => ["surface", "surfacez"]; + + public string[] ExtensionsSaving => ["surface"]; + + public string HelpInformation => "Saves just the surface, without any other metadata, such as the document title."; + + public object Load(string file) => + Serializer.Load(file, file.EndsWith('z')); + + public bool Save(object instance, string file) + { + instance = instance as Model.Document ?? instance; + + if (!file.EndsWith(ExtensionsSaving[0], StringComparison.InvariantCulture)) + file += "." + ExtensionsSaving[0]; + + if (instance is ScreenSurface surface) + { + try + { + Serializer.Save(surface, file, false); + + return true; + } + catch (Exception e) + { + ImGuiCore.Alert($"Unable to save file.\r\n\r\n{e.Message}"); + return false; + } + } + + ImGuiCore.Alert($"Unable to save file.\r\n\r\nWrong type sent to handler:\r\n {instance.GetType().Name}"); + return false; + } +} diff --git a/Samples/Editor/FileHandlers/SurfaceFileCompressed.cs b/Samples/Editor/FileHandlers/SurfaceFileCompressed.cs new file mode 100644 index 000000000..b1fed6e25 --- /dev/null +++ b/Samples/Editor/FileHandlers/SurfaceFileCompressed.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SadConsole.Editor.FileHandlers; +internal class SurfaceFileCompressed : IFileHandler +{ + public bool SupportsLoad => true; + + public bool SupportsSave => true; + + public string FriendlyName => "Surface (Compressed)"; + + public string[] ExtensionsLoading => ["surfacez"]; + + public string[] ExtensionsSaving => ["surfacez"]; + + public string HelpInformation => "Saves just the surface, without any other metadata, such as the document title. The file is compressed with gzip."; + + public object Load(string file) => + Serializer.Load(file, true); + + public bool Save(object instance, string file) + { + if (instance is ScreenSurface surface) + { + try + { + Serializer.Save(surface, file, true); + + return true; + } + catch (Exception e) + { + ImGuiCore.Alert($"Unable to save file.\r\n\r\n{e.Message}"); + return false; + } + } + + ImGuiCore.Alert($"Unable to save file.\r\n\r\nWrong type sent to handler:\r\n {instance.GetType().Name}"); + return false; + } +} diff --git a/Samples/Editor/GuiParts/DocumentTypeListBox.cs b/Samples/Editor/GuiParts/DocumentTypeListBox.cs new file mode 100644 index 000000000..7f2affd9c --- /dev/null +++ b/Samples/Editor/GuiParts/DocumentTypeListBox.cs @@ -0,0 +1,47 @@ +using System.Diagnostics.CodeAnalysis; +using System.Numerics; +using SadConsole.Editor.Model; + +namespace ImGuiNET; + +public class DocumentTypeListControl +{ + /// + /// Draws the list box. + /// + public static bool DrawListBox(string label, int heightInItems, ref int selectedIndex, [NotNullWhen(true)] ref Document? selectedDocument) + { + // If item chosen, or an item is selected but there's no actual document instance yet + if (ImGui.ListBox(label, ref selectedIndex, DocumentTypeNames.Names, DocumentTypeNames.Names.Length, heightInItems) || selectedIndex != -1 && selectedDocument == null) + { + selectedDocument = selectedIndex switch + { + 0 => new SurfaceDocument(), + 2 => new AnimationDocument(), + _ => new SurfaceDocument(), + }; + + return true; + } + + return false; + } + + public static bool DrawComboBox(string label, int heightInItems, ref int selectedIndex, [NotNullWhen(true)] ref Document? selectedDocument) + { + // If item chosen, or an item is selected but there's no actual document instance yet + if (ImGui.Combo(label, ref selectedIndex, DocumentTypeNames.Names, DocumentTypeNames.Names.Length, heightInItems) || selectedIndex != -1 && selectedDocument == null) + { + selectedDocument = selectedIndex switch + { + 0 => new SurfaceDocument(), + 2 => new AnimationDocument(), + _ => new SurfaceDocument(), + }; + + return true; + } + + return false; + } +} diff --git a/Samples/Editor/GuiParts/FileListBox.cs b/Samples/Editor/GuiParts/FileListBox.cs new file mode 100644 index 000000000..6bdde8fe2 --- /dev/null +++ b/Samples/Editor/GuiParts/FileListBox.cs @@ -0,0 +1,302 @@ +using System.Numerics; +using System.Diagnostics.CodeAnalysis; + +namespace ImGuiNET; + +public class FileListBox +{ + public event EventHandler? ItemHighlighted; + public event EventHandler? ItemSelected; + public event EventHandler? ChangeDirectory; + + // Settings + public bool AllowNavigation { get; set; } = true; + public bool ShowDirectories { get; set; } = true; + public bool ShowFiles { get; set; } = true; + public bool DoubleClickToSelect { get; set; } = true; + public Color DirectoryColor { get; set; } = Color.Yellow; + public Color FileColor { get; set; } = Color.White; + public string SearchPattern + { + get => _searchPattern; + set + { + if (_searchPattern == value) return; + + _searchPattern = string.IsNullOrEmpty(value) ? "*.*" : value; + NavigateTo(CurrentDirectory.FullName); + } + } + public Dictionary ColoredExtensions { get; set; } = new Dictionary(); + public bool UseEvents { get; set; } = true; + + // Selected item + public FileSystemInfo? SelectedItem { get; set; } + public bool IsSelectedItemDirectory => SelectedItem != null + ? SelectedItem is DirectoryInfo + : false; + + // Highlighted item + public FileSystemInfo? HighlightedItem { get; set; } + public bool IsHighlightedItemDirectory => HighlightedItem != null + ? HighlightedItem is DirectoryInfo + : false; + private ItemType? _activeItem; + + // Public state + public DirectoryInfo CurrentDirectory => _currentFolder; + + // Private state + private ItemType[] _currentFolderItems = []; + private bool _isRoot; + + private DirectoryInfo _rootFolder; + private DirectoryInfo _currentFolder; + private ItemType _parentDirectoryItem; + private Task _directoryLoader; + private string _searchPattern = "*.*"; + + public FileListBox(string rootFolder) + { + _rootFolder = new DirectoryInfo(rootFolder); + + if (!_rootFolder.Exists) throw new DirectoryNotFoundException($"Folder not found: {rootFolder}"); + + NavigateTo(_rootFolder.FullName); + } + + public FileListBox(string rootFolder, string currentFolder) + { + _rootFolder = new DirectoryInfo(rootFolder); + DirectoryInfo currentFolderToCheck = new(currentFolder); + + if (!_rootFolder.Exists) throw new DirectoryNotFoundException($"Folder not found: {rootFolder}"); + if (!currentFolderToCheck.Exists) throw new DirectoryNotFoundException($"Folder not found: {currentFolder}"); + if (!currentFolderToCheck.FullName.StartsWith(_rootFolder.FullName)) throw new Exception("Current folder must be a sub folder of the root folder"); + + NavigateTo(currentFolderToCheck.FullName); + } + + /// + /// Draws the list box. + /// + public void Draw() => + Draw(out _, out _); + + public void Begin(string id) => + ImGui.BeginChild(id); + + public void Begin(string id, Vector2 size, bool showBorder = false, ImGuiWindowFlags flags = ImGuiWindowFlags.None) => + ImGui.BeginChild(id, size, showBorder, flags); + + public void End() => + ImGui.EndChild(); + + /// + /// Draws the list box. + /// + /// True when an item is single-clicked and is true. + /// True when an item is selected or highlighted. + public bool Draw(out bool itemSelected, out bool itemHighlighted) + { + itemSelected = false; + itemHighlighted = false; + + // Loading directory + if (!_directoryLoader.IsCompleted) + { + ImGui.Text("Loading..."); + } + else + { + // Draw the ..\ folder + if (_currentFolder.Parent != null && !_currentFolder.FullName.Equals(_rootFolder.FullName)) + { + ImGui.PushStyleColor(ImGuiCol.Text, _parentDirectoryItem.Color.PackedValue); + if (ImGui.Selectable(_parentDirectoryItem.Name, false, ImGuiSelectableFlags.DontClosePopups | ImGuiSelectableFlags.AllowDoubleClick)) + { + if (DoubleClickToSelect && ImGui.IsMouseDoubleClicked(ImGuiMouseButton.Left)) + NavigateTo(_currentFolder.Parent.FullName); + + else if (!DoubleClickToSelect) + NavigateTo(_currentFolder.Parent.FullName); + + } + ImGui.PopStyleColor(); + } + + // If we're at the root, but there are no dirs nor files, alert the user + if (_currentFolder.FullName.Equals(_rootFolder.FullName) && _currentFolderItems.Length == 0) + { + ImGui.Text("No files match the specified criteria."); + } + + // Draw items + foreach (ItemType item in _currentFolderItems) + { + ImGui.PushStyleColor(ImGuiCol.Text, item.Color.PackedValue); + + ImGuiSelectableFlags flags = ImGuiSelectableFlags.DontClosePopups; + if (DoubleClickToSelect) flags |= ImGuiSelectableFlags.AllowDoubleClick; + + // Draw item. If selected... + if (ImGui.Selectable(item.Name, item == _activeItem, flags)) + { + // When doubleclick is enabled, you can highlight (single click). + if (DoubleClickToSelect) + { + // Item double clicked, navigate to directory or select it item/dir + if (ImGui.IsMouseDoubleClicked(ImGuiMouseButton.Left)) + { + if (item.IsDirectory && AllowNavigation) + { + NavigateTo(item.FullName); + } + else + { + itemSelected = true; + OnSelected(item); + } + } + + // Single clicked, item is highlighted + else + { + itemHighlighted = true; + OnHighlighted(item); + } + } + + // Single click to select means no highlighting. You click, it selects. + else + { + if (item.IsDirectory && AllowNavigation) + { + NavigateTo(item.FullName); + } + else + { + itemSelected = true; + OnSelected(item); + } + } + } + ImGui.PopStyleColor(); + } + } + + return itemSelected || itemHighlighted; + } + + [MemberNotNull(nameof(_directoryLoader), nameof(_currentFolder), nameof(_parentDirectoryItem))] + private void NavigateTo(string path) + { + // Clear highlighted + HighlightedItem = null; + SelectedItem = null; + _activeItem = null; + + // Load with the latest settings + _parentDirectoryItem = ItemType.AsDirectory($"..{Path.DirectorySeparatorChar}", "", DirectoryColor); + + // Move directories + _currentFolder = new(path); + + // Check if it exists + if (!_currentFolder.Exists) + throw new DirectoryNotFoundException($"Folder not found: {_currentFolder}"); + + _directoryLoader = Task.Run(LoadDirectory).ContinueWith((t) => OnDirectoryChanged(), TaskContinuationOptions.ExecuteSynchronously); + } + + private void LoadDirectory() + { + List items = new(); + _activeItem = null; + HighlightedItem = null; + SelectedItem = null; + + if (ShowDirectories) + { + foreach (DirectoryInfo item in _currentFolder.EnumerateDirectories()) + items.Add(ItemType.AsDirectory(item.Name, item.FullName, DirectoryColor)); + } + + if (ShowFiles) + { + string[] fileSearches = SearchPattern.Split(';'); + + if (fileSearches.Length == 1 && fileSearches[0] == "*.*") + fileSearches[0] = "*"; + + List fileItemsOnly = new(); + + foreach (string pattern in fileSearches) + { + foreach (FileInfo item in _currentFolder.EnumerateFiles(pattern, SearchOption.TopDirectoryOnly)) + { + Color itemColor = FileColor; + string extension = item.Extension.ToLower(); + + if (ColoredExtensions.TryGetValue(extension, out Color value)) + itemColor = value; + + fileItemsOnly.Add(ItemType.AsFile(item.Name, item.FullName, itemColor)); + } + } + + items.AddRange(fileItemsOnly.OrderBy(item => item.Name)); + } + + _currentFolderItems = items.Count != 0 ? items.ToArray() : Array.Empty(); + } + + private void OnDirectoryChanged() + { + if (UseEvents) + ChangeDirectory?.Invoke(this, EventArgs.Empty); + } + + private void OnHighlighted(ItemType item) + { + _activeItem = item; + + if (item.IsDirectory) + HighlightedItem = new DirectoryInfo(item.FullName); + else + HighlightedItem = new FileInfo(item.FullName); + + if (UseEvents) + ItemHighlighted?.Invoke(this, EventArgs.Empty); + } + + private void OnSelected(ItemType item) + { + _activeItem = item; + + if (item.IsDirectory) + SelectedItem = new DirectoryInfo(item.FullName); + else + SelectedItem = new FileInfo(item.FullName); + + if (UseEvents) + ItemSelected?.Invoke(this, EventArgs.Empty); + } + + private class ItemType + { + public bool IsDirectory { get; } + public string Name { get; } + public string FullName { get; } + public Color Color { get; } + + private ItemType(string name, string fullName, bool isDirectory, Color color) => + (Name, FullName, IsDirectory, Color) = (name, fullName, isDirectory, color); + + public static ItemType AsFile(string name, string fullName, Color color) => + new(name, fullName, false, color); + + public static ItemType AsDirectory(string name, string fullName, Color color) => + new(name, fullName, true, color); + } +} diff --git a/Samples/Editor/GuiParts/FontGlyph.cs b/Samples/Editor/GuiParts/FontGlyph.cs new file mode 100644 index 000000000..a8d12a7c0 --- /dev/null +++ b/Samples/Editor/GuiParts/FontGlyph.cs @@ -0,0 +1,60 @@ +using System.Numerics; +using SadConsole.Editor; +using SadConsole.ImGuiSystem; +using SadConsole.Host; +using SadConsole.Editor.Windows; + +namespace ImGuiNET; + +public static class FontGlyph +{ + public static void DrawWithPopup(ImGuiRenderer renderer, string id, string popupId, IFont font, Vector4 foreground, Vector4 background, ref int selectedGlyph, bool showNumber) + { + nint fontTexture = renderer.BindTexture(((GameTexture)font.Image).Texture); + Rectangle rect = font.GetGlyphSourceRectangle(selectedGlyph); + Point fontTextureSize = new Point(font.Image.Width, font.Image.Height); + + Vector2 renderAreaSize = font.GetFontSize(IFont.Sizes.Two).ToVector2(); + + if (showNumber) + { + if (ImGui.InputInt(id, ref selectedGlyph, 1)) + selectedGlyph = Math.Clamp(selectedGlyph, 0, font.TotalGlyphs - 1); + + ImGui.SameLine(); + } + + // TODO: Apply mirror to UV + ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, Vector2.One); + if (ImGui.ImageButton($"{id}##tool_tip_settings_button", fontTexture, + renderAreaSize, + rect.Position.ToUV(fontTextureSize), (rect.Position + rect.Size).ToUV(fontTextureSize), + background, foreground)) + { + ImGuiCore.State.OpenPopup("glyph_select"); + } + + ImGui.PopStyleVar(); + GlyphPickerPopup.Show(renderer, "glyph_select", font, fontTexture, fontTextureSize, ref selectedGlyph); + ImGuiCore.State.CheckSetPopupOpen("glyph_select"); + } + + public static void Draw(ImGuiRenderer renderer, string id, IFont font, Vector4 foreground, Vector4 background, int glyph) + { + nint fontTexture = renderer.BindTexture(((GameTexture)font.Image).Texture); + Rectangle rect = font.GetGlyphSourceRectangle(glyph); + Point fontTextureSize = new(font.Image.Width, font.Image.Height); + + Vector2 renderAreaSize = font.GetFontSize(IFont.Sizes.Two).ToVector2(); + + // TODO: Apply mirror to UV + ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, Vector2.One); + ImGui.BeginDisabled(); + ImGui.ImageButton($"{id}##tool_tip_settings_button", fontTexture, + renderAreaSize, + rect.Position.ToUV(fontTextureSize), (rect.Position + rect.Size).ToUV(fontTextureSize), + background, foreground); + ImGui.EndDisabled(); + ImGui.PopStyleVar(); + } +} diff --git a/Samples/Editor/GuiParts/GuiDockspace.cs b/Samples/Editor/GuiParts/GuiDockspace.cs index 079683dc7..769a8e3b9 100644 --- a/Samples/Editor/GuiParts/GuiDockspace.cs +++ b/Samples/Editor/GuiParts/GuiDockspace.cs @@ -53,15 +53,12 @@ public unsafe override void BuildUI(ImGuiRenderer renderer) ImGuiInternal.DockBuilderSetNodeSize(id, size); ImGuiInternal.DockBuilderSetNodePos(id, nodePos); - uint documentsSide; - uint toolsSide; uint singleDocSide; - ImGuiInternal.DockBuilderSplitNode(id, ImGuiDir.Up, 0.5f, out documentsSide, out toolsSide); + ImGuiInternal.DockBuilderSplitNode(id, ImGuiDir.Up, 0.5f, out uint documentsSide, out uint toolsSide); singleDocSide = ImGuiInternal.DockBuilderSplitNode(id, ImGuiDir.Right, 0.7f, out _, out _); //var dock3 = ImGuiInternal.DockBuilderSplitNode(id, ImGuiDir.Right, 0.5f, out _, out _); - ImGuiInternal.DockBuilderDockWindow("Active Documents", documentsSide); ImGuiInternal.DockBuilderDockWindow("Tools", toolsSide); ImGuiInternal.DockBuilderDockWindow("Open Document", singleDocSide); diff --git a/Samples/Editor/GuiParts/GuiStartup.cs b/Samples/Editor/GuiParts/GuiStartup.cs new file mode 100644 index 000000000..b09dc51be --- /dev/null +++ b/Samples/Editor/GuiParts/GuiStartup.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Text; +using ImGuiNET; +using SadConsole.Editor.Windows; +using SadConsole.ImGuiSystem; + +namespace SadConsole.Editor.GuiParts +{ + public class GuiStartup : ImGuiObjectBase + { + public override void BuildUI(ImGuiRenderer renderer) + { + ImGuiCore.State.IsPopupOpen = false; + } + } +} diff --git a/Samples/Editor/GuiParts/GuiTopBar.cs b/Samples/Editor/GuiParts/GuiTopBar.cs index 681b01717..0b418b40b 100644 --- a/Samples/Editor/GuiParts/GuiTopBar.cs +++ b/Samples/Editor/GuiParts/GuiTopBar.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; +using System.Numerics; using System.Text; using ImGuiNET; +using SadConsole.Editor.Windows; using SadConsole.ImGuiSystem; namespace SadConsole.Editor.GuiParts @@ -23,6 +25,8 @@ public class GuiTopBar : ImGuiObjectBase public bool ShowMetrics; private bool _debug; + public List<(Vector4 Color, string Text)> StatusItems = new(); + public override void BuildUI(ImGuiRenderer renderer) { if (_debug) @@ -39,9 +43,31 @@ public override void BuildUI(ImGuiRenderer renderer) { ImGuiCore.ShowCreateDocument(); } - if (ImGui.MenuItem("Close", "c")) + if (ImGui.MenuItem("Open", "o")) + { + OpenFile window = new(); + window.Closed += (s, e) => + { + if (window.DialogResult) + { + ImGuiCore.State.OpenDocuments = [.. ImGuiCore.State.OpenDocuments, window.Document!]; + ImGuiCore.State.SelectedDocumentIndex = ImGuiCore.State.OpenDocuments.Length - 1; + } + }; + window.Show(); + } + if (ImGuiCore.State.SelectedDocumentIndex != -1) { - + ImGui.Separator(); + if (ImGui.MenuItem("Save", "s")) + { + SaveFile window = new(); + window.Show(ImGuiCore.State.GetOpenDocument()); + } + if (ImGui.MenuItem("Close", "c")) + { + + } } ImGui.EndMenu(); @@ -67,6 +93,15 @@ public override void BuildUI(ImGuiRenderer renderer) ImGui.EndMenu(); } + // Write status items at the top + foreach ((Vector4 Color, string Text) item in StatusItems) + { + if (item.Color == Vector4.Zero) + ImGui.Text(item.Text); + else + ImGui.TextColored(item.Color, item.Text); + } + StatusItems.Clear(); ImGui.EndMainMenuBar(); } } diff --git a/Samples/Editor/GuiParts/Tools/SettingsTable.DrawingTip.cs b/Samples/Editor/GuiParts/Tools/SettingsTable.DrawingTip.cs new file mode 100644 index 000000000..87edb1051 --- /dev/null +++ b/Samples/Editor/GuiParts/Tools/SettingsTable.DrawingTip.cs @@ -0,0 +1,132 @@ +using ImGuiNET; +using System.Numerics; +using SadConsole.ImGuiSystem; +using SadConsole.Editor.Model; +using SadConsole.Editor.Tools; +using static Microsoft.Xna.Framework.Graphics.SpriteFont; +using static System.Net.Mime.MediaTypeNames; +using SadConsole.Editor.Model.SadConsoleTypes; +using SadConsole.Editor.Windows; + +namespace SadConsole.Editor.GuiParts.Tools; + +internal static partial class SettingsTable +{ + public static void DrawCheckbox(string label, string id, ref bool isChecked) + { + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.AlignTextToFramePadding(); + ImGui.Text(label); + ImGui.TableSetColumnIndex(1); + + ImGui.Checkbox(id, ref isChecked); + } + + public static void DrawInt(string label, string id, ref int intValue, int minValue = 0, int maxValue = -1) + { + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.AlignTextToFramePadding(); + ImGui.Text(label); + ImGui.TableSetColumnIndex(1); + + ImGui.InputInt(id, ref intValue); + + if (intValue < minValue) + intValue = minValue; + + if (maxValue > -1 && intValue > maxValue) + intValue = maxValue; + } + + public static void DrawColor(string label, string id, ref Vector4 color, Vector4? resetColor, out bool colorRightClicked) + { + Vector4 colorCopy = color; + + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.AlignTextToFramePadding(); + ImGui.Text(label); + + ImGui.TableSetColumnIndex(1); + ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X); + if (ImGui.ColorEdit4(id, ref colorCopy, ImGuiColorEditFlags.AlphaPreviewHalf | ImGuiColorEditFlags.NoInputs)) + color = colorCopy; + + colorRightClicked = ImGui.IsItemClicked(ImGuiMouseButton.Right); + + ImGui.SameLine(); + if (ImGui.Button($"Palette{id}")) + ImGuiCore.State.OpenPopup($"palettepopup##{id}"); + + Color col = color.ToColor(); + if (PalettePopup.Show($"palettepopup##{id}", ref col)) + color = col.ToVector4(); + + if (resetColor.HasValue) + { + ImGui.SameLine(); + if (ImGui.Button($"Reset{id}")) + color = resetColor.Value; + } + } + + public static void DrawFontGlyph(string label, string id, ref int glyph, Vector4 glyphForeground, Vector4 glyphBackground, IFont font, ImGuiRenderer renderer) + { + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.AlignTextToFramePadding(); + ImGui.Text(label); + ImGui.TableSetColumnIndex(1); + + FontGlyph.DrawWithPopup(renderer, id, "glyph_select", font, glyphForeground, glyphBackground, ref glyph, true); + } + + public static void DrawMirror(string label, string id, ref Mirror mirror) + { + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.AlignTextToFramePadding(); + ImGui.Text(label); + ImGui.TableSetColumnIndex(1); + + int itemIndex = Model.SadConsoleTypes.Mirror.GetIndexFromValue(mirror); + + if (ImGui.Combo(id, ref itemIndex, Model.SadConsoleTypes.Mirror.Names, Model.SadConsoleTypes.Mirror.Names.Length)) + { + mirror = Model.SadConsoleTypes.Mirror.GetValueFromIndex(itemIndex); + } + } + + public static void DrawCommonSettings(string id, bool showForeground, bool showBackground, bool showMirror, bool showGlyph, bool enableSwapForeBackRightClick, + ref Vector4 foreground, Vector4? foregroundResetColor, + ref Vector4 background, Vector4? backgroundResetColor, + ref Mirror mirror, + ref int glyph, IFont font, ImGuiRenderer renderer) + { + BeginTable(id); + + if (showForeground) + { + DrawColor("Foreground:", "##fore", ref foreground, foregroundResetColor, out bool colorRightClicked); + if (colorRightClicked && enableSwapForeBackRightClick) + (background, foreground) = (foreground, background); + } + + if (showBackground) + { + DrawColor("Background:", "##back", ref background, backgroundResetColor, out bool colorRightClicked); + if (colorRightClicked && enableSwapForeBackRightClick) + (background, foreground) = (foreground, background); + } + + if (showMirror) + DrawMirror("Mirror:", "##mirror", ref mirror); + + if (showGlyph) + DrawFontGlyph("Glyph:", "##glyph", ref glyph, foreground, background, font, renderer); + + EndTable(); + } +} diff --git a/Samples/Editor/GuiParts/Tools/SettingsTable.cs b/Samples/Editor/GuiParts/Tools/SettingsTable.cs new file mode 100644 index 000000000..46ddd092d --- /dev/null +++ b/Samples/Editor/GuiParts/Tools/SettingsTable.cs @@ -0,0 +1,20 @@ +using ImGuiNET; + +namespace SadConsole.Editor.GuiParts.Tools; + +internal static partial class SettingsTable +{ + public static void BeginTable(string id) + { + if (ImGui.BeginTable(id, 2)) + { + ImGui.TableSetupColumn("one", ImGuiTableColumnFlags.WidthFixed); + ImGui.TableSetupColumn("two"); + } + } + + public static void EndTable() + { + ImGui.EndTable(); + } +} diff --git a/Samples/Editor/GuiParts/Tools/ShapeSettings.cs b/Samples/Editor/GuiParts/Tools/ShapeSettings.cs new file mode 100644 index 000000000..e8ebd51f8 --- /dev/null +++ b/Samples/Editor/GuiParts/Tools/ShapeSettings.cs @@ -0,0 +1,35 @@ +using System.Numerics; +using ImGuiNET; +using SadConsole.ImGuiSystem; + +namespace SadConsole.Editor.GuiParts.Tools; + +internal static class ShapeSettings +{ + public struct Settings + { + // Stolen from ShapreParameters and converted to fields + public bool HasBorder; + public bool HasFill; + public bool IgnoreFillForeground; + public bool IgnoreFillBackground; + public bool IgnoreFillGlyph; + public bool IgnoreFillMirror; + public bool IgnoreBorderForeground; + public bool IgnoreBorderBackground; + public bool IgnoreBorderGlyph; + public bool IgnoreBorderMirror; + public ColoredGlyphBase? FillGlyph; + public int[]? BoxBorderStyle; + public ColoredGlyphBase[]? BoxBorderStyleGlyphs; + public ColoredGlyphBase? BorderGlyph; + + // flags + public bool UseBoxBorderStyle; + + public ShapeParameters ToShapeParameters() => + new ShapeParameters(HasBorder, BorderGlyph, IgnoreBorderForeground, IgnoreBorderBackground, IgnoreBorderGlyph, IgnoreBorderMirror, + HasFill, FillGlyph, IgnoreFillForeground, IgnoreFillBackground, IgnoreFillGlyph, IgnoreFillMirror, + BoxBorderStyle, BoxBorderStyleGlyphs); + } +} diff --git a/Samples/Editor/GuiParts/Tools/ToolHelpers.cs b/Samples/Editor/GuiParts/Tools/ToolHelpers.cs new file mode 100644 index 000000000..2020e32ce --- /dev/null +++ b/Samples/Editor/GuiParts/Tools/ToolHelpers.cs @@ -0,0 +1,41 @@ +using System.Numerics; +using ImGuiNET; + +namespace SadConsole.Editor.GuiParts.Tools; + +internal static class ToolHelpers +{ + public static Vector2 TransformCellCenter(Point cellPosition, Point viewOffset, Point fontSize) + { + Vector2 topleft = ImGui.GetItemRectMin(); + + Point pixelCellPosition = (cellPosition - viewOffset) * fontSize; + + return new Vector2(topleft.X + pixelCellPosition.X + (fontSize.X / 2), topleft.Y + pixelCellPosition.Y + (fontSize.Y / 2)); + } + + public static Vector2 TransformMouseCellCenter(Vector2 imGuiMouse, Point fontSize) + { + Vector2 topleft = ImGui.GetItemRectMin(); + imGuiMouse = imGuiMouse - topleft; + imGuiMouse = new(imGuiMouse.X / fontSize.X, imGuiMouse.Y / fontSize.Y); + + Point pixelCellPosition = new((int)imGuiMouse.X * fontSize.X, (int)imGuiMouse.Y * fontSize.Y); + + return new Vector2(topleft.X + pixelCellPosition.X + (fontSize.X / 2), topleft.Y + pixelCellPosition.Y + (fontSize.Y / 2)); + } + + public static void HighlightCell(Point cellPosition, Point viewOffset, Point fontSize, Color color) + { + Vector2 topleft = ImGui.GetItemRectMin(); + + Point pixelCellPosition = (cellPosition - viewOffset) * fontSize; + + ImDrawListPtr drawList = ImGui.GetWindowDrawList(); + + Vector2 boxTopLeft = new(topleft.X + pixelCellPosition.X, topleft.Y + pixelCellPosition.Y); + Vector2 boxBottomRight = boxTopLeft + new Vector2(fontSize.X, fontSize.Y); + + drawList.AddRect(boxTopLeft, boxBottomRight, color.PackedValue); + } +} diff --git a/Samples/Editor/GuiParts/WindowActiveDocuments.cs b/Samples/Editor/GuiParts/WindowActiveDocuments.cs index c938504b7..da7684c30 100644 --- a/Samples/Editor/GuiParts/WindowActiveDocuments.cs +++ b/Samples/Editor/GuiParts/WindowActiveDocuments.cs @@ -1,18 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using System.Text; -using System.Threading.Tasks; +using System.Numerics; using ImGuiNET; using SadConsole.ImGuiSystem; -using SadRogue.Primitives; namespace SadConsole.Editor.GuiParts; public class WindowActiveDocuments : ImGuiObjectBase { - private int _selectedDocIndex = -1; private Model.Document _selectedDocument = null; public override void BuildUI(ImGuiRenderer renderer) @@ -22,30 +15,19 @@ public override void BuildUI(ImGuiRenderer renderer) ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X); if (ImGuiCore.State.OpenDocuments.Length > 0) { - if (ImGui.ListBox("##documentslist", ref _selectedDocIndex, ImGuiCore.State.OpenDocumentTitles, ImGuiCore.State.OpenDocuments.Length, ImGuiCore.State.OpenDocuments.Length <= 4 ? 4 : 6)) - { - bool sameDocument = false; - - ImGuiCore.State.SelectedDocumentIndex = _selectedDocIndex; - - if (_selectedDocument != null) - { - if (_selectedDocument == ImGuiCore.State.OpenDocuments[_selectedDocIndex]) - sameDocument = true; - else - _selectedDocument.OnHide(renderer); - } + ImGui.ListBox("##documentslist", ref ImGuiCore.State.SelectedDocumentIndex, ImGuiCore.State.OpenDocumentTitles, ImGuiCore.State.OpenDocuments.Length, ImGuiCore.State.OpenDocuments.Length <= 4 ? 4 : 6); - if (!sameDocument) + // Some document is selected + if (ImGuiCore.State.SelectedDocumentIndex != -1) + { + if (_selectedDocument != ImGuiCore.State.OpenDocuments[ImGuiCore.State.SelectedDocumentIndex]) { - _selectedDocument = ImGuiCore.State.OpenDocuments[_selectedDocIndex]; + _selectedDocument?.OnHide(renderer); + _selectedDocument = ImGuiCore.State.OpenDocuments[ImGuiCore.State.SelectedDocumentIndex]; _selectedDocument.OnShow(renderer); } - } - if (_selectedDocIndex != -1) - { - _selectedDocument.BuildUIEdit(renderer, true); + _selectedDocument?.BuildUIEdit(renderer, true); } } else diff --git a/Samples/Editor/GuiParts/WindowTools.cs b/Samples/Editor/GuiParts/WindowTools.cs index 00114b0d8..54df96c34 100644 --- a/Samples/Editor/GuiParts/WindowTools.cs +++ b/Samples/Editor/GuiParts/WindowTools.cs @@ -1,14 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using System.Security.Cryptography.X509Certificates; -using System.Text; -using System.Threading.Tasks; +using System.Numerics; using ImGuiNET; using SadConsole.Editor.Model; using SadConsole.ImGuiSystem; -using SadRogue.Primitives; namespace SadConsole.Editor.GuiParts; @@ -31,8 +24,32 @@ public override void BuildUI(ImGuiRenderer renderer) ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X); if (docTools.State.ToolObjects.Length != 0) { - ImGui.ListBox("##toolsList", ref docTools.State.SelectedToolIndex, docTools.State.ToolNames, docTools.State.ToolObjects.Length, docTools.State.ToolObjects.Length <= 4 ? 4 : 10); - ImGui.Separator(); + int selectedToolIndex = docTools.State.SelectedToolIndex; + + ImGui.TextDisabled("(?)"); + if (ImGui.IsItemHovered()) + { + ImGui.BeginTooltip(); + ImGui.PushTextWrapPos(ImGui.GetFontSize() * 25.0f); + ImGui.TextUnformatted(docTools.State.SelectedTool.Description); + ImGui.PopTextWrapPos(); + ImGui.EndTooltip(); + } + ImGui.SameLine(); + + if (ImGui.Combo("##toolsList", ref selectedToolIndex, docTools.State.ToolNames, docTools.State.ToolObjects.Length, docTools.State.ToolObjects.Length <= 4 ? 4 : 6)) + { + if (docTools.State.SelectedToolIndex != selectedToolIndex) + { + Editor.Tools.ITool oldTool = docTools.State.SelectedTool!; + + docTools.State.SelectedTool.OnDeselected(); + docTools.State.SelectedToolIndex = selectedToolIndex; + docTools.ToolChanged(oldTool, docTools.State.SelectedTool); + docTools.State.SelectedTool.OnSelected(); + } + } + docTools.State.SelectedTool.BuildSettingsPanel(renderer); } else diff --git a/Samples/Editor/ImGuiCore.State.cs b/Samples/Editor/ImGuiCore.State.cs index 1538ce503..dee73f8d9 100644 --- a/Samples/Editor/ImGuiCore.State.cs +++ b/Samples/Editor/ImGuiCore.State.cs @@ -1,11 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using ImGuiNET; -using SadConsole; -using SadConsole.Editor.GuiParts; +using ImGuiNET; using SadConsole.ImGuiSystem; namespace SadConsole.Editor; @@ -14,13 +7,18 @@ public static partial class ImGuiCore { public static class State { + public static bool IsPopupOpen; private static ImGuiWindowClass _noTabBarWindowClassPtr; public static ImGuiWindowClassPtr NoTabBarWindowClass; public static int SelectedDocumentIndex = -1; - public static Model.Document[] OpenDocuments = Array.Empty(); + public static Model.Document[] OpenDocuments = []; public static string[] OpenDocumentTitles => OpenDocuments.Select(d => d.Name).ToArray(); + public static FileHandlers.IFileHandler[] FileHandlerInstances = [new FileHandlers.SurfaceFile()]; + + //public static Dictionary FontTexturesPure; + public static Model.Document GetOpenDocument() => OpenDocuments[SelectedDocumentIndex]; public static class LayoutInfo @@ -33,13 +31,43 @@ static State() { unsafe { - fixed(ImGuiWindowClass* pointer = &_noTabBarWindowClassPtr) + fixed (ImGuiWindowClass* pointer = &_noTabBarWindowClassPtr) NoTabBarWindowClass = new ImGuiWindowClassPtr(pointer); NoTabBarWindowClass.DockNodeFlagsOverrideSet = (ImGuiDockNodeFlags)(1 << 12) // ImGuiDockNodeFlags_NoTabBar | (ImGuiDockNodeFlags)(1 << 16); // ImGuiDockNodeFlags_NoDocking //| (ImGuiDockNodeFlags)(1 << 17); // ImGuiDockNodeFlags_NoDockingSplitMe } + + //FontTexturesRebuilt = new(); } + + public static bool CheckSetPopupOpen(string id) => + IsPopupOpen |= ImGui.IsPopupOpen(id); + + public static void SetPopupOpen() => + IsPopupOpen = true; + + public static void OpenPopup(string id) + { + ImGui.OpenPopup(id); + IsPopupOpen = true; + } + //public Texture2D BuildPureFontTexture(IFont font) + //{ + // if (font is SadFont sfont) + // { + // //RenderTarget2D texture = new(SadConsole.Host.Global.GraphicsDevice, 16 * sfont.GlyphWidth, sfont.TotalGlyphs / 16 * sfont.GlyphHeight); + // Texture2D texture = new(SadConsole.Host.Global.GraphicsDevice, 16 * sfont.GlyphWidth, sfont.TotalGlyphs / 16 * sfont.GlyphHeight); + // Host.GameTexture wrapper = new(texture, false); + // Color[] pixels = wrapper.GetPixels(); + // Color + + // for (int i = 0; i < sfont.TotalGlyphs; i++) + // { + // sfont.edit + // } + // } + //} } } diff --git a/Samples/Editor/ImGuiCore.cs b/Samples/Editor/ImGuiCore.cs index c794458c6..a904310ab 100644 --- a/Samples/Editor/ImGuiCore.cs +++ b/Samples/Editor/ImGuiCore.cs @@ -1,11 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using ImGuiNET; -using SadConsole; -using SadConsole.Editor.GuiParts; +using SadConsole.Editor.GuiParts; +using SadConsole.Editor.Windows; using SadConsole.ImGuiSystem; namespace SadConsole.Editor; @@ -29,11 +23,12 @@ public static bool IsOpened //private static CoolTheme coolTheme = new CoolTheme(); + public static GuiStartup GuiStartup; public static GuiTopBar GuiTopBar; public static GuiDockspace GuiDockspace; public static WindowActiveDocuments GuiSidePane; - public static WindowDocumentsHost GuiDocumentsHost; public static WindowTools GuiToolsWindow; + public static WindowDocumentsHost GuiDocumentsHost; private static DebuggingTools _debuggingTools; @@ -42,34 +37,23 @@ public static bool IsOpened ///
public static void BasicInit() { - _imGui = new ImGuiMonoGameComponent(SadConsole.Host.Global.GraphicsDeviceManager, Game.Instance.MonoGameInstance, true); + _imGui = new ImGuiMonoGameComponent(Host.Global.GraphicsDeviceManager, Game.Instance.MonoGameInstance, true); //_imGui.Font = "Roboto-Regular.ttf"; //_imGui.fontSize = 14f; Game.Instance.MonoGameInstance.Components.Add(_imGui); - SadConsole.Game.Instance.MonoGameInstance.SadConsoleComponent.Enabled = false; + Game.Instance.MonoGameInstance.SadConsoleComponent.Enabled = false; } public static void Start() { - if (_imGui != null) - { - _imGui.Visible = true; - _imGui.Enabled = true; - - SadConsole.Game.Instance.MonoGameInstance.SadConsoleComponent.Enabled = false; - SadConsole.Settings.DoFinalDraw = false; - - return; - } + Settings.DoFinalDraw = false; - SadConsole.Game.Instance.MonoGameInstance.SadConsoleComponent.Enabled = false; - SadConsole.Settings.DoFinalDraw = false; + //Game.Instance.MonoGameInstance.SadConsoleComponent.Enabled = false; + //Game.Instance.MonoGameInstance.ClearScreenComponent.Visible = false; + //Game.Instance.MonoGameInstance.ClearScreenComponent.Enabled = false; - //SadConsole.Game.Instance.MonoGameInstance.ClearScreenComponent.Visible = false; - //SadConsole.Game.Instance.MonoGameInstance.ClearScreenComponent.Enabled = false; - - _imGui = new ImGuiMonoGameComponent(SadConsole.Host.Global.GraphicsDeviceManager, Game.Instance.MonoGameInstance, true); + _imGui = new ImGuiMonoGameComponent(Host.Global.GraphicsDeviceManager, Game.Instance.MonoGameInstance, true); var value = _imGui.ImGuiRenderer.AddFontTTF("JetBrains Mono SemiBold Nerd Font Complete.ttf", 16f); _imGui.ImGuiRenderer.SetDefaultFont(value); @@ -82,11 +66,12 @@ public static void Start() //ImGuiNET.ImGui.GetStyle().ScaleAllSizes(2f); + GuiStartup = new(); GuiTopBar = new(); GuiDockspace = new(); GuiSidePane = new(); - GuiDocumentsHost = new(); GuiToolsWindow = new(); + GuiDocumentsHost = new(); _debuggingTools = new(); ResetUIList(); @@ -100,14 +85,15 @@ public static void Start() //ImGuiNET.ImGui. // Test code - var doc = Model.SurfaceDocument.FromSettings(280, 225, SadRogue.Primitives.Color.White, SadRogue.Primitives.Color.Black); - State.OpenDocuments = State.OpenDocuments.Append(doc).ToArray(); - ((Model.SurfaceDocument)State.OpenDocuments[0]).Surface.Surface.View = new SadRogue.Primitives.Rectangle(0, 0, 10, 10); + var doc = Model.SurfaceDocument.FromSettings(280, 225, Color.White, Color.Black); + State.OpenDocuments = [.. State.OpenDocuments, doc]; + ((Model.SurfaceDocument)State.OpenDocuments[0]).Surface.Surface.View = new Rectangle(0, 0, 10, 10); } public static void ResetUIList() { _imGui.UIComponents.Clear(); + _imGui.UIComponents.Add(GuiStartup); _imGui.UIComponents.Add(GuiTopBar); _imGui.UIComponents.Add(GuiDockspace); _imGui.UIComponents.Add(GuiSidePane); @@ -118,26 +104,29 @@ public static void ResetUIList() public static void ShowCreateDocument() { - PopupNewFileWindow window = new PopupNewFileWindow(); - window.IsOpen = true; - ImGuiCore.GuiComponents.Add(window); + NewFile window = new(); window.Closed += (s, e) => { if (window.DialogResult) - { - State.OpenDocuments = State.OpenDocuments.Append(window.Document).ToArray(); - } + State.OpenDocuments = [.. State.OpenDocuments, window.Document]; }; + window.Show(); + } + + public static void Alert(string message) + { + MessageWindow window = new(message); + window.Show(); } - private static void _imGui_HostClosed(object sender, EventArgs e) => + private static void _imGui_HostClosed(object? sender, EventArgs e) => Stop(); public static void Stop() { - SadConsole.Game.Instance.MonoGameInstance.SadConsoleComponent.Visible = true; - SadConsole.Game.Instance.MonoGameInstance.SadConsoleComponent.Enabled = true; - SadConsole.Settings.DoFinalDraw = true; + Game.Instance.MonoGameInstance.SadConsoleComponent.Visible = true; + Game.Instance.MonoGameInstance.SadConsoleComponent.Enabled = true; + Settings.DoFinalDraw = true; //SadConsole.Game.Instance.MonoGameInstance.ClearScreenComponent.Visible = true; //SadConsole.Game.Instance.MonoGameInstance.ClearScreenComponent.Enabled = true; diff --git a/Samples/Editor/Model/AnimationDocument.IDocumentTools.cs b/Samples/Editor/Model/AnimationDocument.IDocumentTools.cs new file mode 100644 index 000000000..9e6722702 --- /dev/null +++ b/Samples/Editor/Model/AnimationDocument.IDocumentTools.cs @@ -0,0 +1,25 @@ +using SadConsole.Editor.Tools; +using SadConsole.ImGuiSystem; + +namespace SadConsole.Editor.Model; + +internal partial class AnimationDocument : IDocumentTools +{ + IDocumentToolsState IDocumentTools.State { get; } = new IDocumentToolsState(); + + bool IDocumentTools.ShowToolsList { get; set; } + + void IDocumentTools.BuildUI(ImGuiRenderer renderer) + { + + } + + void IDocumentTools.ToolChanged(ITool? oldTool, ITool newTool) + { + if (oldTool != null && oldTool is IOverlay overlay) + Surface.SadComponents.Remove(overlay.Overlay); + + if (newTool is IOverlay) + Surface.SadComponents.Add(((IOverlay)newTool).Overlay); + } +} diff --git a/Samples/Editor/Model/AnimationDocument.IFileHandler.cs b/Samples/Editor/Model/AnimationDocument.IFileHandler.cs new file mode 100644 index 000000000..ed1a47ca8 --- /dev/null +++ b/Samples/Editor/Model/AnimationDocument.IFileHandler.cs @@ -0,0 +1,46 @@ +using SadConsole.Editor.FileHandlers; +using SadConsole.ImGuiSystem; + +namespace SadConsole.Editor.Model; + +internal partial class AnimationDocument +{ + bool IFileHandler.SupportsLoad => true; + + bool IFileHandler.SupportsSave => true; + + string IFileHandler.FriendlyName => "Editor Document"; + + string[] IFileHandler.ExtensionsLoading => ["sadsurface"]; + + string[] IFileHandler.ExtensionsSaving => ["sadsurface"]; + + public string HelpInformation => "Saves just the surface document."; + + object IFileHandler.Load(string file) => + Serializer.Load(file, file.EndsWith('z')); + + bool IFileHandler.Save(object instance, string file) + { + if (!file.EndsWith(((IFileHandler)this).ExtensionsSaving[0], StringComparison.InvariantCulture)) + file += "." + ((IFileHandler)this).ExtensionsSaving[0]; + + if (instance is AnimationDocument surface) + { + try + { + Serializer.Save(surface, file, false); + + return true; + } + catch (Exception e) + { + ImGuiCore.Alert($"Unable to save file.\r\n\r\n{e.Message}"); + return false; + } + } + + ImGuiCore.Alert($"Unable to save file.\r\n\r\nWrong type sent to handler:\r\n {instance.GetType().Name}"); + return false; + } +} diff --git a/Samples/Editor/Model/AnimationDocument.cs b/Samples/Editor/Model/AnimationDocument.cs new file mode 100644 index 000000000..7c58bc465 --- /dev/null +++ b/Samples/Editor/Model/AnimationDocument.cs @@ -0,0 +1,237 @@ +using System.Numerics; +using System.Runtime.Serialization; +using ImGuiNET; +using SadConsole.Editor.FileHandlers; +using SadConsole.Editor.Windows; +using SadConsole.ImGuiSystem; + +namespace SadConsole.Editor.Model; + +internal partial class AnimationDocument : Document, IDocumentTools, IFileHandler +{ + private int _sliderValueY; + private int _sliderValueX; + + public int Width = 40; + public int Height = 20; + public int FrameCount; + + public int ViewX; + public int ViewY; + + public Vector4 DefaultForeground = Color.White.ToVector4(); + public Vector4 DefaultBackground = Color.Black.ToVector4(); + + [DataMember] + public Point SurfaceFontSize; + + [DataMember] + public Point EditorFontSize; + + [DataMember] + AnimatedScreenObject _animatedScreenObject; + + public AnimationDocument() + { + //DocumentType = Types.Surface; + + Options.UseToolsWindow = true; + Options.ToolsWindowShowToolsList = true; + + ((IDocumentTools)this).ShowToolsList = true; + ((IDocumentTools)this).State.ToolObjects = [ new Tools.Info(), new Tools.Empty(), new Tools.Pencil(), new Tools.Recolor(), new Tools.Fill(), new Tools.Box(), new Tools.Circle(), new Tools.Line() ]; + ((IDocumentTools)this).State.ToolNames = ((IDocumentTools)this).State.ToolObjects.Select(t => t.Name).ToArray(); + + Name = GenerateName("Animation"); + } + + public override void BuildUINew(ImGuiRenderer renderer) + { + float paddingX = ImGui.GetStyle().WindowPadding.X; + float windowWidth = ImGui.GetWindowWidth(); + + ImGui.Text("Name"); + ImGui.InputText("##name", ref Name, 50); + + ImGui.Separator(); + + ImGui.Text("Width: "); + ImGui.SameLine(ImGui.CalcTextSize("Frames: ").X + (ImGui.GetStyle().ItemSpacing.X * 2)); + ImGui.InputInt("##docwidth", ref Width); + + ImGui.Text("Height: "); + ImGui.SameLine(); + ImGui.InputInt("##docheight", ref Height); + + ImGui.Text("Frames: "); + ImGui.SameLine(); + ImGui.InputInt("##frames", ref FrameCount, 1); + FrameCount = Math.Clamp(FrameCount, 1, 25); + + ImGui.Text("Def. Foreground: "); + ImGui.SameLine(windowWidth - paddingX - ImGuiCore.State.LayoutInfo.ColorEditBoxWidth); + ImGui.ColorEdit4("##fore", ref DefaultForeground, ImGuiColorEditFlags.NoInputs); + ImGuiCore.State.CheckSetPopupOpen("##forepicker"); + ImGuiCore.State.LayoutInfo.ColorEditBoxWidth = ImGui.GetItemRectSize().X; + + ImGui.Text("Def. Background: "); + ImGui.SameLine(windowWidth - paddingX - ImGuiCore.State.LayoutInfo.ColorEditBoxWidth); + ImGui.ColorEdit4("##back", ref DefaultBackground, ImGuiColorEditFlags.NoInputs); + ImGuiCore.State.CheckSetPopupOpen("##backpicker"); + } + + public override void BuildUIEdit(ImGuiRenderer renderer, bool readOnly) + { + ImGuiWidgets.BeginGroupPanel("Animation Settings"); + + ImGui.AlignTextToFramePadding(); + ImGui.Text("Name:"); + ImGui.SameLine(); + ImGui.InputText("##name", ref Name, 50); + + ImGui.Separator(); + + if (ImGui.BeginTable("table1", 3)) + { + ImGui.TableSetupColumn("one", ImGuiTableColumnFlags.WidthFixed); + ImGui.TableSetupColumn("two", ImGuiTableColumnFlags.WidthFixed); + + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.AlignTextToFramePadding(); + ImGui.Text("Width: "); + ImGui.TableSetColumnIndex(1); + ImGui.Text(Width.ToString()); + ImGui.TableSetColumnIndex(2); + + if (ImGui.Button("Resize")) + ImGuiCore.State.OpenPopup("resize_document"); + + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.AlignTextToFramePadding(); + ImGui.Text("Height: "); + ImGui.TableSetColumnIndex(1); + ImGui.Text(Height.ToString()); + + if (ResizeSurfacePopup.Show("resize_document", ref Width, ref Height, out bool dialogResult)) + { + if (dialogResult) + { + if (Surface.Surface.ViewWidth > Width) + Surface.Surface.ViewWidth = Width; + if (Surface.Surface.ViewHeight > Height) + Surface.Surface.ViewHeight = Height; + + //TODO: TEST + ((ICellSurfaceResize)Surface.Surface).Resize(Surface.Surface.ViewWidth, Surface.Surface.ViewHeight, Width, Height, false); + Surface.IsDirty = true; + } + else + { + Width = Surface.Surface.Width; + Height = Surface.Surface.Height; + } + } + + ImGui.EndTable(); + } + + ImGui.Separator(); + + ImGui.AlignTextToFramePadding(); + ImGui.Text("Font: "); + ImGui.SameLine(); + if (ImGui.Button($"{Surface.Font.Name} | {SurfaceFontSize}")) + { + FontPicker popup = new(Surface.Font, SurfaceFontSize); + popup.Closed += FontPicker_Closed; + popup.Show(); + } + ImGui.Text("Editor Font Size: "); + ImGui.SameLine(); + if (ImGui.Button(EditorFontSize.ToString())) + { + ImGuiCore.State.OpenPopup("editorfontsize_select"); + } + ImGui.SameLine(); + if (ImGui.Button("Reset")) + { + EditorFontSize = SurfaceFontSize; + Surface.FontSize = EditorFontSize; + + // Force overlay to update and match surface + Components.Overlay? overlay = Surface.GetSadComponent(); + if (overlay != null) + overlay.Update(Surface, TimeSpan.Zero); + } + + if (FontSizePopup.Show(renderer, "editorfontsize_select", Surface.Font, ref EditorFontSize)) + { + Surface.FontSize = EditorFontSize; + + // Force overlay to update and match surface + Components.Overlay? overlay = Surface.GetSadComponent(); + if (overlay != null) + overlay.Update(Surface, TimeSpan.Zero); + } + + ImGuiWidgets.EndGroupPanel(); + } + + private void FontPicker_Closed(object? sender, EventArgs e) + { + FontPicker window = (FontPicker)sender!; + if (window.DialogResult) + { + Surface.Font = window.Font; + Surface.FontSize = window.FontSize; + EditorFontSize = window.FontSize; + SurfaceFontSize = window.FontSize; + } + } + + public override void BuildUIDocument(ImGuiRenderer renderer) + { + BuildUIDocumentStandard(renderer, Surface); + } + + public override void OnShow(ImGuiRenderer renderer) + { + SadConsole.Game.Instance.Screen = Surface; + } + + public override void OnHide(ImGuiRenderer renderer) + { + SadConsole.Game.Instance.Screen = null; + } + + public override IEnumerable GetLoadHandlers() => + [this, new SurfaceFile()]; + + public override IEnumerable GetSaveHandlers() => + [this, new SurfaceFile(), new SurfaceFileCompressed()]; + + public override bool HydrateFromFileHandler(IFileHandler handler, string file) + { + return false; + } + + public override object DehydrateToFileHandler(IFileHandler handler, string file) + { + return this; + } + + public override void Create() + { + _animatedScreenObject = new(Name, Width, Height); + for (int i = 0; i < FrameCount; i++) + _animatedScreenObject.CreateFrame(); + Surface.Surface.DefaultBackground = DefaultBackground.ToColor(); + Surface.Surface.DefaultForeground = DefaultForeground.ToColor(); + Surface.Surface.Clear(); + EditorFontSize = Surface.FontSize; + SurfaceFontSize = Surface.FontSize; + _animatedScreenObject.Render(TimeSpan.Zero); + } +} diff --git a/Samples/Editor/Model/Document.cs b/Samples/Editor/Model/Document.cs index fdecc6e28..588a68eaf 100644 --- a/Samples/Editor/Model/Document.cs +++ b/Samples/Editor/Model/Document.cs @@ -1,62 +1,157 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.Json; -using System.Threading.Tasks; +using System.Numerics; +using System.Runtime.Serialization; +using ImGuiNET; +using SadConsole.Editor.FileHandlers; using SadConsole.ImGuiSystem; -namespace SadConsole.Editor.Model +namespace SadConsole.Editor.Model; + +public abstract class Document { - public abstract class Document + private static int _id; + private string _uniqueIdentifier = GenerateCharacterId(); + + public DocumentOptions Options = new DocumentOptions(); + + public string UniqueIdentifier => _uniqueIdentifier; + + public string Name = GenerateName("Document"); + + public DocumentTypes DocumentType; + + public IScreenSurface Surface; + + public virtual void OnShow(ImGuiRenderer renderer) + { + + } + + public virtual void OnHide(ImGuiRenderer renderer) { - private static int _id; - private string _uniqueIdentifier = GenerateCharacterId(); - public DocumentOptions Options = new DocumentOptions(); + } - public string UniqueIdentifier => _uniqueIdentifier; + public abstract void Create(); - public enum Types + public abstract void BuildUIEdit(ImGuiRenderer renderer, bool readOnly); + + public abstract void BuildUINew(ImGuiRenderer renderer); + + public abstract void BuildUIDocument(ImGuiRenderer renderer); + + public abstract IEnumerable GetLoadHandlers(); + public abstract IEnumerable GetSaveHandlers(); + + public abstract bool HydrateFromFileHandler(IFileHandler handler, string file); + + public abstract object DehydrateToFileHandler(IFileHandler handler, string file); + + protected static string GenerateName(string prefix) => + $"{prefix}|{GenerateCharacterId()}"; + + protected static string GenerateCharacterId() + { + char[] characters = new char[6]; + foreach (var index in Enumerable.Range(1, 6)) { - Surface, - Scene, - Animation + characters[index - 1] = (char)Random.Shared.Next((int)'a', ((int)'z') + 1); } + return new string(characters); + } - public string Name = GenerateName(); + protected void BuildUIDocumentStandard(ImGuiRenderer renderer, IScreenSurface Surface) + { + Vector2 topLeft = ImGui.GetCursorPos(); + Vector2 region = ImGui.GetContentRegionAvail(); + Vector2 imageSize = new Vector2(Surface.Renderer!.Output.Width, Surface.Renderer.Output.Height); + int barSize = 15; + Vector2 padding = ImGui.GetStyle().FramePadding; + bool bigX = false; - public Types DocumentType; + int newViewWidth = (int)(region.X - barSize - (padding.X * 2)) / Surface.FontSize.X; + int newViewHeight = (int)(region.Y - barSize - 2 - (padding.Y * 2)) / Surface.FontSize.Y; // minus 2 is here because of button height - public virtual void OnShow(ImGuiRenderer renderer) - { + newViewWidth = Math.Max(newViewWidth, 1); + newViewHeight = Math.Max(newViewHeight, 1); - } + if (Surface.Surface.Width < newViewWidth && Surface.Surface.Width != Surface.Surface.ViewWidth) + Surface.Surface.ViewWidth = Surface.Surface.Width; + else if (Surface.Surface.Width > newViewWidth) + Surface.Surface.ViewWidth = newViewWidth; - public virtual void OnHide(ImGuiRenderer renderer) - { + if (Surface.Surface.Height < newViewHeight && Surface.Surface.Height != Surface.Surface.ViewHeight) + Surface.Surface.ViewHeight = Surface.Surface.Height; + else if (Surface.Surface.Height > newViewHeight) + Surface.Surface.ViewHeight = newViewHeight; + + // Print stats + ImGuiCore.GuiTopBar.StatusItems.Add((Vector4.Zero, "| ViewPort:")); + ImGuiCore.GuiTopBar.StatusItems.Add((Color.Yellow.ToVector4(), Surface.Surface.View.ToString())); + + // Force overlay to update and match surface + Components.Overlay? overlay = Surface.GetSadComponent(); + overlay?.Update(Surface, TimeSpan.Zero); + + Point hoveredCellPosition; + + ImGui.BeginChild("doc_surface"); + //isActive = ImGui.IsItemActive(); + //if (idother != 0 && idvalue != idother) System.Diagnostics.Debugger.Break(); + + ImGuiExt.DrawTextureChild("output_preview_surface1", true, ImGuiExt.ZoomNormal, ((Host.GameTexture)Surface.Renderer.Output).Texture, imageSize, renderer, out bool isActive, out bool isHovered); + + Tools.ITool? tool = ((IDocumentTools)this).State.SelectedTool; + tool?.DrawOverDocument(); + + Vector2 mousePosition = ImGui.GetMousePos(); + Vector2 pos = mousePosition - ImGui.GetItemRectMin(); + if (Surface.AbsoluteArea.WithPosition((0, 0)).Contains(new Point((int)pos.X, (int)pos.Y))) + { + if (isHovered) + { + hoveredCellPosition = new Point((int)pos.X, (int)pos.Y).PixelLocationToSurface(Surface.FontSize) + Surface.Surface.ViewPosition; + tool?.MouseOver(Surface, hoveredCellPosition, isActive, renderer); + ImGuiCore.GuiTopBar.StatusItems.Add((Vector4.Zero, "| Mouse:")); + ImGuiCore.GuiTopBar.StatusItems.Add((Color.Yellow.ToVector4(), hoveredCellPosition.ToString())); + } } - public abstract void Create(); + Rectangle view = Surface.Surface.View; + + if (view.Height != Surface.Surface.Height) + { + ImGui.SameLine(); - public abstract void BuildUIEdit(ImGuiRenderer renderer, bool readOnly); + int _sliderValueY = view.Position.Y; - public abstract void BuildUINew(ImGuiRenderer renderer); + ImGui.BeginDisabled(Options.DisableScrolling); - public abstract void BuildUIDocument(ImGuiRenderer renderer); + if (ImGuiExt.VSliderIntNudges("##height", new Vector2(barSize, imageSize.Y), ref _sliderValueY, Surface.Surface.Height - view.Height, 0, ImGuiSliderFlags.AlwaysClamp)) + { + Surface.Surface.ViewPosition = Surface.Surface.ViewPosition.WithY(_sliderValueY); + tool?.DocumentViewChanged(); + } - private static string GenerateName() => - $"Document"; + ImGui.EndDisabled(); + } - protected static string GenerateCharacterId() + if (view.Width != Surface.Surface.Width) { - char[] characters = new char[6]; - foreach (var index in Enumerable.Range(1, 6)) + int _sliderValueX = view.Position.X; + + ImGui.BeginDisabled(Options.DisableScrolling); + + if (ImGuiExt.SliderIntNudges("##width", (int)imageSize.X, ref _sliderValueX, 0, Surface.Surface.Width - view.Width, bigX ? "BIG" : "%d", ImGuiSliderFlags.AlwaysClamp)) { - characters[index - 1] = (char)Random.Shared.Next((int)'a', ((int)'z') + 1); + Surface.Surface.ViewPosition = Surface.Surface.ViewPosition.WithX(_sliderValueX); + tool?.DocumentViewChanged(); } - return new string(characters); + + ImGui.EndDisabled(); } + + ImGui.EndChild(); } + } diff --git a/Samples/Editor/Model/DocumentOptions.cs b/Samples/Editor/Model/DocumentOptions.cs index f4e52370c..47cdb6302 100644 --- a/Samples/Editor/Model/DocumentOptions.cs +++ b/Samples/Editor/Model/DocumentOptions.cs @@ -11,5 +11,8 @@ namespace SadConsole.Editor.Model; public class DocumentOptions { public bool UseToolsWindow { get; set; } + public bool ToolsWindowShowToolsList { get; set; } + + public bool DisableScrolling { get; set; } } diff --git a/Samples/Editor/Model/DocumentTypes.cs b/Samples/Editor/Model/DocumentTypes.cs new file mode 100644 index 000000000..3ff51cb8d --- /dev/null +++ b/Samples/Editor/Model/DocumentTypes.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SadConsole.Editor.Model; + +public enum DocumentTypes +{ + Surface, + Scene, + Animation +} + +public static class DocumentTypeNames +{ + public const string Surface = "Surface"; + public const string Scene = "Scene"; + public const string Animation = "Animation"; + + private static string[] _names = { Surface, Scene, Animation }; + + public static string[] Names => _names; +} diff --git a/Samples/Editor/Model/IDocumentSurface.cs b/Samples/Editor/Model/IDocumentSurface.cs deleted file mode 100644 index cc16d8716..000000000 --- a/Samples/Editor/Model/IDocumentSurface.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace SadConsole.Editor.Model; -internal interface IDocumentSurface -{ - IScreenSurface Surface { get; } -} diff --git a/Samples/Editor/Model/IDocumentTools.cs b/Samples/Editor/Model/IDocumentTools.cs index 80251ceaf..870d902d3 100644 --- a/Samples/Editor/Model/IDocumentTools.cs +++ b/Samples/Editor/Model/IDocumentTools.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using SadConsole.Editor.Tools; using SadConsole.ImGuiSystem; namespace SadConsole.Editor.Model; @@ -14,6 +10,8 @@ internal interface IDocumentTools bool ShowToolsList { get; set; } void BuildUI(ImGuiRenderer renderer); + + void ToolChanged(ITool? oldTool, ITool newTool); } public class IDocumentToolsState @@ -23,5 +21,5 @@ public class IDocumentToolsState public string[] ToolNames; public Tools.ITool[] ToolObjects; - public Tools.ITool SelectedTool => SelectedToolIndex != -1 ? ToolObjects[SelectedToolIndex] : null; + public Tools.ITool? SelectedTool => SelectedToolIndex != -1 ? ToolObjects[SelectedToolIndex] : null; } diff --git a/Samples/Editor/Model/SadConsoleTypes/ConnectedGlyphs.cs b/Samples/Editor/Model/SadConsoleTypes/ConnectedGlyphs.cs new file mode 100644 index 000000000..3128cfe2e --- /dev/null +++ b/Samples/Editor/Model/SadConsoleTypes/ConnectedGlyphs.cs @@ -0,0 +1,25 @@ +using System.Linq; + +namespace SadConsole.Editor.Model.SadConsoleTypes; + +internal class ConnectedGlyphs +{ + public static string[] _names = { "Empty", "Thin", "Thin Extended", "Thick", "3D" }; + public static int[][] _values = { ICellSurface.ConnectedLineEmpty, ICellSurface.ConnectedLineThin, ICellSurface.ConnectedLineThinExtended, ICellSurface.ConnectedLineThick, ICellSurface.Connected3dBox }; + + public static string[] Names => _names; + + public static int[] GetValueFromIndex(int index) => + _values[index]; + + public static int GetIndexFromValue(int[] value) + { + for (int i = 0; i < _values.Length; i++) + { + if (_values[i].SequenceEqual(value)) + return i; + } + + return -1; + } +} diff --git a/Samples/Editor/Model/SadConsoleTypes/Mirror.cs b/Samples/Editor/Model/SadConsoleTypes/Mirror.cs new file mode 100644 index 000000000..55789fe05 --- /dev/null +++ b/Samples/Editor/Model/SadConsoleTypes/Mirror.cs @@ -0,0 +1,16 @@ +namespace SadConsole.Editor.Model.SadConsoleTypes; + +public static class Mirror +{ + private static string[] _names = Enum.GetNames(); + private static SadConsole.Mirror[] _values = Enum.GetValues(); + + public static string[] Names => _names; + + public static SadConsole.Mirror GetValueFromIndex(int index) => + _values[index]; + + public static int GetIndexFromValue(SadConsole.Mirror value) => + Array.IndexOf(_values, value); + +} diff --git a/Samples/Editor/Model/SurfaceDocument.IDocumentTools.cs b/Samples/Editor/Model/SurfaceDocument.IDocumentTools.cs index c997aa9e5..19123f83d 100644 --- a/Samples/Editor/Model/SurfaceDocument.IDocumentTools.cs +++ b/Samples/Editor/Model/SurfaceDocument.IDocumentTools.cs @@ -1,13 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using System.Text; -using System.Threading.Tasks; -using ImGuiNET; -using Microsoft.Xna.Framework.Graphics; +using SadConsole.Editor.Tools; using SadConsole.ImGuiSystem; -using SadRogue.Primitives; namespace SadConsole.Editor.Model; @@ -21,4 +13,13 @@ void IDocumentTools.BuildUI(ImGuiRenderer renderer) { } + + void IDocumentTools.ToolChanged(ITool? oldTool, ITool newTool) + { + if (oldTool != null && oldTool is IOverlay overlay) + Surface.SadComponents.Remove(overlay.Overlay); + + if (newTool is IOverlay) + Surface.SadComponents.Add(((IOverlay)newTool).Overlay); + } } diff --git a/Samples/Editor/Model/SurfaceDocument.IFileHandler.cs b/Samples/Editor/Model/SurfaceDocument.IFileHandler.cs new file mode 100644 index 000000000..93cc9acb4 --- /dev/null +++ b/Samples/Editor/Model/SurfaceDocument.IFileHandler.cs @@ -0,0 +1,46 @@ +using SadConsole.Editor.FileHandlers; +using SadConsole.ImGuiSystem; + +namespace SadConsole.Editor.Model; + +internal partial class SurfaceDocument +{ + bool IFileHandler.SupportsLoad => true; + + bool IFileHandler.SupportsSave => true; + + string IFileHandler.FriendlyName => "Editor Document"; + + string[] IFileHandler.ExtensionsLoading => ["sadsurface"]; + + string[] IFileHandler.ExtensionsSaving => ["sadsurface"]; + + public string HelpInformation => "Saves just the surface document."; + + object IFileHandler.Load(string file) => + Serializer.Load(file, file.EndsWith('z')); + + bool IFileHandler.Save(object instance, string file) + { + if (!file.EndsWith(((IFileHandler)this).ExtensionsSaving[0], StringComparison.InvariantCulture)) + file += "." + ((IFileHandler)this).ExtensionsSaving[0]; + + if (instance is SurfaceDocument surface) + { + try + { + Serializer.Save(surface, file, false); + + return true; + } + catch (Exception e) + { + ImGuiCore.Alert($"Unable to save file.\r\n\r\n{e.Message}"); + return false; + } + } + + ImGuiCore.Alert($"Unable to save file.\r\n\r\nWrong type sent to handler:\r\n {instance.GetType().Name}"); + return false; + } +} diff --git a/Samples/Editor/Model/SurfaceDocument.cs b/Samples/Editor/Model/SurfaceDocument.cs index 6b94d65fe..30fd81091 100644 --- a/Samples/Editor/Model/SurfaceDocument.cs +++ b/Samples/Editor/Model/SurfaceDocument.cs @@ -1,18 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using System.Text; -using System.Threading.Tasks; +using System.Numerics; +using System.Runtime.Serialization; using ImGuiNET; -using Microsoft.Xna.Framework.Graphics; +using SadConsole.Editor.FileHandlers; +using SadConsole.Editor.Windows; using SadConsole.ImGuiSystem; -using SadRogue.Primitives; -using ImGuiInternal = ImGuiNET.Internal.ImGui; namespace SadConsole.Editor.Model; -internal partial class SurfaceDocument : Document, IDocumentTools, IDocumentSurface +internal partial class SurfaceDocument : Document, IDocumentTools, IFileHandler { private int _sliderValueY; private int _sliderValueX; @@ -22,26 +17,30 @@ internal partial class SurfaceDocument : Document, IDocumentTools, IDocumentSurf public int ViewX; public int ViewY; - public int ViewWidth; - public int ViewHeight; public Vector4 DefaultForeground = Color.White.ToVector4(); public Vector4 DefaultBackground = Color.Black.ToVector4(); - public IScreenSurface Surface; + [DataMember] + public Point SurfaceFontSize; + [DataMember] + public Point EditorFontSize; - IScreenSurface IDocumentSurface.Surface => Surface; + [DataMember] + public IScreenSurface Surface { get => base.Surface; set => base.Surface = value; } public SurfaceDocument() { - DocumentType = Types.Surface; + //DocumentType = Types.Surface; Options.UseToolsWindow = true; Options.ToolsWindowShowToolsList = true; ((IDocumentTools)this).ShowToolsList = true; - ((IDocumentTools)this).State.ToolNames = new string[] { "Pencil", "Fill" }; - ((IDocumentTools)this).State.ToolObjects = new Tools.ITool[] { new Tools.Pencil(), new Tools.Fill() }; + ((IDocumentTools)this).State.ToolObjects = [ new Tools.Info(), new Tools.Empty(), new Tools.Pencil(), new Tools.Recolor(), + new Tools.Fill(), new Tools.Box(), new Tools.Circle(), new Tools.Line(), + new Tools.Text(), new Tools.Selection() ]; + ((IDocumentTools)this).State.ToolNames = ((IDocumentTools)this).State.ToolObjects.Select(t => t.Name).ToArray(); } public override void BuildUINew(ImGuiRenderer renderer) @@ -65,11 +64,13 @@ public override void BuildUINew(ImGuiRenderer renderer) ImGui.Text("Def. Foreground: "); ImGui.SameLine(windowWidth - paddingX - ImGuiCore.State.LayoutInfo.ColorEditBoxWidth); ImGui.ColorEdit4("##fore", ref DefaultForeground, ImGuiColorEditFlags.NoInputs); + ImGuiCore.State.CheckSetPopupOpen("##forepicker"); ImGuiCore.State.LayoutInfo.ColorEditBoxWidth = ImGui.GetItemRectSize().X; ImGui.Text("Def. Background: "); ImGui.SameLine(windowWidth - paddingX - ImGuiCore.State.LayoutInfo.ColorEditBoxWidth); ImGui.ColorEdit4("##back", ref DefaultBackground, ImGuiColorEditFlags.NoInputs); + ImGuiCore.State.CheckSetPopupOpen("##backpicker"); } public override void BuildUIEdit(ImGuiRenderer renderer, bool readOnly) @@ -95,7 +96,9 @@ public override void BuildUIEdit(ImGuiRenderer renderer, bool readOnly) ImGui.TableSetColumnIndex(1); ImGui.Text(Width.ToString()); ImGui.TableSetColumnIndex(2); - ImGui.Button("Resize"); + + if (ImGui.Button("Resize")) + ImGuiCore.State.OpenPopup("resize_document"); ImGui.TableNextRow(); ImGui.TableSetColumnIndex(0); @@ -104,6 +107,25 @@ public override void BuildUIEdit(ImGuiRenderer renderer, bool readOnly) ImGui.TableSetColumnIndex(1); ImGui.Text(Height.ToString()); + if (ResizeSurfacePopup.Show("resize_document", ref Width, ref Height, out bool dialogResult)) + { + if (dialogResult) + { + if (Surface.Surface.ViewWidth > Width) + Surface.Surface.ViewWidth = Width; + if (Surface.Surface.ViewHeight > Height) + Surface.Surface.ViewHeight = Height; + + ((ICellSurfaceResize)Surface.Surface).Resize(Surface.Surface.ViewWidth, Surface.Surface.ViewHeight, Width, Height, false); + Surface.IsDirty = true; + } + else + { + Width = Surface.Surface.Width; + Height = Surface.Surface.Height; + } + } + ImGui.EndTable(); } @@ -115,6 +137,7 @@ public override void BuildUIEdit(ImGuiRenderer renderer, bool readOnly) ImGui.SameLine(); if (ImGui.ColorEdit4("##fore", ref DefaultForeground, ImGuiColorEditFlags.NoInputs)) Surface.Surface.DefaultForeground = DefaultForeground.ToColor(); + ImGuiCore.State.CheckSetPopupOpen("##forepicker"); DefaultBackground = Surface.Surface.DefaultBackground.ToVector4(); ImGui.AlignTextToFramePadding(); @@ -122,29 +145,65 @@ public override void BuildUIEdit(ImGuiRenderer renderer, bool readOnly) ImGui.SameLine(); if (ImGui.ColorEdit4("##back", ref DefaultBackground, ImGuiColorEditFlags.NoInputs)) Surface.Surface.DefaultBackground = DefaultBackground.ToColor(); + ImGuiCore.State.CheckSetPopupOpen("##backpicker"); + + ImGui.Separator(); + + ImGui.AlignTextToFramePadding(); + ImGui.Text("Font: "); + ImGui.SameLine(); + if (ImGui.Button($"{Surface.Font.Name} | {SurfaceFontSize}")) + { + FontPicker popup = new(Surface.Font, SurfaceFontSize); + popup.Closed += FontPicker_Closed; + popup.Show(); + } + ImGui.Text("Editor Font Size: "); + ImGui.SameLine(); + if (ImGui.Button(EditorFontSize.ToString())) + { + ImGuiCore.State.OpenPopup("editorfontsize_select"); + } + ImGui.SameLine(); + if (ImGui.Button("Reset")) + { + EditorFontSize = SurfaceFontSize; + Surface.FontSize = EditorFontSize; + + // Force overlay to update and match surface + Components.Overlay? overlay = Surface.GetSadComponent(); + if (overlay != null) + overlay.Update(Surface, TimeSpan.Zero); + } + + if (FontSizePopup.Show(renderer, "editorfontsize_select", Surface.Font, ref EditorFontSize)) + { + Surface.FontSize = EditorFontSize; + + // Force overlay to update and match surface + Components.Overlay? overlay = Surface.GetSadComponent(); + if (overlay != null) + overlay.Update(Surface, TimeSpan.Zero); + } ImGuiWidgets.EndGroupPanel(); } - public override void BuildUIDocument(ImGuiRenderer renderer) + private void FontPicker_Closed(object? sender, EventArgs e) { - //ImGui.BeginTabBar("document_host_tabbar", ImGuiTabBarFlags.NoCloseWithMiddleMouseButton); - - //if (ImGui.BeginTabItem($"{Name}##{UniqueIdentifier}")) - //{ - var topLeft = ImGui.GetCursorPos(); - if (ImGuiExt.ScrollableSurface("doc_surface", Surface, out Point hoveredCellPosition, renderer)) + FontPicker window = (FontPicker)sender!; + if (window.DialogResult) { - Tools.ITool tool = ((IDocumentTools)this).State.SelectedTool; - if (tool != null) - { - tool.MouseOver(Surface, hoveredCellPosition, renderer); - } + Surface.Font = window.Font; + Surface.FontSize = window.FontSize; + EditorFontSize = window.FontSize; + SurfaceFontSize = window.FontSize; } - //ImGui.EndTabItem(); - //} + } - //ImGui.EndTabBar(); + public override void BuildUIDocument(ImGuiRenderer renderer) + { + BuildUIDocumentStandard(renderer, Surface); } public override void OnShow(ImGuiRenderer renderer) @@ -157,12 +216,82 @@ public override void OnHide(ImGuiRenderer renderer) SadConsole.Game.Instance.Screen = null; } + public override IEnumerable GetLoadHandlers() => + [this, new SurfaceFile()]; + + public override IEnumerable GetSaveHandlers() => + [this, new SurfaceFile(), new SurfaceFileCompressed()]; + + public override bool HydrateFromFileHandler(IFileHandler handler, string file) + { + if (handler is SurfaceDocument documentHandler) + { + documentHandler = (SurfaceDocument)handler.Load(file); + //TODO: Should most of the these ... = documentHandler.value calls just go to the surface directly?? + Surface = documentHandler.Surface; + DefaultBackground = Surface.Surface.DefaultBackground.ToVector4(); + DefaultForeground = Surface.Surface.DefaultForeground.ToVector4(); + DocumentType = documentHandler.DocumentType; + Height = documentHandler.Height; + Width = documentHandler.Width; + Name = documentHandler.Name; + Options = documentHandler.Options; + ViewX = 0; + ViewY = 0; + EditorFontSize = documentHandler.EditorFontSize; + SurfaceFontSize = documentHandler.SurfaceFontSize; + Surface.FontSize = EditorFontSize; + Surface.Render(TimeSpan.Zero); + return true; + } + else if (handler is SurfaceFileCompressed compressedHandler) + { + Surface = (ScreenSurface)compressedHandler.Load(file); + Width = Surface.Surface.Width; + Height = Surface.Surface.Height; + DefaultBackground = Surface.Surface.DefaultBackground.ToVector4(); + DefaultForeground = Surface.Surface.DefaultForeground.ToVector4(); + EditorFontSize = Surface.FontSize; + SurfaceFontSize = Surface.FontSize; + Surface.Render(TimeSpan.Zero); + return true; + } + else if (handler is SurfaceFile surfaceHandler) + { + Surface = (ScreenSurface)surfaceHandler.Load(file); + Width = Surface.Surface.Width; + Height = Surface.Surface.Height; + DefaultBackground = Surface.Surface.DefaultBackground.ToVector4(); + DefaultForeground = Surface.Surface.DefaultForeground.ToVector4(); + EditorFontSize = Surface.FontSize; + SurfaceFontSize = Surface.FontSize; + Surface.Render(TimeSpan.Zero); + return true; + } + else + return false; + } + + public override object DehydrateToFileHandler(IFileHandler handler, string file) + { + if (handler is SurfaceFileCompressed || handler is SurfaceFile) + { + ScreenSurface newSurface = new(Surface.Surface, Surface.Font, SurfaceFontSize); + return newSurface; + } + + return this; + } + + public override void Create() { Surface = new ScreenSurface(Width, Height); Surface.Surface.DefaultBackground = DefaultBackground.ToColor(); Surface.Surface.DefaultForeground = DefaultForeground.ToColor(); Surface.Surface.Clear(); + EditorFontSize = Surface.FontSize; + SurfaceFontSize = Surface.FontSize; Surface.Render(TimeSpan.Zero); } diff --git a/Samples/Editor/Program.cs b/Samples/Editor/Program.cs index 155c24250..e31419cc0 100644 --- a/Samples/Editor/Program.cs +++ b/Samples/Editor/Program.cs @@ -1,57 +1,149 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Xml.Linq; -using System.Linq; -using Newtonsoft.Json; -using SadConsole; -using SadConsole.Components; +using Gum.Wireframe; +using SadConsole.Configuration; +using SadConsole.Editor; using SadConsole.Input; -using SadConsole.Renderers; -using SadRogue.Primitives; -using Console = SadConsole.Console; -using SadRogue.Primitives.GridViews; -using SadConsole.Effects; -using System.Collections; -using SadConsole.Quick; -using SadConsole.Readers; -using System.IO; -using Newtonsoft.Json.Linq; - -namespace SadConsole.Editor +using SadConsole.UI; +using SadConsole.UI.Controls; + +int MainWidth = 80; +int MainHeight = 23; +int HeaderWidth = 80; +int HeaderHeight = 2; + +Settings.WindowTitle = "SadEditor v0.1"; + +Builder config = + new Builder() + .SetScreenSize(130, 50) + .OnStart(StartHandler); + + //.UseDefaultConsole() + //.IsStartingScreenFocused(true) + //.SetStartingScreen(); + + //.SetStartingScreen() + //.IsStartingScreenFocused(true); + +Game.Create(config); +Game.Instance.Run(); +Game.Instance.Dispose(); + +static void StartHandler(object? sender, GameHost host) { - internal class Program - { - public static int MainWidth = 80; - public static int MainHeight = 23; - public static int HeaderWidth = 80; - public static int HeaderHeight = 2; + ImGuiCore.Start(); +} - private static void Main(string[] args) +public class KeyboardScreen : ScreenObject +{ + + public override bool ProcessKeyboard(Keyboard keyboard) + { + Game.Instance.Keyboard.InitialRepeatDelay = -1; + + if (keyboard.IsKeyPressed(Keys.NumPad2)) { - Settings.WindowTitle = "Feature Demo (MonoGame)"; - Settings.CreateStartingConsole = false; - - Game.Configuration config = - new Game.Configuration() - .SetScreenSize(130, 50) - .OnStart(Init) - .SetStartingScreen(); - - SadConsole.Game.Create(config); - SadConsole.Game.Instance.Run(); - SadConsole.Game.Instance.Dispose(); + if (keyboard.IsKeyDown(Keys.LeftShift) || keyboard.IsKeyDown(Keys.RightShift)) + System.Diagnostics.Debug.WriteLine("NumPad2 with shift"); + else + System.Diagnostics.Debug.WriteLine("NumPad2 without shift"); + } - /// - /// test - /// - private static void Init() + return false; + } +} + + +class ExampleConsole : ControlsConsole +{ + public ExampleConsole() : base(130, 50) + { + GraphicalUiElement.CanvasWidth = Width; + GraphicalUiElement.CanvasHeight = Height; + + //SadConsoleGumHost parentContainer = new(this) + //{ + // HeightUnits = Gum.DataTypes.DimensionUnitType.RelativeToChildren, + // ChildrenLayout = Gum.Managers.ChildrenLayout.AutoGridVertical, + // WrapsChildren = true + //}; + + //for (int i = 0; i < 10; i++) + //{ + // SadConsoleGumControl buttonWrapper = new((renderObject) => new ButtonBox((int)renderObject.Width, (int)renderObject.Height) { Text = "Button " + i.ToString() }) + // { + // XOrigin = RenderingLibrary.Graphics.HorizontalAlignment.Left, + // XUnits = Gum.Converters.GeneralUnitType.PixelsFromSmall, + // }; + + // parentContainer.Children.Add(buttonWrapper); + //} + + //parentContainer.Generate(); + + /////////////////////// + + Panel panel = new(Width, Height) { - // Register the types provided by the SadConsole.Extended library - SadConsole.UI.RegistrarExtended.Register(); + }; + + GraphicalUiElement parentContainer = new(new ControlBaseGumWrapper(panel), null!) + { + Width = Width, + Height = Height, + ChildrenLayout = Gum.Managers.ChildrenLayout.TopToBottomStack + }; + + + + SelectionButton? previousButton = null; + + //for (int i = 0; i < 10; i++) + //{ + + SelectionButton button1 = new(1, 1); + + + GraphicalUiElement buttonWrapper = new(new ControlBaseGumWrapper(button1), null!) + { + XOrigin = RenderingLibrary.Graphics.HorizontalAlignment.Left, + XUnits = Gum.Converters.GeneralUnitType.PixelsFromSmall, + Width = 10, + Height = 1 + }; + + parentContainer.Children.Add(buttonWrapper); + + + buttonWrapper.WidthUnits = Gum.DataTypes.DimensionUnitType.RelativeToContainer; + buttonWrapper.Width = 0; + + //} + + //foreach (IRenderableIpso child in parentContainer.Children) + //{ + // SelectionButton button1 = new((int)child.Width, (int)child.Height) + // { + // Text = "Test", + // Position = ((int)child.X, (int)child.Y) + // }; + + // if (previousButton != null) + // { + // button1.PreviousSelection = previousButton; + // previousButton.NextSelection = button1; + // } + + // previousButton = button1; + + // panel.Add(button1); + //} + + //((SelectionButton)panel[^1]).NextSelection = (SelectionButton)panel[0]; + //((SelectionButton)panel[0]).PreviousSelection = (SelectionButton)panel[^1]; + + Controls.Add(panel); - ImGuiCore.Start(); - } } + } diff --git a/Samples/Editor/Tools/Box.cs b/Samples/Editor/Tools/Box.cs new file mode 100644 index 000000000..b86ce1ad4 --- /dev/null +++ b/Samples/Editor/Tools/Box.cs @@ -0,0 +1,224 @@ +using System.Numerics; +using ImGuiNET; +using SadConsole.Components; +using SadConsole.Editor.GuiParts.Tools; +using SadConsole.Editor.Model; +using SadConsole.ImGuiSystem; + +namespace SadConsole.Editor.Tools; + +internal class Box : ITool, IOverlay +{ + private bool _isFirstPointSelected = false; + private Rectangle _boxArea; + private Point _firstPoint; + private Point _secondPoint; + private Overlay _toolOverlay = new(); + private bool _isCancelled; + + private ShapeSettings.Settings _shapeSettings; + + public string Name => "Box"; + + public string Description => """ + Draws a box. + + The border and fill of the box can be customized. + + Depress the left mouse button to start drawing. Hold down the button and drag the mouse to draw the box. Let go of the button to finish drawing. + + To cancel drawing, depress the right mouse button or press the ESC key. + """; + + public Overlay Overlay => _toolOverlay; + + public void BuildSettingsPanel(ImGuiRenderer renderer) + { + ImGuiWidgets.BeginGroupPanel("Settings"); + + IScreenSurface surface = ImGuiCore.State.GetOpenDocument().Surface; + + //GuiParts.Tools.SettingsTable.BeginTable("toolsettings"); + + ImGui.Checkbox("Has Border", ref _shapeSettings.HasBorder); + //GuiParts.Tools.SettingsTable.DrawCheckbox("Has Border", "##hasborder", ref _shapeSettings.HasBorder); + if (_shapeSettings.HasBorder) + { + + _shapeSettings.BorderGlyph ??= new ColoredGlyph(); + + // Data for border settings + Vector4 foreground = _shapeSettings.BorderGlyph.Foreground.ToVector4(); + Vector4 background = _shapeSettings.BorderGlyph.Background.ToVector4(); + Mirror mirror = _shapeSettings.BorderGlyph.Mirror; + int glyph = _shapeSettings.BorderGlyph.Glyph; + + ImGuiWidgets.BeginGroupPanel("Border"); + GuiParts.Tools.SettingsTable.BeginTable("bordersettings"); + + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.AlignTextToFramePadding(); + ImGui.Checkbox("Use Line Style", ref _shapeSettings.UseBoxBorderStyle); + ImGui.TableSetColumnIndex(1); + + int itemIndex = Model.SadConsoleTypes.ConnectedGlyphs.GetIndexFromValue(_shapeSettings.BoxBorderStyle ?? ICellSurface.ConnectedLineEmpty); + + ImGui.Combo("##border_line_style", ref itemIndex, Model.SadConsoleTypes.ConnectedGlyphs.Names, Model.SadConsoleTypes.ConnectedGlyphs.Names.Length); + + if (_shapeSettings.UseBoxBorderStyle) + _shapeSettings.BoxBorderStyle = Model.SadConsoleTypes.ConnectedGlyphs.GetValueFromIndex(itemIndex); + else + _shapeSettings.BoxBorderStyle = null; + + + GuiParts.Tools.SettingsTable.DrawColor("Foreground:", "##fore", ref foreground, surface.Surface.DefaultForeground.ToVector4(), out bool colorRightClicked); + if (colorRightClicked) + (background, foreground) = (foreground, background); + + GuiParts.Tools.SettingsTable.DrawColor("Background:", "##back", ref background, surface.Surface.DefaultBackground.ToVector4(), out colorRightClicked); + if (colorRightClicked) + (background, foreground) = (foreground, background); + + GuiParts.Tools.SettingsTable.DrawMirror("Mirror:", "##mirror", ref mirror); + + GuiParts.Tools.SettingsTable.DrawFontGlyph("Glyph:", "##glyph", ref glyph, foreground, background, surface.Font, renderer); + GuiParts.Tools.SettingsTable.EndTable(); + if (ImGui.CollapsingHeader("Ignore Options##border")) + { + GuiParts.Tools.SettingsTable.BeginTable("bordersettings_ignore"); + GuiParts.Tools.SettingsTable.DrawCheckbox("Ignore Foreground", "##ignore_border_foreground", ref _shapeSettings.IgnoreBorderForeground); + GuiParts.Tools.SettingsTable.DrawCheckbox("Ignore Background", "##ignore_border_background", ref _shapeSettings.IgnoreBorderBackground); + GuiParts.Tools.SettingsTable.DrawCheckbox("Ignore Mirror", "##ignore_border_mirror", ref _shapeSettings.IgnoreBorderMirror); + GuiParts.Tools.SettingsTable.DrawCheckbox("Ignore Glyph", "##ignore_border_glyph", ref _shapeSettings.IgnoreBorderGlyph); + GuiParts.Tools.SettingsTable.EndTable(); + } + // Store the altered settings + _shapeSettings.BorderGlyph.Foreground = foreground.ToColor(); + _shapeSettings.BorderGlyph.Background = background.ToColor(); + _shapeSettings.BorderGlyph.Mirror = mirror; + _shapeSettings.BorderGlyph.Glyph = glyph; + + ImGuiWidgets.EndGroupPanel(); + } + + ImGui.Checkbox("Has Fill", ref _shapeSettings.HasFill); + //GuiParts.Tools.SettingsTable.DrawCheckbox("Has Border", "##hasborder", ref _shapeSettings.HasBorder); + if (_shapeSettings.HasFill) + { + + _shapeSettings.FillGlyph ??= new ColoredGlyph(); + + // Data for border settings + Vector4 foreground = _shapeSettings.FillGlyph.Foreground.ToVector4(); + Vector4 background = _shapeSettings.FillGlyph.Background.ToVector4(); + Mirror mirror = _shapeSettings.FillGlyph.Mirror; + int glyph = _shapeSettings.FillGlyph.Glyph; + + ImGuiWidgets.BeginGroupPanel("Border"); + GuiParts.Tools.SettingsTable.BeginTable("bordersettings"); + + GuiParts.Tools.SettingsTable.DrawColor("Foreground:", "##fore", ref foreground, surface.Surface.DefaultForeground.ToVector4(), out bool colorRightClicked); + if (colorRightClicked) + (background, foreground) = (foreground, background); + + GuiParts.Tools.SettingsTable.DrawColor("Background:", "##back", ref background, surface.Surface.DefaultBackground.ToVector4(), out colorRightClicked); + if (colorRightClicked) + (background, foreground) = (foreground, background); + + GuiParts.Tools.SettingsTable.DrawMirror("Mirror:", "##mirror", ref mirror); + + GuiParts.Tools.SettingsTable.DrawFontGlyph("Glyph:", "##glyph", ref glyph, foreground, background, surface.Font, renderer); + GuiParts.Tools.SettingsTable.EndTable(); + if (ImGui.CollapsingHeader("Ignore Options##fill")) + { + GuiParts.Tools.SettingsTable.BeginTable("fillsettings_ignore"); + GuiParts.Tools.SettingsTable.DrawCheckbox("Ignore Foreground", "##ignore_Fill_foreground", ref _shapeSettings.IgnoreFillForeground); + GuiParts.Tools.SettingsTable.DrawCheckbox("Ignore Background", "##ignore_Fill_background", ref _shapeSettings.IgnoreFillBackground); + GuiParts.Tools.SettingsTable.DrawCheckbox("Ignore Mirror", "##ignore_Fill_mirror", ref _shapeSettings.IgnoreFillMirror); + GuiParts.Tools.SettingsTable.DrawCheckbox("Ignore Glyph", "##ignore_Fill_glyph", ref _shapeSettings.IgnoreFillGlyph); + GuiParts.Tools.SettingsTable.EndTable(); + } + + // Store the altered settings + _shapeSettings.FillGlyph.Foreground = foreground.ToColor(); + _shapeSettings.FillGlyph.Background = background.ToColor(); + _shapeSettings.FillGlyph.Mirror = mirror; + _shapeSettings.FillGlyph.Glyph = glyph; + + ImGuiWidgets.EndGroupPanel(); + } + + ImGuiWidgets.EndGroupPanel(); + } + + public void MouseOver(IScreenSurface surface, Point hoveredCellPosition, bool isActive, ImGuiRenderer renderer) + { + if (ImGuiCore.State.IsPopupOpen) return; + + GuiParts.Tools.ToolHelpers.HighlightCell(hoveredCellPosition, surface.Surface.ViewPosition, surface.FontSize, Color.Green); + + // No settings to draw, exit + if (!_shapeSettings.HasFill && !_shapeSettings.HasBorder) + return; + + // Cancelled but left mouse finally released, exit cancelled + if (_isCancelled && ImGui.IsMouseReleased(ImGuiMouseButton.Left)) + _isCancelled = false; + + // Cancelled + if (ImGui.IsMouseDown(ImGuiMouseButton.Left) && (ImGui.IsMouseClicked(ImGuiMouseButton.Right) || ImGui.IsKeyReleased(ImGuiKey.Escape))) + { + OnDeselected(); + _isCancelled = true; + } + + if (_isCancelled) + return; + + if (ImGui.IsMouseDown(ImGuiMouseButton.Left) && isActive) + { + if (!_isFirstPointSelected) + { + _isFirstPointSelected = true; + + _firstPoint = hoveredCellPosition - surface.Surface.ViewPosition; + } + + _secondPoint = hoveredCellPosition - surface.Surface.ViewPosition; + + _boxArea = new(new Point(Math.Min(_firstPoint.X, _secondPoint.X), Math.Min(_firstPoint.Y, _secondPoint.Y)), + new Point(Math.Max(_firstPoint.X, _secondPoint.X), Math.Max(_firstPoint.Y, _secondPoint.Y))); + + Overlay.Surface.Clear(); + Overlay.Surface.DrawBox(_boxArea, _shapeSettings.ToShapeParameters()); + } + else if (ImGui.IsMouseReleased(ImGuiMouseButton.Left)) + { + if (_boxArea != Rectangle.Empty) + { + surface.Surface.DrawBox(_boxArea.Translate(surface.Surface.ViewPosition), _shapeSettings.ToShapeParameters()); + Overlay.Surface.Clear(); + } + + OnDeselected(); + } + } + + public void OnSelected() + { + + } + + public void OnDeselected() + { + Overlay.Surface.Clear(); + _isCancelled = false; + _boxArea = Rectangle.Empty; + _isFirstPointSelected = false; + } + + public void DocumentViewChanged() { } + + public void DrawOverDocument() { } +} diff --git a/Samples/Editor/Tools/Circle.cs b/Samples/Editor/Tools/Circle.cs new file mode 100644 index 000000000..d3257dc59 --- /dev/null +++ b/Samples/Editor/Tools/Circle.cs @@ -0,0 +1,208 @@ +using System.Globalization; +using System.Numerics; +using ImGuiNET; +using SadConsole.Components; +using SadConsole.Editor.GuiParts.Tools; +using SadConsole.Editor.Model; +using SadConsole.ImGuiSystem; + +namespace SadConsole.Editor.Tools; + +internal class Circle : ITool, IOverlay +{ + private bool _isFirstPointSelected = false; + private Rectangle _boxArea; + private Point _firstPoint; + private Point _secondPoint; + private Overlay _toolOverlay = new(); + private bool _isCancelled; + + private GuiParts.Tools.ShapeSettings.Settings _shapeSettings; + + public string Name => "Circle"; + + public string Description => """ + Draws a circle. + + The border and fill of the circle can be customized. + + Depress the left mouse button to start drawing. Hold down the button and drag the mouse to draw the circle. Let go of the button to finish drawing. + + To cancel drawing, depress the right mouse button or press the ESC key. + """; + + public Overlay Overlay => _toolOverlay; + + public void BuildSettingsPanel(ImGuiRenderer renderer) + { + ImGuiWidgets.BeginGroupPanel("Settings"); + + IScreenSurface surface = ImGuiCore.State.GetOpenDocument().Surface; + + //GuiParts.Tools.SettingsTable.BeginTable("toolsettings"); + + ImGui.Checkbox("Has Border", ref _shapeSettings.HasBorder); + //GuiParts.Tools.SettingsTable.DrawCheckbox("Has Border", "##hasborder", ref _shapeSettings.HasBorder); + if (_shapeSettings.HasBorder) + { + _shapeSettings.BorderGlyph ??= new ColoredGlyph(); + + // Data for border settings + Vector4 foreground = _shapeSettings.BorderGlyph.Foreground.ToVector4(); + Vector4 background = _shapeSettings.BorderGlyph.Background.ToVector4(); + Mirror mirror = _shapeSettings.BorderGlyph.Mirror; + int glyph = _shapeSettings.BorderGlyph.Glyph; + + ImGuiWidgets.BeginGroupPanel("Border"); + GuiParts.Tools.SettingsTable.BeginTable("bordersettings"); + + GuiParts.Tools.SettingsTable.DrawColor("Foreground:", "##fore", ref foreground, surface.Surface.DefaultForeground.ToVector4(), out bool colorRightClicked); + if (colorRightClicked) + (background, foreground) = (foreground, background); + + GuiParts.Tools.SettingsTable.DrawColor("Background:", "##back", ref background, surface.Surface.DefaultBackground.ToVector4(), out colorRightClicked); + if (colorRightClicked) + (background, foreground) = (foreground, background); + + GuiParts.Tools.SettingsTable.DrawMirror("Mirror:", "##mirror", ref mirror); + + GuiParts.Tools.SettingsTable.DrawFontGlyph("Glyph:", "##glyph", ref glyph, foreground, background, surface.Font, renderer); + GuiParts.Tools.SettingsTable.EndTable(); + if (ImGui.CollapsingHeader("Ignore Options##border")) + { + GuiParts.Tools.SettingsTable.BeginTable("bordersettings_ignore"); + GuiParts.Tools.SettingsTable.DrawCheckbox("Ignore Foreground", "##ignore_border_foreground", ref _shapeSettings.IgnoreBorderForeground); + GuiParts.Tools.SettingsTable.DrawCheckbox("Ignore Background", "##ignore_border_background", ref _shapeSettings.IgnoreBorderBackground); + GuiParts.Tools.SettingsTable.DrawCheckbox("Ignore Mirror", "##ignore_border_mirror", ref _shapeSettings.IgnoreBorderMirror); + GuiParts.Tools.SettingsTable.DrawCheckbox("Ignore Glyph", "##ignore_border_glyph", ref _shapeSettings.IgnoreBorderGlyph); + GuiParts.Tools.SettingsTable.EndTable(); + } + // Store the altered settings + _shapeSettings.BorderGlyph.Foreground = foreground.ToColor(); + _shapeSettings.BorderGlyph.Background = background.ToColor(); + _shapeSettings.BorderGlyph.Mirror = mirror; + _shapeSettings.BorderGlyph.Glyph = glyph; + + ImGuiWidgets.EndGroupPanel(); + } + + ImGui.Checkbox("Has Fill", ref _shapeSettings.HasFill); + //GuiParts.Tools.SettingsTable.DrawCheckbox("Has Border", "##hasborder", ref _shapeSettings.HasBorder); + if (_shapeSettings.HasFill) + { + + _shapeSettings.FillGlyph ??= new ColoredGlyph(); + + // Data for border settings + Vector4 foreground = _shapeSettings.FillGlyph.Foreground.ToVector4(); + Vector4 background = _shapeSettings.FillGlyph.Background.ToVector4(); + Mirror mirror = _shapeSettings.FillGlyph.Mirror; + int glyph = _shapeSettings.FillGlyph.Glyph; + + ImGuiWidgets.BeginGroupPanel("Border"); + GuiParts.Tools.SettingsTable.BeginTable("bordersettings"); + + GuiParts.Tools.SettingsTable.DrawColor("Foreground:", "##fore", ref foreground, surface.Surface.DefaultForeground.ToVector4(), out bool colorRightClicked); + if (colorRightClicked) + (background, foreground) = (foreground, background); + + GuiParts.Tools.SettingsTable.DrawColor("Background:", "##back", ref background, surface.Surface.DefaultBackground.ToVector4(), out colorRightClicked); + if (colorRightClicked) + (background, foreground) = (foreground, background); + + GuiParts.Tools.SettingsTable.DrawMirror("Mirror:", "##mirror", ref mirror); + + GuiParts.Tools.SettingsTable.DrawFontGlyph("Glyph:", "##glyph", ref glyph, foreground, background, surface.Font, renderer); + GuiParts.Tools.SettingsTable.EndTable(); + if (ImGui.CollapsingHeader("Ignore Options##fill")) + { + GuiParts.Tools.SettingsTable.BeginTable("fillsettings_ignore"); + GuiParts.Tools.SettingsTable.DrawCheckbox("Ignore Foreground", "##ignore_Fill_foreground", ref _shapeSettings.IgnoreFillForeground); + GuiParts.Tools.SettingsTable.DrawCheckbox("Ignore Background", "##ignore_Fill_background", ref _shapeSettings.IgnoreFillBackground); + GuiParts.Tools.SettingsTable.DrawCheckbox("Ignore Mirror", "##ignore_Fill_mirror", ref _shapeSettings.IgnoreFillMirror); + GuiParts.Tools.SettingsTable.DrawCheckbox("Ignore Glyph", "##ignore_Fill_glyph", ref _shapeSettings.IgnoreFillGlyph); + GuiParts.Tools.SettingsTable.EndTable(); + } + + // Store the altered settings + _shapeSettings.FillGlyph.Foreground = foreground.ToColor(); + _shapeSettings.FillGlyph.Background = background.ToColor(); + _shapeSettings.FillGlyph.Mirror = mirror; + _shapeSettings.FillGlyph.Glyph = glyph; + + ImGuiWidgets.EndGroupPanel(); + } + + ImGuiWidgets.EndGroupPanel(); + } + + public void MouseOver(IScreenSurface surface, Point hoveredCellPosition, bool isActive, ImGuiRenderer renderer) + { + if (ImGuiCore.State.IsPopupOpen) return; + + GuiParts.Tools.ToolHelpers.HighlightCell(hoveredCellPosition, surface.Surface.ViewPosition, surface.FontSize, Color.Green); + + // No settings to draw, exit + if (!_shapeSettings.HasFill && !_shapeSettings.HasBorder) + return; + + // Cancelled but left mouse finally released, exit cancelled + if (_isCancelled && ImGui.IsMouseReleased(ImGuiMouseButton.Left)) + _isCancelled = false; + + // Cancelled + if (ImGui.IsMouseDown(ImGuiMouseButton.Left) && (ImGui.IsMouseClicked(ImGuiMouseButton.Right) || ImGui.IsKeyReleased(ImGuiKey.Escape))) + { + OnDeselected(); + _isCancelled = true; + Overlay.Surface.Clear(); + } + + if (_isCancelled) + return; + + if (ImGui.IsMouseDown(ImGuiMouseButton.Left) && isActive) + { + if (!_isFirstPointSelected) + { + _isFirstPointSelected = true; + + _firstPoint = hoveredCellPosition - surface.Surface.ViewPosition; + } + + _secondPoint = hoveredCellPosition - surface.Surface.ViewPosition; + + _boxArea = new(new Point(Math.Min(_firstPoint.X, _secondPoint.X), Math.Min(_firstPoint.Y, _secondPoint.Y)), + new Point(Math.Max(_firstPoint.X, _secondPoint.X), Math.Max(_firstPoint.Y, _secondPoint.Y))); + + Overlay.Surface.Clear(); + Overlay.Surface.DrawCircle(_boxArea, _shapeSettings.ToShapeParameters()); + } + else if (ImGui.IsMouseReleased(ImGuiMouseButton.Left)) + { + if (_boxArea != Rectangle.Empty) + { + surface.Surface.DrawCircle(_boxArea.Translate(surface.Surface.ViewPosition), _shapeSettings.ToShapeParameters()); + Overlay.Surface.Clear(); + } + + OnDeselected(); + } + } + + public void OnSelected() + { + + } + + public void OnDeselected() + { + _isCancelled = false; + _boxArea = Rectangle.Empty; + _isFirstPointSelected = false; + } + + public void DocumentViewChanged() { } + + public void DrawOverDocument() { } +} diff --git a/Samples/Editor/Tools/Empty.cs b/Samples/Editor/Tools/Empty.cs new file mode 100644 index 000000000..e2a157d68 --- /dev/null +++ b/Samples/Editor/Tools/Empty.cs @@ -0,0 +1,84 @@ +using System; +using System.Numerics; +using ImGuiNET; +using SadConsole.Components; +using SadConsole.Editor.GuiParts.Tools; +using SadConsole.Editor.Model; +using SadConsole.ImGuiSystem; + +namespace SadConsole.Editor.Tools; + +internal class Empty : ITool, IOverlay +{ + private Color _emptyCellColor = Color.NavajoWhite; + private nint _gridImage = -1; + private Overlay _toolOverlay = new(); + + public string Name => "Empty Cells"; + + public Overlay Overlay => _toolOverlay; + + public string Description => "Empties existing cells."; + + public void BuildSettingsPanel(ImGuiRenderer renderer) + { + } + + public void MouseOver(IScreenSurface surface, Point hoveredCellPosition, bool isActive, ImGuiRenderer renderer) + { + ToolHelpers.HighlightCell(hoveredCellPosition, surface.Surface.ViewPosition, surface.FontSize, Color.Green); + + if (!isActive) return; + + if (ImGui.IsMouseDown(ImGuiMouseButton.Left)) + { + surface.Surface[hoveredCellPosition].Clear(); + surface.Surface[hoveredCellPosition].Glyph = 0; + surface.Surface.IsDirty = true; + + Overlay.Surface.Surface[hoveredCellPosition.X - surface.Surface.ViewPosition.X, hoveredCellPosition.Y - surface.Surface.ViewPosition.Y].Background = _emptyCellColor; + Overlay.Surface.IsDirty = true; + } + } + + public void OnSelected() + { + DocumentViewChanged(); + } + + public void OnDeselected() { } + + public void DocumentViewChanged() + { + IScreenSurface surface = ImGuiCore.State.GetOpenDocument().Surface; + + Overlay.Update(surface, TimeSpan.Zero); + + Overlay.Surface.Surface.DefaultBackground = new Color(0.5f, 0.5f, 0.5f, 0.5f); + Overlay.Surface.Surface.Clear(); + + var clearCell = new ColoredGlyph(surface.Surface.DefaultForeground, surface.Surface.DefaultBackground, 0); + + for (int index = 0; index < Overlay.Surface.Surface.Count; index++) + { + ColoredGlyphBase renderCell = surface.Surface[(Point.FromIndex(index, Overlay.Surface.Surface.Width) + surface.Surface.ViewPosition).ToIndex(surface.Surface.Width)]; + + if (renderCell.Foreground == clearCell.Foreground && + renderCell.Background == clearCell.Background && + renderCell.Glyph == clearCell.Glyph) + + Overlay.Surface.Surface[index].Background = _emptyCellColor; + } + + //Overlay.Surface.Render(TimeSpan.Zero); + //_gridImage = ImGuiCore.Renderer.BindTexture(((Host.GameTexture)_gridSurface.Renderer!.Output).Texture); + } + + public void DrawOverDocument() + { + //Vector2 min = ImGui.GetItemRectMin(); + //Vector2 max = ImGui.GetItemRectMax(); + //ImDrawListPtr drawList = ImGui.GetWindowDrawList(); + //drawList.AddImage(_gridImage, min, max); + } +} diff --git a/Samples/Editor/Tools/Fill.cs b/Samples/Editor/Tools/Fill.cs index ae98dfda2..3cea9bfe5 100644 --- a/Samples/Editor/Tools/Fill.cs +++ b/Samples/Editor/Tools/Fill.cs @@ -1,119 +1,63 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using System.Text; -using System.Threading.Tasks; +using System.Numerics; using ImGuiNET; -using SadConsole.Ansi; +using SadConsole.Editor.GuiParts.Tools; using SadConsole.Editor.Model; using SadConsole.ImGuiSystem; -using SadRogue.Primitives; namespace SadConsole.Editor.Tools; + internal class Fill : ITool { public string Name => "Fill"; - private ColoredGlyph _tip = CommonToolSettings.Tip; + public string Description => """ + Fills an area of the surface. + + Use the left-mouse button to fill. + + The right-mouse button changes the current fill tip to the foreground, background, and glyph, that is under the cursor. + """; public void BuildSettingsPanel(ImGuiRenderer renderer) { - ImGui.TextDisabled("(?)"); - if (ImGui.IsItemHovered()) - { - ImGui.BeginTooltip(); - ImGui.PushTextWrapPos(ImGui.GetFontSize() * 25.0f); - ImGui.TextUnformatted("Fills an area of the surface.\n\nUse the left-mouse button to fill.\n\nThe right-mouse button changes the current fill tip to the foreground, background, and glyph, that is under the cursor."); - ImGui.PopTextWrapPos(); - ImGui.EndTooltip(); - } - ImGuiWidgets.BeginGroupPanel("Settings"); - if (ImGui.BeginTable("table1", 2)) - { - ImGui.TableSetupColumn("one", ImGuiTableColumnFlags.WidthFixed); - ImGui.TableSetupColumn("two"); - - System.Numerics.Vector4 color = _tip.Foreground.ToVector4(); - - // Foreground - ImGui.TableNextRow(); - ImGui.TableSetColumnIndex(0); - ImGui.AlignTextToFramePadding(); - ImGui.Text("Foreground:"); - - ImGui.TableSetColumnIndex(1); - ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X); - if (ImGui.ColorEdit4("##fore", ref color, ImGuiColorEditFlags.AlphaPreviewHalf | ImGuiColorEditFlags.NoInputs)) - _tip.Foreground = color.ToColor(); - - // Background - ImGui.TableNextRow(); - ImGui.TableSetColumnIndex(0); - color = _tip.Background.ToVector4(); - ImGui.AlignTextToFramePadding(); - ImGui.Text("Background:"); - - ImGui.TableSetColumnIndex(1); - ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X); - if (ImGui.ColorEdit4("##back", ref color, ImGuiColorEditFlags.AlphaPreviewHalf | ImGuiColorEditFlags.NoInputs)) - _tip.Background = color.ToColor(); - - // Glyph - ImGui.TableNextRow(); - ImGui.TableSetColumnIndex(0); - ImGui.AlignTextToFramePadding(); - ImGui.Text("Glyph:"); - - ImGui.TableSetColumnIndex(1); - - var surface = (ImGuiCore.State.GetOpenDocument() as IDocumentSurface).Surface; - - int glyph = _tip.Glyph; - if (ImGui.InputInt("##glyphinput", ref glyph, 1)) - { - _tip.Glyph = Math.Clamp(glyph, 0, surface.Font.TotalGlyphs - 1); - } - ImGui.SameLine(); - + Vector4 foreground = SharedToolSettings.Tip.Foreground.ToVector4(); + Vector4 background = SharedToolSettings.Tip.Background.ToVector4(); + Mirror mirror = SharedToolSettings.Tip.Mirror; + int glyph = SharedToolSettings.Tip.Glyph; + IScreenSurface surface = ImGuiCore.State.GetOpenDocument().Surface; - var fontTexture = renderer.BindTexture(((Host.GameTexture)surface.Font.Image).Texture); - var rect = surface.Font.GetGlyphSourceRectangle(_tip.Glyph); - var textureSize = new SadRogue.Primitives.Point(surface.Font.Image.Width, surface.Font.Image.Height); + SettingsTable.DrawCommonSettings("fillsettings", true, true, true, true, true, + ref foreground, surface.Surface.DefaultForeground.ToVector4(), + ref background, surface.Surface.DefaultBackground.ToVector4(), + ref mirror, + ref glyph, surface.Font, renderer); - var renderAreaSize = surface.Font.GetFontSize(IFont.Sizes.Two).ToVector2(); - - if (ImGui.ImageButton(fontTexture, - renderAreaSize, - rect.Position.ToUV(textureSize), (rect.Position + rect.Size).ToUV(textureSize), - 0, _tip.Background.ToVector4(), _tip.Foreground.ToVector4())) - { - ImGui.OpenPopup("glyph_select"); - } + SharedToolSettings.Tip.Foreground = foreground.ToColor(); + SharedToolSettings.Tip.Background = background.ToColor(); + SharedToolSettings.Tip.Mirror = mirror; + SharedToolSettings.Tip.Glyph = glyph; - if (ImGui.BeginPopup("glyph_select")) - { - ImGui.Text("Select your glyph"); - ImGui.EndPopup(); - } - - ImGui.EndTable(); - } ImGuiWidgets.EndGroupPanel(); } - public void MouseOver(IScreenSurface surface, SadRogue.Primitives.Point hoveredCellPosition, ImGuiRenderer renderer) + public void MouseOver(IScreenSurface surface, Point hoveredCellPosition, bool isActive, ImGuiRenderer renderer) { + if (ImGuiCore.State.IsPopupOpen) return; + + ToolHelpers.HighlightCell(hoveredCellPosition, surface.Surface.ViewPosition, surface.FontSize, Color.Green); + + if (!isActive) return; + if (ImGui.IsMouseDown(ImGuiMouseButton.Left)) { - ColoredGlyph cellToMatch = new ColoredGlyph(); - ColoredGlyph currentFillCell = _tip; + ColoredGlyph cellToMatch = new(); + ColoredGlyphBase currentFillCell = SharedToolSettings.Tip; surface.Surface[hoveredCellPosition].CopyAppearanceTo(cellToMatch); - Func isTargetCell = (c) => + Func isTargetCell = (c) => { if (c.Glyph == 0 && cellToMatch.Glyph == 0) return c.Background == cellToMatch.Background; @@ -124,17 +68,17 @@ public void MouseOver(IScreenSurface surface, SadRogue.Primitives.Point hoveredC c.Mirror == cellToMatch.Mirror; }; - Action fillCell = (c) => + Action fillCell = (c) => { currentFillCell.CopyAppearanceTo(c); //console.TextSurface.SetEffect(c, _currentFillCell.Effect); }; - System.Collections.Generic.List cells = new System.Collections.Generic.List(surface.Surface); + List cells = new List(surface.Surface); - Func> getConnectedCells = (c) => + Func> getConnectedCells = (c) => { - Algorithms.NodeConnections connections = new Algorithms.NodeConnections(); + Algorithms.NodeConnections connections = new Algorithms.NodeConnections(); var position = Point.FromIndex(cells.IndexOf(c), surface.Surface.Width); @@ -147,15 +91,23 @@ public void MouseOver(IScreenSurface surface, SadRogue.Primitives.Point hoveredC }; if (!isTargetCell(currentFillCell)) - SadConsole.Algorithms.FloodFill(surface.Surface[hoveredCellPosition], isTargetCell, fillCell, getConnectedCells); + SadConsole.Algorithms.FloodFill(surface.Surface[hoveredCellPosition], isTargetCell, fillCell, getConnectedCells); surface.Surface.IsDirty = true; } else if (ImGui.IsMouseDown(ImGuiMouseButton.Right)) { - surface.Surface[hoveredCellPosition].CopyAppearanceTo(_tip); + surface.Surface[hoveredCellPosition].CopyAppearanceTo(SharedToolSettings.Tip); surface.IsDirty = true; } } + + public void OnSelected() { } + + public void OnDeselected() { } + + public void DocumentViewChanged() { } + + public void DrawOverDocument() { } } diff --git a/Samples/Editor/Tools/IOverlay.cs b/Samples/Editor/Tools/IOverlay.cs new file mode 100644 index 000000000..c05e87174 --- /dev/null +++ b/Samples/Editor/Tools/IOverlay.cs @@ -0,0 +1,8 @@ +using SadConsole.Components; + +namespace SadConsole.Editor.Tools; + +public interface IOverlay +{ + public Overlay Overlay { get; } +} diff --git a/Samples/Editor/Tools/ITool.cs b/Samples/Editor/Tools/ITool.cs index 834ff8918..69bfbd710 100644 --- a/Samples/Editor/Tools/ITool.cs +++ b/Samples/Editor/Tools/ITool.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using SadConsole.ImGuiSystem; +using SadConsole.ImGuiSystem; namespace SadConsole.Editor.Tools; @@ -11,7 +6,17 @@ public interface ITool { string Name { get; } + string Description { get; } + void BuildSettingsPanel(ImGuiRenderer renderer); - void MouseOver(IScreenSurface surface, SadRogue.Primitives.Point hoveredCellPosition, ImGuiRenderer renderer); + void MouseOver(IScreenSurface surface, Point hoveredCellPosition, bool isActive, ImGuiRenderer renderer); + + void OnSelected(); + + void OnDeselected(); + + void DocumentViewChanged(); + + void DrawOverDocument(); } diff --git a/Samples/Editor/Tools/Info.cs b/Samples/Editor/Tools/Info.cs new file mode 100644 index 000000000..9b214f3ff --- /dev/null +++ b/Samples/Editor/Tools/Info.cs @@ -0,0 +1,86 @@ +using System.Numerics; +using ImGuiNET; +using SadConsole.Editor.Model; +using SadConsole.ImGuiSystem; + +namespace SadConsole.Editor.Tools; +internal class Info : ITool +{ + public string Name => "Info"; + + public string Description => "Displays information about the glyph under the cursor."; + + private ColoredGlyph _tip = SharedToolSettings.Tip; + + public bool IsHoverShowing; + + public void BuildSettingsPanel(ImGuiRenderer renderer) + { + } + + public void MouseOver(IScreenSurface surface, Point hoveredCellPosition, bool isActive, ImGuiRenderer renderer) + { + if (ImGuiCore.State.IsPopupOpen) return; + + Vector2 textureArea = ImGui.GetItemRectSize(); + Vector2 itemRectMin = ImGui.GetItemRectMin(); + + // Fix this up... + if (ImGui.IsMouseHoveringRect(itemRectMin, itemRectMin + textureArea))//new Vector2(gameTexture.Texture.Width, gameTexture.Texture.Height))) + //if (ImGui.IsMouseHoveringRect(pos1, pos1 + new Vector2(gameTexture.Texture.Width, gameTexture.Texture.Height))) + { + IsHoverShowing = true; + ImGui.OpenPopup("surface_cell"); + + Vector2 mousePosition = ImGui.GetMousePos(); + Vector2 pos = mousePosition - ImGui.GetItemRectMin(); + //if (surface.AbsoluteArea.WithPosition((0, 0)).Contains(new SadRogue.Primitives.Point((int)pos.X, (int)pos.Y))) + { + //SadRogue.Primitives.Point cellPosition = new SadRogue.Primitives.Point((int)pos.X, (int)pos.Y).PixelLocationToSurface(surface.FontSize) + surface.Surface.ViewPosition; + Point cellPosition = hoveredCellPosition; + // TODO: Keep this on the screen + ImGui.PushStyleVar(ImGuiStyleVar.WindowRounding, 4f); + ImGui.BeginTooltip(); + + var fontTexture = renderer.BindTexture(((Host.GameTexture)surface.Font.Image).Texture); + var rect = surface.Font.GetGlyphSourceRectangle(surface.Surface[cellPosition].Glyph); + var textureSize = new SadRogue.Primitives.Point(surface.Font.Image.Width, surface.Font.Image.Height); + + ImGui.Image(fontTexture, surface.Font.GetFontSize(IFont.Sizes.Two).ToVector2(), rect.Position.ToUV(textureSize), (rect.Position + rect.Size).ToUV(textureSize)); + var cursorPosForSecondFont = ImGui.GetCursorPos(); + ImGui.SameLine(); + var cursorPosForDetails = ImGui.GetCursorPos(); + ImGui.SetCursorPos(cursorPosForSecondFont); + + var rectSolid = surface.Font.SolidGlyphRectangle; + ImGui.Image(fontTexture, surface.Font.GetFontSize(IFont.Sizes.Two).ToVector2(), rectSolid.Position.ToUV(textureSize), (rectSolid.Position + rectSolid.Size).ToUV(textureSize), surface.Surface[cellPosition].Background.ToVector4()); + ImGui.SetCursorPos(cursorPosForSecondFont); + ImGui.Image(fontTexture, surface.Font.GetFontSize(IFont.Sizes.Two).ToVector2(), rect.Position.ToUV(textureSize), (rect.Position + rect.Size).ToUV(textureSize), surface.Surface[cellPosition].Foreground.ToVector4()); + + ImGui.SetCursorPos(cursorPosForDetails); + ImGui.BeginGroup(); + { + ImGui.Text($"Glyph: {surface.Surface[cellPosition].Glyph}"); + ImGui.Text($"Foreground: "); + ImGui.SameLine(); + ImGui.ColorButton("surface_cell_color", surface.Surface[cellPosition].Foreground.ToVector4(), ImGuiColorEditFlags.NoPicker); + ImGui.Text($"Background: "); + ImGui.SameLine(); + ImGui.ColorButton("surface_cell_color", surface.Surface[cellPosition].Background.ToVector4(), ImGuiColorEditFlags.NoPicker); + } + ImGui.EndGroup(); + + ImGui.EndTooltip(); + ImGui.PopStyleVar(); + } + } + } + + public void OnSelected() { } + + public void OnDeselected() { } + + public void DocumentViewChanged() { } + + public void DrawOverDocument() { } +} diff --git a/Samples/Editor/Tools/Line.cs b/Samples/Editor/Tools/Line.cs new file mode 100644 index 000000000..b35efb74e --- /dev/null +++ b/Samples/Editor/Tools/Line.cs @@ -0,0 +1,135 @@ +using System.Numerics; +using ImGuiNET; +using SadConsole.Components; +using SadConsole.Editor.GuiParts.Tools; +using SadConsole.Editor.Model; +using SadConsole.ImGuiSystem; + +namespace SadConsole.Editor.Tools; + +internal class Line : ITool, IOverlay +{ + private bool _isDrawing = false; + private bool _isFirstPointSelected = false; + private Point _firstPoint; + private Point _secondPoint; + private Overlay _toolOverlay = new(); + private bool _isCancelled; + + public string Name => "Line"; + + public string Description => """ + Draws a line. + + The line can be set to use a connected glyph. + + Depress the left mouse button to start drawing. Hold down the button and drag the mouse to draw the line. Let go of the button to finish drawing. + + To cancel drawing, depress the right mouse button or press the ESC key. + """; + + public Overlay Overlay => _toolOverlay; + + public void BuildSettingsPanel(ImGuiRenderer renderer) + { + ImGuiWidgets.BeginGroupPanel("Settings"); + + Vector4 foreground = SharedToolSettings.Tip.Foreground.ToVector4(); + Vector4 background = SharedToolSettings.Tip.Background.ToVector4(); + Mirror mirror = SharedToolSettings.Tip.Mirror; + int glyph = SharedToolSettings.Tip.Glyph; + IScreenSurface surface = ImGuiCore.State.GetOpenDocument().Surface; + + SettingsTable.DrawCommonSettings("fillsettings", true, true, true, true, true, + ref foreground, surface.Surface.DefaultForeground.ToVector4(), + ref background, surface.Surface.DefaultBackground.ToVector4(), + ref mirror, + ref glyph, surface.Font, renderer); + + SharedToolSettings.Tip.Foreground = foreground.ToColor(); + SharedToolSettings.Tip.Background = background.ToColor(); + SharedToolSettings.Tip.Mirror = mirror; + SharedToolSettings.Tip.Glyph = glyph; + + ImGuiWidgets.EndGroupPanel(); + } + + public void MouseOver(IScreenSurface surface, Point hoveredCellPosition, bool isActive, ImGuiRenderer renderer) + { + if (ImGuiCore.State.IsPopupOpen) return; + + GuiParts.Tools.ToolHelpers.HighlightCell(hoveredCellPosition, surface.Surface.ViewPosition, surface.FontSize, Color.Green); + + if (!_isDrawing) + { + // Cancelled but left mouse finally released, exit cancelled + if (_isCancelled && ImGui.IsMouseReleased(ImGuiMouseButton.Left)) + _isCancelled = false; + + // Cancelled + if (ImGui.IsMouseDown(ImGuiMouseButton.Left) && (ImGui.IsMouseClicked(ImGuiMouseButton.Right) || ImGui.IsKeyReleased(ImGuiKey.Escape))) + { + OnDeselected(); + _isCancelled = true; + Overlay.Surface.Clear(); + } + + if (_isCancelled) + return; + + if (ImGui.IsMouseDown(ImGuiMouseButton.Left) && isActive) + { + if (!_isFirstPointSelected) + { + _isFirstPointSelected = true; + + _firstPoint = hoveredCellPosition - surface.Surface.ViewPosition; + } + + _secondPoint = hoveredCellPosition - surface.Surface.ViewPosition; + + Overlay.Surface.Clear(); + Overlay.Surface.DrawLine(_firstPoint, + _secondPoint, + SharedToolSettings.Tip.Glyph, + SharedToolSettings.Tip.Foreground, + SharedToolSettings.Tip.Background, + SharedToolSettings.Tip.Mirror); + } + else if (ImGui.IsMouseReleased(ImGuiMouseButton.Left)) + { + if (_firstPoint != Point.None) + { + surface.Surface.DrawLine(_firstPoint + surface.Surface.ViewPosition, + _secondPoint + surface.Surface.ViewPosition, + SharedToolSettings.Tip.Glyph, + SharedToolSettings.Tip.Foreground, + SharedToolSettings.Tip.Background, + SharedToolSettings.Tip.Mirror); + + + Overlay.Surface.Clear(); + } + + OnDeselected(); + } + } + } + + public void OnSelected() + { + + } + + public void OnDeselected() + { + _isCancelled = false; + _isDrawing = false; + _firstPoint = Point.None; + _isFirstPointSelected = false; + } + + public void DocumentViewChanged() { } + + public void DrawOverDocument() { } +} diff --git a/Samples/Editor/Tools/Pencil.cs b/Samples/Editor/Tools/Pencil.cs index f352175b6..a07c07428 100644 --- a/Samples/Editor/Tools/Pencil.cs +++ b/Samples/Editor/Tools/Pencil.cs @@ -1,121 +1,72 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using System.Text; -using System.Threading.Tasks; +using System.Numerics; using ImGuiNET; -using SadConsole.Ansi; +using SadConsole.Editor.GuiParts.Tools; using SadConsole.Editor.Model; using SadConsole.ImGuiSystem; namespace SadConsole.Editor.Tools; + internal class Pencil : ITool { public string Name => "Pencil"; - private ColoredGlyph _tip = CommonToolSettings.Tip; + public string Description => """ + Draw glyphs on the surface. + + Use the left-mouse button to draw. + + The right-mouse button changes the current pencil tip to the foreground, background, and glyph, that is under the cursor. + """; public void BuildSettingsPanel(ImGuiRenderer renderer) { - ImGui.TextDisabled("(?)"); - if (ImGui.IsItemHovered()) - { - ImGui.BeginTooltip(); - ImGui.PushTextWrapPos(ImGui.GetFontSize() * 25.0f); - ImGui.TextUnformatted("Draw glyphs on the surface.\n\nUse the left-mouse button to draw.\n\nThe right-mouse button changes the current pencil tip to the foreground, background, and glyph, that is under the cursor."); - ImGui.PopTextWrapPos(); - ImGui.EndTooltip(); - } - ImGuiWidgets.BeginGroupPanel("Settings"); - if (ImGui.BeginTable("table1", 2)) - { - ImGui.TableSetupColumn("one", ImGuiTableColumnFlags.WidthFixed); - ImGui.TableSetupColumn("two"); - - System.Numerics.Vector4 color = _tip.Foreground.ToVector4(); - - // Foreground - ImGui.TableNextRow(); - ImGui.TableSetColumnIndex(0); - ImGui.AlignTextToFramePadding(); - ImGui.Text("Foreground:"); - - ImGui.TableSetColumnIndex(1); - ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X); - if (ImGui.ColorEdit4("##fore", ref color, ImGuiColorEditFlags.AlphaPreviewHalf | ImGuiColorEditFlags.NoInputs)) - _tip.Foreground = color.ToColor(); - - // Background - ImGui.TableNextRow(); - ImGui.TableSetColumnIndex(0); - color = _tip.Background.ToVector4(); - ImGui.AlignTextToFramePadding(); - ImGui.Text("Background:"); - - ImGui.TableSetColumnIndex(1); - ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X); - if (ImGui.ColorEdit4("##back", ref color, ImGuiColorEditFlags.AlphaPreviewHalf | ImGuiColorEditFlags.NoInputs)) - _tip.Background = color.ToColor(); - - // Glyph - ImGui.TableNextRow(); - ImGui.TableSetColumnIndex(0); - ImGui.AlignTextToFramePadding(); - ImGui.Text("Glyph:"); - - ImGui.TableSetColumnIndex(1); - - var surface = (ImGuiCore.State.GetOpenDocument() as IDocumentSurface).Surface; - - int glyph = _tip.Glyph; - if (ImGui.InputInt("##glyphinput", ref glyph, 1)) - { - _tip.Glyph = Math.Clamp(glyph, 0, surface.Font.TotalGlyphs - 1); - } - ImGui.SameLine(); - - - var fontTexture = renderer.BindTexture(((Host.GameTexture)surface.Font.Image).Texture); - var rect = surface.Font.GetGlyphSourceRectangle(_tip.Glyph); - var textureSize = new SadRogue.Primitives.Point(surface.Font.Image.Width, surface.Font.Image.Height); - - var renderAreaSize = surface.Font.GetFontSize(IFont.Sizes.Two).ToVector2(); - - if (ImGui.ImageButton(fontTexture, - renderAreaSize, - rect.Position.ToUV(textureSize), (rect.Position + rect.Size).ToUV(textureSize), - 0, _tip.Background.ToVector4(), _tip.Foreground.ToVector4())) - { - ImGui.OpenPopup("glyph_select"); - } - - if (ImGui.BeginPopup("glyph_select")) - { - ImGui.Text("Select your glyph"); - ImGui.EndPopup(); - } - - ImGui.EndTable(); - } + Vector4 foreground = SharedToolSettings.Tip.Foreground.ToVector4(); + Vector4 background = SharedToolSettings.Tip.Background.ToVector4(); + int glyph = SharedToolSettings.Tip.Glyph; + Mirror mirror = SharedToolSettings.Tip.Mirror; + IScreenSurface surface = ImGuiCore.State.GetOpenDocument().Surface; + + SettingsTable.DrawCommonSettings("pencilsettings", true, true, true, true, true, + ref foreground, surface.Surface.DefaultForeground.ToVector4(), + ref background, surface.Surface.DefaultBackground.ToVector4(), + ref mirror, + ref glyph, surface.Font, renderer); + + SharedToolSettings.Tip.Foreground = foreground.ToColor(); + SharedToolSettings.Tip.Background = background.ToColor(); + SharedToolSettings.Tip.Mirror = mirror; + SharedToolSettings.Tip.Glyph = glyph; + ImGuiWidgets.EndGroupPanel(); } - public void MouseOver(IScreenSurface surface, SadRogue.Primitives.Point hoveredCellPosition, ImGuiRenderer renderer) + public void MouseOver(IScreenSurface surface, Point hoveredCellPosition, bool isActive, ImGuiRenderer renderer) { + if (ImGuiCore.State.IsPopupOpen) return; + + ToolHelpers.HighlightCell(hoveredCellPosition, surface.Surface.ViewPosition, surface.FontSize, Color.Green); + + if (!isActive) return; + if (ImGui.IsMouseDown(ImGuiMouseButton.Left)) { surface.Surface[hoveredCellPosition].Clear(); - _tip.CopyAppearanceTo(surface.Surface[hoveredCellPosition]); + SharedToolSettings.Tip.CopyAppearanceTo(surface.Surface[hoveredCellPosition]); surface.IsDirty = true; } else if (ImGui.IsMouseDown(ImGuiMouseButton.Right)) { - surface.Surface[hoveredCellPosition].CopyAppearanceTo(_tip); - - surface.IsDirty = true; + surface.Surface[hoveredCellPosition].CopyAppearanceTo(SharedToolSettings.Tip); } } + + public void OnSelected() { } + + public void OnDeselected() { } + + public void DocumentViewChanged() { } + + public void DrawOverDocument() { } } diff --git a/Samples/Editor/Tools/Recolor.cs b/Samples/Editor/Tools/Recolor.cs new file mode 100644 index 000000000..bd72e5c33 --- /dev/null +++ b/Samples/Editor/Tools/Recolor.cs @@ -0,0 +1,286 @@ +using System.Numerics; +using ImGuiNET; +using SadConsole.Editor.GuiParts.Tools; +using SadConsole.Editor.Model; +using SadConsole.Editor.Windows; +using SadConsole.ImGuiSystem; + +namespace SadConsole.Editor.Tools; + +internal class Recolor : ITool +{ + private bool _isMatchForeground = false; + private bool _isMatchBackground = false; + private bool _isMatchGlyph = false; + private bool _isMatchMirror = false; + + private Vector4 _matchForeground = Color.White.ToVector4(); + private Vector4 _matchBackground = Color.Black.ToVector4(); + private int _matchGlyph = 0; + private Mirror _matchMirror = Mirror.None; + + private bool _isApplyForeground = false; + private bool _isApplyBackground = false; + private bool _isApplyGlyph = false; + private bool _isApplyMirror = false; + + private Vector4 _applyForeground = Color.White.ToVector4(); + private Vector4 _applyBackground = Color.Black.ToVector4(); + private int _applyGlyph = 0; + private Mirror _applyMirror = Mirror.None; + + public string Name => "Recolor"; + + public string Description => """ + Recolor the glyphs on the surface. + + Set the match conditions, then use the left-mouse button to apply the new colors based on the match conditions. + + The right-mouse button changes the current condition parameters to match the foreground, background, and glyph that is under the cursor. + """; + + public void BuildSettingsPanel(ImGuiRenderer renderer) + { + IScreenSurface surface = ImGuiCore.State.GetOpenDocument().Surface; + + ImGuiWidgets.BeginGroupPanel("Match"); + + GuiParts.Tools.SettingsTable.BeginTable("matchtable"); + + // Foreground + { + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.AlignTextToFramePadding(); + ImGui.Checkbox("Foreground", ref _isMatchForeground); + ImGui.TableSetColumnIndex(1); + + ImGui.BeginDisabled(!_isMatchForeground); + + ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X); + ImGui.ColorEdit4("##foreground_edit", ref _matchForeground, ImGuiColorEditFlags.AlphaPreviewHalf | ImGuiColorEditFlags.NoInputs); + ImGuiCore.State.CheckSetPopupOpen($"##foregroundpicker"); + + ImGui.SameLine(); + if (ImGui.Button($"Palette##foreground_edit")) + ImGuiCore.State.OpenPopup($"palettepopup##foreground_edit"); + + Color col = _matchForeground.ToColor(); + if (PalettePopup.Show($"palettepopup##foreground_edit", ref col)) + _matchForeground = col.ToVector4(); + + ImGui.EndDisabled(); + } + + // Background + { + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.AlignTextToFramePadding(); + ImGui.Checkbox("Background", ref _isMatchBackground); + ImGui.TableSetColumnIndex(1); + + ImGui.BeginDisabled(!_isMatchBackground); + + ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X); + ImGui.ColorEdit4("##background_edit", ref _matchBackground, ImGuiColorEditFlags.AlphaPreviewHalf | ImGuiColorEditFlags.NoInputs); + ImGuiCore.State.CheckSetPopupOpen($"##backgroundpicker"); + + ImGui.SameLine(); + if (ImGui.Button($"Palette##background_edit")) + ImGuiCore.State.OpenPopup($"palettepopup##background_edit"); + + Color col = _matchBackground.ToColor(); + if (PalettePopup.Show($"palettepopup##background_edit", ref col)) + _matchBackground = col.ToVector4(); + + ImGui.EndDisabled(); + } + + // Mirror + { + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.AlignTextToFramePadding(); + ImGui.Checkbox("Mirror", ref _isMatchMirror); + ImGui.TableSetColumnIndex(1); + + ImGui.BeginDisabled(!_isMatchMirror); + + int itemIndex = Model.SadConsoleTypes.Mirror.GetIndexFromValue(_matchMirror); + + if (ImGui.Combo("##mirror_edit", ref itemIndex, Model.SadConsoleTypes.Mirror.Names, Model.SadConsoleTypes.Mirror.Names.Length)) + { + _matchMirror = Model.SadConsoleTypes.Mirror.GetValueFromIndex(itemIndex); + } + + ImGui.EndDisabled(); + } + + // Glyph + { + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.AlignTextToFramePadding(); + ImGui.Checkbox("Glyph", ref _isMatchGlyph); + ImGui.TableSetColumnIndex(1); + + ImGui.BeginDisabled(!_isMatchGlyph); + + FontGlyph.DrawWithPopup(renderer, "##glyph_edit", "match_font", surface.Font, _matchForeground, _matchBackground, ref _matchGlyph,true); + + ImGui.EndDisabled(); + } + + GuiParts.Tools.SettingsTable.EndTable(); + ImGuiWidgets.EndGroupPanel(); + + + ImGuiWidgets.BeginGroupPanel("Apply"); + + GuiParts.Tools.SettingsTable.BeginTable("applytable"); + + // Foreground + { + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.AlignTextToFramePadding(); + ImGui.Checkbox("Foreground", ref _isApplyForeground); + ImGui.TableSetColumnIndex(1); + + ImGui.BeginDisabled(!_isApplyForeground); + + ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X); + ImGui.ColorEdit4("##foreground_edit", ref _applyForeground, ImGuiColorEditFlags.AlphaPreviewHalf | ImGuiColorEditFlags.NoInputs); + ImGuiCore.State.CheckSetPopupOpen($"##foregroundpicker"); + + ImGui.SameLine(); + if (ImGui.Button($"Palette##foreground_edit")) + ImGuiCore.State.OpenPopup($"palettepopup##foreground_edit"); + + Color col = _applyForeground.ToColor(); + if (PalettePopup.Show($"palettepopup##foreground_edit", ref col)) + _applyForeground = col.ToVector4(); + + ImGui.EndDisabled(); + } + + // Background + { + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.AlignTextToFramePadding(); + ImGui.Checkbox("Background", ref _isApplyBackground); + ImGui.TableSetColumnIndex(1); + + ImGui.BeginDisabled(!_isApplyBackground); + + ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X); + ImGui.ColorEdit4("##background_edit", ref _applyBackground, ImGuiColorEditFlags.AlphaPreviewHalf | ImGuiColorEditFlags.NoInputs); + ImGuiCore.State.CheckSetPopupOpen($"##backgroundpicker"); + + ImGui.SameLine(); + if (ImGui.Button($"Palette##background_edit")) + ImGuiCore.State.OpenPopup($"palettepopup##background_edit"); + + Color col = _applyBackground.ToColor(); + if (PalettePopup.Show($"palettepopup##background_edit", ref col)) + _applyBackground = col.ToVector4(); + + ImGui.EndDisabled(); + } + + // Mirror + { + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.AlignTextToFramePadding(); + ImGui.Checkbox("Mirror", ref _isApplyMirror); + ImGui.TableSetColumnIndex(1); + + ImGui.BeginDisabled(!_isApplyMirror); + + int itemIndex = Model.SadConsoleTypes.Mirror.GetIndexFromValue(_applyMirror); + + if (ImGui.Combo("##mirror_edit", ref itemIndex, Model.SadConsoleTypes.Mirror.Names, Model.SadConsoleTypes.Mirror.Names.Length)) + { + _applyMirror = Model.SadConsoleTypes.Mirror.GetValueFromIndex(itemIndex); + } + + ImGui.EndDisabled(); + } + + // Glyph + { + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.AlignTextToFramePadding(); + ImGui.Checkbox("Glyph", ref _isApplyGlyph); + ImGui.TableSetColumnIndex(1); + + ImGui.BeginDisabled(!_isApplyGlyph); + + FontGlyph.DrawWithPopup(renderer, "##glyph_edit", "apply_font", surface.Font, _applyForeground, _applyBackground, ref _applyGlyph, true); + + ImGui.EndDisabled(); + } + + GuiParts.Tools.SettingsTable.EndTable(); + ImGuiWidgets.EndGroupPanel(); + + } + + public void MouseOver(IScreenSurface surface, Point hoveredCellPosition, bool isActive, ImGuiRenderer renderer) + { + if (ImGuiCore.State.IsPopupOpen) return; + + ToolHelpers.HighlightCell(hoveredCellPosition, surface.Surface.ViewPosition, surface.FontSize, Color.Green); + + if (!isActive) return; + + if (ImGui.IsMouseDown(ImGuiMouseButton.Left)) + { + ColoredGlyphBase targetCell = surface.Surface[hoveredCellPosition]; + + bool matched = false; + + matched = _isMatchForeground || _isMatchBackground || _isMatchMirror || _isMatchGlyph; + matched &= !_isMatchForeground || (_isMatchForeground && targetCell.Foreground == _matchForeground.ToColor()); + matched &= !_isMatchBackground || (_isMatchBackground && targetCell.Background == _matchBackground.ToColor()); + matched &= !_isMatchMirror || (_isMatchMirror && targetCell.Mirror == _matchMirror); + matched &= !_isMatchGlyph || (_isMatchGlyph && targetCell.Glyph == _matchGlyph); + + // If found, apply + if (matched) + { + if (_isApplyForeground) + targetCell.Foreground = _applyForeground.ToColor(); + if (_isApplyBackground) + targetCell.Background = _applyBackground.ToColor(); + if (_isApplyMirror) + targetCell.Mirror = _applyMirror; + if (_isApplyGlyph) + targetCell.Glyph = _applyGlyph; + + surface.IsDirty = true; + } + } + else if (ImGui.IsMouseDown(ImGuiMouseButton.Right)) + { + ColoredGlyphBase tempCell = surface.Surface[hoveredCellPosition]; + + _matchForeground = tempCell.Foreground.ToVector4(); + _matchBackground = tempCell.Background.ToVector4(); + _matchMirror = tempCell.Mirror; + _matchGlyph = tempCell.Glyph; + } + } + + public void OnSelected() { } + + public void OnDeselected() { } + + public void DocumentViewChanged() { } + + public void DrawOverDocument() { } +} diff --git a/Samples/Editor/Tools/Selection.cs b/Samples/Editor/Tools/Selection.cs new file mode 100644 index 000000000..4420bd253 --- /dev/null +++ b/Samples/Editor/Tools/Selection.cs @@ -0,0 +1,176 @@ +using System.Numerics; +using ImGuiNET; +using SadConsole.Components; +using SadConsole.Editor.GuiParts.Tools; +using SadConsole.Editor.Model; +using SadConsole.ImGuiSystem; + +namespace SadConsole.Editor.Tools; + +internal class Selection : ITool, IOverlay +{ + private bool _isFirstPointSelected = false; + private Rectangle _boxArea; + private Point _firstPoint; + private bool _boxCreated; + private Overlay _toolOverlay = new(); + private bool _isCancelled; + private bool _isPasting; + CellSurface? _blueprint; + private string[] _blueprintNames = []; + private CellSurface[] _blueprintValues = []; + private int _blueprintSelection = -1; + + private ShapeSettings.Settings _shapeSettings; + + public string Name => "Selection"; + + public string Description => """ + ... + To cancel drawing, depress the right mouse button or press the ESC key. + """; + + public Overlay Overlay => _toolOverlay; + + public void BuildSettingsPanel(ImGuiRenderer renderer) + { + /* + * --- Selection --- + * BtnReset + * BtnCopy + * BtnStore + * + * --- Blueprints --- + * List Item + * List Item + * List Item + * List Item + * + * Btn + * + */ + + ImGuiWidgets.BeginGroupPanel("Settings"); + + ImGui.BeginDisabled(!_boxCreated); + + if (ImGui.Button("Reset")) + { + OnDeselected(); + } + else if (ImGui.Button("Copy")) + { + + } + else if (ImGui.Button("Store")) + { + ImGui.OpenPopup("Blueprint Name"); + } + + ImGui.EndDisabled(); + + if (_blueprint != null) + { + string blueprintName = string.Empty; + bool validateOutput(string name) => + string.IsNullOrEmpty(name) || string.IsNullOrWhiteSpace(name) || _blueprintNames.Contains(name); + + if (Windows.RenameWindow.Show("Blueprint Name", "Name", ref blueprintName, out bool accepted, validateOutput)) + { + if (accepted) + { + _blueprintNames = [.. _blueprintNames, blueprintName]; + _blueprintValues = [.. _blueprintValues, _blueprint]; + } + } + } + + ImGui.Separator(); + ImGui.Text("Stored Blueprints"); + + ImGui.ListBox("##listblueprints", ref _blueprintSelection, _blueprintNames, _blueprintNames.Length, 5); + + ImGuiWidgets.EndGroupPanel(); + } + + public void MouseOver(IScreenSurface surface, Point hoveredCellPosition, bool isActive, ImGuiRenderer renderer) + { + if (ImGuiCore.State.IsPopupOpen) return; + + GuiParts.Tools.ToolHelpers.HighlightCell(hoveredCellPosition, surface.Surface.ViewPosition, surface.FontSize, Color.Green); + + // Cancelled but left mouse finally released, exit cancelled + if (_isCancelled && ImGui.IsMouseReleased(ImGuiMouseButton.Left)) + _isCancelled = false; + + // Cancelled + if (ImGui.IsMouseDown(ImGuiMouseButton.Left) && (ImGui.IsMouseClicked(ImGuiMouseButton.Right) || ImGui.IsKeyReleased(ImGuiKey.Escape))) + { + OnDeselected(); + _isCancelled = true; + } + + if (_isCancelled) + return; + + if (_isPasting) + { + _boxArea = _boxArea.WithCenter(hoveredCellPosition - surface.Surface.ViewPosition); + Overlay.Surface.Clear(); + Overlay.Surface.DrawBox(_boxArea, ShapeParameters.CreateStyledBoxThin(Color.White)); + } + else + { + if (ImGui.IsMouseDown(ImGuiMouseButton.Left) && isActive) + { + if (!_isFirstPointSelected) + { + _isFirstPointSelected = true; + + _firstPoint = hoveredCellPosition - surface.Surface.ViewPosition; + } + + Point secondPoint = hoveredCellPosition - surface.Surface.ViewPosition; + + _boxArea = new(new Point(Math.Min(_firstPoint.X, secondPoint.X), Math.Min(_firstPoint.Y, secondPoint.Y)), + new Point(Math.Max(_firstPoint.X, secondPoint.X), Math.Max(_firstPoint.Y, secondPoint.Y))); + + Overlay.Surface.Clear(); + Overlay.Surface.DrawBox(_boxArea, ShapeParameters.CreateStyledBoxThin(Color.White)); + } + else if (ImGui.IsMouseReleased(ImGuiMouseButton.Left)) + { + if (_boxArea != Rectangle.Empty) + { + _blueprint = new(_boxArea.Width, _boxArea.Height); + surface.Surface.Copy(_boxArea.Translate(surface.Surface.ViewPosition), _blueprint, 0, 0); + _boxCreated = true; + _isFirstPointSelected = false; + } + } + else if (ImGui.IsMouseDown(ImGuiMouseButton.Right)) + { + OnDeselected(); + } + } + } + + public void OnSelected() + { + + } + + public void OnDeselected() + { + Overlay.Surface.Clear(); + _isCancelled = false; + _boxArea = Rectangle.Empty; + _isFirstPointSelected = false; + _boxCreated = false; + _blueprint = null; + } + + public void DocumentViewChanged() { } + + public void DrawOverDocument() { } +} diff --git a/Samples/Editor/Tools/CommonToolSettings.cs b/Samples/Editor/Tools/SharedToolSettings.cs similarity index 64% rename from Samples/Editor/Tools/CommonToolSettings.cs rename to Samples/Editor/Tools/SharedToolSettings.cs index f5eecde62..1d01a031e 100644 --- a/Samples/Editor/Tools/CommonToolSettings.cs +++ b/Samples/Editor/Tools/SharedToolSettings.cs @@ -6,12 +6,12 @@ namespace SadConsole.Editor.Tools; -internal static class CommonToolSettings +internal static class SharedToolSettings { public static ColoredGlyph Tip { get; set; } - static CommonToolSettings() + static SharedToolSettings() { - Tip = new ColoredGlyph(); + Tip = new ColoredGlyph() { Glyph = 1 }; } } diff --git a/Samples/Editor/Tools/Text.cs b/Samples/Editor/Tools/Text.cs new file mode 100644 index 000000000..c6c5e4809 --- /dev/null +++ b/Samples/Editor/Tools/Text.cs @@ -0,0 +1,125 @@ +using System.Numerics; +using ImGuiNET; +using SadConsole.Components; +using SadConsole.Editor.GuiParts.Tools; +using SadConsole.Editor.Model; +using SadConsole.ImGuiSystem; + +namespace SadConsole.Editor.Tools; + +internal class Text : ITool +{ + private bool _isWriting; + private Point _cursorPosition; + private Cursor? _cursor; + private Input.Keyboard _keyboard = new(); + + public string Name => "Text"; + + public string Description => """ + Write text to a surface. + + Use the left-mouse button to start writing or move the text cursor. + + The right-mouse button or the ESC key turns off the cursor. + """; + + public void BuildSettingsPanel(ImGuiRenderer renderer) + { + ImGuiWidgets.BeginGroupPanel("Settings"); + + Vector4 foreground = SharedToolSettings.Tip.Foreground.ToVector4(); + Vector4 background = SharedToolSettings.Tip.Background.ToVector4(); + int glyph = SharedToolSettings.Tip.Glyph; + Mirror mirror = SharedToolSettings.Tip.Mirror; + IScreenSurface surface = ImGuiCore.State.GetOpenDocument().Surface; + + SettingsTable.BeginTable("matchtable"); + + SettingsTable.DrawColor("Foreground", "##fore", ref foreground, surface.Surface.DefaultForeground.ToVector4(), out bool rightClicked); + + if (rightClicked) + (background, foreground) = (foreground, background); + + SettingsTable.DrawColor("Background", "##back", ref background, surface.Surface.DefaultBackground.ToVector4(), out rightClicked); + + if (rightClicked) + (background, foreground) = (foreground, background); + + SettingsTable.EndTable(); + + SharedToolSettings.Tip.Foreground = foreground.ToColor(); + SharedToolSettings.Tip.Background = background.ToColor(); + + ImGuiWidgets.EndGroupPanel(); + } + + public void MouseOver(IScreenSurface surface, Point hoveredCellPosition, bool isActive, ImGuiRenderer renderer) + { + if (ImGuiCore.State.IsPopupOpen) return; + + ToolHelpers.HighlightCell(hoveredCellPosition, surface.Surface.ViewPosition, surface.FontSize, Color.Green); + + if (_isWriting) + { + if (ImGui.IsMouseReleased(ImGuiMouseButton.Right) || ImGui.IsKeyReleased(ImGuiKey.Escape)) + { + OnDeselected(); + } + } + else + { + if (ImGui.IsMouseDown(ImGuiMouseButton.Right)) + { + surface.Surface[hoveredCellPosition].CopyAppearanceTo(SharedToolSettings.Tip); + } + } + + if (!isActive) return; + + if (ImGui.IsMouseClicked(ImGuiMouseButton.Left)) + { + _isWriting = true; + _cursorPosition = hoveredCellPosition - surface.Surface.ViewPosition; + if (_cursor is null) + { + _cursor = new() { IsVisible = true, IsEnabled = true }; + surface.SadComponents.Add(_cursor); + } + _cursor.Position = _cursorPosition; + _cursor.PrintAppearance = SharedToolSettings.Tip; + _cursor.PrintOnlyCharacterData = false; + _cursor.PrintAppearanceMatchesHost = false; + ImGuiCore.State.GetOpenDocument().Options.DisableScrolling = true; + } + } + + public void OnSelected() { } + + public void OnDeselected() + { + _keyboard.Clear(); + _isWriting = false; + + Document doc = ImGuiCore.State.GetOpenDocument(); + + if (_cursor != null) + { + doc.Surface.SadComponents.Remove(_cursor); + _cursor = null; + } + + doc.Options.DisableScrolling = false; + } + + public void DocumentViewChanged() { } + + public void DrawOverDocument() + { + if (_isWriting && _cursor != null) + { + _keyboard.Update(Game.Instance.UpdateFrameDelta); + ((Components.IComponent)_cursor).ProcessKeyboard(ImGuiCore.State.GetOpenDocument().Surface, _keyboard, out bool handled); + } + } +} diff --git a/Samples/Editor/Windows/FilePicker.cs b/Samples/Editor/Windows/FilePicker.cs new file mode 100644 index 000000000..c8149059f --- /dev/null +++ b/Samples/Editor/Windows/FilePicker.cs @@ -0,0 +1,181 @@ +using ImGuiNET; +using System.Numerics; + +namespace SadConsole.Editor.Windows; + +public class FilePicker +{ + static readonly Dictionary _filePickers = new Dictionary(); + + public string RootFolder; + public string CurrentFolder; + public string SelectedFile; + public List AllowedExtensions; + public bool OnlyAllowFolders; + + public static FilePicker GetFolderPicker(object o, string startingPath) + => GetFilePicker(o, startingPath, null, true); + + public static FilePicker GetFilePicker(object o, string startingPath, string searchFilter = null, bool onlyAllowFolders = false) + { + if (File.Exists(startingPath)) + { + startingPath = new FileInfo(startingPath).DirectoryName; + } + else if (string.IsNullOrEmpty(startingPath) || !Directory.Exists(startingPath)) + { + startingPath = Environment.CurrentDirectory; + if (string.IsNullOrEmpty(startingPath)) + startingPath = AppContext.BaseDirectory; + } + + if (!_filePickers.TryGetValue(o, out FilePicker fp)) + { + fp = new FilePicker(); + fp.RootFolder = startingPath; + fp.CurrentFolder = startingPath; + fp.OnlyAllowFolders = onlyAllowFolders; + + if (searchFilter != null) + { + if (fp.AllowedExtensions != null) + fp.AllowedExtensions.Clear(); + else + fp.AllowedExtensions = new List(); + + fp.AllowedExtensions.AddRange(searchFilter.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries)); + } + + _filePickers.Add(o, fp); + } + + return fp; + } + + public static void RemoveFilePicker(object o) => _filePickers.Remove(o); + + public bool Draw() + { + ImGui.Text("Current Folder: " + Path.GetFileName(RootFolder) + CurrentFolder.Replace(RootFolder, "")); + bool result = false; + + if (ImGui.BeginChildFrame(1, new Vector2(400, 400))) + { + var di = new DirectoryInfo(CurrentFolder); + if (di.Exists) + { + if (di.Parent != null && CurrentFolder != RootFolder) + { + ImGui.PushStyleColor(ImGuiCol.Text, Color.Yellow.PackedValue); + if (ImGui.Selectable("../", false, ImGuiSelectableFlags.DontClosePopups)) + CurrentFolder = di.Parent.FullName; + + ImGui.PopStyleColor(); + } + + var fileSystemEntries = GetFileSystemEntries(di.FullName); + foreach (var fse in fileSystemEntries) + { + if (Directory.Exists(fse)) + { + var name = Path.GetFileName(fse); + ImGui.PushStyleColor(ImGuiCol.Text, Color.Yellow.PackedValue); + if (ImGui.Selectable(name + "/", false, ImGuiSelectableFlags.DontClosePopups)) + CurrentFolder = fse; + ImGui.PopStyleColor(); + } + else + { + var name = Path.GetFileName(fse); + bool isSelected = SelectedFile == fse; + if (ImGui.Selectable(name, isSelected, ImGuiSelectableFlags.DontClosePopups)) + SelectedFile = fse; + + if (ImGui.IsMouseDoubleClicked(0)) + { + result = true; + ImGui.CloseCurrentPopup(); + } + } + } + } + } + ImGui.EndChildFrame(); + + + if (ImGui.Button("Cancel")) + { + result = false; + ImGui.CloseCurrentPopup(); + } + + if (OnlyAllowFolders) + { + ImGui.SameLine(); + if (ImGui.Button("Open")) + { + result = true; + SelectedFile = CurrentFolder; + ImGui.CloseCurrentPopup(); + } + } + else if (SelectedFile != null) + { + ImGui.SameLine(); + if (ImGui.Button("Open")) + { + result = true; + ImGui.CloseCurrentPopup(); + } + } + + return result; + } + + bool TryGetFileInfo(string fileName, out FileInfo realFile) + { + try + { + realFile = new FileInfo(fileName); + return true; + } + catch + { + realFile = null; + return false; + } + } + + List GetFileSystemEntries(string fullName) + { + var files = new List(); + var dirs = new List(); + + foreach (var fse in Directory.GetFileSystemEntries(fullName, "")) + { + if (Directory.Exists(fse)) + { + dirs.Add(fse); + } + else if (!OnlyAllowFolders) + { + if (AllowedExtensions != null) + { + var ext = Path.GetExtension(fse); + if (AllowedExtensions.Contains(ext)) + files.Add(fse); + } + else + { + files.Add(fse); + } + } + } + + var ret = new List(dirs); + ret.AddRange(files); + + return ret; + } + +} diff --git a/Samples/Editor/Windows/FontPicker.cs b/Samples/Editor/Windows/FontPicker.cs new file mode 100644 index 000000000..878b03315 --- /dev/null +++ b/Samples/Editor/Windows/FontPicker.cs @@ -0,0 +1,116 @@ +using ImGuiNET; +using SadConsole.ImGuiSystem; + +namespace SadConsole.Editor.Windows; + +public class FontPicker : ImGuiWindow +{ + private int _fontSelectedIndex = -1; + private string _fontName = ""; + private string[] _fontNames; + private IFont[] _fonts; + private IFont? _listboxFont; + + private int _selectedSize = -1; + private string[] _sizes; + + public IFont Font { get; set; } + public Point FontSize { get; set; } + + public FontPicker(IFont font, Point fontSize) + { + Title = "Font picker"; + Font = font; + FontSize = fontSize; + + List fonts = [Game.Instance.EmbeddedFont, Game.Instance.EmbeddedFontExtended]; + + foreach (IFont fontItem in Game.Instance.Fonts.Values) + if (!fonts.Contains(fontItem)) + fonts.Add(fontItem); + + _fontNames = Game.Instance.Fonts.Select(f => f.Key).ToArray(); + _fontNames[0] = "SadConsole Built in IBM 8x16"; + _fontNames[1] = "SadConsole Built in IBM 8x16 extended"; + + _fontSelectedIndex = fonts.IndexOf(font); + _listboxFont = font; + + _sizes = ["x0.25", "x0.50", "x1", "x2", "x3", "x4"]; + List _tempSizes = [Font.GetFontSize(IFont.Sizes.Quarter), Font.GetFontSize(IFont.Sizes.Half), Font.GetFontSize(IFont.Sizes.One), Font.GetFontSize(IFont.Sizes.Two), Font.GetFontSize(IFont.Sizes.Three), Font.GetFontSize(IFont.Sizes.Four)]; + _selectedSize = _tempSizes.IndexOf(fontSize); + if (_selectedSize == -1) + _selectedSize = 2; + } + + public void Show() + { + IsOpen = true; + + if (!ImGuiCore.GuiComponents.Contains(this)) + ImGuiCore.GuiComponents.Add(this); + } + + public override void BuildUI(ImGuiRenderer renderer) + { + if (IsOpen) + { + ImGui.OpenPopup(Title); + + ImGuiExt.CenterNextWindow(); + ImGui.SetNextWindowSize(new System.Numerics.Vector2(350, -1)); + if (ImGui.BeginPopupModal(Title, ref IsOpen, ImGuiWindowFlags.NoResize)) + { + ImGui.Text("Fonts"); + ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X); + if (ImGui.ListBox("##Document Type", ref _fontSelectedIndex, _fontNames, _fontNames.Length, 4)) + _listboxFont = Game.Instance.Fonts.ToArray()[_fontSelectedIndex].Value; + + ImGui.Text("Name: "); + ImGui.SameLine(); + ImGui.Text(_listboxFont!.Name); + ImGui.Text("Size: "); + ImGui.SameLine(); + ImGui.Text(_listboxFont.GetFontSize(IFont.Sizes.One).ToString()); + + ImGui.Separator(); + ImGui.Text("Choose a font size"); + if (ImGui.ListBox("##fontsizes", ref _selectedSize, _sizes, _sizes.Length, _sizes.Length)) + { + + } + ImGui.SameLine(); + ImGui.BeginGroup(); + ImGui.Text("Size:"); + ImGui.SameLine(); + ImGui.Text(_listboxFont.GetFontSize((IFont.Sizes)_selectedSize).ToString()); + ImGui.EndGroup(); + // + // Bottom buttons + // + ImGui.Separator(); + + if (ImGui.Button("Cancel")) { DialogResult = false; IsOpen = false; } + + // Right-align button + float pos = ImGui.GetItemRectSize().X + ImGui.GetStyle().ItemSpacing.X; + ImGui.SameLine(ImGui.GetWindowWidth() - pos); + + if (ImGui.Button("Accept")) + { + Font = _listboxFont; + FontSize = _listboxFont.GetFontSize((IFont.Sizes)_selectedSize); + DialogResult = true; + IsOpen = false; + } + + ImGui.EndPopup(); + } + } + else + { + OnClosed(); + ImGuiCore.GuiComponents.Remove(this); + } + } +} diff --git a/Samples/Editor/Windows/FontSizePopup.cs b/Samples/Editor/Windows/FontSizePopup.cs new file mode 100644 index 000000000..583d29306 --- /dev/null +++ b/Samples/Editor/Windows/FontSizePopup.cs @@ -0,0 +1,45 @@ +using ImGuiNET; +using SadConsole.ImGuiSystem; + +namespace SadConsole.Editor.Windows; + +public static class FontSizePopup +{ + private static int _selectedSize = -1; + private static string[] _sizes = ["x0.25", "x0.50", "x1", "x2", "x3", "x4"]; + + public static bool Show(ImGuiRenderer renderer, string popupId, IFont font, ref Point fontSize) + { + bool returnValue = false; + + if (ImGui.BeginPopup(popupId)) + { + List _tempSizes = [font.GetFontSize(IFont.Sizes.Quarter), font.GetFontSize(IFont.Sizes.Half), font.GetFontSize(IFont.Sizes.One), font.GetFontSize(IFont.Sizes.Two), font.GetFontSize(IFont.Sizes.Three), font.GetFontSize(IFont.Sizes.Four)]; + _selectedSize = _tempSizes.IndexOf(fontSize); + if (_selectedSize == -1) + _selectedSize = 2; + + ImGui.Text("Name: "); + ImGui.SameLine(); + ImGui.Text(font.Name); + ImGui.Text("Size: "); + ImGui.SameLine(); + ImGui.Text(font.GetFontSize(IFont.Sizes.One).ToString()); + + ImGui.Separator(); + ImGui.Text("Choose a font size"); + if (ImGui.ListBox("##fontsizes", ref _selectedSize, _sizes, _sizes.Length, _sizes.Length)) + { + fontSize = font.GetFontSize((IFont.Sizes)_selectedSize); + ImGui.CloseCurrentPopup(); + returnValue = true; + } + + ImGui.EndPopup(); + } + + ImGuiCore.State.CheckSetPopupOpen(popupId); + + return returnValue; + } +} diff --git a/Samples/Editor/Windows/GlyphPickerPopup.cs b/Samples/Editor/Windows/GlyphPickerPopup.cs new file mode 100644 index 000000000..521a8b148 --- /dev/null +++ b/Samples/Editor/Windows/GlyphPickerPopup.cs @@ -0,0 +1,86 @@ +using ImGuiNET; +using System.Numerics; +using SadConsole.ImGuiSystem; + +namespace SadConsole.Editor.Windows; + +public static class GlyphPickerPopup +{ + public static bool Show(ImGuiRenderer renderer, string popupId, IFont font, nint fontTexture, Point fontTextureSize, ref int selectedGlyph) + { + bool returnValue = false; + + if (ImGui.BeginPopup(popupId)) + { + // Get shared space to reserve font icon preview + Vector2 size = ImGui.CalcTextSize("Glyph: None"); + size.Y = Math.Max(size.Y, font.GlyphHeight); + + // Draw font image + ImGui.Text("Select your glyph"); + ImGuiExt.DrawTextureChild("font_preview", true, ImGuiExt.ZoomNormal, ((Host.GameTexture)font.Image).Texture, renderer, out bool isActive, out bool isHovered); + + if (isHovered) + { + Vector2 mousePosition = ImGui.GetMousePos(); + Vector2 itemRectMin = ImGui.GetItemRectMin(); + Vector2 itemRectMax = ImGui.GetItemRectMax(); + //Vector2 itemRectSize = ImGui.GetItemRectSize(); + Vector2 pos = mousePosition - itemRectMin; + + KeyValuePair glyphRect = ((SadFont)font).GlyphRectangles.Where(kv => kv.Value.Contains(new Point((int)pos.X, (int)pos.Y))).FirstOrDefault(); + + if (glyphRect.Value != Rectangle.Empty) + if (isActive) + { + ImGui.CloseCurrentPopup(); + selectedGlyph = glyphRect.Key; + returnValue = true; + } + else + { + ImDrawListPtr drawList = ImGui.GetWindowDrawList();//->AddRect(labelMin, labelMax, IM_COL32(255, 0, 255, 255)); + + Vector2 boxTopLeft = new(itemRectMin.X + glyphRect.Value.X, itemRectMin.Y + glyphRect.Value.Y); + Vector2 boxBottomRight = boxTopLeft + new Vector2(glyphRect.Value.Width, glyphRect.Value.Height); + + drawList.AddRect(boxTopLeft, boxBottomRight, Color.Violet.PackedValue); + + float verticalLineX = boxTopLeft.X + glyphRect.Value.Width / 2; + float horizontalLineY = boxTopLeft.Y + glyphRect.Value.Height / 2; + + //drawList.AddLine(new Vector2(verticalLineX, itemRectMin.Y), new Vector2(verticalLineX, boxTopLeft.Y), Color.Violet.PackedValue, 2); + //drawList.AddLine(new Vector2(verticalLineX, boxBottomRight.Y - 1), new Vector2(verticalLineX, itemRectMax.Y), Color.Violet.PackedValue, 2); + //drawList.AddLine(new Vector2(itemRectMin.X, horizontalLineY), new Vector2(boxTopLeft.X, horizontalLineY), Color.Violet.PackedValue, 2); + //drawList.AddLine(new Vector2(boxBottomRight.X - 1, horizontalLineY), new Vector2(itemRectMax.X, horizontalLineY), Color.Violet.PackedValue, 2); + + ImGui.Text($"Glyph: {glyphRect.Key}"); + ImGui.SameLine(); + ImGui.Image(fontTexture, + new Vector2(glyphRect.Value.Width, glyphRect.Value.Height), + glyphRect.Value.Position.ToUV(fontTextureSize), (glyphRect.Value.Position + glyphRect.Value.Size).ToUV(fontTextureSize)); + } + else + { + Vector2 cursor = ImGui.GetCursorPos(); + ImGui.InvisibleButton("##padded-area-preview", size); + ImGui.SetCursorPos(cursor); + ImGui.Text($"Glyph: None"); + } + } + else + { + Vector2 cursor = ImGui.GetCursorPos(); + ImGui.InvisibleButton("##padded-area-preview", size); + ImGui.SetCursorPos(cursor); + ImGui.Text($"Glyph: None"); + } + + ImGui.EndPopup(); + } + + ImGuiCore.State.CheckSetPopupOpen(popupId); + + return returnValue; + } +} diff --git a/Samples/Editor/Windows/MessageWindow.cs b/Samples/Editor/Windows/MessageWindow.cs new file mode 100644 index 000000000..d5218fcc1 --- /dev/null +++ b/Samples/Editor/Windows/MessageWindow.cs @@ -0,0 +1,51 @@ +using ImGuiNET; +using SadConsole.Editor.Model; +using SadConsole.ImGuiSystem; + +namespace SadConsole.Editor.Windows; + +public class MessageWindow : ImGuiWindow +{ + private string _message; + + public MessageWindow(string message) + { + Title = "Unable to save file"; + _message = message; + } + + public void Show() + { + IsOpen = true; + + if (!ImGuiCore.GuiComponents.Contains(this)) + ImGuiCore.GuiComponents.Add(this); + } + + + public override void BuildUI(ImGuiRenderer renderer) + { + if (IsOpen) + { + ImGui.OpenPopup(Title); + + ImGuiExt.CenterNextWindow(); + ImGui.SetNextWindowSize(new System.Numerics.Vector2(350, -1)); + if (ImGui.BeginPopupModal(Title, ref IsOpen, ImGuiWindowFlags.NoResize)) + { + ImGui.TextWrapped(_message); + + // Right-align button + float pos = ImGui.CalcTextSize("Close").X + ImGui.GetStyle().ItemSpacing.X * 2; + ImGui.SetCursorPosX(ImGui.GetWindowWidth() - pos); + if (ImGui.Button("Close")) { DialogResult = false; IsOpen = false; } + ImGui.EndPopup(); + } + } + else + { + OnClosed(); + ImGuiCore.GuiComponents.Remove(this); + } + } +} diff --git a/Samples/Editor/GuiParts/PopupNewFileWindow.cs b/Samples/Editor/Windows/NewFile.cs similarity index 52% rename from Samples/Editor/GuiParts/PopupNewFileWindow.cs rename to Samples/Editor/Windows/NewFile.cs index 8aec9f439..1b1cd0756 100644 --- a/Samples/Editor/GuiParts/PopupNewFileWindow.cs +++ b/Samples/Editor/Windows/NewFile.cs @@ -1,44 +1,42 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using ImGuiNET; +using ImGuiNET; +using SadConsole.Editor.Model; using SadConsole.ImGuiSystem; -using SadConsole.UI; -namespace SadConsole.Editor.GuiParts; +namespace SadConsole.Editor.Windows; -public class PopupNewFileWindow: ImGuiWindow +public class NewFile : ImGuiWindow { - private int _documentSelectedIndex = -1; + private int _documentSelectedIndex = 0; private string _documentName = ""; - public Model.Document Document; + public Document? Document; - public PopupNewFileWindow() + public NewFile() { Title = "New file"; } + public void Show() + { + IsOpen = true; + + if (!ImGuiCore.GuiComponents.Contains(this)) + ImGuiCore.GuiComponents.Add(this); + } + + public override void BuildUI(ImGuiRenderer renderer) { if (IsOpen) { ImGui.OpenPopup(Title); - + ImGuiExt.CenterNextWindow(); ImGui.SetNextWindowSize(new System.Numerics.Vector2(350, -1)); if (ImGui.BeginPopupModal(Title, ref IsOpen, ImGuiWindowFlags.NoResize)) { ImGui.Text("Document Type:"); ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X); - if (ImGui.ListBox("##Document Type", ref _documentSelectedIndex, new[] { "Surface", "Scene", "Animation" }, 3)) - { - Document = _documentSelectedIndex switch - { - _ => new Model.SurfaceDocument(), - }; - } + DocumentTypeListControl.DrawListBox("##Document Type", 3, ref _documentSelectedIndex, ref Document); if (Document != null) { @@ -54,22 +52,17 @@ public override void BuildUI(ImGuiRenderer renderer) float pos = ImGui.GetItemRectSize().X + ImGui.GetStyle().ItemSpacing.X; ImGui.SameLine(ImGui.GetWindowWidth() - pos); - if (Document == null) - { - ImGui.BeginDisabled(); - ImGui.Button("Create"); - ImGui.EndDisabled(); - } - else + ImGui.BeginDisabled(Document == null); + + if (ImGui.Button("Create")) { - if (ImGui.Button("Create")) - { - Document.Create(); - DialogResult = true; - IsOpen = false; - } + Document!.Create(); + DialogResult = true; + IsOpen = false; } + ImGui.EndDisabled(); + ImGui.EndPopup(); } } @@ -78,6 +71,5 @@ public override void BuildUI(ImGuiRenderer renderer) OnClosed(); ImGuiCore.GuiComponents.Remove(this); } - } } diff --git a/Samples/Editor/Windows/OpenFile.cs b/Samples/Editor/Windows/OpenFile.cs new file mode 100644 index 000000000..8ec89ad65 --- /dev/null +++ b/Samples/Editor/Windows/OpenFile.cs @@ -0,0 +1,141 @@ +using System.Numerics; +using ImGuiNET; +using SadConsole.Editor.FileHandlers; +using SadConsole.ImGuiSystem; + +namespace SadConsole.Editor.Windows; + +public class OpenFile : ImGuiWindow +{ + private int _documentSelectedIndex = 0; + private string _documentName = ""; + private FileListBox _fileListBox; + private bool _hideDocumentTypes; + public Model.Document? Document; + private IFileHandler[] _fileLoaders; + private string[] _fileLoadersNames; + private int _fileLoaderSelectedIndex; + + public IFileHandler SelectedHandler; + public FileInfo SelectedFile; + + public OpenFile() + { + Title = "Open file"; + _fileListBox = new(Directory.GetCurrentDirectory()); + + //_fileListBox.ItemSelected += (s, e) => selectedEvent = true; + //_fileListBox.ItemHighlighted += (s, e) => highlightedEvent = true; + //_fileListBox.ChangeDirectory += (s, e) => { changedDirEvent = true; counter++; }; + } + + public void Show() + { + IsOpen = true; + _hideDocumentTypes = false; + + if (!ImGuiCore.GuiComponents.Contains(this)) + ImGuiCore.GuiComponents.Add(this); + } + + public void Show(Model.Document document) + { + IsOpen = true; + _hideDocumentTypes = true; + Document = document; + + _fileLoaders = Document.GetLoadHandlers().ToArray(); + _fileLoadersNames = _fileLoaders.Select(f => f.FriendlyName).ToArray(); + _fileLoaderSelectedIndex = 0; + + if (!ImGuiCore.GuiComponents.Contains(this)) + ImGuiCore.GuiComponents.Add(this); + } + + public override void BuildUI(ImGuiRenderer renderer) + { + if (IsOpen) + { + ImGui.OpenPopup(Title); + + ImGuiExt.CenterNextWindow(); + ImGui.SetNextWindowSize(new System.Numerics.Vector2(550, -1)); + //ImGui.SetNextWindowSize(new System.Numerics.Vector2(350, 300)); + + if (ImGui.BeginPopupModal(Title, ref IsOpen)) + { + if (!_hideDocumentTypes) + { + ImGui.Text("Document Type:"); + ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X); + if (DocumentTypeListControl.DrawListBox("##Document Type", 3, ref _documentSelectedIndex, ref Document)) + { + _fileLoaders = Document.GetLoadHandlers().ToArray(); + _fileLoadersNames = _fileLoaders.Select(f => f.FriendlyName).ToArray(); + _fileLoaderSelectedIndex = 0; + } + ImGui.Separator(); + } + + if (Document != null) + { + if (ImGui.BeginTable("table1", 2, ImGuiTableFlags.BordersInnerV)) + { + ImGui.TableSetupColumn("one", ImGuiTableColumnFlags.WidthFixed, ImGui.GetContentRegionAvail().X / 3); + ImGui.TableSetupColumn("two"); + + // Draw left-side file handlers + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.AlignTextToFramePadding(); + ImGui.Text("File Types"); + ImGui.SetNextItemWidth(-1); + ImGui.ListBox("##handlers", ref _fileLoaderSelectedIndex, _fileLoadersNames, _fileLoadersNames.Length, 10); + + // Draw right-side file list box; + ImGui.TableSetColumnIndex(1); + _fileListBox.SearchPattern = string.Join(';', _fileLoaders[_fileLoaderSelectedIndex].ExtensionsLoading.Select(ex => $"*.{ex}")); + _fileListBox.Begin("listbox", new(-1, 300)); + _fileListBox.Draw(); + _fileListBox.End(); + + ImGui.EndTable(); + } + + //ImGui.GetWindowDrawList().AddRectFilled(boxStart, boxEnd, Color.Blue.PackedValue); + //ImGui.SameLine(); + } + + // Draw final row of dialog buttons + ImGui.Separator(); + if (ImGui.Button("Cancel")) { DialogResult = false; IsOpen = false; } + + // Right-align button + float pos = ImGui.GetItemRectSize().X + ImGui.GetStyle().ItemSpacing.X; + ImGui.SameLine(ImGui.GetWindowWidth() - pos); + + + ImGui.BeginDisabled(_fileListBox.HighlightedItem == null || _fileListBox.IsHighlightedItemDirectory); + + if (ImGui.Button("Open")) + { + SelectedHandler = _fileLoaders[_fileLoaderSelectedIndex]; + SelectedFile = (FileInfo)_fileListBox.HighlightedItem!; + DialogResult = Document!.HydrateFromFileHandler(SelectedHandler, SelectedFile.FullName); + if (!DialogResult) + ImGuiCore.Alert("Unable to load file."); + IsOpen = false; + } + + ImGui.EndDisabled(); + + ImGui.EndPopup(); + } + } + else + { + OnClosed(); + ImGuiCore.GuiComponents.Remove(this); + } + } +} diff --git a/Samples/Editor/Windows/PalettePopup.cs b/Samples/Editor/Windows/PalettePopup.cs new file mode 100644 index 000000000..0fb03f0f4 --- /dev/null +++ b/Samples/Editor/Windows/PalettePopup.cs @@ -0,0 +1,190 @@ +using System.Numerics; +using System.Reflection; +using ImGuiNET; + +namespace SadConsole.Editor.Windows; + +public static class PalettePopup +{ + static Color s_ansiColor = Color.AnsiRed; + static int s_themeColorIndex = 0; + static string[] s_themeColorNames = Enum.GetNames(typeof(SadConsole.UI.Colors.ColorNames)); + static string s_filter = string.Empty; + + public static bool Show(string popupId, ref Color color) + { + bool returnValue = false; + + if (ImGui.BeginPopup(popupId)) + { + Vector2 padding = ImGui.GetStyle().FramePadding; + Vector2 spacing = ImGui.GetStyle().ItemSpacing; + + if (ImGui.BeginTabBar("tabs")) + { + if (ImGui.BeginTabItem("Ansi")) + { + ImGuiTableFlags flags = ImGuiTableFlags.SizingStretchSame; + if (ImGui.BeginTable("colors", 2, flags)) + { + ImGui.TableSetupColumn("one"); + ImGui.TableSetupColumn("two"); + } + bool clicked = false; + + GenerateAnsiComboRow("RED DARK", Color.AnsiRed, "RED BRIGHT", Color.AnsiRedBright, ref clicked, ref color); + GenerateAnsiComboRow("YELLOW DARK", Color.AnsiYellow, "YELLOW BRIGHT", Color.AnsiYellowBright, ref clicked, ref color); + GenerateAnsiComboRow("GREEN DARK", Color.AnsiGreen, "GREEN BRIGHT", Color.AnsiGreenBright, ref clicked, ref color); + GenerateAnsiComboRow("CYAN DARK", Color.AnsiCyan, "CYAN BRIGHT", Color.AnsiCyanBright, ref clicked, ref color); + GenerateAnsiComboRow("BLUE DARK", Color.AnsiBlue, "BLUE BRIGHT", Color.AnsiBlueBright, ref clicked, ref color); + GenerateAnsiComboRow("MAGENTA DARK", Color.AnsiMagenta, "MAGENTA BRIGHT", Color.AnsiMagentaBright, ref clicked, ref color); + GenerateAnsiComboRow("BLACK DARK", Color.AnsiBlack, "BLACK BRIGHT", Color.AnsiBlackBright, ref clicked, ref color); + GenerateAnsiComboRow("WHITE DARK", Color.AnsiWhite, "WHITE BRIGHT", Color.AnsiWhiteBright, ref clicked, ref color); + + static void GenerateAnsiComboRow(string name, Color color, string name2, Color color2, ref bool clicked, ref Color selectedColor) + { + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + Vector2 pos = ImGui.GetCursorPos(); + + if (ImGui.Selectable($"##ansi{name}", false, ImGuiSelectableFlags.DontClosePopups)) + { + clicked = true; + selectedColor = color; + } + ImGui.SetCursorPos(pos); + ImGui.TextColored(color2.ToVector4(), name); + ImGui.TableSetBgColor(ImGuiTableBgTarget.CellBg, color.PackedValue); + + ImGui.TableSetColumnIndex(1); + + pos = ImGui.GetCursorPos(); + if (ImGui.Selectable($"##ansi{name2}", false, ImGuiSelectableFlags.DontClosePopups)) + { + clicked = true; + selectedColor = color2; + } + ImGui.SetCursorPos(pos); + ImGui.TextColored(color.ToVector4(), name2); + ImGui.TableSetBgColor(ImGuiTableBgTarget.CellBg, color2.PackedValue); + } + + if (clicked) + { + ImGui.CloseCurrentPopup(); + returnValue = true; + } + + ImGui.EndTable(); + + ImGui.EndTabItem(); + } + if (ImGui.BeginTabItem("SadConsole")) + { + if (ImGui.BeginListBox("##themelist")) + { + ImDrawListPtr drawData = ImGui.GetWindowDrawList(); + + foreach (string colorname in s_themeColorNames) + { + Vector2 pos = ImGui.GetCursorPos(); + Color parsedColor = UI.Colors.Default.FromColorName(Enum.Parse(colorname)); + + if (GenerateSelectableColor(colorname, parsedColor, drawData, ref color)) + { + color = parsedColor; + returnValue = true; + ImGui.CloseCurrentPopup(); + } + } + + ImGui.EndListBox(); + } + + ImGui.EndTabItem(); + } + if (ImGui.BeginTabItem("Primitives")) + { + Vector2 area = ImGui.GetItemRectSize(); + + ImGui.AlignTextToFramePadding(); + ImGui.Text("Filter:"); + ImGui.SameLine(); + ImGui.SetNextItemWidth(area.Y - ImGui.GetCursorPosY() - spacing.Y - area.Y); + ImGui.InputText("##Filter", ref s_filter, 50); + ImGui.SameLine(); + if (ImGui.Button("X")) + s_filter = string.Empty; + + if (ImGui.BeginListBox("##primcolorlist")) + { + ImDrawListPtr drawData = ImGui.GetWindowDrawList(); + + Type colorType = typeof(Color); + foreach (FieldInfo item in colorType.GetFields(BindingFlags.Public | BindingFlags.Static).Where((t) => t.FieldType.Name == colorType.Name && t.Name.Contains(s_filter, StringComparison.OrdinalIgnoreCase))) + { + Color parsedColor = (Color)item.GetValue(null)!; + + if (GenerateSelectableColor(item.Name, parsedColor, drawData, ref color)) + { + color = parsedColor; + returnValue = true; + ImGui.CloseCurrentPopup(); + } + } + + ImGui.EndListBox(); + } + + ImGui.EndTabItem(); + } + if (ImGui.BeginTabItem("Custom")) + { + ImGui.EndTabItem(); + } + + ImGui.EndTabBar(); + } + + bool GenerateSelectableColor(string name, Color color, ImDrawListPtr drawData, ref Color selectedColor) + { + bool returnValue = false; + Vector2 pos = ImGui.GetCursorPos(); + returnValue = ImGui.Selectable($"##{name}"); + + Vector2 topLeft = ImGui.GetItemRectMin(); + Vector2 bottomRight = ImGui.GetItemRectMax(); + Vector2 bottomRightMax = ImGui.GetItemRectMax(); + + bottomRight = topLeft + new Vector2(bottomRight.Y - topLeft.Y, bottomRight.Y - topLeft.Y); + + topLeft += padding; + bottomRight.Y -= padding.Y; + bottomRight.X = topLeft.X + bottomRight.Y - topLeft.Y; + + pos += new Vector2(bottomRight.X - topLeft.X + spacing.X, 0f); + + ImGui.SetCursorPos(pos); + ImGui.Text(name); + + drawData.AddRectFilled(topLeft, bottomRight, color.PackedValue); + + pos += new Vector2(bottomRight.X - topLeft.X + spacing.X, 0f); + + topLeft.X = ImGui.CalcTextSize(name).X + bottomRight.X + spacing.X + spacing.X; + bottomRight = bottomRightMax; + bottomRight -= padding; + + drawData.AddRectFilled(topLeft, bottomRight, color.PackedValue); + + return returnValue; + } + + ImGui.EndPopup(); + } + + ImGuiCore.State.CheckSetPopupOpen(popupId); + + return returnValue; + } +} diff --git a/Samples/Editor/Windows/RenameWindow.cs b/Samples/Editor/Windows/RenameWindow.cs new file mode 100644 index 000000000..f77226fd8 --- /dev/null +++ b/Samples/Editor/Windows/RenameWindow.cs @@ -0,0 +1,43 @@ +using ImGuiNET; +using SadConsole.ImGuiSystem; + +namespace SadConsole.Editor.Windows; + +public static class RenameWindow +{ + static string _tempString = string.Empty; + + public static bool Show(string popupIdAndTitle, string message, ref string output, out bool accepted, Func validateText) + { + bool returnValue = false; + + ImGuiExt.CenterNextWindow(); + accepted = false; + if (ImGui.BeginPopupModal(popupIdAndTitle)) + { + ImGui.TextWrapped(message); + + ImGui.InputText("##input", ref _tempString, 512); + + if (ImGuiWindow.DrawButtons(out bool result, validateText(_tempString))) + { + output = _tempString; + _tempString = string.Empty; + + if (result) + { + returnValue = true; + accepted = true; + } + + ImGui.CloseCurrentPopup(); + } + + ImGui.EndPopup(); + } + + ImGuiCore.State.CheckSetPopupOpen(popupIdAndTitle); + + return returnValue; + } +} diff --git a/Samples/Editor/Windows/ResizeSurfacePopup.cs b/Samples/Editor/Windows/ResizeSurfacePopup.cs new file mode 100644 index 000000000..5c3a45b24 --- /dev/null +++ b/Samples/Editor/Windows/ResizeSurfacePopup.cs @@ -0,0 +1,38 @@ +using ImGuiNET; +using SadConsole.Editor.GuiParts.Tools; +using SadConsole.ImGuiSystem; + +namespace SadConsole.Editor.Windows; + +public static class ResizeSurfacePopup +{ + public static int MaxWidth = 2000; + public static int MaxHeight = 2000; + + public static bool Show(string popupId, ref int width, ref int height, out bool dialogResult) + { + bool returnValue = false; + dialogResult = false; + + ImGuiCore.State.CheckSetPopupOpen(popupId); + if (ImGui.BeginPopup(popupId)) + { + SettingsTable.BeginTable("resize_surface_table"); + + SettingsTable.DrawInt("Width:", "width", ref width, 1, MaxWidth); + SettingsTable.DrawInt("Height:", "height", ref height, 1, MaxHeight); + + SettingsTable.EndTable(); + + if (ImGuiWindow.DrawButtons(out dialogResult)) + { + returnValue = true; + ImGui.CloseCurrentPopup(); + } + + ImGui.EndPopup(); + } + + return returnValue; + } +} diff --git a/Samples/Editor/Windows/SaveFile.cs b/Samples/Editor/Windows/SaveFile.cs new file mode 100644 index 000000000..63720b767 --- /dev/null +++ b/Samples/Editor/Windows/SaveFile.cs @@ -0,0 +1,119 @@ +using System.Numerics; +using ImGuiNET; +using SadConsole.Editor.FileHandlers; +using SadConsole.ImGuiSystem; + +namespace SadConsole.Editor.Windows; + +public class SaveFile : ImGuiWindow +{ + private FileListBox _fileListBox; + public Model.Document? Document; + private IFileHandler[] _fileLoaders; + private string[] _fileLoadersNames; + private int _fileLoaderSelectedIndex; + private string _selectedFileName = ""; + + + public SaveFile() + { + Title = "Save file"; + _fileListBox = new(Directory.GetCurrentDirectory()); + + //_fileListBox.ItemSelected += (s, e) => selectedEvent = true; + //_fileListBox.ItemHighlighted += (s, e) => highlightedEvent = true; + //_fileListBox.ChangeDirectory += (s, e) => { changedDirEvent = true; counter++; }; + } + + public void Show(Model.Document document) + { + IsOpen = true; + Document = document; + + _fileLoaders = Document.GetSaveHandlers().ToArray(); + _fileLoadersNames = _fileLoaders.Select(f => f.FriendlyName).ToArray(); + _fileLoaderSelectedIndex = 0; + + if (!ImGuiCore.GuiComponents.Contains(this)) + ImGuiCore.GuiComponents.Add(this); + } + + public override void BuildUI(ImGuiRenderer renderer) + { + if (IsOpen) + { + ImGui.OpenPopup(Title); + + ImGuiExt.CenterNextWindow(); + ImGui.SetNextWindowSize(new System.Numerics.Vector2(550, -1)); + //ImGui.SetNextWindowSize(new System.Numerics.Vector2(350, 300)); + + if (ImGui.BeginPopupModal(Title, ref IsOpen)) + { + if (ImGui.BeginTable("table1", 2, ImGuiTableFlags.BordersInnerV)) + { + ImGui.TableSetupColumn("one", ImGuiTableColumnFlags.WidthFixed, ImGui.GetContentRegionAvail().X / 3); + ImGui.TableSetupColumn("two"); + + // Draw left-side file handlers + ImGui.TableNextRow(); + ImGui.TableSetColumnIndex(0); + ImGui.AlignTextToFramePadding(); + ImGui.Text("File Types"); + ImGui.SetNextItemWidth(-1); + ImGui.ListBox("##handlers", ref _fileLoaderSelectedIndex, _fileLoadersNames, _fileLoadersNames.Length, 10); + + // Draw right-side file list box; + ImGui.TableSetColumnIndex(1); + _fileListBox.SearchPattern = string.Join(';', _fileLoaders[_fileLoaderSelectedIndex].ExtensionsSaving.Select(ex => $"*.{ex}")); + _fileListBox.Begin("listbox", new(-1, 300)); + if (_fileListBox.Draw(out bool fileSelected, out bool fileHighlighted)) + { + if (!_fileListBox.IsSelectedItemDirectory) + _selectedFileName = _fileListBox.HighlightedItem!.Name; + } + _fileListBox.End(); + + ImGui.EndTable(); + } + ImGui.Separator(); + + ImGui.Text("File name: "); + ImGui.SameLine(); + ImGui.InputText("##filename", ref _selectedFileName, 200); + + //ImGui.GetWindowDrawList().AddRectFilled(boxStart, boxEnd, Color.Blue.PackedValue); + //ImGui.SameLine(); + + + // Draw final row of dialog buttons + ImGui.Separator(); + if (ImGui.Button("Cancel")) { DialogResult = false; IsOpen = false; } + + // Right-align button + float pos = ImGui.GetItemRectSize().X + ImGui.GetStyle().ItemSpacing.X; + ImGui.SameLine(ImGui.GetWindowWidth() - pos); + + ImGui.BeginDisabled(string.IsNullOrEmpty(_selectedFileName)); + + if (ImGui.Button("Save")) + { + string file = Path.Combine(_fileListBox.CurrentDirectory.FullName, _selectedFileName); + object savableInstance = Document!.DehydrateToFileHandler(_fileLoaders[_fileLoaderSelectedIndex], file); + _fileLoaders[_fileLoaderSelectedIndex].Save(savableInstance, file); + DialogResult = true; + IsOpen = false; + } + + ImGui.EndDisabled(); + + ImGui.EndPopup(); + } + } + else + { + OnClosed(); + ImGuiCore.GuiComponents.Remove(this); + } + } +} diff --git a/Samples/Editor/app.manifest b/Samples/Editor/app.manifest index f5cb98b6d..c98ccbfd4 100644 --- a/Samples/Editor/app.manifest +++ b/Samples/Editor/app.manifest @@ -3,8 +3,7 @@ - PerMonitor - true/pm + diff --git a/Samples/Editor/zGumTests.cs b/Samples/Editor/zGumTests.cs new file mode 100644 index 000000000..6ebdf91bd --- /dev/null +++ b/Samples/Editor/zGumTests.cs @@ -0,0 +1,164 @@ +using RenderingLibrary.Graphics; +using RenderingLibrary; +using Gum.Wireframe; +using System.Collections.ObjectModel; +using SadConsole.UI.Controls; +using SadConsole.UI; + +namespace SadConsole.Editor; + +public class SadConsoleGumControl : GraphicalUiElement +{ + public Func? CreationCallBack; + + public SadConsoleGumControl(Func createControl): base(new InvisibleRenderable(), null!) + { + WidthUnits = Gum.DataTypes.DimensionUnitType.Absolute; + HeightUnits = Gum.DataTypes.DimensionUnitType.Absolute; + + CreationCallBack = createControl; + } +} + +public class SadConsoleGumHost : GraphicalUiElement +{ + ControlsConsole _host; + + public SadConsoleGumHost(ControlsConsole hostConsole) : base(new InvisibleRenderable(), null!) + { + WrapsChildren = true; + CanvasWidth = hostConsole.Width; + CanvasHeight = hostConsole.Height; + X = 0; + Y = 0; + Width = hostConsole.Width; + Height = hostConsole.Height; + _host = hostConsole; + } + + public void Generate() + { + UpdateLayout(); + + foreach (SadConsoleGumControl child in Children) + { + ControlBase control = child.CreationCallBack!(child); + control.Position = ((int)child.X, (int)child.Y); + _host.Controls.Add(control); + child.CreationCallBack = null; + } + } +} + +public class ControlBaseGumWrapper : IVisible, IRenderableIpso, IRenderable, IPositionedSizedObject, ISetClipsChildren +{ + private ObservableCollection children = new ObservableCollection(); + + private float height; + + private IRenderableIpso mParent; + + public ControlBase SadControl { get; set; } + + public bool AbsoluteVisible + { + get + { + if (((IVisible)this).Parent == null) + { + return Visible; + } + + return Visible && ((IVisible)this).Parent.AbsoluteVisible; + } + } + + public Gum.BlendState BlendState => Gum.BlendState.NonPremultiplied; + + public ObservableCollection Children => children; + + ColorOperation IRenderableIpso.ColorOperation => ColorOperation.Modulate; + + public bool ClipsChildren { get; set; } + + public float Height { get => SadControl.Height; set => SadControl.Resize(SadControl.Width, value == 0 ? 1 : (int)value); } + + public string Name { get; set; } + + public IRenderableIpso Parent + { + get + { + return mParent; + } + set + { + if (mParent != value) + { + if (mParent != null) + { + mParent.Children.Remove(this); + } + + mParent = value; + if (mParent != null) + { + mParent.Children.Add(this); + } + } + } + } + + public float Rotation { get; set; } + + public object Tag { get; set; } + + public bool Visible { get; set; } = true; + + + public float Width { get => SadControl.Width; set => SadControl.Resize(value == 0 ? 1 : (int)value, SadControl.Height); } + + public bool Wrap => false; + + public float X { get => SadControl.Position.X; set => SadControl.Position = SadControl.Position.WithX((int)value); } + + public float Y { get => SadControl.Position.Y; set => SadControl.Position = SadControl.Position.WithY((int)value); } + + public float Z { get; set; } + + public bool FlipHorizontal { get; set; } + + IVisible IVisible.Parent => Parent; + + public ControlBaseGumWrapper(ControlBase control) + { + SadControl = control; + } + + public void PreRender() + { + } + + public void Render(ISystemManagers managers) + { + } + + void IRenderableIpso.SetParentDirect(IRenderableIpso parent) + { + mParent = parent; + + if (parent is null) return; + + if (((GraphicalUiElement)parent).Component is ControlBaseGumWrapper wrapper) + { + if (wrapper.SadControl is Panel panel) + SadControl.Parent = panel; + } + + } + + public override string ToString() + { + return Name; + } +} diff --git a/Samples/MonoGameFirst/.config/dotnet-tools.json b/Samples/MonoGameFirst/.config/dotnet-tools.json new file mode 100644 index 000000000..efabe22ef --- /dev/null +++ b/Samples/MonoGameFirst/.config/dotnet-tools.json @@ -0,0 +1,36 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "dotnet-mgcb": { + "version": "3.8.1.303", + "commands": [ + "mgcb" + ] + }, + "dotnet-mgcb-editor": { + "version": "3.8.1.303", + "commands": [ + "mgcb-editor" + ] + }, + "dotnet-mgcb-editor-linux": { + "version": "3.8.1.303", + "commands": [ + "mgcb-editor-linux" + ] + }, + "dotnet-mgcb-editor-windows": { + "version": "3.8.1.303", + "commands": [ + "mgcb-editor-windows" + ] + }, + "dotnet-mgcb-editor-mac": { + "version": "3.8.1.303", + "commands": [ + "mgcb-editor-mac" + ] + } + } +} \ No newline at end of file diff --git a/Samples/MonoGameFirst/Content/Content.mgcb b/Samples/MonoGameFirst/Content/Content.mgcb new file mode 100644 index 000000000..ddc4c3679 --- /dev/null +++ b/Samples/MonoGameFirst/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/Samples/MonoGameFirst/Game1.cs b/Samples/MonoGameFirst/Game1.cs new file mode 100644 index 000000000..67859c15e --- /dev/null +++ b/Samples/MonoGameFirst/Game1.cs @@ -0,0 +1,64 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; + +namespace MonoGameFirst +{ + public class Game1 : Game + { + private GraphicsDeviceManager _graphics; + private SpriteBatch _spriteBatch; + + public Game1() + { + _graphics = new GraphicsDeviceManager(this); + Content.RootDirectory = "Content"; + IsMouseVisible = true; + } + + protected override void Initialize() + { + // Some global variables for SadConsole + SadConsole.Game.Instance.MonoGameInstance = this; + SadConsole.Host.Global.GraphicsDeviceManager = _graphics; + + // Initialize the SadConsole engine + SadConsole.Host.Global.SadConsoleComponent = new SadConsole.Host.SadConsoleGameComponent(this); + Components.Add(SadConsole.Host.Global.SadConsoleComponent); + + base.Initialize(); + } + + protected override void LoadContent() + { + _spriteBatch = new SpriteBatch(GraphicsDevice); + + // TODO: use this.Content to load your game content here + } + + protected override void Update(GameTime gameTime) + { + if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape)) + Exit(); + + // TODO: Add your update logic here + + base.Update(gameTime); + } + + protected override void Draw(GameTime gameTime) + { + // Do your offscreen drawing (the SadConsole component gets drawn here) + base.Draw(gameTime); + + // Clear the graphics device + GraphicsDevice.Clear(Color.CornflowerBlue); + + // Render your scene and render SadConsole to something + _spriteBatch.Begin(); + _spriteBatch.Draw(SadConsole.Host.Global.RenderOutput, new Rectangle(50, 100, SadConsole.Host.Global.RenderOutput.Width / 3, SadConsole.Host.Global.RenderOutput.Height / 3), Color.White); + _spriteBatch.Draw(SadConsole.Host.Global.RenderOutput, new Rectangle(150, 25, (int)(SadConsole.Host.Global.RenderOutput.Width / 1.5), (int)(SadConsole.Host.Global.RenderOutput.Height / 1.5)), Color.White); + _spriteBatch.End(); + } + } +} diff --git a/Samples/MonoGameFirst/Icon.bmp b/Samples/MonoGameFirst/Icon.bmp new file mode 100644 index 000000000..2b481653e Binary files /dev/null and b/Samples/MonoGameFirst/Icon.bmp differ diff --git a/Samples/MonoGameFirst/Icon.ico b/Samples/MonoGameFirst/Icon.ico new file mode 100644 index 000000000..7d9dec187 Binary files /dev/null and b/Samples/MonoGameFirst/Icon.ico differ diff --git a/Samples/MonoGameFirst/MonoGameFirst.csproj b/Samples/MonoGameFirst/MonoGameFirst.csproj new file mode 100644 index 000000000..ae660cea5 --- /dev/null +++ b/Samples/MonoGameFirst/MonoGameFirst.csproj @@ -0,0 +1,39 @@ + + + WinExe + net8.0 + Major + false + false + + + app.manifest + Icon.ico + true + $(DefineConstants);MONOGAME + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/templates/template_code/SadConsole.Project.MonoGame.CSharp/SadConsole.Project.MonoGame.CSharp.sln b/Samples/MonoGameFirst/MonoGameFirst.sln similarity index 56% rename from templates/template_code/SadConsole.Project.MonoGame.CSharp/SadConsole.Project.MonoGame.CSharp.sln rename to Samples/MonoGameFirst/MonoGameFirst.sln index 23e94a848..35638490c 100644 --- a/templates/template_code/SadConsole.Project.MonoGame.CSharp/SadConsole.Project.MonoGame.CSharp.sln +++ b/Samples/MonoGameFirst/MonoGameFirst.sln @@ -1,9 +1,9 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 -VisualStudioVersion = 17.5.002.0 +VisualStudioVersion = 17.10.34916.146 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyGame", "MyGame.csproj", "{28BD4D81-BE23-4DDB-95A1-4FF3E00EFFE3}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonoGameFirst", "MonoGameFirst.csproj", "{E8A7E4B3-E3C1-4D63-B32C-1815C165E8B0}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -11,15 +11,15 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {28BD4D81-BE23-4DDB-95A1-4FF3E00EFFE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {28BD4D81-BE23-4DDB-95A1-4FF3E00EFFE3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {28BD4D81-BE23-4DDB-95A1-4FF3E00EFFE3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {28BD4D81-BE23-4DDB-95A1-4FF3E00EFFE3}.Release|Any CPU.Build.0 = Release|Any CPU + {E8A7E4B3-E3C1-4D63-B32C-1815C165E8B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E8A7E4B3-E3C1-4D63-B32C-1815C165E8B0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E8A7E4B3-E3C1-4D63-B32C-1815C165E8B0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E8A7E4B3-E3C1-4D63-B32C-1815C165E8B0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {94CC880C-3027-4370-AF2E-EB285EA847E7} + SolutionGuid = {5CED2FF8-4A51-45B3-8342-6C3066AB2549} EndGlobalSection EndGlobal diff --git a/Samples/MonoGameFirst/Program.cs b/Samples/MonoGameFirst/Program.cs new file mode 100644 index 000000000..d632eafdf --- /dev/null +++ b/Samples/MonoGameFirst/Program.cs @@ -0,0 +1,32 @@ +using Console = SadConsole.Console; +using SadConsole; +using SadConsole.Configuration; +using SadRogue.Primitives; + +// Configure how SadConsole starts up +Builder startup = new Builder() + .SetScreenSize(90, 30) + .UseDefaultConsole() + .OnStart(Game_Started) + .IsStartingScreenFocused(true) + .ConfigureFonts(true) + .SkipMonoGameGameCreation() + ; + +Settings.DoFinalDraw = false; + +// Setup the engine and start the game +Game.Create(startup); + +void Game_Started(object? sender, GameHost host) +{ + ColoredGlyph boxBorder = new(Color.White, Color.Black, 178); + ColoredGlyph boxFill = new(Color.White, Color.Black); + + Game.Instance.StartingConsole.FillWithRandomGarbage(255); + Game.Instance.StartingConsole.DrawBox(new Rectangle(2, 2, 26, 5), ShapeParameters.CreateFilled(boxBorder, boxFill)); + Game.Instance.StartingConsole.Print(4, 4, "Welcome to SadConsole!"); +} + +using var game = new MonoGameFirst.Game1(); +game.Run(); diff --git a/Samples/MonoGameFirst/app.manifest b/Samples/MonoGameFirst/app.manifest new file mode 100644 index 000000000..0dd9d3cf1 --- /dev/null +++ b/Samples/MonoGameFirst/app.manifest @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true/pm + permonitorv2,permonitor + + + + diff --git a/Samples/SadPython/Program.cs b/Samples/SadPython/Program.cs new file mode 100644 index 000000000..5e61f28c2 --- /dev/null +++ b/Samples/SadPython/Program.cs @@ -0,0 +1,56 @@ +using Microsoft.Scripting.Hosting; +using SadConsole.Configuration; + +// Python config options +Dictionary options = new(); +options["Debug"] = true; + +// Create the python engine +ScriptEngine engine = IronPython.Hosting.Python.CreateEngine(options); + +try +{ + // Load game.py and compile it + ScriptSource script = engine.CreateScriptSourceFromFile("game.py"); + CompiledCode compiledScript = script.Compile(); + + // Run the script + compiledScript.Execute(); + + // Run the getGameConfig method and get its return value, which should be a SadConsole.Configuration.Builder type + Builder config = compiledScript.DefaultScope.GetVariable("getGameConfig")(); + + // Boot up SadConsole + Game.Create(config); + Game.Instance.Run(); + Game.Instance.Dispose(); +} +catch (Exception e1) +{ + // The game crashed. To help debug Python, this exception catch formats + // the exception text and recreates SadConsole to print out the error + + string errorMessage = engine.GetService().FormatException(e1); + + System.Console.WriteLine(errorMessage); + + ColorExtensions2.ColorMappings?.Clear(); + + if (Game.Instance != null && Game.Instance.MonoGameInstance != null) + Game.Instance.MonoGameInstance.Dispose(); + + Game.Instance = null!; + Game.Create(80, 20, new(StartupCrash)); + Game.Instance!.Run(); + Game.Instance.Dispose(); + + void StartupCrash(object? sender, GameHost gameHost) + { + gameHost.StartingConsole!.Clear(); + gameHost.StartingConsole!.Cursor.DisableWordBreak = true; + gameHost.StartingConsole!.Cursor.Move(0, 1) + .Print("Error starting SadConsole with Python") + .NewLine() + .Print(errorMessage); + } +} diff --git a/Samples/SadPython/SadPython.csproj b/Samples/SadPython/SadPython.csproj new file mode 100644 index 000000000..364e15847 --- /dev/null +++ b/Samples/SadPython/SadPython.csproj @@ -0,0 +1,66 @@ + + + + WinExe + net8.0 + enable + enable + SadConsole.Examples + + + + + + + + + + + + + + + + + + + + + monogame + + + + true + + + $(DefineConstants);MONOGAME + $(DefineConstants);SFML + + + + + + + + + + + + + + + + + + + + + + + + + Always + + + + diff --git a/Samples/SadPython/game.py b/Samples/SadPython/game.py new file mode 100644 index 000000000..e92d1829a --- /dev/null +++ b/Samples/SadPython/game.py @@ -0,0 +1,40 @@ +import clr + +clr.AddReference("SadConsole") +clr.AddReference("SadConsole.Host.MonoGame") +clr.AddReference("TheSadRogue.Primitives") + +from SadConsole import Game, CellSurfaceEditor as Edit, Settings as SadConsoleSettings +from SadConsole import ScreenSurface, IScreenObject +from SadConsole import Configuration +from SadRogue.Primitives import Color as Color + +SadConsoleSettings.WindowTitle = "Python Game" + +def getGameConfig(): + config = Configuration.Builder() + Configuration.Extensions.SetScreenSize(config, 20, 10) + Configuration.Extensions.SetStartingScreen(config, lambda host: MyConsole()) + return config + +class MyConsole(ScreenSurface): + def __new__(cls, *args): + if len(args) == 2: + instance = ScreenSurface.__new__(cls, args[0], args[1]) + + if len(args) == 0: + instance = ScreenSurface.__new__(cls, 20, 10) + else: + raise ValueError('I created this class to only support constructors of (Width, Height)') + + Edit.FillWithRandomGarbage(instance, Game.Instance.DefaultFont) + + return instance + + def Update(self, delta): + Edit.Print(self, 1, 2, "Frame update time:", Color.White, Color.Black, 0) + Edit.Clear(self, 1, 3, 15) + Edit.Print(self, 3, 3, delta.ToString("G")) + Edit.Print(self, 1, 4, "Frame counter: ", Color.White, Color.Black, 0) + Edit.Clear(self, 1, 5, 15) + Edit.Print(self, 3, 5, Game.Instance.FrameNumber.ToString()) diff --git a/changelog.md b/changelog.md index a6b58c9ea..441b993d4 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,16 @@ +## v10.4.1 (06/12/2024) + +- [Core] When a Cursor is removed from a host object, the object is set to dirty now. +- [Core] Overlay component now calls update on the attached visual. +- [Core] Fix bug with DrawCircle not setting dirty. +- [Core] Fix bug with DrawBox not respecting ignore glyph setting. + +- [MonoGame] Change host to be a lot more friendly towards running from an existing MonoGame Game instead of SadConsole's game. +- [Renderers] Cursor rendering has changed to draw on the hosting surface. Previously it was rendered on top as an entire separate process. + +- [Misc] Added MonoGame-first game sample. +- [Misc] Added Python sample. + ## v10.4.0 (03/31/2024) New versioning system. Versions for SadConsole will be in the following format: [all libs major].[all libs minor].[individual lib revision] diff --git a/nuget/build.ps1 b/nuget/build.ps1 index 4efe53cc2..16e22b510 100644 --- a/nuget/build.ps1 +++ b/nuget/build.ps1 @@ -4,13 +4,11 @@ Remove-Item "*.nupkg","*.snupkg" -Force # Build SadConsole Write-Output "Building SadConsole Debug and Release" -$output = Invoke-Expression "dotnet restore ..\SadConsole\SadConsole.csproj -c Debug --no-cache"; if ($LASTEXITCODE -ne 0) { Write-Error "Failed"; Write-Output $output; throw } -$output = Invoke-Expression "dotnet build ..\SadConsole\SadConsole.csproj -c Debug"; if ($LASTEXITCODE -ne 0) { Write-Error "Failed"; Write-Output $output; throw } -$output = Invoke-Expression "dotnet restore ..\SadConsole\SadConsole.csproj -c Release --no-cache"; if ($LASTEXITCODE -ne 0) { Write-Error "Failed"; Write-Output $output; throw } -$output = Invoke-Expression "dotnet build ..\SadConsole\SadConsole.csproj -c Release"; if ($LASTEXITCODE -ne 0) { Write-Error "Failed"; Write-Output $output; throw } +$output = Invoke-Expression "dotnet build ..\SadConsole\SadConsole.csproj -c Debug --no-cache"; if ($LASTEXITCODE -ne 0) { Write-Error "Failed"; Write-Output $output; throw } +$output = Invoke-Expression "dotnet build ..\SadConsole\SadConsole.csproj -c Release --no-cache"; if ($LASTEXITCODE -ne 0) { Write-Error "Failed"; Write-Output $output; throw } # Find the version we're using -$version = (Get-Content ..\SadConsole\SadConsole.csproj | Select-String "(.*)<").Matches[0].Groups[1].Value +$version = (Get-Content ..\MSBuild\Common.props | Select-String "(.*)<").Matches[0].Groups[1].Value $nugetKey = Get-Content nuget.key Write-Output "Target SadConsole version is $version" @@ -19,7 +17,7 @@ Write-Output "Pushing SadConsole packages" $sadConsolePackages = Get-ChildItem "SadConsole.*.nupkg" | Select-Object -ExpandProperty Name foreach ($package in $sadConsolePackages) { - $output = Invoke-Expression "dotnet nuget push `"$package`" -s nuget.org -k $nugetKey"; if ($LASTEXITCODE -ne 0) { Write-Error "Failed"; Write-Output $output; throw } + $output = Invoke-Expression "dotnet nuget push `"$package`" -s nuget.org -k $nugetKey --skip-duplicate"; if ($LASTEXITCODE -ne 0) { Write-Error "Failed"; Write-Output $output; throw } } Write-Output "Query NuGet for 10 minutes to find the new package" @@ -58,21 +56,19 @@ if ($foundPackage){ # SadConsole Extended Write-Output "Building $project Debug and Release" - $output = Invoke-Expression "dotnet restore ..\$project\$project.csproj -c Debug -p:UseProjectReferences=false --no-cache"; if ($LASTEXITCODE -ne 0) { Write-Error "Failed"; Write-Output $output; throw } - $output = Invoke-Expression "dotnet build ..\$project\$project.csproj -c Debug -p:UseProjectReferences=false"; if ($LASTEXITCODE -ne 0) { Write-Error "Failed"; Write-Output $output; throw } - $output = Invoke-Expression "dotnet restore ..\$project\$project.csproj -c Release -p:UseProjectReferences=false --no-cache"; if ($LASTEXITCODE -ne 0) { Write-Error "Failed"; Write-Output $output; throw } - $output = Invoke-Expression "dotnet build ..\$project\$project.csproj -c Release -p:UseProjectReferences=false"; if ($LASTEXITCODE -ne 0) { Write-Error "Failed"; Write-Output $output; throw } + $output = Invoke-Expression "dotnet build ..\$project\$project.csproj -c Debug -p:UseProjectReferences=false --no-cache"; if ($LASTEXITCODE -ne 0) { Write-Error "Failed"; Write-Output $output; throw } + $output = Invoke-Expression "dotnet build ..\$project\$project.csproj -c Release -p:UseProjectReferences=false --no-cache"; if ($LASTEXITCODE -ne 0) { Write-Error "Failed"; Write-Output $output; throw } # Push packages to nuget Write-Output "Pushing SadConsole packages" $sadConsolePackages = Get-ChildItem "$project*.nupkg" | Select-Object -ExpandProperty Name foreach ($package in $sadConsolePackages) { - $output = Invoke-Expression "dotnet nuget push `"$package`" -s nuget.org -k $nugetKey"; if ($LASTEXITCODE -ne 0) { Write-Error "Failed"; Write-Output $output; throw } + $output = Invoke-Expression "dotnet nuget push `"$package`" -s nuget.org -k $nugetKey --skip-duplicate"; if ($LASTEXITCODE -ne 0) { Write-Error "Failed"; Write-Output $output; throw } } } # Archive the packages Move-Item "*.nupkg","*.snupkg" .\archive\ -force -} \ No newline at end of file +} diff --git a/nuget/build_sadconsole.ps1 b/nuget/build_sadconsole.ps1 index 7ff5595b6..dde2cbeb4 100644 --- a/nuget/build_sadconsole.ps1 +++ b/nuget/build_sadconsole.ps1 @@ -17,7 +17,7 @@ Write-Output "Pushing SadConsole packages" $sadConsolePackages = Get-ChildItem "SadConsole.*.nupkg" | Select-Object -ExpandProperty Name foreach ($package in $sadConsolePackages) { - $output = Invoke-Expression "dotnet nuget push `"$package`" -s nuget.org -k $nugetKey"; if ($LASTEXITCODE -ne 0) { Write-Error "Failed"; Write-Output $output; throw } + $output = Invoke-Expression "dotnet nuget push `"$package`" -s nuget.org -k $nugetKey --skip-duplicate"; if ($LASTEXITCODE -ne 0) { Write-Error "Failed"; Write-Output $output; throw } } Write-Output "Query NuGet for 10 minutes to find the new package" diff --git a/nuget/build_sadconsole_ext.ps1 b/nuget/build_sadconsole_ext.ps1 index 71fc53874..aed2168b1 100644 --- a/nuget/build_sadconsole_ext.ps1 +++ b/nuget/build_sadconsole_ext.ps1 @@ -14,7 +14,7 @@ Write-Output "Pushing SadConsole packages" $sadConsolePackages = Get-ChildItem "SadConsole.Extended*.nupkg" | Select-Object -ExpandProperty Name foreach ($package in $sadConsolePackages) { - $output = Invoke-Expression "dotnet nuget push `"$package`" -s nuget.org -k $nugetKey"; if ($LASTEXITCODE -ne 0) { Write-Error "Failed"; Write-Output $output; throw } + $output = Invoke-Expression "dotnet nuget push `"$package`" -s nuget.org -k $nugetKey --skip-duplicate"; if ($LASTEXITCODE -ne 0) { Write-Error "Failed"; Write-Output $output; throw } } Write-Output "Finished" diff --git a/nuget/build_template.ps1 b/nuget/build_template.ps1 index 8b1dedfec..2154dfb07 100644 --- a/nuget/build_template.ps1 +++ b/nuget/build_template.ps1 @@ -16,7 +16,7 @@ Write-Output "Pushing template package" $sadConsolePackages = Get-ChildItem "SadConsole.Templates.$version.nupkg" | Select-Object -ExpandProperty Name foreach ($package in $sadConsolePackages) { - $output = Invoke-Expression "dotnet nuget push `"$package`" -s nuget.org -k $nugetKey"; if ($LASTEXITCODE -ne 0) { Write-Error "Failed"; Write-Output $output; throw } + $output = Invoke-Expression "dotnet nuget push `"$package`" -s nuget.org -k $nugetKey --skip-duplicate"; if ($LASTEXITCODE -ne 0) { Write-Error "Failed"; Write-Output $output; throw } # Archive the package Move-Item *.nupkg .\archive\ -force diff --git a/templates/README.md b/templates/README.md new file mode 100644 index 000000000..549220d70 --- /dev/null +++ b/templates/README.md @@ -0,0 +1,8 @@ +![SadConsole Logo](https://raw.githubusercontent.com/Thraka/SadConsole/master/images/SadConsoleLogo.gif) + +[![Chat on discord](https://img.shields.io/discord/501465397518925843.svg)](https://discord.gg/pAFNKYjczM) +[![NuGet](https://img.shields.io/nuget/v/SadConsole.svg)][nuget] + +SadConsole is a C#-based .NET cross-platform terminal, ascii, console, game engine. It simulates these types of programs and with it you can make ascii-styled games for modern platforms. At its heart, SadConsole is really a giant tile-based game engine. However, its object model is conceptually similar to a traditional console app. + +These are the .NET templates for SadConsole. For more information, see [Create a new SadConsole .NET project with the SadConsole templates](https://sadconsole.com/articles/getting-started-cli.html). diff --git a/templates/SadConsole.Templates.csproj b/templates/SadConsole.Templates.csproj index 70359fd36..1131eaea5 100644 --- a/templates/SadConsole.Templates.csproj +++ b/templates/SadConsole.Templates.csproj @@ -2,7 +2,7 @@ Template - 10.0.14 + 10.4.1 SadConsole.Templates SadConsole Game Templates Thraka @@ -18,6 +18,7 @@ + diff --git a/templates/template_code/SadConsole.Examples.Demo.CSharp/Content/Assets.mgcb b/templates/template_code/SadConsole.Examples.Demo.CSharp/Content/Assets.mgcb new file mode 100644 index 000000000..946fcdc72 --- /dev/null +++ b/templates/template_code/SadConsole.Examples.Demo.CSharp/Content/Assets.mgcb @@ -0,0 +1,26 @@ + +#----------------------------- Global Properties ----------------------------# + +/outputDir:bin/$(Platform) +/intermediateDir:obj/$(Platform) +/platform:DesktopGL +/config: +/profile:Reach +/compress:False + +#-------------------------------- References --------------------------------# + + +#---------------------------------- Content ---------------------------------# + +#begin FinalDraw.fx +/importer:EffectImporter +/processor:EffectProcessor +/processorParam:DebugMode=Auto +/build:FinalDraw.fx + +#begin crt-lottes-mg.fx +/importer:EffectImporter +/processor:EffectProcessor +/processorParam:DebugMode=Auto +/build:crt-lottes-mg.fx diff --git a/templates/template_code/SadConsole.Examples.Demo.CSharp/Content/FinalDraw.fx b/templates/template_code/SadConsole.Examples.Demo.CSharp/Content/FinalDraw.fx new file mode 100644 index 000000000..a4b3878c1 --- /dev/null +++ b/templates/template_code/SadConsole.Examples.Demo.CSharp/Content/FinalDraw.fx @@ -0,0 +1,38 @@ +#if OPENGL + #define SV_POSITION POSITION + #define VS_SHADERMODEL vs_3_0 + #define PS_SHADERMODEL ps_3_0 +#else + #define VS_SHADERMODEL vs_4_0_level_9_1 + #define PS_SHADERMODEL ps_4_0_level_9_1 +#endif + +Texture2D SpriteTexture; +sampler s0; + +sampler2D SpriteTextureSampler = sampler_state +{ + Texture = ; +}; + +struct VertexShaderOutput +{ + float4 Position : SV_POSITION; + float4 Color : COLOR0; + float2 TextureCoordinates : TEXCOORD0; +}; + +float4 MainPS(VertexShaderOutput input) : COLOR +{ + float4 color = tex2D(SpriteTextureSampler,input.TextureCoordinates) * input.Color; + color.gb = color.r; + return color; +} + +technique SpriteDrawing +{ + pass P0 + { + PixelShader = compile PS_SHADERMODEL MainPS(); + } +}; \ No newline at end of file diff --git a/templates/template_code/SadConsole.Examples.Demo.CSharp/Content/crt-lottes-mg.fx b/templates/template_code/SadConsole.Examples.Demo.CSharp/Content/crt-lottes-mg.fx new file mode 100644 index 000000000..3aba04124 --- /dev/null +++ b/templates/template_code/SadConsole.Examples.Demo.CSharp/Content/crt-lottes-mg.fx @@ -0,0 +1,317 @@ +#if OPENGL + #define SV_POSITION POSITION + #define VS_SHADERMODEL vs_3_0 + #define PS_SHADERMODEL ps_3_0 +#else + #define VS_SHADERMODEL vs_4_0_level_9_1 + #define PS_SHADERMODEL ps_4_0_level_9_1 + #define HLSL_4 +#endif + +struct VertexShaderOutput +{ + float4 Position : SV_POSITION; + float4 Color : COLOR0; + float2 texCoord : TEXCOORD0; +}; + +// +// PUBLIC DOMAIN CRT STYLED SCAN-LINE SHADER +// +// by Timothy Lottes +// +// This is more along the style of a really good CGA arcade monitor. +// With RGB inputs instead of NTSC. +// The shadow mask example has the mask rotated 90 degrees for less chromatic aberration. +// +// Left it unoptimized to show the theory behind the algorithm. +// +// It is an example what I personally would want as a display option for pixel art games. +// Please take and use, change, or whatever. +// + +// -- config -- // +// #pragma parameter hardScan "hardScan" -8.0 -20.0 0.0 1.0 // default, minimum, maximum, optional step +// #pragma parameter hardPix "hardPix" -3.0 -20.0 0.0 1.0 +// #pragma parameter warpX "warpX" 0.031 0.0 0.125 0.01 +// #pragma parameter warpY "warpY" 0.041 0.0 0.125 0.01 +// #pragma parameter maskDark "maskDark" 0.5 0.0 2.0 0.1 +// #pragma parameter maskLight "maskLight" 1.5 0.0 2.0 0.1 +// #pragma parameter scaleInLinearGamma "scaleInLinearGamma" 1.0 0.0 1.0 1.0 +// #pragma parameter shadowMask "shadowMask" 3.0 0.0 4.0 1.0 +// #pragma parameter brightboost "brightness" 1.0 0.0 2.0 0.05 +// #pragma parameter hardBloomPix "bloom-x soft" -1.5 -2.0 -0.5 0.1 +// #pragma parameter hardBloomScan "bloom-y soft" -2.0 -4.0 -1.0 0.1 +// #pragma parameter bloomAmount "bloom amt" 0.15 0.0 1.0 0.05 +// #pragma parameter shape "filter kernel shape" 2.0 0.0 10.0 0.05 + +float hardScan; +float hardPix; +float warpX; +float warpY; +float maskDark; +float maskLight; +float scaleInLinearGamma; +float shadowMask; +float brightboost; +float hardBloomScan; +float hardBloomPix; +float bloomAmount; +float shape; + +float2 textureSize; +float2 videoSize; +float2 outputSize; + +//Uncomment to reduce instructions with simpler linearization +//(fixes HD3000 Sandy Bridge IGP) +//#define SIMPLE_LINEAR_GAMMA +#define DO_BLOOM 1 + +// ------------- // + +Texture2D decal; +sampler2D DecalSampler = sampler_state +{ + Texture = ; +}; + +float4x4 modelViewProj; + +#define warp float2(warpX,warpY) + +//------------------------------------------------------------------------ + +// sRGB to Linear. +// Assuing using sRGB typed textures this should not be needed. +#ifdef SIMPLE_LINEAR_GAMMA +float ToLinear1(float c) +{ + return c; +} +float3 ToLinear(float3 c) +{ + return c; +} + +float3 ToSrgb(float3 c) +{ + return pow(c, 1.0 / 2.2); +} +#else +float ToLinear1(float c) +{ + if (scaleInLinearGamma==0) return c; + return(c<=0.04045)?c/12.92:pow((c+0.055)/1.055,2.4); +} +float3 ToLinear(float3 c) +{ + if (scaleInLinearGamma==0) return c; + return float3(ToLinear1(c.r),ToLinear1(c.g),ToLinear1(c.b)); +} + +// Linear to sRGB. +// Assuming using sRGB typed textures this should not be needed. +float ToSrgb1(float c) +{ + if (scaleInLinearGamma==0) return c; + return(c<0.0031308?c*12.92:1.055*pow(c,0.41666)-0.055); +} + +float3 ToSrgb(float3 c) +{ + if (scaleInLinearGamma==0) return c; + return float3(ToSrgb1(c.r),ToSrgb1(c.g),ToSrgb1(c.b)); +} +#endif + +// Nearest emulated sample given floating point position and texel offset. +// Also zero's off screen. +float3 Fetch(float2 pos, float2 off, float2 texture_size){ + pos=(floor(pos*texture_size.xy+off)+float2(0.5,0.5))/texture_size.xy; +#ifdef SIMPLE_LINEAR_GAMMA + return ToLinear(brightboost * pow(tex2D(DecalSampler,pos.xy).rgb, 2.2)); +#else + return ToLinear(brightboost * tex2D(DecalSampler,pos.xy).rgb); +#endif +} + +// Distance in emulated pixels to nearest texel. +float2 Dist(float2 pos, float2 texture_size){pos=pos*texture_size.xy;return -(frac(pos)-float2(0.5, 0.5));} + +// 1D Gaussian. +float Gaus(float pos,float scale){return exp2(scale*pow(abs(pos),shape));} + +// 3-tap Gaussian filter along horz line. +float3 Horz3(float2 pos, float off, float2 texture_size){ + float3 b=Fetch(pos,float2(-1.0,off),texture_size); + float3 c=Fetch(pos,float2( 0.0,off),texture_size); + float3 d=Fetch(pos,float2( 1.0,off),texture_size); + float dst=Dist(pos, texture_size).x; + // Convert distance to weight. + float scale=hardPix; + float wb=Gaus(dst-1.0,scale); + float wc=Gaus(dst+0.0,scale); + float wd=Gaus(dst+1.0,scale); + // Return filtered sample. + return (b*wb+c*wc+d*wd)/(wb+wc+wd);} + +// 5-tap Gaussian filter along horz line. +float3 Horz5(float2 pos, float off, float2 texture_size){ + float3 a=Fetch(pos,float2(-2.0,off),texture_size); + float3 b=Fetch(pos,float2(-1.0,off),texture_size); + float3 c=Fetch(pos,float2( 0.0,off),texture_size); + float3 d=Fetch(pos,float2( 1.0,off),texture_size); + float3 e=Fetch(pos,float2( 2.0,off),texture_size); + float dst=Dist(pos, texture_size).x; + // Convert distance to weight. + float scale=hardPix; + float wa=Gaus(dst-2.0,scale); + float wb=Gaus(dst-1.0,scale); + float wc=Gaus(dst+0.0,scale); + float wd=Gaus(dst+1.0,scale); + float we=Gaus(dst+2.0,scale); + // Return filtered sample. + return (a*wa+b*wb+c*wc+d*wd+e*we)/(wa+wb+wc+wd+we);} + +// 7-tap Gaussian filter along horz line. +float3 Horz7(float2 pos, float off, float2 texture_size){ + float3 a=Fetch(pos,float2(-3.0,off),texture_size); + float3 b=Fetch(pos,float2(-2.0,off),texture_size); + float3 c=Fetch(pos,float2(-1.0,off),texture_size); + float3 d=Fetch(pos,float2( 0.0,off),texture_size); + float3 e=Fetch(pos,float2( 1.0,off),texture_size); + float3 f=Fetch(pos,float2( 2.0,off),texture_size); + float3 g=Fetch(pos,float2( 3.0,off),texture_size); + float dst=Dist(pos, texture_size).x; + // Convert distance to weight. + float scale=hardBloomPix; + float wa=Gaus(dst-3.0,scale); + float wb=Gaus(dst-2.0,scale); + float wc=Gaus(dst-1.0,scale); + float wd=Gaus(dst+0.0,scale); + float we=Gaus(dst+1.0,scale); + float wf=Gaus(dst+2.0,scale); + float wg=Gaus(dst+3.0,scale); + // Return filtered sample. + return (a*wa+b*wb+c*wc+d*wd+e*we+f*wf+g*wg)/(wa+wb+wc+wd+we+wf+wg);} + +// Return scanline weight. +float Scan(float2 pos,float off, float2 texture_size){ + float dst=Dist(pos, texture_size).y; + return Gaus(dst+off,hardScan);} + + // Return scanline weight for bloom. +float BloomScan(float2 pos,float off, float2 texture_size){ + float dst=Dist(pos, texture_size).y; + return Gaus(dst+off,hardBloomScan);} + +// Allow nearest three lines to effect pixel. +float3 Tri(float2 pos, float2 texture_size){ + float3 a=Horz3(pos,-1.0, texture_size); + float3 b=Horz5(pos, 0.0, texture_size); + float3 c=Horz3(pos, 1.0, texture_size); + float wa=Scan(pos,-1.0, texture_size); + float wb=Scan(pos, 0.0, texture_size); + float wc=Scan(pos, 1.0, texture_size); + return a*wa+b*wb+c*wc;} + +// Small bloom. +float3 Bloom(float2 pos, float2 texture_size){ + float3 a=Horz5(pos,-2.0, texture_size); + float3 b=Horz7(pos,-1.0, texture_size); + float3 c=Horz7(pos, 0.0, texture_size); + float3 d=Horz7(pos, 1.0, texture_size); + float3 e=Horz5(pos, 2.0, texture_size); + float wa=BloomScan(pos,-2.0, texture_size); + float wb=BloomScan(pos,-1.0, texture_size); + float wc=BloomScan(pos, 0.0, texture_size); + float wd=BloomScan(pos, 1.0, texture_size); + float we=BloomScan(pos, 2.0, texture_size); + return a*wa+b*wb+c*wc+d*wd+e*we;} + +// Distortion of scanlines, and end of screen alpha. +float2 Warp(float2 pos){ + pos=pos*2.0-1.0; + pos*=float2(1.0+(pos.y*pos.y)*warp.x,1.0+(pos.x*pos.x)*warp.y); + return pos*0.5+0.5;} + +// Shadow mask +float3 Mask(float2 pos){ + float3 mask=float3(maskDark,maskDark,maskDark); + + // Very compressed TV style shadow mask. + if (shadowMask == 1) { + float mask_line = maskLight; + float odd=0.0; + if(frac(pos.x/6.0)<0.5) odd = 1.0; + if(frac((pos.y+odd)/2.0)<0.5) mask_line = maskDark; + pos.x=frac(pos.x/3.0); + + if(pos.x<0.333)mask.r=maskLight; + else if(pos.x<0.666)mask.g=maskLight; + else mask.b=maskLight; + mask *= mask_line; + } + + // Aperture-grille. + else if (shadowMask == 2) { + pos.x=frac(pos.x/3.0); + + if(pos.x<0.333)mask.r=maskLight; + else if(pos.x<0.666)mask.g=maskLight; + else mask.b=maskLight; + } + + // Stretched VGA style shadow mask (same as prior shaders). + else if (shadowMask == 3) { + pos.x+=pos.y*3.0; + pos.x=frac(pos.x/6.0); + + if(pos.x<0.333)mask.r=maskLight; + else if(pos.x<0.666)mask.g=maskLight; + else mask.b=maskLight; + } + + // VGA style shadow mask. + else if (shadowMask == 4) { + pos.xy=floor(pos.xy*float2(1.0,0.5)); + pos.x+=pos.y*3.0; + pos.x=frac(pos.x/6.0); + + if(pos.x<0.333)mask.r=maskLight; + else if(pos.x<0.666)mask.g=maskLight; + else mask.b=maskLight; + } + + return mask; +} + +float4 crt_lottes(float2 texture_size, float2 video_size, float2 output_size, float2 tex, sampler2D s0) +{ + float2 pos=Warp(tex.xy*(texture_size.xy/video_size.xy))*(video_size.xy/texture_size.xy); + float3 outColor = Tri(pos, texture_size); + +#ifdef DO_BLOOM + //Add Bloom + outColor.rgb+=Bloom(pos, texture_size)*bloomAmount; +#endif + + if(shadowMask) + outColor.rgb*=Mask(floor(tex.xy*(texture_size.xy/video_size.xy)*output_size.xy)+float2(0.5,0.5)); + + return float4(ToSrgb(outColor.rgb),1.0); +} + +float4 main_fragment(VertexShaderOutput VOUT) : COLOR0 +{ + return crt_lottes(textureSize, videoSize, outputSize, VOUT.texCoord, DecalSampler); +} + +technique +{ + pass + { + PixelShader = compile PS_SHADERMODEL main_fragment(); + } +} diff --git a/templates/template_code/SadConsole.Examples.Demo.CSharp/DemoSecondSurfaceRenderer.cs b/templates/template_code/SadConsole.Examples.Demo.CSharp/DemoSecondSurfaceRenderer.cs index 17e60c4e1..93b5901db 100644 --- a/templates/template_code/SadConsole.Examples.Demo.CSharp/DemoSecondSurfaceRenderer.cs +++ b/templates/template_code/SadConsole.Examples.Demo.CSharp/DemoSecondSurfaceRenderer.cs @@ -26,7 +26,7 @@ internal class SurfaceSecondRenderer : ControlsConsole public SurfaceSecondRenderer() : base(28, 4) { - // Create the other console where the keyboard handler will be set + // Create the other console where the extra render step will draw something _otherSurface = new ScreenSurface(GameSettings.SCREEN_DEMO_WIDTH - 8, GameSettings.SCREEN_DEMO_HEIGHT - this.Height - 3) { Position = (8, this.Height + 3), diff --git a/templates/template_code/SadConsole.Examples.Demo.CSharp/DemoShader.FNA.cs b/templates/template_code/SadConsole.Examples.Demo.CSharp/DemoShader.FNA.cs new file mode 100644 index 000000000..b2f4f54d0 --- /dev/null +++ b/templates/template_code/SadConsole.Examples.Demo.CSharp/DemoShader.FNA.cs @@ -0,0 +1,28 @@ +#if FNA +namespace SadConsole.Examples; + +internal class DemoShader : IDemo +{ + public string Title => "Using Shaders"; + + public string Description => "This demo provides the option to apply a shader to a specific surface or all " + + "of SadConsole.\r\n"; + + public string CodeFile => "DemoShader.FNA.cs"; + + public IScreenSurface CreateDemoScreen() => + new ShaderController(); + + public override string ToString() => + Title; +} + + +class ShaderController : ScreenSurface +{ + public ShaderController() : base(GameSettings.ScreenDemoBounds.Height, GameSettings.ScreenDemoBounds.Height / 2) + { + Surface.Print(0, 0, "Not supported in FNA"); + } +} +#endif diff --git a/templates/template_code/SadConsole.Examples.Demo.CSharp/DemoShader.MonoGame.cs b/templates/template_code/SadConsole.Examples.Demo.CSharp/DemoShader.MonoGame.cs new file mode 100644 index 000000000..0a387a88d --- /dev/null +++ b/templates/template_code/SadConsole.Examples.Demo.CSharp/DemoShader.MonoGame.cs @@ -0,0 +1,143 @@ +#if MONOGAME && !FNA +using Microsoft.Xna.Framework.Graphics; +using SadConsole.DrawCalls; +using SadConsole.Renderers; +using SadConsole.UI; +using SadConsole.UI.Controls; +using Vector2 = Microsoft.Xna.Framework.Vector2; +using XnaColor = Microsoft.Xna.Framework.Color; + +namespace SadConsole.Examples; + +internal class DemoShader : IDemo +{ + public string Title => "Using Shaders"; + + public string Description => "This demo provides the option to apply a shader to a specific surface or all " + + "of SadConsole.\r\n"; + + public string CodeFile => "DemoShader.MonoGame.cs"; + + public IScreenSurface CreateDemoScreen() => + new ShaderController(); + + public override string ToString() => + Title; +} + +internal class ShaderController : ControlsConsole +{ + private static bool _isGlobal = false; + private static bool _isLoaded = false; + private static SadConsole.Host.PostProcessingFX _monoGameComponent; + private readonly ScreenSurface _otherSurface; + private static Effect Effect; + + public ShaderController() : base(28, 4) + { + // Create the other console where the keyboard handler will be set + _otherSurface = new ScreenSurface(GameSettings.SCREEN_DEMO_WIDTH - 8, GameSettings.SCREEN_DEMO_HEIGHT - this.Height - 3) + { + Position = (8, this.Height + 3), + }; + _otherSurface.FillWithRandomGarbage(_otherSurface.Font); + Border.CreateForSurface(_otherSurface, ""); + + Children.Add(_otherSurface); + + // Load the shader once + //if (Effect == null) + { + + } + + // Create the controls + RadioButton radSurfaceOnly = new("Apply to surface"); + radSurfaceOnly.Position = (1, 1); + radSurfaceOnly.IsSelectedChanged += Surface_IsSelectedChanged; + + RadioButton radGlobal = new("Apply globally"); + radGlobal.Position = (1, 2); + radGlobal.IsSelectedChanged += Global_IsSelectedChanged; + + if (_isGlobal) + radGlobal.IsSelected = true; + + Controls.Add(radSurfaceOnly); + Controls.Add(radGlobal); + } + + private void Surface_IsSelectedChanged(object? sender, EventArgs e) + { + RadioButton radioButton = (RadioButton)sender!; + + // Surface-only on + if (radioButton.IsSelected) + { + OutputSurfaceRenderStep step = _otherSurface.Renderer!.Steps.OfType().First(); + + Effect = SadConsole.Game.Instance.MonoGameInstance.Content.Load("crt-lottes-mg"); + Effect.Parameters["hardScan"]?.SetValue(-8.0f); + Effect.Parameters["hardPix"]?.SetValue(-3.0f); + Effect.Parameters["warpX"]?.SetValue(.031f); + Effect.Parameters["warpY"]?.SetValue(0.041f); + Effect.Parameters["maskDark"]?.SetValue(0.5f); + Effect.Parameters["maskLight"]?.SetValue(1.5f); + Effect.Parameters["scaleInLinearGamma"]?.SetValue(1.0f); + Effect.Parameters["shadowMask"]?.SetValue(3.0f); + Effect.Parameters["brightboost"]?.SetValue(1.0f); + Effect.Parameters["hardBloomScan"]?.SetValue(-1.5f); + Effect.Parameters["hardBloomPix"]?.SetValue(-2.0f); + Effect.Parameters["bloomAmount"]?.SetValue(2f); + Effect.Parameters["shape"]?.SetValue(5.0f); + Effect.Parameters["brightboost"].SetValue(0.5f); + Effect.Parameters["textureSize"].SetValue(new Vector2(_otherSurface.AbsoluteArea.Width, _otherSurface.AbsoluteArea.Height)); + Effect.Parameters["outputSize"].SetValue(new Vector2(_otherSurface.AbsoluteArea.Width, _otherSurface.AbsoluteArea.Height)); + Effect.Parameters["videoSize"].SetValue(new Vector2(_otherSurface.AbsoluteArea.Width, _otherSurface.AbsoluteArea.Height)); + + step.ShaderEffect = Effect; + } + + // Surface-only off + else + { + OutputSurfaceRenderStep step = _otherSurface.Renderer!.Steps.OfType().First(); + step.ShaderEffect = null; + } + } + + private void Global_IsSelectedChanged(object? sender, EventArgs e) + { + RadioButton radioButton = (RadioButton)sender!; + + // Global + if (radioButton.IsSelected) + { + _isGlobal = true; + + if (_monoGameComponent == null) + { + // Create the component + _monoGameComponent = new(); + SadConsole.Game.Instance.MonoGameInstance.Components.Add(_monoGameComponent); + } + + _monoGameComponent.Visible = true; + SadConsole.Settings.DoFinalDraw = false; + } + // Surface-only + else + { + _isGlobal = false; + + // Turn off the global one if it's there + if (_monoGameComponent != null) + { + _monoGameComponent.Visible = false; + SadConsole.Settings.DoFinalDraw = true; + } + } + } +} + +#endif diff --git a/templates/template_code/SadConsole.Examples.Demo.CSharp/DemoShader.SFML.cs b/templates/template_code/SadConsole.Examples.Demo.CSharp/DemoShader.SFML.cs new file mode 100644 index 000000000..f7a253362 --- /dev/null +++ b/templates/template_code/SadConsole.Examples.Demo.CSharp/DemoShader.SFML.cs @@ -0,0 +1,28 @@ +#if SFML +namespace SadConsole.Examples; + +internal class DemoShader : IDemo +{ + public string Title => "Using Shaders"; + + public string Description => "This demo provides the option to apply a shader to a specific surface or all " + + "of SadConsole.\r\n"; + + public string CodeFile => "DemoShader.SFML.cs"; + + public IScreenSurface CreateDemoScreen() => + new ShaderController(); + + public override string ToString() => + Title; +} + + +class ShaderController : ScreenSurface +{ + public ShaderController() : base(GameSettings.ScreenDemoBounds.Height, GameSettings.ScreenDemoBounds.Height / 2) + { + Surface.Print(0, 0, "Not supported in SFML"); + } +} +#endif diff --git a/templates/template_code/SadConsole.Examples.Demo.CSharp/Examples.csproj b/templates/template_code/SadConsole.Examples.Demo.CSharp/Examples.csproj index 94b3186bb..fdaa22c52 100644 --- a/templates/template_code/SadConsole.Examples.Demo.CSharp/Examples.csproj +++ b/templates/template_code/SadConsole.Examples.Demo.CSharp/Examples.csproj @@ -27,10 +27,9 @@ - - - - + + + @@ -40,15 +39,27 @@ + + + + + + + - ..\..\..\SadConsole.Host.FNA\FNA\FNA.dll + bin\net8.0\FNA.dll + + + + + diff --git a/templates/template_code/SadConsole.Examples.Demo.CSharp/GameSettings.cs b/templates/template_code/SadConsole.Examples.Demo.CSharp/GameSettings.cs index 1628f669c..29df448b0 100644 --- a/templates/template_code/SadConsole.Examples.Demo.CSharp/GameSettings.cs +++ b/templates/template_code/SadConsole.Examples.Demo.CSharp/GameSettings.cs @@ -21,6 +21,7 @@ static class GameSettings public static IDemo[] Demos = { + new DemoShader(), new DemoSecondSurfaceRenderer(), new DemoRandomScrolling(), new DemoAutoTyping(), diff --git a/templates/template_code/SadConsole.Examples.Demo.CSharp/Program.cs b/templates/template_code/SadConsole.Examples.Demo.CSharp/Program.cs index a80ec0f82..3d1a6b719 100644 --- a/templates/template_code/SadConsole.Examples.Demo.CSharp/Program.cs +++ b/templates/template_code/SadConsole.Examples.Demo.CSharp/Program.cs @@ -4,9 +4,7 @@ Welcome to the SadConsole demo template! This template is the project I use to t SadConsole as I create new features. It's also a great way to see how to write code that uses different SadConsole features. -This template will be updated with each release of SadConsole. For the v10 Alpha 1 -release, this template is a little small and will almost double in size by the -time v10 is completed. +This template will be updated with each release of SadConsole. ========================================================= Important things you should know about this template @@ -31,7 +29,13 @@ box so that you can preview each demo object. The second class is the actual con using SadConsole.Examples; using SadConsole.Configuration; -Settings.WindowTitle = "SadConsole Examples"; +#if FNA +Settings.WindowTitle = "SadConsole Examples (FNA)"; +#elif MONOGAME +Settings.WindowTitle = "SadConsole Examples (MONOGAME)"; +#elif SFML +Settings.WindowTitle = "SadConsole Examples (SFML)"; +#endif Builder startup = new Builder() .SetScreenSize(GameSettings.GAME_WIDTH, GameSettings.GAME_HEIGHT) diff --git a/templates/template_code/SadConsole.Examples.Demo.CSharp/ShaderMonoGameComponent.cs b/templates/template_code/SadConsole.Examples.Demo.CSharp/ShaderMonoGameComponent.cs new file mode 100644 index 000000000..ecff0c56d --- /dev/null +++ b/templates/template_code/SadConsole.Examples.Demo.CSharp/ShaderMonoGameComponent.cs @@ -0,0 +1,70 @@ +#if MONOGAME +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; +using Color = Microsoft.Xna.Framework.Color; + +namespace SadConsole.Host; + +public class PostProcessingFX : DrawableGameComponent +{ + public Effect Effect; + private static int width = 960; + private static int height = 720; + + public PostProcessingFX() : base((Microsoft.Xna.Framework.Game)SadConsole.Game.Instance.MonoGameInstance) + { + DrawOrder = 6; + + Effect = SadConsole.Game.Instance.MonoGameInstance.Content.Load("crt-lottes-mg"); + } + + protected override void LoadContent() + { + base.LoadContent(); + } + + public override void Draw(GameTime gameTime) + { + if (Visible) + { + // Respect the draw flag for sadconsole + width = Host.Global.RenderOutput.Width; + height = Host.Global.RenderOutput.Height; + + + Effect.Parameters["hardScan"]?.SetValue(-8.0f); + Effect.Parameters["hardPix"]?.SetValue(-3.0f); + Effect.Parameters["warpX"]?.SetValue(.031f); + Effect.Parameters["warpY"]?.SetValue(0.041f); + Effect.Parameters["maskDark"]?.SetValue(0.5f); + Effect.Parameters["maskLight"]?.SetValue(1.5f); + Effect.Parameters["scaleInLinearGamma"]?.SetValue(1.0f); + Effect.Parameters["shadowMask"]?.SetValue(3.0f); + Effect.Parameters["brightboost"]?.SetValue(1.0f); + Effect.Parameters["hardBloomScan"]?.SetValue(-1.5f); + Effect.Parameters["hardBloomPix"]?.SetValue(-2.0f); + Effect.Parameters["bloomAmount"]?.SetValue(2f); + Effect.Parameters["shape"]?.SetValue(2.0f); + + + Effect.Parameters["brightboost"].SetValue(0.5f); + Effect.Parameters["textureSize"].SetValue(new Vector2(width, height)); + Effect.Parameters["outputSize"].SetValue(new Vector2(SadConsole.Settings.Rendering.RenderRect.Width, SadConsole.Settings.Rendering.RenderRect.Height)); + Effect.Parameters["videoSize"].SetValue(new Vector2(width, height)); + + Host.Global.SharedSpriteBatch.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied, SamplerState.PointClamp, DepthStencilState.DepthRead, RasterizerState.CullNone); + + //Apply the shader before draw, but after begin. + Effect.CurrentTechnique.Passes[0].Apply(); + + Host.Global.SharedSpriteBatch.Draw(Host.Global.RenderOutput, SadConsole.Settings.Rendering.RenderRect.ToMonoRectangle(), Color.White); + Host.Global.SharedSpriteBatch.End(); + } + } +} +#endif diff --git a/templates/template_code/SadConsole.Project.MonoGame.CSharp/MyGame.csproj b/templates/template_code/SadConsole.Project.MonoGame.CSharp/MyGame.csproj index 43361e759..1376f91ee 100644 --- a/templates/template_code/SadConsole.Project.MonoGame.CSharp/MyGame.csproj +++ b/templates/template_code/SadConsole.Project.MonoGame.CSharp/MyGame.csproj @@ -17,7 +17,8 @@ - + + diff --git a/templates/template_code/SadConsole.Project.SFML.CSharp/MyGame.csproj b/templates/template_code/SadConsole.Project.SFML.CSharp/MyGame.csproj index c0f267542..3c296bca1 100644 --- a/templates/template_code/SadConsole.Project.SFML.CSharp/MyGame.csproj +++ b/templates/template_code/SadConsole.Project.SFML.CSharp/MyGame.csproj @@ -16,7 +16,8 @@ - + + diff --git a/v9SplashScreen.zip b/v9SplashScreen.zip deleted file mode 100644 index 224cd54c4..000000000 Binary files a/v9SplashScreen.zip and /dev/null differ