diff --git a/README.md b/README.md
index 6eda66c..2dbe168 100644
--- a/README.md
+++ b/README.md
@@ -46,23 +46,21 @@ This solution contains several projects that work together to provide the ZX Bas
## Credits
- Icons from [SVG REPO](https://www.svgrepo.com/):
+ - SVG Repo CC0 License
+ - Ananthanath A X Kalaiism in PD License
- Blivesta in MIT License
- Dazzle Ui in CC Attribution License
- Kde in GPL License
- - Konstantin Filatov in CC Attribution
+ - Konstantin Filatov in CC Attribution License
+ - Krystonschwarze in CC Attribution License
- Mohamed Raouf in CC Attribution License
+ - Neuicons in MIT License via
- Phosphor in MIT License
- Steve Schoger in PD License
+ - Siemens in MIT License
- Thewolfkit in CC Attribution License
- Uxaspects in Apache License
- Uxwb in GPL License
- Wishforge.games in CC Attribution License
- Yamatsum in MIT License
- Zest in MIT License
- - Neuicons in MIT License via
- - Dazzle Ui in CC Attribution License
- - Siemens in MIT License
- - Zest in MIT License
- - Ananthanath A X Kalaiism in PD License
- - Siemens in MIT License
- - SVG Repo CC0 License
diff --git a/ZXBSInstaller.Log/ServiceLayer.cs b/ZXBSInstaller.Log/ServiceLayer.cs
index 4c8a65c..be60f49 100644
--- a/ZXBSInstaller.Log/ServiceLayer.cs
+++ b/ZXBSInstaller.Log/ServiceLayer.cs
@@ -1905,7 +1905,8 @@ private static ExternalTools_Version[] GetTBBLueMAMEVersions()
// All versions
var v = new ExternalTools_Version()
{
- DownloadUrl = "https://github.com/Threetwosevensixseven/NexCreator/raw/master/bootroms/tbblue.zip",
+ //DownloadUrl = "https://github.com/Threetwosevensixseven/NexCreator/raw/master/bootroms/tbblue.zip",
+ DownloadUrl = "https://www.specnext.com/forum/download/file.php?id=1164",
OperatingSystem = OperatingSystems.All,
Version = "3.02.03",
};
diff --git a/ZXBSInstaller/ZXBSInstaller.csproj b/ZXBSInstaller/ZXBSInstaller.csproj
index d23c315..dc4319e 100644
--- a/ZXBSInstaller/ZXBSInstaller.csproj
+++ b/ZXBSInstaller/ZXBSInstaller.csproj
@@ -6,7 +6,7 @@
app.manifest
true
zxbs.ico
- 1.0.2.0
+ 1.0.3
diff --git a/ZXBStudio/Assets/zxGraphics_map.png b/ZXBStudio/Assets/zxGraphics_map.png
index 52848ea..91d74bb 100644
Binary files a/ZXBStudio/Assets/zxGraphics_map.png and b/ZXBStudio/Assets/zxGraphics_map.png differ
diff --git a/ZXBStudio/Assets/zxmaps_map.png b/ZXBStudio/Assets/zxmaps_map.png
new file mode 100644
index 0000000..415f68f
Binary files /dev/null and b/ZXBStudio/Assets/zxmaps_map.png differ
diff --git a/ZXBStudio/Assets/zxGraphics_til.png b/ZXBStudio/Assets/zxmaps_til.png
similarity index 54%
rename from ZXBStudio/Assets/zxGraphics_til.png
rename to ZXBStudio/Assets/zxmaps_til.png
index fd075ab..3e12359 100644
Binary files a/ZXBStudio/Assets/zxGraphics_til.png and b/ZXBStudio/Assets/zxmaps_til.png differ
diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/FontGDUExportDialog.axaml.cs b/ZXBStudio/DocumentEditors/ZXGraphics/FontGDUExportDialog.axaml.cs
index f907cec..b0f0144 100644
--- a/ZXBStudio/DocumentEditors/ZXGraphics/FontGDUExportDialog.axaml.cs
+++ b/ZXBStudio/DocumentEditors/ZXGraphics/FontGDUExportDialog.axaml.cs
@@ -175,7 +175,7 @@ private void CreateExample_BIN()
{
case FileTypes.Font:
sb.AppendLine("' Example of use of custom font type");
- sb.AppendLine("POKE (uinteger 23606, @MyFont-256)");
+ sb.AppendLine("POKE (uinteger 23606, @MyFont(0,0)-256)");
sb.AppendLine("PRINT \"Hello World!\"");
sb.AppendLine("STOP");
sb.AppendLine("");
@@ -189,7 +189,7 @@ private void CreateExample_BIN()
break;
case FileTypes.UDG:
sb.AppendLine("' Example of use of UDG/GDU");
- sb.AppendLine("POKE (uinteger 23675, @MyUDG)");
+ sb.AppendLine("POKE (uinteger 23675, @MyUDG(0,0))");
sb.AppendLine("PRINT \"UDG/GDU Table\"");
sb.AppendLine("FOR n=0 TO 20");
sb.AppendLine(" PRINT (144+n);\" - \";CHR(n+65);\": \";CHR(144+n)");
@@ -245,13 +245,13 @@ private void CreateExample_ASM()
sb.AppendLine("' Example of use of the asm format");
if (fileType.FileType == FileTypes.Font)
{
- sb.AppendLine(string.Format("POKE (uinteger 23606, @{0}-256)", txtLabelName.Text));
+ sb.AppendLine(string.Format("POKE (uinteger 23606, @{0}(0,0)-256)", txtLabelName.Text));
sb.AppendLine("PRINT \"Hello World!\"");
sb.AppendLine("STOP");
}
else
{
- sb.AppendLine(string.Format("POKE (uinteger 23675, @{0})", txtLabelName.Text));
+ sb.AppendLine(string.Format("POKE (uinteger 23675, @{0}(0,0))", txtLabelName.Text));
sb.AppendLine("PRINT \"UDG/GDU Table\"");
sb.AppendLine("FOR n=0 TO 20");
sb.AppendLine(" PRINT (144+n);\" - \";CHR(n+65);\": \";CHR(144+n)");
@@ -275,7 +275,7 @@ private void CreateExample_DIM()
switch (fileType.FileType)
{
case FileTypes.UDG:
- sb.AppendLine(string.Format("POKE (uinteger 23675, @{0})", txtLabelName.Text));
+ sb.AppendLine(string.Format("POKE (uinteger 23675, @{0}(0))", txtLabelName.Text));
sb.AppendLine("PRINT \"UDG/GDU Table\"");
sb.AppendLine("FOR n=0 TO 20");
sb.AppendLine(" PRINT (144+n);\" - \";CHR(n+65);\": \";CHR(144+n)");
@@ -283,7 +283,7 @@ private void CreateExample_DIM()
sb.AppendLine("STOP");
break;
case FileTypes.Font:
- sb.AppendLine(string.Format("POKE (uinteger 23606, @{0}-256)", txtLabelName.Text));
+ sb.AppendLine(string.Format("POKE (uinteger 23606, @{0}(0,0)-256)", txtLabelName.Text));
sb.AppendLine("PRINT \"Hello World!\"");
sb.AppendLine("STOP");
break;
diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/ImageViewImportControl.cs b/ZXBStudio/DocumentEditors/ZXGraphics/ImageViewImportControl.cs
index 422b891..8d8b5b1 100644
--- a/ZXBStudio/DocumentEditors/ZXGraphics/ImageViewImportControl.cs
+++ b/ZXBStudio/DocumentEditors/ZXGraphics/ImageViewImportControl.cs
@@ -9,12 +9,15 @@
using SixLabors.ImageSharp.PixelFormats;
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Runtime.Intrinsics.X86;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using ZXBasicStudio.DocumentEditors.NextDows.log;
+using ZXBasicStudio.DocumentEditors.ZXGraphics.neg;
namespace ZXBasicStudio.DocumentEditors.ZXGraphics
{
@@ -136,20 +139,12 @@ public ImageViewImportControl()
public async void LoadImage(IStorageFile file)
{
- try
- {
- await using (var stream = await file.OpenReadAsync())
- {
- imageData = SixLabors.ImageSharp.Image.Load(stream);
- }
- this.InvalidateVisual();
- }
- catch (Exception ex)
- {
- }
+ imageData = ZXGraphics.log.ServiceLayer.LoadImage(file);
+ this.InvalidateVisual();
}
+
public void Refresh()
{
this.InvalidateVisual();
@@ -200,7 +195,7 @@ public override void Render(DrawingContext context)
}
else
{
- context.FillRectangle(brushWhite, r0);
+ context.FillRectangle(brushWhite, r0);
}
pair = !pair;
}
@@ -268,13 +263,13 @@ public override void Render(DrawingContext context)
int x2 = SpriteWidth * _Zoom;
int y2 = SpriteHeight * _Zoom;
- var r0 = new Rect(x2, 0, 400-x2, 400);
+ var r0 = new Rect(x2, 0, 400 - x2, 400);
context.FillRectangle(brushMask, r0);
- var r1 = new Rect(0, y2, x2, 400-y2);
+ var r1 = new Rect(0, y2, x2, 400 - y2);
context.FillRectangle(brushMask, r1);
context.DrawRectangle(penRed, new Rect(-1, -1, x2, y2));
- }
+ }
}
catch (Exception ex)
{
@@ -282,7 +277,7 @@ public override void Render(DrawingContext context)
}
}
-#endregion
+ #endregion
#region Mouse
diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/SelectExportTypeControl.axaml.cs b/ZXBStudio/DocumentEditors/ZXGraphics/SelectExportTypeControl.axaml.cs
index aca2dab..e17021f 100644
--- a/ZXBStudio/DocumentEditors/ZXGraphics/SelectExportTypeControl.axaml.cs
+++ b/ZXBStudio/DocumentEditors/ZXGraphics/SelectExportTypeControl.axaml.cs
@@ -46,6 +46,38 @@ public SelectExportTypeControl()
}
+ public bool InitializeTile()
+ {
+ ExportTypesList = new List();
+ ExportTypesList.Add(new ExportTypeDescrioptionItem()
+ {
+ Description = "Boriel Basic's PutChars embeded library.",
+ ExportType = ExportTypes.PutChars,
+ Image = "/Svg/Seal.svg",
+ Name = "PutChars"
+ });
+ lstOptions.ItemsSource = ExportTypesList;
+ lstOptions.SelectionChanged += LstOptions_SelectionChanged;
+ return true;
+ }
+
+
+ public bool InitializeMap()
+ {
+ ExportTypesList = new List();
+ ExportTypesList.Add(new ExportTypeDescrioptionItem()
+ {
+ Description = "Raw array of bytes, one byte for tile",
+ ExportType = ExportTypes.Array,
+ Image = "/Svg/boxes-stacked-solid.svg",
+ Name = "Array"
+ });
+ lstOptions.ItemsSource = ExportTypesList;
+ lstOptions.SelectionChanged += LstOptions_SelectionChanged;
+ return true;
+ }
+
+
public bool InitializeSprite()
{
ExportTypesList = new List();
diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/SpriteEditor.axaml b/ZXBStudio/DocumentEditors/ZXGraphics/SpriteEditor.axaml
index f38140e..1513953 100644
--- a/ZXBStudio/DocumentEditors/ZXGraphics/SpriteEditor.axaml
+++ b/ZXBStudio/DocumentEditors/ZXGraphics/SpriteEditor.axaml
@@ -1,193 +1,193 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Zoom 24x
-
- Frame
-
-
-
-
-
- Paper
-
-
- 0
-
-
-
-
-
- Ink
-
-
- 1
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:svg="using:Avalonia.Svg.Skia"
+ xmlns:local="using:ZXBasicStudio.DocumentEditors.ZXGraphics"
+ xmlns:zxd="using:ZXBasicStudio.Controls.DockSystem"
+ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+ Focusable="True"
+ Background="#ff202020"
+ x:Class="ZXBasicStudio.DocumentEditors.ZXGraphics.SpriteEditor">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Zoom 24x
+
+ Frame
+
+
+
+
+
+ Paper
+
+
+ 0
+
+
+
+
+
+ Ink
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/SpriteEditor.axaml.cs b/ZXBStudio/DocumentEditors/ZXGraphics/SpriteEditor.axaml.cs
index 3d27d3f..ac5948d 100644
--- a/ZXBStudio/DocumentEditors/ZXGraphics/SpriteEditor.axaml.cs
+++ b/ZXBStudio/DocumentEditors/ZXGraphics/SpriteEditor.axaml.cs
@@ -133,12 +133,12 @@ public override bool SaveDocument(TextWriter OutputLog)
try
{
var masterList = SpritePatternsList.Select(d => d.SpriteData).ToArray();
- var sprList = new List();
+ var sprList = new List();
foreach (var spr in masterList)
{
sprList.Add(spr); //.Clonar());
}
- foreach (Sprite spr in sprList)
+ foreach (ZXMapsTile spr in sprList)
{
if (spr == null)
{
@@ -259,7 +259,7 @@ private void _Initialize(string fileName)
var dataS = Encoding.UTF8.GetString(data);
if (!string.IsNullOrEmpty(dataS))
{
- Sprite[] sprites = dataS.Deserializar();
+ ZXMapsTile[] sprites = dataS.Deserializar();
foreach (var sprite in sprites)
{
@@ -560,7 +560,7 @@ private void SpriteProperties_FrameUpdate(SpritePropertiesControl sender, string
}
- private void SpriteList_AddSprite(Sprite spriteData)
+ private void SpriteList_AddSprite(ZXMapsTile spriteData)
{
SpritePatternControl selectedSprite = null;
int id = 0;
@@ -631,7 +631,7 @@ private void SpriteList_Delete(SpritePatternControl control)
}
- private void SpriteList_Insert(Sprite spriteData)
+ private void SpriteList_Insert(ZXMapsTile spriteData)
{
var current = spriteData.CurrentFrame;
var curPat = spriteData.Patterns[current];
@@ -650,7 +650,7 @@ private void SpriteList_Insert(Sprite spriteData)
}
- private void SpriteList_Clone(Sprite spriteData)
+ private void SpriteList_Clone(ZXMapsTile spriteData)
{
SpritePatternControl selectedSprite = null;
int id = SpritePatternsList.Where(d => d.SpriteData != null).Max(d => d.SpriteData.Id) + 1;
@@ -659,7 +659,7 @@ private void SpriteList_Clone(Sprite spriteData)
{
return;
}
- var sd = spriteData.Clonar();
+ var sd = spriteData.Clonar();
sd.Id = id;
sd.Name = spriteData.Name + " - copy";
spc.SpriteData = sd;
@@ -673,7 +673,7 @@ private void SpriteList_Clone(Sprite spriteData)
}
- private void SpriteList_Modified(Sprite spriteData)
+ private void SpriteList_Modified(ZXMapsTile spriteData)
{
if (!_Modified)
{
@@ -1174,7 +1174,7 @@ private void Import()
}
- private void Import_Command(Sprite sprite, string command)
+ private void Import_Command(ZXMapsTile sprite, string command)
{
switch (command)
{
diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/SpriteExportDialog.axaml.cs b/ZXBStudio/DocumentEditors/ZXGraphics/SpriteExportDialog.axaml.cs
index 4d64348..e25c1a0 100644
--- a/ZXBStudio/DocumentEditors/ZXGraphics/SpriteExportDialog.axaml.cs
+++ b/ZXBStudio/DocumentEditors/ZXGraphics/SpriteExportDialog.axaml.cs
@@ -24,7 +24,7 @@ namespace ZXBasicStudio.DocumentEditors.ZXGraphics
public partial class SpriteExportDialog : Window
{
private string fileName = "";
- private IEnumerable sprites = null;
+ private IEnumerable sprites = null;
private FileTypeConfig fileType = null;
@@ -47,7 +47,7 @@ public SpriteExportDialog()
}
- public bool Initialize(string fileName, IEnumerable spritesData)
+ public bool Initialize(string fileName, IEnumerable spritesData)
{
this.fileName = fileName;
this.sprites = spritesData;
diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/SpriteImportDialog.axaml.cs b/ZXBStudio/DocumentEditors/ZXGraphics/SpriteImportDialog.axaml.cs
index 47e7129..3f3d917 100644
--- a/ZXBStudio/DocumentEditors/ZXGraphics/SpriteImportDialog.axaml.cs
+++ b/ZXBStudio/DocumentEditors/ZXGraphics/SpriteImportDialog.axaml.cs
@@ -44,9 +44,9 @@ public partial class SpriteImportDialog : Window, IDisposable
private ExportConfig exportConfig = null;
*/
- private Action CallBackCommand = null;
- private IEnumerable sprites = null;
- private Sprite sprite = null;
+ private Action CallBackCommand = null;
+ private IEnumerable sprites = null;
+ private ZXMapsTile sprite = null;
private string spriteName = "";
private GraphicsModes spriteMode = GraphicsModes.Monochrome;
@@ -80,7 +80,7 @@ public SpriteImportDialog()
btnImport.Tapped += BtnImport_Tapped;
}
- public bool Initialize(string fileName, IEnumerable sprites, Action callBackCommand)
+ public bool Initialize(string fileName, IEnumerable sprites, Action callBackCommand)
{
this.CallBackCommand = callBackCommand;
this.sprites = sprites;
@@ -119,12 +119,13 @@ public void Dispose()
private async void BtnFile_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
{
- var fileTypes = new FilePickerFileType[4];
+ var fileTypes = new FilePickerFileType[5];
fileTypes[0] = new FilePickerFileType("All files") { Patterns = new[] { "*", "*.*" } };
fileTypes[1] = new FilePickerFileType("BMP files") { Patterns = new[] { "*.bmp" } };
fileTypes[2] = new FilePickerFileType("JPG files") { Patterns = new[] { "*.jpg", "*.jpeg" } };
fileTypes[3] = new FilePickerFileType("PNG files") { Patterns = new[] { "*.png" } };
- /*fileTypes[4] = new FilePickerFileType("SCR Spectrum screen files") { Patterns = new[] { "*.scr" } };*/
+ fileTypes[4] = new FilePickerFileType("ZX Paintbrush SCR files") { Patterns = new[] { "*.scr" } };
+ /*fileTypes[5] = new FilePickerFileType("SCR Spectrum screen files") { Patterns = new[] { "*.scr" } };*/
var select = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
{
@@ -247,7 +248,7 @@ private void _UpdatePreview()
GetProperties();
- var s = new Sprite();
+ var s = new ZXMapsTile();
s.CurrentFrame = 0;
s.DefaultColor = 7;
s.Export = true;
@@ -622,7 +623,7 @@ private async void Import()
for (int n = 0; n < sprite.Patterns.Count(); n++)
{
- var spr = sprite.Clonar();
+ var spr = sprite.Clonar();
spr.Patterns = spr.Patterns.Skip(n).Take(1).ToList();
spr.Frames = 1;
spr.Name = sprName + "_" + n.ToString();
diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/SpritePatternControl.axaml.cs b/ZXBStudio/DocumentEditors/ZXGraphics/SpritePatternControl.axaml.cs
index 05cbb5d..ce98b17 100644
--- a/ZXBStudio/DocumentEditors/ZXGraphics/SpritePatternControl.axaml.cs
+++ b/ZXBStudio/DocumentEditors/ZXGraphics/SpritePatternControl.axaml.cs
@@ -18,7 +18,7 @@ public partial class SpritePatternControl : UserControl
///
/// Sprite data
///
- public Sprite SpriteData
+ public ZXMapsTile SpriteData
{
get
{
@@ -82,7 +82,7 @@ public bool SettingsChanged
private bool _SettingsChanged = false;
private bool refreshing = false;
private bool newSprite = true;
- private Sprite _SpriteData = null;
+ private ZXMapsTile _SpriteData = null;
#endregion
@@ -101,7 +101,7 @@ public SpritePatternControl()
/// Data of the sprite, if is null, the "Add" icon is visible and no properties are shown
/// CallBak for actions command, line "ADD", "CLONE", "DELETE" or "SELECTED"
///
- public bool Initialize(Sprite spriteData, Action callBackCommand)
+ public bool Initialize(ZXMapsTile spriteData, Action callBackCommand)
{
this.SpriteData = spriteData;
this.CallBackCommand = callBackCommand;
@@ -271,7 +271,7 @@ public void ApplySettings(bool askForApply)
}
else
{
- var sp = _SpriteData.Clonar();
+ var sp = _SpriteData.Clonar();
if (sp.Width != _SpriteData.Width || sp.Height != _SpriteData.Height)
{
@@ -321,7 +321,7 @@ public void ApplySettings(bool askForApply)
private void AddNew()
{
- var sp = new Sprite()
+ var sp = new ZXMapsTile()
{
CurrentFrame = 0,
DefaultColor = 0,
diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/SpritePatternEditor.axaml.cs b/ZXBStudio/DocumentEditors/ZXGraphics/SpritePatternEditor.axaml.cs
index 1c280e8..4f91b95 100644
--- a/ZXBStudio/DocumentEditors/ZXGraphics/SpritePatternEditor.axaml.cs
+++ b/ZXBStudio/DocumentEditors/ZXGraphics/SpritePatternEditor.axaml.cs
@@ -26,7 +26,7 @@ public partial class SpritePatternEditor : UserControl
///
/// Actual Sprite data
///
- public Sprite SpriteData
+ public ZXMapsTile SpriteData
{
get
{
@@ -110,7 +110,7 @@ public bool ViewAttributes
#region Private fields
- private Sprite _SpriteData = null;
+ private ZXMapsTile _SpriteData = null;
private int _Zoom = 24;
private Action CallBackCommand = null;
private int? lastId = null;
diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/SpritePreviewControl.axaml.cs b/ZXBStudio/DocumentEditors/ZXGraphics/SpritePreviewControl.axaml.cs
index c02bb82..e815456 100644
--- a/ZXBStudio/DocumentEditors/ZXGraphics/SpritePreviewControl.axaml.cs
+++ b/ZXBStudio/DocumentEditors/ZXGraphics/SpritePreviewControl.axaml.cs
@@ -23,7 +23,7 @@ public partial class SpritePreviewControl : UserControl, IDisposable
///
/// Sprite data
///
- public Sprite? SpriteData { get; set; }
+ public ZXMapsTile? SpriteData { get; set; }
public int Zoom { get; set; } = 4;
#endregion
@@ -60,7 +60,7 @@ public SpritePreviewControl()
/// Data of the sprite, if is null, the "Add" icon is visible and no properties are shown
/// CallBak for actions command, line "ADD", "CLONE", "DELETE" or "SELECTED"
///
- public bool Initialize(Sprite spriteData)
+ public bool Initialize(ZXMapsTile spriteData)
{
this.SpriteData = spriteData;
diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/SpritePropertiesControl.axaml.cs b/ZXBStudio/DocumentEditors/ZXGraphics/SpritePropertiesControl.axaml.cs
index c5b3b74..7502743 100644
--- a/ZXBStudio/DocumentEditors/ZXGraphics/SpritePropertiesControl.axaml.cs
+++ b/ZXBStudio/DocumentEditors/ZXGraphics/SpritePropertiesControl.axaml.cs
@@ -20,7 +20,7 @@ public partial class SpritePropertiesControl : UserControl
///
/// Sprite data
///
- public Sprite SpriteData
+ public ZXMapsTile SpriteData
{
get
{
@@ -68,7 +68,7 @@ public bool IsSelected
private bool _SettingsChanged = false;
private bool refreshing = false;
private bool newSprite = true;
- private Sprite _SpriteData = null;
+ private ZXMapsTile _SpriteData = null;
#endregion
@@ -88,7 +88,7 @@ public SpritePropertiesControl()
/// Data of the sprite, if is null, the "Add" icon is visible and no properties are shown
/// CallBak for actions command, line "ADD", "CLONE", "DELETE" or "SELECTED"
///
- public bool Initialize(Sprite spriteData, Action callBackCommand)
+ public bool Initialize(ZXMapsTile spriteData, Action callBackCommand)
{
this.SpriteData = spriteData;
this.CallBackCommand = callBackCommand;
diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/ZXGridImageView.axaml.cs b/ZXBStudio/DocumentEditors/ZXGraphics/ZXGridImageView.axaml.cs
index e063b0e..e8dbeb5 100644
--- a/ZXBStudio/DocumentEditors/ZXGraphics/ZXGridImageView.axaml.cs
+++ b/ZXBStudio/DocumentEditors/ZXGraphics/ZXGridImageView.axaml.cs
@@ -5,6 +5,7 @@
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Skia;
+using FFmpeg.AutoGen;
using SkiaSharp;
using System;
using System.Diagnostics;
@@ -14,7 +15,8 @@ namespace ZXBasicStudio.DocumentEditors.ZXGraphics
{
public partial class ZXGridImageView : UserControl
{
- public bool ViewAttributes {
+ public bool ViewAttributes
+ {
get
{
return _ViewAttributes;
@@ -51,6 +53,7 @@ public int Zoom
}
}
+
public SKColor GridColor
{
get => gridColor;
@@ -61,6 +64,18 @@ public SKColor GridColor
}
}
+
+ public SKColor Grid8x8Color
+ {
+ get => grid8x8Color;
+ set
+ {
+ grid8x8Color = value;
+ InvalidateVisual();
+ }
+ }
+
+
public IZXBitmap? BackgroundImage
{
get => backgroundImage;
@@ -79,6 +94,7 @@ public IZXBitmap? BackgroundImage
}
public bool Show8x8Grid { get; set; } = true;
+ public bool ShowTileGrid { get; set; } = true;
public ZXGridImageView()
{
@@ -123,34 +139,51 @@ private void EnsureGrid()
using var paint = new SKPaint { Color = gridColor, StrokeWidth = 1, IsAntialias = false };
using var paintGrid = new SKPaint { Color = grid8x8Color, StrokeWidth = 1, IsAntialias = false };
- //Draw vertical lines
- for (int x = 0; x < gridImage.PixelSize.Width + 1; x += Zoom + 1)
+ if (Show8x8Grid)
{
- if ((x % 8) == 0)
+ //Draw vertical lines
+ for (int x = 0; x < gridImage.PixelSize.Width + 1; x += Zoom + 1)
{
- canvas.DrawLine(new SKPoint(x, 0), new SKPoint(x, gridImage.PixelSize.Height), paintGrid);
+ if ((x % 8) == 0)
+ {
+ canvas.DrawLine(new SKPoint(x, 0), new SKPoint(x, gridImage.PixelSize.Height), paintGrid);
+ }
+ else
+ {
+ canvas.DrawLine(new SKPoint(x, 0), new SKPoint(x, gridImage.PixelSize.Height), paint);
+ }
}
- else
+
+ //Draw horizontal lines
+ for (int y = 0; y < gridImage.PixelSize.Height + 1; y += Zoom + 1)
{
- canvas.DrawLine(new SKPoint(x, 0), new SKPoint(x, gridImage.PixelSize.Height), paint);
+ if ((y % 8) == 0)
+ {
+ canvas.DrawLine(new SKPoint(0, y), new SKPoint(gridImage.PixelSize.Width, y), paintGrid);
+ }
+ else
+ {
+ canvas.DrawLine(new SKPoint(0, y), new SKPoint(gridImage.PixelSize.Width, y), paint);
+ }
}
}
- //Draw horizontal lines
- for (int y = 0; y < gridImage.PixelSize.Height + 1; y += Zoom + 1)
+ if (ShowTileGrid)
{
- if ((y % 8) == 0)
{
- canvas.DrawLine(new SKPoint(0, y), new SKPoint(gridImage.PixelSize.Width, y), paintGrid);
+ int limit = gridImage.PixelSize.Width + 1;
+ canvas.DrawLine(new SKPoint(0, 0), new SKPoint(0, gridImage.PixelSize.Height), paintGrid);
+ canvas.DrawLine(new SKPoint(limit, 0), new SKPoint(limit, gridImage.PixelSize.Height), paintGrid);
}
- else
{
- canvas.DrawLine(new SKPoint(0, y), new SKPoint(gridImage.PixelSize.Width, y), paint);
+ int limit = gridImage.PixelSize.Height + 1;
+ canvas.DrawLine(new SKPoint(0, 0), new SKPoint(gridImage.PixelSize.Height, 0), paintGrid);
+ canvas.DrawLine(new SKPoint(0, limit), new SKPoint(gridImage.PixelSize.Height, limit), paintGrid);
}
}
-
}
+
public override void Render(DrawingContext context)
{
EnsureGrid();
@@ -163,20 +196,22 @@ public override void Render(DrawingContext context)
context.DrawImage(backgroundImage, new Rect(0, 0, gridImage.Size.Width, gridImage.Size.Height));
- if (zoom > 4)
+ if (zoom > 4 || ShowTileGrid)
+ {
context.DrawImage(gridImage, new Rect(0, 0, gridImage.Size.Width, gridImage.Size.Height));
+ }
}
protected override void OnPointerPressed(PointerPressedEventArgs e)
{
base.OnPointerPressed(e);
- Debug.WriteLine($"OnPointerPressed: {e.GetPosition(this)}");
+ //Debug.WriteLine($"OnPointerPressed: {e.GetPosition(this)}");
}
protected override void OnPointerMoved(PointerEventArgs e)
{
base.OnPointerMoved(e);
- Debug.WriteLine($"OnPointerMoved: {this.Width}/{this.Height} : {e.GetPosition(this)}");
+ //Debug.WriteLine($"OnPointerMoved: {this.Width}/{this.Height} : {e.GetPosition(this)}");
}
}
}
\ No newline at end of file
diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/ZXSpriteImage.cs b/ZXBStudio/DocumentEditors/ZXGraphics/ZXSpriteImage.cs
index 4b03213..eff30ec 100644
--- a/ZXBStudio/DocumentEditors/ZXGraphics/ZXSpriteImage.cs
+++ b/ZXBStudio/DocumentEditors/ZXGraphics/ZXSpriteImage.cs
@@ -30,7 +30,7 @@ public ZXSpriteImage()
Clear(Colors.White);
}
- public ZXSpriteImage(Sprite Sprite, int FrameNumber)
+ public ZXSpriteImage(ZXMapsTile Sprite, int FrameNumber)
{
bitmap = new WriteableBitmap(new PixelSize(Sprite.Width, Sprite.Height), new Vector(72, 72), Avalonia.Platform.PixelFormat.Rgba8888, Avalonia.Platform.AlphaFormat.Opaque);
@@ -60,7 +60,7 @@ public unsafe void Clear(Color ClearColor)
IsEmpty = true;
}
- public unsafe void RenderSprite(Sprite Sprite, int FrameNumber)
+ public unsafe void RenderSprite(ZXMapsTile Sprite, int FrameNumber)
{
try
{
@@ -151,7 +151,7 @@ uint ToRgba(Color Color)
{
return (uint)((255 << 24) | (Color.B << 16) | (Color.G << 8) | Color.R);
}
- private AttributeColor GetAttribute(Sprite Sprite, Pattern Pattern, int X, int Y)
+ private AttributeColor GetAttribute(ZXMapsTile Sprite, Pattern Pattern, int X, int Y)
{
int cW = Sprite.Width / 8;
int cX = X / 8;
diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/log/ExportManager.cs b/ZXBStudio/DocumentEditors/ZXGraphics/log/ExportManager.cs
index b0c94ef..20369ad 100644
--- a/ZXBStudio/DocumentEditors/ZXGraphics/log/ExportManager.cs
+++ b/ZXBStudio/DocumentEditors/ZXGraphics/log/ExportManager.cs
@@ -1,5 +1,6 @@
using CoreSpectrum.SupportClasses;
using Newtonsoft.Json;
+using SixLabors.ImageSharp.Formats.Gif;
using System;
using System.Collections.Generic;
using System.IO;
@@ -11,6 +12,7 @@
using ZXBasicStudio.Common;
using ZXBasicStudio.Common.TAPTools;
using ZXBasicStudio.DocumentEditors.ZXGraphics.neg;
+using ZXBasicStudio.DocumentEditors.ZXMaps.Neg;
using ZXBasicStudio.DocumentModel.Enums;
using ZXBasicStudio.DocumentModel.Interfaces;
using static System.Runtime.InteropServices.JavaScript.JSType;
@@ -88,7 +90,24 @@ public bool Build(string BuildPath, ZXBuildStage Stage, ZXBuildType BuildType, Z
}
}
break;
-
+ case FileTypes.Tile:
+ {
+ var tiles = CreateTiles(fileData);
+ if (!ExportTiles(exportConfig, tiles))
+ {
+ return false;
+ }
+ }
+ break;
+ case FileTypes.Map:
+ {
+ var map = CreateMap(fileData);
+ if (!ExportMap(exportConfig, map))
+ {
+ return false;
+ }
+ }
+ break;
}
}
@@ -389,12 +408,12 @@ public static void Export_TAP(FileTypeConfig fileType, Pattern[] patterns, Expor
#region Sprites
- private Sprite[] CreateSprites(byte[] fileData)
+ private ZXMapsTile[] CreateSprites(byte[] fileData)
{
try
{
var dataS = Encoding.UTF8.GetString(fileData);
- var sprites = dataS.Deserializar();
+ var sprites = dataS.Deserializar();
// Check attributes for ZX Spectrum mode
foreach (var sprite in sprites)
@@ -433,7 +452,7 @@ private Sprite[] CreateSprites(byte[] fileData)
/// ExportConfig of the file
/// Sprites to convert
///
- public bool ExportSprites(ExportConfig exportConfig, IEnumerable sprites)
+ public bool ExportSprites(ExportConfig exportConfig, IEnumerable sprites)
{
// Export depending on the type
string exportedData = "";
@@ -480,7 +499,7 @@ public bool ExportSprites(ExportConfig exportConfig, IEnumerable sprites
/// ExportConfig information
/// Sprites to convert
/// string with the conversion commands for the export dialog samble textbox
- public static string Export_Sprite_PutChars(ExportConfig exportConfig, IEnumerable sprites)
+ public static string Export_Sprite_PutChars(ExportConfig exportConfig, IEnumerable sprites)
{
string res = Export_Sprite_PutChars_Check(exportConfig, sprites);
if (res.StartsWith("ERROR:"))
@@ -506,7 +525,7 @@ public static string Export_Sprite_PutChars(ExportConfig exportConfig, IEnumerab
}
}
- private static string Export_Sprite_PutChars_Check(ExportConfig exportConfig, IEnumerable sprites)
+ private static string Export_Sprite_PutChars_Check(ExportConfig exportConfig, IEnumerable sprites)
{
foreach (var sprite in sprites)
{
@@ -529,7 +548,7 @@ private static string Export_Sprite_PutChars_Check(ExportConfig exportConfig, IE
#region DIM
- public static string Export_Sprite_PutChars_DIM(ExportConfig exportConfig, IEnumerable sprites)
+ public static string Export_Sprite_PutChars_DIM(ExportConfig exportConfig, IEnumerable sprites)
{
int min = 0;
switch (exportConfig.ArrayBase)
@@ -740,7 +759,7 @@ public static string Export_Sprite_PutChars_DIM(ExportConfig exportConfig, IEnum
}
- private static string Export_Sprite_PutChars_Pattern(Sprite sprite, int n, ExportConfig exportConfig, int firstItem)
+ private static string Export_Sprite_PutChars_Pattern(ZXMapsTile sprite, int n, ExportConfig exportConfig, int firstItem)
{
var sb = new StringBuilder();
@@ -805,7 +824,7 @@ private static string Export_Sprite_PutChars_Pattern(Sprite sprite, int n, Expor
}
- private static string Export_Sprite_PutChars_Attribute(Sprite sprite, int n, ExportConfig exportConfig, int firstItem)
+ private static string Export_Sprite_PutChars_Attribute(ZXMapsTile sprite, int n, ExportConfig exportConfig, int firstItem)
{
var sb = new StringBuilder();
@@ -896,7 +915,7 @@ private static string Export_Sprite_PutChars_Attribute(Sprite sprite, int n, Exp
#region ASM
- public static string Export_Sprite_PutChars_ASM(ExportConfig exportConfig, IEnumerable sprites)
+ public static string Export_Sprite_PutChars_ASM(ExportConfig exportConfig, IEnumerable sprites)
{
int min = 0;
@@ -988,7 +1007,7 @@ public static string Export_Sprite_PutChars_ASM(ExportConfig exportConfig, IEnum
}
- private static string Export_Sprite_PutChars_Pattern_ASM(Sprite sprite, int n, ExportConfig exportConfig)
+ private static string Export_Sprite_PutChars_Pattern_ASM(ZXMapsTile sprite, int n, ExportConfig exportConfig)
{
var sb = new StringBuilder();
@@ -1030,7 +1049,7 @@ private static string Export_Sprite_PutChars_Pattern_ASM(Sprite sprite, int n, E
}
- private static string Export_Sprite_PutChars_Attribute_ASM(Sprite sprite, int n, ExportConfig exportConfig)
+ private static string Export_Sprite_PutChars_Attribute_ASM(ZXMapsTile sprite, int n, ExportConfig exportConfig)
{
var sb = new StringBuilder();
@@ -1087,7 +1106,7 @@ private static string Export_Sprite_PutChars_Attribute_ASM(Sprite sprite, int n,
#region TAP and BIN
- public static string Export_Sprite_PutChars_BIN(ExportConfig exportConfig, IEnumerable sprites)
+ public static string Export_Sprite_PutChars_BIN(ExportConfig exportConfig, IEnumerable sprites)
{
var binData = Export_Sprite_PutChars_GetBinaryData(sprites);
ServiceLayer.Files_SaveFileData(exportConfig.ExportFilePath, binData);
@@ -1095,7 +1114,7 @@ public static string Export_Sprite_PutChars_BIN(ExportConfig exportConfig, IEnum
}
- public static string Export_Sprite_PutChars_TAP(ExportConfig exportConfig, IEnumerable sprites)
+ public static string Export_Sprite_PutChars_TAP(ExportConfig exportConfig, IEnumerable sprites)
{
var binData = Export_Sprite_PutChars_GetBinaryData(sprites);
binData = ServiceLayer.Bin2Tap(exportConfig.ZXFileName, exportConfig.ZXAddress, binData);
@@ -1104,7 +1123,7 @@ public static string Export_Sprite_PutChars_TAP(ExportConfig exportConfig, IEnum
}
- private static byte[] Export_Sprite_PutChars_GetBinaryData(IEnumerable sprites)
+ private static byte[] Export_Sprite_PutChars_GetBinaryData(IEnumerable sprites)
{
var binData = new List();
@@ -1137,7 +1156,7 @@ private static byte[] Export_Sprite_PutChars_GetBinaryData(IEnumerable s
/// ExportConfig information
/// Sprites to convert
/// string with the conversion commands for the export dialog samble textbox
- public static string Export_Sprite_MaskedSprites(ExportConfig exportConfig, IEnumerable sprites)
+ public static string Export_Sprite_MaskedSprites(ExportConfig exportConfig, IEnumerable sprites)
{
var res = Export_Sprite_MaskedSprites_Check(exportConfig, sprites);
if (res.StartsWith("ERROR:"))
@@ -1172,7 +1191,7 @@ public static string Export_Sprite_MaskedSprites(ExportConfig exportConfig, IEnu
/// masking, frame count, and dimensions if it is marked for export.
/// A string indicating the result of the validation. Returns "OK" if all sprites are valid for export;
/// otherwise, returns an error message describing the first encountered issue.
- private static string Export_Sprite_MaskedSprites_Check(ExportConfig exportConfig, IEnumerable sprites)
+ private static string Export_Sprite_MaskedSprites_Check(ExportConfig exportConfig, IEnumerable sprites)
{
string txt = "";
foreach (var sprite in sprites)
@@ -1205,7 +1224,7 @@ private static string Export_Sprite_MaskedSprites_Check(ExportConfig exportConfi
}
- private static byte[] Export_Sprite_MaskedSprites_GenerateData(ExportConfig exportConfig, Sprite sprite)
+ private static byte[] Export_Sprite_MaskedSprites_GenerateData(ExportConfig exportConfig, ZXMapsTile sprite)
{
try
{
@@ -1240,7 +1259,7 @@ private static byte[] Export_Sprite_MaskedSprites_GenerateData(ExportConfig expo
#region DIM
- public static string Export_Sprite_MaskedSprites_DIM(ExportConfig exportConfig, IEnumerable sprites)
+ public static string Export_Sprite_MaskedSprites_DIM(ExportConfig exportConfig, IEnumerable sprites)
{
var sb = new StringBuilder();
sb.AppendLine("'- Sprite definitions --------------------------------------");
@@ -1326,7 +1345,7 @@ public static string Export_Sprite_MaskedSprites_DIM(ExportConfig exportConfig,
#region ASM
- public static string Export_Sprite_MaskedSprites_ASM(ExportConfig exportConfig, IEnumerable sprites)
+ public static string Export_Sprite_MaskedSprites_ASM(ExportConfig exportConfig, IEnumerable sprites)
{
var sb = new StringBuilder();
sb.AppendLine("'- Sprite definitions --------------------------------------");
@@ -1387,7 +1406,7 @@ public static string Export_Sprite_MaskedSprites_ASM(ExportConfig exportConfig,
#region TAP and BIN
- public static string Export_Sprite_MaskedSprites_BIN(ExportConfig exportConfig, IEnumerable sprites)
+ public static string Export_Sprite_MaskedSprites_BIN(ExportConfig exportConfig, IEnumerable sprites)
{
var binData = new List();
foreach (var sprite in sprites)
@@ -1408,7 +1427,7 @@ public static string Export_Sprite_MaskedSprites_BIN(ExportConfig exportConfig,
}
- public static string Export_Sprite_MaskedSprites_TAP(ExportConfig exportConfig, IEnumerable sprites)
+ public static string Export_Sprite_MaskedSprites_TAP(ExportConfig exportConfig, IEnumerable sprites)
{
var binData = new List();
foreach (var sprite in sprites)
@@ -1436,5 +1455,794 @@ public static string Export_Sprite_MaskedSprites_TAP(ExportConfig exportConfig,
#endregion
+
+
+ #region Tiles
+
+ private ZXMapsTile[] CreateTiles(byte[] fileData)
+ {
+ try
+ {
+ var dataS = Encoding.UTF8.GetString(fileData);
+ var tiles = dataS.Deserializar();
+
+ // Check attributes for ZX Spectrum mode
+ foreach (var tile in tiles)
+ {
+ if (tile != null && tile.GraphicMode == GraphicsModes.ZXSpectrum)
+ {
+ foreach (var pattern in tile.Patterns)
+ {
+ if (pattern.Attributes == null)
+ {
+ int cW = tile.Width / 8;
+ int cH = tile.Height / 8;
+ pattern.Attributes = new AttributeColor[cW * cH];
+ foreach (var attr in pattern.Attributes)
+ {
+ attr.Ink = 1;
+ attr.Paper = 0;
+ }
+ }
+ }
+ }
+ }
+
+ return tiles;
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
+
+ public bool ExportTiles(ExportConfig exportConfig, IEnumerable tiles)
+ {
+ // Export depending on the type
+ string exportedData = "";
+ bool createTextFile = false;
+
+ switch (exportConfig.ExportType)
+ {
+ case ExportTypes.PutChars:
+ exportedData = Export_Tiles_PutChars(exportConfig, tiles);
+ createTextFile = true;
+ break;
+ default:
+ return true;
+ }
+
+ if (!string.IsNullOrEmpty(exportedData) && createTextFile)
+ {
+ var sb = new StringBuilder();
+ sb.AppendLine("'------------------------------------------------------------------------------");
+ sb.AppendLine("'- Build data generated by ZXBasicStudio --------------------------------------");
+ sb.AppendLine("'- Do not modify this file, its contents are deleted at each build ------------");
+ sb.AppendLine("'------------------------------------------------------------------------------");
+ sb.AppendLine("");
+ sb.Append(exportedData);
+ ServiceLayer.Files_SaveFileString(exportConfig.ExportFilePath, sb.ToString());
+ }
+ return true;
+ }
+
+
+
+ #region PutChars
+
+ ///
+ /// Generate the export data for PutChars Tiles
+ ///
+ /// ExportConfig information
+ /// Tiles to convert
+ /// string with the conversion commands for the export dialog samble textbox
+ public static string Export_Tiles_PutChars(ExportConfig exportConfig, IEnumerable tiles)
+ {
+ string res = Export_Tile_PutChars_Check(exportConfig, tiles);
+ if (res.StartsWith("ERROR:"))
+ {
+ if (OutputLog != null)
+ {
+ OutputLog.WriteLine(res);
+ }
+ return res;
+ }
+ switch (exportConfig.ExportDataType)
+ {
+ case ExportDataTypes.DIM:
+ return Export_Tile_PutChars_DIM(exportConfig, tiles);
+ case ExportDataTypes.ASM:
+ return Export_Tile_PutChars_ASM(exportConfig, tiles);
+ case ExportDataTypes.BIN:
+ return Export_Tile_PutChars_BIN(exportConfig, tiles);
+ case ExportDataTypes.TAP:
+ return Export_Tile_PutChars_TAP(exportConfig, tiles);
+ default:
+ return "ERROR: Not implemented!";
+ }
+ }
+
+
+ private static string Export_Tile_PutChars_Check(ExportConfig exportConfig, IEnumerable tiles)
+ {
+ return "OK";
+ }
+
+
+ #region DIM
+
+ public static string Export_Tile_PutChars_DIM(ExportConfig exportConfig, IEnumerable tiles)
+ {
+ int min = 0;
+ switch (exportConfig.ArrayBase)
+ {
+ case 1:
+ min = 1;
+ break;
+ case 2:
+ {
+ var settings = ServiceLayer.GetProjectSettings();
+ if (settings != null)
+ {
+ if (settings.ArrayBase == 1)
+ {
+ min = 1;
+ }
+ }
+
+ }
+ break;
+ }
+
+ var sb = new StringBuilder();
+ sb.AppendLine("'- Tile definitions --------------------------------------");
+
+ var t = tiles.ElementAt(0);
+ if (t == null)
+ {
+ return "";
+ }
+ var max = tiles.Count() - 1; // The last tile is always null
+ var num = max + min;
+ sb.AppendLine(string.Format(
+ "DIM {0}({1},{2}) AS UByte => {{ _",
+ exportConfig.LabelName,
+ num - 1,
+ ((t.Width / 8) * t.Height) - 1 + min
+ ));
+
+ for (int n = 0; n < max; n++)
+ {
+ var tile = tiles.ElementAt(n);
+ if (tile == null)
+ {
+ continue;
+ }
+ // Content
+ sb.AppendLine("\t{ _");
+ var data = Export_Tile_PutChars_Pattern(tile, 0, exportConfig);
+ sb.Append(data);
+ if (n == (max - 1))
+ {
+ sb.AppendLine("\t} _");
+ }
+ else
+ {
+ sb.AppendLine("\t}, _");
+ }
+
+ }
+ // Footer
+ sb.AppendLine("}");
+ sb.AppendLine("");
+
+
+ // Attributes
+ if (exportConfig.IncludeAttr)
+ {
+ sb.AppendLine(string.Format(
+ "DIM {0}_Attr({1},{2}) AS UByte => {{ _",
+ exportConfig.LabelName,
+ num - 1,
+ ((t.Width / 8) * (t.Height / 8)) - 1 + min
+ ));
+ foreach (var tile in tiles)
+ {
+ if (tile == null)
+ {
+ continue;
+ }
+ // Content
+ var data = Export_Tile_PutChars_Attribute(tile, 0, exportConfig, 0);
+ sb.Append(data);
+ // Footer
+ }
+ sb.AppendLine("");
+
+ sb.Append(" _\r\n}");
+ sb.AppendLine("");
+ }
+ return sb.ToString();
+ }
+
+
+ private static string Export_Tile_PutChars_Pattern(ZXMapsTile tile, int n, ExportConfig exportConfig)
+ {
+ var sb = new StringBuilder();
+
+ var pattern = tile.Patterns[n];
+ var data = ServiceLayer.Files_CreateBinDataUpDown(pattern, tile.Width, tile.Height);
+
+ int col = 0;
+ int row = 0;
+ foreach (var d in data)
+ {
+ if (col == 0)
+ {
+ if (row == 0)
+ {
+ row = 1;
+ }
+ else
+ {
+ sb.AppendLine(", _");
+ }
+ sb.Append("\t\t");
+ }
+ if (col > 0)
+ {
+ sb.Append(",");
+ }
+ var x = string.Format("${0:X2}", d);
+ sb.Append(x);
+
+ col++;
+ if (col >= 8)
+ {
+ col = 0;
+ }
+ }
+ sb.AppendLine(" _");
+
+ return sb.ToString();
+ }
+
+
+ private static string Export_Tile_PutChars_Attribute(ZXMapsTile tile, int n, ExportConfig exportConfig, int firstItem)
+ {
+ var sb = new StringBuilder();
+
+ var pattern = tile.Patterns[n];
+
+ if (pattern.Attributes == null)
+ {
+ return "";
+ }
+
+ if (tile.Masked)
+ {
+ if (tile.Frames > 2)
+ {
+ if (n > firstItem)
+ {
+ sb.AppendLine(", _");
+ }
+ sb.AppendLine("\t{ _");
+ }
+ }
+ else
+ if (tile.Frames > 1)
+ {
+ if (n > firstItem)
+ {
+ sb.AppendLine(", _");
+ }
+ sb.AppendLine("\t{ _");
+ }
+
+ int col = 0;
+ int row = 0;
+ int max = (tile.Width / 8) * (tile.Height / 8);
+ int idx = 0;
+ foreach (var d in pattern.Attributes)
+ {
+ if (idx++ >= max)
+ {
+ break;
+ }
+ if (col == 0)
+ {
+ if (row == 0)
+ {
+ row = 1;
+ }
+ else
+ {
+ sb.AppendLine(", _");
+ }
+ sb.Append("\t");
+ if (tile.Frames > 1)
+ {
+ sb.Append("\t");
+ }
+ }
+ if (col > 0)
+ {
+ sb.Append(",");
+ }
+ var x = string.Format("${0:X2}", d.Attribute);
+ sb.Append(x);
+
+ col++;
+ if (col >= 8)
+ {
+ col = 0;
+ }
+ }
+ sb.AppendLine(" _");
+
+ if (tile.Frames == 1)
+ {
+ sb.Append("}");
+ }
+ else
+ {
+ sb.Append("\t}");
+ }
+
+ return sb.ToString();
+ }
+
+ #endregion
+
+
+ #region ASM
+
+
+ public static string Export_Tile_PutChars_ASM(ExportConfig exportConfig, IEnumerable tiles)
+ {
+ int min = 0;
+
+ var sb = new StringBuilder();
+ sb.AppendLine("'- Tile definitions --------------------------------------");
+
+ // All tiles
+ foreach (var tile in tiles)
+ {
+ if (tile == null || !tile.Export)
+ {
+ continue;
+ }
+
+ if (tile.Frames == 0)
+ {
+ continue;
+ }
+
+ // Header
+ sb.AppendLine(string.Format(
+ "{0}{1}:",
+ exportConfig.LabelName,
+ tile.Name.Replace(" ", "_")));
+ sb.AppendLine("ASM");
+
+ // Data frames
+ for (int n = 0; n < tile.Frames; n++)
+ {
+ if (tile.Masked && (n % 2) == 1)
+ {
+ continue;
+ }
+ // Content
+ var data = Export_Tile_PutChars_Pattern_ASM(tile, n, exportConfig);
+ sb.Append(data);
+ }
+ sb.AppendLine("END ASM");
+ sb.AppendLine("");
+
+ // Mask frames
+ if (tile.Masked)
+ {
+ // Header
+ sb.AppendLine(string.Format(
+ "{0}{1}_Mask:",
+ exportConfig.LabelName,
+ tile.Name.Replace(" ", "_")));
+ sb.AppendLine("ASM");
+ for (int n = 0; n < tile.Frames; n++)
+ {
+ if ((n % 2) == 0)
+ {
+ continue;
+ }
+ // Content
+ var data = Export_Tile_PutChars_Pattern_ASM(tile, n, exportConfig);
+ sb.Append(data);
+ }
+ sb.AppendLine("END ASM");
+ sb.AppendLine("");
+ }
+
+ // Attributes
+ if (exportConfig.IncludeAttr && tile.GraphicMode == GraphicsModes.ZXSpectrum)
+ {
+ // Header
+ sb.AppendLine(string.Format(
+ "{0}{1}_Attr:",
+ exportConfig.LabelName,
+ tile.Name.Replace(" ", "_")));
+ sb.AppendLine("ASM");
+ for (int n = 0; n < tile.Frames; n++)
+ {
+ if (tile.Masked && (n % 2) == 1)
+ {
+ continue;
+ }
+ // Content
+ var data = Export_Tile_PutChars_Attribute_ASM(tile, n, exportConfig);
+ sb.Append(data);
+ }
+ sb.AppendLine("END ASM");
+ sb.AppendLine("");
+ }
+ }
+
+ return sb.ToString();
+ }
+
+
+ private static string Export_Tile_PutChars_Pattern_ASM(ZXMapsTile tile, int n, ExportConfig exportConfig)
+ {
+ var sb = new StringBuilder();
+
+ var pattern = tile.Patterns[n];
+ var data = ServiceLayer.Files_CreateBinDataUpDown(pattern, tile.Width, tile.Height);
+
+ int col = 0;
+ int row = 0;
+ foreach (var d in data)
+ {
+ if (col == 0)
+ {
+ if (row == 0)
+ {
+ row = 1;
+ }
+ else
+ {
+ sb.AppendLine("");
+ }
+ sb.Append("\tDB ");
+ }
+ if (col > 0)
+ {
+ sb.Append(",");
+ }
+ var x = string.Format("${0:X2}", d);
+ sb.Append(x);
+
+ col++;
+ if (col >= 8)
+ {
+ col = 0;
+ }
+ }
+ sb.AppendLine("");
+
+ return sb.ToString();
+ }
+
+
+ private static string Export_Tile_PutChars_Attribute_ASM(ZXMapsTile tile, int n, ExportConfig exportConfig)
+ {
+ var sb = new StringBuilder();
+
+ var pattern = tile.Patterns[n];
+
+ if (pattern.Attributes == null)
+ {
+ return "";
+ }
+
+ int col = 0;
+ int row = 0;
+ int max = (tile.Width / 8) * (tile.Height / 8);
+ int idx = 0;
+ foreach (var d in pattern.Attributes)
+ {
+ if (idx++ >= max)
+ {
+ break;
+ }
+ if (col == 0)
+ {
+ if (row == 0)
+ {
+ row = 1;
+ }
+ else
+ {
+ sb.AppendLine("");
+ }
+ sb.Append("\tDB ");
+ }
+ if (col > 0)
+ {
+ sb.Append(",");
+ }
+ var x = string.Format("${0:X2}", d.Attribute);
+ sb.Append(x);
+
+ col++;
+ if (col >= 8)
+ {
+ col = 0;
+ }
+ }
+ sb.AppendLine("");
+
+ return sb.ToString();
+ }
+
+ #endregion
+
+
+ #region TAP and BIN
+
+
+ public static string Export_Tile_PutChars_BIN(ExportConfig exportConfig, IEnumerable tiles)
+ {
+ var binData = Export_Tile_PutChars_GetBinaryData(tiles);
+ ServiceLayer.Files_SaveFileData(exportConfig.ExportFilePath, binData);
+ return "";
+ }
+
+
+ public static string Export_Tile_PutChars_TAP(ExportConfig exportConfig, IEnumerable tiles)
+ {
+ var binData = Export_Tile_PutChars_GetBinaryData(tiles);
+ binData = ServiceLayer.Bin2Tap(exportConfig.ZXFileName, exportConfig.ZXAddress, binData);
+ ServiceLayer.Files_SaveFileData(exportConfig.ExportFilePath, binData);
+ return "";
+ }
+
+
+ private static byte[] Export_Tile_PutChars_GetBinaryData(IEnumerable tiles)
+ {
+ var binData = new List();
+
+ foreach (var tile in tiles)
+ {
+ if (tile != null)
+ {
+ foreach (var pattern in tile.Patterns)
+ {
+ var data = ServiceLayer.Files_CreateBinDataUpDown(pattern, tile.Width, tile.Height);
+ binData.AddRange(data);
+ }
+ }
+ }
+
+ return binData.ToArray();
+ }
+
+ #endregion
+
+ #endregion
+
+ #endregion
+
+
+ #region Maps
+
+ private ZXMapsMap CreateMap(byte[] fileData)
+ {
+ try
+ {
+ var dataS = Encoding.UTF8.GetString(fileData);
+ var map = dataS.Deserializar();
+ return map;
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
+
+ public bool ExportMap(ExportConfig exportConfig, ZXMapsMap map)
+ {
+ // Export depending on the type
+ string exportedData = "";
+ bool createTextFile = false;
+
+ switch (exportConfig.ExportType)
+ {
+ case ExportTypes.Array:
+ exportedData = Export_Map_Array(exportConfig, map);
+ createTextFile = true;
+ break;
+ default:
+ return true;
+ }
+
+ if (!string.IsNullOrEmpty(exportedData) && createTextFile)
+ {
+ var sb = new StringBuilder();
+ sb.AppendLine("'------------------------------------------------------------------------------");
+ sb.AppendLine("'- Build data generated by ZXBasicStudio --------------------------------------");
+ sb.AppendLine("'- Do not modify this file, its contents are deleted at each build ------------");
+ sb.AppendLine("'------------------------------------------------------------------------------");
+ sb.AppendLine("");
+ sb.Append(exportedData);
+ ServiceLayer.Files_SaveFileString(exportConfig.ExportFilePath, sb.ToString());
+ }
+ return true;
+ }
+
+
+ ///
+ /// Generate the export data for Map as Array
+ ///
+ /// ExportConfig information
+ /// Tiles to convert
+ /// string with the conversion commands for the export dialog samble textbox
+ public static string Export_Map_Array(ExportConfig exportConfig, ZXMapsMap map)
+ {
+ string res = Export_Map_Check(exportConfig, map);
+ if (res.StartsWith("ERROR:"))
+ {
+ if (OutputLog != null)
+ {
+ OutputLog.WriteLine(res);
+ }
+ return res;
+ }
+
+ switch (exportConfig.ExportDataType)
+ {
+ case ExportDataTypes.DIM:
+ return Export_Map_Array_DIM(exportConfig, map);
+ //case ExportDataTypes.ASM:
+ // return Export_Tile_PutChars_ASM(exportConfig, tiles);
+ //case ExportDataTypes.BIN:
+ // return Export_Tile_PutChars_BIN(exportConfig, tiles);
+ //case ExportDataTypes.TAP:
+ // return Export_Tile_PutChars_TAP(exportConfig, tiles);
+ default:
+ return "ERROR: Not implemented!";
+ }
+ }
+
+
+ private static string Export_Map_Check(ExportConfig exportConfig, ZXMapsMap map)
+ {
+ return "OK";
+ }
+
+
+ private static string Export_Map_Array_DIM(ExportConfig exportConfig, ZXMapsMap map)
+ {
+ int min = 0;
+ switch (exportConfig.ArrayBase)
+ {
+ case 1:
+ min = 1;
+ break;
+ case 2:
+ {
+ var settings = ServiceLayer.GetProjectSettings();
+ if (settings != null)
+ {
+ if (settings.ArrayBase == 1)
+ {
+ min = 1;
+ }
+ }
+
+ }
+ break;
+ }
+
+ var sb = new StringBuilder();
+ sb.AppendLine("'- Map definitions --------------------------------------");
+
+
+ sb.AppendLine($"DIM {exportConfig.LabelName}({map.Width - 1 + min},{map.Height - 1 + min}) AS UByte => {{ _");
+
+ for (int x = 0; x < map.Width; x++)
+ {
+ sb.Append("\t{ ");
+ for (int y = 0; y < map.Height; y++)
+ {
+ var d = $"{map.TileArrays[x, y].TileId.ToString("000")}";
+ sb.Append(d);
+ if (y == (map.Height - 1))
+ {
+ if (x == (map.Width - 1))
+ {
+ sb.AppendLine("} _");
+ }
+ else
+ {
+ sb.AppendLine("}, _");
+ }
+ }
+ else
+ {
+ sb.Append(",");
+ }
+ }
+ }
+ sb.AppendLine("}");
+ sb.AppendLine("");
+
+ return sb.ToString();
+ }
+
+
+
+ private static string Export_Map_Array_DIM_DEPRECATED(ExportConfig exportConfig, ZXMapsMap map)
+ {
+ int min = 0;
+ switch (exportConfig.ArrayBase)
+ {
+ case 1:
+ min = 1;
+ break;
+ case 2:
+ {
+ var settings = ServiceLayer.GetProjectSettings();
+ if (settings != null)
+ {
+ if (settings.ArrayBase == 1)
+ {
+ min = 1;
+ }
+ }
+
+ }
+ break;
+ }
+
+ var sb = new StringBuilder();
+ sb.AppendLine("'- Map definitions --------------------------------------");
+
+
+ sb.AppendLine($"DIM {exportConfig.LabelName}({map.Height - 1 + min},{map.Width - 1 + min}) AS UByte => {{ _");
+
+ for (int y = 0; y < map.Height; y++)
+ {
+ sb.Append("\t{ ");
+ for (int x = 0; x < map.Width; x++)
+ {
+ var d = $"{map.TileArrays[x, y].TileId.ToString("000")}";
+ sb.Append(d);
+ if (x == (map.Width - 1))
+ {
+ if (y == (map.Height - 1))
+ {
+ sb.AppendLine("} _");
+ }
+ else
+ {
+ sb.AppendLine("}, _");
+ }
+ }
+ else
+ {
+ sb.Append(",");
+ }
+ }
+ }
+ sb.AppendLine("}");
+ sb.AppendLine("");
+
+ return sb.ToString();
+ }
+
+ #endregion
+
}
}
diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/log/ServiceLayer.cs b/ZXBStudio/DocumentEditors/ZXGraphics/log/ServiceLayer.cs
index da4a606..554eed6 100644
--- a/ZXBStudio/DocumentEditors/ZXGraphics/log/ServiceLayer.cs
+++ b/ZXBStudio/DocumentEditors/ZXGraphics/log/ServiceLayer.cs
@@ -1,22 +1,24 @@
-using ZXBasicStudio.DocumentEditors.ZXGraphics.dat;
-using ZXBasicStudio.DocumentEditors.ZXGraphics.neg;
+using Avalonia.Controls.Shapes;
+using Avalonia.Metadata;
+using Avalonia.Platform.Storage;
+using AvaloniaEdit;
+using Newtonsoft.Json;
+using SixLabors.ImageSharp.PixelFormats;
using System;
using System.Collections.Generic;
+using System.Diagnostics;
+using System.Drawing.Imaging;
using System.IO;
using System.Linq;
+using System.Runtime;
using ZXBasicStudio.Classes;
using ZXBasicStudio.Common;
-using System.Runtime;
-using Newtonsoft.Json;
using ZXBasicStudio.Common.TAPTools;
+using ZXBasicStudio.DocumentEditors.ZXGraphics.dat;
+using ZXBasicStudio.DocumentEditors.ZXGraphics.neg;
using ZXBasicStudio.DocumentModel.Classes;
-using ZXBasicStudio.IntegratedDocumentTypes.ZXGraphics;
using ZXBasicStudio.IntegratedDocumentTypes.CodeDocuments.Basic;
-using System.Drawing.Imaging;
-using Avalonia.Metadata;
-using Avalonia.Controls.Shapes;
-using AvaloniaEdit;
-using System.Diagnostics;
+using ZXBasicStudio.IntegratedDocumentTypes.ZXGraphics;
namespace ZXBasicStudio.DocumentEditors.ZXGraphics.log
{
@@ -506,6 +508,42 @@ public static ExportConfig Export_Sprite_GetDefaultConfig(string fileName)
}
+ ///
+ /// Creates the default export config for a tile document
+ ///
+ /// Document file name
+ /// The new export configuration
+ public static ExportConfig Export_Tile_GetDefaultConfig(string fileName)
+ {
+ var docType = ZXDocumentProvider.GetDocumentTypeInstance(typeof(ZXBasicDocument));
+ var exportConfig = new ExportConfig();
+ exportConfig.ArrayBase = 0;
+ exportConfig.AutoExport = true;
+ exportConfig.ExportFilePath = fileName + ".bas";
+ exportConfig.ExportType = ExportTypes.PutChars;
+ exportConfig.LabelName = System.IO.Path.GetFileNameWithoutExtension(fileName).Replace(" ", "_");
+ return exportConfig;
+ }
+
+
+ ///
+ /// Creates the default export config for a map document
+ ///
+ /// Document file name
+ /// The new export configuration
+ public static ExportConfig Export_Map_GetDefaultConfig(string fileName)
+ {
+ var docType = ZXDocumentProvider.GetDocumentTypeInstance(typeof(ZXBasicDocument));
+ var exportConfig = new ExportConfig();
+ exportConfig.ArrayBase = 0;
+ exportConfig.AutoExport = true;
+ exportConfig.ExportFilePath = fileName + ".bas";
+ exportConfig.ExportType = ExportTypes.Array;
+ exportConfig.LabelName = System.IO.Path.GetFileNameWithoutExtension(fileName).Replace(" ", "_");
+ return exportConfig;
+ }
+
+
public static bool Export_SetConfigFile(string fileName, ExportConfig exportConfig)
{
try
@@ -615,7 +653,7 @@ public static PaletteColor[] GetPalette(GraphicsModes mode)
/// Old Width of the sprite, the new must set in sprite parameter
/// Old Height of the spritye, the new must set in sprite parameter
/// True if OK or False if error
- public static bool SpriteData_Resize(ref Sprite sprite, int oldWidth, int oldHeight)
+ public static bool SpriteData_Resize(ref ZXMapsTile sprite, int oldWidth, int oldHeight)
{
for (int p = 0; p < sprite.Patterns.Count; p++)
{
@@ -653,7 +691,7 @@ public static bool SpriteData_Resize(ref Sprite sprite, int oldWidth, int oldHei
/// Old sprite with new graphic mode property set to target. Patterns will be updated.
/// Old graphic mode, the new must set in sprite parameter
/// True if OK or False if error
- public static bool SpriteData_ChangeMode(ref Sprite sprite, GraphicsModes oldMode)
+ public static bool SpriteData_ChangeMode(ref ZXMapsTile sprite, GraphicsModes oldMode)
{
// TODO: Do it!!!
return true;
@@ -666,7 +704,7 @@ public static bool SpriteData_ChangeMode(ref Sprite sprite, GraphicsModes oldMod
/// Old sprite with new mask status property set to target. Patterns will be updated.
/// Old mask value, the new must set in sprite parameter
/// True if OK or False if error
- public static bool SpriteData_ChangeMasked(ref Sprite sprite, bool oldMasked)
+ public static bool SpriteData_ChangeMasked(ref ZXMapsTile sprite, bool oldMasked)
{
// TODO: Do it!!!
return true;
@@ -679,7 +717,7 @@ public static bool SpriteData_ChangeMasked(ref Sprite sprite, bool oldMasked)
/// Old sprite with new Frames property set to target. Patterns will be updated.
/// Old Frames value, the new must set in sprite parameter
/// True if OK or False if error
- public static bool SpriteData_ChangeFrames(ref Sprite sprite, byte oldFrames)
+ public static bool SpriteData_ChangeFrames(ref ZXMapsTile sprite, byte oldFrames)
{
// TODO: Do it!!!
return true;
@@ -688,7 +726,6 @@ public static bool SpriteData_ChangeFrames(ref Sprite sprite, byte oldFrames)
#endregion
-
#region Tools
public static int GetColor(byte r, byte g, byte b, PaletteColor[] palette, byte cutOff = 5)
@@ -765,5 +802,100 @@ private static double GetColorDistance(PaletteColor c1, PaletteColor c2)
}
#endregion
+
+
+ #region Screen
+
+ ///
+ /// Obtiene el offset dentro de la memoria de pantalla del ZX Spectrum
+ /// para el byte que contiene el pÃxel (x,y).
+ ///
+ /// Coordenada X (0-255)
+ /// Coordenada Y (0-191)
+ /// Offset (0-6143)
+ public static int GetSpectrumScreenOffset(int x, int y)
+ {
+ if (x < 0 || x > 255 || y<0 || y>191)
+ {
+ return -1;
+ }
+
+ int offset =
+ ((y & 0b11000000) << 5) | // Bits 6-7 -> 11-12
+ ((y & 0b00000111) << 8) | // Bits 0-2 -> 8-10
+ ((y & 0b00111000) << 2) | // Bits 3-5 -> 5-7
+ (x >> 3); // Columna (0-31)
+ return offset;
+ }
+
+ #endregion
+
+
+ #region Images
+
+ public static SixLabors.ImageSharp.Image LoadImage(IStorageFile file)
+ {
+ try
+ {
+ SixLabors.ImageSharp.Image imageData = null;
+
+ using (var stream = file.OpenReadAsync().Result)
+ {
+ if (file.Name.ToLower().EndsWith(".scr"))
+ {
+ // Load SCR file
+ var scrData = new byte[6912];
+ stream.ReadAsync(scrData, 0, 6912).GetAwaiter();
+ imageData = new SixLabors.ImageSharp.Image(256, 192);
+ var palete = ZXGraphics.log.ServiceLayer.GetPalette(GraphicsModes.ZXSpectrum);
+ for (int y = 0; y < 192; y++)
+ {
+ for (int cx = 0; cx < 32; cx++)
+ {
+ int pixelIndex = ZXGraphics.log.ServiceLayer.GetSpectrumScreenOffset(cx * 8, y);
+ int attrIndex = 6144 + (((y / 8) * 32) + cx);
+ var attr = new AttributeColor()
+ {
+ Attribute = scrData[attrIndex]
+ };
+ var paper = palete[attr.Paper];
+ var colorOFF = new Rgba32(paper.Red, paper.Green, paper.Blue);
+ var ink = palete[attr.Ink];
+ var colorON = new Rgba32(ink.Red, ink.Green, ink.Blue);
+
+ byte byteData = scrData[pixelIndex];
+ string bits = $"{byteData:B8}";
+ for (int bx = 0; bx < 8; bx++)
+ {
+ Rgba32 color = new Rgba32(0);
+ if (bits.Substring(bx, 1) == "0")
+ {
+ color = colorOFF;
+ }
+ else
+ {
+ color = colorON;
+ }
+ int x = (cx * 8) + bx;
+ imageData[x, y] = color;
+ }
+ }
+ }
+ }
+ else
+ {
+ // Load other image formats
+ imageData = SixLabors.ImageSharp.Image.Load(stream);
+ }
+ }
+ return imageData;
+ }
+ catch (Exception ex)
+ {
+ return null;
+ }
+ }
+
+ #endregion
}
}
\ No newline at end of file
diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/neg/ExportTypes.cs b/ZXBStudio/DocumentEditors/ZXGraphics/neg/ExportTypes.cs
index b08d00b..3656f39 100644
--- a/ZXBStudio/DocumentEditors/ZXGraphics/neg/ExportTypes.cs
+++ b/ZXBStudio/DocumentEditors/ZXGraphics/neg/ExportTypes.cs
@@ -20,10 +20,13 @@ public enum ExportTypes
Dim,
Data,
- // Sprites
+ // Sprites and tiles
PutChars,
MaskedSprites,
GUSprite,
- FourSprites
+ FourSprites,
+
+ // Maps
+ Array,
}
}
diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/neg/Sprite.cs b/ZXBStudio/DocumentEditors/ZXGraphics/neg/Sprite.cs
index a7f1319..a85991d 100644
--- a/ZXBStudio/DocumentEditors/ZXGraphics/neg/Sprite.cs
+++ b/ZXBStudio/DocumentEditors/ZXGraphics/neg/Sprite.cs
@@ -3,28 +3,29 @@
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using ZXBasicStudio.DocumentEditors.ZXGraphics.neg;
-namespace ZXBasicStudio.DocumentEditors.ZXGraphics.neg
+namespace ZXBasicStudio.DocumentEditors.ZXMaps.Neg
{
///
- /// Represents an sprite with his properties
+ /// Represents an tile with his properties
///
- public class Sprite
+ public class Tile
{
///
- /// Id of the sprite
+ /// Id of the tile
///
public int Id { get; set; }
///
- /// Name of the sprite
+ /// Name of the tile
///
public string Name { get; set; }
///
- /// Graphics mode of the sprite
+ /// Graphics mode of the tile
///
public GraphicsModes GraphicMode { get; set; }
///
- /// Width of the sprite in pixels
+ /// Width of the tile in pixels
///
public int Width { get; set; }
///
@@ -32,11 +33,11 @@ public class Sprite
///
public int Height { get; set; }
///
- /// True if the sprite is masked (not suported in all modes)
+ /// True if the tile is masked (not suported in all modes)
///
public bool Masked { get; set; }
///
- /// Number of frames for the sprite
+ /// Number of frames for the tile
///
public byte Frames { get; set; }
///
@@ -44,7 +45,7 @@ public class Sprite
///
public byte CurrentFrame { get; set; }
///
- /// Patterns for the sprite (one pattern for frame)
+ /// Patterns for the tile (one pattern for frame)
///
public List Patterns { get; set; }
///
@@ -52,11 +53,11 @@ public class Sprite
///
public byte DefaultColor { get; set; }
///
- /// Palete color for the sprite
+ /// Palete color for the tile
///
public PaletteColor[] Palette { get; set; }
///
- /// True when the sprite was auto-exported
+ /// True when the tile was auto-exported
///
public bool Export { get; set; }
}
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/LayerItemControl.axaml b/ZXBStudio/DocumentEditors/ZXMaps/LayerItemControl.axaml
new file mode 100644
index 0000000..665ac75
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/LayerItemControl.axaml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/LayerItemControl.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/LayerItemControl.axaml.cs
new file mode 100644
index 0000000..09008dd
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/LayerItemControl.axaml.cs
@@ -0,0 +1,61 @@
+using Avalonia;
+using Avalonia.Controls;
+//using Avalonia.Controls.Shapes;
+using Avalonia.Media;
+using Avalonia.Threading;
+using AvaloniaEdit.Folding;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using ZXBasicStudio.Common;
+using ZXBasicStudio.DocumentEditors.NextDows.neg;
+using ZXBasicStudio.DocumentEditors.ZXGraphics;
+using ZXBasicStudio.DocumentEditors.ZXGraphics.neg;
+using ZXBasicStudio.DocumentEditors.ZXMaps.Neg;
+using ZXBasicStudio.DocumentEditors.ZXTextEditor.Classes.Folding;
+using ZXBasicStudio.DocumentModel.Classes;
+
+namespace ZXBasicStudio.DocumentEditors.ZXMaps
+{
+ public partial class LayerItemControl : UserControl
+ {
+ public ZXMapsLayer Layer { get; set; }
+
+ public bool Selected
+ {
+ get
+ {
+ return _Selected;
+ }
+ set
+ {
+ _Selected = value;
+ Refresh();
+ }
+ }
+ private bool _Selected = false;
+
+
+ public LayerItemControl()
+ {
+ InitializeComponent();
+ }
+
+
+ public bool Initialize(ZXMapsLayer layer)
+ {
+ btnShowLayer.IsVisible = layer.Visible;
+ btnHideLayer.IsVisible = !layer.Visible;
+ txtLayerName.Text = layer.Name;
+ _Selected = layer.Selected;
+ return true;
+ }
+
+
+ public void Refresh()
+ {
+ svgSelected.IsVisible = _Selected;
+ }
+ }
+}
\ No newline at end of file
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/LayersControl.axaml b/ZXBStudio/DocumentEditors/ZXMaps/LayersControl.axaml
new file mode 100644
index 0000000..6f86c4a
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/LayersControl.axaml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/LayersControl.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/LayersControl.axaml.cs
new file mode 100644
index 0000000..b132aab
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/LayersControl.axaml.cs
@@ -0,0 +1,70 @@
+using Avalonia;
+using Avalonia.Controls;
+//using Avalonia.Controls.Shapes;
+using Avalonia.Media;
+using Avalonia.Threading;
+using AvaloniaEdit.Folding;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using ZXBasicStudio.Common;
+using ZXBasicStudio.DocumentEditors.NextDows.neg;
+using ZXBasicStudio.DocumentEditors.ZXGraphics;
+using ZXBasicStudio.DocumentEditors.ZXGraphics.neg;
+using ZXBasicStudio.DocumentEditors.ZXMaps.Neg;
+using ZXBasicStudio.DocumentEditors.ZXTextEditor.Classes.Folding;
+using ZXBasicStudio.DocumentModel.Classes;
+
+namespace ZXBasicStudio.DocumentEditors.ZXMaps
+{
+ public partial class LayersControl : UserControl
+ {
+ private List Layers = null;
+ private List LayerItems = new List();
+
+
+ public LayersControl()
+ {
+ InitializeComponent();
+ }
+
+
+ public void AddLayer(ZXMapsLayer layer)
+ {
+
+ }
+
+
+ public bool Initialize(List layers)
+ {
+ this.Layers = layers;
+ Refresh();
+ return true;
+ }
+
+
+ public void Refresh()
+ {
+ pnlLayers.Children.Clear();
+ LayerItems.Clear();
+ bool first = true;
+ foreach (ZXMapsLayer layer in Layers.OrderBy(d=>d.Order))
+ {
+ var li=new LayerItemControl();
+ LayerItems.Add(li);
+ pnlLayers.Children.Add(li);
+ li.Initialize(layer);
+ if (first)
+ {
+ li.Selected = true;
+ first = false;
+ }
+ else
+ {
+ li.Selected = false;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/Log/ServiceLayer_Maps.cs b/ZXBStudio/DocumentEditors/ZXMaps/Log/ServiceLayer_Maps.cs
new file mode 100644
index 0000000..321f633
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/Log/ServiceLayer_Maps.cs
@@ -0,0 +1,70 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using ZXBasicStudio.Classes;
+using ZXBasicStudio.Common;
+using ZXBasicStudio.DocumentEditors.ZXMaps.Neg;
+
+namespace ZXBasicStudio.DocumentEditors.ZXMaps.Log
+{
+ public static class ServiceLayer_Maps
+ {
+
+ public static ZXMapsMap Maps_LoadMapByName(string name)
+ {
+ try
+ {
+ var fileName = Path.Combine(ZXProjectManager.Current.ProjectPath, name + ".zxmap");
+ if (File.Exists(fileName))
+ {
+ var json = File.ReadAllText(fileName);
+ return json.Deserializar();
+ }
+ else
+ {
+ return null;
+ }
+ }
+ catch (Exception ex)
+ {
+ //MessageBox.Show($"Error loading file {FileName}: {ex.Message}");
+ return null;
+ }
+ }
+
+
+ public static bool Maps_SaveMap(string fileName, ZXMapsMap map)
+ {
+ try
+ {
+ //var fileName = Path.Combine(ZXProjectManager.Current.ProjectPath, map.Name + ".zxmap");
+ var json = map.Serializar();
+ File.WriteAllText(fileName, json);
+ return true;
+ }
+ catch (Exception ex)
+ {
+ //MessageBox.Show($"Error saving file {FileName}: {ex.Message}");
+ return false;
+ }
+ }
+
+
+ public static ZXMapsTiles Tiles_LoadMapsTiles(string fileName)
+ {
+ try
+ {
+ var json = File.ReadAllText(fileName);
+ return json.Deserializar();
+ }
+ catch (Exception ex)
+ {
+ //MessageBox.Show($"Error saving file {FileName}: {ex.Message}");
+ return null;
+ }
+ }
+ }
+}
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/MapExportDialog.axaml b/ZXBStudio/DocumentEditors/ZXMaps/MapExportDialog.axaml
new file mode 100644
index 0000000..1b62f6f
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/MapExportDialog.axaml
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+ Options
+
+
+
+
+
+
+ Enable build:
+
+
+ Data type:
+
+ DIM (data as array)
+ ASM (DEFB inside ASM Block)
+ .BIN file (INCBIN "file.bin")
+ .TAP file (LOAD "" CODE)
+
+
+ File path:
+
+
+
+ Var prefix:
+
+
+ Array base:
+
+ 0 (default)
+ 1
+ From project settings
+
+
+ Transpose data (swap X and Y)
+
+
+
+
+ You have not selected any sprite to export. Check the "Include in the export" box of the sprites you want to include in the export.
+
+
+ Example of usage
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/MapExportDialog.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/MapExportDialog.axaml.cs
new file mode 100644
index 0000000..7d092a0
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/MapExportDialog.axaml.cs
@@ -0,0 +1,385 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Platform.Storage;
+using MsBox.Avalonia;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Linq;
+using System.Text;
+using ZXBasicStudio.Classes;
+using ZXBasicStudio.Common;
+using ZXBasicStudio.DocumentEditors.ZXGraphics.log;
+using ZXBasicStudio.DocumentEditors.ZXGraphics.neg;
+using ZXBasicStudio.DocumentEditors.ZXMaps.Neg;
+using ZXBasicStudio.DocumentModel.Classes;
+using ZXBasicStudio.DocumentModel.Interfaces;
+using ZXBasicStudio.Extensions;
+using ZXBasicStudio.IntegratedDocumentTypes.CodeDocuments.Basic;
+using static System.Net.Mime.MediaTypeNames;
+
+namespace ZXBasicStudio.DocumentEditors.ZXMaps
+{
+ public partial class MapExportDialog : Window
+ {
+ private string fileName = "";
+ private ZXMapsMap map = null;
+
+
+ private FileTypeConfig fileType = null;
+ private Pattern[] patterns = null;
+ private ExportConfig exportConfig = null;
+
+
+ public MapExportDialog()
+ {
+ InitializeComponent();
+
+ btnCancel.Tapped += BtnCancel_Tapped;
+ btnCopy.Tapped += BtnCopy_Tapped;
+ btnExport.Tapped += BtnExport_Tapped;
+ btnOutputFile.Tapped += BtnOutputFile_Tapped;
+ cmbDataType.SelectionChanged += CmbDataType_SelectionChanged;
+ cmbArrayBase.SelectionChanged += CmbArrayBase_SelectionChanged;
+ btnSave.Tapped += BtnSave_Tapped;
+ }
+
+
+ public bool Initialize(string fileName, ZXMapsMap map)
+ {
+ this.fileName = fileName;
+ this.map = map;
+
+ if (map == null)
+ {
+ return false;
+ }
+
+ var p = Path.GetDirectoryName(fileName);
+ p = Path.Combine(p, map.Name + ".zxmap");
+ exportConfig = ServiceLayer.Export_GetConfigFile(map.Name + ".zbs");
+ if (exportConfig == null)
+ {
+ exportConfig = ServiceLayer.Export_Map_GetDefaultConfig(p);
+ }
+
+ this.cmbSelectExportType.Initialize(ExportType_Changed);
+ cmbSelectExportType.InitializeMap();
+
+ txtOutputFile.Text = exportConfig.ExportFilePath;
+ txtLabelName.Text = exportConfig.LabelName;
+ chkAuto.IsChecked = exportConfig.AutoExport;
+ cmbDataType.SelectedIndex = (int)exportConfig.ExportDataType;
+ cmbArrayBase.SelectedIndex = exportConfig.ArrayBase.ToInteger();
+
+ cmbSelectExportType.ExportType = exportConfig.ExportType;
+
+ return true;
+ }
+
+
+ #region ExportOptions
+
+ private void ExportType_Changed(ExportTypes exportType)
+ {
+ exportConfig.ExportType = exportType;
+ Refresh();
+ }
+
+
+ private void Refresh()
+ {
+ grdOptions.IsVisible = true;
+
+ chkAuto.IsVisible = true;
+
+ lblOutputFile.IsVisible = true;
+ txtOutputFile.IsVisible = true;
+ btnOutputFile.IsVisible = true;
+
+ lblLabelName.IsVisible = true;
+ txtLabelName.IsVisible = true;
+
+ lblArrayBase.IsVisible = true;
+ cmbDataType.IsVisible = true;
+ cmbArrayBase.IsVisible = true;
+
+ bool canExport = false;
+ if (map == null || map.TileArrays==null || map.TileArrays.Length == 0)
+ {
+ txtError.IsVisible = true;
+ }
+ else
+ {
+ canExport = true;
+ txtError.IsVisible = false;
+ }
+
+ switch (exportConfig.ExportType)
+ {
+ case ExportTypes.Array:
+ CreateExportPath(".bas");
+ if (canExport)
+ {
+ CreateExample_Array();
+ }
+ break;
+ default:
+ grdOptions.IsVisible = false;
+ break;
+ }
+ }
+
+
+ private void CreateExportPath(string extension)
+ {
+ if (string.IsNullOrEmpty(exportConfig.ExportFilePath))
+ {
+ txtOutputFile.Text = fileType.FileName + extension;
+ var spName = Path.GetFileNameWithoutExtension(fileType.FileName.ToStringNoNull());
+ txtLabelName.Text = spName;
+ if (spName.Length > 10)
+ {
+ spName = spName.Substring(0, 10);
+ }
+ }
+ }
+
+
+ #endregion
+
+
+ #region Examples
+
+ private void GetConfigFromUI()
+ {
+ if (exportConfig == null)
+ {
+ exportConfig = new ExportConfig();
+ }
+ exportConfig.ArrayBase = cmbArrayBase.SelectedIndex.ToInteger();
+ exportConfig.AutoExport = chkAuto.IsChecked == true;
+ exportConfig.ExportDataType = (ExportDataTypes)cmbDataType.SelectedIndex.ToInteger();
+ exportConfig.ExportFilePath = txtOutputFile.Text.ToStringNoNull();
+ exportConfig.ExportType = cmbSelectExportType.ExportType;
+ exportConfig.LabelName = txtLabelName.Text.ToStringNoNull();
+ exportConfig.ZXAddress = 0;
+ exportConfig.ZXFileName = "";
+ }
+
+ private void CreateExample_Array()
+ {
+ if (map == null || map.TileArrays==null || map.TileArrays.Length == 0)
+ {
+ txtCode.Text = "";
+ }
+
+ var res = ExportManager.Export_Map_Array(exportConfig, map);
+ if (res.StartsWith("ERROR:"))
+ {
+ txtError.Text = res;
+ txtError.IsVisible = true;
+ return;
+ }
+ else
+ {
+ txtError.IsVisible = false;
+ }
+
+ var sb = new StringBuilder();
+ switch (exportConfig.ExportDataType)
+ {
+ case ExportDataTypes.DIM:
+ {
+ sb.AppendLine("'- Includes -----------------------------------------------");
+ sb.AppendLine("#INCLUDE ");
+ sb.AppendLine("");
+ sb.AppendLine(string.Format("' Can use: #INCLUDE \"{0}\"",
+ Path.GetFileName(exportConfig.ExportFilePath)));
+ sb.AppendLine(res);
+ sb.AppendLine("");
+ sb.AppendLine("'- Draw tile --------------------------------------------");
+
+ //sb.AppendLine(string.Format(
+ // "putChars(10,5,{0},{1},@{2}({3},0))",
+ // tile.Width / 8,
+ // tile.Height / 8,
+ // exportConfig.LabelName,
+ // tile.Frames == 1 ? "0" : "0,0"));
+ sb.AppendLine("");
+ }
+ break;
+
+ case ExportDataTypes.ASM:
+ {
+ sb.AppendLine("'- Includes -----------------------------------------------");
+ sb.AppendLine("#INCLUDE ");
+ sb.AppendLine("");
+ sb.AppendLine("'- Draw tile --------------------------------------------");
+ //sb.AppendLine(string.Format(
+ // "putChars(10,5,{0},{1},@{2}{3})",
+ // tile.Width / 8,
+ // tile.Height / 8,
+ // exportConfig.LabelName,
+ // tile.Name.Replace(" ", "_")));
+ sb.AppendLine("");
+ sb.AppendLine("' This section must not be executed");
+ sb.AppendLine(string.Format("' Can use: #INCLUDE \"{0}\"",
+ Path.GetFileName(exportConfig.ExportFilePath)));
+ sb.AppendLine(res);
+ }
+ break;
+
+ case ExportDataTypes.BIN:
+ {
+ sb.AppendLine("'- Includes -----------------------------------------------");
+ sb.AppendLine("#INCLUDE ");
+ sb.AppendLine("");
+ sb.AppendLine("'- Draw tile --------------------------------------------");
+ //sb.AppendLine(string.Format(
+ // "putChars(10,5,{0},{1},@{2})",
+ // tile.Width / 8,
+ // tile.Height / 8,
+ // exportConfig.LabelName));
+ sb.AppendLine("");
+ sb.AppendLine("' This section must not be executed");
+ sb.AppendLine(string.Format(
+ "{0}:",
+ exportConfig.LabelName));
+ sb.AppendLine("ASM");
+ sb.AppendLine(string.Format("\tINCBIN \"{0}\"",
+ Path.GetFileName(exportConfig.ExportFilePath)));
+ sb.AppendLine("END ASM");
+ }
+ break;
+
+ case ExportDataTypes.TAP:
+ {
+ sb.AppendLine("'- Includes -----------------------------------------------");
+ sb.AppendLine("#INCLUDE ");
+ sb.AppendLine("");
+ sb.AppendLine("' Load .tap data ------------------------------------------");
+ sb.AppendLine("LOAD \"\" CODE");
+ sb.AppendLine("");
+ sb.AppendLine("'- Draw tile --------------------------------------------");
+ //sb.AppendLine(string.Format(
+ // "putChars(10,5,{0},{1},@{2})",
+ // tile.Width / 8,
+ // tile.Height / 8,
+ // exportConfig.LabelName));
+ sb.AppendLine("");
+ }
+ break;
+ }
+
+ txtCode.Text = sb.ToString();
+ }
+
+ #endregion
+
+
+ #region Export
+
+ private void Export()
+ {
+ GetConfigFromUI();
+ var em = new ExportManager();
+ em.Initialize(FileTypes.Map);
+ em.ExportMap(exportConfig, map);
+ }
+
+ #endregion
+
+
+ #region Buttons
+
+ private void BtnSave_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
+ {
+ GetConfigFromUI();
+ var p = Path.GetDirectoryName(fileName);
+ p = Path.Combine(p, map.Name + ".zxmap.zbs");
+ ServiceLayer.Export_SetConfigFile(p, exportConfig);
+ Export();
+ //this.Close();
+ }
+
+ private async void BtnOutputFile_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
+ {
+ var fileTypes = new FilePickerFileType[2];
+ fileTypes[1] = new FilePickerFileType("All files") { Patterns = new[] { "*", "*.*" } };
+ fileTypes[0] = new FilePickerFileType("Maps files") { Patterns = new[] { "*.zxmapdata" } };
+
+ var select = await StorageProvider.SaveFilePickerAsync(new Avalonia.Platform.Storage.FilePickerSaveOptions
+ {
+ ShowOverwritePrompt = true,
+ FileTypeChoices = fileTypes,
+ Title = "Select Export path...",
+ });
+
+ if (select != null)
+ {
+ txtOutputFile.Text = Path.GetFullPath(select.Path.LocalPath);
+ }
+ }
+
+
+ private void BtnExport_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
+ {
+ GetConfigFromUI();
+ Export();
+ this.Close();
+ }
+
+
+ private void BtnCopy_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
+ {
+ TopLevel.GetTopLevel(this)?.Clipboard?.SetTextAsync(txtCode.Text);
+ }
+
+
+ private void BtnCancel_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
+ {
+ this.Close();
+ }
+
+
+
+ private void CmbArrayBase_SelectionChanged(object? sender, SelectionChangedEventArgs e)
+ {
+ var idx = cmbArrayBase.SelectedIndex;
+ exportConfig.ArrayBase = idx;
+ Refresh();
+ }
+
+
+ private void CmbDataType_SelectionChanged(object? sender, SelectionChangedEventArgs e)
+ {
+ var idx = cmbDataType.SelectedIndex;
+ exportConfig.ExportDataType = (ExportDataTypes)idx;
+
+ var ext = "";
+ switch (exportConfig.ExportDataType)
+ {
+ case ExportDataTypes.ASM:
+ case ExportDataTypes.DIM:
+ ext = ".bas";
+ break;
+ case ExportDataTypes.BIN:
+ ext = ".bin";
+ break;
+ case ExportDataTypes.TAP:
+ ext = ".tap";
+ break;
+ default:
+ return;
+ }
+ txtOutputFile.Text = Path.Combine(Path.GetDirectoryName(txtOutputFile.Text), Path.GetFileNameWithoutExtension(txtOutputFile.Text) + ext);
+ exportConfig.ExportFilePath = txtOutputFile.Text;
+ Refresh();
+ }
+
+ #endregion
+ }
+}
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/MapFileSelectorControl.axaml b/ZXBStudio/DocumentEditors/ZXMaps/MapFileSelectorControl.axaml
new file mode 100644
index 0000000..0e9719c
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/MapFileSelectorControl.axaml
@@ -0,0 +1,15 @@
+
+
+ File Name:
+
+
+
+
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/MapFileSelectorControl.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/MapFileSelectorControl.axaml.cs
new file mode 100644
index 0000000..64f1d2a
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/MapFileSelectorControl.axaml.cs
@@ -0,0 +1,147 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.Primitives.PopupPositioning;
+using Avalonia.Interactivity;
+
+//using Avalonia.Controls.Shapes;
+using Avalonia.Media;
+using Avalonia.Threading;
+using AvaloniaEdit.Folding;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Security.Cryptography;
+using ZXBasicStudio.Classes;
+using ZXBasicStudio.Common;
+using ZXBasicStudio.DocumentEditors.NextDows.neg;
+using ZXBasicStudio.DocumentEditors.ZXGraphics;
+using ZXBasicStudio.DocumentEditors.ZXGraphics.neg;
+using ZXBasicStudio.DocumentEditors.ZXMaps.Log;
+using ZXBasicStudio.DocumentEditors.ZXMaps.Neg;
+using ZXBasicStudio.DocumentEditors.ZXTextEditor.Classes.Folding;
+using ZXBasicStudio.DocumentModel.Classes;
+
+namespace ZXBasicStudio.DocumentEditors.ZXMaps
+{
+ public partial class MapFileSelectorControl : UserControl
+ {
+ public ZXMapsMap Map
+ {
+ get
+ {
+ return _Map;
+ }
+ set
+ {
+ _Map = value;
+ Refresh();
+ }
+ }
+
+ private ZXMapsMap _Map = null;
+
+ private int CurrentTap = 0;
+ private Action Comando = null;
+ private bool comboUpdating = false;
+
+
+ public MapFileSelectorControl()
+ {
+ InitializeComponent();
+ }
+
+
+ public bool Initialize(ZXMapsMap map, Action callBackComando)
+ {
+ this._Map = map;
+ this.Comando = callBackComando;
+
+ FillCombo();
+ return true;
+ }
+
+
+ public bool Initialize(string mapName, Action callBackComando)
+ {
+ this.Comando = callBackComando;
+ if (!string.IsNullOrEmpty(mapName))
+ {
+ Map = ServiceLayer_Maps.Maps_LoadMapByName(mapName);
+ }
+ FillCombo();
+ return true;
+ }
+
+
+ public void Refresh()
+ {
+
+ }
+
+
+ private void FillCombo()
+ {
+ try
+ {
+ if (comboUpdating)
+ {
+ return;
+ }
+ comboUpdating = true;
+
+ var files = Directory.GetFiles(ZXProjectManager.Current.ProjectPath, "*.zxmap").
+ Select(Path.GetFileNameWithoutExtension).
+ OrderBy(f => f).
+ ToList();
+ var sel = cmbFileName.SelectedValue;
+ cmbFileName.ItemsSource = null;
+ cmbFileName.Items.Clear();
+
+ var numFiles = files.Count();
+ if (numFiles == 0)
+ {
+ files.Add("");
+ cmbFileName.ItemsSource = files;
+ cmbFileName.SelectedIndex = 0;
+ }
+ else
+ {
+ cmbFileName.ItemsSource = files;
+ if (sel == null)
+ {
+ cmbFileName.SelectedValue = Map?.Name;
+ }
+ else
+ {
+ cmbFileName.SelectedValue = sel;
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ // TODO: Tratar excepciones
+ }
+ finally
+ {
+ comboUpdating = false;
+ }
+ }
+
+
+ private void cmbFileName_SelectionChanged(object? sender, SelectionChangedEventArgs e)
+ {
+ try
+ {
+ var name = cmbFileName.Text;
+ Map = ServiceLayer_Maps.Maps_LoadMapByName(name);
+ Comando?.Invoke(this, "SELECTED");
+ }
+ catch (Exception ex)
+ {
+ //MessageBox.Show($"Error loading file {FileName}: {ex.Message}");
+ }
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/MapPatternEditor.axaml b/ZXBStudio/DocumentEditors/ZXMaps/MapPatternEditor.axaml
new file mode 100644
index 0000000..64ad870
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/MapPatternEditor.axaml
@@ -0,0 +1,150 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Zoom 24x
+
+
+ Frame
+
+
+
+
+
+ Paper
+
+
+ 0
+
+
+
+
+
+ Ink
+
+
+ 1
+
+
+
+
+
+
+
+
+
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/MapPatternEditor.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/MapPatternEditor.axaml.cs
new file mode 100644
index 0000000..065866b
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/MapPatternEditor.axaml.cs
@@ -0,0 +1,687 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.Shapes;
+using Avalonia.Input;
+using Avalonia.Media;
+using Avalonia.Platform;
+using Avalonia.Themes.Fluent;
+using Avalonia.Threading;
+using Svg;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks.Sources;
+using System.Xml.Schema;
+using ZXBasicStudio.Common;
+using ZXBasicStudio.DocumentEditors.ZXGraphics;
+using ZXBasicStudio.DocumentEditors.ZXGraphics.log;
+using ZXBasicStudio.DocumentEditors.ZXGraphics.neg;
+using ZXBasicStudio.DocumentEditors.ZXMaps.Neg;
+using ZXBasicStudio.Extensions;
+
+namespace ZXBasicStudio.DocumentEditors.ZXMaps
+{
+ public partial class MapPatternEditor : UserControl
+ {
+ #region Private properties
+
+ ///
+ /// last zoom value
+ ///
+ private int lastZoom = 0;
+
+ ///
+ /// Zooms values
+ ///
+ private int[] zooms = new int[]
+ {
+ 1,2,4,8,16,24,32,48,64
+ };
+ private int pixelSize = 72;
+
+ private byte actualFrame = 0;
+
+ #endregion
+
+
+ #region Public properties
+
+ ///
+ /// Zoom
+ ///
+ public int Zoom
+ {
+ get
+ {
+ return _Zoom;
+ }
+ set
+ {
+ _Zoom = value;
+ Refresh(false);
+ }
+ }
+
+ ///
+ /// Color index of the primary color
+ ///
+ public int PrimaryColorIndex { get; set; }
+
+ ///
+ /// Color index of the secondary color
+ ///
+ public int SecondaryColorIndex { get; set; }
+
+ public bool Bright { get; set; }
+ public bool Flash { get; set; }
+
+ private bool _ViewAttributes = true;
+
+ public bool InvertPixelsCell { get; set; } = false;
+
+ public bool InvertColorsCell { get; set; } = false;
+
+ public bool ColorPicker { get; set; } = false;
+
+ public ZXMapsMap Map
+ {
+ get
+ {
+ return _map;
+ }
+ set
+ {
+ _map = value;
+ Refresh(false);
+ }
+ }
+
+ public ZXMapsTiles Tiles
+ {
+ get
+ {
+ return _tiles;
+ }
+ set
+ {
+ _tiles = value;
+ Refresh(false);
+ }
+ }
+
+ public int CurrentTile { get; set; }
+
+ #endregion
+
+
+ #region Private fields
+
+ private ZXMapsMap _map = null;
+ private ZXMapsTiles _tiles = null;
+ private int _Zoom = 24;
+ private Action CallBackCommand = null;
+ private int? lastId = null;
+ private ZXGridImageView[,] images = null;
+ private PaletteColor[] ZXSpectrumPalette = null;
+
+ ///
+ /// True when mouse left button is pressed
+ ///
+ private bool MouseLeftPressed = false;
+ ///
+ /// True when moude right button is pressed
+ ///
+ private bool MouseRightPressed = false;
+
+ ///
+ /// Dispatcher for refresh operations
+ ///
+ private DispatcherTimer tmr = null;
+
+ #endregion
+
+
+ #region Public Methods
+
+ public MapPatternEditor()
+ {
+ InitializeComponent();
+
+ ZXSpectrumPalette = ServiceLayer.GetPalette(GraphicsModes.ZXSpectrum);
+ PrimaryColorIndex = 0;
+ SecondaryColorIndex = 7;
+
+ cnvEditor.PointerMoved += CnvEditor_PointerMoved;
+ cnvEditor.PointerPressed += CnvEditor_PointerPressed;
+
+ sldZoom.PropertyChanged += SldZoom_PropertyChanged;
+ txtFrame.PropertyChanged += TxtFrame_PropertyChanged;
+
+ btnClear.Tapped += BtnClear_Tapped;
+ btnExport.Tapped += BtnExport_Tapped;
+
+ btnUndo.Tapped += BtnUndo_Tapped;
+ btnRedo.Tapped += BtnRedo_Tapped;
+
+ btnPaper.Tapped += BtnPaper_Click;
+ btnInk.Tapped += BtnInk_Tapped;
+
+ UpdateColorPanel();
+ }
+
+
+ ///
+ /// Initializes the control
+ ///
+ /// CallBack for commands: "REFRESH"
+ /// True if OK or False if error
+ public bool Initialize(ZXMapsMap map, ZXMapsTiles tileData, Action callBackCommand)
+ {
+ this._map = map;
+ this._tiles = tileData;
+ this.CallBackCommand = callBackCommand;
+ Refresh(false);
+ return true;
+ }
+
+
+ ///
+ /// Refresh the draw area
+ ///
+ public void Refresh(bool withCallBack)
+ {
+ if (_map == null || _tiles == null)
+ {
+ cnvEditor.Children.Clear();
+ return;
+ }
+
+
+ if (images == null)
+ {
+ images = new ZXGridImageView[_map.Width, _map.Height];
+ for (int y = 0; y < _map.Height; y++)
+ {
+ for (int x = 0; x < _map.Width; x++)
+ {
+ var i = new ZXGridImageView();
+ i.Show8x8Grid = false;
+ i.ViewAttributes = false;
+ i.ShowTileGrid = true;
+ i.Grid8x8Color = new SkiaSharp.SKColor(64, 0, 0);
+
+ //i.PointerEntered += I_PointerEntered;
+ //i.PointerExited += I_PointerExited;
+ //i.PointerPressed += I_PointerPressed;
+ //i.PointerReleased += I_PointerReleased;
+ //i.PointerMoved += I_PointerMoved;
+ //i.Tag = x + (y * _map.Width);
+ images[x, y] = i;
+ cnvEditor.Children.Add(i);
+ }
+ }
+ }
+
+ if (_map.TileArrays == null)
+ {
+ _map.TileArrays = new ZXMapsTileArray[_map.Width, _map.Height];
+ for (int y = 0; y < _map.Height; y++)
+ {
+ for (int x = 0; x < _map.Width; x++)
+ {
+ _map.TileArrays[x, y] = new ZXMapsTileArray()
+ {
+ Id = x + (y * _map.Width),
+ Properties = new List(),
+ TileId = 0,
+ X = x,
+ Y = y,
+ Z = 0
+ };
+ }
+ }
+ }
+
+ for (int y = 0; y < _map.Height; y++)
+ {
+ int yy = y * _Zoom * 4;
+ for (int x = 0; x < _map.Width; x++)
+ {
+ var i = images[x, y];
+ i.Zoom = _Zoom;
+ Canvas.SetLeft(i, x * i.Width);
+ Canvas.SetTop(i, y * i.Height);
+ if (x < _map.TileArrays.GetLength(0) &&
+ y < _map.TileArrays.GetLength(1))
+ {
+ var td = _map.TileArrays[x, y];
+ if (td != null)
+ {
+ var tile = _tiles.Tiles.FirstOrDefault(d => d != null && d.Id == td.TileId);
+ //tile.Palette[0] = new PaletteColor() { Red = 0x00, Green = 0x00, Blue = 0x00 };
+ //tile.Palette[1]= new PaletteColor() { Red = 0x00, Green = 0x00, Blue = 0xa0 };
+
+ var aspect = new TileImage();
+ aspect.RenderTile(tile, 0);
+ i.BackgroundImage = aspect;
+ }
+ }
+ }
+ }
+
+ try
+ {
+ cnvEditor.Width = images[0, 0].Width * _map.Width;
+ cnvEditor.Height = images[0, 0].Height * _map.Height;
+ }
+ catch { }
+
+ this.InvalidateVisual();
+
+ if (withCallBack)
+ {
+ CallBackCommand?.Invoke(this, "REFRESH");
+ }
+ }
+
+ #endregion
+
+
+ #region Undo and Redo
+
+ private List operations = new List();
+ private int operationIndex = -1;
+
+ public void Undo()
+ {
+ if (operationIndex > operations.Count - 1)
+ {
+ operationIndex = operations.Count - 1;
+ }
+ if (operationIndex < 0)
+ {
+ return;
+ }
+ var op = operations[operationIndex];
+ if (op == null)
+ {
+ return;
+ }
+ _map.TileArrays[op.X, op.Y] = op;
+ operationIndex--;
+ Refresh(true);
+ }
+
+
+ public void Redo()
+ {
+ if (operationIndex > operations.Count - 1)
+ {
+ return;
+ }
+ if (operationIndex < 0)
+ {
+ return;
+ }
+ var op = operations[operationIndex];
+ _map.TileArrays[op.X, op.Y] = op;
+ operationIndex++;
+ Refresh(true);
+ }
+
+ #endregion
+
+
+ #region Drawing events
+
+ ///
+ /// Event for mouse pressed
+ ///
+ ///
+ ///
+ private void CnvEditor_PointerPressed(object? sender, Avalonia.Input.PointerPressedEventArgs e)
+ {
+ var p = e.GetCurrentPoint(cnvEditor);
+ var w = images[0, 0].Width;
+ var h = images[0, 0].Height;
+ int x = (int)(p.Position.X / w);
+ int y = (int)(p.Position.Y / h);
+
+ if (p.Properties.IsLeftButtonPressed)
+ {
+ SetTile(x, y, CurrentTile);
+ }
+ if (p.Properties.IsRightButtonPressed)
+ {
+ SetTile(x, y, 0);
+ }
+ }
+
+
+ ///
+ /// Event for pointer moved
+ ///
+ ///
+ ///
+ private void CnvEditor_PointerMoved(object? sender, Avalonia.Input.PointerEventArgs e)
+ {
+ if (e.Properties.IsLeftButtonPressed || e.Properties.IsRightButtonPressed)
+ {
+ var p = e.GetCurrentPoint(cnvEditor);
+ var w = images[0, 0].Width;
+ var h = images[0, 0].Height;
+ int x = (int)(p.Position.X / w);
+ int y = (int)(p.Position.Y / h);
+
+ if (e.Properties.IsLeftButtonPressed)
+ {
+ SetTile(x, y, CurrentTile);
+ }
+ else if (e.Properties.IsRightButtonPressed)
+ {
+ SetTile(x, y, 0);
+ }
+ }
+ }
+
+
+ private int lastX = -1;
+ private int lastY = -1;
+ private int lastValue = -1;
+
+ ///
+ /// Set point to a color value
+ ///
+ /// Absolute x
+ /// Absolute y
+ /// Value of the point
+ private void SetTile(int x, int y, int value)
+ {
+ if (_map == null)
+ {
+ return;
+ }
+
+ if (lastX == x && lastY == y && lastValue == value)
+ {
+ return;
+ }
+ var ta = _map.TileArrays[x, y];
+ operations.Add(ta.Clonar());
+ operationIndex = operations.Count - 1;
+ lastX = ta.X;
+ lastY = ta.Y;
+ lastValue = value;
+
+ if (ta.TileId != value)
+ {
+ ta.TileId = value;
+ Refresh(true);
+ }
+ }
+
+
+ private void Tmr_Tick(object? sender, EventArgs e)
+ {
+ Refresh(true);
+ tmr.Stop();
+ }
+
+ #endregion
+
+
+ #region Icon actions
+
+
+ ///
+ /// Clear Tile
+ ///
+ public void Clear()
+ {
+ for (int y = 0; y < _map.Height; y++)
+ {
+ for (int x = 0; x < _map.Width; x++)
+ {
+ SetTile(x, y, 0);
+ }
+ }
+ Refresh(true);
+ }
+
+ #endregion
+
+
+ #region Color
+
+ private void BtnPaper_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
+ {
+ if (ctrlColorPicker.IsVisible)
+ {
+ ctrlColorPicker.IsVisible = false;
+ return;
+ }
+ var Tile = Tiles.Tiles[0];
+ //Tile.GraphicMode = GraphicsModes.ZXSpectrum;
+ //Tile.Palette=ZXGraphics.log.ServiceLayer.GetPalette(Tile.GraphicMode);
+ ctrlColorPicker.IsVisible = true;
+ var p = ServiceLayer.GetPalette(GraphicsModes.ZXSpectrum);
+ ctrlColorPicker.Inicialize(GraphicsModes.ZXSpectrum, p, SecondaryColorIndex, ColorPickerPaper_Action);
+ }
+
+
+ private void BtnInk_Tapped(object? sender, TappedEventArgs e)
+ {
+ if (ctrlColorPicker.IsVisible)
+ {
+ ctrlColorPicker.IsVisible = false;
+ return;
+ }
+ var Tile = Tiles.Tiles[0];
+ //Tile.GraphicMode = GraphicsModes.ZXSpectrum;
+ //Tile.Palette=ZXGraphics.log.ServiceLayer.GetPalette(Tile.GraphicMode);
+ ctrlColorPicker.IsVisible = true;
+ var p = ServiceLayer.GetPalette(GraphicsModes.ZXSpectrum);
+ ctrlColorPicker.Inicialize(GraphicsModes.ZXSpectrum, p, PrimaryColorIndex, ColorPickerInk_Action);
+ }
+
+
+ public void UpdateColorPanel()
+ {
+ if (Tiles == null)
+ {
+ return;
+ }
+
+ switch (Tiles.GraphicMode)
+ {
+ case GraphicsModes.Monochrome:
+ {
+ if (Tiles.Tiles[0].Palette == null)
+ {
+ Tiles.Tiles[0].Palette = ServiceLayer.GetPalette(Tiles.GraphicMode);
+ }
+ var ink = Tiles.Tiles[0].Palette[1];
+ var paper = Tiles.Tiles[0].Palette[0];
+ grdPaper.Background = new SolidColorBrush(Color.FromRgb(paper.Red, paper.Green, paper.Blue));
+ txtPaper.Foreground = new SolidColorBrush(Color.FromRgb(ink.Red, ink.Green, ink.Blue));
+ txtPaper.Text = "0";
+ grdInk.Background = new SolidColorBrush(Color.FromRgb(ink.Red, ink.Green, ink.Blue));
+ txtInk.Foreground = new SolidColorBrush(Color.FromRgb(paper.Red, paper.Green, paper.Blue));
+ txtInk.Text = "1";
+ }
+ break;
+
+ case GraphicsModes.ZXSpectrum:
+ case GraphicsModes.Next:
+ {
+ var ink = Tiles.Tiles[0].Palette[PrimaryColorIndex];
+ var paper = Tiles.Tiles[0].Palette[SecondaryColorIndex];
+ grdPaper.Background = new SolidColorBrush(Color.FromRgb(paper.Red, paper.Green, paper.Blue));
+ txtPaper.Foreground = new SolidColorBrush(Color.FromRgb(ink.Red, ink.Green, ink.Blue));
+ txtPaper.Text = SecondaryColorIndex.ToString();
+ grdInk.Background = new SolidColorBrush(Color.FromRgb(ink.Red, ink.Green, ink.Blue));
+ txtInk.Foreground = new SolidColorBrush(Color.FromRgb(paper.Red, paper.Green, paper.Blue));
+ txtInk.Text = PrimaryColorIndex.ToString();
+ }
+ break;
+ }
+ }
+
+
+
+ private void ColorPickerPaper_Action(string command, int indexColor)
+ {
+ SecondaryColorIndex = indexColor;
+ UpdatePalette();
+ UpdateColorPanel();
+ CallBackCommand(this, "REFRESH");
+ }
+
+
+ private void ColorPickerInk_Action(string command, int indexColor)
+ {
+ PrimaryColorIndex = indexColor;
+ UpdatePalette();
+ UpdateColorPanel();
+ CallBackCommand(this, "REFRESH");
+ }
+
+
+ private void UpdatePalette()
+ {
+ if (Tiles.GraphicMode == GraphicsModes.Monochrome)
+ {
+ var p = ServiceLayer.GetPalette(GraphicsModes.ZXSpectrum);
+ foreach (var t in Tiles.Tiles)
+ {
+ if (t == null)
+ {
+ continue;
+ }
+ t.Palette[0] = p[SecondaryColorIndex];
+ t.Palette[1] = p[PrimaryColorIndex];
+ }
+ UpdateColorPanel();
+ }
+ Refresh(true);
+ }
+
+ #endregion
+
+
+
+ #region Main editor
+
+ ///
+ /// Zoom changed
+ ///
+ ///
+ ///
+ private void SldZoom_PropertyChanged(object? sender, Avalonia.AvaloniaPropertyChangedEventArgs e)
+ {
+ int z = (int)sldZoom.Value;
+ if (z == 0 || z == lastZoom)
+ {
+ return;
+ }
+ lastZoom = z;
+
+ z = zooms[z - 1];
+ pixelSize = z * 4;
+ txtZoom.Text = "Zoom " + z.ToString() + "x";
+ Zoom = z;
+ }
+
+
+ public void ZoomIn()
+ {
+ int v = sldZoom.Value.ToInteger();
+ if (v < zooms.Length - 1)
+ {
+ sldZoom.Value = v - 1;
+ }
+ }
+
+
+ public void ZoomOut()
+ {
+ int v = sldZoom.Value.ToInteger();
+ if (v > 0)
+ {
+ sldZoom.Value = v - 1;
+ }
+
+ }
+
+
+ private void TxtFrame_PropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
+ {
+ // TODO: Multiple frames per tile?
+ /*
+ byte v = txtFrame.Text.ToByte();
+ if (actualFrame == v ||
+ ctrlProperties.TileData == null ||
+ v < 0 ||
+ v >= (ctrlProperties.TileData.Frames))
+ {
+ return;
+ }
+ if (v > 255)
+ {
+ v = 255;
+ }
+ actualFrame = v;
+ if (TileData != null)
+ {
+ TileData.CurrentFrame = actualFrame;
+ Refresh();
+ }
+ Refresh();
+ */
+ }
+
+ #endregion
+
+
+ #region ToolBar
+
+
+ private void BtnRedo_Tapped(object? sender, TappedEventArgs e)
+ {
+ Redo();
+ }
+
+
+ private void BtnUndo_Tapped(object? sender, TappedEventArgs e)
+ {
+ Undo();
+ }
+
+
+ ///
+ /// Clear click
+ ///
+ ///
+ ///
+ private void BtnClear_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
+ {
+ this.Clear();
+ }
+
+
+ private void BtnExport_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
+ {
+ Export();
+ }
+
+
+ public void Export()
+ {
+ CallBackCommand(this, "EXPORT");
+ }
+
+ #endregion
+ }
+}
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/MapPropertiesControl.axaml b/ZXBStudio/DocumentEditors/ZXMaps/MapPropertiesControl.axaml
new file mode 100644
index 0000000..d662de6
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/MapPropertiesControl.axaml
@@ -0,0 +1,112 @@
+
+
+
+
+
+
+ Map type:
+
+
+ Multidirectional scroll
+
+
+ Map width:
+
+
+
+ (tiles)
+
+ Map height:
+
+
+
+ (tiles)
+
+ Tile width:
+
+
+
+ (pixels)
+
+ Tile height:
+
+
+
+ (pixels)
+
+ Mapping type:
+
+ Sequential
+ Coordinates
+ Blocks
+
+
+
+
+
+
+
+
+
+
+
+
+ Name:
+
+
+ Layer type:
+
+ Tiles
+ Masks
+ Sprites
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Name:
+
+
+
+
+
+
+
+
+
+
+
+
+ Tile id:
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/MapPropertiesControl.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/MapPropertiesControl.axaml.cs
new file mode 100644
index 0000000..87d1e09
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/MapPropertiesControl.axaml.cs
@@ -0,0 +1,189 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.Primitives.PopupPositioning;
+using Avalonia.Interactivity;
+
+//using Avalonia.Controls.Shapes;
+using Avalonia.Media;
+using Avalonia.Threading;
+using AvaloniaEdit.Folding;
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.IO;
+using System.Linq;
+using System.Security.Cryptography;
+using ZXBasicStudio.Classes;
+using ZXBasicStudio.Common;
+using ZXBasicStudio.DocumentEditors.NextDows.neg;
+using ZXBasicStudio.DocumentEditors.ZXGraphics;
+using ZXBasicStudio.DocumentEditors.ZXGraphics.neg;
+using ZXBasicStudio.DocumentEditors.ZXMaps.Neg;
+using ZXBasicStudio.DocumentEditors.ZXTextEditor.Classes.Folding;
+using ZXBasicStudio.DocumentModel.Classes;
+
+namespace ZXBasicStudio.DocumentEditors.ZXMaps
+{
+ public partial class MapPropertiesControl : UserControl
+ {
+ public ZXMapsMap Map
+ {
+ get
+ {
+ return _Map;
+ }
+ set
+ {
+ _Map = value;
+ Refresh();
+ }
+ }
+ private ZXMapsMap _Map = null;
+
+ public ZXMapsTiles Tiles
+ {
+ get
+ {
+ return _Tiles;
+ }
+ set
+ {
+ _Tiles = value;
+ Refresh();
+ }
+ }
+ private ZXMapsTiles _Tiles = null;
+
+ private Action Command = null;
+
+
+ public MapPropertiesControl()
+ {
+ InitializeComponent();
+ }
+
+
+ public bool Initialize(ZXMapsMap map, ZXMapsTiles tiles, Action callBackCommand)
+ {
+ this._Map = map;
+ this._Tiles = tiles;
+ this.Command = callBackCommand;
+
+ cmbMapType.SelectionChanged += CmbMapType_SelectionChanged;
+ txtMapWidth.ValueChanged += TxtMapWidth_ValueChanged;
+ txtMapHeight.ValueChanged += TxtMapHeight_ValueChanged;
+ txtMapWidth.ValueChanged += TxtMapWidth_ValueChanged;
+ txtMapTileWidth.ValueChanged += TxtMapTileWidth_ValueChanged;
+ txtMapTileHeight.ValueChanged += TxtMapTileHeight_ValueChanged;
+ //cmbMappingType.SelectionChanged += CmbMappingType_SelectionChanged;
+
+ Refresh();
+
+ return true;
+ }
+
+
+ public void Refresh()
+ {
+ if (Map == null)
+ {
+ tabControl.SelectedIndex = 0;
+ //tabControl.IsVisible = false;
+ }
+ else
+ {
+ // Map
+ cmbMapType.SelectedIndex = (int)Map.MapType;
+ txtMapHeight.Value = Map.Height;
+ txtMapWidth.Value = Map.Width;
+ txtMapTileHeight.Value = Map.TileHeight;
+ txtMapTileWidth.Value = Map.TileWidth;
+ cmbMappingType.SelectedIndex = (int)Map.MappingType;
+ // Layer
+ var layer = Map.Layers?.FirstOrDefault(d => d.Selected);
+ if (layer == null)
+ {
+ tabControl.SelectedIndex = 0;
+ }
+ else
+ {
+ txtLayerName.Text = layer.Name;
+ cmbLayerType.SelectedIndex = (int)layer.LayersType;
+ }
+ }
+
+ if(_Map!=null && _Tiles != null)
+ {
+ if (_Map.TileWidth != _Tiles.Width)
+ {
+ txtMapTileWidth.Background = new SolidColorBrush(Colors.Red);
+ }
+ else
+ {
+ txtMapTileWidth.Background = null;
+ }
+ if (_Map.TileHeight != _Tiles.Height)
+ {
+ txtMapTileHeight.Background = new SolidColorBrush(Colors.Red);
+ }
+ else
+ {
+ txtMapTileHeight.Background = null;
+ }
+ }
+ }
+
+
+ #region Values changed
+
+ private void CmbMappingType_SelectionChanged(object? sender, SelectionChangedEventArgs e)
+ {
+ Map.MappingType = (ZXMapsMappingTypes)cmbMappingType.SelectedIndex;
+ Command?.Invoke(this, "UPDATE");
+ Refresh();
+ }
+
+
+ private void TxtMapTileHeight_ValueChanged(object? sender, NumericUpDownValueChangedEventArgs e)
+ {
+ Map.TileHeight = txtMapTileHeight.Text.ToInteger();
+ Command?.Invoke(this, "UPDATE");
+ Refresh();
+ }
+
+
+ private void TxtMapTileWidth_ValueChanged(object? sender, NumericUpDownValueChangedEventArgs e)
+ {
+ Map.TileWidth = txtMapTileWidth.Text.ToInteger();
+ Command?.Invoke(this, "UPDATE");
+ Refresh();
+ }
+
+
+ private void TxtMapHeight_ValueChanged(object? sender, NumericUpDownValueChangedEventArgs e)
+ {
+ Map.Height = txtMapHeight.Text.ToInteger();
+ Command?.Invoke(this, "UPDATE");
+ Refresh();
+ }
+
+
+ private void TxtMapWidth_ValueChanged(object? sender, NumericUpDownValueChangedEventArgs e)
+ {
+ Map.Width = txtMapWidth.Text.ToInteger();
+ Command?.Invoke(this, "UPDATE");
+ Refresh();
+ }
+
+
+ private void CmbMapType_SelectionChanged(object? sender, SelectionChangedEventArgs e)
+ {
+ Map.MapType = (ZXMapsTypes)cmbMapType.SelectedIndex;
+ Command?.Invoke(this, "UPDATE");
+ Refresh();
+ }
+
+ #endregion
+
+ }
+}
\ No newline at end of file
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsLayer.cs b/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsLayer.cs
new file mode 100644
index 0000000..45f8084
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsLayer.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ZXBasicStudio.DocumentEditors.ZXMaps.Neg
+{
+ ///
+ /// Represents a map layer within the ZX Maps system, providing properties to control its visibility, rendering
+ /// order, and data type.
+ ///
+ /// Use this class to manage the display and organization of individual layers in a ZX Maps
+ /// document. The layer's type, order, and visibility can be customized to control how map data is presented and
+ /// interacted with. The specific kind of data represented by the layer is determined by the LayersType
+ /// property.
+ public class ZXMapsLayer
+ {
+ ///
+ /// Gets or sets the unique identifier for the entity.
+ ///
+ public int Id { get; set; }
+ ///
+ /// Gets or sets the name of the entity.
+ ///
+ public string Name { get; set; }
+ ///
+ /// Gets or sets the order in which the item appears in a collection.
+ ///
+ public int Order { get; set; }
+ ///
+ /// Gets or sets a value indicating whether the associated element is visible.
+ ///
+ public bool Visible { get; set; }
+ ///
+ /// Gets or sets a value indicating whether the item is selected.
+ ///
+ public bool Selected { get; set; }
+ ///
+ /// Gets or sets the type of map layers to display.
+ ///
+ public ZXMapsLayersType LayersType { get; set; }
+ }
+}
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsLayersType.cs b/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsLayersType.cs
new file mode 100644
index 0000000..a72eae3
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsLayersType.cs
@@ -0,0 +1,14 @@
+namespace ZXBasicStudio.DocumentEditors.ZXMaps.Neg
+{
+ ///
+ /// Specifies the types of layers that can be used in ZX Maps, such as tiles, masks, and sprites.
+ ///
+ /// Use this enumeration to indicate the layer type when configuring or rendering map elements in
+ /// ZX Maps. Each value represents a distinct visual or functional layer within the map system.
+ public enum ZXMapsLayersType
+ {
+ Tiles = 0,
+ Masks = 1,
+ Sprites = 2
+ }
+}
\ No newline at end of file
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsMap.cs b/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsMap.cs
new file mode 100644
index 0000000..f877e47
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsMap.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ZXBasicStudio.DocumentEditors.ZXMaps.Neg
+{
+ ///
+ /// Represents a map
+ ///
+ public class ZXMapsMap
+ {
+ public string Name { get; set; }
+ public ZXMapsTypes MapType { get; set; }
+ public int Width { get; set; }
+ public int Height { get; set; }
+ public int TileWidth { get; set; }
+ public int TileHeight { get; set; }
+ public ZXMapsMappingTypes MappingType { get; set; }
+ public List PropertyDefinitons { get; set; }
+ public List Layers { get; set; }
+ public ZXMapsTileArray[,] TileArrays { get; set; }
+ }
+}
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsMappingTypes.cs b/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsMappingTypes.cs
new file mode 100644
index 0000000..591150a
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsMappingTypes.cs
@@ -0,0 +1,9 @@
+namespace ZXBasicStudio.DocumentEditors.ZXMaps.Neg
+{
+ public enum ZXMapsMappingTypes
+ {
+ Sequential = 0,
+ Coordinates = 1,
+ Blocks = 2
+ }
+}
\ No newline at end of file
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsProperty.cs b/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsProperty.cs
new file mode 100644
index 0000000..ed8edfb
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsProperty.cs
@@ -0,0 +1,8 @@
+namespace ZXBasicStudio.DocumentEditors.ZXMaps.Neg
+{
+ public class ZXMapsProperty
+ {
+ public int Id { get; set; }
+ public string Value { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsPropertyDefiniton.cs b/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsPropertyDefiniton.cs
new file mode 100644
index 0000000..484df27
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsPropertyDefiniton.cs
@@ -0,0 +1,11 @@
+namespace ZXBasicStudio.DocumentEditors.ZXMaps.Neg
+{
+ public class ZXMapsPropertyDefiniton
+ {
+ public int Id { get; set; }
+ public string Name { get; set; }
+ public ZXMapsPropertyTypes PropertyType { get; set; }
+ public int MinValue { get; set; }
+ public int MaxValue { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsPropertyTypes.cs b/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsPropertyTypes.cs
new file mode 100644
index 0000000..20ca864
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsPropertyTypes.cs
@@ -0,0 +1,10 @@
+namespace ZXBasicStudio.DocumentEditors.ZXMaps.Neg
+{
+ public enum ZXMapsPropertyTypes
+ {
+ Byte = 0,
+ Integer = 1,
+ String = 2,
+ Bit = 3,
+ }
+}
\ No newline at end of file
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsRoom.cs b/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsRoom.cs
new file mode 100644
index 0000000..ebf89a8
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsRoom.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ZXBasicStudio.DocumentEditors.ZXMaps.Neg
+{
+ public class ZXMapsRoom
+ {
+ public int Id { get; set; }
+ public string Name { get; set; }
+ public int IdLayer { get; set; }
+ public List Properties { get; set; } = new List();
+ }
+}
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsTile.cs b/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsTile.cs
new file mode 100644
index 0000000..30ef700
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsTile.cs
@@ -0,0 +1,63 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ZXBasicStudio.DocumentEditors.ZXGraphics.neg
+{
+ ///
+ /// Represents a tile with his properties
+ ///
+ public class ZXMapsTile
+ {
+ ///
+ /// Id of the tile
+ ///
+ public int Id { get; set; }
+ ///
+ /// Name of the tile
+ ///
+ public string Name { get; set; }
+ ///
+ /// Graphics mode of the tile
+ ///
+ public GraphicsModes GraphicMode { get; set; }
+ ///
+ /// Width of the tile in pixels
+ ///
+ public int Width { get; set; }
+ ///
+ /// Height in pixels
+ ///
+ public int Height { get; set; }
+ ///
+ /// True if the tile is masked (not suported in all modes)
+ ///
+ public bool Masked { get; set; }
+ ///
+ /// Number of frames for the tile
+ ///
+ public byte Frames { get; set; }
+ ///
+ /// Current frame
+ ///
+ public byte CurrentFrame { get; set; }
+ ///
+ /// Patterns for the tile (one pattern for frame)
+ ///
+ public List Patterns { get; set; }
+ ///
+ /// Default transparent or background color
+ ///
+ public byte DefaultColor { get; set; }
+ ///
+ /// Palete color for the tile
+ ///
+ public PaletteColor[] Palette { get; set; }
+ ///
+ /// True when the tile was auto-exported
+ ///
+ public bool Export { get; set; }
+ }
+}
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsTileArray.cs b/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsTileArray.cs
new file mode 100644
index 0000000..5f932c8
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsTileArray.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ZXBasicStudio.DocumentEditors.ZXMaps.Neg
+{
+ ///
+ /// Tile reference into a map
+ ///
+ public class ZXMapsTileArray
+ {
+ ///
+ /// Unique Id
+ ///
+ public int Id { get; set; }
+ ///
+ /// Tile id in this map position
+ ///
+ public int TileId { get; set; }
+ ///
+ /// X position in this map
+ ///
+ public int X { get; set; }
+ ///
+ /// Y position in this map
+ ///
+ public int Y { get; set; }
+ ///
+ /// Z position in this map
+ ///
+ public int Z { get; set; }
+ ///
+ /// Properties for this tile in this place of the map
+ ///
+ public List Properties { get; set; }
+ }
+}
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsTiles.cs b/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsTiles.cs
new file mode 100644
index 0000000..2fa1cf1
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsTiles.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using ZXBasicStudio.DocumentEditors.ZXGraphics.neg;
+
+namespace ZXBasicStudio.DocumentEditors.ZXMaps.Neg
+{
+ public class ZXMapsTiles
+ {
+ public string Name { get; set; }
+ public GraphicsModes GraphicMode { get; set; }
+ public int Width { get; set; }
+ public int Height { get; set; }
+
+ public string DefaultMap { get; set; }
+ public List Tiles { get; set; }
+ }
+}
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsTypes.cs b/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsTypes.cs
new file mode 100644
index 0000000..3f274c6
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsTypes.cs
@@ -0,0 +1,10 @@
+namespace ZXBasicStudio.DocumentEditors.ZXMaps.Neg
+{
+ public enum ZXMapsTypes
+ {
+ MultidirectionalScroll = 0,
+ Rooms = 1,
+ HorizontalScroll = 2,
+ VerticalScroll = 3,
+ }
+}
\ No newline at end of file
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml b/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml
new file mode 100644
index 0000000..69c2418
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml
@@ -0,0 +1,105 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml.cs
new file mode 100644
index 0000000..8dabb66
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml.cs
@@ -0,0 +1,1123 @@
+using Avalonia.Controls;
+using System.Diagnostics;
+using ZXBasicStudio.DocumentEditors.ZXGraphics.log;
+using ZXBasicStudio.DocumentEditors.ZXGraphics.neg;
+using ZXBasicStudio.DocumentEditors.ZXMaps.Neg;
+using System;
+using System.Linq;
+using ZXBasicStudio.Common;
+using Avalonia;
+using ZXBasicStudio.DocumentModel.Classes;
+using System.IO;
+using ZXBasicStudio.Classes;
+using AvaloniaEdit.Folding;
+using Avalonia.Threading;
+using ZXBasicStudio.DocumentEditors.ZXTextEditor.Classes.Folding;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading;
+using ShimSkiaSharp;
+using Avalonia.Interactivity;
+using Avalonia.Input;
+using AvaloniaEdit;
+using System.Reflection;
+using FFmpeg.AutoGen;
+using Avalonia.Media;
+using ZXBasicStudio.DocumentEditors.ZXMaps.Log;
+using ZXBasicStudio.Controls;
+using System.Data;
+
+namespace ZXBasicStudio.DocumentEditors.ZXMaps
+{
+ ///
+ /// Editor for GDUs and Fonts
+ ///
+ public partial class TileEditor : ZXDocumentEditorBase, IObserver
+ {
+ #region Events
+
+ public override event EventHandler? DocumentRestored;
+ public override event EventHandler? DocumentModified;
+ public override event EventHandler? DocumentSaved;
+ public override event EventHandler? RequestSaveDocument;
+
+ #endregion
+
+
+ #region ZXDocumentBase properties
+
+ public override string DocumentName
+ {
+ get
+ {
+ return Path.GetFileName(FileName);
+ }
+ }
+
+
+ public override string DocumentPath
+ {
+ get
+ {
+ return Path.GetFullPath(FileName);
+ }
+ }
+
+
+ public override bool Modified
+ {
+ get
+ {
+ return _Modified;
+ }
+ }
+
+ private bool _Modified = false;
+
+ protected virtual AbstractFoldingStrategy? foldingStrategy { get { return null; } }
+
+ private Guid documentTypeId = Guid.Empty;
+
+ private int selectedTileId = 0;
+
+ #endregion
+
+
+ #region IObserver implementation for modified document notifications
+
+ public void OnCompleted()
+ {
+
+ }
+
+ public void OnError(Exception error)
+ {
+
+ }
+
+ public void OnNext(AvaloniaPropertyChangedEventArgs value)
+ {
+
+ }
+
+
+
+ #endregion
+
+
+ #region Private variables
+
+ private string FileName = "";
+
+ private ZXMapsTiles TileMain = null;
+ private int tileWidth = 16;
+ private int tileHeight = 16;
+ private ZXMapsMap Map = null;
+
+ ///
+ /// List of Tiles patterns controls in the set
+ ///
+ private List TilePatternsList = null;
+
+ private FoldingManager? fManager;
+ private DispatcherTimer? updateFoldingsTimer;
+
+ #endregion
+
+
+ #region ZXDocumentBase functions
+
+ public override bool SaveDocument(TextWriter OutputLog)
+ {
+ try
+ {
+ var masterList = TilePatternsList.Select(d => d.TileData).ToArray();
+ var sprList = new List();
+ foreach (var spr in masterList)
+ {
+ sprList.Add(spr); //.Clonar());
+ }
+ foreach (ZXMapsTile spr in sprList)
+ {
+ if (spr == null)
+ {
+ continue;
+ }
+
+ if (spr.Frames < spr.Patterns.Count())
+ {
+ spr.Patterns = spr.Patterns.Take(spr.Frames).ToList();
+ }
+ }
+ TileMain.Tiles = sprList;
+ var dataJSon = TileMain.Serializar();
+ if (!ServiceLayer.Files_SaveFileString(FileName, dataJSon))
+ {
+ return false;
+ }
+
+ if (Map != null)
+ {
+ var json = Map.Serializar();
+ var fileName = Path.Combine(ZXProjectManager.Current.ProjectPath, Map.Name + ".zxmap");
+ if (!ServiceLayer.Files_SaveFileString(fileName, json))
+ {
+ return false;
+ }
+ }
+
+ _Modified = false;
+ DocumentSaved?.Invoke(this, EventArgs.Empty);
+ return true;
+ }
+ catch (Exception ex)
+ {
+ OutputLog.WriteLine($"Error saving file {FileName}: {ex.Message}");
+ return false;
+ }
+ }
+
+
+ public override bool RenameDocument(string NewName, TextWriter OutputLog)
+ {
+ FileName = NewName;
+ //ServiceLayer_Maps.Maps_RemoveMap(FileName);
+ //ServiceLayer_Maps.Maps_AddMap(FileName,Map);
+ return true;
+ }
+
+
+ public override bool CloseDocument(TextWriter OutputLog, bool ForceClose)
+ {
+ //ServiceLayer_Maps.Maps_RemoveMap(FileName);
+ return true;
+ }
+
+
+ public override void Dispose()
+ {
+ }
+
+
+ public override void Activated()
+ {
+ this.Focus();
+ ctrlPreview.Start();
+ base.Activated();
+ }
+
+
+ public override void Deactivated()
+ {
+ ctrlPreview.Stop();
+ base.Deactivated();
+ }
+
+ #endregion
+
+
+ ///
+ /// Constructor, without parameters is mandatory to cal Initialize
+ ///
+ public TileEditor()
+ {
+ InitializeComponent();
+ }
+
+
+ ///
+ /// Constructor
+ ///
+ /// Name of the file
+ public TileEditor(string fileName)
+ {
+ InitializeComponent();
+ new Thread(() => Initialize(fileName)).Start();
+ }
+
+
+ ///
+ /// Initialize the system
+ ///
+ ///
+ /// True if OK, or False if error
+ public bool Initialize(string fileName)
+ {
+ Dispatcher.UIThread.InvokeAsync(() =>
+ {
+ _Initialize(fileName);
+ });
+ return true;
+ }
+
+
+ private void _Initialize(string fileName)
+ {
+ _Modified = true;
+
+ ServiceLayer.Initialize();
+
+ FileName = fileName;
+
+ TilePatternsList = new List();
+
+ TileMain = ServiceLayer_Maps.Tiles_LoadMapsTiles(fileName);
+ if (TileMain == null)
+ {
+ TileMain = new ZXMapsTiles()
+ {
+ GraphicMode = GraphicsModes.Monochrome,
+ Height = 16,
+ Width = 16,
+ Name = Path.GetFileName(fileName).ToStringNoNull().Replace(".zxtil", ""),
+ Tiles = new List()
+ };
+ }
+
+ wpTileList.ItemWidth = (TileMain.Width * 4) + 4;
+ wpTileList.ItemHeight = (TileMain.Height * 4) + 4;
+
+ foreach (var Tile in TileMain.Tiles)
+ {
+ // Create pattern list
+ var tpc = new TilePatternControl();
+ tpc.Initialize(Tile, TileList_Command, TileMain.Width, TileMain.Height);
+ TilePatternsList.Add(tpc);
+ wpTileList.Children.Add(tpc);
+ wpTileList.InvalidateMeasure();
+ }
+
+ if (TilePatternsList.Count == 0)
+ {
+ TileList_AddTile(null);
+ ctrlEditor.Initialize(Editor_Command);
+ ctrlPreview.Initialize(null);
+ ctrlTileProperties.Initialize(TileMain, TileProperties_Command);
+ }
+ else
+ {
+ ctrlEditor.Initialize(Editor_Command);
+ ctrlPreview.Initialize(TilePatternsList[0].TileData);
+ ctrlTileProperties.Initialize(TileMain, TileProperties_Command);
+ }
+
+ ctrlMapFileSelector.Initialize(TileMain.DefaultMap, MapFileSelector_Command);
+ ctrlMapProperties.Initialize(ctrlMapFileSelector.Map, TileMain, MapProperties_Command);
+ ctrlMapEditor.Initialize(ctrlMapFileSelector.Map, TileMain, MapEditor_Command);
+
+ btnTileMoveLeft.Tapped += BtnTileMoveLeft_Tapped;
+ btnTileMoveRight.Tapped += BtnTileMoveRight_Tapped;
+ btnTileInfoHide.Tapped += BtnTileInfoHide_Tapped;
+ btnTileInfoShow.Tapped += BtnTileInfoShow_Tapped;
+ btnTileDelete.Tapped += BtnTileDelete_Tapped;
+
+ Tiles_Renum();
+ Refresh();
+
+ if (TilePatternsList.Count > 1)
+ {
+ TileList_Command(TilePatternsList.ElementAt(0), "SELECTED");
+ }
+
+ InitializeShortcuts();
+
+ this.AddHandler(KeyDownEvent, Keyboard_Down, handledEventsToo: true);
+ this.Focus();
+ grdProcesando.IsVisible = false;
+ _Modified = false;
+
+ // Calcular altura inicial del WrapPanel
+ CalculateWrapPanelHeight();
+
+ // Suscribirse a cambios de tamaño para recalcular la altura
+ wpTileList.SizeChanged += (s, e) => CalculateWrapPanelHeight();
+
+ tileWidth = TileMain.Width;
+ tileHeight = TileMain.Height;
+ }
+
+ private void BtnTileDelete_Tapped(object? sender, TappedEventArgs e)
+ {
+ var ctrl = TilePatternsList.FirstOrDefault(d => d.TileData.Id == selectedTileId);
+ if (ctrl != null)
+ {
+ TileList_Delete(ctrl);
+ }
+ }
+
+ public void Refresh()
+ {
+ ctrlEditor.Refresh();
+ }
+
+
+ #region TileList
+
+ private void TileList_Command(TilePatternControl sender, string command)
+ {
+ switch (command)
+ {
+ case "ADD":
+ if (sender.TileData != null)
+ {
+ sender.TileData.Id = TilePatternsList.Count - 1;
+ }
+ TileList_AddTile(null);
+ ctrlEditor.TileData = sender.TileData;
+ ctrlPreview.TileData = sender.TileData;
+ ctrlPreview.Refresh();
+ TileList_Modified(sender.TileData);
+ break;
+ case "SELECTED":
+ TileList_Unselect(sender);
+ ctrlEditor.TileData = sender.TileData;
+ ctrlPreview.TileData = sender.TileData;
+ ctrlEditor.Frame = 0;
+ ctrlPreview.Refresh();
+ TileProperties_FrameUpdate(ctrlTileProperties, command);
+ if (sender.TileData != null)
+ {
+ ctrlMapEditor.CurrentTile = sender.TileData.Id;
+ }
+ selectedTileId = sender.TileData?.Id ?? 0;
+ break;
+ }
+ }
+
+
+ private void BtnTileMoveRight_Tapped(object? sender, TappedEventArgs e)
+ {
+ if (selectedTileId == (TileMain.Tiles.Count - 1))
+ {
+ return;
+ }
+
+ var temp = TileMain.Tiles[selectedTileId];
+ TileMain.Tiles[selectedTileId] = TileMain.Tiles[selectedTileId + 1];
+ TileMain.Tiles[selectedTileId + 1] = temp;
+ selectedTileId++;
+ Tiles_Renum();
+ Tiles_Select(selectedTileId);
+ }
+
+
+ private void BtnTileMoveLeft_Tapped(object? sender, TappedEventArgs e)
+ {
+ if (selectedTileId < 1)
+ {
+ return;
+ }
+
+ var temp = TileMain.Tiles[selectedTileId];
+ TileMain.Tiles[selectedTileId] = TileMain.Tiles[selectedTileId - 1];
+ TileMain.Tiles[selectedTileId - 1] = temp;
+ selectedTileId--;
+ Tiles_Renum();
+ Tiles_Select(selectedTileId);
+ }
+
+
+ private void Tiles_Select(int id)
+ {
+ var tpc = TilePatternsList.FirstOrDefault(d =>
+ d != null &&
+ d.TileData != null &&
+ d.TileData.Id == id);
+ if (tpc != null)
+ {
+ tpc.Select();
+ }
+ }
+
+
+ private void Tiles_Renum()
+ {
+ for (int n = 0; n < TilePatternsList.Count; n++)
+ {
+ if (TilePatternsList[n].TileData == null || TileMain.Tiles[n] == null)
+ {
+ TilePatternsList[n].TileData = null;
+ TileMain.Tiles[n] = null;
+ continue;
+ }
+
+ TileMain.Tiles[n].Id = n;
+ var spc = TilePatternsList[n];
+ if (spc.TileData != null)
+ {
+ spc.TileData = TileMain.Tiles[n];
+ spc.TileData.Id = n;
+ spc.IsSelected = false;
+ if (n == selectedTileId)
+ {
+ ctrlEditor.TileData = TileMain.Tiles[n];
+ ctrlEditor.Refresh();
+ ctrlPreview.TileData = TileMain.Tiles[n];
+ ctrlPreview.Refresh();
+ }
+ }
+ }
+ ctrlMapEditor.Refresh(true);
+ }
+
+
+ private void BtnTileInfoHide_Tapped(object? sender, TappedEventArgs e)
+ {
+ btnTileInfoShow.IsVisible = true;
+ btnTileInfoHide.IsVisible = false;
+ foreach (var ctrl in TilePatternsList)
+ {
+ ctrl.InfoVisible = false;
+ }
+ }
+
+
+ private void BtnTileInfoShow_Tapped(object? sender, TappedEventArgs e)
+ {
+ btnTileInfoShow.IsVisible = false;
+ btnTileInfoHide.IsVisible = true;
+ foreach (var ctrl in TilePatternsList)
+ {
+ ctrl.InfoVisible = true;
+ }
+ }
+
+
+ private void TileList_AddTile(ZXMapsTile TileData)
+ {
+ if (TileData!=null && TileData.Palette == null)
+ {
+ TileData.Palette = ServiceLayer.GetPalette(TileData.GraphicMode);
+ }
+ TileMain.Tiles.Add(TileData);
+
+ TilePatternControl selectedTile = null;
+ int id = 0;
+ if (TilePatternsList.Count > 0)
+ {
+ var sds = TilePatternsList.Where(d => d.TileData != null);
+ if (sds.Any())
+ {
+ id = sds.Max(d => d.TileData.Id) + 1;
+ }
+ }
+ for (int n = 0; n < TilePatternsList.Count; n++)
+ {
+ var spc = TilePatternsList[n];
+ if (spc.TileData != null && spc.TileData.Id < 0)
+ {
+ spc.TileData.Id = id;
+ if (string.IsNullOrEmpty(spc.TileData.Name))
+ {
+ spc.TileData.Name = "Tile " + spc.TileData.Id.ToString();
+ }
+ spc.TileData.Export = true;
+ selectedTile = spc;
+ break;
+ }
+ if (spc.TileData == null && TileData != null)
+ {
+ TilePatternsList[n].TileData = TileData;
+ selectedTile = TilePatternsList[n];
+ break;
+ }
+ }
+
+ // Add void Tile
+ var TilePattern = new TilePatternControl();
+ TilePatternsList.Add(TilePattern);
+ wpTileList.Children.Add(TilePattern);
+ TilePattern.Initialize(null, TileList_Command, TileMain.Width, TileMain.Height);
+ wpTileList.InvalidateMeasure();
+ CalculateWrapPanelHeight();
+ TileList_Unselect(selectedTile);
+ }
+
+
+ private void TileList_Unselect(TilePatternControl selected)
+ {
+ foreach (var control in TilePatternsList)
+ {
+ if (control == selected)
+ {
+ control.IsSelected = true;
+ }
+ else
+ {
+ control.IsSelected = false;
+ }
+ }
+ }
+
+
+ private void TileList_Delete(TilePatternControl control)
+ {
+ try
+ {
+ var t = TileMain.Tiles.FirstOrDefault(d => d.Id == control.TileData.Id);
+ if (t != null)
+ {
+ TileMain.Tiles.Remove(t);
+ }
+ TilePatternsList.Remove(control);
+ wpTileList.Children.Remove(control);
+ ctrlEditor.TileData = null;
+ ctrlPreview.TileData = null;
+ ctrlTileProperties.TileData = null;
+ control = null;
+ CalculateWrapPanelHeight();
+ Tiles_Renum();
+ }
+ catch { }
+ }
+
+
+ private void TileList_Insert(ZXMapsTile TileData)
+ {
+ var current = TileData.CurrentFrame;
+ var curPat = TileData.Patterns[current];
+
+ var pat = curPat.Clonar();
+ pat.RawData = new int[pat.RawData.Length];
+
+ var pats = TileData.Patterns.Take(current).ToList();
+ pats.Add(pat);
+ pats.AddRange(TileData.Patterns.Skip(current));
+ TileData.Patterns = pats;
+ TileData.Frames++;
+ ctrlTileProperties.Refresh();
+ ctrlEditor.Refresh();
+ }
+
+
+ private void TileList_Modified(ZXMapsTile TileData)
+ {
+ if (!_Modified)
+ {
+ _Modified = true;
+ DocumentModified?.Invoke(this, EventArgs.Empty);
+ }
+
+ if (TileData == null)
+ {
+ return;
+ }
+
+ var ctrl = TilePatternsList.FirstOrDefault(d => d.TileData != null && d.TileData.Id == TileData.Id);
+ if (ctrl != null)
+ {
+ ctrl.TileData = TileData;
+ ctrl.Refresh();
+ }
+
+ var td = TileMain.Tiles.FirstOrDefault(d => d != null && d.Id == TileData.Id);
+ if (td != null)
+ {
+ td = TileData;
+ }
+ else
+ {
+ for (int n = 0; n < TileMain.Tiles.Count; n++)
+ {
+ if (TileMain.Tiles[n] == null)
+ {
+ TileMain.Tiles[n] = TileData;
+ break;
+ }
+ }
+ }
+
+ wpTileList.ItemWidth = (TileMain.Width * 4) + 4;
+ wpTileList.ItemHeight = (TileMain.Height * 4) + 4;
+
+ // Calcular altura del WrapPanel basada en el contenido
+ CalculateWrapPanelHeight();
+ wpTileList.InvalidateMeasure();
+
+ ctrlMapEditor.Refresh(false);
+ }
+
+ private void CalculateWrapPanelHeight()
+ {
+ if (wpTileList.ItemWidth <= 0 || wpTileList.ItemHeight <= 0 || wpTileList.Children.Count == 0)
+ {
+ return;
+ }
+
+ // Obtener el ancho disponible del ScrollViewer padre
+ double availableWidth = wpTileList.Bounds.Width;
+
+ // Si el ancho no está disponible aún, intentar obtenerlo del control padre
+ if (availableWidth <= 0)
+ {
+ var parent = wpTileList.Parent as Control;
+ if (parent != null)
+ {
+ availableWidth = parent.Bounds.Width;
+ }
+ }
+
+ // Si aún no está disponible, usar una estimación basada en el número de elementos
+ if (availableWidth <= 0)
+ {
+ // Estimación: asumir que el ScrollViewer tiene aproximadamente 160-180 px de ancho
+ // Esto es relativo al contenedor padre
+ availableWidth = 160;
+ }
+
+ // Asegurar que hay espacio suficiente
+ if (availableWidth < wpTileList.ItemWidth)
+ {
+ availableWidth = wpTileList.ItemWidth;
+ }
+
+ // Calcular el número de columnas que caben
+ int itemsPerRow = Math.Max(1, (int)(availableWidth / wpTileList.ItemWidth));
+
+ // Calcular el número de filas necesarias
+ int totalRows = Math.Max(1, (int)Math.Ceiling((double)wpTileList.Children.Count / itemsPerRow));
+
+ // Calcular la altura total (número de filas * altura de cada item)
+ double calculatedHeight = totalRows * wpTileList.ItemHeight;
+
+ wpTileList.Height = calculatedHeight;
+ }
+
+ #endregion
+
+
+ #region Tile preview
+
+ private void TilePreview_Command(TilePreviewControl sender, string command)
+ {
+
+ }
+
+
+ private void TileProperties_FrameUpdate(TilePropertiesControl sender, string command)
+ {
+ if (sender.TileData == null)
+ {
+ return;
+ }
+
+ // TODO: Multiple frames per tile?
+ /*
+ int f = sender.TileData.Frames - 1;
+ if (f < 0)
+ {
+ f = 0;
+ }
+ else if (f > 255)
+ {
+ f = 255;
+ }
+ txtFrame.Maximum = f;
+ txtFrame.Text = f.ToString();
+ txtFrame.UpdateLayout();
+ */
+ TileProperties_Command(sender, "REFRESH");
+ }
+ #endregion
+
+
+ #region Tile properties
+
+ private void TileProperties_Command(TilePropertiesControl sender, string command)
+ {
+ try
+ {
+ var selectedTile = TilePatternsList.FirstOrDefault(d => d.IsSelected)?.TileData;
+
+ switch (command)
+ {
+ case "MODE":
+ {
+ switch (sender.TileData.GraphicMode)
+ {
+ case GraphicsModes.Monochrome:
+ ctrlEditor.PrimaryColorIndex = 1;
+ ctrlEditor.SecondaryColorIndex = 0;
+ break;
+ case GraphicsModes.ZXSpectrum:
+ ctrlEditor.PrimaryColorIndex = 0;
+ ctrlEditor.SecondaryColorIndex = 7;
+ break;
+ }
+ ctrlEditor.TileData = selectedTile;
+ TileList_Modified(selectedTile);
+ ctrlEditor.UpdateColorPanel();
+ }
+ break;
+ case "NAME":
+ break;
+ case "SIZE":
+ ResizePatterns();
+ break;
+ }
+ }
+ catch { }
+ }
+
+
+ private void ResizePatterns()
+ {
+ for (int n = 0; n < TilePatternsList.Count; n++)
+ {
+ var pattern = TilePatternsList[n];
+ if (pattern != null && pattern.TileData != null)
+ {
+ if (pattern.TileData.Patterns != null && pattern.TileData.Patterns.Count > 0)
+ {
+ pattern.TileData.Patterns[0] = ResizePattern(pattern.TileData.Patterns[0]);
+ }
+ pattern.TileData.Width = TileMain.Width;
+ pattern.TileData.Height = TileMain.Height;
+ pattern.Refresh();
+ }
+ }
+ tileWidth = TileMain.Width;
+ tileHeight = TileMain.Height;
+
+ ctrlPreview.TileData.Width = TileMain.Width;
+ ctrlPreview.TileData.Height = TileMain.Height;
+ ctrlPreview.Refresh();
+ ctrlEditor.TileData.Width = TileMain.Width;
+ ctrlEditor.TileData.Height = TileMain.Height;
+ ctrlEditor.Refresh();
+ }
+
+
+ private Pattern ResizePattern(Pattern pattern)
+ {
+ var pat = new Pattern()
+ {
+ Id = pattern.Id,
+ Name = pattern.Name,
+ Number = pattern.Number,
+ RawData = new int[TileMain.Width * TileMain.Height],
+ Attributes = new AttributeColor[(TileMain.Width / 8) * (TileMain.Height / 8)]
+ };
+ for (int n = 0; n < pat.Attributes.Length; n++)
+ {
+ pat.Attributes[n] = new AttributeColor()
+ {
+ Paper = ctrlEditor.SecondaryColorIndex,
+ Ink = ctrlEditor.PrimaryColorIndex
+ };
+ }
+
+ for (int y = 0; y < TileMain.Height; y++)
+ {
+ for (int x = 0; x < TileMain.Width; x++)
+ {
+ int colorIndex = 0;
+ if (x < tileWidth && y < tileHeight)
+ {
+ colorIndex = pattern.RawData[(y * tileWidth) + x];
+ }
+
+ pat.RawData[(y * TileMain.Width) + x] = colorIndex;
+ }
+ }
+ return pat;
+ }
+
+ #endregion
+
+
+
+ private void TileList_Clone(ZXMapsTile TileData)
+ {
+ TilePatternControl selectedTile = null;
+ int id = TilePatternsList.Where(d => d.TileData != null).Max(d => d.TileData.Id) + 1;
+ var spc = TilePatternsList.FirstOrDefault(d => d.TileData == null);
+ if (spc == null)
+ {
+ return;
+ }
+ var sd = TileData.Clonar();
+ sd.Id = id;
+ sd.Name = TileData.Name + " - copy";
+ spc.TileData = sd;
+
+ var TilePattern = new TilePatternControl();
+ TilePatternsList.Add(TilePattern);
+ wpTileList.Children.Add(TilePattern);
+ TilePattern.Initialize(null, TileList_Command, TileMain.Width, TileMain.Height);
+ wpTileList.InvalidateMeasure();
+ CalculateWrapPanelHeight();
+
+ spc.Select();
+ }
+
+
+
+
+
+ #region Tile editor
+
+ private void Editor_Command(TilePatternEditor sender, string command)
+ {
+ try
+ {
+ switch (command)
+ {
+ case "ADD":
+ var tile = sender.TileData;
+ if (tile == null)
+ {
+ return;
+ }
+ TileList_AddTile(tile);
+ TileList_Modified(sender.TileData);
+ ctrlPreview.TileData = tile;
+ ctrlPreview.Refresh();
+ ctrlTileProperties.TileData = TileMain;
+ break;
+
+ case "REFRESH":
+ {
+ var cur = TilePatternsList.FirstOrDefault(d => d.IsSelected);
+ if (cur != null)
+ {
+ cur.TileData = sender.TileData;
+ cur.Refresh();
+ TileList_Modified(sender.TileData);
+ ctrlTileProperties.TileData = TileMain;
+ ctrlTileProperties.PrimaryColor = sender.PrimaryColorIndex;
+ ctrlTileProperties.SecondaryColor = sender.SecondaryColorIndex;
+ ctrlPreview.TileData = sender.TileData;
+
+ //btnColorPicker.IsChecked = !ctrlEditor.ColorPicker;
+ ctrlEditor.UpdateColorPanel();
+ }
+ }
+ break;
+
+ case "IMPORT":
+ Import();
+ break;
+
+ case "EXPORT":
+ Tiles_Export();
+ break;
+ }
+ }
+ catch { }
+ }
+
+
+ private void Tiles_Export()
+ {
+ var dlg = new TileExportDialog();
+ dlg.Initialize(FileName, TilePatternsList.Select(d => d.TileData));
+ dlg.ShowDialog(this.VisualRoot as Window);
+ }
+
+ #endregion
+
+
+ #region Keyboard shortcus
+
+ private Dictionary _keybCommands = new Dictionary();
+
+ internal static Dictionary keyboardCommands = new Dictionary {
+ { "Save", new ZXKeybCommand{ CommandId = Guid.Parse("87f7d73b-d28a-44f4-ba0c-41baa4de238c"), CommandName = "Save", Key = Key.S, Modifiers = KeyModifiers.Control } },
+ { "Copy",new ZXKeybCommand{ CommandId = Guid.Parse("fee014bb-222b-42e3-80f3-048325b70e34"), CommandName = "Copy", Key = Key.C, Modifiers = KeyModifiers.Control } },
+ { "Cut",new ZXKeybCommand{ CommandId = Guid.Parse("1edf352f-238b-421e-b69f-613dc63c0e47"), CommandName = "Cut", Key = Key.X, Modifiers = KeyModifiers.Control } },
+ { "Paste", new ZXKeybCommand{ CommandId = Guid.Parse("f5d450b0-d126-4f62-885b-b3e28e638542"), CommandName = "Paste", Key = Key.V, Modifiers = KeyModifiers.Control } },
+ { "Undo", new ZXKeybCommand{ CommandId = Guid.Parse("912c1887-ab37-4c0a-9aee-65d84b4521c7"), CommandName = "Undo", Key = Key.Z, Modifiers = KeyModifiers.Control } },
+ { "Redo", new ZXKeybCommand{ CommandId = Guid.Parse("c5c506f0-d5e4-429f-ad21-af8cee7d1d9a"), CommandName = "Redo", Key = Key.Z, Modifiers = KeyModifiers.Control | KeyModifiers.Shift } },
+
+ { "Clear", new ZXKeybCommand{ CommandId = Guid.Parse("246e1449-0a64-4327-a8d1-f1dbecbc69d2"), CommandName = "Clear", Key = Key.Q, Modifiers = KeyModifiers.Control | KeyModifiers.Shift } },
+ { "Rotate Right", new ZXKeybCommand{ CommandId = new Guid("490e8830-f268-48f3-8989-92d6e22b8790"), CommandName = "Rotate Right", Key = Key.R, Modifiers = KeyModifiers.None } },
+ { "Rotate Left", new ZXKeybCommand{ CommandId = new Guid("1da1bae1-2242-4061-9fa2-9265fc7f74b1"), CommandName = "Rotate Left", Key = Key.R, Modifiers = KeyModifiers.Shift } },
+ { "Horizontal Mirror", new ZXKeybCommand{ CommandId = new Guid("aad6fb75-15c7-4f09-aa1c-af1e1b62f849"), CommandName = "Horizontal Mirror", Key = Key.H, Modifiers = KeyModifiers.None } },
+ { "Vertical Mirror", new ZXKeybCommand{ CommandId = new Guid("3e20f906-c2e5-46b1-aa6d-8ee503769917"), CommandName = "Vertical Mirror", Key = Key.V, Modifiers = KeyModifiers.None } },
+ { "Shift Up", new ZXKeybCommand{ CommandId = new Guid("7a2f0381-b603-4c5f-aefd-08ebafc74e5c"), CommandName = "Shift Up", Key = Key.Up, Modifiers = KeyModifiers.None } },
+ { "Shift Right", new ZXKeybCommand{ CommandId = new Guid("75a310dd-66ec-41a5-bc8d-1b3408fd4f41"), CommandName = "Shift Right", Key = Key.Right, Modifiers = KeyModifiers.None } },
+ { "Shift Down", new ZXKeybCommand{ CommandId = new Guid("2a73e9cd-f318-4a15-bdc7-daf35429c6e5"), CommandName = "Shift Down", Key = Key.Down, Modifiers = KeyModifiers.None } },
+ { "Shift Left", new ZXKeybCommand{ CommandId = new Guid("1c8f1ec5-596c-4e7f-9fb6-9d5ff527b37a"), CommandName = "Shift Left", Key = Key.Left, Modifiers = KeyModifiers.None } },
+ { "Move Up", new ZXKeybCommand{ CommandId = new Guid("6c202c3d-921f-43f7-b810-33febc8ad947"), CommandName = "Move Up", Key = Key.Up, Modifiers = KeyModifiers.Shift } },
+ { "Move Right", new ZXKeybCommand{ CommandId = new Guid("8e2e96d0-0aa4-426e-a340-32cb53368000"), CommandName = "Move Right", Key = Key.Right, Modifiers = KeyModifiers.Shift } },
+ { "Move Down", new ZXKeybCommand{ CommandId = new Guid("bdd1c206-e3ec-4a7d-8e61-42b620097b31"), CommandName = "Move Down", Key = Key.Down, Modifiers = KeyModifiers.Shift } },
+ { "Move Left", new ZXKeybCommand{ CommandId = new Guid("6a6b98cf-5427-4d85-a230-6ab18544e312"), CommandName = "Move Left", Key = Key.Left, Modifiers = KeyModifiers.Shift } },
+ { "Invert", new ZXKeybCommand{ CommandId = new Guid("e88125e8-f671-4085-ae8c-8d1f4866738a"), CommandName = "Invert", Key = Key.I, Modifiers = KeyModifiers.None } },
+ { "Mask", new ZXKeybCommand{ CommandId = new Guid("efd5a1f4-cab8-4003-8370-8adf81176081"), CommandName = "Mask", Key = Key.M, Modifiers = KeyModifiers.None } },
+ { "Export", new ZXKeybCommand{ CommandId = new Guid("321119c0-5c9b-40b5-9385-a62ca013f83c"), CommandName = "Export", Key = Key.E, Modifiers = KeyModifiers.None } },
+ { "Zoom In", new ZXKeybCommand{ CommandId = new Guid("7e958c3d-c135-46b2-b041-63b8c7828787"), CommandName = "Zoom In", Key = Key.Add, Modifiers = KeyModifiers.None } },
+ { "Zoom Out", new ZXKeybCommand{ CommandId = new Guid("d69843bb-8ad0-4eea-be58-29646dddaeed"), CommandName = "Zoom Out", Key = Key.Subtract, Modifiers = KeyModifiers.None } },
+ };
+
+
+ private void InitializeShortcuts()
+ {
+ DisableCommand(ApplicationCommands.Cut);
+ DisableCommand(ApplicationCommands.Copy);
+ DisableCommand(ApplicationCommands.Paste);
+
+ _keybCommands = new Dictionary()
+ {
+ { keyboardCommands["Save"].CommandId, () => { RequestSaveDocument?.Invoke(this, EventArgs.Empty); } },
+ { keyboardCommands["Copy"].CommandId, () => { ctrlEditor.Copy(); } },
+ { keyboardCommands["Cut"].CommandId, () => { ctrlEditor.Cut(); } },
+ { keyboardCommands["Paste"].CommandId, () => { ctrlEditor.Paste(); } },
+ { keyboardCommands["Undo"].CommandId, () => { ctrlEditor.Undo(); } },
+ { keyboardCommands["Redo"].CommandId, () => { ctrlEditor.Redo(); } },
+
+ { keyboardCommands["Clear"].CommandId, () => { ctrlEditor.Clear(); } },
+ { keyboardCommands["Rotate Right"].CommandId, () => { ctrlEditor.RotateRight(); } },
+ { keyboardCommands["Rotate Left"].CommandId, () => { ctrlEditor.RotateLeft(); } },
+ { keyboardCommands["Horizontal Mirror"].CommandId, () => { ctrlEditor.HorizontalMirror(); } },
+ { keyboardCommands["Vertical Mirror"].CommandId, () => { ctrlEditor.VerticalMirror(); } },
+ { keyboardCommands["Shift Up"].CommandId, () => { ctrlEditor.ShiftUp(); } },
+ { keyboardCommands["Shift Right"].CommandId, () => { ctrlEditor.ShiftRight(); } },
+ { keyboardCommands["Shift Down"].CommandId, () => { ctrlEditor.ShiftDown(); } },
+ { keyboardCommands["Shift Left"].CommandId, () => { ctrlEditor.ShiftLeft(); } },
+ { keyboardCommands["Move Up"].CommandId, () => { ctrlEditor.MoveUp(); } },
+ { keyboardCommands["Move Right"].CommandId, () => { ctrlEditor.MoveRight(); } },
+ { keyboardCommands["Move Down"].CommandId, () => { ctrlEditor.MoveDown(); } },
+ { keyboardCommands["Move Left"].CommandId, () => { ctrlEditor.MoveLeft(); } },
+ { keyboardCommands["Invert"].CommandId, () => { ctrlEditor.Invert(); } },
+ { keyboardCommands["Mask"].CommandId, () => { ctrlEditor.Mask(); } },
+ { keyboardCommands["Export"].CommandId, () => { ctrlEditor.Export(); } },
+ { keyboardCommands["Zoom In"].CommandId, () => { ctrlEditor.ZoomIn(); } },
+ { keyboardCommands["Zoom Out"].CommandId, () => { ctrlEditor.ZoomOut(); } },
+ };
+ }
+
+
+ private void DisableCommand(RoutedCommand cut)
+ {
+ var field = typeof(RoutedCommand).GetField("k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic);
+ KeyGesture g = new KeyGesture(Key.None, KeyModifiers.None);
+ field.SetValue(cut, g);
+ }
+
+ private void Keyboard_Down(object? sender, Avalonia.Input.KeyEventArgs e)
+ {
+ var commandId = ZXKeybMapper.GetCommandId(documentTypeId, e.Key, e.KeyModifiers);
+
+ if (commandId != null && _keybCommands.ContainsKey(commandId.Value))
+ {
+ _keybCommands[commandId.Value]();
+ }
+ }
+
+ private void tabControlEditor_SelectionChanged(object? sender, SelectionChangedEventArgs e)
+ {
+ if (tabControlEditor == null)
+ {
+ return;
+ }
+
+ if (tabControlEditor.SelectedIndex == 1)
+ {
+ ctrlMapEditor.Refresh(false);
+ }
+ }
+
+ #endregion
+
+
+ #region Import tiles
+
+ private void Import()
+ {
+ var dlg = new TileImportDialog();
+ dlg.Initialize(TilePatternsList.Select(d => d.TileData), Import_Command);
+ dlg.ShowDialog(this.VisualRoot as Window);
+ }
+
+
+ private void Import_Command(ZXMapsTile Tile, string command)
+ {
+ try
+ {
+ switch (command)
+ {
+ case "ADD":
+ Tile.Id = TilePatternsList.Count - 1;
+ TileList_AddTile(Tile);
+ ctrlEditor.TileData = Tile;
+ ctrlPreview.TileData = Tile;
+ ctrlPreview.Refresh();
+ ctrlMapEditor.Refresh(false);
+ break;
+ }
+ }
+ catch { }
+ }
+
+ #endregion
+
+
+ #region MapFileSelector
+
+ private void MapFileSelector_Command(MapFileSelectorControl sender, string command)
+ {
+ Map = sender.Map;
+ var old = TileMain.DefaultMap;
+ if (Map == null)
+ {
+ TileMain.DefaultMap = null;
+ }
+ else
+ {
+ TileMain.DefaultMap = Map.Name;
+ }
+ ctrlMapProperties.Map = sender.Map;
+
+ if (old != TileMain.DefaultMap)
+ {
+ _Modified = true;
+ DocumentModified?.Invoke(this, EventArgs.Empty);
+ ctrlMapEditor.Map = Map;
+ }
+ }
+
+ #endregion
+
+
+ #region MapProperties
+
+ private void MapProperties_Command(MapPropertiesControl control, string arg2)
+ {
+
+ }
+
+ #endregion
+
+
+ #region MapEditor
+
+ private void MapEditor_Command(MapPatternEditor editor, string comando)
+ {
+ switch (comando)
+ {
+ case "REFRESH":
+ {
+ foreach(var tc in TilePatternsList)
+ {
+ tc.Refresh();
+ }
+ }
+ break;
+ case "EXPORT":
+ Map_Export();
+ break;
+ }
+ }
+
+
+ private void Map_Export()
+ {
+ var dlg = new MapExportDialog();
+ dlg.Initialize(FileName, Map);
+ dlg.ShowDialog(this.VisualRoot as Window);
+ }
+
+ #endregion
+ }
+}
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TileExportDialog.axaml b/ZXBStudio/DocumentEditors/ZXMaps/TileExportDialog.axaml
new file mode 100644
index 0000000..4d314ef
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/TileExportDialog.axaml
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+ Options
+
+
+
+
+
+
+ Enable build:
+
+
+ Data type:
+
+ DIM (data as array)
+ ASM (DEFB inside ASM Block)
+ .BIN file (INCBIN "file.bin")
+ .TAP file (LOAD "" CODE)
+
+
+ File path:
+
+
+
+ Var prefix:
+
+
+ Array base:
+
+ 0 (default)
+ 1
+ From project settings
+
+
+ Export attributes
+
+
+
+
+ You have not selected any sprite to export. Check the "Include in the export" box of the sprites you want to include in the export.
+
+
+ Example of usage
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TileExportDialog.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/TileExportDialog.axaml.cs
new file mode 100644
index 0000000..72094db
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/TileExportDialog.axaml.cs
@@ -0,0 +1,386 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Platform.Storage;
+using MsBox.Avalonia;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Linq;
+using System.Text;
+using ZXBasicStudio.Classes;
+using ZXBasicStudio.Common;
+using ZXBasicStudio.DocumentEditors.ZXGraphics.log;
+using ZXBasicStudio.DocumentEditors.ZXGraphics.neg;
+using ZXBasicStudio.DocumentModel.Classes;
+using ZXBasicStudio.DocumentModel.Interfaces;
+using ZXBasicStudio.Extensions;
+using ZXBasicStudio.IntegratedDocumentTypes.CodeDocuments.Basic;
+using static System.Net.Mime.MediaTypeNames;
+
+namespace ZXBasicStudio.DocumentEditors.ZXMaps
+{
+ public partial class TileExportDialog : Window
+ {
+ private string fileName = "";
+ private IEnumerable tiles = null;
+
+
+ private FileTypeConfig fileType = null;
+ private Pattern[] patterns = null;
+ private ExportConfig exportConfig = null;
+
+
+ public TileExportDialog()
+ {
+ InitializeComponent();
+
+ btnCancel.Tapped += BtnCancel_Tapped;
+ btnCopy.Tapped += BtnCopy_Tapped;
+ btnExport.Tapped += BtnExport_Tapped;
+ btnOutputFile.Tapped += BtnOutputFile_Tapped;
+ cmbDataType.SelectionChanged += CmbDataType_SelectionChanged;
+ cmbArrayBase.SelectionChanged += CmbArrayBase_SelectionChanged;
+ chkAttr.Click += ChkAttr_Click;
+ btnSave.Tapped += BtnSave_Tapped;
+ }
+
+
+ public bool Initialize(string fileName, IEnumerable tilesData)
+ {
+ this.fileName = fileName;
+ this.tiles = tilesData;
+
+ exportConfig = ServiceLayer.Export_GetConfigFile(fileName + ".zbs");
+ if (exportConfig == null)
+ {
+ exportConfig = ServiceLayer.Export_Tile_GetDefaultConfig(fileName);
+ }
+
+ this.cmbSelectExportType.Initialize(ExportType_Changed);
+ cmbSelectExportType.InitializeTile();
+
+ txtOutputFile.Text = exportConfig.ExportFilePath;
+ txtLabelName.Text = exportConfig.LabelName;
+ chkAuto.IsChecked = exportConfig.AutoExport;
+ cmbDataType.SelectedIndex = (int)exportConfig.ExportDataType;
+ cmbArrayBase.SelectedIndex = exportConfig.ArrayBase.ToInteger();
+ chkAttr.IsChecked = exportConfig.IncludeAttr;
+
+ cmbSelectExportType.ExportType = exportConfig.ExportType;
+
+ return true;
+ }
+
+
+ #region ExportOptions
+
+ private void ExportType_Changed(ExportTypes exportType)
+ {
+ exportConfig.ExportType = exportType;
+ Refresh();
+ }
+
+
+ private void Refresh()
+ {
+ grdOptions.IsVisible = true;
+
+ chkAuto.IsVisible = true;
+
+ lblOutputFile.IsVisible = true;
+ txtOutputFile.IsVisible = true;
+ btnOutputFile.IsVisible = true;
+
+ lblLabelName.IsVisible = true;
+ txtLabelName.IsVisible = true;
+
+ lblArrayBase.IsVisible = true;
+ cmbDataType.IsVisible = true;
+ cmbArrayBase.IsVisible = true;
+
+ bool canExport = false;
+ if (tiles == null || tiles.Where(d => d != null && d.Export).Count() == 0)
+ {
+ txtError.IsVisible = true;
+ }
+ else
+ {
+ canExport = true;
+ txtError.IsVisible = false;
+ }
+
+ switch (exportConfig.ExportType)
+ {
+ case ExportTypes.PutChars:
+ CreateExportPath(".bas");
+ if (canExport)
+ {
+ CreateExample_PutChars();
+ }
+ break;
+ default:
+ grdOptions.IsVisible = false;
+ break;
+ }
+ }
+
+
+ private void CreateExportPath(string extension)
+ {
+ if (string.IsNullOrEmpty(exportConfig.ExportFilePath))
+ {
+ txtOutputFile.Text = fileType.FileName + extension;
+ var spName = Path.GetFileNameWithoutExtension(fileType.FileName.ToStringNoNull());
+ txtLabelName.Text = spName;
+ if (spName.Length > 10)
+ {
+ spName = spName.Substring(0, 10);
+ }
+ }
+ }
+
+
+ #endregion
+
+
+ #region Examples
+
+ private void GetConfigFromUI()
+ {
+ if (exportConfig == null)
+ {
+ exportConfig = new ExportConfig();
+ }
+ exportConfig.ArrayBase = cmbArrayBase.SelectedIndex.ToInteger();
+ exportConfig.AutoExport = chkAuto.IsChecked == true;
+ exportConfig.ExportDataType = (ExportDataTypes)cmbDataType.SelectedIndex.ToInteger();
+ exportConfig.ExportFilePath = txtOutputFile.Text.ToStringNoNull();
+ exportConfig.ExportType = cmbSelectExportType.ExportType;
+ exportConfig.LabelName = txtLabelName.Text.ToStringNoNull();
+ exportConfig.ZXAddress = 0;
+ exportConfig.ZXFileName = "";
+ exportConfig.IncludeAttr = chkAttr.IsChecked == true;
+ }
+
+ private void CreateExample_PutChars()
+ {
+ if (tiles == null || tiles.Count() == 0)
+ {
+ txtCode.Text = "";
+ }
+
+ var tile = tiles.ElementAt(0);
+ var res = ExportManager.Export_Tiles_PutChars(exportConfig, tiles);
+ if (res.StartsWith("ERROR:"))
+ {
+ txtError.Text = res;
+ txtError.IsVisible = true;
+ return;
+ }
+ else
+ {
+ txtError.IsVisible = false;
+ }
+
+ var sb = new StringBuilder();
+ switch (exportConfig.ExportDataType)
+ {
+ case ExportDataTypes.DIM:
+ {
+ sb.AppendLine("'- Includes -----------------------------------------------");
+ sb.AppendLine("#INCLUDE ");
+ sb.AppendLine("");
+ sb.AppendLine(string.Format("' Can use: #INCLUDE \"{0}\"",
+ Path.GetFileName(exportConfig.ExportFilePath)));
+ sb.AppendLine(res);
+ sb.AppendLine("");
+ sb.AppendLine("'- Draw tile --------------------------------------------");
+
+ sb.AppendLine(string.Format(
+ "putChars(10,5,{0},{1},@{2}({3},0))",
+ tile.Width / 8,
+ tile.Height / 8,
+ exportConfig.LabelName,
+ tile.Frames == 1 ? "0" : "0,0"));
+ sb.AppendLine("");
+ }
+ break;
+
+ case ExportDataTypes.ASM:
+ {
+ sb.AppendLine("'- Includes -----------------------------------------------");
+ sb.AppendLine("#INCLUDE ");
+ sb.AppendLine("");
+ sb.AppendLine("'- Draw tile --------------------------------------------");
+ sb.AppendLine(string.Format(
+ "putChars(10,5,{0},{1},@{2}{3})",
+ tile.Width / 8,
+ tile.Height / 8,
+ exportConfig.LabelName,
+ tile.Name.Replace(" ", "_")));
+ sb.AppendLine("");
+ sb.AppendLine("' This section must not be executed");
+ sb.AppendLine(string.Format("' Can use: #INCLUDE \"{0}\"",
+ Path.GetFileName(exportConfig.ExportFilePath)));
+ sb.AppendLine(res);
+ }
+ break;
+
+ case ExportDataTypes.BIN:
+ {
+ sb.AppendLine("'- Includes -----------------------------------------------");
+ sb.AppendLine("#INCLUDE ");
+ sb.AppendLine("");
+ sb.AppendLine("'- Draw tile --------------------------------------------");
+ sb.AppendLine(string.Format(
+ "putChars(10,5,{0},{1},@{2})",
+ tile.Width / 8,
+ tile.Height / 8,
+ exportConfig.LabelName));
+ sb.AppendLine("");
+ sb.AppendLine("' This section must not be executed");
+ sb.AppendLine(string.Format(
+ "{0}:",
+ exportConfig.LabelName));
+ sb.AppendLine("ASM");
+ sb.AppendLine(string.Format("\tINCBIN \"{0}\"",
+ Path.GetFileName(exportConfig.ExportFilePath)));
+ sb.AppendLine("END ASM");
+ }
+ break;
+
+ case ExportDataTypes.TAP:
+ {
+ sb.AppendLine("'- Includes -----------------------------------------------");
+ sb.AppendLine("#INCLUDE ");
+ sb.AppendLine("");
+ sb.AppendLine("' Load .tap data ------------------------------------------");
+ sb.AppendLine("LOAD \"\" CODE");
+ sb.AppendLine("");
+ sb.AppendLine("'- Draw tile --------------------------------------------");
+ sb.AppendLine(string.Format(
+ "putChars(10,5,{0},{1},@{2})",
+ tile.Width / 8,
+ tile.Height / 8,
+ exportConfig.LabelName));
+ sb.AppendLine("");
+ }
+ break;
+ }
+
+ txtCode.Text = sb.ToString();
+ }
+
+ #endregion
+
+
+ #region Export
+
+ private void Export()
+ {
+ GetConfigFromUI();
+ var em = new ExportManager();
+ em.Initialize(FileTypes.Tile);
+ em.ExportTiles(exportConfig, tiles);
+ }
+
+ #endregion
+
+
+ #region Buttons
+
+ private void BtnSave_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
+ {
+ GetConfigFromUI();
+ ServiceLayer.Export_SetConfigFile(fileName + ".zbs", exportConfig);
+ Export();
+ //this.Close();
+ }
+
+ private async void BtnOutputFile_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
+ {
+ var fileTypes = new FilePickerFileType[2];
+ fileTypes[1] = new FilePickerFileType("All files") { Patterns = new[] { "*", "*.*" } };
+ fileTypes[0] = new FilePickerFileType("Tile files") { Patterns = new[] { "*.zxtiledata" } };
+
+ var select = await StorageProvider.SaveFilePickerAsync(new Avalonia.Platform.Storage.FilePickerSaveOptions
+ {
+ ShowOverwritePrompt = true,
+ FileTypeChoices = fileTypes,
+ Title = "Select Export path...",
+ });
+
+ if (select != null)
+ {
+ txtOutputFile.Text = Path.GetFullPath(select.Path.LocalPath);
+ }
+ }
+
+
+ private void BtnExport_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
+ {
+ GetConfigFromUI();
+ Export();
+ this.Close();
+ }
+
+
+ private void BtnCopy_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
+ {
+ TopLevel.GetTopLevel(this)?.Clipboard?.SetTextAsync(txtCode.Text);
+ }
+
+
+ private void BtnCancel_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
+ {
+ this.Close();
+ }
+
+
+
+ private void CmbArrayBase_SelectionChanged(object? sender, SelectionChangedEventArgs e)
+ {
+ var idx = cmbArrayBase.SelectedIndex;
+ exportConfig.ArrayBase = idx;
+ Refresh();
+ }
+
+
+ private void CmbDataType_SelectionChanged(object? sender, SelectionChangedEventArgs e)
+ {
+ var idx = cmbDataType.SelectedIndex;
+ exportConfig.ExportDataType = (ExportDataTypes)idx;
+
+ var ext = "";
+ switch (exportConfig.ExportDataType)
+ {
+ case ExportDataTypes.ASM:
+ case ExportDataTypes.DIM:
+ ext = ".bas";
+ break;
+ case ExportDataTypes.BIN:
+ ext = ".bin";
+ break;
+ case ExportDataTypes.TAP:
+ ext = ".tap";
+ break;
+ default:
+ return;
+ }
+ txtOutputFile.Text = Path.Combine(Path.GetDirectoryName(txtOutputFile.Text), Path.GetFileNameWithoutExtension(txtOutputFile.Text) + ext);
+ exportConfig.ExportFilePath = txtOutputFile.Text;
+ Refresh();
+ }
+
+
+ private void ChkAttr_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
+ {
+ exportConfig.IncludeAttr = chkAttr.IsChecked.ToBoolean();
+ Refresh();
+ }
+
+ #endregion
+ }
+}
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TileImage.cs b/ZXBStudio/DocumentEditors/ZXMaps/TileImage.cs
new file mode 100644
index 0000000..b580dec
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/TileImage.cs
@@ -0,0 +1,235 @@
+using Avalonia;
+using Avalonia.Media;
+using Avalonia.Media.Imaging;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using ZXBasicStudio.DocumentEditors.ZXGraphics;
+using ZXBasicStudio.DocumentEditors.ZXGraphics.neg;
+
+namespace ZXBasicStudio.DocumentEditors.ZXMaps
+{
+ public class TileImage : IZXBitmap, IDisposable
+ {
+ #region Private fields
+ private WriteableBitmap? bitmap;
+ #endregion
+
+ #region Public properties
+
+ public bool IsEmpty { get; private set; }
+ public bool ViewAttributes { get; set; } = true;
+
+ #endregion
+
+ #region Constructors
+
+ public TileImage()
+ {
+ bitmap = new WriteableBitmap(new PixelSize(8, 8), new Vector(72, 72), Avalonia.Platform.PixelFormat.Rgba8888, Avalonia.Platform.AlphaFormat.Opaque);
+
+ Clear(Colors.White);
+ }
+
+
+ public TileImage(ZXMapsTile Tile, int FrameNumber)
+ {
+ bitmap = new WriteableBitmap(new PixelSize(Tile.Width, Tile.Height), new Vector(72, 72), Avalonia.Platform.PixelFormat.Rgba8888, Avalonia.Platform.AlphaFormat.Opaque);
+
+ RenderTile(Tile, FrameNumber);
+ }
+
+ #endregion
+
+ #region Public functions
+
+ public unsafe void Clear(Color ClearColor)
+ {
+ if (bitmap == null) //disposed
+ return;
+
+ using var lockData = bitmap.Lock();
+ uint* data = (uint*)lockData.Address;
+ uint color = ToRgba(ClearColor);
+
+ for (int y = 0; y < bitmap.PixelSize.Height; y++)
+ {
+ for (int x = 0; x < bitmap.PixelSize.Width; x++)
+ {
+ data[y * lockData.RowBytes / 4 + x] = color;
+ }
+
+ }
+
+ IsEmpty = true;
+ }
+
+
+ public unsafe void RenderTile(ZXMapsTile Tile, int FrameNumber)
+ {
+ try
+ {
+ if (Tile == null)
+ {
+ return;
+ }
+ if (bitmap == null) //disposed
+ throw new ObjectDisposedException("ZXTileImage");
+
+ if (bitmap.PixelSize.Width != Tile.Width || bitmap.PixelSize.Height != Tile.Height)
+ {
+ bitmap.Dispose();
+ bitmap = new WriteableBitmap(new PixelSize(Tile.Width, Tile.Height), new Vector(72,72), Avalonia.Platform.PixelFormat.Rgba8888, Avalonia.Platform.AlphaFormat.Opaque);
+ }
+
+ using var lockData = bitmap.Lock();
+ uint* data = (uint*)lockData.Address;
+
+ var frame = Tile.Patterns[FrameNumber];
+ int index = 0;
+
+ for (int y = 0; y < Tile.Height; y++)
+ {
+ for (int x = 0; x < Tile.Width; x++)
+ {
+ int colorIndex = frame.RawData[index++];
+
+ PaletteColor color;
+
+ switch (Tile.GraphicMode)
+ {
+ case GraphicsModes.ZXSpectrum:
+ {
+ var attr = GetAttribute(Tile, frame, x, y);
+ if (colorIndex == 0)
+ {
+ if (ViewAttributes)
+ {
+ color = Tile.Palette[attr.Paper];
+ }
+ else
+ {
+ color = Tile.Palette[7];
+ }
+ }
+ else
+ {
+ if(ViewAttributes)
+ {
+ color = Tile.Palette[attr.Ink];
+ }
+ else
+ {
+ color = Tile.Palette[0];
+ }
+ }
+ }
+ break;
+ case GraphicsModes.Monochrome:
+ if (colorIndex > Tile.Palette.Length - 1)
+ {
+ colorIndex = Tile.Palette.Length - 1;
+ }
+ color = Tile.Palette[colorIndex];
+ break;
+ default:
+ color = new PaletteColor { Red = 0xFF, Green = 0xFF, Blue = 0xFF };
+ break;
+ }
+
+ data[y * lockData.RowBytes / 4 + x] = ToRgba(color);
+
+ }
+ }
+
+ IsEmpty = false;
+ } catch(Exception ex)
+ {
+
+ }
+ }
+
+ #endregion
+
+ #region Private functions
+
+ uint ToRgba(PaletteColor Color)
+ {
+ return (uint)((255 << 24) | (Color.Blue << 16) | (Color.Green << 8) | Color.Red);
+ }
+ uint ToRgba(Color Color)
+ {
+ return (uint)((255 << 24) | (Color.B << 16) | (Color.G << 8) | Color.R);
+ }
+ private AttributeColor GetAttribute(ZXMapsTile Tile, Pattern Pattern, int X, int Y)
+ {
+ int cW = Tile.Width / 8;
+ int cX = X / 8;
+ int cY = Y / 8;
+ int dir = (cY * cW) + cX;
+ if (Pattern.Attributes == null)
+ {
+ Pattern.Attributes = new AttributeColor[(Tile.Width + Tile.Height) / 8];
+ for (int n = 0; n < Pattern.Attributes.Length; n++)
+ {
+ Pattern.Attributes[n] = new AttributeColor()
+ {
+ Attribute = 56 // Paper 7, ink 0
+ };
+ }
+ }
+ if (dir > Pattern.Attributes.Length)
+ {
+ return new AttributeColor()
+ {
+ Attribute = 56 // Paper 7, ink 0
+ };
+ }
+ return Pattern.Attributes[dir];
+ }
+
+ #endregion
+
+ #region IZXBitmap implementation
+ public Size Size
+ {
+ get
+ {
+ return bitmap.Size;
+ }
+ }
+
+
+ public PixelSize PixelSize
+ {
+ get
+ {
+ return bitmap.PixelSize;
+ }
+ }
+
+
+ public void Draw(DrawingContext context, Rect sourceRect, Rect destRect)
+ {
+ ((IImage)bitmap).Draw(context, sourceRect, destRect);
+ }
+
+ #endregion
+
+ #region IDisposable implementation
+
+ public void Dispose()
+ {
+ if (bitmap == null)
+ return;
+
+ bitmap.Dispose();
+ bitmap = null;
+ IsEmpty = true;
+ }
+
+ #endregion
+ }
+}
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TileImportDialog.axaml b/ZXBStudio/DocumentEditors/ZXMaps/TileImportDialog.axaml
new file mode 100644
index 0000000..47ff475
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/TileImportDialog.axaml
@@ -0,0 +1,97 @@
+
+
+
+
+ Source
+
+
+
+ Move the image with the arrow keys (shift faster)
+
+
+
+
+
+
+ Zoom 1x
+
+
+
+
+ Target
+
+
+
+ Mode:
+
+ Monochrome
+ ZX Spectrum
+
+
+ Width:
+
+
+
+
+ Height:
+
+
+
+
+
+
+ Append to actual tiles
+
+
+
+
+ Insert blank tile at start
+
+
+
+
+ Remove duplicated tiles
+
+
+
+
+ Invert palette (only in monochrome mode)
+
+
+
+
+ Cutoff threshold:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TileImportDialog.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/TileImportDialog.axaml.cs
new file mode 100644
index 0000000..a774940
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/TileImportDialog.axaml.cs
@@ -0,0 +1,682 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Media;
+using Avalonia.Media.Imaging;
+using Avalonia.Metadata;
+using Avalonia.Platform.Storage;
+using Avalonia.Threading;
+using MsBox.Avalonia.Dto;
+using MsBox.Avalonia.Enums;
+using MsBox.Avalonia;
+using SixLabors.ImageSharp.PixelFormats;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Drawing.Imaging;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using ZXBasicStudio.Classes;
+using ZXBasicStudio.Common;
+using ZXBasicStudio.DocumentEditors.ZXGraphics.log;
+using ZXBasicStudio.DocumentEditors.ZXGraphics.neg;
+using ZXBasicStudio.DocumentModel.Classes;
+using ZXBasicStudio.DocumentModel.Interfaces;
+using ZXBasicStudio.Extensions;
+using ZXBasicStudio.IntegratedDocumentTypes.CodeDocuments.Basic;
+using static System.Net.Mime.MediaTypeNames;
+using Avalonia.Controls.Presenters;
+using Avalonia.Input;
+using System.Formats.Tar;
+
+namespace ZXBasicStudio.DocumentEditors.ZXMaps
+{
+ public partial class TileImportDialog : Window, IDisposable
+ {
+ /*
+ private string fileName = "";
+
+
+ private FileTypeConfig fileType = null;
+ private Pattern[] patterns = null;
+ private ExportConfig exportConfig = null;
+ */
+
+ private Action CallBackCommand = null;
+ private IEnumerable tiles = null;
+ private ZXMapsTile tile = null;
+
+ private GraphicsModes tileMode = GraphicsModes.Monochrome;
+ private int tileWidth = 16;
+ private int tileHeight = 16;
+ private int tileZoom = 4;
+ private int cutOff = 5;
+ private int tileFrames = 1;
+ private bool appendToTiles = false;
+ private bool insertBlankAtStart = true;
+ private bool removeDuplicatedTiles = true;
+ private DispatcherTimer tmr = null;
+ private bool resetFocus = false;
+ private bool invert = true;
+
+ public TileImportDialog()
+ {
+ InitializeComponent();
+
+ btnFile.Tapped += BtnFile_Tapped;
+ sldZoom.PropertyChanged += SldZoom_PropertyChanged;
+
+ grdImport.KeyDown += GrdImport_KeyDown;
+
+ cmbMode.SelectionChanged += CmbMode_SelectionChanged;
+ txtWidth.ValueChanged += TxtWidth_ValueChanged;
+ txtHeight.ValueChanged += TxtHeight_ValueChanged;
+ sldCutOff.PropertyChanged += sldCutOff_PropertyChanged;
+
+ btnCancel.Tapped += BtnCancel_Tapped;
+ btnImport.Tapped += BtnImport_Tapped;
+ }
+
+ public bool Initialize(IEnumerable tiles, Action callBackCommand)
+ {
+ this.CallBackCommand = callBackCommand;
+ this.tiles = tiles;
+
+ tileMode = GraphicsModes.Monochrome;
+ tileWidth = 16;
+ tileHeight = 16;
+
+ cmbMode.SelectedIndex = 0;
+ txtWidth.Text = tileWidth.ToString();
+ txtHeight.Text = tileHeight.ToString();
+ sldZoom.Value = tileZoom;
+
+ tmr = new DispatcherTimer(TimeSpan.FromMilliseconds(1000), DispatcherPriority.Normal, UpdatePreview);
+
+ return true;
+ }
+
+ public void Dispose()
+ {
+ if (tmr != null)
+ {
+ tmr.Stop();
+ tmr = null;
+ }
+ }
+
+ #region Image source
+
+ private async void BtnFile_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
+ {
+ var fileTypes = new FilePickerFileType[5];
+ fileTypes[0] = new FilePickerFileType("All files") { Patterns = new[] { "*", "*.*" } };
+ fileTypes[1] = new FilePickerFileType("BMP files") { Patterns = new[] { "*.bmp" } };
+ fileTypes[2] = new FilePickerFileType("JPG files") { Patterns = new[] { "*.jpg", "*.jpeg" } };
+ fileTypes[3] = new FilePickerFileType("PNG files") { Patterns = new[] { "*.png" } };
+ fileTypes[4] = new FilePickerFileType("ZX Paintbrush SCR files") { Patterns = new[] { "*.scr" } };
+ /*fileTypes[5] = new FilePickerFileType("SCR Spectrum screen files") { Patterns = new[] { "*.scr" } };*/
+
+ var select = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
+ {
+ AllowMultiple = false,
+ FileTypeFilter = fileTypes,
+ Title = "Select file to import...",
+ });
+
+ if (select != null && select.Count > 0)
+ {
+ cnvSource.LoadImage(select[0]);
+ }
+ }
+
+
+ private int lastZoom = 0;
+
+ ///
+ /// Zoom changed
+ ///
+ ///
+ ///
+ private void SldZoom_PropertyChanged(object? sender, Avalonia.AvaloniaPropertyChangedEventArgs e)
+ {
+ int z = (int)sldZoom.Value;
+ if (z == 0 || z == lastZoom)
+ {
+ return;
+ }
+ lastZoom = z;
+
+ txtZoom.Text = "Zoom " + z.ToString() + "x";
+ cnvSource.Zoom = z;
+ }
+
+
+ ///
+ /// CutOff changed
+ ///
+ ///
+ ///
+ private void sldCutOff_PropertyChanged(object? sender, Avalonia.AvaloniaPropertyChangedEventArgs e)
+ {
+ cutOff = (int)sldCutOff.Value;
+ }
+
+
+ private void GrdImport_KeyDown(object? sender, Avalonia.Input.KeyEventArgs e)
+ {
+ switch (e.Key)
+ {
+ case Avalonia.Input.Key.Up:
+ if (e.KeyModifiers == Avalonia.Input.KeyModifiers.Shift)
+ {
+ cnvSource.OffsetY += 8;
+ }
+ else
+ {
+ cnvSource.OffsetY++;
+ }
+ break;
+ case Avalonia.Input.Key.Down:
+ if (e.KeyModifiers == Avalonia.Input.KeyModifiers.Shift)
+ {
+ cnvSource.OffsetY -= 8;
+ }
+ else
+ {
+ cnvSource.OffsetY--;
+ }
+ break;
+ case Avalonia.Input.Key.Left:
+ if (e.KeyModifiers == Avalonia.Input.KeyModifiers.Shift)
+ {
+ cnvSource.OffsetX += 8;
+ }
+ else
+ {
+ cnvSource.OffsetX++;
+ }
+ break;
+ case Avalonia.Input.Key.Right:
+ if (e.KeyModifiers == Avalonia.Input.KeyModifiers.Shift)
+ {
+ cnvSource.OffsetX -= 8;
+ }
+ else
+ {
+ cnvSource.OffsetX--;
+ }
+ break;
+ }
+ }
+
+ #endregion
+
+
+ #region Preview
+
+
+ private void UpdatePreview(object userState, EventArgs e)
+ {
+ _UpdatePreview();
+ if (resetFocus)
+ {
+ btnFile.Focus();
+ resetFocus = false;
+ }
+ }
+
+
+ private void _UpdatePreview()
+ {
+ try
+ {
+ ReadProperties();
+
+ var imgData = cnvSource.imageData;
+ if (imgData == null)
+ {
+ return;
+ }
+
+ GetProperties();
+
+ var s = new ZXMapsTile();
+ s.CurrentFrame = 0;
+ s.DefaultColor = 7;
+ s.Export = true;
+ s.Frames = 1;
+ s.GraphicMode = tileMode;
+ s.Height = tileHeight;
+ s.Id = 0;
+ s.Masked = false;
+ s.Name = "";
+ s.Palette = ServiceLayer.GetPalette(tileMode);
+ s.Patterns = new List();
+ s.Width = tileWidth;
+
+ int numAttr = (tileWidth / 8) * (tileHeight / 8);
+ var attrsDefault = new AttributeColor[numAttr];
+
+ for (int n = 0; n < numAttr; n++)
+ {
+ var attr = new AttributeColor();
+ attr.Paper = 7;
+ attr.Ink = 0;
+ attr.Bright = false;
+ attr.Flash = false;
+ attrsDefault[n] = attr;
+ }
+
+ var w = scrPreview.Bounds.Width;
+ var h = scrPreview.Bounds.Width;
+ double ix = (tileWidth + 2) * 4;
+ double iy = (tileHeight + 2) * 4;
+ int sw = tileWidth / 8;
+ int sh = tileHeight / 8;
+ int px = 0;
+ int py = 0;
+ int xs = 0;
+ int ys = 0;
+ int paper = -1;
+ int ink = -1;
+ int prevX = 0;
+ int prevY = 0;
+ int anchoPreview = 0;
+ int altoPreview = 0;
+ int maxX = (int)(cnvSource.Width / tileWidth);
+ int maxY = (int)(cnvSource.Height / tileHeight);
+
+ pnlPreview.Children.Clear();
+
+ tileFrames = maxX * maxY;
+
+ int frameNumber = 0;
+ int framesX = imgData.Width / tileWidth;
+ int framesY = imgData.Height / tileHeight;
+
+ for (int fy = 0; fy < framesY; fy++)
+ {
+ cnvSource.OffsetY = fy * tileHeight;
+ for (int fx = 0; fx < framesX; fx++)
+ {
+ cnvSource.OffsetX = fx * tileWidth;
+
+ var attrs = attrsDefault.Clonar();
+ var pattern = new Pattern();
+ pattern.Attributes = attrs; // new AttributeColor[(tileWidth / 8) * (tileHeight / 8)];
+ pattern.Id = frameNumber;
+ pattern.Name = "";
+ pattern.Number = frameNumber.ToString();
+ pattern.RawData = new int[tileWidth * tileHeight];
+
+ int dir = 0;
+ for (int cy = 0; cy < sh; cy++)
+ {
+ py = cy * 8;
+ for (int cx = 0; cx < sw; cx++)
+ {
+ //ys = cnvSource.OffsetY + py;
+ ys = (fy * tileHeight) + py;
+
+ px = cx * 8;
+ paper = -1;
+ ink = -1;
+ for (int y = 0; y < 8; y++)
+ {
+ //xs = (cnvSource.OffsetX /*+ (n * tileWidth)*/) + px;
+ xs = (fx * tileWidth) + px;
+
+ dir = (((cy * 8) + y) * tileWidth) + (cx * 8);
+ for (int x = 0; x < 8; x++)
+ {
+ if (xs >= 0 && xs < imgData.Width &&
+ ys >= 0 && ys < imgData.Height)
+ {
+ var c = imgData[xs, ys];
+ var idxAttr = GetColor(c.R, c.G, c.B, s.Palette);
+ if (tileMode == GraphicsModes.ZXSpectrum)
+ {
+ // Fijar el color
+ var dirAttr = (cy * sw) + cx;
+ var attr = pattern.Attributes[dirAttr];
+ attr.Bright = attr.Bright | (idxAttr > 7);
+ byte cCol = (byte)(idxAttr /*& 0b111*/);
+ if (paper == -1)
+ {
+ attr.Paper = cCol;
+ paper = cCol;
+ }
+ else if (ink == -1 && paper != cCol)
+ {
+ attr.Ink = cCol;
+ ink = cCol;
+ }
+ if (cCol == paper)
+ {
+ idxAttr = 0;
+ }
+ else
+ {
+ idxAttr = 7;
+ }
+ }
+ else if (tileMode == GraphicsModes.Monochrome && invert)
+ {
+ if (idxAttr == 0)
+ {
+ idxAttr = 1;
+ }
+ else
+ {
+ idxAttr = 0;
+ }
+ }
+ pattern.RawData[dir] = idxAttr;
+ }
+ else
+ {
+ pattern.RawData[dir] = 0;
+ }
+ dir++;
+ xs++;
+ }
+ ys++;
+ }
+ }
+ }
+
+ bool addToPatterns = true;
+ if (removeDuplicatedTiles)
+ {
+ if (PatternExists(s.Patterns, pattern))
+ {
+ addToPatterns = false;
+ }
+ }
+ if (addToPatterns)
+ {
+ s.Patterns.Add(pattern);
+
+ var prev = new ZXGraphics.ZXSpriteImage();
+ var img = new Avalonia.Controls.Image();
+ img.Width = tileWidth * tileZoom;
+ img.Height = tileHeight * tileZoom;
+ img.Source = prev;
+ pnlPreview.Children.Add(img);
+
+ Canvas.SetLeft(img, prevX);
+ Canvas.SetTop(img, prevY);
+ prevX += (tileWidth * 4);
+
+ if (anchoPreview < prevX)
+ {
+ anchoPreview = prevX;
+ }
+ if ((prevX + sw) > w)
+ {
+ prevY += (tileHeight * 4);
+ prevX = 0;
+ if (altoPreview < prevY)
+ {
+ altoPreview = prevY;
+ }
+ }
+
+ prev.RenderSprite(s, frameNumber);
+ frameNumber++;
+ }
+ }
+ }
+
+ cnvSource.OffsetX = 0;
+ cnvSource.OffsetY = 0;
+
+ tile = s;
+ pnlPreview.Width = anchoPreview + (tileWidth * 4);
+ pnlPreview.Height = altoPreview + (tileHeight * 4);
+ }
+ catch (Exception ex)
+ {
+
+ }
+ }
+
+ private bool PatternExists(List patterns, Pattern pattern)
+ {
+ foreach (var p1 in patterns)
+ {
+ bool igual = true;
+ for (int n = 0; n < p1.RawData.Count(); n++)
+ {
+ if (p1.RawData[n] != pattern.RawData[n])
+ {
+ igual = false;
+ break;
+ }
+ }
+ if (igual)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private int GetColor(byte r, byte g, byte b, PaletteColor[] palette)
+ {
+ bool brigth = false;
+ if (r > 250 || g > 250 || b > 250)
+ {
+ brigth = true;
+ }
+ byte cr = GetColor_CutOff(r);
+ byte cg = GetColor_CutOff(g);
+ byte cb = GetColor_CutOff(b);
+ PaletteColor targetColor = new PaletteColor()
+ {
+ Blue = cb,
+ Green = cg,
+ Red = cr
+ };
+
+ var palColor = palette.OrderBy(c => GetColorDistance(c, targetColor)).First();
+ for (int n = 0; n < palette.Count(); n++)
+ {
+ var p = palette[n];
+ if (palColor.Red == p.Red &&
+ palColor.Green == p.Green &&
+ palColor.Blue == p.Blue)
+ {
+ if (brigth && n < 8)
+ {
+ return n + 8;
+ }
+ return n;
+ }
+ }
+ return 0;
+ }
+
+
+ private byte GetColor_CutOff(byte c)
+ {
+ if (cutOff == 5)
+ {
+ return c;
+ }
+
+ if (cutOff < 5)
+ {
+ int ic = c - (25 * (5 - cutOff));
+ if (ic < 0)
+ {
+ return 0;
+ }
+ else
+ {
+ return ic.ToByte();
+ }
+ }
+ else
+ {
+ int ic = c + (25 * (cutOff - 5));
+ if (ic > 255)
+ {
+ return 255;
+ }
+ else
+ {
+ return ic.ToByte();
+ }
+ }
+ }
+
+
+ private static double GetColorDistance(PaletteColor c1, PaletteColor c2)
+ {
+ int rDiff = Math.Abs(c1.Red - c2.Red);
+ int gDiff = Math.Abs(c1.Green - c2.Green);
+ int bDiff = Math.Abs(c1.Blue - c2.Blue);
+ return Math.Sqrt(rDiff * rDiff + gDiff * gDiff + bDiff * bDiff);
+ }
+
+
+ private void GetProperties()
+ {
+ tileWidth = txtWidth.Text.ToInteger();
+ tileHeight = txtHeight.Text.ToInteger();
+ tileMode = (GraphicsModes)cmbMode.SelectedIndex;
+ }
+
+ private void CmbMode_SelectionChanged(object? sender, SelectionChangedEventArgs e)
+ {
+ resetFocus = true;
+ if (cmbMode.SelectedIndex == 0)
+ {
+ pnlInvert.IsEnabled = true;
+ }else
+ {
+ pnlInvert.IsEnabled = false;
+ }
+ //btnFile.Focus();
+ }
+
+
+ private void ChangeMode(GraphicsModes oldMode, GraphicsModes newMode)
+ {
+ resetFocus = true;
+ }
+
+
+ private void TxtWidth_ValueChanged(object? sender, NumericUpDownValueChangedEventArgs e)
+ {
+ resetFocus = true;
+ cnvSource.SpriteWidth = txtWidth.Text.ToInteger();
+ cnvSource.Refresh();
+ }
+
+ private void TxtHeight_ValueChanged(object? sender, NumericUpDownValueChangedEventArgs e)
+ {
+ resetFocus = true;
+ cnvSource.SpriteHeight = txtHeight.Text.ToInteger();
+ cnvSource.Refresh();
+ }
+
+ #endregion
+
+
+ #region Buttons
+
+ private void BtnImport_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
+ {
+ Import();
+ }
+
+ private void BtnCancel_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
+ {
+ this.Close();
+ this.Dispose();
+ }
+
+ #endregion
+
+
+ #region Import
+
+ private async void Import()
+ {
+ try
+ {
+ GetProperties();
+
+ if (!appendToTiles)
+ {
+ // Check if existing tiles an overwrite
+ if (tiles.Any())
+ {
+ if (!await this.ShowConfirm("Confirm overwrite", "The existing tiles will be deleted.\r\nAre you sure you want to overwrite it?"))
+ {
+ return;
+ }
+
+ }
+ }
+
+ ReadProperties();
+
+ for (int n = 0; n < tile.Patterns.Count(); n++)
+ {
+ var spr = tile.Clonar();
+ spr.Id = n;
+ spr.Patterns = spr.Patterns.Skip(n).Take(1).ToList();
+ spr.Frames = 1;
+ spr.Name = n.ToString();
+ var spr2 = tiles.FirstOrDefault(d => d != null && d.Name == spr.Name);
+ if (spr2 == null)
+ {
+ var id = 0;
+ if (tiles.Count() > 0 && tiles.ElementAt(0) != null)
+ {
+ id = tiles.Where(d => d != null).Max(d => d.Id) + 1;
+ }
+ spr.Id = id;
+ CallBackCommand?.Invoke(spr, "ADD");
+ }
+ else
+ {
+ spr.Id = spr2.Id;
+ CallBackCommand?.Invoke(tile, "UPDATE");
+ }
+ }
+
+ this.Close();
+ this.Dispose();
+ }
+ catch (Exception ex)
+ {
+ this.ShowError("ERROR importing image", ex.Message + ex.StackTrace);
+ }
+ }
+
+
+ private void ReadProperties()
+ {
+ tileMode = (GraphicsModes)cmbMode.SelectedIndex;
+ tileWidth = txtWidth.Text.ToInteger();
+ tileHeight = txtHeight.Text.ToInteger();
+ appendToTiles = chkAppend.IsChecked.ToBoolean();
+ insertBlankAtStart = chkInsertBlank.IsChecked.ToBoolean();
+ removeDuplicatedTiles = chkRemoveDuplicated.IsChecked.ToBoolean();
+ invert = chkInvert.IsChecked.ToBoolean();
+ if (tileMode != GraphicsModes.Monochrome)
+ {
+ invert = false;
+ }
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TilePatternControl.axaml b/ZXBStudio/DocumentEditors/ZXMaps/TilePatternControl.axaml
new file mode 100644
index 0000000..bacdbc2
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/TilePatternControl.axaml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TilePatternControl.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/TilePatternControl.axaml.cs
new file mode 100644
index 0000000..04e15ed
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/TilePatternControl.axaml.cs
@@ -0,0 +1,419 @@
+using Avalonia.Controls;
+using Avalonia.Controls.Shapes;
+using Avalonia.Media;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using ZXBasicStudio.Common;
+using ZXBasicStudio.DocumentEditors.ZXGraphics.log;
+using ZXBasicStudio.DocumentEditors.ZXGraphics.neg;
+
+namespace ZXBasicStudio.DocumentEditors.ZXMaps
+{
+ public partial class TilePatternControl : UserControl
+ {
+ #region Public properties
+
+ ///
+ /// Tile data
+ ///
+ public ZXMapsTile TileData
+ {
+ get
+ {
+ return _TileData;
+ }
+ set
+ {
+ _TileData = value;
+ IsSelected = true;
+ //ApplySettings(false);
+ Refresh();
+ }
+ }
+
+ ///
+ /// CallBack for commands: "ADD", "CLONE", "DELETE", "SELECT", "MODE", "SIZE"
+ ///
+ public Action CallBackCommand { get; set; }
+
+ ///
+ /// True when then control is selected
+ ///
+ public bool IsSelected
+ {
+ get
+ {
+ return _IsSelected;
+ }
+ set
+ {
+ if (_IsSelected != value)
+ {
+ _IsSelected = value;
+ Refresh();
+ }
+ }
+ }
+
+ public bool InfoVisible
+ {
+ get
+ {
+ return _InfoVisible;
+ }
+ set
+ {
+ _InfoVisible = value;
+ Refresh();
+ }
+ }
+
+ ///
+ /// The settings has changed
+ ///
+ public bool SettingsChanged
+ {
+ get
+ {
+ return _SettingsChanged;
+ }
+ set
+ {
+ _SettingsChanged = value;
+ Refresh();
+ }
+ }
+
+ #endregion
+
+
+ #region Private fields
+
+ private bool _IsSelected = false;
+ private bool _SettingsChanged = false;
+ private bool refreshing = false;
+ private bool newTile = true;
+ private ZXMapsTile _TileData = null;
+
+ private int tileWidth = 16;
+ private int tileHeight = 16;
+
+ private bool _InfoVisible = true;
+
+ #endregion
+
+
+ #region Constructor and public methods
+
+ public TilePatternControl()
+ {
+ InitializeComponent();
+ }
+
+
+ ///
+ /// Initializes the control
+ ///
+ /// Data of the Tile, if is null, the "Add" icon is visible and no properties are shown
+ /// CallBak for actions command, line "ADD", "CLONE", "DELETE" or "SELECTED"
+ ///
+ public bool Initialize(ZXMapsTile TileData, Action callBackCommand, int tileWidth, int tileHeight)
+ {
+ this.TileData = TileData;
+ this.CallBackCommand = callBackCommand;
+ this.tileWidth = tileWidth;
+ this.tileHeight = tileHeight;
+
+ this.PointerPressed += TilePropertiesControl_PointerPressed;
+
+ btnNew.Tapped += BtnNew_Tapped;
+
+ _SettingsChanged = false;
+ newTile = true;
+
+ Select();
+
+ return true;
+ }
+
+
+ public void Refresh()
+ {
+ if (refreshing)
+ {
+ return;
+ }
+ refreshing = true;
+
+ try
+ {
+ if (TileData != null)
+ {
+ tileWidth = TileData.Width;
+ tileHeight = TileData.Height;
+ }
+
+ var w = tileWidth * 4;
+ var h = tileHeight * 4;
+ var w4 = w + 4;
+ var h4 = h + 4;
+
+ if (this.Width != w4 || this.Height != h4)
+ {
+ this.Width = w + 4;
+ this.Height = h + 4;
+ cnvPoints.Width = w;
+ cnvPoints.Height = h;
+ }
+
+ if (TileData == null)
+ {
+ btnNew.IsVisible = true;
+ cnvPoints.IsVisible = false;
+ lblNumber.IsVisible = false;
+ return;
+ }
+
+ if (_IsSelected)
+ {
+ brdMain.BorderBrush = new SolidColorBrush(Colors.Red);
+ }
+ else
+ {
+ brdMain.BorderBrush = new SolidColorBrush(Colors.Gray);
+ }
+
+ btnNew.IsVisible = false;
+
+ cnvPoints.IsVisible = true;
+
+ if (TileData.Patterns == null || TileData.Patterns.Count == 0)
+ {
+ TileData.Patterns = new List();
+ TileData.Patterns.Add(new Pattern()
+ {
+ RawData = new int[tileHeight*tileHeight],
+ Id = 0,
+ Name = "",
+ Number = ""
+ });
+ }
+ if (TileData.Palette == null || TileData.Palette.Length == 0)
+ {
+ TileData.Palette = ServiceLayer.GetPalette(TileData.GraphicMode);
+ }
+
+ // Delete background
+ {
+ var r = new Rectangle();
+ r.Width = cnvPoints.Width;
+ r.Height = cnvPoints.Height;
+ r.Fill = new SolidColorBrush(new Color(255, 0x28, 0x28, 0x28));
+ cnvPoints.Children.Add(r);
+ Canvas.SetTop(r, 0);
+ Canvas.SetLeft(r, 0);
+ }
+
+ cnvPoints.Width = TileData.Width * 4;
+ cnvPoints.Height = TileData.Height * 4;
+
+ cnvPoints.Children.Clear();
+ int index = 0;
+ var frame = TileData.Patterns[0];
+ for (int y = 0; y < TileData.Height; y++)
+ {
+ for (int x = 0; x < TileData.Width; x++)
+ {
+ var colorIndex = frame.RawData[index];
+ var r = new Rectangle();
+ r.Width = 4;
+ r.Height = 4;
+ //var palette = TileData.Palette[p];
+
+ //r.Fill = new SolidColorBrush(new Color(255, palette.Red, palette.Green, palette.Blue));
+
+ switch (TileData.GraphicMode)
+ {
+ case GraphicsModes.ZXSpectrum:
+ {
+ var attr = GetAttribute(frame, x, y);
+ PaletteColor palette = null;
+ if (colorIndex == 0)
+ {
+ palette = TileData.Palette[attr.Paper];
+ }
+ else
+ {
+ palette = TileData.Palette[attr.Ink];
+ }
+ r.Fill = new SolidColorBrush(new Color(255, palette.Red, palette.Green, palette.Blue));
+ }
+ break;
+ case GraphicsModes.Monochrome:
+ case GraphicsModes.Next:
+ {
+ var palette = TileData.Palette[colorIndex];
+ r.Fill = new SolidColorBrush(new Color(255, palette.Red, palette.Green, palette.Blue));
+ }
+ break;
+
+ }
+
+ cnvPoints.Children.Add(r);
+ Canvas.SetTop(r, y * 4);
+ Canvas.SetLeft(r, x * 4);
+ index++;
+ }
+ }
+
+ lblNumber.IsVisible = _InfoVisible;
+ if (_InfoVisible)
+ {
+ lblNumber.Text = TileData.Id.ToStringNoNull();
+ }
+ }
+ catch (Exception ex)
+ {
+ }
+ finally
+ {
+ refreshing = false;
+ }
+ }
+
+
+ private AttributeColor GetAttribute(Pattern pattern, int x, int y)
+ {
+ int cW = TileData.Width / 8;
+ int cX = x / 8;
+ int cY = y / 8;
+ return pattern.Attributes[(cY * cW) + cX];
+ }
+
+
+ public void Select()
+ {
+ _IsSelected = true;
+ Refresh();
+ CallBackCommand(this, "SELECTED");
+ }
+
+
+ public void ApplySettings(bool askForApply)
+ {
+ /*
+ if (_TileData == null)
+ {
+ return;
+ }
+ else
+ {
+ var sp = _TileData.Clonar();
+
+ if (sp.Width != _TileData.Width || sp.Height != _TileData.Height)
+ {
+ if (!ServiceLayer.TileData_Resize(ref sp, _TileData.Width, _TileData.Height))
+ {
+ // TODO: Report error
+ return;
+ }
+ }
+ if (sp.GraphicMode != _TileData.GraphicMode)
+ {
+ if (!ServiceLayer.TileData_ChangeMode(ref sp, _TileData.GraphicMode))
+ {
+ // TODO: Report error
+ return;
+ }
+ }
+ if (sp.Masked != _TileData.Masked)
+ {
+ if (!ServiceLayer.TileData_ChangeMasked(ref sp, _TileData.Masked))
+ {
+ // TODO: Report error
+ return;
+ }
+ }
+ if (sp.Frames != _TileData.Frames)
+ {
+ if (!ServiceLayer.TileData_ChangeFrames(ref sp, _TileData.Frames))
+ {
+ // TODO: Report error
+ return;
+ }
+ }
+
+ _TileData = sp;
+ //CallBackCommand?.Invoke(this, "UPDATE");
+ //Refresh();
+ newTile = false;
+ SettingsChanged = false;
+ }
+ */
+ }
+
+ #endregion
+
+
+ #region Private methods
+
+ private void AddNew()
+ {
+ var sp = new ZXMapsTile()
+ {
+ CurrentFrame = 0,
+ DefaultColor = 0,
+ Frames = 1,
+ GraphicMode = GraphicsModes.Monochrome,
+ Height = tileWidth,
+ Id = -1,
+ Masked = false,
+ Name = "",
+ Patterns = new List(),
+ Width = tileHeight
+ };
+ sp.Palette = ServiceLayer.GetPalette(sp.GraphicMode);
+ sp.Patterns.Add(CreatePattern());
+ TileData = sp;
+ _IsSelected = true;
+ Refresh();
+
+ CallBackCommand?.Invoke(this, "ADD");
+ }
+
+
+ private Pattern CreatePattern()
+ {
+ var pat = new Pattern()
+ {
+ Id = 0,
+ Name = "",
+ Number = "0",
+ RawData = new int[tileWidth*tileHeight]
+ };
+ return pat;
+ }
+
+
+ #endregion
+
+
+ #region Button events
+
+ private void BtnNew_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
+ {
+ AddNew();
+ }
+
+
+ private void TilePropertiesControl_PointerPressed(object? sender, Avalonia.Input.PointerPressedEventArgs e)
+ {
+ Select();
+ }
+
+ #endregion
+
+ }
+}
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TilePatternEditor.axaml b/ZXBStudio/DocumentEditors/ZXMaps/TilePatternEditor.axaml
new file mode 100644
index 0000000..529a0a4
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/TilePatternEditor.axaml
@@ -0,0 +1,149 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Zoom 24x
+
+
+ Frame
+
+
+
+
+
+ Paper
+
+
+ 0
+
+
+
+
+
+ Ink
+
+
+ 1
+
+
+
+
+
+
+
+
+
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TilePatternEditor.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/TilePatternEditor.axaml.cs
new file mode 100644
index 0000000..63b3e32
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/TilePatternEditor.axaml.cs
@@ -0,0 +1,1666 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.Shapes;
+using Avalonia.Input;
+using Avalonia.Media;
+using Avalonia.Platform;
+using Avalonia.Themes.Fluent;
+using Avalonia.Threading;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks.Sources;
+using System.Xml.Schema;
+using ZXBasicStudio.Common;
+using ZXBasicStudio.DocumentEditors.ZXGraphics.log;
+using ZXBasicStudio.DocumentEditors.ZXGraphics.neg;
+using ZXBasicStudio.Extensions;
+
+namespace ZXBasicStudio.DocumentEditors.ZXMaps
+{
+ public partial class TilePatternEditor : UserControl
+ {
+ #region Private properties
+
+ ///
+ /// last zoom value
+ ///
+ private int lastZoom = 0;
+
+ ///
+ /// Zooms values
+ ///
+ private int[] zooms = new int[]
+ {
+ 1,2,4,8,16,24,32,48,64
+ };
+
+ private byte actualFrame = 0;
+
+ #endregion
+
+
+ #region Public properties
+
+ ///
+ /// Actual Tile data
+ ///
+ public ZXMapsTile TileData
+ {
+ get
+ {
+ return _TileData;
+ }
+ set
+ {
+ _TileData = value;
+ if (_TileData == null)
+ {
+ lastId = null;
+ return;
+ }
+ if (lastId != _TileData.Id)
+ {
+ _TileData.CurrentFrame = 0;
+ lastId = _TileData.Id;
+ }
+ txtFrame.MaxHeight = _TileData.Frames - 1;
+ Undo_AddPoint();
+ Refresh(true);
+ }
+ }
+
+
+ ///
+ /// Zoom
+ ///
+ public int Zoom
+ {
+ get
+ {
+ return _Zoom;
+ }
+ set
+ {
+ _Zoom = value;
+ Refresh(true);
+ }
+ }
+
+
+ ///
+ /// Color index of the primary color
+ ///
+ public int PrimaryColorIndex { get; set; }
+
+ ///
+ /// Color index of the secondary color
+ ///
+ public int SecondaryColorIndex { get; set; }
+
+ public bool Bright { get; set; }
+ public bool Flash { get; set; }
+
+ public bool ViewAttributes
+ {
+ get
+ {
+ return _ViewAttributes;
+ }
+ set
+ {
+ _ViewAttributes = value;
+ aspect.ViewAttributes = value;
+ Refresh(false);
+ }
+ }
+
+ private bool _ViewAttributes = true;
+
+ public bool InvertPixelsCell { get; set; } = false;
+
+ public bool InvertColorsCell { get; set; } = false;
+
+ public bool ColorPicker { get; set; } = false;
+
+ public int Frame
+ {
+ get
+ {
+ return txtFrame.Text.ToInteger();
+ }
+ set
+ {
+ txtFrame.Text = value.ToString();
+ }
+ }
+
+ #endregion
+
+
+ #region Private fields
+
+ private ZXMapsTile _TileData = null;
+ private int _Zoom = 24;
+ private Action CallBackCommand = null;
+ private int? lastId = null;
+ private TileImage aspect = new TileImage();
+
+ ///
+ /// True when mouse left button is pressed
+ ///
+ private bool MouseLeftPressed = false;
+ ///
+ /// True when moude right button is pressed
+ ///
+ private bool MouseRightPressed = false;
+
+ ///
+ /// Dispatcher for refresh operations
+ ///
+ private DispatcherTimer tmr = null;
+
+ #endregion
+
+
+ #region Public Methods
+
+ public TilePatternEditor()
+ {
+ InitializeComponent();
+
+ grdEditor.BackgroundImage = aspect;
+
+ PrimaryColorIndex = 1;
+ SecondaryColorIndex = 0;
+
+ grdEditor.PointerMoved += GrdEditor_PointerMoved;
+ grdEditor.PointerPressed += GrdEditor_PointerPressed;
+ grdEditor.PointerReleased += GrdEditor_PointerReleased;
+ grdEditor.PointerExited += GrdEditor_PointerExited;
+
+ aspect.ViewAttributes = true;
+
+ sldZoom.PropertyChanged += SldZoom_PropertyChanged;
+ txtFrame.PropertyChanged += TxtFrame_PropertyChanged;
+
+ btnClear.Tapped += BtnClear_Tapped;
+ btnCut.Tapped += BtnCut_Tapped;
+ btnCopy.Tapped += BtnCopy_Tapped;
+ btnPaste.Tapped += BtnPaste_Tapped;
+ btnHMirror.Tapped += BtnHMirror_Tapped;
+ btnVMirror.Tapped += BtnVMirror_Tapped;
+ btnRotateLeft.Tapped += BtnRotateLeft_Tapped;
+ btnRotateRight.Tapped += BtnRotateRight_Tapped;
+ btnShiftUp.Tapped += BtnShiftUp_Tapped;
+ btnShiftRight.Tapped += BtnShiftRight_Tapped;
+ btnShiftDown.Tapped += BtnShiftDown_Tapped;
+ btnShiftLeft.Tapped += BtnShiftLeft_Tapped;
+ btnMoveUp.Tapped += BtnMoveUp_Tapped;
+ btnMoveRight.Tapped += BtnMoveRight_Tapped;
+ btnMoveDown.Tapped += BtnMoveDown_Tapped;
+ btnMoveLeft.Tapped += BtnMoveLeft_Tapped;
+ btnInvert.Tapped += BtnInvert_Tapped;
+ btnMask.Tapped += BtnMask_Tapped;
+ btnExport.Tapped += BtnExport_Tapped;
+ btnImport.Tapped += BtnImport_Tapped;
+
+ btnUndo.Tapped += BtnUndo_Tapped;
+ btnRedo.Tapped += BtnRedo_Tapped;
+
+ //btnViewAttributes.Tapped += BtnViewAttributes_Tapped;
+ //btnColorPicker.Tapped += BtnColorPicker_Tapped;
+ //btnInvertColorsCell.Tapped += BtnInvertColorsCell_Tapped;
+ //btnInvertPixelsCell.Tapped += BtnInvertPixelsCell_Tapped;
+
+ btnPaper.Tapped += BtnPaper_Click;
+ btnInk.Tapped += BtnInk_Tapped;
+ UpdateColorPanel();
+
+ }
+
+
+ ///
+ /// Initializes the control
+ ///
+ /// CallBack for commands: "REFRESH"
+ /// True if OK or False if error
+ public bool Initialize(Action callBackCommand)
+ {
+ this.CallBackCommand = callBackCommand;
+ return true;
+ }
+
+
+ ///
+ /// Refresh the draw area
+ ///
+ public void Refresh(bool callBack = false)
+ {
+ //cnvEditor.Children.Clear();
+ if (TileData == null)
+ {
+ aspect.Clear(Colors.White);
+ grdEditor.InvalidateVisual();
+ return;
+ }
+
+
+ aspect.RenderTile(TileData, TileData.CurrentFrame);
+ grdEditor.Zoom = _Zoom;
+ this.InvalidateVisual();
+
+ if (callBack)
+ {
+ CallBackCommand?.Invoke(this, "REFRESH");
+ }
+ }
+
+ #endregion
+
+
+ #region Undo and Redo
+
+ private List operations = new List();
+ private int operationIndex = -1;
+
+ public void Undo()
+ {
+ if (operationIndex > operations.Count - 1)
+ {
+ operationIndex = operations.Count - 1;
+ }
+ if (operationIndex < 0)
+ {
+ return;
+ }
+ var op = operations[operationIndex];
+ if (Pattern_Equals(TileData.Patterns[TileData.CurrentFrame], op))
+ {
+ operationIndex--;
+ Undo();
+ return;
+ }
+ if (op != null)
+ {
+ TileData.Patterns[TileData.CurrentFrame] = op;
+ Refresh();
+ }
+ CallBackCommand(this, "REFRESH");
+ }
+
+
+ public void Redo()
+ {
+ if (operationIndex > operations.Count - 1)
+ {
+ return;
+ }
+ if (operationIndex < 0)
+ {
+ return;
+ }
+ var op = operations[operationIndex];
+ if (Pattern_Equals(TileData.Patterns[TileData.CurrentFrame], op))
+ {
+ operationIndex++;
+ Redo();
+ return;
+ }
+ if (op != null)
+ {
+ TileData.Patterns[TileData.CurrentFrame] = op;
+ Refresh();
+ }
+ }
+
+
+ private void Undo_AddPoint()
+ {
+ try
+ {
+ if (TileData == null || TileData.Patterns == null)
+ {
+ operations.Clear();
+ return;
+ }
+
+ if (operationIndex >= 0 &&
+ operationIndex < operations.Count)
+ {
+ var lastOp = operations[operationIndex];
+ if (Pattern_Equals(TileData.Patterns[TileData.CurrentFrame], lastOp))
+ {
+ return; // No changes
+ }
+ }
+ operations = operations.Take(operationIndex + 1).ToList();
+
+ var op = TileData.Patterns[TileData.CurrentFrame].Clonar();
+ operations.Add(op);
+ operationIndex = operations.Count - 1;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine($"Error in Undo_AddPoint: {ex.Message}");
+ }
+ }
+
+ private bool Pattern_Equals(Pattern p1, Pattern p2)
+ {
+ if (p1 == null || p2 == null)
+ {
+ return false;
+ }
+ var pc1 = p1.Serializar();
+ var pc2 = p2.Serializar();
+ return pc1 == pc2;
+ }
+ #endregion
+
+
+ #region Drawing events
+
+ ///
+ /// Event for mouse pressed
+ ///
+ ///
+ ///
+ private void GrdEditor_PointerPressed(object? sender, Avalonia.Input.PointerPressedEventArgs e)
+ {
+ var p = e.GetCurrentPoint(grdEditor);
+
+ if (ColorPicker)
+ {
+ int x = (int)p.Position.X;
+ int y = (int)p.Position.Y;
+ x = x / (_Zoom + 1);
+ y = y / (_Zoom + 1);
+ var atr = GetAttribute(TileData.Patterns[TileData.CurrentFrame], x, y);
+ PrimaryColorIndex = atr.Ink;
+ SecondaryColorIndex = atr.Paper;
+ ColorPicker = false;
+ Refresh(true);
+ }
+ else if (InvertPixelsCell)
+ {
+ GrdEditor_InvertPixelsCell(p.Position.X, p.Position.Y);
+ }
+ else if (InvertColorsCell)
+ {
+ GrdEditor_InvertColorsCell(p.Position.X, p.Position.Y);
+ }
+ else
+ {
+ if (p.Properties.IsLeftButtonPressed)
+ {
+ SetPoint(p.Position.X, p.Position.Y, PrimaryColorIndex);
+ MouseLeftPressed = true;
+ MouseRightPressed = false;
+ }
+ else if (p.Properties.IsRightButtonPressed)
+ {
+ SetPoint(p.Position.X, p.Position.Y, SecondaryColorIndex);
+ MouseLeftPressed = false;
+ MouseRightPressed = true;
+ }
+ }
+ }
+
+
+ ///
+ /// Event for mouse released
+ ///
+ ///
+ ///
+ private void GrdEditor_PointerReleased(object? sender, Avalonia.Input.PointerReleasedEventArgs e)
+ {
+ MouseLeftPressed = false;
+ MouseRightPressed = false;
+ }
+
+
+ ///
+ /// Event for pointer moved outside control
+ ///
+ ///
+ ///
+ private void GrdEditor_PointerExited(object? sender, Avalonia.Input.PointerEventArgs e)
+ {
+ MouseLeftPressed = false;
+ MouseRightPressed = false;
+ }
+
+
+ ///
+ /// Event for pointer moved
+ ///
+ ///
+ ///
+ private void GrdEditor_PointerMoved(object? sender, Avalonia.Input.PointerEventArgs e)
+ {
+ var p = e.GetCurrentPoint(grdEditor);
+ if (MouseLeftPressed)
+ {
+ SetPoint(p.Position.X, p.Position.Y, PrimaryColorIndex);
+ }
+ else if (MouseRightPressed)
+ {
+ SetPoint(p.Position.X, p.Position.Y, SecondaryColorIndex);
+ }
+ }
+
+
+ ///
+ /// Set point to a color value
+ ///
+ /// Absolute x
+ /// Absolute y
+ /// Value of the point
+ private void SetPoint(double mx, double my, int value)
+ {
+ if (TileData == null)
+ {
+ return;
+ }
+
+ int x = (int)mx;
+ int y = (int)my;
+
+ x = x / (_Zoom + 1);
+ y = y / (_Zoom + 1);
+
+ if (x < 0 || y < 0 || x >= TileData.Width || y >= TileData.Height)
+ {
+ return;
+ }
+
+ int dir = (TileData.Width * y) + x;
+ var Tile = TileData.Patterns[TileData.CurrentFrame];
+
+ switch (TileData.GraphicMode)
+ {
+ case GraphicsModes.Monochrome:
+ Tile.RawData[dir] = value;
+ break;
+ case GraphicsModes.ZXSpectrum:
+ {
+ if (value == PrimaryColorIndex)
+ {
+ Tile.RawData[dir] = 1;
+ }
+ else
+ {
+ Tile.RawData[dir] = 0;
+ }
+ SetAttribute(Tile, x, y);
+ }
+ break;
+ }
+
+ Undo_AddPoint();
+ Refresh(false);
+
+ if (tmr == null)
+ {
+ tmr = new DispatcherTimer();
+ tmr.Tick += Tmr_Tick;
+ tmr.Interval = TimeSpan.FromMilliseconds(250);
+ }
+ tmr.Stop();
+ tmr.Start();
+ }
+
+
+ private void SetAttribute(Pattern pattern, int x, int y)
+ {
+ int cW = TileData.Width / 8;
+ int cX = x / 8;
+ int cY = y / 8;
+ var attr = pattern.Attributes[(cY * cW) + cX];
+ attr.Ink = PrimaryColorIndex;
+ attr.Paper = SecondaryColorIndex;
+ attr.Flash = false;
+ switch (TileData.GraphicMode)
+ {
+ case GraphicsModes.Monochrome:
+ attr.Bright = false;
+ break;
+ case GraphicsModes.ZXSpectrum:
+ if (PrimaryColorIndex > 7 || SecondaryColorIndex > 7)
+ {
+ attr.Bright = true;
+ }
+ else
+ {
+ attr.Bright = false;
+ }
+ break;
+ }
+ pattern.Attributes[(cY * cW) + cX] = attr;
+ }
+
+
+ private AttributeColor GetAttribute(Pattern pattern, int x, int y)
+ {
+ if (pattern.Attributes == null)
+ {
+ pattern.Attributes = new AttributeColor[(TileData.Width / 8) * (TileData.Height / 8)];
+ }
+ int cW = TileData.Width / 8;
+ int cX = x / 8;
+ int cY = y / 8;
+ return pattern.Attributes[(cY * cW) + cX];
+ }
+
+
+ private void Tmr_Tick(object? sender, EventArgs e)
+ {
+ Refresh(true);
+ tmr.Stop();
+ }
+
+ #endregion
+
+
+ #region Icon actions
+
+
+ ///
+ /// Clear Tile
+ ///
+ public void Clear()
+ {
+ if (TileData == null || TileData.Patterns == null ||
+ TileData.CurrentFrame >= (TileData.Patterns.Count) ||
+ TileData.Patterns[TileData.CurrentFrame].RawData == null)
+ {
+ return;
+ }
+ for (int n = 0; n < TileData.Patterns[TileData.CurrentFrame].RawData.Length; n++)
+ {
+ TileData.Patterns[TileData.CurrentFrame].RawData[n] = SecondaryColorIndex;
+ }
+ Undo_AddPoint();
+ Refresh(true);
+ }
+
+
+ ///
+ /// Cut patterns to clipboard
+ ///
+ public void Cut()
+ {
+ Undo_AddPoint();
+ Copy();
+ Clear();
+ }
+
+
+ ///
+ /// Copy patterns to clipboard
+ ///
+ public void Copy()
+ {
+ var patterns = new Pattern[1] { TileData.Patterns[TileData.CurrentFrame] };
+ TopLevel.GetTopLevel(this)?.Clipboard?.SetTextAsync(patterns.Serializar()).Wait();
+ }
+
+
+ ///
+ /// Paste patterns from clipboard
+ ///
+ public async void Paste()
+ {
+ var pattern = TileData.Patterns[TileData.CurrentFrame];
+ var cbData = await TopLevel.GetTopLevel(this).Clipboard.GetTextAsync();
+ if (string.IsNullOrEmpty(cbData))
+ {
+ return;
+ }
+ var cbPatterns = cbData.Deserializar();
+ if (cbPatterns == null)
+ {
+ return;
+ }
+
+ if (cbPatterns.Length == 1)
+ {
+ if (cbPatterns[0].RawData == null)
+ {
+ TileData.Patterns[TileData.CurrentFrame].RawData = ServiceLayer.PointData2RawData(cbPatterns[0].Data, 8, 8);
+ }
+ else
+ {
+ TileData.Patterns[TileData.CurrentFrame].RawData = cbPatterns[0].RawData;
+ TileData.Patterns[TileData.CurrentFrame].Attributes = cbPatterns[0].Attributes;
+ }
+ }
+ else
+ {
+ // Create an empty pattern
+ var pat2 = pattern.Clonar();
+ pat2.RawData = new int[TileData.Width * TileData.Height];
+
+ if (cbPatterns[0].RawData == null)
+ {
+ // Paste from PointData
+ int ox = 0;
+ int oy = 0;
+ int dir = 0;
+ for (int n = 0; n < cbPatterns.Length; n++)
+ {
+ //int d1 = (n / TileData.Width);
+ //int d2 = (d1 * TileData.Width) - n;
+ for (int py = 0; py < 8; py++)
+ {
+ for (int px = 0; px < 8; px++)
+ {
+ var po = cbPatterns[n].Data.FirstOrDefault(d => d.X == px && d.Y == py);
+ if (po != null)
+ {
+ dir = ((oy + py) * TileData.Width) + (ox + px);
+
+ if (dir < pattern.RawData.Length)
+ {
+ TileData.Patterns[TileData.CurrentFrame].RawData[dir] = po.ColorIndex;
+ //pattern.RawData[dir] = po.ColorIndex;
+ }
+ }
+ }
+ }
+ ox += 8;
+ if (ox >= TileData.Width)
+ {
+ ox = 0;
+ oy += 8;
+ }
+ }
+ //TileData.Patterns[TileData.CurrentFrame].Data = pat2.Data;
+ }
+ else
+ {
+ // Paste from RawData
+ var dat = TileData.Patterns[TileData.CurrentFrame].RawData;
+ var cbDat = cbPatterns[0].RawData;
+ for (int n = 0; n < cbDat.Length && n < dat.Length; n++)
+ {
+ dat[n] = cbDat[n];
+ }
+ }
+ }
+ Undo_AddPoint();
+ Refresh(true);
+ }
+
+
+ ///
+ /// Horizontal mirror of selected patterns
+ ///
+ public void HorizontalMirror()
+ {
+ int maxWidth = TileData.Width;
+ int maxHeight = TileData.Height;
+ var pat1 = TileData.Patterns[TileData.CurrentFrame];
+ var pat2 = pat1.Clonar();
+ for (int y = 0; y < maxHeight; y++)
+ {
+ for (int x = 0; x < maxWidth; x++)
+ {
+ var pd1 = GetPointValue(x, y, pat1);
+ SetPointValue(maxWidth - x - 1, y, pd1, ref pat2);
+ }
+ }
+ TileData.Patterns[TileData.CurrentFrame] = pat2;
+ Undo_AddPoint();
+ Refresh(true);
+ }
+
+
+ ///
+ /// Vertical mirror of selected patterns
+ ///
+ public void VerticalMirror()
+ {
+ int maxWidth = TileData.Width;
+ int maxHeight = TileData.Height;
+ var pat1 = TileData.Patterns[TileData.CurrentFrame];
+ var pat2 = pat1.Clonar();
+ for (int y = 0; y < maxHeight; y++)
+ {
+ for (int x = 0; x < maxWidth; x++)
+ {
+ var pd1 = GetPointValue(x, y, pat1);
+ SetPointValue(x, maxHeight - y - 1, pd1, ref pat2);
+ }
+ }
+ TileData.Patterns[TileData.CurrentFrame] = pat2;
+ Undo_AddPoint();
+ Refresh(true);
+ }
+
+
+ ///
+ /// Rotate left of selected patterns
+ ///
+ public void RotateLeft()
+ {
+ if (TileData.Width != TileData.Height)
+ {
+ Window.GetTopLevel(this)?.ShowError("Can't do this!", "Only square graphics can be rotated (with the same width as height).");
+ return;
+ }
+
+ var pattern = TileData.Patterns[TileData.CurrentFrame];
+ var pattern2 = pattern.Clonar();
+
+ int maxWidth = TileData.Width;
+ int maxHeight = TileData.Height;
+ for (int y = 0; y < maxHeight; y++)
+ {
+ for (int x = 0; x < maxWidth; x++)
+ {
+ var pd1 = GetPointValue(x, y, pattern);
+ SetPointValue(y, maxWidth - x - 1, pd1, ref pattern2);
+ }
+ }
+ TileData.Patterns[TileData.CurrentFrame] = pattern2;
+ Undo_AddPoint();
+ Refresh(true);
+ }
+
+
+ ///
+ /// Rotate right of selected patterns
+ ///
+ public void RotateRight()
+ {
+ if (TileData.Width != TileData.Height)
+ {
+ Window.GetTopLevel(this)?.ShowError("Can't do this!", "Only square graphics can be rotated (with the same width as height).");
+ return;
+ }
+
+ var pattern = TileData.Patterns[TileData.CurrentFrame];
+ var pattern2 = pattern.Clonar();
+
+ int maxWidth = TileData.Width;
+ int maxHeight = TileData.Height;
+ for (int y = 0; y < maxHeight; y++)
+ {
+ for (int x = 0; x < maxWidth; x++)
+ {
+ var pd1 = GetPointValue(x, y, pattern);
+ SetPointValue(maxHeight - y - 1, x, pd1, ref pattern2);
+ }
+ }
+ TileData.Patterns[TileData.CurrentFrame] = pattern2;
+ Undo_AddPoint();
+ Refresh(true);
+ }
+
+
+ ///
+ /// Move up and put the disappearing pixels at the bottom.
+ ///
+ public void ShiftUp()
+ {
+ var pattern = TileData.Patterns[TileData.CurrentFrame];
+ var pattern2 = pattern.Clonar();
+
+ int maxWidth = TileData.Width;
+ int maxHeight = TileData.Height;
+ for (int y = 0; y < maxHeight; y++)
+ {
+ for (int x = 0; x < maxWidth; x++)
+ {
+ var pd1 = GetPointValue(x, y, pattern);
+ int y2 = y - 1;
+ if (y2 < 0)
+ {
+ y2 = maxHeight - 1;
+ }
+ SetPointValue(x, y2, pd1, ref pattern2);
+ }
+ }
+ TileData.Patterns[TileData.CurrentFrame] = pattern2;
+ Undo_AddPoint();
+ Refresh(true);
+ }
+
+
+ ///
+ /// Move right and put the disappearing pixels at the left.
+ ///
+ public void ShiftRight()
+ {
+ var pattern = TileData.Patterns[TileData.CurrentFrame];
+ var pattern2 = pattern.Clonar();
+
+ int maxWidth = TileData.Width;
+ int maxHeight = TileData.Height;
+ for (int y = 0; y < maxHeight; y++)
+ {
+ for (int x = 0; x < maxWidth; x++)
+ {
+ var pd1 = GetPointValue(x, y, pattern);
+ int x2 = x + 1;
+ if (x2 >= maxWidth)
+ {
+ x2 = 0;
+ }
+ SetPointValue(x2, y, pd1, ref pattern2);
+ }
+ }
+ TileData.Patterns[TileData.CurrentFrame] = pattern2;
+ Undo_AddPoint();
+ Refresh(true);
+ }
+
+
+ ///
+ /// Move down and put the disappearing pixels at the top.
+ ///
+ public void ShiftDown()
+ {
+ var pattern = TileData.Patterns[TileData.CurrentFrame];
+ var pattern2 = pattern.Clonar();
+
+ int maxWidth = TileData.Width;
+ int maxHeight = TileData.Height;
+ for (int y = 0; y < maxHeight; y++)
+ {
+ for (int x = 0; x < maxWidth; x++)
+ {
+ var pd1 = GetPointValue(x, y, pattern);
+ int y2 = y + 1;
+ if (y2 >= maxHeight)
+ {
+ y2 = 0;
+ }
+ SetPointValue(x, y2, pd1, ref pattern2);
+ }
+ }
+ TileData.Patterns[TileData.CurrentFrame] = pattern2;
+ Undo_AddPoint();
+ Refresh(true);
+ }
+
+
+ ///
+ /// Move left and put the disappearing pixels at the right.
+ ///
+ public void ShiftLeft()
+ {
+ var pattern = TileData.Patterns[TileData.CurrentFrame];
+ var pattern2 = pattern.Clonar();
+
+ int maxWidth = TileData.Width;
+ int maxHeight = TileData.Height;
+ for (int y = 0; y < maxHeight; y++)
+ {
+ for (int x = 0; x < maxWidth; x++)
+ {
+ var pd1 = GetPointValue(x, y, pattern);
+ int x2 = x - 1;
+ if (x2 < 0)
+ {
+ x2 = maxWidth - 1;
+ }
+ SetPointValue(x2, y, pd1, ref pattern2);
+ }
+ }
+ TileData.Patterns[TileData.CurrentFrame] = pattern2;
+ Undo_AddPoint();
+ Refresh(true);
+ }
+
+
+ ///
+ /// Move up
+ ///
+ public void MoveUp()
+ {
+ var pattern = TileData.Patterns[TileData.CurrentFrame];
+ var pattern2 = pattern.Clonar();
+
+ int maxWidth = TileData.Width;
+ int maxHeight = TileData.Height;
+ for (int y = 0; y < maxHeight; y++)
+ {
+ for (int x = 0; x < maxWidth; x++)
+ {
+ var pd1 = GetPointValue(x, y, pattern);
+ int y2 = y - 1;
+ if (y2 < 0)
+ {
+ y2 = maxHeight - 1;
+ pd1 = 0;
+ }
+ SetPointValue(x, y2, pd1, ref pattern2);
+ }
+ }
+ TileData.Patterns[TileData.CurrentFrame] = pattern2;
+ Undo_AddPoint();
+ Refresh(true);
+ }
+
+
+ ///
+ /// Move rigth
+ ///
+ public void MoveRight()
+ {
+ var pattern = TileData.Patterns[TileData.CurrentFrame];
+ var pattern2 = pattern.Clonar();
+
+ int maxWidth = TileData.Width;
+ int maxHeight = TileData.Height;
+ for (int y = 0; y < maxHeight; y++)
+ {
+ for (int x = 0; x < maxWidth; x++)
+ {
+ var pd1 = GetPointValue(x, y, pattern);
+ int x2 = x + 1;
+ if (x2 >= maxWidth)
+ {
+ x2 = 0;
+ pd1 = 0;
+ }
+ SetPointValue(x2, y, pd1, ref pattern2);
+ }
+ }
+ TileData.Patterns[TileData.CurrentFrame] = pattern2;
+ Undo_AddPoint();
+ Refresh(true);
+ }
+
+
+ ///
+ /// Move down
+ ///
+ public void MoveDown()
+ {
+ var pattern = TileData.Patterns[TileData.CurrentFrame];
+ var pattern2 = pattern.Clonar();
+
+ int maxWidth = TileData.Width;
+ int maxHeight = TileData.Height;
+ for (int y = 0; y < maxHeight; y++)
+ {
+ for (int x = 0; x < maxWidth; x++)
+ {
+ var pd1 = GetPointValue(x, y, pattern);
+ int y2 = y + 1;
+ if (y2 >= maxHeight)
+ {
+ y2 = 0;
+ pd1 = 0;
+ }
+ SetPointValue(x, y2, pd1, ref pattern2);
+ }
+ }
+ TileData.Patterns[TileData.CurrentFrame] = pattern2;
+ Undo_AddPoint();
+ Refresh(true);
+ }
+
+
+ ///
+ /// Move left
+ ///
+ public void MoveLeft()
+ {
+ var pattern = TileData.Patterns[TileData.CurrentFrame];
+ var pattern2 = pattern.Clonar();
+
+ int maxWidth = TileData.Width;
+ int maxHeight = TileData.Height;
+ for (int y = 0; y < maxHeight; y++)
+ {
+ for (int x = 0; x < maxWidth; x++)
+ {
+ var pd1 = GetPointValue(x, y, pattern);
+ int x2 = x - 1;
+ if (x2 < 0)
+ {
+ x2 = maxWidth - 1;
+ pd1 = 0;
+ }
+ SetPointValue(x2, y, pd1, ref pattern2);
+ }
+ }
+ TileData.Patterns[TileData.CurrentFrame] = pattern2;
+ Undo_AddPoint();
+ Refresh(true);
+ }
+
+
+ ///
+ /// Invert pixels
+ ///
+ public void Invert()
+ {
+ var pattern = TileData.Patterns[TileData.CurrentFrame];
+ var pattern2 = pattern.Clonar();
+
+ int maxWidth = TileData.Width;
+ int maxHeight = TileData.Height;
+ for (int y = 0; y < maxHeight; y++)
+ {
+ for (int x = 0; x < maxWidth; x++)
+ {
+ var pd1 = GetPointValue(x, y, pattern);
+ if (pd1 == PrimaryColorIndex)
+ {
+ pd1 = SecondaryColorIndex;
+ }
+ else if (pd1 == SecondaryColorIndex)
+ {
+ pd1 = PrimaryColorIndex;
+ }
+ else
+ {
+
+ }
+ SetPointValue(x, y, pd1, ref pattern2);
+ }
+ }
+ TileData.Patterns[TileData.CurrentFrame] = pattern2;
+ Undo_AddPoint();
+ Refresh(true);
+ }
+
+
+ ///
+ /// Create a mask
+ ///
+ public void Mask()
+ {
+ var pattern = TileData.Patterns[TileData.CurrentFrame];
+ int maxX = (TileData.Width) - 1;
+ int maxY = (TileData.Height) - 1;
+
+ var pattern2 = new Pattern()
+ {
+ Id = pattern.Id,
+ Name = pattern.Name,
+ Number = pattern.Number,
+ RawData = new int[TileData.Width * TileData.Height]
+ };
+
+ _Mask(0, 0, ref pattern, ref pattern2);
+ _Mask(maxX, 0, ref pattern, ref pattern2);
+ _Mask(0, maxY, ref pattern, ref pattern2);
+ _Mask(maxX, maxY, ref pattern, ref pattern2);
+ TileData.Patterns[TileData.CurrentFrame] = pattern2;
+ Undo_AddPoint();
+ Refresh(true);
+ }
+
+
+ ///
+ /// Create a mask (fills pattern2 from pattern1 data) called recursively
+ ///
+ /// Coord x
+ /// Coord Y
+ /// Original patterns
+ /// Void pattens
+ private void _Mask(int x, int y, ref Pattern pattern1, ref Pattern pattern2)
+ {
+ var p1 = GetPointValue(x, y, pattern1);
+ if (p1 != 0)
+ {
+ return;
+ }
+ var p2 = GetPointValue(x, y, pattern2);
+ if (p2 != 0)
+ {
+ return;
+ }
+ p2 = 1;
+ SetPointValue(x, y, p2, ref pattern2);
+
+ if (x > 1)
+ {
+ _Mask(x - 1, y, ref pattern1, ref pattern2);
+ }
+ if (x < (TileData.Width) - 1)
+ {
+ _Mask(x + 1, y, ref pattern1, ref pattern2);
+ }
+ if (y > 1)
+ {
+ _Mask(x, y - 1, ref pattern1, ref pattern2);
+ }
+ if (y < (TileData.Height) - 1)
+ {
+ _Mask(x, y + 1, ref pattern1, ref pattern2);
+ }
+ }
+
+
+ ///
+ /// Get a point (pixel) from the selected pattern
+ ///
+ /// X coord
+ /// Y coord
+ /// Pattern to use
+ /// Point data or null if no data
+ private int GetPointValue(int x, int y, Pattern pattern)
+ {
+ if (x < 0 || y < 0 || x > (TileData.Width - 1) || y > (TileData.Height - 1))
+ {
+ return SecondaryColorIndex;
+ }
+ return pattern.RawData[(TileData.Width * y) + x];
+ }
+
+
+ ///
+ /// Set a point (pixel) from the selected pattern
+ ///
+ /// X coord
+ /// Y coord
+ /// Color index to set
+ /// Pattern to use
+ private void SetPointValue(int x, int y, int colorIndex, ref Pattern pattern)
+ {
+ if (x < 0 || y < 0 || x > (TileData.Width - 1) || y > (TileData.Height - 1))
+ {
+ return;
+ }
+ pattern.RawData[(TileData.Width * y) + x] = colorIndex;
+ }
+
+ #endregion
+
+
+ #region Invert pixels and colors
+
+ private void GrdEditor_InvertPixelsCell(double mx, double my)
+ {
+ if (TileData == null)
+ {
+ return;
+ }
+
+ int x = (int)mx;
+ int y = (int)my;
+
+ x = x / (_Zoom + 1);
+ y = y / (_Zoom + 1);
+
+ x = x / 8;
+ y = y / 8;
+
+ if (x < 0 || y < 0 || x >= TileData.Width || y >= TileData.Height)
+ {
+ return;
+ }
+
+ for (int py = 0; py < 8; py++)
+ {
+ for (int px = 0; px < 8; px++)
+ {
+ int dir = ((y * 8 + py) * TileData.Width) + (x * 8 + px);
+ if (dir < TileData.Patterns[TileData.CurrentFrame].RawData.Length)
+ {
+ var value = TileData.Patterns[TileData.CurrentFrame].RawData[dir];
+ if (value == PrimaryColorIndex)
+ {
+ TileData.Patterns[TileData.CurrentFrame].RawData[dir] = SecondaryColorIndex;
+ }
+ else if (value == SecondaryColorIndex)
+ {
+ TileData.Patterns[TileData.CurrentFrame].RawData[dir] = PrimaryColorIndex;
+ }
+ }
+ }
+ }
+ Refresh();
+ }
+
+ private void GrdEditor_InvertColorsCell(double mx, double my)
+ {
+ if (TileData == null)
+ {
+ return;
+ }
+ int x = (int)mx;
+ int y = (int)my;
+ x = x / (_Zoom + 1);
+ y = y / (_Zoom + 1);
+
+ var inkBak = PrimaryColorIndex;
+ var paperBak = SecondaryColorIndex;
+ var attr = GetAttribute(TileData.Patterns[TileData.CurrentFrame], x, y);
+ if (attr == null)
+ {
+ return;
+ }
+ PrimaryColorIndex = attr.Paper;
+ SecondaryColorIndex = attr.Ink;
+ SetAttribute(TileData.Patterns[TileData.CurrentFrame], x, y);
+ PrimaryColorIndex = inkBak;
+ SecondaryColorIndex = paperBak;
+ Refresh();
+ }
+ #endregion
+
+
+
+ #region Color
+
+ private void BtnPaper_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
+ {
+ if (ctrlColorPicker.IsVisible)
+ {
+ ctrlColorPicker.IsVisible = false;
+ return;
+ }
+ var Tile = TileData;
+ if (Tile.GraphicMode == GraphicsModes.Monochrome)
+ {
+ return;
+ }
+ ctrlColorPicker.IsVisible = true;
+ ctrlColorPicker.Inicialize(Tile.GraphicMode, Tile.Palette, SecondaryColorIndex, ColorPickerPaper_Action);
+ }
+
+
+ private void BtnInk_Tapped(object? sender, TappedEventArgs e)
+ {
+ if (ctrlColorPicker.IsVisible)
+ {
+ ctrlColorPicker.IsVisible = false;
+ return;
+ }
+ var Tile = TileData;
+ if (Tile.GraphicMode == GraphicsModes.Monochrome)
+ {
+ return;
+ }
+ ctrlColorPicker.IsVisible = true;
+ ctrlColorPicker.Inicialize(Tile.GraphicMode, Tile.Palette, PrimaryColorIndex, ColorPickerInk_Action);
+ }
+
+
+ public void UpdateColorPanel()
+ {
+ var Tile = TileData;
+ if (Tile == null)
+ {
+ return;
+ }
+ switch (Tile.GraphicMode)
+ {
+ case GraphicsModes.Monochrome:
+ {
+ var ink = Tile.Palette[1];
+ var paper = Tile.Palette[0];
+ grdPaper.Background = new SolidColorBrush(Color.FromRgb(paper.Red, paper.Green, paper.Blue));
+ txtPaper.Foreground = new SolidColorBrush(Color.FromRgb(ink.Red, ink.Green, ink.Blue));
+ txtPaper.Text = "0";
+ grdInk.Background = new SolidColorBrush(Color.FromRgb(ink.Red, ink.Green, ink.Blue));
+ txtInk.Foreground = new SolidColorBrush(Color.FromRgb(paper.Red, paper.Green, paper.Blue));
+ txtInk.Text = "1";
+ }
+ break;
+
+ case GraphicsModes.ZXSpectrum:
+ case GraphicsModes.Next:
+ {
+ var ink = Tile.Palette[PrimaryColorIndex];
+ var paper = Tile.Palette[SecondaryColorIndex];
+ grdPaper.Background = new SolidColorBrush(Color.FromRgb(paper.Red, paper.Green, paper.Blue));
+ txtPaper.Foreground = new SolidColorBrush(Color.FromRgb(ink.Red, ink.Green, ink.Blue));
+ txtPaper.Text = SecondaryColorIndex.ToString();
+ grdInk.Background = new SolidColorBrush(Color.FromRgb(ink.Red, ink.Green, ink.Blue));
+ txtInk.Foreground = new SolidColorBrush(Color.FromRgb(paper.Red, paper.Green, paper.Blue));
+ txtInk.Text = PrimaryColorIndex.ToString();
+ }
+ break;
+ }
+ }
+
+
+
+ private void ColorPickerPaper_Action(string command, int indexColor)
+ {
+ SecondaryColorIndex = indexColor;
+ UpdateColorPanel();
+ }
+
+
+ private void ColorPickerInk_Action(string command, int indexColor)
+ {
+ PrimaryColorIndex = indexColor;
+ UpdateColorPanel();
+ }
+
+ #endregion
+
+
+
+ #region Main editor
+
+
+ private void Refresh()
+ {
+ // TODO: Multiple frames per tile?
+ /*
+ txtFrame.Text = actualFrame.ToString();
+ if (ctrlProperties.TileData != null)
+ {
+ txtFrame.Maximum = ctrlProperties.TileData.Frames - 1;
+ }
+ else
+ {
+ txtFrame.Maximum = 0;
+ }
+ txtFrame.UpdateLayout();
+ */
+ }
+
+
+ ///
+ /// Zoom changed
+ ///
+ ///
+ ///
+ private void SldZoom_PropertyChanged(object? sender, Avalonia.AvaloniaPropertyChangedEventArgs e)
+ {
+ int z = (int)sldZoom.Value;
+ if (z == 0 || z == lastZoom)
+ {
+ return;
+ }
+ lastZoom = z;
+
+ z = zooms[z - 1];
+ txtZoom.Text = "Zoom " + z.ToString() + "x";
+ Zoom = z;
+ }
+
+
+ public void ZoomIn()
+ {
+ int v = sldZoom.Value.ToInteger();
+ if (v < zooms.Length - 1)
+ {
+ sldZoom.Value = v - 1;
+ }
+ }
+
+
+ public void ZoomOut()
+ {
+ int v = sldZoom.Value.ToInteger();
+ if (v > 0)
+ {
+ sldZoom.Value = v - 1;
+ }
+
+ }
+
+
+ private void TxtFrame_PropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
+ {
+ // TODO: Multiple frames per tile?
+ /*
+ byte v = txtFrame.Text.ToByte();
+ if (actualFrame == v ||
+ ctrlProperties.TileData == null ||
+ v < 0 ||
+ v >= (ctrlProperties.TileData.Frames))
+ {
+ return;
+ }
+ if (v > 255)
+ {
+ v = 255;
+ }
+ actualFrame = v;
+ if (TileData != null)
+ {
+ TileData.CurrentFrame = actualFrame;
+ Refresh();
+ }
+ Refresh();
+ */
+ }
+
+ #endregion
+
+
+ #region ToolBar
+
+ ///
+ /// Clear click
+ ///
+ ///
+ ///
+ private void BtnClear_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
+ {
+ this.Clear();
+ }
+
+ ///
+ /// Cut click
+ ///
+ ///
+ ///
+ private void BtnCut_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
+ {
+ this.Cut();
+ }
+
+
+ //Copy click
+ private void BtnCopy_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
+ {
+ this.Copy();
+ }
+
+
+ ///
+ /// Paste click
+ ///
+ ///
+ ///
+ private void BtnPaste_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
+ {
+ this.Paste();
+ }
+
+
+ ///
+ /// Horizontal mirror click
+ ///
+ ///
+ ///
+ private void BtnHMirror_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
+ {
+ this.HorizontalMirror();
+ }
+
+
+ ///
+ /// Vertical mirror click
+ ///
+ ///
+ ///
+ private void BtnVMirror_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
+ {
+ this.VerticalMirror();
+ }
+
+
+ ///
+ /// Rotate left
+ ///
+ ///
+ ///
+ private void BtnRotateLeft_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
+ {
+ this.RotateLeft();
+ }
+
+
+ ///
+ /// Rotate right
+ ///
+ ///
+ ///
+ private void BtnRotateRight_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
+ {
+ this.RotateRight();
+ }
+
+
+ ///
+ /// Shift up
+ ///
+ ///
+ ///
+ private void BtnShiftUp_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
+ {
+ this.ShiftUp();
+ }
+
+
+ ///
+ /// Shift right
+ ///
+ ///
+ ///
+ private void BtnShiftRight_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
+ {
+ this.ShiftRight();
+ }
+
+
+ ///
+ /// Shift down
+ ///
+ ///
+ ///
+ private void BtnShiftDown_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
+ {
+ this.ShiftDown();
+ }
+
+
+ ///
+ /// Shift left
+ ///
+ ///
+ ///
+ private void BtnShiftLeft_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
+ {
+ this.ShiftLeft();
+ }
+
+
+ ///
+ /// Move up
+ ///
+ ///
+ ///
+ private void BtnMoveUp_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
+ {
+ this.MoveUp();
+ }
+
+
+ ///
+ /// Move right
+ ///
+ ///
+ ///
+ private void BtnMoveRight_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
+ {
+ this.MoveRight();
+ }
+
+
+ ///
+ /// Move down
+ ///
+ ///
+ ///
+ private void BtnMoveDown_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
+ {
+ this.MoveDown();
+ }
+
+
+ ///
+ /// Move left
+ ///
+ ///
+ ///
+ private void BtnMoveLeft_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
+ {
+ this.MoveLeft();
+ }
+
+
+ ///
+ /// Invert
+ ///
+ ///
+ ///
+ private void BtnInvert_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
+ {
+ this.Invert();
+ }
+
+
+ ///
+ /// Mask
+ ///
+ ///
+ ///
+ private void BtnMask_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
+ {
+ this.Mask();
+ }
+
+
+ private void BtnExport_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
+ {
+ Export();
+ }
+
+
+ public void Export()
+ {
+ CallBackCommand(this, "EXPORT");
+ }
+
+
+ private void BtnImport_Tapped(object? sender, TappedEventArgs e)
+ {
+ CallBackCommand(this, "IMPORT");
+ }
+
+
+ private void BtnRedo_Tapped(object? sender, TappedEventArgs e)
+ {
+ Redo();
+ }
+
+
+ private void BtnUndo_Tapped(object? sender, TappedEventArgs e)
+ {
+ Undo();
+ }
+
+ #endregion
+
+ }
+}
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TilePreviewControl.axaml b/ZXBStudio/DocumentEditors/ZXMaps/TilePreviewControl.axaml
new file mode 100644
index 0000000..6ca3356
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/TilePreviewControl.axaml
@@ -0,0 +1,17 @@
+
+
+ .zxmap file
+
+ None
+
+
+
+
+
+
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TilePreviewControl.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/TilePreviewControl.axaml.cs
new file mode 100644
index 0000000..284ab8e
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/TilePreviewControl.axaml.cs
@@ -0,0 +1,143 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.Shapes;
+using Avalonia.Media;
+using Avalonia.Media.Imaging;
+using Avalonia.Threading;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Reflection.Metadata.Ecma335;
+using System.Runtime.InteropServices;
+using ZXBasicStudio.Common;
+using ZXBasicStudio.DocumentEditors.ZXGraphics.log;
+using ZXBasicStudio.DocumentEditors.ZXGraphics.neg;
+
+namespace ZXBasicStudio.DocumentEditors.ZXMaps
+{
+ public partial class TilePreviewControl : UserControl, IDisposable
+ {
+ #region Public properties
+
+ ///
+ /// Tile data
+ ///
+ public ZXMapsTile? TileData { get; set; }
+ public int Zoom { get; set; } = 4;
+
+ #endregion
+
+ #region Private fields
+
+ private int frameNumber = 0;
+ private int speed = 1;
+ private DispatcherTimer tmr;
+ private Color emptyColor = new Color(255, 0x28, 0x28, 0x28);
+ TileImage aspect = new TileImage();
+
+ ///
+ /// Speeds in milliseconds
+ ///
+ private int[] speeds = new int[] { 600000, 1000, 500, 250, 200, 125, 100, 66, 50, 20 };
+
+ #endregion
+
+ public TilePreviewControl()
+ {
+ tmr = new DispatcherTimer(TimeSpan.FromMilliseconds(speeds[2]), DispatcherPriority.Normal, Refresh);
+ tmr.Stop();
+ aspect.Clear(emptyColor);
+ InitializeComponent();
+ imgPreview.Source = aspect;
+ imgPreview.Width = aspect.Size.Width;
+ imgPreview.Height = aspect.Size.Height;
+ }
+
+ ///
+ /// Initializes the control
+ ///
+ /// Data of the Tile, if is null, the "Add" icon is visible and no properties are shown
+ /// CallBak for actions command, line "ADD", "CLONE", "DELETE" or "SELECTED"
+ ///
+ public bool Initialize(ZXMapsTile TileData)
+ {
+ this.TileData = TileData;
+
+ //this.cmbSpeed.SelectionChanged += CmbSpeed_SelectionChanged;
+ tmr.Interval = TimeSpan.FromMilliseconds(speeds[speed]);
+
+ return true;
+ }
+
+ public void Start()
+ {
+ tmr.Start();
+ }
+
+ public void Stop()
+ {
+ tmr.Stop();
+ }
+
+ //private void CmbSpeed_SelectionChanged(object? sender, SelectionChangedEventArgs e)
+ //{
+ //var s = cmbSpeed.SelectedIndex.ToInteger();
+ //if (s < 0 || s >= speeds.Length)
+ //{
+ // s = 2;
+ //}
+ //speed = s;
+ //tmr.Interval = TimeSpan.FromMilliseconds(speeds[speed]);
+ //}
+
+ public void Refresh(object? sender = null, EventArgs? e = null)
+ {
+ if (TileData == null || TileData.Patterns == null || TileData.Patterns.Count == 0)
+ {
+ // Delete background
+
+ if (aspect.IsEmpty)
+ return;
+
+ aspect.Clear(emptyColor);
+ imgPreview.Width = aspect.Size.Width;
+ imgPreview.Height = aspect.Size.Height;
+ imgPreview.InvalidateVisual();
+ return;
+ }
+
+ if (tmr == null)
+ {
+ tmr = new DispatcherTimer(TimeSpan.FromMilliseconds(speeds[speed]), DispatcherPriority.Normal, Refresh);
+ }
+
+ if (TileData.Masked)
+ {
+ frameNumber += 2;
+ }
+ else
+ {
+ frameNumber++;
+ }
+
+ if (frameNumber >= TileData.Frames)
+ {
+ frameNumber = 0;
+ }
+
+ imgPreview.Width = TileData.Width * Zoom;
+ imgPreview.Height = TileData.Height * Zoom;
+
+ aspect.RenderTile(TileData, frameNumber);
+ imgPreview.InvalidateVisual();
+ }
+
+ public void Dispose()
+ {
+ Stop();
+ aspect.Dispose();
+ imgPreview.Source = null;
+ }
+ }
+}
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TilePropertiesControl.axaml b/ZXBStudio/DocumentEditors/ZXMaps/TilePropertiesControl.axaml
new file mode 100644
index 0000000..8d2e570
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/TilePropertiesControl.axaml
@@ -0,0 +1,31 @@
+
+
+
+ Name:
+
+
+ Width:
+
+
+
+
+ Height:
+
+
+
+
+ Mode:
+
+ Monochrome
+ ZX Spectrum
+
+
+
+
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TilePropertiesControl.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/TilePropertiesControl.axaml.cs
new file mode 100644
index 0000000..7fca1e9
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/TilePropertiesControl.axaml.cs
@@ -0,0 +1,185 @@
+using Avalonia.Controls;
+using Avalonia.Controls.Shapes;
+using Avalonia.Media;
+using SkiaSharp;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Net;
+using ZXBasicStudio.Common;
+using ZXBasicStudio.DocumentEditors.ZXMaps.Neg;
+
+namespace ZXBasicStudio.DocumentEditors.ZXMaps
+{
+ public partial class TilePropertiesControl : UserControl
+ {
+ #region Public properties
+
+ ///
+ /// Tile data
+ ///
+ public ZXMapsTiles TileData
+ {
+ get
+ {
+ return _TileData;
+ }
+ set
+ {
+ _TileData = value;
+ IsSelected = true;
+ }
+ }
+
+ ///
+ /// CallBack for commands: "SIZE", "MODE", "NAME"
+ ///
+ public Action CallBackCommand { get; set; }
+
+ ///
+ /// True when then control is selected
+ ///
+ public bool IsSelected
+ {
+ get
+ {
+ return _IsSelected;
+ }
+ set
+ {
+ _IsSelected = value;
+ Refresh();
+ }
+ }
+
+
+ public int PrimaryColor = 1;
+ public int SecondaryColor = 0;
+
+ #endregion
+
+
+ #region Private fields
+
+ private bool _IsSelected = false;
+ private bool _SettingsChanged = false;
+ private bool refreshing = false;
+ private bool newTile = true;
+ private ZXMapsTiles _TileData = null;
+
+ #endregion
+
+
+ #region Constructor and public methods
+
+ public TilePropertiesControl()
+ {
+ InitializeComponent();
+ Refresh();
+ }
+
+
+ ///
+ /// Initializes the control
+ ///
+ /// Data of the Tile, if is null, the "Add" icon is visible and no properties are shown
+ /// CallBak for actions command, line "ADD", "CLONE", "DELETE" or "SELECTED"
+ ///
+ public bool Initialize(ZXMapsTiles TileData, Action callBackCommand)
+ {
+ this.TileData = TileData;
+ this.CallBackCommand = callBackCommand;
+
+ txtName.TextChanged += TxtName_TextChanged;
+ cmbMode.SelectionChanged += CmbMode_SelectionChanged;
+ txtWidth.ValueChanged += TxtWidth_ValueChanged;
+ txtHeight.ValueChanged += TxtHeight_ValueChanged;
+
+ _SettingsChanged = false;
+
+ return true;
+ }
+
+
+ public void Refresh()
+ {
+ if (refreshing)
+ {
+ return;
+ }
+ refreshing = true;
+
+ try
+ {
+ if (TileData == null)
+ {
+ pnlProperties.IsVisible = false;
+ return;
+ }
+ else
+ {
+ pnlProperties.IsVisible = true;
+ }
+
+ txtName.Text = TileData.Name;
+ txtWidth.Text = TileData.Width.ToStringNoNull();
+ txtHeight.Text = TileData.Height.ToStringNoNull();
+ cmbMode.SelectedIndex = (int)TileData.GraphicMode;
+
+ txtHeight.Value = TileData.Height;
+ txtName.Text = TileData.Name;
+ txtWidth.Value = TileData.Width;
+ cmbMode.SelectedIndex = (byte)TileData.GraphicMode;
+
+ }
+ catch { }
+ finally
+ {
+ refreshing = false;
+ }
+ }
+
+ #endregion
+
+
+ #region Private methods
+
+
+
+ #endregion
+
+
+ #region Button events
+
+ private void CmbMode_SelectionChanged(object? sender, SelectionChangedEventArgs e)
+ {
+ TileData.GraphicMode = (ZXGraphics.neg.GraphicsModes)cmbMode.SelectedIndex;
+ CallBackCommand(this, "MODE");
+ }
+
+
+ private void TxtWidth_ValueChanged(object? sender, NumericUpDownValueChangedEventArgs e)
+ {
+ TileData.Width = txtWidth.Value.ToInteger();
+ CallBackCommand(this, "SIZE");
+ }
+
+
+ private void TxtHeight_ValueChanged(object? sender, NumericUpDownValueChangedEventArgs e)
+ {
+ TileData.Height = txtHeight.Value.ToInteger();
+ CallBackCommand(this, "SIZE");
+ }
+
+
+ private void TxtName_TextChanged(object? sender, TextChangedEventArgs e)
+ {
+ TileData.Name = txtName.Text.ToStringNoNull();
+ CallBackCommand(this, "NAME");
+ }
+
+ #endregion
+
+ }
+}
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/ZXMapsEditor.axaml b/ZXBStudio/DocumentEditors/ZXMaps/ZXMapsEditor.axaml
new file mode 100644
index 0000000..7944d40
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/ZXMapsEditor.axaml
@@ -0,0 +1,158 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Open .zxtil and use tile editor to edit map
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Zoom 24x
+
+ Width
+
+
+
+ Height
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Preview
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ZXBStudio/DocumentEditors/ZXMaps/ZXMapsEditor.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/ZXMapsEditor.axaml.cs
new file mode 100644
index 0000000..02019ce
--- /dev/null
+++ b/ZXBStudio/DocumentEditors/ZXMaps/ZXMapsEditor.axaml.cs
@@ -0,0 +1,330 @@
+using Avalonia;
+using Avalonia.Controls;
+//using Avalonia.Controls.Shapes;
+using Avalonia.Media;
+using Avalonia.Threading;
+using AvaloniaEdit.Folding;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using ZXBasicStudio.Common;
+using ZXBasicStudio.Dialogs;
+using ZXBasicStudio.DocumentEditors.NextDows.neg;
+using ZXBasicStudio.DocumentEditors.ZXGraphics;
+using ZXBasicStudio.DocumentEditors.ZXGraphics.neg;
+using ZXBasicStudio.DocumentEditors.ZXMaps.Log;
+using ZXBasicStudio.DocumentEditors.ZXMaps.Neg;
+using ZXBasicStudio.DocumentEditors.ZXTextEditor.Classes.Folding;
+using ZXBasicStudio.DocumentModel.Classes;
+
+namespace ZXBasicStudio.DocumentEditors.ZXMaps
+{
+ public partial class ZXMapsEditor : ZXDocumentEditorBase//, IObserver
+ {
+ #region Events
+
+ public override event EventHandler? DocumentRestored;
+ public override event EventHandler? DocumentModified;
+ public override event EventHandler? DocumentSaved;
+ public override event EventHandler? RequestSaveDocument;
+
+ #endregion
+
+
+ #region ZXDocumentBase properties
+
+ public override string DocumentName
+ {
+ get
+ {
+ return Path.GetFileName(FileName);
+ }
+ }
+
+
+ public override string DocumentPath
+ {
+ get
+ {
+ return Path.GetFullPath(FileName);
+ }
+ }
+
+ public override bool Modified
+ {
+ get
+ {
+ return _Modified;
+ }
+ }
+
+ private bool _Modified = false;
+
+ protected virtual AbstractFoldingStrategy? foldingStrategy { get { return null; } }
+
+ private Guid documentTypeId = Guid.Empty;
+
+ #endregion
+
+
+ #region IObserver implementation for modified document notifications
+
+ public void OnCompleted()
+ {
+
+ }
+
+ public void OnError(Exception error)
+ {
+
+ }
+
+ public void OnNext(AvaloniaPropertyChangedEventArgs value)
+ {
+
+ }
+
+ #endregion
+
+
+ #region Private variables
+
+ private string FileName = "";
+
+ ///
+ /// List of sprites patterns controls in the set
+ ///
+ private List SpritePatternsList = null;
+
+ ///
+ /// last zoom value
+ ///
+ private int lastZoom = 0;
+
+ ///
+ /// Zooms values
+ ///
+ private int[] zooms = new int[]
+ {
+ 1,2,4,8,16,24,32,48,64
+ };
+
+ private byte actualFrame = 0;
+
+ private FoldingManager? fManager;
+ private DispatcherTimer? updateFoldingsTimer;
+
+ #endregion
+
+
+ #region ZXDocumentBase functions
+
+ public override bool SaveDocument(TextWriter OutputLog)
+ {
+ try
+ {
+ var json = Map.Serializar();
+ File.WriteAllText(FileName, json);
+ _Modified = false;
+ DocumentSaved?.Invoke(this, EventArgs.Empty);
+ return true;
+ }
+ catch (Exception ex)
+ {
+ OutputLog.WriteLine($"Error saving file {FileName}: {ex.Message}");
+ return false;
+ }
+ }
+
+
+ public override bool RenameDocument(string NewName, TextWriter OutputLog)
+ {
+ Map.Name = Path.GetFileNameWithoutExtension(NewName);
+ FileName = NewName;
+ return true;
+ }
+
+
+ public override bool CloseDocument(TextWriter OutputLog, bool ForceClose)
+ {
+ return true;
+ }
+
+
+ public override void Dispose()
+ {
+ }
+
+
+ public override void Activated()
+ {
+ this.Focus();
+ //ctrlPreview.Start();
+ base.Activated();
+ }
+
+
+ public override void Deactivated()
+ {
+ //ctrlPreview.Stop();
+ base.Deactivated();
+ }
+
+ #endregion
+
+
+
+
+
+ public List Controls { get; internal set; }
+
+ public int WindowWidth { get; set; }
+ public int WindowHeight { get; set; }
+
+
+ ///
+ /// Zoom level of the editor
+ ///
+ public int Zoom { get; set; }
+
+
+ ///
+ /// Defines is grid is visible
+ ///
+ public bool IsGridOn { get; set; }
+
+
+ private ControlItem Viewport = null;
+ //private PaletteColor[] Palette=null;
+
+
+ private ZXMapsMap Map = null;
+
+
+ public ZXMapsEditor(string fileName)
+ {
+ FileName = fileName;
+ if (File.Exists(FileName))
+ {
+ try
+ {
+ var fileData = File.ReadAllText(FileName);
+ if (!string.IsNullOrEmpty(fileData))
+ {
+ Map = fileData.Deserializar();
+ }
+ }
+ catch (Exception ex)
+ {
+ //MessageBox.Show($"Error loading file {FileName}: {ex.Message}");
+ }
+ }
+
+ if (Map == null)
+ {
+ Map = new ZXMapsMap();
+ Map.Name = Path.GetFileNameWithoutExtension(FileName);
+ Map.Height = 12;
+ Map.Width=16;
+ Map.MappingType = ZXMapsMappingTypes.Sequential;
+ Map.MapType = ZXMapsTypes.Rooms;
+ Map.TileHeight = 16;
+ Map.TileWidth = 16;
+
+ Map.Layers = new List();
+ Map.Layers.Add(new ZXMapsLayer()
+ {
+ Id = 0,
+ LayersType = ZXMapsLayersType.Tiles,
+ Name = "Default layer",
+ Order = 0,
+ Selected = true,
+ Visible = true
+ });
+
+ Map.PropertyDefinitons = new List();
+ ServiceLayer_Maps.Maps_SaveMap(FileName, Map);
+ }
+
+ InitializeComponent();
+
+ ctrlLayers.Initialize(Map.Layers);
+ ctrlProperties.Initialize(Map,null,MapProperties_Command);
+ }
+
+ private void MapProperties_Command(MapPropertiesControl control, string arg2)
+ {
+ //throw new NotImplementedException();
+ }
+
+ public bool Initialize()
+ {
+ Controls = new List();
+ //Palette = log.ServiceLayer.CrateNextDefaultPalette();
+ Zoom = 4;
+ WindowWidth = 300;
+ WindowHeight = 226;
+ Refresh();
+
+ return true;
+ }
+
+
+
+
+ ///
+ /// Refresh the editor UI
+ ///
+ public void Refresh()
+ {
+ //cnvEditor.Children.Clear();
+ Viewport = Controls.FirstOrDefault(d => d.Id == 0);
+ if (Viewport == null)
+ {
+ Viewport = new ControlItem()
+ {
+ ContainerId = 0,
+ ControlType = ControlsTypes.Panel,
+ Height = WindowHeight,
+ Id = 0,
+ Ink = 0,
+ Left = 0,
+ Name = "Main panel",
+ Paper = 255,
+ Top = 0,
+ Visible = true,
+ Width = WindowWidth,
+ };
+ Viewport.Properties = new List();
+ Controls.Add(Viewport);
+ }
+
+ foreach (var control in Controls.OrderBy(d => d.Id))
+ {
+ switch (control.ControlType)
+ {
+ case ControlsTypes.Panel:
+ Draw_Panel(control);
+ break;
+ }
+ }
+
+ //cnvEditor.Width = WindowWidth * Zoom;
+ //cnvEditor.Height = WindowHeight * Zoom;
+ }
+
+
+ public void Draw_Panel(ControlItem control)
+ {
+ //var r = new Rectangle();
+ //r.Width = Zoom * control.Width;
+ //r.Height = Zoom *control.Height;
+ //r.Stroke = Palette[control.Paper].Brush;
+ //r.Fill = Palette[control.Paper].Brush;
+ ////cnvEditor.Children.Add(r);
+ //Canvas.SetTop(r, control.Top*Zoom);
+ //Canvas.SetLeft(r, control.Left*Zoom);
+ }
+ }
+}
diff --git a/ZXBStudio/DocumentModel/Classes/ZXDocumentProvider.cs b/ZXBStudio/DocumentModel/Classes/ZXDocumentProvider.cs
index 872318b..b98cd18 100644
--- a/ZXBStudio/DocumentModel/Classes/ZXDocumentProvider.cs
+++ b/ZXBStudio/DocumentModel/Classes/ZXDocumentProvider.cs
@@ -13,6 +13,7 @@
using ZXBasicStudio.IntegratedDocumentTypes.NextDows;
using ZXBasicStudio.IntegratedDocumentTypes.TapeDocuments.ZXTapeBuilder;
using ZXBasicStudio.IntegratedDocumentTypes.Resources.ZXRamDisk;
+using ZXBasicStudio.IntegratedDocumentTypes.ZXMaps;
namespace ZXBasicStudio.DocumentModel.Classes
{
@@ -36,6 +37,8 @@ static ZXDocumentProvider()
_docTypes.Add(new UDGDocument());
_docTypes.Add(new FontDocument());
_docTypes.Add(new SpriteDocument());
+ _docTypes.Add(new ZXMapsDocument());
+ _docTypes.Add(new ZXTilemapDocument());
// NextDows
//_docTypes.Add(new ZXFormsDocument());
//TODO: Load external document types
diff --git a/ZXBStudio/IntegratedDocumentTypes/ZXMaps/ZXMapsDocument.cs b/ZXBStudio/IntegratedDocumentTypes/ZXMaps/ZXMapsDocument.cs
new file mode 100644
index 0000000..8ff093d
--- /dev/null
+++ b/ZXBStudio/IntegratedDocumentTypes/ZXMaps/ZXMapsDocument.cs
@@ -0,0 +1,72 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Media.Imaging;
+using Avalonia.Platform;
+using Avalonia.Svg.Skia;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using ZXBasicStudio.Classes;
+using ZXBasicStudio.DocumentEditors.NextDows.log;
+using ZXBasicStudio.DocumentModel.Enums;
+using ZXBasicStudio.DocumentModel.Interfaces;
+
+namespace ZXBasicStudio.IntegratedDocumentTypes.ZXMaps
+{
+ public class ZXMapsDocument : IZXDocumentType
+ {
+ static readonly string[] _docExtensions = { ".zxmap" };
+ static readonly string _docName = "ZXMaps file";
+ static readonly string _docDesc = "ZXMaps allows you to create different types of maps using tiles generated by the ZXGraphics sprite and tile editor.";
+ static readonly string _docCat = "Graphics";
+ static readonly string _docAspect = "/Svg/Documents/file-map.svg";
+ static readonly Guid _docId = Guid.Parse("2e2cdf61-a43e-44ef-99ac-172a724a3e62");
+
+ static readonly ZXMapsDocumentFactory _factory = new ZXMapsDocumentFactory();
+ Bitmap? _icon;
+
+ public string[] DocumentExtensions => _docExtensions;
+ public string DocumentName => _docName;
+ public string DocumentDescription => _docDesc;
+ public string DocumentCategory => _docCat;
+ public string? DocumentAspect => _docAspect;
+
+ private static readonly ExportManager _exportManager = new ExportManager();
+
+
+ public ZXMapsDocument()
+ {
+ _exportManager.Initialize(DocumentEditors.ZXGraphics.neg.FileTypes.Map);
+ }
+
+
+ public Bitmap DocumentIcon
+ {
+ get
+ {
+ if (_icon == null)
+ {
+ _icon = new Bitmap(AssetLoader.Open(new Uri("avares://ZXBasicStudio/Assets/zxmaps_map.png")));
+ }
+
+ return _icon;
+ }
+ }
+
+ public bool CanCreate => true;
+
+ public bool CanEdit => true;
+
+ public IZXDocumentFactory DocumentFactory => _factory;
+
+ public IZXDocumentBuilder? DocumentBuilder => _exportManager;
+
+ public ZXBuildStage? DocumentBuildStage => ZXBuildStage.PreBuild;
+
+ Guid IZXDocumentType.DocumentTypeId => _docId;
+
+ ZXKeybCommand[]? IZXDocumentType.EditorCommands => new ZXKeybCommand[0];
+ }
+}
diff --git a/ZXBStudio/IntegratedDocumentTypes/ZXMaps/ZXMapsDocumentFactory.cs b/ZXBStudio/IntegratedDocumentTypes/ZXMaps/ZXMapsDocumentFactory.cs
new file mode 100644
index 0000000..fb3635f
--- /dev/null
+++ b/ZXBStudio/IntegratedDocumentTypes/ZXMaps/ZXMapsDocumentFactory.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using ZXBasicStudio.DocumentEditors.ZXMaps;
+using ZXBasicStudio.DocumentModel.Classes;
+using ZXBasicStudio.DocumentModel.Interfaces;
+
+namespace ZXBasicStudio.IntegratedDocumentTypes.ZXMaps
+{
+ public class ZXMapsDocumentFactory : IZXDocumentFactory
+ {
+ public bool CreateDocument(string Path, TextWriter OutputLog)
+ {
+ var type = ZXDocumentProvider.GetDocumentType(Path);
+
+ if (type is not ZXMapsDocument)
+ {
+ OutputLog.WriteLine($"Document {Path} is not an ZXMaps file, internal document handling error, operation aborted.");
+ return false;
+ }
+
+ if (File.Exists(Path))
+ {
+ OutputLog.WriteLine($"File {Path} already exists, cannot create.");
+ return false;
+ }
+
+ try
+ {
+ File.Create(Path).Dispose();
+ return true;
+ }
+ catch (Exception ex)
+ {
+ OutputLog.WriteLine($"Error creating file {Path}: {ex.Message}");
+ return false;
+ }
+ }
+
+ public ZXDocumentEditorBase? CreateEditor(string Path, TextWriter OutputLog)
+ {
+ var type = ZXDocumentProvider.GetDocumentType(Path);
+
+ if (type is not ZXMapsDocument)
+ {
+ OutputLog.WriteLine($"Document {Path} is not an ZXMaps file, internal document handling error, operation aborted.");
+ return null;
+ }
+ ZXMapsEditor editor = new ZXMapsEditor(Path);
+ return editor;
+ }
+ }
+}
diff --git a/ZXBStudio/IntegratedDocumentTypes/ZXMaps/ZXTilemapDocument.cs b/ZXBStudio/IntegratedDocumentTypes/ZXMaps/ZXTilemapDocument.cs
new file mode 100644
index 0000000..c01f3e2
--- /dev/null
+++ b/ZXBStudio/IntegratedDocumentTypes/ZXMaps/ZXTilemapDocument.cs
@@ -0,0 +1,102 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Media.Imaging;
+using Avalonia.Platform;
+using Avalonia.Svg.Skia;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using ZXBasicStudio.Classes;
+using ZXBasicStudio.DocumentEditors.ZXGraphics;
+using ZXBasicStudio.DocumentEditors.ZXGraphics.log;
+using ZXBasicStudio.DocumentModel.Enums;
+using ZXBasicStudio.DocumentModel.Interfaces;
+
+namespace ZXBasicStudio.IntegratedDocumentTypes.ZXGraphics
+{
+ public class ZXTilemapDocument : IZXDocumentType
+ {
+ static readonly string[] _docExtensions = { ".zxtil" };
+ static readonly string _docName = "Tilemap file";
+ static readonly string _docDesc = "Tilemap files allow you to create and modify a set of tiles for use in maps. Once created, it can be used in the zxmap built-in editor.";
+ static readonly string _docCat = "Graphics";
+ static readonly string _docAspect = "/Svg/Documents/file-tile.svg";
+ static readonly Guid _docId = Guid.Parse("baed2950-b902-44dd-aac6-4912b533684f");
+ public static Guid Id => _docId;
+
+ static readonly ZXTilemapDocumentFactory _factory = new ZXTilemapDocumentFactory();
+ Bitmap? _icon;
+
+ public string[] DocumentExtensions => _docExtensions;
+ public string DocumentName => _docName;
+ public string DocumentDescription => _docDesc;
+ public string DocumentCategory => _docCat;
+ public string? DocumentAspect => _docAspect;
+
+ private static readonly ExportManager _exportManager = new ExportManager();
+
+ static readonly ZXKeybCommand[] _editCommands = new ZXKeybCommand[]
+ {
+ SpriteEditor.keyboardCommands["Save"],
+ SpriteEditor.keyboardCommands["Cut"],
+ SpriteEditor.keyboardCommands["Copy"],
+ SpriteEditor.keyboardCommands["Paste"],
+ SpriteEditor.keyboardCommands["Clear"],
+ SpriteEditor.keyboardCommands["Undo"],
+ SpriteEditor.keyboardCommands["Redo"],
+ SpriteEditor.keyboardCommands["Rotate Right"],
+ SpriteEditor.keyboardCommands["Rotate Left"],
+ SpriteEditor.keyboardCommands["Horizontal Mirror"],
+ SpriteEditor.keyboardCommands["Vertical Mirror"],
+ SpriteEditor.keyboardCommands["Shift Up"],
+ SpriteEditor.keyboardCommands["Shift Right"],
+ SpriteEditor.keyboardCommands["Shift Down"],
+ SpriteEditor.keyboardCommands["Shift Left"],
+ SpriteEditor.keyboardCommands["Move Up"],
+ SpriteEditor.keyboardCommands["Move Right"],
+ SpriteEditor.keyboardCommands["Move Down"],
+ SpriteEditor.keyboardCommands["Move Left"],
+ SpriteEditor.keyboardCommands["Invert"],
+ SpriteEditor.keyboardCommands["Mask"],
+ SpriteEditor.keyboardCommands["Export"],
+ SpriteEditor.keyboardCommands["Zoom In"],
+ SpriteEditor.keyboardCommands["Zoom Out"],
+ };
+
+
+ public ZXTilemapDocument()
+ {
+ _exportManager.Initialize(DocumentEditors.ZXGraphics.neg.FileTypes.Sprite);
+ }
+
+
+ public Bitmap DocumentIcon
+ {
+ get
+ {
+ if (_icon == null)
+ {
+ _icon = new Bitmap(AssetLoader.Open(new Uri("avares://ZXBasicStudio/Assets/zxmaps_til.png")));
+ }
+
+ return _icon;
+ }
+ }
+
+ public bool CanCreate => true;
+
+ public bool CanEdit => true;
+
+ public IZXDocumentFactory DocumentFactory => _factory;
+
+ public IZXDocumentBuilder? DocumentBuilder => _exportManager;
+
+ public ZXBuildStage? DocumentBuildStage => ZXBuildStage.PreBuild;
+
+ Guid IZXDocumentType.DocumentTypeId => _docId;
+
+ ZXKeybCommand[]? IZXDocumentType.EditorCommands => _editCommands;
+ }
+}
diff --git a/ZXBStudio/IntegratedDocumentTypes/ZXMaps/ZXTilemapDocumentFactory.cs b/ZXBStudio/IntegratedDocumentTypes/ZXMaps/ZXTilemapDocumentFactory.cs
new file mode 100644
index 0000000..81356da
--- /dev/null
+++ b/ZXBStudio/IntegratedDocumentTypes/ZXMaps/ZXTilemapDocumentFactory.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using ZXBasicStudio.DocumentEditors.ZXGraphics;
+using ZXBasicStudio.DocumentEditors.ZXMaps;
+using ZXBasicStudio.DocumentEditors.ZXTextEditor.Controls;
+using ZXBasicStudio.DocumentModel.Classes;
+using ZXBasicStudio.DocumentModel.Interfaces;
+
+namespace ZXBasicStudio.IntegratedDocumentTypes.ZXGraphics
+{
+ public class ZXTilemapDocumentFactory : IZXDocumentFactory
+ {
+ public bool CreateDocument(string Path, TextWriter OutputLog)
+ {
+ var type = ZXDocumentProvider.GetDocumentType(Path);
+
+ if (type is not ZXTilemapDocument)
+ {
+ OutputLog.WriteLine($"Document {Path} is not a tilemap file, internal document handling error, operation aborted.");
+ return false;
+ }
+
+ if (File.Exists(Path))
+ {
+ OutputLog.WriteLine($"File {Path} already exists, cannot create.");
+ return false;
+ }
+
+ try
+ {
+ File.Create(Path).Dispose();
+ return true;
+ }
+ catch (Exception ex)
+ {
+ OutputLog.WriteLine($"Error creating file {Path}: {ex.Message}");
+ return false;
+ }
+ }
+
+ public ZXDocumentEditorBase? CreateEditor(string Path, TextWriter OutputLog)
+ {
+ var type = ZXDocumentProvider.GetDocumentType(Path);
+
+ if (type is not ZXTilemapDocument)
+ {
+ OutputLog.WriteLine($"Document {Path} is not a tile file, internal document handling error, operation aborted.");
+ return null;
+ }
+ TileEditor editor = new TileEditor(Path);
+ return editor;
+ }
+ }
+}
diff --git a/ZXBStudio/MainWindow.axaml.cs b/ZXBStudio/MainWindow.axaml.cs
index 9b9a918..2fa9637 100644
--- a/ZXBStudio/MainWindow.axaml.cs
+++ b/ZXBStudio/MainWindow.axaml.cs
@@ -1485,7 +1485,7 @@ private async void BuildAndRun(object? sender, Avalonia.Interactivity.RoutedEven
try
{
var emulatorName = Path.GetFileNameWithoutExtension(emulatorPath);
- if (emulatorName.ToLower()=="apprun")
+ if (emulatorName.ToLower() == "apprun")
{
emulatorName = "mame";
}
@@ -2532,19 +2532,19 @@ private string BuildAndRun_MAME(string emulatorPath, string nextDrive, ZXBuildSe
// emulatorPath = emulatorPath.Replace("/AppRun", "");
//}
- var basePath =Directory.GetParent(Directory.GetParent(emulatorPath).FullName).FullName;
- var sdimagePath= Path.Combine(basePath, "nextsdimage", "cspect-next-2gb.img");
+ var basePath = Directory.GetParent(Directory.GetParent(emulatorPath).FullName).FullName;
+ var sdimagePath = Path.Combine(basePath, "nextsdimage", "cspect-next-2gb.img");
// Delete /nextzxos/autoexec.1st
Hdfmonkey($"rm \"{sdimagePath}\" /nextzxos/autoexec.1st", basePath);
// Create autoexec.bas
- var nextPath =Path.GetFileNameWithoutExtension(project.GetMainFile());
+ var nextPath = Path.GetFileNameWithoutExtension(project.GetMainFile());
string autoexec = $@"#autostart 10
10 CD ""{nextPath}""
20 .nexload {nextPath}.nex
";
- var bytes=Common.Txt2Bas.Txt2BasConverter.Text2Bas(autoexec);
+ var bytes = Common.Txt2Bas.Txt2BasConverter.Text2Bas(autoexec);
var autoexecPath = Path.Combine(basePath, "hdfmonkey", "autoexec.bas");
File.WriteAllBytes(autoexecPath, bytes);
@@ -2607,17 +2607,17 @@ 20 .nexload {nextPath}.nex
private bool Hdfmonkey(string parameters, string basePath)
{
var hdfPath = Path.Combine(basePath, "hdfmonkey");
+ string hdfmonkeyExe = Path.Combine(hdfPath, "hdfmonkey.exe");
- string hdfmonkeyExe = "hdfmonkey.exe";
- if (!OperatingSystem.IsWindows())
+ if (!File.Exists(hdfmonkeyExe))
{
- hdfmonkeyExe = "hdfmonkey";
+ hdfmonkeyExe = Path.Combine(hdfPath, "hdfmonkey");
}
outLog.Writer.WriteLine($"> hdfmonkey {parameters}");
var psi = new ProcessStartInfo()
{
- FileName = Path.Combine(hdfPath, hdfmonkeyExe),
+ FileName = hdfmonkeyExe,
Arguments = parameters,
WorkingDirectory = hdfPath,
UseShellExecute = false,
diff --git a/ZXBStudio/Program.cs b/ZXBStudio/Program.cs
index a25ee63..c24d3c3 100644
--- a/ZXBStudio/Program.cs
+++ b/ZXBStudio/Program.cs
@@ -42,6 +42,8 @@ public static void Main(string[] args)
{
SetVersion();
+ //var id= Guid.NewGuid();
+
if (args.Contains("--version"))
{
Console.WriteLine($"{Version}");
diff --git a/ZXBStudio/Svg/Documents/file-blank.svg b/ZXBStudio/Svg/Documents/file-blank.svg
new file mode 100644
index 0000000..e5703ec
--- /dev/null
+++ b/ZXBStudio/Svg/Documents/file-blank.svg
@@ -0,0 +1,43 @@
+
+
diff --git a/ZXBStudio/Svg/Documents/file-map.svg b/ZXBStudio/Svg/Documents/file-map.svg
index e5703ec..030552e 100644
--- a/ZXBStudio/Svg/Documents/file-map.svg
+++ b/ZXBStudio/Svg/Documents/file-map.svg
@@ -3,8 +3,8 @@
viewBox="0 0 384 512"
version="1.1"
id="svg12"
- sodipodi:docname="file-zx-tape.svg"
- inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
+ sodipodi:docname="file-map.svg"
+ inkscape:version="1.4 (86a8ad7, 2024-10-11)"
xml:space="preserve"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
@@ -20,24 +20,21 @@
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
- inkscape:zoom="1.4183568"
- inkscape:cx="374.02436"
- inkscape:cy="333.83701"
- inkscape:window-width="1841"
- inkscape:window-height="1270"
- inkscape:window-x="951"
- inkscape:window-y="453"
+ inkscape:zoom="0.50146486"
+ inkscape:cx="143.57935"
+ inkscape:cy="307.10028"
+ inkscape:window-width="1424"
+ inkscape:window-height="820"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg12" />
+ style="fill:#ffffff" />
diff --git a/ZXBStudio/Svg/Documents/file-tile.svg b/ZXBStudio/Svg/Documents/file-tile.svg
index e5703ec..a123629 100644
--- a/ZXBStudio/Svg/Documents/file-tile.svg
+++ b/ZXBStudio/Svg/Documents/file-tile.svg
@@ -3,8 +3,8 @@
viewBox="0 0 384 512"
version="1.1"
id="svg12"
- sodipodi:docname="file-zx-tape.svg"
- inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
+ sodipodi:docname="file-tile.svg"
+ inkscape:version="1.4 (86a8ad7, 2024-10-11)"
xml:space="preserve"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
@@ -20,24 +20,39 @@
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
- inkscape:zoom="1.4183568"
- inkscape:cx="374.02436"
- inkscape:cy="333.83701"
- inkscape:window-width="1841"
- inkscape:window-height="1270"
- inkscape:window-x="951"
- inkscape:window-y="453"
- inkscape:window-maximized="0"
+ inkscape:zoom="1.0029297"
+ inkscape:cx="-33.402141"
+ inkscape:cy="344.49074"
+ inkscape:window-width="1920"
+ inkscape:window-height="1009"
+ inkscape:window-x="2638"
+ inkscape:window-y="-8"
+ inkscape:window-maximized="1"
inkscape:current-layer="svg12" />
+ style="fill:#ffffff" />
diff --git a/ZXBStudio/Svg/White/check.svg b/ZXBStudio/Svg/White/check.svg
new file mode 100644
index 0000000..3405511
--- /dev/null
+++ b/ZXBStudio/Svg/White/check.svg
@@ -0,0 +1,5 @@
+
+
+
\ No newline at end of file
diff --git a/ZXBStudio/Svg/add.svg b/ZXBStudio/Svg/add.svg
new file mode 100644
index 0000000..15436c4
--- /dev/null
+++ b/ZXBStudio/Svg/add.svg
@@ -0,0 +1,7 @@
+
+
+
\ No newline at end of file
diff --git a/ZXBStudio/Svg/show.svg b/ZXBStudio/Svg/show.svg
new file mode 100644
index 0000000..ea0a452
--- /dev/null
+++ b/ZXBStudio/Svg/show.svg
@@ -0,0 +1,4 @@
+
+
\ No newline at end of file
diff --git a/ZXBStudio/ZXBasicStudio.csproj b/ZXBStudio/ZXBasicStudio.csproj
index 84719dd..045e5fc 100644
--- a/ZXBStudio/ZXBasicStudio.csproj
+++ b/ZXBStudio/ZXBasicStudio.csproj
@@ -68,8 +68,8 @@
-
+
@@ -121,6 +121,7 @@
+
@@ -202,6 +203,7 @@
+
@@ -316,7 +318,8 @@
-
+
+
@@ -363,15 +366,19 @@
+
+
+
+
@@ -632,6 +639,48 @@
ZXGridImageView.axaml
+
+ LayerItemControl.axaml
+
+
+ MapFileSelectorControl.axaml
+
+
+ MapPropertiesControl.axaml
+
+
+ LayersControl.axaml
+
+
+ MapPatternEditor.axaml
+
+
+ MapExportDialog.axaml
+
+
+ TileExportDialog.axaml
+
+
+ TileImportDialog.axaml
+
+
+ TilePropertiesControl.axaml
+
+
+ TilePreviewControl.axaml
+
+
+ TilePatternControl.axaml
+
+
+ TilePatternEditor.axaml
+
+
+ TileEditor.axaml
+
+
+ ZXMapsEditor.axaml
+
ZXKeyView.axaml
diff --git a/ZXBasicStudio.Log/Class1.cs b/ZXBasicStudio.Log/Class1.cs
new file mode 100644
index 0000000..46431c3
--- /dev/null
+++ b/ZXBasicStudio.Log/Class1.cs
@@ -0,0 +1,7 @@
+namespace ZXBasicStudio.Log
+{
+ public class Class1
+ {
+
+ }
+}
diff --git a/ZXBasicStudio.Log/ZXBasicStudio.Log.csproj b/ZXBasicStudio.Log/ZXBasicStudio.Log.csproj
new file mode 100644
index 0000000..fa71b7a
--- /dev/null
+++ b/ZXBasicStudio.Log/ZXBasicStudio.Log.csproj
@@ -0,0 +1,9 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+