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
- ..\..\..\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