From 2345e768babe1da8f1ec9e78da1c8b79cca16013 Mon Sep 17 00:00:00 2001 From: Meta-Agents Date: Sun, 5 Apr 2026 16:40:01 +0200 Subject: [PATCH 01/18] Working with map editor --- ZXBStudio/Assets/zxGraphics_map.png | Bin 525 -> 720 bytes .../DocumentEditors/ZXMaps/ZXMapsEditor.axaml | 149 +++++++++ .../ZXMaps/ZXMapsEditor.axaml.cs | 299 ++++++++++++++++++ .../Classes/ZXDocumentProvider.cs | 2 + .../ZXGraphics/SpriteDocument.cs | 2 +- .../ZXMaps/ZXMapsDocument.cs | 72 +++++ .../ZXMaps/ZXMapsDocumentFactory.cs | 56 ++++ ZXBStudio/Svg/Documents/file-blank.svg | 43 +++ ZXBStudio/Svg/Documents/file-map.svg | 35 +- ZXBStudio/ZXBasicStudio.csproj | 4 + 10 files changed, 642 insertions(+), 20 deletions(-) create mode 100644 ZXBStudio/DocumentEditors/ZXMaps/ZXMapsEditor.axaml create mode 100644 ZXBStudio/DocumentEditors/ZXMaps/ZXMapsEditor.axaml.cs create mode 100644 ZXBStudio/IntegratedDocumentTypes/ZXMaps/ZXMapsDocument.cs create mode 100644 ZXBStudio/IntegratedDocumentTypes/ZXMaps/ZXMapsDocumentFactory.cs create mode 100644 ZXBStudio/Svg/Documents/file-blank.svg diff --git a/ZXBStudio/Assets/zxGraphics_map.png b/ZXBStudio/Assets/zxGraphics_map.png index 52848ea0f8a784c186af47aa1155b4e47f357180..91d74bb72b474c7c031c7fde663a3bca113bcb41 100644 GIT binary patch delta 345 zcmeBWxxhL>l7pFnfk8u;KX{{J4r4uAfKQ0)|16*o!z9Jp_dtrNB*-uLKf}}Q23|lO zXMsm#F#`j)FbFd;%$g$s6qGD+jVKAuPb(=;EJ|evNX*PD(erZ+Q83jr)H5^^W^^wB zD(Xv(@J#dc)MDTOa#$Ip7+Dz@fh>@r(ol8~Lp{(bjLcvOCZL2NBNKxlkd6Z4%yt&A zcn**a0-lnL3@?CghtcTP0$t9)FoB(c1*q1*$k>2!0mR(@|H0ZBIOF_PD*nmc1@f#s zT^vI+f=l~Oy7DqOa4>EBzsy{4ngqYy0h<69;gkRchSUXqOl679n>IJj-(%?ZJaVFs f_>1-8`y0P9M6)w&b)2Cy4P>aNtDnm{r-UW|yh=;@ delta 149 zcmcb>+RHLQl7o$bfkFQB|HO@oIgIt31s;*b3=G`DAk4@xYmNj^Hq6t-F~q|EZC@Z? zg8_&0!N>0$;>0(z^sGJbXa4(k!>AR1p4<@ltRC=p`Gy@vtHT%UUZP&bx86K4E%D19 z8JD}MYGV6@KBO+2>)xbwGKmdKI;RB2CIH2U BIx+wN diff --git a/ZXBStudio/DocumentEditors/ZXMaps/ZXMapsEditor.axaml b/ZXBStudio/DocumentEditors/ZXMaps/ZXMapsEditor.axaml new file mode 100644 index 0000000..a5cbe81 --- /dev/null +++ b/ZXBStudio/DocumentEditors/ZXMaps/ZXMapsEditor.axaml @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Zoom 24x + + Width + + + + Height + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ZXBStudio/DocumentEditors/ZXMaps/ZXMapsEditor.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/ZXMapsEditor.axaml.cs new file mode 100644 index 0000000..3efa5d6 --- /dev/null +++ b/ZXBStudio/DocumentEditors/ZXMaps/ZXMapsEditor.axaml.cs @@ -0,0 +1,299 @@ +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.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 masterList = SpritePatternsList.Select(d => d.SpriteData).ToArray(); + var sprList = new List(); + foreach (var spr in masterList) + { + sprList.Add(spr); //.Clonar()); + } + foreach (Sprite spr in sprList) + { + if (spr == null) + { + continue; + } + + if (spr.Frames < spr.Patterns.Count()) + { + spr.Patterns = spr.Patterns.Take(spr.Frames).ToList(); + } + } + + var dataJSon = sprList.Serializar(); + //if (!ServiceLayer.Files_SaveFileString(FileName, dataJSon)) + //{ + // 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; + 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; + + + public ZXMapsEditor(string fileName) + { + FileName= fileName; + InitializeComponent(); + } + + + 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..2169298 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,7 @@ static ZXDocumentProvider() _docTypes.Add(new UDGDocument()); _docTypes.Add(new FontDocument()); _docTypes.Add(new SpriteDocument()); + _docTypes.Add(new ZXMapsDocument()); // NextDows //_docTypes.Add(new ZXFormsDocument()); //TODO: Load external document types diff --git a/ZXBStudio/IntegratedDocumentTypes/ZXGraphics/SpriteDocument.cs b/ZXBStudio/IntegratedDocumentTypes/ZXGraphics/SpriteDocument.cs index 2b91d6e..f95cf13 100644 --- a/ZXBStudio/IntegratedDocumentTypes/ZXGraphics/SpriteDocument.cs +++ b/ZXBStudio/IntegratedDocumentTypes/ZXGraphics/SpriteDocument.cs @@ -18,7 +18,7 @@ namespace ZXBasicStudio.IntegratedDocumentTypes.ZXGraphics { public class SpriteDocument : IZXDocumentType { - static readonly string[] _docExtensions = { ".spr" }; + static readonly string[] _docExtensions = { ".spr", ".til" }; static readonly string _docName = "Sprites file"; static readonly string _docDesc = "Sprite files allow you to create and modify graphics of different sizes and formats. Once created, they can be built in multiple formats."; static readonly string _docCat = "Graphics"; diff --git a/ZXBStudio/IntegratedDocumentTypes/ZXMaps/ZXMapsDocument.cs b/ZXBStudio/IntegratedDocumentTypes/ZXMaps/ZXMapsDocument.cs new file mode 100644 index 0000000..59e589e --- /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/zxGraphics_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/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/ZXBasicStudio.csproj b/ZXBStudio/ZXBasicStudio.csproj index 2db2105..c3bb9c7 100644 --- a/ZXBStudio/ZXBasicStudio.csproj +++ b/ZXBStudio/ZXBasicStudio.csproj @@ -365,6 +365,7 @@ + @@ -632,6 +633,9 @@ ZXGridImageView.axaml + + ZXMapsEditor.axaml + ZXKeyView.axaml From 5ffe98afa572e7d937259e0705d8f364fbe801f1 Mon Sep 17 00:00:00 2001 From: Meta-Agents Date: Mon, 20 Apr 2026 22:09:01 +0200 Subject: [PATCH 02/18] Working with Tile editor --- README.md | 14 +- ZXBStudio/Assets/zxmaps_map.png | Bin 0 -> 715 bytes .../{zxGraphics_til.png => zxmaps_til.png} | Bin 544 -> 710 bytes .../ZXGraphics/SpriteEditor.axaml | 382 ++--- .../ZXGraphics/SpriteEditor.axaml.cs | 18 +- .../ZXGraphics/SpriteExportDialog.axaml.cs | 4 +- .../ZXGraphics/SpriteImportDialog.axaml.cs | 12 +- .../ZXGraphics/SpritePatternControl.axaml.cs | 10 +- .../ZXGraphics/SpritePatternEditor.axaml.cs | 4 +- .../ZXGraphics/SpritePreviewControl.axaml.cs | 4 +- .../SpritePropertiesControl.axaml.cs | 6 +- .../ZXGraphics/ZXSpriteImage.cs | 6 +- .../ZXGraphics/log/ExportManager.cs | 42 +- .../ZXGraphics/log/ServiceLayer.cs | 8 +- .../DocumentEditors/ZXGraphics/neg/Sprite.cs | 25 +- .../ZXMaps/LayerItemControl.axaml | 22 + .../ZXMaps/LayerItemControl.axaml.cs | 61 + .../ZXMaps/LayersControl.axaml | 38 + .../ZXMaps/LayersControl.axaml.cs | 70 + .../ZXMaps/MapPropertiesControl.axaml | 109 ++ .../ZXMaps/MapPropertiesControl.axaml.cs | 66 + .../DocumentEditors/ZXMaps/Neg/ZXMapsLayer.cs | 44 + .../ZXMaps/Neg/ZXMapsLayersType.cs | 14 + .../DocumentEditors/ZXMaps/Neg/ZXMapsMap.cs | 23 + .../ZXMaps/Neg/ZXMapsMappingTypes.cs | 9 + .../ZXMaps/Neg/ZXMapsProperty.cs | 8 + .../ZXMaps/Neg/ZXMapsPropertyDefiniton.cs | 11 + .../ZXMaps/Neg/ZXMapsPropertyTypes.cs | 10 + .../DocumentEditors/ZXMaps/Neg/ZXMapsRoom.cs | 16 + .../DocumentEditors/ZXMaps/Neg/ZXMapsTile.cs | 63 + .../DocumentEditors/ZXMaps/Neg/ZXMapsTiles.cs | 19 + .../DocumentEditors/ZXMaps/Neg/ZXMapsTypes.cs | 10 + .../DocumentEditors/ZXMaps/TileEditor.axaml | 220 +++ .../ZXMaps/TileEditor.axaml.cs | 1374 +++++++++++++++++ ZXBStudio/DocumentEditors/ZXMaps/TileImage.cs | 218 +++ .../ZXMaps/TilePatternControl.axaml | 19 + .../ZXMaps/TilePatternControl.axaml.cs | 418 +++++ .../ZXMaps/TilePatternEditor.axaml | 11 + .../ZXMaps/TilePatternEditor.axaml.cs | 1184 ++++++++++++++ .../ZXMaps/TilePreviewControl.axaml | 26 + .../ZXMaps/TilePreviewControl.axaml.cs | 143 ++ .../ZXMaps/TilePropertiesControl.axaml | 31 + .../ZXMaps/TilePropertiesControl.axaml.cs | 185 +++ .../DocumentEditors/ZXMaps/ZXMapsEditor.axaml | 55 +- .../ZXMaps/ZXMapsEditor.axaml.cs | 66 +- .../Classes/ZXDocumentProvider.cs | 1 + .../ZXGraphics/SpriteDocument.cs | 2 +- .../ZXMaps/ZXMapsDocument.cs | 2 +- .../ZXMaps/ZXTilemapDocument.cs | 102 ++ .../ZXMaps/ZXTilemapDocumentFactory.cs | 58 + ZXBStudio/Program.cs | 2 + ZXBStudio/Svg/Documents/file-tile.svg | 55 +- ZXBStudio/Svg/White/check.svg | 5 + ZXBStudio/Svg/add.svg | 7 + ZXBStudio/Svg/show.svg | 4 + ZXBStudio/ZXBasicStudio.csproj | 34 +- 56 files changed, 5023 insertions(+), 327 deletions(-) create mode 100644 ZXBStudio/Assets/zxmaps_map.png rename ZXBStudio/Assets/{zxGraphics_til.png => zxmaps_til.png} (54%) create mode 100644 ZXBStudio/DocumentEditors/ZXMaps/LayerItemControl.axaml create mode 100644 ZXBStudio/DocumentEditors/ZXMaps/LayerItemControl.axaml.cs create mode 100644 ZXBStudio/DocumentEditors/ZXMaps/LayersControl.axaml create mode 100644 ZXBStudio/DocumentEditors/ZXMaps/LayersControl.axaml.cs create mode 100644 ZXBStudio/DocumentEditors/ZXMaps/MapPropertiesControl.axaml create mode 100644 ZXBStudio/DocumentEditors/ZXMaps/MapPropertiesControl.axaml.cs create mode 100644 ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsLayer.cs create mode 100644 ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsLayersType.cs create mode 100644 ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsMap.cs create mode 100644 ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsMappingTypes.cs create mode 100644 ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsProperty.cs create mode 100644 ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsPropertyDefiniton.cs create mode 100644 ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsPropertyTypes.cs create mode 100644 ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsRoom.cs create mode 100644 ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsTile.cs create mode 100644 ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsTiles.cs create mode 100644 ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsTypes.cs create mode 100644 ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml create mode 100644 ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml.cs create mode 100644 ZXBStudio/DocumentEditors/ZXMaps/TileImage.cs create mode 100644 ZXBStudio/DocumentEditors/ZXMaps/TilePatternControl.axaml create mode 100644 ZXBStudio/DocumentEditors/ZXMaps/TilePatternControl.axaml.cs create mode 100644 ZXBStudio/DocumentEditors/ZXMaps/TilePatternEditor.axaml create mode 100644 ZXBStudio/DocumentEditors/ZXMaps/TilePatternEditor.axaml.cs create mode 100644 ZXBStudio/DocumentEditors/ZXMaps/TilePreviewControl.axaml create mode 100644 ZXBStudio/DocumentEditors/ZXMaps/TilePreviewControl.axaml.cs create mode 100644 ZXBStudio/DocumentEditors/ZXMaps/TilePropertiesControl.axaml create mode 100644 ZXBStudio/DocumentEditors/ZXMaps/TilePropertiesControl.axaml.cs create mode 100644 ZXBStudio/IntegratedDocumentTypes/ZXMaps/ZXTilemapDocument.cs create mode 100644 ZXBStudio/IntegratedDocumentTypes/ZXMaps/ZXTilemapDocumentFactory.cs create mode 100644 ZXBStudio/Svg/White/check.svg create mode 100644 ZXBStudio/Svg/add.svg create mode 100644 ZXBStudio/Svg/show.svg 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/ZXBStudio/Assets/zxmaps_map.png b/ZXBStudio/Assets/zxmaps_map.png new file mode 100644 index 0000000000000000000000000000000000000000..415f68f0db6c8a99d1a1eac50a2e5ef8f02efb6a GIT binary patch literal 715 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6d5J!b-+_FEWRDb`J1#c2)=|%1_J8 zNd+2uJ~<)5sGy{OO|GKyppK57palE#2aX*$!@$;g@+fN`Bcq){SzKM5 z7BkzAvff5Pm-uU}Gmkn*J@C@g&|R^bC(h~VYcg~hpJUwQI%(6v zH@pgEfgTf#C)Bc>c;?-{VSU35Pl0d30=qe0a4P)f_NcX*arOwK8T+AWj2pd!v>m<~ zSv(gH5ldZ`K1D+!Dk@6LV%Zb*rCKcEV$+vA6VtAErutl5^oh9Yv?a?Jt~%XeXKNB@ zT_A9oA#2Nplm$i%N10o`S_;VQ>q$x%NaC0f6BWXiYGfoS{7m`G4CR9d9_*je*!Yy+ z>CBSpjfstX%*_8oT4u~rWNvP-V?MmklHtHH|G)W@p0WS~mMy?1#Pxp`ki{@bvGzTX zVk!yp3;xgWbi08UkjGiz5n0T@z%2~Ij105pNB{*TOI#yLg7ec#$`gxH83GbB^Gfvm zTtgI0^$hh4jf5H9OMr^{QX@RmJUz7-IDi~h1}R2X21XzYWT-Th4T^dVMrN=$6Oe7l z$iyHBq@#d1vz-Mjo&#iqfTtuQ!wX;vfzjx;0v*o4FoB%`BHJ>CHtPpU1=Eu( jWR;jw9|$>|SisG|U^kDUXR?RH29R-{u6{1-oD!M*AN9$JwrW1BVk7O5(Wl_zSIcM zG*3@01`Z&Hl|hP;m4Ok+@&aOMC_4zq)?j1?i!%Y)hKx)MfB1)v15!U0}MNkl - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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..30da91c 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; @@ -247,7 +247,7 @@ private void _UpdatePreview() GetProperties(); - var s = new Sprite(); + var s = new ZXMapsTile(); s.CurrentFrame = 0; s.DefaultColor = 7; s.Export = true; @@ -622,7 +622,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/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..a24008a 100644 --- a/ZXBStudio/DocumentEditors/ZXGraphics/log/ExportManager.cs +++ b/ZXBStudio/DocumentEditors/ZXGraphics/log/ExportManager.cs @@ -389,12 +389,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 +433,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 +480,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 +506,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 +529,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 +740,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 +805,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 +896,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 +988,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 +1030,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 +1087,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 +1095,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 +1104,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 +1137,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 +1172,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 +1205,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 +1240,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 +1326,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 +1387,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 +1408,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) diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/log/ServiceLayer.cs b/ZXBStudio/DocumentEditors/ZXGraphics/log/ServiceLayer.cs index da4a606..655fe91 100644 --- a/ZXBStudio/DocumentEditors/ZXGraphics/log/ServiceLayer.cs +++ b/ZXBStudio/DocumentEditors/ZXGraphics/log/ServiceLayer.cs @@ -615,7 +615,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 +653,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 +666,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 +679,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; 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/MapPropertiesControl.axaml b/ZXBStudio/DocumentEditors/ZXMaps/MapPropertiesControl.axaml new file mode 100644 index 0000000..ba3172d --- /dev/null +++ b/ZXBStudio/DocumentEditors/ZXMaps/MapPropertiesControl.axaml @@ -0,0 +1,109 @@ + + + + + + + + + + + Map type: + + Rooms + Horizontal scroll + Vertical scroll + Multidirectional scroll + + + Map width: + + + + + Map height: + + + + + Tile width: + + + + + Tile height: + + + + + 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..929fb03 --- /dev/null +++ b/ZXBStudio/DocumentEditors/ZXMaps/MapPropertiesControl.axaml.cs @@ -0,0 +1,66 @@ +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 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 + { + private ZXMapsMap Map = null; + private int CurrentTap = 0; + + + public MapPropertiesControl() + { + InitializeComponent(); + } + + + public bool Initialize(ZXMapsMap map) + { + this.Map = map; + Refresh(); + return true; + } + + + public void Refresh() + { + // 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; + } + } + } +} \ 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..80bc0e5 --- /dev/null +++ b/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsMap.cs @@ -0,0 +1,23 @@ +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 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; } + } +} 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..1891cd4 --- /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 an sprite with his properties + /// + public class ZXMapsTile + { + /// + /// Id of the sprite + /// + public int Id { get; set; } + /// + /// Name of the sprite + /// + public string Name { get; set; } + /// + /// Graphics mode of the sprite + /// + public GraphicsModes GraphicMode { get; set; } + /// + /// Width of the sprite in pixels + /// + public int Width { get; set; } + /// + /// Height in pixels + /// + public int Height { get; set; } + /// + /// True if the sprite is masked (not suported in all modes) + /// + public bool Masked { get; set; } + /// + /// Number of frames for the sprite + /// + public byte Frames { get; set; } + /// + /// Current frame + /// + public byte CurrentFrame { get; set; } + /// + /// Patterns for the sprite (one pattern for frame) + /// + public List Patterns { get; set; } + /// + /// Default transparent or background color + /// + public byte DefaultColor { get; set; } + /// + /// Palete color for the sprite + /// + public PaletteColor[] Palette { get; set; } + /// + /// True when the sprite was auto-exported + /// + public bool Export { get; set; } + } +} diff --git a/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsTiles.cs b/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsTiles.cs new file mode 100644 index 0000000..b907410 --- /dev/null +++ b/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsTiles.cs @@ -0,0 +1,19 @@ +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 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..ca8b155 --- /dev/null +++ b/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsTypes.cs @@ -0,0 +1,10 @@ +namespace ZXBasicStudio.DocumentEditors.ZXMaps.Neg +{ + public enum ZXMapsTypes + { + Rooms = 0, + HorizontalScroll = 1, + VerticalScroll = 2, + MultidirectionalScroll = 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..f57b831 --- /dev/null +++ b/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml @@ -0,0 +1,220 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Zoom 24x + + + Frame + + + + + + Paper + + + 0 + + + + + + Ink + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml.cs new file mode 100644 index 0000000..1a200f7 --- /dev/null +++ b/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml.cs @@ -0,0 +1,1374 @@ +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; + +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; + + #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; + + /// + /// List of Tiles patterns controls in the set + /// + private List TilePatternsList = 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 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; + } + ; + + _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; + 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 + + + /// + /// 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(); + + var data = ServiceLayer.GetFileData(fileName); + if (data == null || data.Length == 0) + { + TileMain = new ZXMapsTiles() + { + GraphicMode = GraphicsModes.Monochrome, + Height = 16, + Width = 16, + Name = Path.GetFileName(fileName).ToStringNoNull().Replace(".til", "").Replace(".zxtil", ""), + Tiles = new List() + }; + } + if (data != null) + { + var dataS = Encoding.UTF8.GetString(data); + if (!string.IsNullOrEmpty(dataS)) + { + TileMain = dataS.Deserializar(); + + wpTileList.ItemWidth = (TileMain.Width * 4) + 4; + wpTileList.ItemHeight = (TileMain.Height * 4) + 4; + + foreach (var Tile in TileMain.Tiles) + { + // Check attributes for ZX Spectrum mode + if (Tile != null && Tile.Patterns != null) + { + var al = (Tile.Width / 8) * (Tile.Height / 8); + foreach (var pattern in Tile.Patterns) + { + if (pattern.Attributes != null) + { + pattern.Attributes = pattern.Attributes.Take(al).ToArray(); + } + } + } + + // 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); + ctrlProperties.Initialize(TileMain, TileProperties_Command); + } + else + { + ctrlEditor.Initialize(Editor_Command); + ctrlPreview.Initialize(TilePatternsList[0].TileData); + ctrlProperties.Initialize(TileMain, TileProperties_Command); + } + + 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; + + btnTileInfoHide.Tapped += BtnTileInfoHide_Tapped; + btnTileInfoShow.Tapped += BtnTileInfoShow_Tapped; + + Refresh(); + + if (TilePatternsList.Count > 1) + { + TileList_Command(TilePatternsList.ElementAt(0), "SELECTED"); + } + + btnPaper.Tapped += BtnPaper_Click; + btnInk.Tapped += BtnInk_Tapped; + UpdateColorPanel(); + + 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; + } + + + #region Color + + private void BtnPaper_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e) + { + if (ctrlColorPicker.IsVisible) + { + ctrlColorPicker.IsVisible = false; + return; + } + var Tile = ctrlEditor.TileData; + if (Tile.GraphicMode == GraphicsModes.Monochrome) + { + return; + } + ctrlColorPicker.IsVisible = true; + ctrlColorPicker.Inicialize(Tile.GraphicMode, Tile.Palette, ctrlEditor.SecondaryColorIndex, ColorPickerPaper_Action); + } + + + private void BtnInk_Tapped(object? sender, TappedEventArgs e) + { + if (ctrlColorPicker.IsVisible) + { + ctrlColorPicker.IsVisible = false; + return; + } + var Tile = ctrlEditor.TileData; + if (Tile.GraphicMode == GraphicsModes.Monochrome) + { + return; + } + ctrlColorPicker.IsVisible = true; + ctrlColorPicker.Inicialize(Tile.GraphicMode, Tile.Palette, ctrlEditor.PrimaryColorIndex, ColorPickerInk_Action); + } + + + private void UpdateColorPanel() + { + var Tile = ctrlEditor.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[ctrlEditor.PrimaryColorIndex]; + var paper = Tile.Palette[ctrlEditor.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 = ctrlEditor.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 = ctrlEditor.PrimaryColorIndex.ToString(); + } + break; + } + } + + + + private void ColorPickerPaper_Action(string command, int indexColor) + { + ctrlEditor.SecondaryColorIndex = indexColor; + UpdateColorPanel(); + } + + + private void ColorPickerInk_Action(string command, int indexColor) + { + ctrlEditor.PrimaryColorIndex = indexColor; + UpdateColorPanel(); + } + + #endregion + + + #region TileList + + private void TileList_Command(TilePatternControl sender, string command) + { + switch (command) + { + case "ADD": + 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; + ctrlPreview.Refresh(); + TileProperties_FrameUpdate(ctrlProperties, command); + txtFrame.Text = "0"; + break; + } + } + + + private void TilePreview_Command(TilePreviewControl sender, string command) + { + + } + + + 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); + 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; + } + + + 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"); + } + + + private void TileList_AddTile(ZXMapsTile TileData) + { + 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) + { + TilePatternsList.Remove(control); + wpTileList.Children.Remove(control); + ctrlEditor.TileData = null; + ctrlPreview.TileData = null; + ctrlProperties.TileData = null; + control = null; + CalculateWrapPanelHeight(); + } + + + 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++; + ctrlProperties.Refresh(); + ctrlEditor.Refresh(); + txtFrame.MaxHeight = TileData.Frames - 1; + } + + + 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(); + } + + + 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(); + } + + wpTileList.ItemWidth = (TileMain.Width * 4) + 4; + wpTileList.ItemHeight = (TileMain.Height * 4) + 4; + + // Calcular altura del WrapPanel basada en el contenido + CalculateWrapPanelHeight(); + wpTileList.InvalidateMeasure(); + } + + 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; + } + + + 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; + } + } + + + + #endregion + + + #region Tile editor + + private void Editor_Command(TilePatternEditor sender, string command) + { + try + { + switch (command) + { + case "REFRESH": + { + var cur = TilePatternsList.FirstOrDefault(d => d.IsSelected); + if (cur != null) + { + cur.TileData = sender.TileData; + cur.Refresh(); + TileList_Modified(sender.TileData); + ctrlProperties.TileData = TileMain; + ctrlProperties.PrimaryColor = sender.PrimaryColorIndex; + ctrlProperties.SecondaryColor = sender.SecondaryColorIndex; + ctrlPreview.TileData = sender.TileData; + + btnColorPicker.IsChecked = !ctrlEditor.ColorPicker; + UpdateColorPanel(); + } + } + break; + } + } + catch { } + } + + #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"; + ctrlEditor.Zoom = z; + } + + + private void ZoomIn() + { + int v = sldZoom.Value.ToInteger(); + if (v < zooms.Length - 1) + { + sldZoom.Value = v - 1; + } + } + + + private 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 (ctrlEditor.TileData != null) + { + ctrlEditor.TileData.CurrentFrame = actualFrame; + ctrlEditor.Refresh(); + } + Refresh(); + */ + } + + #endregion + + + #region ToolBar + + /// + /// Clear click + /// + /// + /// + private void BtnClear_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) + { + this.ctrlEditor.Clear(); + } + + /// + /// Cut click + /// + /// + /// + private void BtnCut_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) + { + this.ctrlEditor.Cut(); + } + + + //Copy click + private void BtnCopy_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) + { + this.ctrlEditor.Copy(); + } + + + /// + /// Paste click + /// + /// + /// + private void BtnPaste_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) + { + this.ctrlEditor.Paste(); + } + + + /// + /// Horizontal mirror click + /// + /// + /// + private void BtnHMirror_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) + { + this.ctrlEditor.HorizontalMirror(); + } + + + /// + /// Vertical mirror click + /// + /// + /// + private void BtnVMirror_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) + { + this.ctrlEditor.VerticalMirror(); + } + + + /// + /// Rotate left + /// + /// + /// + private void BtnRotateLeft_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) + { + this.ctrlEditor.RotateLeft(); + } + + + /// + /// Rotate right + /// + /// + /// + private void BtnRotateRight_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) + { + this.ctrlEditor.RotateRight(); + } + + + /// + /// Shift up + /// + /// + /// + private void BtnShiftUp_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) + { + this.ctrlEditor.ShiftUp(); + } + + + /// + /// Shift right + /// + /// + /// + private void BtnShiftRight_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) + { + this.ctrlEditor.ShiftRight(); + } + + + /// + /// Shift down + /// + /// + /// + private void BtnShiftDown_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) + { + this.ctrlEditor.ShiftDown(); + } + + + /// + /// Shift left + /// + /// + /// + private void BtnShiftLeft_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) + { + this.ctrlEditor.ShiftLeft(); + } + + + /// + /// Move up + /// + /// + /// + private void BtnMoveUp_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) + { + this.ctrlEditor.MoveUp(); + } + + + /// + /// Move right + /// + /// + /// + private void BtnMoveRight_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) + { + this.ctrlEditor.MoveRight(); + } + + + /// + /// Move down + /// + /// + /// + private void BtnMoveDown_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) + { + this.ctrlEditor.MoveDown(); + } + + + /// + /// Move left + /// + /// + /// + private void BtnMoveLeft_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) + { + this.ctrlEditor.MoveLeft(); + } + + + /// + /// Invert + /// + /// + /// + private void BtnInvert_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) + { + this.ctrlEditor.Invert(); + } + + + /// + /// Mask + /// + /// + /// + private void BtnMask_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) + { + this.ctrlEditor.Mask(); + } + + + private void BtnExport_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) + { + Export(); + } + + + private void Export() + { + /* + var dlg = new TileExportDialog(); + dlg.Initialize(FileName, TilePatternsList.Select(d => d.TileData)); + dlg.ShowDialog(this.VisualRoot as Window); + */ + } + + + private void BtnImport_Tapped(object? sender, TappedEventArgs e) + { + Import(); + } + + + + private void BtnViewAttributes_Tapped(object? sender, TappedEventArgs e) + { + ctrlEditor.ViewAttributes = btnViewAttributes.IsChecked == true; + } + + private void BtnColorPicker_Tapped(object? sender, TappedEventArgs e) + { + ctrlEditor.ColorPicker = btnColorPicker.IsChecked == false; + } + + + private void BtnInvertPixelsCell_Tapped(object? sender, TappedEventArgs e) + { + ctrlEditor.InvertPixelsCell = btnInvertPixelsCell.IsChecked == false; + if (ctrlEditor.InvertPixelsCell) + { + btnInvertColorsCell.IsChecked = true; + ctrlEditor.InvertColorsCell = false; + } + } + + + private void BtnInvertColorsCell_Tapped(object? sender, TappedEventArgs e) + { + ctrlEditor.InvertColorsCell = btnInvertColorsCell.IsChecked == false; + if (ctrlEditor.InvertColorsCell) + { + btnInvertPixelsCell.IsChecked = true; + ctrlEditor.InvertPixelsCell = false; + } + } + + + private void BtnRedo_Tapped(object? sender, TappedEventArgs e) + { + ctrlEditor.Redo(); + } + + + private void BtnUndo_Tapped(object? sender, TappedEventArgs e) + { + ctrlEditor.Undo(); + } + + #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, () => { Export(); } }, + { keyboardCommands["Zoom In"].CommandId, () => { ZoomIn(); } }, + { keyboardCommands["Zoom Out"].CommandId, () => { 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](); + } + } + + #endregion + + + #region Import + + private void Import() + { + /* + var dlg = new TileImportDialog(); + dlg.Initialize(FileName, 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": + TileList_AddTile(Tile); + ctrlEditor.TileData = Tile; + ctrlPreview.TileData = Tile; + ctrlPreview.Refresh(); + ctrlProperties.TileData = TileMain; + //TileList_Modified(sender.TileData); + break; + case "UPDATE": + //var spr = TilePatternsList.FirstOrDefault(d => d.Name == Tile.Name); + //if (spr != null) + //{ + // TileList_Modified(Tile); + //} + break; + } + } + catch { } + } + + + #endregion + } +} diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TileImage.cs b/ZXBStudio/DocumentEditors/ZXMaps/TileImage.cs new file mode 100644 index 0000000..6caa726 --- /dev/null +++ b/ZXBStudio/DocumentEditors/ZXMaps/TileImage.cs @@ -0,0 +1,218 @@ +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 (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/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..46bc7ea --- /dev/null +++ b/ZXBStudio/DocumentEditors/ZXMaps/TilePatternControl.axaml.cs @@ -0,0 +1,418 @@ +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 = false; + + #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; + 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..2d724ff --- /dev/null +++ b/ZXBStudio/DocumentEditors/ZXMaps/TilePatternEditor.axaml @@ -0,0 +1,11 @@ + + + + + diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TilePatternEditor.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/TilePatternEditor.axaml.cs new file mode 100644 index 0000000..19cb02a --- /dev/null +++ b/ZXBStudio/DocumentEditors/ZXMaps/TilePatternEditor.axaml.cs @@ -0,0 +1,1184 @@ +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 Public properties + + /// + /// Actual Tile data + /// + public ZXMapsTile TileData + { + get + { + return _TileData; + } + set + { + _TileData = value; + if (_TileData == null) + { + lastId = null; + } + else + { + if (lastId != _TileData.Id) + { + _TileData.CurrentFrame = 0; + lastId = _TileData.Id; + } + } + 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; + + #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; + } + + + /// + /// 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 + } +} diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TilePreviewControl.axaml b/ZXBStudio/DocumentEditors/ZXMaps/TilePreviewControl.axaml new file mode 100644 index 0000000..6b15371 --- /dev/null +++ b/ZXBStudio/DocumentEditors/ZXMaps/TilePreviewControl.axaml @@ -0,0 +1,26 @@ + + + Speed + + Disabled + 1 fps (1000ms) + 2 fps (500ms) + 4 fps (250ms) + 5 fps (200ms) + 8 fps (125ms) + 10 fps (100ms) + 15 fps (66ms) + 20 fps (50ms) + 50 fps (20ms) + + + + + + diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TilePreviewControl.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/TilePreviewControl.axaml.cs new file mode 100644 index 0000000..3e6700e --- /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 index a5cbe81..f123395 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/ZXMapsEditor.axaml +++ b/ZXBStudio/DocumentEditors/ZXMaps/ZXMapsEditor.axaml @@ -10,7 +10,7 @@ Background="#ff202020" x:Class="ZXBasicStudio.DocumentEditors.ZXMaps.ZXMapsEditor"> - + @@ -119,31 +119,34 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + Preview + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ZXBStudio/DocumentEditors/ZXMaps/ZXMapsEditor.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/ZXMapsEditor.axaml.cs index 3efa5d6..1f208d7 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/ZXMapsEditor.axaml.cs +++ b/ZXBStudio/DocumentEditors/ZXMaps/ZXMapsEditor.axaml.cs @@ -12,6 +12,7 @@ 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; @@ -29,7 +30,6 @@ public partial class ZXMapsEditor : ZXDocumentEditorBase//, IObserver 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 (Tile spr in sprList) { if (spr == null) { @@ -150,6 +151,7 @@ public override bool SaveDocument(TextWriter OutputLog) _Modified = false; DocumentSaved?.Invoke(this, EventArgs.Empty); + */ return true; } catch (Exception ex) @@ -220,10 +222,56 @@ public override void Deactivated() //private PaletteColor[] Palette=null; + private ZXMapsMap Map = null; + + public ZXMapsEditor(string fileName) { - FileName= 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.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(); + } + InitializeComponent(); + + ctrlLayers.Initialize(Map.Layers); + ctrlProperties.Initialize(Map); } @@ -246,7 +294,7 @@ public bool Initialize() /// Refresh the editor UI /// public void Refresh() - { + { //cnvEditor.Children.Clear(); Viewport = Controls.FirstOrDefault(d => d.Id == 0); if (Viewport == null) @@ -267,11 +315,11 @@ public void Refresh() }; Viewport.Properties = new List(); Controls.Add(Viewport); - } + } - foreach (var control in Controls.OrderBy(d=>d.Id)) + foreach (var control in Controls.OrderBy(d => d.Id)) { - switch(control.ControlType) + switch (control.ControlType) { case ControlsTypes.Panel: Draw_Panel(control); @@ -281,7 +329,7 @@ public void Refresh() //cnvEditor.Width = WindowWidth * Zoom; //cnvEditor.Height = WindowHeight * Zoom; - } + } public void Draw_Panel(ControlItem control) diff --git a/ZXBStudio/DocumentModel/Classes/ZXDocumentProvider.cs b/ZXBStudio/DocumentModel/Classes/ZXDocumentProvider.cs index 2169298..b98cd18 100644 --- a/ZXBStudio/DocumentModel/Classes/ZXDocumentProvider.cs +++ b/ZXBStudio/DocumentModel/Classes/ZXDocumentProvider.cs @@ -38,6 +38,7 @@ static ZXDocumentProvider() _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/ZXGraphics/SpriteDocument.cs b/ZXBStudio/IntegratedDocumentTypes/ZXGraphics/SpriteDocument.cs index f95cf13..2b91d6e 100644 --- a/ZXBStudio/IntegratedDocumentTypes/ZXGraphics/SpriteDocument.cs +++ b/ZXBStudio/IntegratedDocumentTypes/ZXGraphics/SpriteDocument.cs @@ -18,7 +18,7 @@ namespace ZXBasicStudio.IntegratedDocumentTypes.ZXGraphics { public class SpriteDocument : IZXDocumentType { - static readonly string[] _docExtensions = { ".spr", ".til" }; + static readonly string[] _docExtensions = { ".spr" }; static readonly string _docName = "Sprites file"; static readonly string _docDesc = "Sprite files allow you to create and modify graphics of different sizes and formats. Once created, they can be built in multiple formats."; static readonly string _docCat = "Graphics"; diff --git a/ZXBStudio/IntegratedDocumentTypes/ZXMaps/ZXMapsDocument.cs b/ZXBStudio/IntegratedDocumentTypes/ZXMaps/ZXMapsDocument.cs index 59e589e..8ff093d 100644 --- a/ZXBStudio/IntegratedDocumentTypes/ZXMaps/ZXMapsDocument.cs +++ b/ZXBStudio/IntegratedDocumentTypes/ZXMaps/ZXMapsDocument.cs @@ -48,7 +48,7 @@ public Bitmap DocumentIcon { if (_icon == null) { - _icon = new Bitmap(AssetLoader.Open(new Uri("avares://ZXBasicStudio/Assets/zxGraphics_map.png"))); + _icon = new Bitmap(AssetLoader.Open(new Uri("avares://ZXBasicStudio/Assets/zxmaps_map.png"))); } return _icon; diff --git a/ZXBStudio/IntegratedDocumentTypes/ZXMaps/ZXTilemapDocument.cs b/ZXBStudio/IntegratedDocumentTypes/ZXMaps/ZXTilemapDocument.cs new file mode 100644 index 0000000..c37042d --- /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 = { ".til", ".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/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-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 c3bb9c7..0fbe52b 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,6 +366,7 @@ + @@ -371,8 +375,10 @@ + + @@ -633,6 +639,30 @@ ZXGridImageView.axaml + + LayerItemControl.axaml + + + MapPropertiesControl.axaml + + + LayersControl.axaml + + + TilePropertiesControl.axaml + + + TilePreviewControl.axaml + + + TilePatternControl.axaml + + + TilePatternEditor.axaml + + + TileEditor.axaml + ZXMapsEditor.axaml From aa3f95f5a77352ccd7ceacea77df253cb88764de Mon Sep 17 00:00:00 2001 From: Meta-Agents Date: Sun, 17 May 2026 16:14:55 +0200 Subject: [PATCH 03/18] Working with Tile editor --- .../DocumentEditors/ZXMaps/TileEditor.axaml | 12 ++++++------ .../ZXMaps/TilePatternControl.axaml.cs | 2 +- .../ZXMaps/TilePreviewControl.axaml | 15 +++------------ ZXBStudio/MainWindow.axaml.cs | 18 +++++++++--------- 4 files changed, 19 insertions(+), 28 deletions(-) diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml b/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml index f57b831..217c1e2 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml +++ b/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml @@ -11,7 +11,7 @@ Background="#ff202020" x:Class="ZXBasicStudio.DocumentEditors.ZXMaps.TileEditor"> - + @@ -192,18 +192,18 @@ - - + + - + - + - + diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TilePatternControl.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/TilePatternControl.axaml.cs index 46bc7ea..4a2cd0e 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/TilePatternControl.axaml.cs +++ b/ZXBStudio/DocumentEditors/ZXMaps/TilePatternControl.axaml.cs @@ -100,7 +100,7 @@ public bool SettingsChanged private int tileWidth = 16; private int tileHeight = 16; - private bool _InfoVisible = false; + private bool _InfoVisible = true; #endregion diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TilePreviewControl.axaml b/ZXBStudio/DocumentEditors/ZXMaps/TilePreviewControl.axaml index 6b15371..6ca3356 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/TilePreviewControl.axaml +++ b/ZXBStudio/DocumentEditors/ZXMaps/TilePreviewControl.axaml @@ -6,18 +6,9 @@ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="ZXBasicStudio.DocumentEditors.ZXMaps.TilePreviewControl"> - Speed - - Disabled - 1 fps (1000ms) - 2 fps (500ms) - 4 fps (250ms) - 5 fps (200ms) - 8 fps (125ms) - 10 fps (100ms) - 15 fps (66ms) - 20 fps (50ms) - 50 fps (20ms) + .zxmap file + + None 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, From 6b2996fb6aacd21af6d209ec71b3fdaa2ec69e2d Mon Sep 17 00:00:00 2001 From: Meta-Agents Date: Fri, 5 Jun 2026 14:02:28 +0200 Subject: [PATCH 04/18] Updated ExternalTools reference for new compiler address --- ZXBSInstaller.Log/ExternalTools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ZXBSInstaller.Log/ExternalTools.json b/ZXBSInstaller.Log/ExternalTools.json index 4665c56..5ffc542 100644 --- a/ZXBSInstaller.Log/ExternalTools.json +++ b/ZXBSInstaller.Log/ExternalTools.json @@ -36,7 +36,7 @@ "SiteUrl": "https://boriel-basic.net", "LicenseType": "GNU Affero General Public License v3.0", "LicenceUrl": "https://raw.githubusercontent.com/boriel-basic/zxbasic/refs/heads/main/LICENSE.txt", - "VersionsUrl": "https://boriel.com/files/zxb/", + "VersionsUrl": "https://files.boriel-basic.net/zxb/", "LocalPath": "", "Unzip": true, "CreateVerFile": false, From 185e49655b2e9fa94e63afb2edc97291055f1a45 Mon Sep 17 00:00:00 2001 From: Meta-Agents Date: Tue, 14 Jul 2026 20:30:47 +0200 Subject: [PATCH 05/18] Working with TileEditor --- .../DocumentEditors/ZXMaps/TileEditor.axaml | 280 +++++++++--------- .../ZXMaps/TilePreviewControl.axaml.cs | 22 +- 2 files changed, 153 insertions(+), 149 deletions(-) diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml b/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml index 217c1e2..7b41e50 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml +++ b/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml @@ -18,7 +18,7 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Zoom 24x - - - Frame - - - - - - Paper - - - 0 - - - - - - Ink - - - 1 - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Zoom 24x + + + Frame + + + + + + Paper + + + 0 + + + + + + Ink + + + 1 + + + + + + + + + + diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TilePreviewControl.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/TilePreviewControl.axaml.cs index 3e6700e..284ab8e 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/TilePreviewControl.axaml.cs +++ b/ZXBStudio/DocumentEditors/ZXMaps/TilePreviewControl.axaml.cs @@ -64,7 +64,7 @@ public bool Initialize(ZXMapsTile TileData) { this.TileData = TileData; - this.cmbSpeed.SelectionChanged += CmbSpeed_SelectionChanged; + //this.cmbSpeed.SelectionChanged += CmbSpeed_SelectionChanged; tmr.Interval = TimeSpan.FromMilliseconds(speeds[speed]); return true; @@ -80,16 +80,16 @@ 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]); - } + //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) { From ca001234e88cc4a38ea7f48d1ad69c0f6e36ab93 Mon Sep 17 00:00:00 2001 From: Meta-Agents Date: Thu, 16 Jul 2026 18:49:06 +0200 Subject: [PATCH 06/18] Working with map editor --- .../ZXMaps/MapFileSelectorControl.axaml | 15 + .../ZXMaps/MapFileSelectorControl.axaml.cs | 166 +++++ .../ZXMaps/MapPropertiesControl.axaml | 82 +-- .../ZXMaps/MapPropertiesControl.axaml.cs | 51 +- .../DocumentEditors/ZXMaps/Neg/ZXMapsMap.cs | 1 + .../DocumentEditors/ZXMaps/TileEditor.axaml | 164 +---- .../ZXMaps/TileEditor.axaml.cs | 616 ++---------------- .../ZXMaps/TilePatternEditor.axaml | 155 ++++- .../ZXMaps/TilePatternEditor.axaml.cs | 586 ++++++++++++++++- .../ZXMaps/ZXMapsEditor.axaml.cs | 35 +- ZXBStudio/ZXBasicStudio.csproj | 3 + 11 files changed, 1078 insertions(+), 796 deletions(-) create mode 100644 ZXBStudio/DocumentEditors/ZXMaps/MapFileSelectorControl.axaml create mode 100644 ZXBStudio/DocumentEditors/ZXMaps/MapFileSelectorControl.axaml.cs 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..6613e7a --- /dev/null +++ b/ZXBStudio/DocumentEditors/ZXMaps/MapFileSelectorControl.axaml.cs @@ -0,0 +1,166 @@ +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.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; + LoadMap(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; + LoadMap(name); + Comando?.Invoke(this, "SELECTED"); + } + catch (Exception ex) + { + //MessageBox.Show($"Error loading file {FileName}: {ex.Message}"); + } + + } + + + private void LoadMap(string name) + { + try + { + var fileName = Path.Combine(ZXProjectManager.Current.ProjectPath, name + ".zxmap"); + if (File.Exists(fileName)) + { + var json = File.ReadAllText(fileName); + Map = json.Deserializar(); + } + else + { + Map = null; + } + } + catch (Exception ex) + { + //MessageBox.Show($"Error loading file {FileName}: {ex.Message}"); + } + } + } +} \ No newline at end of file diff --git a/ZXBStudio/DocumentEditors/ZXMaps/MapPropertiesControl.axaml b/ZXBStudio/DocumentEditors/ZXMaps/MapPropertiesControl.axaml index ba3172d..c06add6 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/MapPropertiesControl.axaml +++ b/ZXBStudio/DocumentEditors/ZXMaps/MapPropertiesControl.axaml @@ -8,51 +8,55 @@ Background="#ff202020" x:Class="ZXBasicStudio.DocumentEditors.ZXMaps.MapPropertiesControl"> - - + - - - - - - Map type: - - Rooms - Horizontal scroll - Vertical scroll - Multidirectional scroll - + + + Map type: + + + Multidirectional scroll + - Map width: - - - + Map width: + + + + (tiles) - Map height: - - - + Map height: + + + + (tiles) - Tile width: - - - + Tile width: + + + + (pixels) - Tile height: - - - + Tile height: + + + + (pixels) - Mapping type: - - Sequential - Coordinates - Blocks - - - - + Mapping type: + + Sequential + + + + diff --git a/ZXBStudio/DocumentEditors/ZXMaps/MapPropertiesControl.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/MapPropertiesControl.axaml.cs index 929fb03..2037cf1 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/MapPropertiesControl.axaml.cs +++ b/ZXBStudio/DocumentEditors/ZXMaps/MapPropertiesControl.axaml.cs @@ -11,6 +11,8 @@ 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; @@ -23,7 +25,19 @@ namespace ZXBasicStudio.DocumentEditors.ZXMaps { public partial class MapPropertiesControl : UserControl { - private ZXMapsMap Map = null; + public ZXMapsMap Map + { + get + { + return _Map; + } + set + { + _Map = value; + Refresh(); + } + } + private ZXMapsMap _Map = null; private int CurrentTap = 0; @@ -36,30 +50,37 @@ public MapPropertiesControl() public bool Initialize(ZXMapsMap map) { this.Map = map; - Refresh(); return true; } public void Refresh() - { - // 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) + { + if (Map == null) { tabControl.SelectedIndex = 0; + //tabControl.IsVisible = false; } else { - txtLayerName.Text = layer.Name; - cmbLayerType.SelectedIndex = (int)layer.LayersType; + // 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; + } } } } diff --git a/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsMap.cs b/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsMap.cs index 80bc0e5..c214090 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsMap.cs +++ b/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsMap.cs @@ -11,6 +11,7 @@ namespace ZXBasicStudio.DocumentEditors.ZXMaps.Neg /// public class ZXMapsMap { + public string Name { get; set; } public ZXMapsTypes MapType { get; set; } public int Width { get; set; } public int Height { get; set; } diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml b/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml index 7b41e50..6121dd8 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml +++ b/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml @@ -51,161 +51,43 @@ - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Zoom 24x - - - Frame - - - - - - Paper - - - 0 - - - - - - Ink - - - 1 - - - - - + + - - + + - - + + + - + - + + + + + + + + + + + + @@ -214,8 +96,6 @@ - - diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml.cs index 1a200f7..334c6f7 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml.cs +++ b/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml.cs @@ -105,27 +105,13 @@ public void OnNext(AvaloniaPropertyChangedEventArgs value) 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; - /// - /// 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; @@ -162,7 +148,16 @@ public override bool SaveDocument(TextWriter OutputLog) { 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); @@ -308,47 +303,18 @@ private void _Initialize(string fileName) TileList_AddTile(null); ctrlEditor.Initialize(Editor_Command); ctrlPreview.Initialize(null); - ctrlProperties.Initialize(TileMain, TileProperties_Command); + ctrlTileProperties.Initialize(TileMain, TileProperties_Command); } else { ctrlEditor.Initialize(Editor_Command); ctrlPreview.Initialize(TilePatternsList[0].TileData); - ctrlProperties.Initialize(TileMain, TileProperties_Command); + ctrlTileProperties.Initialize(TileMain, TileProperties_Command); } - 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; - + ctrlMapFileSelector.Initialize(Path.GetFileNameWithoutExtension(fileName), MapFileSelector_Command); + ctrlMapProperties.Initialize(ctrlMapFileSelector.Map); + btnTileInfoHide.Tapped += BtnTileInfoHide_Tapped; btnTileInfoShow.Tapped += BtnTileInfoShow_Tapped; @@ -359,10 +325,6 @@ private void _Initialize(string fileName) TileList_Command(TilePatternsList.ElementAt(0), "SELECTED"); } - btnPaper.Tapped += BtnPaper_Click; - btnInk.Tapped += BtnInk_Tapped; - UpdateColorPanel(); - InitializeShortcuts(); this.AddHandler(KeyDownEvent, Keyboard_Down, handledEventsToo: true); @@ -381,98 +343,11 @@ private void _Initialize(string fileName) } - #region Color - - private void BtnPaper_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e) - { - if (ctrlColorPicker.IsVisible) - { - ctrlColorPicker.IsVisible = false; - return; - } - var Tile = ctrlEditor.TileData; - if (Tile.GraphicMode == GraphicsModes.Monochrome) - { - return; - } - ctrlColorPicker.IsVisible = true; - ctrlColorPicker.Inicialize(Tile.GraphicMode, Tile.Palette, ctrlEditor.SecondaryColorIndex, ColorPickerPaper_Action); - } - - - private void BtnInk_Tapped(object? sender, TappedEventArgs e) - { - if (ctrlColorPicker.IsVisible) - { - ctrlColorPicker.IsVisible = false; - return; - } - var Tile = ctrlEditor.TileData; - if (Tile.GraphicMode == GraphicsModes.Monochrome) - { - return; - } - ctrlColorPicker.IsVisible = true; - ctrlColorPicker.Inicialize(Tile.GraphicMode, Tile.Palette, ctrlEditor.PrimaryColorIndex, ColorPickerInk_Action); - } - - - private void UpdateColorPanel() + public void Refresh() { - var Tile = ctrlEditor.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[ctrlEditor.PrimaryColorIndex]; - var paper = Tile.Palette[ctrlEditor.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 = ctrlEditor.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 = ctrlEditor.PrimaryColorIndex.ToString(); - } - break; - } - } - - - - private void ColorPickerPaper_Action(string command, int indexColor) - { - ctrlEditor.SecondaryColorIndex = indexColor; - UpdateColorPanel(); - } - - - private void ColorPickerInk_Action(string command, int indexColor) - { - ctrlEditor.PrimaryColorIndex = indexColor; - UpdateColorPanel(); + ctrlEditor.Refresh(); } - #endregion - - #region TileList private void TileList_Command(TilePatternControl sender, string command) @@ -490,9 +365,9 @@ private void TileList_Command(TilePatternControl sender, string command) TileList_Unselect(sender); ctrlEditor.TileData = sender.TileData; ctrlPreview.TileData = sender.TileData; + ctrlEditor.Frame = 0; ctrlPreview.Refresh(); - TileProperties_FrameUpdate(ctrlProperties, command); - txtFrame.Text = "0"; + TileProperties_FrameUpdate(ctrlTileProperties, command); break; } } @@ -527,7 +402,7 @@ private void TileProperties_Command(TilePropertiesControl sender, string command } ctrlEditor.TileData = selectedTile; TileList_Modified(selectedTile); - UpdateColorPanel(); + ctrlEditor.UpdateColorPanel(); } break; case "NAME": @@ -701,7 +576,7 @@ private void TileList_Delete(TilePatternControl control) wpTileList.Children.Remove(control); ctrlEditor.TileData = null; ctrlPreview.TileData = null; - ctrlProperties.TileData = null; + ctrlTileProperties.TileData = null; control = null; CalculateWrapPanelHeight(); } @@ -720,9 +595,8 @@ private void TileList_Insert(ZXMapsTile TileData) pats.AddRange(TileData.Patterns.Skip(current)); TileData.Patterns = pats; TileData.Frames++; - ctrlProperties.Refresh(); + ctrlTileProperties.Refresh(); ctrlEditor.Refresh(); - txtFrame.MaxHeight = TileData.Frames - 1; } @@ -829,7 +703,7 @@ private void BtnTileInfoHide_Tapped(object? sender, TappedEventArgs e) { btnTileInfoShow.IsVisible = true; btnTileInfoHide.IsVisible = false; - foreach(var ctrl in TilePatternsList) + foreach (var ctrl in TilePatternsList) { ctrl.InfoVisible = false; } @@ -858,6 +732,19 @@ private void Editor_Command(TilePatternEditor sender, string command) { 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); @@ -866,13 +753,13 @@ private void Editor_Command(TilePatternEditor sender, string command) cur.TileData = sender.TileData; cur.Refresh(); TileList_Modified(sender.TileData); - ctrlProperties.TileData = TileMain; - ctrlProperties.PrimaryColor = sender.PrimaryColorIndex; - ctrlProperties.SecondaryColor = sender.SecondaryColorIndex; + ctrlTileProperties.TileData = TileMain; + ctrlTileProperties.PrimaryColor = sender.PrimaryColorIndex; + ctrlTileProperties.SecondaryColor = sender.SecondaryColorIndex; ctrlPreview.TileData = sender.TileData; - btnColorPicker.IsChecked = !ctrlEditor.ColorPicker; - UpdateColorPanel(); + //btnColorPicker.IsChecked = !ctrlEditor.ColorPicker; + ctrlEditor.UpdateColorPanel(); } } break; @@ -884,362 +771,6 @@ private void Editor_Command(TilePatternEditor sender, string command) #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"; - ctrlEditor.Zoom = z; - } - - - private void ZoomIn() - { - int v = sldZoom.Value.ToInteger(); - if (v < zooms.Length - 1) - { - sldZoom.Value = v - 1; - } - } - - - private 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 (ctrlEditor.TileData != null) - { - ctrlEditor.TileData.CurrentFrame = actualFrame; - ctrlEditor.Refresh(); - } - Refresh(); - */ - } - - #endregion - - - #region ToolBar - - /// - /// Clear click - /// - /// - /// - private void BtnClear_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) - { - this.ctrlEditor.Clear(); - } - - /// - /// Cut click - /// - /// - /// - private void BtnCut_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) - { - this.ctrlEditor.Cut(); - } - - - //Copy click - private void BtnCopy_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) - { - this.ctrlEditor.Copy(); - } - - - /// - /// Paste click - /// - /// - /// - private void BtnPaste_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) - { - this.ctrlEditor.Paste(); - } - - - /// - /// Horizontal mirror click - /// - /// - /// - private void BtnHMirror_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) - { - this.ctrlEditor.HorizontalMirror(); - } - - - /// - /// Vertical mirror click - /// - /// - /// - private void BtnVMirror_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) - { - this.ctrlEditor.VerticalMirror(); - } - - - /// - /// Rotate left - /// - /// - /// - private void BtnRotateLeft_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) - { - this.ctrlEditor.RotateLeft(); - } - - - /// - /// Rotate right - /// - /// - /// - private void BtnRotateRight_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) - { - this.ctrlEditor.RotateRight(); - } - - - /// - /// Shift up - /// - /// - /// - private void BtnShiftUp_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) - { - this.ctrlEditor.ShiftUp(); - } - - - /// - /// Shift right - /// - /// - /// - private void BtnShiftRight_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) - { - this.ctrlEditor.ShiftRight(); - } - - - /// - /// Shift down - /// - /// - /// - private void BtnShiftDown_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) - { - this.ctrlEditor.ShiftDown(); - } - - - /// - /// Shift left - /// - /// - /// - private void BtnShiftLeft_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) - { - this.ctrlEditor.ShiftLeft(); - } - - - /// - /// Move up - /// - /// - /// - private void BtnMoveUp_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) - { - this.ctrlEditor.MoveUp(); - } - - - /// - /// Move right - /// - /// - /// - private void BtnMoveRight_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) - { - this.ctrlEditor.MoveRight(); - } - - - /// - /// Move down - /// - /// - /// - private void BtnMoveDown_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) - { - this.ctrlEditor.MoveDown(); - } - - - /// - /// Move left - /// - /// - /// - private void BtnMoveLeft_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) - { - this.ctrlEditor.MoveLeft(); - } - - - /// - /// Invert - /// - /// - /// - private void BtnInvert_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) - { - this.ctrlEditor.Invert(); - } - - - /// - /// Mask - /// - /// - /// - private void BtnMask_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) - { - this.ctrlEditor.Mask(); - } - - - private void BtnExport_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) - { - Export(); - } - - - private void Export() - { - /* - var dlg = new TileExportDialog(); - dlg.Initialize(FileName, TilePatternsList.Select(d => d.TileData)); - dlg.ShowDialog(this.VisualRoot as Window); - */ - } - - - private void BtnImport_Tapped(object? sender, TappedEventArgs e) - { - Import(); - } - - - - private void BtnViewAttributes_Tapped(object? sender, TappedEventArgs e) - { - ctrlEditor.ViewAttributes = btnViewAttributes.IsChecked == true; - } - - private void BtnColorPicker_Tapped(object? sender, TappedEventArgs e) - { - ctrlEditor.ColorPicker = btnColorPicker.IsChecked == false; - } - - - private void BtnInvertPixelsCell_Tapped(object? sender, TappedEventArgs e) - { - ctrlEditor.InvertPixelsCell = btnInvertPixelsCell.IsChecked == false; - if (ctrlEditor.InvertPixelsCell) - { - btnInvertColorsCell.IsChecked = true; - ctrlEditor.InvertColorsCell = false; - } - } - - - private void BtnInvertColorsCell_Tapped(object? sender, TappedEventArgs e) - { - ctrlEditor.InvertColorsCell = btnInvertColorsCell.IsChecked == false; - if (ctrlEditor.InvertColorsCell) - { - btnInvertPixelsCell.IsChecked = true; - ctrlEditor.InvertPixelsCell = false; - } - } - - - private void BtnRedo_Tapped(object? sender, TappedEventArgs e) - { - ctrlEditor.Redo(); - } - - - private void BtnUndo_Tapped(object? sender, TappedEventArgs e) - { - ctrlEditor.Undo(); - } - - #endregion - - #region Keyboard shortcus private Dictionary _keybCommands = new Dictionary(); @@ -1303,9 +834,9 @@ private void InitializeShortcuts() { keyboardCommands["Move Left"].CommandId, () => { ctrlEditor.MoveLeft(); } }, { keyboardCommands["Invert"].CommandId, () => { ctrlEditor.Invert(); } }, { keyboardCommands["Mask"].CommandId, () => { ctrlEditor.Mask(); } }, - { keyboardCommands["Export"].CommandId, () => { Export(); } }, - { keyboardCommands["Zoom In"].CommandId, () => { ZoomIn(); } }, - { keyboardCommands["Zoom Out"].CommandId, () => { ZoomOut(); } }, + { keyboardCommands["Export"].CommandId, () => { ctrlEditor.Export(); } }, + { keyboardCommands["Zoom In"].CommandId, () => { ctrlEditor.ZoomIn(); } }, + { keyboardCommands["Zoom Out"].CommandId, () => { ctrlEditor.ZoomOut(); } }, }; } @@ -1327,48 +858,37 @@ private void Keyboard_Down(object? sender, Avalonia.Input.KeyEventArgs e) } } - #endregion + private void tabControlEditor_SelectionChanged(object? sender, SelectionChangedEventArgs e) + { + //if (tabControlEditor == null) + //{ + // return; + //} + //int idTab = tabControlEditor.SelectedIndex; + //if (idTab == 0) + //{ + // dockTileProperties.IsVisible = true; + // dockMapProperties.IsVisible = false; + //} + //else + //{ + // dockTileProperties.IsVisible = false; + // dockMapProperties.IsVisible = true; + //} + } - #region Import + #endregion - private void Import() - { - /* - var dlg = new TileImportDialog(); - dlg.Initialize(FileName, TilePatternsList.Select(d => d.TileData), Import_Command); - dlg.ShowDialog(this.VisualRoot as Window); - */ - } + #region MapFileSelector - private void Import_Command(ZXMapsTile Tile, string command) + private void MapFileSelector_Command(MapFileSelectorControl sender,string command) { - try - { - switch (command) - { - case "ADD": - TileList_AddTile(Tile); - ctrlEditor.TileData = Tile; - ctrlPreview.TileData = Tile; - ctrlPreview.Refresh(); - ctrlProperties.TileData = TileMain; - //TileList_Modified(sender.TileData); - break; - case "UPDATE": - //var spr = TilePatternsList.FirstOrDefault(d => d.Name == Tile.Name); - //if (spr != null) - //{ - // TileList_Modified(Tile); - //} - break; - } - } - catch { } + Map = sender.Map; + ctrlMapProperties.Map = sender.Map; } - #endregion } } diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TilePatternEditor.axaml b/ZXBStudio/DocumentEditors/ZXMaps/TilePatternEditor.axaml index 2d724ff..4b5d7ec 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/TilePatternEditor.axaml +++ b/ZXBStudio/DocumentEditors/ZXMaps/TilePatternEditor.axaml @@ -1,11 +1,148 @@ - - - + 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:zxgraphics="using:ZXBasicStudio.DocumentEditors.ZXGraphics" + xmlns:local="clr-namespace:ZXBasicStudio.DocumentEditors.ZXGraphics" + mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" + x:Class="ZXBasicStudio.DocumentEditors.ZXMaps.TilePatternEditor"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Zoom 24x + + + Frame + + + + + + Paper + + + 0 + + + + + + Ink + + + 1 + + + + + + + + diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TilePatternEditor.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/TilePatternEditor.axaml.cs index 19cb02a..beff279 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/TilePatternEditor.axaml.cs +++ b/ZXBStudio/DocumentEditors/ZXMaps/TilePatternEditor.axaml.cs @@ -1,3 +1,4 @@ +using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Shapes; using Avalonia.Input; @@ -21,6 +22,26 @@ 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 /// @@ -38,15 +59,14 @@ public ZXMapsTile TileData if (_TileData == null) { lastId = null; + return; } - else + if (lastId != _TileData.Id) { - if (lastId != _TileData.Id) - { - _TileData.CurrentFrame = 0; - lastId = _TileData.Id; - } + _TileData.CurrentFrame = 0; + lastId = _TileData.Id; } + txtFrame.MaxHeight = _TileData.Frames - 1; Undo_AddPoint(); Refresh(true); } @@ -95,7 +115,7 @@ public bool ViewAttributes aspect.ViewAttributes = value; Refresh(false); } - } + } private bool _ViewAttributes = true; @@ -105,6 +125,18 @@ public bool ViewAttributes public bool ColorPicker { get; set; } = false; + public int Frame + { + get + { + return txtFrame.Text.ToInteger(); + } + set + { + txtFrame.Text = value.ToString(); + } + } + #endregion @@ -150,6 +182,43 @@ public TilePatternEditor() 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(); + } @@ -267,7 +336,7 @@ private void Undo_AddPoint() return; // No changes } } - operations = operations.Take(operationIndex+1).ToList(); + operations = operations.Take(operationIndex + 1).ToList(); var op = TileData.Patterns[TileData.CurrentFrame].Clonar(); operations.Add(op); @@ -309,7 +378,7 @@ private void GrdEditor_PointerPressed(object? sender, Avalonia.Input.PointerPres int y = (int)p.Position.Y; x = x / (_Zoom + 1); y = y / (_Zoom + 1); - var atr=GetAttribute(TileData.Patterns[TileData.CurrentFrame], x,y); + var atr = GetAttribute(TileData.Patterns[TileData.CurrentFrame], x, y); PrimaryColorIndex = atr.Ink; SecondaryColorIndex = atr.Paper; ColorPicker = false; @@ -338,7 +407,7 @@ private void GrdEditor_PointerPressed(object? sender, Avalonia.Input.PointerPres MouseRightPressed = true; } } - } + } /// @@ -476,7 +545,7 @@ private void SetAttribute(Pattern pattern, int x, int y) private AttributeColor GetAttribute(Pattern pattern, int x, int y) { - if(pattern.Attributes == null) + if (pattern.Attributes == null) { pattern.Attributes = new AttributeColor[(TileData.Width / 8) * (TileData.Height / 8)]; } @@ -1132,7 +1201,7 @@ private void GrdEditor_InvertPixelsCell(double mx, double my) return; } - for(int py = 0; py < 8; py++) + for (int py = 0; py < 8; py++) { for (int px = 0; px < 8; px++) { @@ -1167,7 +1236,7 @@ private void GrdEditor_InvertColorsCell(double mx, double my) var inkBak = PrimaryColorIndex; var paperBak = SecondaryColorIndex; - var attr=GetAttribute(TileData.Patterns[TileData.CurrentFrame], x, y); + var attr = GetAttribute(TileData.Patterns[TileData.CurrentFrame], x, y); if (attr == null) { return; @@ -1176,9 +1245,498 @@ private void GrdEditor_InvertColorsCell(double mx, double my) SecondaryColorIndex = attr.Ink; SetAttribute(TileData.Patterns[TileData.CurrentFrame], x, y); PrimaryColorIndex = inkBak; - SecondaryColorIndex=paperBak; + 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() + { + /* + var dlg = new TileExportDialog(); + dlg.Initialize(FileName, TilePatternsList.Select(d => d.TileData)); + dlg.ShowDialog(this.VisualRoot as Window); + */ + } + + + private void BtnImport_Tapped(object? sender, TappedEventArgs e) + { + Import(); + } + + + + private void BtnViewAttributes_Tapped(object? sender, TappedEventArgs e) + { + ViewAttributes = btnViewAttributes.IsChecked == true; + } + + private void BtnColorPicker_Tapped(object? sender, TappedEventArgs e) + { + ColorPicker = btnColorPicker.IsChecked == false; + } + + + private void BtnInvertPixelsCell_Tapped(object? sender, TappedEventArgs e) + { + InvertPixelsCell = btnInvertPixelsCell.IsChecked == false; + if (InvertPixelsCell) + { + btnInvertColorsCell.IsChecked = true; + InvertColorsCell = false; + } + } + + + private void BtnInvertColorsCell_Tapped(object? sender, TappedEventArgs e) + { + InvertColorsCell = btnInvertColorsCell.IsChecked == false; + if (InvertColorsCell) + { + btnInvertPixelsCell.IsChecked = true; + InvertPixelsCell = false; + } + } + + + private void BtnRedo_Tapped(object? sender, TappedEventArgs e) + { + Redo(); + } + + + private void BtnUndo_Tapped(object? sender, TappedEventArgs e) + { + Undo(); + } + + #endregion + + + + #region Import + + private void Import() + { + /* + var dlg = new TileImportDialog(); + dlg.Initialize(FileName, 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": + TileData = Tile; + CallBackCommand?.Invoke(this, "ADD"); + break; + case "UPDATE": + //var spr = TilePatternsList.FirstOrDefault(d => d.Name == Tile.Name); + //if (spr != null) + //{ + // TileList_Modified(Tile); + //} + break; + } + } + catch { } + } + + #endregion } } diff --git a/ZXBStudio/DocumentEditors/ZXMaps/ZXMapsEditor.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/ZXMapsEditor.axaml.cs index 1f208d7..b81821f 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/ZXMapsEditor.axaml.cs +++ b/ZXBStudio/DocumentEditors/ZXMaps/ZXMapsEditor.axaml.cs @@ -9,6 +9,7 @@ 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; @@ -122,36 +123,10 @@ public override bool SaveDocument(TextWriter OutputLog) { try { - /* - var masterList = SpritePatternsList.Select(d => d.SpriteData).ToArray(); - var sprList = new List(); - foreach (var spr in masterList) - { - sprList.Add(spr); //.Clonar()); - } - foreach (Tile spr in sprList) - { - if (spr == null) - { - continue; - } - - if (spr.Frames < spr.Patterns.Count()) - { - spr.Patterns = spr.Patterns.Take(spr.Frames).ToList(); - } - } - - var dataJSon = sprList.Serializar(); - //if (!ServiceLayer.Files_SaveFileString(FileName, dataJSon)) - //{ - // return false; - //} - //; - + var json = Map.Serializar(); + File.WriteAllText(FileName, json); _Modified = false; DocumentSaved?.Invoke(this, EventArgs.Empty); - */ return true; } catch (Exception ex) @@ -164,6 +139,7 @@ public override bool SaveDocument(TextWriter OutputLog) public override bool RenameDocument(string NewName, TextWriter OutputLog) { + Map.Name = Path.GetFileNameWithoutExtension(NewName); FileName = NewName; return true; } @@ -247,6 +223,7 @@ public ZXMapsEditor(string fileName) if (Map == null) { Map = new ZXMapsMap(); + Map.Name = Path.GetFileNameWithoutExtension(FileName); Map.Height = 12; Map.Width=16; Map.MappingType = ZXMapsMappingTypes.Sequential; @@ -330,7 +307,7 @@ public void Refresh() //cnvEditor.Width = WindowWidth * Zoom; //cnvEditor.Height = WindowHeight * Zoom; } - + public void Draw_Panel(ControlItem control) { diff --git a/ZXBStudio/ZXBasicStudio.csproj b/ZXBStudio/ZXBasicStudio.csproj index 0fbe52b..f773cf5 100644 --- a/ZXBStudio/ZXBasicStudio.csproj +++ b/ZXBStudio/ZXBasicStudio.csproj @@ -642,6 +642,9 @@ LayerItemControl.axaml + + MapFileSelectorControl.axaml + MapPropertiesControl.axaml From 2546297915d41d47405a34d9b0c6690184f27988 Mon Sep 17 00:00:00 2001 From: Meta-Agents Date: Wed, 22 Jul 2026 10:52:09 +0200 Subject: [PATCH 07/18] Starting with map editor --- .../ZXMaps/MapFileSelectorControl.axaml.cs | 33 ++---- .../ZXMaps/MapPropertiesControl.axaml | 15 ++- .../ZXMaps/MapPropertiesControl.axaml.cs | 110 +++++++++++++++++- .../DocumentEditors/ZXMaps/Neg/ZXMapsTiles.cs | 1 + .../DocumentEditors/ZXMaps/Neg/ZXMapsTypes.cs | 8 +- .../DocumentEditors/ZXMaps/TileEditor.axaml | 2 +- .../ZXMaps/TileEditor.axaml.cs | 89 +++++++------- .../ZXMaps/ZXMapsEditor.axaml.cs | 8 +- .../ZXMaps/ZXTilemapDocument.cs | 2 +- ZXBasicStudio.Log/Class1.cs | 7 ++ ZXBasicStudio.Log/ZXBasicStudio.Log.csproj | 9 ++ 11 files changed, 200 insertions(+), 84 deletions(-) create mode 100644 ZXBasicStudio.Log/Class1.cs create mode 100644 ZXBasicStudio.Log/ZXBasicStudio.Log.csproj diff --git a/ZXBStudio/DocumentEditors/ZXMaps/MapFileSelectorControl.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/MapFileSelectorControl.axaml.cs index 6613e7a..64f1d2a 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/MapFileSelectorControl.axaml.cs +++ b/ZXBStudio/DocumentEditors/ZXMaps/MapFileSelectorControl.axaml.cs @@ -17,6 +17,7 @@ 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; @@ -64,8 +65,10 @@ public bool Initialize(ZXMapsMap map, Action cal public bool Initialize(string mapName, Action callBackComando) { this.Comando = callBackComando; - LoadMap(mapName); - + if (!string.IsNullOrEmpty(mapName)) + { + Map = ServiceLayer_Maps.Maps_LoadMapByName(mapName); + } FillCombo(); return true; } @@ -73,7 +76,7 @@ public bool Initialize(string mapName, Action ca public void Refresh() { - + } @@ -131,7 +134,7 @@ private void cmbFileName_SelectionChanged(object? sender, SelectionChangedEventA try { var name = cmbFileName.Text; - LoadMap(name); + Map = ServiceLayer_Maps.Maps_LoadMapByName(name); Comando?.Invoke(this, "SELECTED"); } catch (Exception ex) @@ -140,27 +143,5 @@ private void cmbFileName_SelectionChanged(object? sender, SelectionChangedEventA } } - - - private void LoadMap(string name) - { - try - { - var fileName = Path.Combine(ZXProjectManager.Current.ProjectPath, name + ".zxmap"); - if (File.Exists(fileName)) - { - var json = File.ReadAllText(fileName); - Map = json.Deserializar(); - } - else - { - Map = null; - } - } - catch (Exception ex) - { - //MessageBox.Show($"Error loading file {FileName}: {ex.Message}"); - } - } } } \ No newline at end of file diff --git a/ZXBStudio/DocumentEditors/ZXMaps/MapPropertiesControl.axaml b/ZXBStudio/DocumentEditors/ZXMaps/MapPropertiesControl.axaml index c06add6..d662de6 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/MapPropertiesControl.axaml +++ b/ZXBStudio/DocumentEditors/ZXMaps/MapPropertiesControl.axaml @@ -46,20 +46,19 @@ (pixels) - - Mapping type: - + + Mapping type: + Sequential - + - + @@ -80,7 +79,7 @@ - + @@ -93,7 +92,7 @@ - + diff --git a/ZXBStudio/DocumentEditors/ZXMaps/MapPropertiesControl.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/MapPropertiesControl.axaml.cs index 2037cf1..87d1e09 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/MapPropertiesControl.axaml.cs +++ b/ZXBStudio/DocumentEditors/ZXMaps/MapPropertiesControl.axaml.cs @@ -9,6 +9,7 @@ using AvaloniaEdit.Folding; using System; using System.Collections.Generic; +using System.Data; using System.IO; using System.Linq; using System.Security.Cryptography; @@ -38,7 +39,22 @@ public ZXMapsMap Map } } private ZXMapsMap _Map = null; - private int CurrentTap = 0; + + public ZXMapsTiles Tiles + { + get + { + return _Tiles; + } + set + { + _Tiles = value; + Refresh(); + } + } + private ZXMapsTiles _Tiles = null; + + private Action Command = null; public MapPropertiesControl() @@ -47,11 +63,24 @@ public MapPropertiesControl() } - public bool Initialize(ZXMapsMap map) + public bool Initialize(ZXMapsMap map, ZXMapsTiles tiles, Action callBackCommand) { - this.Map = map; + 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() @@ -82,6 +111,79 @@ public void Refresh() 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/ZXMapsTiles.cs b/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsTiles.cs index b907410..2fa1cf1 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsTiles.cs +++ b/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsTiles.cs @@ -14,6 +14,7 @@ public class ZXMapsTiles 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 index ca8b155..3f274c6 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsTypes.cs +++ b/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsTypes.cs @@ -2,9 +2,9 @@ { public enum ZXMapsTypes { - Rooms = 0, - HorizontalScroll = 1, - VerticalScroll = 2, - MultidirectionalScroll = 3 + 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 index 6121dd8..38dba55 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml +++ b/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml @@ -87,7 +87,7 @@ - + diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml.cs index 334c6f7..32d24c8 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml.cs +++ b/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml.cs @@ -23,6 +23,8 @@ using System.Reflection; using FFmpeg.AutoGen; using Avalonia.Media; +using ZXBasicStudio.DocumentEditors.ZXMaps.Log; +using ZXBasicStudio.Controls; namespace ZXBasicStudio.DocumentEditors.ZXMaps { @@ -95,6 +97,8 @@ public void OnNext(AvaloniaPropertyChangedEventArgs value) } + + #endregion @@ -152,7 +156,7 @@ public override bool SaveDocument(TextWriter OutputLog) if (Map != null) { var json = Map.Serializar(); - var fileName = Path.Combine(ZXProjectManager.Current.ProjectPath, Map.Name+ ".zxmap"); + var fileName = Path.Combine(ZXProjectManager.Current.ProjectPath, Map.Name + ".zxmap"); if (!ServiceLayer.Files_SaveFileString(fileName, json)) { return false; @@ -174,12 +178,15 @@ public override bool SaveDocument(TextWriter OutputLog) 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; } @@ -251,51 +258,30 @@ private void _Initialize(string fileName) TilePatternsList = new List(); - var data = ServiceLayer.GetFileData(fileName); - if (data == null || data.Length == 0) + 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(".til", "").Replace(".zxtil", ""), + Name = Path.GetFileName(fileName).ToStringNoNull().Replace(".zxtil", ""), Tiles = new List() }; } - if (data != null) - { - var dataS = Encoding.UTF8.GetString(data); - if (!string.IsNullOrEmpty(dataS)) - { - TileMain = dataS.Deserializar(); - - wpTileList.ItemWidth = (TileMain.Width * 4) + 4; - wpTileList.ItemHeight = (TileMain.Height * 4) + 4; - foreach (var Tile in TileMain.Tiles) - { - // Check attributes for ZX Spectrum mode - if (Tile != null && Tile.Patterns != null) - { - var al = (Tile.Width / 8) * (Tile.Height / 8); - foreach (var pattern in Tile.Patterns) - { - if (pattern.Attributes != null) - { - pattern.Attributes = pattern.Attributes.Take(al).ToArray(); - } - } - } + wpTileList.ItemWidth = (TileMain.Width * 4) + 4; + wpTileList.ItemHeight = (TileMain.Height * 4) + 4; - // 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(); - } - } + 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) @@ -312,9 +298,9 @@ private void _Initialize(string fileName) ctrlTileProperties.Initialize(TileMain, TileProperties_Command); } - ctrlMapFileSelector.Initialize(Path.GetFileNameWithoutExtension(fileName), MapFileSelector_Command); - ctrlMapProperties.Initialize(ctrlMapFileSelector.Map); - + ctrlMapFileSelector.Initialize(TileMain.DefaultMap, MapFileSelector_Command); + ctrlMapProperties.Initialize(ctrlMapFileSelector.Map, TileMain, MapProperties_Command); + btnTileInfoHide.Tapped += BtnTileInfoHide_Tapped; btnTileInfoShow.Tapped += BtnTileInfoShow_Tapped; @@ -883,10 +869,35 @@ private void tabControlEditor_SelectionChanged(object? sender, SelectionChangedE #region MapFileSelector - private void MapFileSelector_Command(MapFileSelectorControl sender,string command) + 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); + } + } + + #endregion + + + #region MapProperties + + private void MapProperties_Command(MapPropertiesControl control, string arg2) + { + } #endregion diff --git a/ZXBStudio/DocumentEditors/ZXMaps/ZXMapsEditor.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/ZXMapsEditor.axaml.cs index b81821f..02019ce 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/ZXMapsEditor.axaml.cs +++ b/ZXBStudio/DocumentEditors/ZXMaps/ZXMapsEditor.axaml.cs @@ -13,6 +13,7 @@ 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; @@ -243,14 +244,19 @@ public ZXMapsEditor(string fileName) }); Map.PropertyDefinitons = new List(); + ServiceLayer_Maps.Maps_SaveMap(FileName, Map); } InitializeComponent(); ctrlLayers.Initialize(Map.Layers); - ctrlProperties.Initialize(Map); + ctrlProperties.Initialize(Map,null,MapProperties_Command); } + private void MapProperties_Command(MapPropertiesControl control, string arg2) + { + //throw new NotImplementedException(); + } public bool Initialize() { diff --git a/ZXBStudio/IntegratedDocumentTypes/ZXMaps/ZXTilemapDocument.cs b/ZXBStudio/IntegratedDocumentTypes/ZXMaps/ZXTilemapDocument.cs index c37042d..c01f3e2 100644 --- a/ZXBStudio/IntegratedDocumentTypes/ZXMaps/ZXTilemapDocument.cs +++ b/ZXBStudio/IntegratedDocumentTypes/ZXMaps/ZXTilemapDocument.cs @@ -18,7 +18,7 @@ namespace ZXBasicStudio.IntegratedDocumentTypes.ZXGraphics { public class ZXTilemapDocument : IZXDocumentType { - static readonly string[] _docExtensions = { ".til", ".zxtil" }; + 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"; 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 + + + From 642e029f2e58d19ab32fed2024d45dec62ec5099 Mon Sep 17 00:00:00 2001 From: Meta-Agents Date: Wed, 22 Jul 2026 14:36:18 +0200 Subject: [PATCH 08/18] Working in map pattern editor --- .../ZXMaps/MapPatternEditor.axaml | 150 ++++ .../ZXMaps/MapPatternEditor.axaml.cs | 838 ++++++++++++++++++ .../DocumentEditors/ZXMaps/Neg/ZXMapsMap.cs | 1 + .../ZXMaps/Neg/ZXMapsTileArray.cs | 39 + .../DocumentEditors/ZXMaps/TileEditor.axaml | 9 +- .../ZXMaps/TileEditor.axaml.cs | 40 +- ZXBStudio/DocumentEditors/ZXMaps/TileImage.cs | 15 +- ZXBStudio/ZXBasicStudio.csproj | 3 + 8 files changed, 1075 insertions(+), 20 deletions(-) create mode 100644 ZXBStudio/DocumentEditors/ZXMaps/MapPatternEditor.axaml create mode 100644 ZXBStudio/DocumentEditors/ZXMaps/MapPatternEditor.axaml.cs create mode 100644 ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsTileArray.cs diff --git a/ZXBStudio/DocumentEditors/ZXMaps/MapPatternEditor.axaml b/ZXBStudio/DocumentEditors/ZXMaps/MapPatternEditor.axaml new file mode 100644 index 0000000..4bd0f3c --- /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..bcb84bf --- /dev/null +++ b/ZXBStudio/DocumentEditors/ZXMaps/MapPatternEditor.axaml.cs @@ -0,0 +1,838 @@ +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; + + + /// + /// 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(); + + PrimaryColorIndex = 1; + SecondaryColorIndex = 0; + + cnvEditor.PointerMoved += CnvEditor_PointerMoved; + cnvEditor.PointerPressed += CnvEditor_PointerPressed; + cnvEditor.PointerReleased += CnvEditor_PointerReleased; + cnvEditor.PointerExited += CnvEditor_PointerExited; + + 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.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); + var aspect = new TileImage(); + aspect.RenderTile(tile, 0); + i.BackgroundImage = aspect; + } + } + } + } + + cnvEditor.Width = images[0, 0].Width * _map.Width; + cnvEditor.Height = images[0, 0].Height * _map.Height; + + 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]; + map.TileArrays + 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(); + } + */ + } + + #endregion + + + #region Drawing events + + private bool pointerPressed = false; + + private void I_PointerExited(object? sender, PointerEventArgs e) + { + ZXGridImageView i = sender as ZXGridImageView; + i.BorderBrush = null; + i.BorderThickness = new Thickness(0); + } + + + private void I_PointerEntered(object? sender, PointerEventArgs e) + { + ZXGridImageView i = sender as ZXGridImageView; + i.BorderBrush = new SolidColorBrush(Colors.Red); + i.BorderThickness = new Thickness(1); + } + + + private void I_PointerPressed(object? sender, PointerPressedEventArgs e) + { + pointerPressed = true; + ZXGridImageView i = sender as ZXGridImageView; + int id = i.Tag.ToInteger(); + int y = id / _map.Width; + int x = id - (y * _map.Width); + _map.TileArrays[x, y].TileId = CurrentTile; + Refresh(true); + } + + + private void I_PointerReleased(object? sender, PointerReleasedEventArgs e) + { + pointerPressed = false; + } + + + private void I_PointerMoved(object? sender, PointerEventArgs e) + { + ZXGridImageView i = sender as ZXGridImageView; + i.BorderBrush = new SolidColorBrush(Colors.Red); + i.BorderThickness = new Thickness(1); + + //if (pointerPressed) + //{ + // int id = i.Tag.ToInteger(); + // Console.WriteLine(id.ToString()); + // int y = id / _map.Width; + // int x = id - (y * _map.Width); + // _map.TileArrays[x, y].TileId = CurrentTile; + // Refresh(false); + //} + } + + + + /// + /// Event for mouse pressed + /// + /// + /// + private void CnvEditor_PointerPressed(object? sender, Avalonia.Input.PointerPressedEventArgs e) + { + //var p = e.GetCurrentPoint(CnvEditor); + + //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) + //{ + // CnvEditor_InvertPixelsCell(p.Position.X, p.Position.Y); + //} + //else if (InvertColorsCell) + //{ + // CnvEditor_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 CnvEditor_PointerReleased(object? sender, Avalonia.Input.PointerReleasedEventArgs e) + { + MouseLeftPressed = false; + MouseRightPressed = false; + } + + + /// + /// Event for pointer moved outside control + /// + /// + /// + private void CnvEditor_PointerExited(object? sender, Avalonia.Input.PointerEventArgs e) + { + MouseLeftPressed = false; + MouseRightPressed = false; + } + + + /// + /// Event for pointer moved + /// + /// + /// + private void CnvEditor_PointerMoved(object? sender, Avalonia.Input.PointerEventArgs e) + { + //var p = e.GetCurrentPoint(cnvEditor); + //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); + } + + #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 + + /// + /// 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() + { + /* + var dlg = new TileExportDialog(); + dlg.Initialize(FileName, TilePatternsList.Select(d => d.TileData)); + dlg.ShowDialog(this.VisualRoot as Window); + */ + } + + #endregion + } +} diff --git a/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsMap.cs b/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsMap.cs index c214090..f877e47 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsMap.cs +++ b/ZXBStudio/DocumentEditors/ZXMaps/Neg/ZXMapsMap.cs @@ -20,5 +20,6 @@ public class ZXMapsMap 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/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/TileEditor.axaml b/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml index 38dba55..4f36d14 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml +++ b/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml @@ -54,19 +54,20 @@ - + - + + - + diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml.cs index 32d24c8..59c1b50 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml.cs +++ b/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml.cs @@ -300,6 +300,7 @@ private void _Initialize(string fileName) ctrlMapFileSelector.Initialize(TileMain.DefaultMap, MapFileSelector_Command); ctrlMapProperties.Initialize(ctrlMapFileSelector.Map, TileMain, MapProperties_Command); + ctrlMapEditor.Initialize(ctrlMapFileSelector.Map, TileMain, MapEditor_Command); btnTileInfoHide.Tapped += BtnTileInfoHide_Tapped; btnTileInfoShow.Tapped += BtnTileInfoShow_Tapped; @@ -334,6 +335,7 @@ public void Refresh() ctrlEditor.Refresh(); } + #region TileList private void TileList_Command(TilePatternControl sender, string command) @@ -354,6 +356,10 @@ private void TileList_Command(TilePatternControl sender, string command) ctrlEditor.Frame = 0; ctrlPreview.Refresh(); TileProperties_FrameUpdate(ctrlTileProperties, command); + if (sender.TileData != null) + { + ctrlMapEditor.CurrentTile = sender.TileData.Id; + } break; } } @@ -846,22 +852,15 @@ private void Keyboard_Down(object? sender, Avalonia.Input.KeyEventArgs e) private void tabControlEditor_SelectionChanged(object? sender, SelectionChangedEventArgs e) { - //if (tabControlEditor == null) - //{ - // return; - //} + if (tabControlEditor == null) + { + return; + } - //int idTab = tabControlEditor.SelectedIndex; - //if (idTab == 0) - //{ - // dockTileProperties.IsVisible = true; - // dockMapProperties.IsVisible = false; - //} - //else - //{ - // dockTileProperties.IsVisible = false; - // dockMapProperties.IsVisible = true; - //} + if (tabControlEditor.SelectedIndex == 1) + { + ctrlMapEditor.Refresh(false); + } } #endregion @@ -887,6 +886,7 @@ private void MapFileSelector_Command(MapFileSelectorControl sender, string comma { _Modified = true; DocumentModified?.Invoke(this, EventArgs.Empty); + ctrlMapEditor.Map = Map; } } @@ -901,5 +901,15 @@ private void MapProperties_Command(MapPropertiesControl control, string arg2) } #endregion + + + #region MapEditor + + private void MapEditor_Command(MapPatternEditor editor, string arg2) + { + + } + + #endregion } } diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TileImage.cs b/ZXBStudio/DocumentEditors/ZXMaps/TileImage.cs index 6caa726..7992019 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/TileImage.cs +++ b/ZXBStudio/DocumentEditors/ZXMaps/TileImage.cs @@ -25,21 +25,26 @@ public class TileImage : IZXBitmap, IDisposable #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 @@ -61,6 +66,7 @@ public unsafe void Clear(Color ClearColor) IsEmpty = true; } + public unsafe void RenderTile(ZXMapsTile Tile, int FrameNumber) { try @@ -71,7 +77,7 @@ public unsafe void RenderTile(ZXMapsTile Tile, int FrameNumber) 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); + 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(); @@ -144,6 +150,7 @@ public unsafe void RenderTile(ZXMapsTile Tile, int FrameNumber) #endregion #region Private functions + uint ToRgba(PaletteColor Color) { return (uint)((255 << 24) | (Color.Blue << 16) | (Color.Green << 8) | Color.Red); @@ -178,6 +185,7 @@ private AttributeColor GetAttribute(ZXMapsTile Tile, Pattern Pattern, int X, int } return Pattern.Attributes[dir]; } + #endregion #region IZXBitmap implementation @@ -189,6 +197,7 @@ public Size Size } } + public PixelSize PixelSize { get @@ -197,13 +206,16 @@ public PixelSize 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) @@ -213,6 +225,7 @@ public void Dispose() bitmap = null; IsEmpty = true; } + #endregion } } diff --git a/ZXBStudio/ZXBasicStudio.csproj b/ZXBStudio/ZXBasicStudio.csproj index f773cf5..ba0b5d9 100644 --- a/ZXBStudio/ZXBasicStudio.csproj +++ b/ZXBStudio/ZXBasicStudio.csproj @@ -651,6 +651,9 @@ LayersControl.axaml + + MapPatternEditor.axaml + TilePropertiesControl.axaml From 56a2fbdd5f30c64214418d4697e3997e366d1bc5 Mon Sep 17 00:00:00 2001 From: Meta-Agents Date: Wed, 22 Jul 2026 19:05:03 +0200 Subject: [PATCH 09/18] Map editor: Undo & redo --- .../ZXGraphics/ZXGridImageView.axaml.cs | 4 +- .../ZXMaps/MapPatternEditor.axaml.cs | 319 ++++-------------- 2 files changed, 71 insertions(+), 252 deletions(-) diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/ZXGridImageView.axaml.cs b/ZXBStudio/DocumentEditors/ZXGraphics/ZXGridImageView.axaml.cs index e063b0e..14c9d03 100644 --- a/ZXBStudio/DocumentEditors/ZXGraphics/ZXGridImageView.axaml.cs +++ b/ZXBStudio/DocumentEditors/ZXGraphics/ZXGridImageView.axaml.cs @@ -170,13 +170,13 @@ public override void Render(DrawingContext context) 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/ZXMaps/MapPatternEditor.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/MapPatternEditor.axaml.cs index bcb84bf..3d5d132 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/MapPatternEditor.axaml.cs +++ b/ZXBStudio/DocumentEditors/ZXMaps/MapPatternEditor.axaml.cs @@ -155,8 +155,6 @@ public MapPatternEditor() cnvEditor.PointerMoved += CnvEditor_PointerMoved; cnvEditor.PointerPressed += CnvEditor_PointerPressed; - cnvEditor.PointerReleased += CnvEditor_PointerReleased; - cnvEditor.PointerExited += CnvEditor_PointerExited; sldZoom.PropertyChanged += SldZoom_PropertyChanged; txtFrame.PropertyChanged += TxtFrame_PropertyChanged; @@ -209,12 +207,12 @@ public void Refresh(bool withCallBack) var i = new ZXGridImageView(); i.Show8x8Grid = false; i.ViewAttributes = false; - 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); + //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); } @@ -275,7 +273,7 @@ public void Refresh(bool withCallBack) CallBackCommand?.Invoke(this, "REFRESH"); } } - + #endregion @@ -286,7 +284,6 @@ public void Refresh(bool withCallBack) public void Undo() { - /* if (operationIndex > operations.Count - 1) { operationIndex = operations.Count - 1; @@ -296,26 +293,18 @@ public void Undo() return; } var op = operations[operationIndex]; - map.TileArrays - if (Pattern_Equals(TileData.Patterns[TileData.CurrentFrame], op)) + if (op == null) { - operationIndex--; - Undo(); return; } - if (op != null) - { - TileData.Patterns[TileData.CurrentFrame] = op; - Refresh(); - } - CallBackCommand(this, "REFRESH"); - */ + _map.TileArrays[op.X, op.Y] = op; + operationIndex--; + Refresh(true); } public void Redo() { - /* if (operationIndex > operations.Count - 1) { return; @@ -325,18 +314,9 @@ public void Redo() 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(); - } - */ + _map.TileArrays[op.X, op.Y] = op; + operationIndex++; + Refresh(true); } #endregion @@ -344,61 +324,6 @@ public void Redo() #region Drawing events - private bool pointerPressed = false; - - private void I_PointerExited(object? sender, PointerEventArgs e) - { - ZXGridImageView i = sender as ZXGridImageView; - i.BorderBrush = null; - i.BorderThickness = new Thickness(0); - } - - - private void I_PointerEntered(object? sender, PointerEventArgs e) - { - ZXGridImageView i = sender as ZXGridImageView; - i.BorderBrush = new SolidColorBrush(Colors.Red); - i.BorderThickness = new Thickness(1); - } - - - private void I_PointerPressed(object? sender, PointerPressedEventArgs e) - { - pointerPressed = true; - ZXGridImageView i = sender as ZXGridImageView; - int id = i.Tag.ToInteger(); - int y = id / _map.Width; - int x = id - (y * _map.Width); - _map.TileArrays[x, y].TileId = CurrentTile; - Refresh(true); - } - - - private void I_PointerReleased(object? sender, PointerReleasedEventArgs e) - { - pointerPressed = false; - } - - - private void I_PointerMoved(object? sender, PointerEventArgs e) - { - ZXGridImageView i = sender as ZXGridImageView; - i.BorderBrush = new SolidColorBrush(Colors.Red); - i.BorderThickness = new Thickness(1); - - //if (pointerPressed) - //{ - // int id = i.Tag.ToInteger(); - // Console.WriteLine(id.ToString()); - // int y = id / _map.Width; - // int x = id - (y * _map.Width); - // _map.TileArrays[x, y].TileId = CurrentTile; - // Refresh(false); - //} - } - - - /// /// Event for mouse pressed /// @@ -406,67 +331,20 @@ private void I_PointerMoved(object? sender, PointerEventArgs e) /// private void CnvEditor_PointerPressed(object? sender, Avalonia.Input.PointerPressedEventArgs e) { - //var p = e.GetCurrentPoint(CnvEditor); - - //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) - //{ - // CnvEditor_InvertPixelsCell(p.Position.X, p.Position.Y); - //} - //else if (InvertColorsCell) - //{ - // CnvEditor_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 CnvEditor_PointerReleased(object? sender, Avalonia.Input.PointerReleasedEventArgs e) - { - MouseLeftPressed = false; - MouseRightPressed = false; - } - + 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); - /// - /// Event for pointer moved outside control - /// - /// - /// - private void CnvEditor_PointerExited(object? sender, Avalonia.Input.PointerEventArgs e) - { - MouseLeftPressed = false; - MouseRightPressed = false; + if (p.Properties.IsLeftButtonPressed) + { + SetTile(x, y, CurrentTile); + } + if (p.Properties.IsRightButtonPressed) + { + SetTile(x, y, 0); + } } @@ -477,121 +355,62 @@ private void CnvEditor_PointerExited(object? sender, Avalonia.Input.PointerEvent /// private void CnvEditor_PointerMoved(object? sender, Avalonia.Input.PointerEventArgs e) { - //var p = e.GetCurrentPoint(cnvEditor); - //if (MouseLeftPressed) - //{ - // SetPoint(p.Position.X, p.Position.Y, PrimaryColorIndex); - //} - //else if (MouseRightPressed) - //{ - // SetPoint(p.Position.X, p.Position.Y, SecondaryColorIndex); - //} + 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 SetPoint(double mx, double my, int value) + private void SetTile(int x, int y, 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(); - } - + if (_map == null) + { + return; + } - 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; + 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 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); From 63ecdda4289047bdea776a9a2bb5a043ef50f77a Mon Sep 17 00:00:00 2001 From: Meta-Agents Date: Tue, 4 Aug 2026 16:09:52 +0200 Subject: [PATCH 10/18] Working with map editor --- .../ZXMaps/MapPatternEditor.axaml.cs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/ZXBStudio/DocumentEditors/ZXMaps/MapPatternEditor.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/MapPatternEditor.axaml.cs index 3d5d132..a891bb9 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/MapPatternEditor.axaml.cs +++ b/ZXBStudio/DocumentEditors/ZXMaps/MapPatternEditor.axaml.cs @@ -428,17 +428,13 @@ private void Tmr_Tick(object? sender, EventArgs e) /// 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(); + for (int y = 0; y < _map.Height; y++) + { + for (int x = 0; x < _map.Width; x++) + { + SetTile(x, y, 0); + } + } Refresh(true); } From 02697f14afd99f8a8137541d9d89ff54a3fa3fa0 Mon Sep 17 00:00:00 2001 From: Meta-Agents Date: Wed, 5 Aug 2026 00:41:36 +0200 Subject: [PATCH 11/18] ServiceLayer_Maps --- .../DocumentEditors/ZXMaps/Log/ServiceLayer_Maps.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 ZXBStudio/DocumentEditors/ZXMaps/Log/ServiceLayer_Maps.cs diff --git a/ZXBStudio/DocumentEditors/ZXMaps/Log/ServiceLayer_Maps.cs b/ZXBStudio/DocumentEditors/ZXMaps/Log/ServiceLayer_Maps.cs new file mode 100644 index 0000000..b391ccf --- /dev/null +++ b/ZXBStudio/DocumentEditors/ZXMaps/Log/ServiceLayer_Maps.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ZXBasicStudio.DocumentEditors.ZXMaps.Log +{ + public static class ServiceLayer_Maps + { + } +} From e5971c59383181bb2e7c0480b7ecc459576ff80b Mon Sep 17 00:00:00 2001 From: Meta-Agents Date: Sat, 8 Aug 2026 17:11:01 +0200 Subject: [PATCH 12/18] SpriteEditor: - Import .scr Tile Editor: - Move tile - Grid in Map --- .../ZXGraphics/ImageViewImportControl.cs | 67 ++- .../ZXGraphics/SpriteImportDialog.axaml.cs | 5 +- .../ZXGraphics/ZXGridImageView.axaml.cs | 53 ++- .../ZXGraphics/log/ServiceLayer.cs | 118 +++++- .../ZXMaps/Log/ServiceLayer_Maps.cs | 58 +++ .../ZXMaps/MapPatternEditor.axaml.cs | 4 +- .../DocumentEditors/ZXMaps/TileEditor.axaml | 4 +- .../ZXMaps/TileEditor.axaml.cs | 382 ++++++++++++------ ZXBStudio/DocumentEditors/ZXMaps/TileImage.cs | 4 + .../ZXMaps/TilePatternEditor.axaml | 3 +- .../ZXMaps/TilePatternEditor.axaml.cs | 42 +- 11 files changed, 529 insertions(+), 211 deletions(-) diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/ImageViewImportControl.cs b/ZXBStudio/DocumentEditors/ZXGraphics/ImageViewImportControl.cs index 422b891..a120def 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,13 +139,62 @@ public ImageViewImportControl() public async void LoadImage(IStorageFile file) { + imageData = ZXGraphics.log.ServiceLayer.LoadImage(file); + this.InvalidateVisual(); + return; + try { await using (var stream = await file.OpenReadAsync()) { - imageData = SixLabors.ImageSharp.Image.Load(stream); + if (file.Name.ToLower().EndsWith(".scr")) + { + // Load SCR file + var scrData = new byte[6912]; + await stream.ReadAsync(scrData, 0, 6912); + 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); + } + this.InvalidateVisual(); } - this.InvalidateVisual(); } catch (Exception ex) { @@ -150,6 +202,7 @@ public async void LoadImage(IStorageFile file) } + public void Refresh() { this.InvalidateVisual(); @@ -200,7 +253,7 @@ public override void Render(DrawingContext context) } else { - context.FillRectangle(brushWhite, r0); + context.FillRectangle(brushWhite, r0); } pair = !pair; } @@ -268,13 +321,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 +335,7 @@ public override void Render(DrawingContext context) } } -#endregion + #endregion #region Mouse diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/SpriteImportDialog.axaml.cs b/ZXBStudio/DocumentEditors/ZXGraphics/SpriteImportDialog.axaml.cs index 30da91c..3f3d917 100644 --- a/ZXBStudio/DocumentEditors/ZXGraphics/SpriteImportDialog.axaml.cs +++ b/ZXBStudio/DocumentEditors/ZXGraphics/SpriteImportDialog.axaml.cs @@ -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 { diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/ZXGridImageView.axaml.cs b/ZXBStudio/DocumentEditors/ZXGraphics/ZXGridImageView.axaml.cs index 14c9d03..3a9effe 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; @@ -79,6 +82,7 @@ public IZXBitmap? BackgroundImage } public bool Show8x8Grid { get; set; } = true; + public bool ShowTileGrid { get; set; } = true; public ZXGridImageView() { @@ -123,34 +127,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,8 +184,10 @@ 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) diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/log/ServiceLayer.cs b/ZXBStudio/DocumentEditors/ZXGraphics/log/ServiceLayer.cs index 655fe91..9a54c79 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 { @@ -688,7 +690,6 @@ public static bool SpriteData_ChangeFrames(ref ZXMapsTile sprite, byte oldFrames #endregion - #region Tools public static int GetColor(byte r, byte g, byte b, PaletteColor[] palette, byte cutOff = 5) @@ -765,5 +766,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/ZXMaps/Log/ServiceLayer_Maps.cs b/ZXBStudio/DocumentEditors/ZXMaps/Log/ServiceLayer_Maps.cs index b391ccf..321f633 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/Log/ServiceLayer_Maps.cs +++ b/ZXBStudio/DocumentEditors/ZXMaps/Log/ServiceLayer_Maps.cs @@ -1,12 +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/MapPatternEditor.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/MapPatternEditor.axaml.cs index a891bb9..f028655 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/MapPatternEditor.axaml.cs +++ b/ZXBStudio/DocumentEditors/ZXMaps/MapPatternEditor.axaml.cs @@ -64,7 +64,6 @@ public int Zoom } } - /// /// Color index of the primary color /// @@ -167,6 +166,7 @@ public MapPatternEditor() btnPaper.Tapped += BtnPaper_Click; btnInk.Tapped += BtnInk_Tapped; + UpdateColorPanel(); } @@ -207,6 +207,8 @@ public void Refresh(bool withCallBack) var i = new ZXGridImageView(); i.Show8x8Grid = false; i.ViewAttributes = false; + i.ShowTileGrid = true; + //i.PointerEntered += I_PointerEntered; //i.PointerExited += I_PointerExited; //i.PointerPressed += I_PointerPressed; diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml b/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml index 4f36d14..69c2418 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml +++ b/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml @@ -28,10 +28,10 @@ - - diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml.cs index 59c1b50..1dc8f38 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml.cs +++ b/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml.cs @@ -25,6 +25,7 @@ using Avalonia.Media; using ZXBasicStudio.DocumentEditors.ZXMaps.Log; using ZXBasicStudio.Controls; +using System.Data; namespace ZXBasicStudio.DocumentEditors.ZXMaps { @@ -77,6 +78,8 @@ public override bool Modified private Guid documentTypeId = Guid.Empty; + private int selectedTileId = 0; + #endregion @@ -301,7 +304,9 @@ private void _Initialize(string fileName) 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; @@ -343,6 +348,10 @@ 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; @@ -360,146 +369,110 @@ private void TileList_Command(TilePatternControl sender, string command) { ctrlMapEditor.CurrentTile = sender.TileData.Id; } + selectedTileId = sender.TileData?.Id ?? 0; break; } } - private void TilePreview_Command(TilePreviewControl sender, string command) + 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 TileProperties_Command(TilePropertiesControl sender, string command) + private void BtnTileMoveLeft_Tapped(object? sender, TappedEventArgs e) { - try + if (selectedTileId < 1) { - 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; - } + return; } - catch { } + + 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 ResizePatterns() + private void Tiles_Select(int id) { - for (int n = 0; n < TilePatternsList.Count; n++) + var tpc = TilePatternsList.FirstOrDefault(d => + d != null && + d.TileData != null && + d.TileData.Id == id); + if (tpc != null) { - 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(); - } + tpc.Select(); } - 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) + private void Tiles_Renum() { - 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++) + for (int n = 0; n < TilePatternsList.Count; n++) { - pat.Attributes[n] = new AttributeColor() + if (TilePatternsList[n].TileData == null || TileMain.Tiles[n] == null) { - Paper = ctrlEditor.SecondaryColorIndex, - Ink = ctrlEditor.PrimaryColorIndex - }; - } + continue; + } - for (int y = 0; y < TileMain.Height; y++) - { - for (int x = 0; x < TileMain.Width; x++) + TileMain.Tiles[n].Id = n; + var spc = TilePatternsList[n]; + if (spc.TileData != null) { - int colorIndex = 0; - if (x < tileWidth && y < tileHeight) + spc.TileData = TileMain.Tiles[n]; + spc.TileData.Id = n; + spc.IsSelected = false; + if (n == selectedTileId) { - colorIndex = pattern.RawData[(y * tileWidth) + x]; + ctrlEditor.TileData = TileMain.Tiles[n]; + ctrlEditor.Refresh(); + ctrlPreview.TileData = TileMain.Tiles[n]; + ctrlPreview.Refresh(); } - - pat.RawData[(y * TileMain.Width) + x] = colorIndex; } } - return pat; + ctrlMapEditor.Refresh(true); } - private void TileProperties_FrameUpdate(TilePropertiesControl sender, string command) - { - if (sender.TileData == null) + private void BtnTileInfoHide_Tapped(object? sender, TappedEventArgs e) + { + btnTileInfoShow.IsVisible = true; + btnTileInfoHide.IsVisible = false; + foreach (var ctrl in TilePatternsList) { - return; + ctrl.InfoVisible = false; } + } - // TODO: Multiple frames per tile? - /* - int f = sender.TileData.Frames - 1; - if (f < 0) - { - f = 0; - } - else if (f > 255) + + private void BtnTileInfoShow_Tapped(object? sender, TappedEventArgs e) + { + btnTileInfoShow.IsVisible = false; + btnTileInfoHide.IsVisible = true; + foreach (var ctrl in TilePatternsList) { - f = 255; + ctrl.InfoVisible = true; } - txtFrame.Maximum = f; - txtFrame.Text = f.ToString(); - txtFrame.UpdateLayout(); - */ - TileProperties_Command(sender, "REFRESH"); } private void TileList_AddTile(ZXMapsTile TileData) - { + { TileMain.Tiles.Add(TileData); TilePatternControl selectedTile = null; @@ -535,7 +508,7 @@ private void TileList_AddTile(ZXMapsTile TileData) } // Add void Tile - var TilePattern = new TilePatternControl(); + var TilePattern = new TilePatternControl(); TilePatternsList.Add(TilePattern); wpTileList.Children.Add(TilePattern); TilePattern.Initialize(null, TileList_Command, TileMain.Width, TileMain.Height); @@ -592,31 +565,6 @@ private void TileList_Insert(ZXMapsTile TileData) } - 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(); - } - - private void TileList_Modified(ZXMapsTile TileData) { if (!_Modified) @@ -629,12 +577,30 @@ private void TileList_Modified(ZXMapsTile TileData) { 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; @@ -642,6 +608,8 @@ private void TileList_Modified(ZXMapsTile TileData) // Calcular altura del WrapPanel basada en el contenido CalculateWrapPanelHeight(); wpTileList.InvalidateMeasure(); + + ctrlMapEditor.Refresh(false); } private void CalculateWrapPanelHeight() @@ -690,32 +658,178 @@ private void CalculateWrapPanelHeight() wpTileList.Height = calculatedHeight; } + #endregion - private void BtnTileInfoHide_Tapped(object? sender, TappedEventArgs e) + + #region Tile preview + + private void TilePreview_Command(TilePreviewControl sender, string command) { - btnTileInfoShow.IsVisible = true; - btnTileInfoHide.IsVisible = false; - foreach (var ctrl in TilePatternsList) + + } + + + private void TileProperties_FrameUpdate(TilePropertiesControl sender, string command) + { + if (sender.TileData == null) { - ctrl.InfoVisible = false; + 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 - private void BtnTileInfoShow_Tapped(object? sender, TappedEventArgs e) + + #region Tile properties + + private void TileProperties_Command(TilePropertiesControl sender, string command) { - btnTileInfoShow.IsVisible = false; - btnTileInfoHide.IsVisible = true; - foreach (var ctrl in TilePatternsList) + try { - ctrl.InfoVisible = true; + 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) diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TileImage.cs b/ZXBStudio/DocumentEditors/ZXMaps/TileImage.cs index 7992019..b580dec 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/TileImage.cs +++ b/ZXBStudio/DocumentEditors/ZXMaps/TileImage.cs @@ -71,6 +71,10 @@ public unsafe void RenderTile(ZXMapsTile Tile, int FrameNumber) { try { + if (Tile == null) + { + return; + } if (bitmap == null) //disposed throw new ObjectDisposedException("ZXTileImage"); diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TilePatternEditor.axaml b/ZXBStudio/DocumentEditors/ZXMaps/TilePatternEditor.axaml index 4b5d7ec..529a0a4 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/TilePatternEditor.axaml +++ b/ZXBStudio/DocumentEditors/ZXMaps/TilePatternEditor.axaml @@ -86,6 +86,7 @@ + + 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 + + + + + Cutoff threshold: + + + + + + + + + + + + + + diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TileImportDialog.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/TileImportDialog.axaml.cs new file mode 100644 index 0000000..2ef16c3 --- /dev/null +++ b/ZXBStudio/DocumentEditors/ZXMaps/TileImportDialog.axaml.cs @@ -0,0 +1,609 @@ +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; + + + 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; + + for (int n = 0; n < tileFrames; n++) + { + var attrs = attrsDefault.Clonar(); + var pattern = new Pattern(); + pattern.Attributes = attrs; // new AttributeColor[(tileWidth / 8) * (tileHeight / 8)]; + pattern.Id = n; + pattern.Name = ""; + pattern.Number = n.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; + px = cx * 8; + paper = -1; + ink = -1; + for (int y = 0; y < 8; y++) + { + xs = (cnvSource.OffsetX + (n * 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; + } + } + pattern.RawData[dir] = idxAttr; + } + else + { + pattern.RawData[dir] = 0; + } + dir++; + xs++; + } + ys++; + } + } + } + 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, n); + + } + tile = s; + pnlPreview.Width = anchoPreview + (tileWidth * 4); + pnlPreview.Height = altoPreview + (tileHeight * 4); + } + catch (Exception ex) + { + + } + } + + + 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; + //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.Patterns = spr.Patterns.Skip(n).Take(1).ToList(); + spr.Frames = 1; + spr.Name = sprName + "_" + 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() + { + tileWidth = txtWidth.Text.ToInteger(); + tileHeight = txtHeight.Text.ToInteger(); + appendToTiles = chkAppend.IsChecked.ToBoolean(); + insertBlankAtStart = chkInsertBlank.IsChecked.ToBoolean(); + removeDuplicatedTiles = chkRemoveDuplicated.IsChecked.ToBoolean(); + } + + #endregion + } +} \ No newline at end of file diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TilePatternEditor.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/TilePatternEditor.axaml.cs index 2210058..922fddf 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/TilePatternEditor.axaml.cs +++ b/ZXBStudio/DocumentEditors/ZXMaps/TilePatternEditor.axaml.cs @@ -1649,7 +1649,7 @@ public void Export() private void BtnImport_Tapped(object? sender, TappedEventArgs e) { - Import(); + CallBackCommand(this, "IMPORT"); } @@ -1666,43 +1666,5 @@ private void BtnUndo_Tapped(object? sender, TappedEventArgs e) #endregion - - - #region Import - - private void Import() - { - /* - var dlg = new TileImportDialog(); - dlg.Initialize(FileName, 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": - TileData = Tile; - CallBackCommand?.Invoke(this, "ADD"); - break; - case "UPDATE": - //var spr = TilePatternsList.FirstOrDefault(d => d.Name == Tile.Name); - //if (spr != null) - //{ - // TileList_Modified(Tile); - //} - break; - } - } - catch { } - } - - - #endregion } } diff --git a/ZXBStudio/DocumentEditors/ZXMaps/ZXMapsEditor.axaml b/ZXBStudio/DocumentEditors/ZXMaps/ZXMapsEditor.axaml index f123395..1b68241 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/ZXMapsEditor.axaml +++ b/ZXBStudio/DocumentEditors/ZXMaps/ZXMapsEditor.axaml @@ -12,7 +12,7 @@ - + diff --git a/ZXBStudio/ZXBasicStudio.csproj b/ZXBStudio/ZXBasicStudio.csproj index ba0b5d9..c560b78 100644 --- a/ZXBStudio/ZXBasicStudio.csproj +++ b/ZXBStudio/ZXBasicStudio.csproj @@ -654,6 +654,9 @@ MapPatternEditor.axaml + + TileImportDialog.axaml + TilePropertiesControl.axaml From caef53da0ee82b1b1d7ef4604ff08c5ce7e48c46 Mon Sep 17 00:00:00 2001 From: Meta-Agents Date: Tue, 11 Aug 2026 01:01:46 +0200 Subject: [PATCH 14/18] Working wqith import tiles --- .../ZXMaps/TileEditor.axaml.cs | 3 +- .../ZXMaps/TileImportDialog.axaml.cs | 215 +++++++++++------- 2 files changed, 134 insertions(+), 84 deletions(-) diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml.cs index ac99a28..396dd33 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml.cs +++ b/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml.cs @@ -1001,7 +1001,8 @@ private void Import_Command(ZXMapsTile Tile, string command) switch (command) { case "ADD": - //TileData = Tile; + //TileMain.Tiles.Add(Tile); + //CallBackCommand?.Invoke(this, "ADD"); break; case "UPDATE": diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TileImportDialog.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/TileImportDialog.axaml.cs index 2ef16c3..091c7b5 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/TileImportDialog.axaml.cs +++ b/ZXBStudio/DocumentEditors/ZXMaps/TileImportDialog.axaml.cs @@ -293,106 +293,136 @@ private void _UpdatePreview() tileFrames = maxX * maxY; - for (int n = 0; n < tileFrames; n++) + int frameNumber = 0; + int framesX = imgData.Width / tileWidth; + int framesY = imgData.Height / tileHeight; + + for (int fy = 0; fy < framesY; fy++) { - var attrs = attrsDefault.Clonar(); - var pattern = new Pattern(); - pattern.Attributes = attrs; // new AttributeColor[(tileWidth / 8) * (tileHeight / 8)]; - pattern.Id = n; - pattern.Name = ""; - pattern.Number = n.ToString(); - pattern.RawData = new int[tileWidth * tileHeight]; - - int dir = 0; - for (int cy = 0; cy < sh; cy++) + cnvSource.OffsetY = fy * tileHeight; + for (int fx = 0; fx < framesX; fx++) { - py = cy * 8; - for (int cx = 0; cx < sw; cx++) + 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++) { - ys = cnvSource.OffsetY + py; - px = cx * 8; - paper = -1; - ink = -1; - for (int y = 0; y < 8; y++) + py = cy * 8; + for (int cx = 0; cx < sw; cx++) { - xs = (cnvSource.OffsetX + (n * tileWidth)) + px; - dir = (((cy * 8) + y) * tileWidth) + (cx * 8); - for (int x = 0; x < 8; x++) + //ys = cnvSource.OffsetY + py; + ys = (fy * tileHeight) + py; + + px = cx * 8; + paper = -1; + ink = -1; + for (int y = 0; y < 8; y++) { - if (xs >= 0 && xs < imgData.Width && - ys >= 0 && ys < imgData.Height) + //xs = (cnvSource.OffsetX /*+ (n * tileWidth)*/) + px; + xs = (fx * tileWidth) + px; + + dir = (((cy * 8) + y) * tileWidth) + (cx * 8); + for (int x = 0; x < 8; x++) { - var c = imgData[xs, ys]; - var idxAttr = GetColor(c.R, c.G, c.B, s.Palette); - if (tileMode == GraphicsModes.ZXSpectrum) + if (xs >= 0 && xs < imgData.Width && + ys >= 0 && ys < imgData.Height) { - // 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 + var c = imgData[xs, ys]; + var idxAttr = GetColor(c.R, c.G, c.B, s.Palette); + if (tileMode == GraphicsModes.ZXSpectrum) { - idxAttr = 7; + // 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; + } } + pattern.RawData[dir] = idxAttr; } - pattern.RawData[dir] = idxAttr; - } - else - { - pattern.RawData[dir] = 0; + else + { + pattern.RawData[dir] = 0; + } + dir++; + xs++; } - dir++; - xs++; + ys++; } - ys++; } } - } - 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); + bool addToPatterns = true; + if (removeDuplicatedTiles) + { + if (PatternExists(s.Patterns, pattern)) + { + addToPatterns = false; + } + } + if (addToPatterns) + { + s.Patterns.Add(pattern); - Canvas.SetLeft(img, prevX); - Canvas.SetTop(img, prevY); - prevX += (tileWidth * 4); + 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); - if (anchoPreview < prevX) - { - anchoPreview = prevX; - } - if ((prevX + sw) > w) - { - prevY += (tileHeight * 4); - prevX = 0; - if (altoPreview < prevY) - { - altoPreview = prevY; + 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++; } } + } - prev.RenderSprite(s, n); + cnvSource.OffsetX = 0; + cnvSource.OffsetY = 0; - } tile = s; pnlPreview.Width = anchoPreview + (tileWidth * 4); pnlPreview.Height = altoPreview + (tileHeight * 4); @@ -403,6 +433,26 @@ private void _UpdatePreview() } } + 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) { @@ -559,14 +609,13 @@ private async void Import() 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 = sprName + "_" + n.ToString(); + spr.Name = n.ToString(); var spr2 = tiles.FirstOrDefault(d => d != null && d.Name == spr.Name); if (spr2 == null) { @@ -584,7 +633,7 @@ private async void Import() CallBackCommand?.Invoke(tile, "UPDATE"); } } - */ + this.Close(); this.Dispose(); } From 9ffb769ecd97fe23b0f2cff6a703e16d4616838d Mon Sep 17 00:00:00 2001 From: Meta-Agents Date: Mon, 17 Aug 2026 00:33:31 +0200 Subject: [PATCH 15/18] Working with tile editor --- .../ZXMaps/TileEditor.axaml.cs | 27 +++++++++++-------- .../ZXMaps/TileImportDialog.axaml | 5 ++++ .../ZXMaps/TileImportDialog.axaml.cs | 26 +++++++++++++++++- 3 files changed, 46 insertions(+), 12 deletions(-) diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml.cs b/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml.cs index 396dd33..b3e9743 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml.cs +++ b/ZXBStudio/DocumentEditors/ZXMaps/TileEditor.axaml.cs @@ -309,7 +309,7 @@ private void _Initialize(string fileName) btnTileMoveRight.Tapped += BtnTileMoveRight_Tapped; btnTileInfoHide.Tapped += BtnTileInfoHide_Tapped; btnTileInfoShow.Tapped += BtnTileInfoShow_Tapped; - + btnTileDelete.Tapped += BtnTileDelete_Tapped; Refresh(); if (TilePatternsList.Count > 1) @@ -334,6 +334,15 @@ private void _Initialize(string fileName) 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); + Tiles_Renum(); + } + } public void Refresh() { @@ -1001,16 +1010,12 @@ private void Import_Command(ZXMapsTile Tile, string command) switch (command) { case "ADD": - //TileMain.Tiles.Add(Tile); - - //CallBackCommand?.Invoke(this, "ADD"); - break; - case "UPDATE": - //var spr = TilePatternsList.FirstOrDefault(d => d.Name == Tile.Name); - //if (spr != null) - //{ - // TileList_Modified(Tile); - //} + Tile.Id = TilePatternsList.Count-1; + TileList_AddTile(Tile); + ctrlEditor.TileData = Tile; + ctrlPreview.TileData = Tile; + ctrlPreview.Refresh(); + ctrlMapEditor.Refresh(false); break; } } diff --git a/ZXBStudio/DocumentEditors/ZXMaps/TileImportDialog.axaml b/ZXBStudio/DocumentEditors/ZXMaps/TileImportDialog.axaml index d75bd18..47ff475 100644 --- a/ZXBStudio/DocumentEditors/ZXMaps/TileImportDialog.axaml +++ b/ZXBStudio/DocumentEditors/ZXMaps/TileImportDialog.axaml @@ -66,6 +66,11 @@ Remove duplicated tiles + + + Invert palette (only in monochrome mode) + + + diff --git a/ZXBStudio/ZXBasicStudio.csproj b/ZXBStudio/ZXBasicStudio.csproj index 89f5eff..85f7231 100644 --- a/ZXBStudio/ZXBasicStudio.csproj +++ b/ZXBStudio/ZXBasicStudio.csproj @@ -13,7 +13,7 @@ ZX Basic Studio False - 1.8.0.5 + 1.8.0.6 @@ -654,6 +654,9 @@ MapPatternEditor.axaml + + MapExportDialog.axaml + TileExportDialog.axaml diff --git a/ZXBStudio/version.txt b/ZXBStudio/version.txt index 655bd5f..b68d1ed 100644 --- a/ZXBStudio/version.txt +++ b/ZXBStudio/version.txt @@ -1 +1 @@ -1.8.0.5 \ No newline at end of file +1.8.0.6 \ No newline at end of file