diff --git a/Minint/Controls/PixelCanvas.cs b/Minint/Controls/PixelCanvas.cs
index 9ca6b70..b9b2e0d 100644
--- a/Minint/Controls/PixelCanvas.cs
+++ b/Minint/Controls/PixelCanvas.cs
@@ -7,6 +7,8 @@ using Avalonia.Input;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Threading;
+using Minint.Core.Models;
+using Minint.ViewModels;
namespace Minint.Controls;
@@ -36,23 +38,30 @@ public class PixelCanvas : Control
#region Events for tool interaction
- /// Fires when the user presses the left mouse button at an image pixel.
public event Action? ToolDown;
-
- /// Fires when the user drags with left button held at an image pixel.
public event Action? ToolDrag;
-
- /// Fires when the cursor moves over the image (pixel coords, or null if outside).
public event Action<(int X, int Y)?>? CursorPixelChanged;
-
- /// Set by the host to provide preview mask pixels for overlay.
public Func?>? GetPreviewMask { get; set; }
+ // Selection events
+ public event Action? SelectionStart;
+ public event Action? SelectionUpdate;
+ public event Action? SelectionEnd;
+
+ // Paste events
+ public event Action? PasteMoved;
+ public event Action? PasteCommitted;
+ public event Action? PasteCancelled;
+
+ /// Provides the current EditorViewModel for reading selection/paste state during render.
+ public EditorViewModel? Editor { get; set; }
+
#endregion
private readonly Viewport _viewport = new();
private bool _isPanning;
private bool _isDrawing;
+ private bool _isSelecting;
private Point _panStart;
private double _panStartOffsetX, _panStartOffsetY;
private bool _viewportInitialized;
@@ -120,6 +129,8 @@ public class PixelCanvas : Control
DrawPixelGrid(context, imgW, imgH);
DrawToolPreview(context, imgW, imgH);
+ DrawSelectionOverlay(context);
+ DrawPastePreview(context);
int w = imgW, h = imgH;
Dispatcher.UIThread.Post(() => SyncScrollBars(w, h), DispatcherPriority.Render);
@@ -199,11 +210,9 @@ public class PixelCanvas : Control
foreach (var (px, py) in mask)
{
var (sx, sy) = _viewport.PixelToScreen(px, py);
- var r = new Rect(sx, sy, zoom, zoom);
- context.FillRectangle(previewBrush, r);
+ context.FillRectangle(previewBrush, new Rect(sx, sy, zoom, zoom));
}
- // Outline around the mask bounding box
if (mask.Count > 0)
{
int minX = mask[0].X, maxX = mask[0].X;
@@ -216,12 +225,63 @@ public class PixelCanvas : Control
if (py > maxY) maxY = py;
}
var (ox, oy) = _viewport.PixelToScreen(minX, minY);
- var outlineRect = new Rect(ox, oy, (maxX - minX + 1) * zoom, (maxY - minY + 1) * zoom);
- context.DrawRectangle(outlinePen, outlineRect);
+ context.DrawRectangle(outlinePen, new Rect(ox, oy, (maxX - minX + 1) * zoom, (maxY - minY + 1) * zoom));
}
}
}
+ private void DrawSelectionOverlay(DrawingContext context)
+ {
+ var sel = Editor?.SelectionRectNormalized;
+ if (sel is null) return;
+
+ var (sx, sy, sw, sh) = sel.Value;
+ double zoom = _viewport.Zoom;
+ var (screenX, screenY) = _viewport.PixelToScreen(sx, sy);
+ var rect = new Rect(screenX, screenY, sw * zoom, sh * zoom);
+
+ var fillBrush = new SolidColorBrush(Color.FromArgb(40, 100, 150, 255));
+ context.FillRectangle(fillBrush, rect);
+
+ var borderPen = new Pen(new SolidColorBrush(Color.FromArgb(200, 100, 150, 255)), 1,
+ new DashStyle([4, 4], 0));
+ context.DrawRectangle(borderPen, rect);
+ }
+
+ private void DrawPastePreview(DrawingContext context)
+ {
+ if (Editor is null || !Editor.IsPasting || Editor.Clipboard is null)
+ return;
+
+ var pos = Editor.PastePosition!.Value;
+ var frag = Editor.Clipboard;
+ double zoom = _viewport.Zoom;
+
+ var clip = new Rect(0, 0, Bounds.Width, Bounds.Height);
+ using (context.PushClip(clip))
+ {
+ for (int fy = 0; fy < frag.Height; fy++)
+ {
+ for (int fx = 0; fx < frag.Width; fx++)
+ {
+ var color = frag.Pixels[fy * frag.Width + fx];
+ if (color.A == 0) continue;
+
+ var (sx, sy) = _viewport.PixelToScreen(pos.X + fx, pos.Y + fy);
+ byte dispA = (byte)(color.A * 180 / 255); // semi-transparent preview
+ var brush = new SolidColorBrush(Color.FromArgb(dispA, color.R, color.G, color.B));
+ context.FillRectangle(brush, new Rect(sx, sy, zoom, zoom));
+ }
+ }
+
+ // Border around the floating fragment
+ var (ox, oy) = _viewport.PixelToScreen(pos.X, pos.Y);
+ var borderPen = new Pen(new SolidColorBrush(Color.FromArgb(200, 255, 200, 50)), 1,
+ new DashStyle([3, 3], 0));
+ context.DrawRectangle(borderPen, new Rect(ox, oy, frag.Width * zoom, frag.Height * zoom));
+ }
+ }
+
#endregion
#region Scrollbar Sync
@@ -284,9 +344,6 @@ public class PixelCanvas : Control
return (px, py);
}
- ///
- /// Recalculates the pixel coordinate under the cursor after a viewport change.
- ///
private void RecalcCursorPixel()
{
if (_lastScreenPos is null) return;
@@ -341,17 +398,35 @@ public class PixelCanvas : Control
_panStartOffsetX = _viewport.OffsetX;
_panStartOffsetY = _viewport.OffsetY;
e.Handled = true;
+ return;
}
- else if (props.IsLeftButtonPressed && !_isPanning)
+
+ if (!props.IsLeftButtonPressed || _isPanning) return;
+
+ var pixel = ScreenToPixelClamped(e.GetPosition(this));
+ if (pixel is null) return;
+
+ // Paste mode: left-click commits
+ if (Editor is not null && Editor.IsPasting)
{
- var pixel = ScreenToPixelClamped(e.GetPosition(this));
- if (pixel is not null)
- {
- _isDrawing = true;
- ToolDown?.Invoke(pixel.Value.X, pixel.Value.Y);
- e.Handled = true;
- }
+ PasteCommitted?.Invoke();
+ e.Handled = true;
+ return;
}
+
+ // Select tool: begin rubber-band
+ if (Editor is not null && Editor.ActiveTool == ToolType.Select)
+ {
+ _isSelecting = true;
+ SelectionStart?.Invoke(pixel.Value.X, pixel.Value.Y);
+ e.Handled = true;
+ return;
+ }
+
+ // Regular drawing tools
+ _isDrawing = true;
+ ToolDown?.Invoke(pixel.Value.X, pixel.Value.Y);
+ e.Handled = true;
}
protected override void OnPointerMoved(PointerEventArgs e)
@@ -378,7 +453,24 @@ public class PixelCanvas : Control
{
_lastCursorPixel = pixel;
CursorPixelChanged?.Invoke(pixel);
+ }
+
+ // Paste mode: floating fragment follows cursor
+ if (Editor is not null && Editor.IsPasting && pixel is not null)
+ {
+ PasteMoved?.Invoke(pixel.Value.X, pixel.Value.Y);
InvalidateVisual();
+ e.Handled = true;
+ return;
+ }
+
+ // Selection rubber-band drag
+ if (_isSelecting && pixel is not null)
+ {
+ SelectionUpdate?.Invoke(pixel.Value.X, pixel.Value.Y);
+ InvalidateVisual();
+ e.Handled = true;
+ return;
}
if (_isDrawing && pixel is not null)
@@ -386,6 +478,10 @@ public class PixelCanvas : Control
ToolDrag?.Invoke(pixel.Value.X, pixel.Value.Y);
e.Handled = true;
}
+ else
+ {
+ InvalidateVisual();
+ }
}
protected override void OnPointerReleased(PointerReleasedEventArgs e)
@@ -397,6 +493,15 @@ public class PixelCanvas : Control
_isPanning = false;
e.Handled = true;
}
+ else if (_isSelecting && e.InitialPressMouseButton == MouseButton.Left)
+ {
+ _isSelecting = false;
+ var pixel = ScreenToPixelClamped(e.GetPosition(this));
+ if (pixel is not null)
+ SelectionEnd?.Invoke(pixel.Value.X, pixel.Value.Y);
+ InvalidateVisual();
+ e.Handled = true;
+ }
else if (_isDrawing && e.InitialPressMouseButton == MouseButton.Left)
{
_isDrawing = false;
@@ -416,6 +521,33 @@ public class PixelCanvas : Control
}
}
+ protected override void OnKeyDown(KeyEventArgs e)
+ {
+ base.OnKeyDown(e);
+
+ if (e.Key == Key.Escape)
+ {
+ if (Editor is not null && Editor.IsPasting)
+ {
+ PasteCancelled?.Invoke();
+ InvalidateVisual();
+ e.Handled = true;
+ }
+ else if (Editor is not null && Editor.HasSelection)
+ {
+ Editor.ClearSelection();
+ InvalidateVisual();
+ e.Handled = true;
+ }
+ }
+ else if (e.Key == Key.Enter && Editor is not null && Editor.IsPasting)
+ {
+ PasteCommitted?.Invoke();
+ InvalidateVisual();
+ e.Handled = true;
+ }
+ }
+
#endregion
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
diff --git a/Minint/ViewModels/EditorViewModel.cs b/Minint/ViewModels/EditorViewModel.cs
index 42c91be..af41da5 100644
--- a/Minint/ViewModels/EditorViewModel.cs
+++ b/Minint/ViewModels/EditorViewModel.cs
@@ -13,6 +13,11 @@ using Minint.Core.Services.Impl;
namespace Minint.ViewModels;
+///
+/// Palette-independent clipboard fragment: stores resolved RGBA pixels.
+///
+public sealed record ClipboardFragment(int Width, int Height, RgbaColor[] Pixels);
+
public partial class EditorViewModel : ViewModelBase
{
private readonly ICompositor _compositor = new Compositor();
@@ -66,6 +71,27 @@ public partial class EditorViewModel : ViewModelBase
public RgbaColor SelectedColor => new(_previewColor.R, _previewColor.G, _previewColor.B, _previewColor.A);
+ // Selection state (Select tool rubber-band)
+ [ObservableProperty]
+ [NotifyPropertyChangedFor(nameof(HasSelection))]
+ private (int X, int Y, int W, int H)? _selectionRect;
+
+ public bool HasSelection => SelectionRect is not null;
+
+ // Clipboard
+ [ObservableProperty]
+ [NotifyPropertyChangedFor(nameof(HasClipboard))]
+ private ClipboardFragment? _clipboard;
+
+ public bool HasClipboard => Clipboard is not null;
+
+ // Paste mode
+ [ObservableProperty]
+ [NotifyPropertyChangedFor(nameof(IsPasting))]
+ private (int X, int Y)? _pastePosition;
+
+ public bool IsPasting => PastePosition is not null;
+
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(Title))]
private string? _filePath;
@@ -113,10 +139,6 @@ public partial class EditorViewModel : ViewModelBase
SyncLayersAndCanvas(doc);
}
- ///
- /// Re-syncs the Documents observable collection after an external modification
- /// to Container.Documents (e.g. pattern generation adding a document).
- ///
public void SyncAfterExternalChange() => SyncDocumentsList();
private void SyncDocumentsList()
@@ -191,7 +213,7 @@ public partial class EditorViewModel : ViewModelBase
private void RemoveDocument()
{
if (Container is null || ActiveDocument is null) return;
- if (Container.Documents.Count <= 1) return; // keep at least one
+ if (Container.Documents.Count <= 1) return;
var doc = ActiveDocument;
int idx = Container.Documents.IndexOf(doc);
@@ -203,10 +225,7 @@ public partial class EditorViewModel : ViewModelBase
}
[RelayCommand]
- private void RenameDocument()
- {
- // Triggered via UI text edit — the Name property is directly editable via TextBox
- }
+ private void RenameDocument() { }
[RelayCommand]
private void MoveDocumentUp()
@@ -254,7 +273,7 @@ public partial class EditorViewModel : ViewModelBase
private void RemoveLayer()
{
if (ActiveDocument is null || ActiveLayer is null) return;
- if (ActiveDocument.Layers.Count <= 1) return; // keep at least one
+ if (ActiveDocument.Layers.Count <= 1) return;
UnsubscribeLayerVisibility();
var layer = ActiveLayer;
@@ -329,6 +348,8 @@ public partial class EditorViewModel : ViewModelBase
if (px < 0 || px >= w || py < 0 || py >= h)
return;
+ if (ActiveTool == ToolType.Select) return; // handled separately
+
switch (ActiveTool)
{
case ToolType.Brush:
@@ -353,7 +374,7 @@ public partial class EditorViewModel : ViewModelBase
public void OnToolDrag(int px, int py)
{
- if (ActiveTool == ToolType.Fill) return;
+ if (ActiveTool is ToolType.Fill or ToolType.Select) return;
OnToolDown(px, py);
}
@@ -361,7 +382,7 @@ public partial class EditorViewModel : ViewModelBase
{
if (PreviewCenter is null || Container is null)
return null;
- if (ActiveTool == ToolType.Fill)
+ if (ActiveTool is ToolType.Fill or ToolType.Select)
return null;
var (cx, cy) = PreviewCenter.Value;
@@ -369,36 +390,161 @@ public partial class EditorViewModel : ViewModelBase
}
[RelayCommand]
- private void SelectBrush() => ActiveTool = ToolType.Brush;
+ private void SelectBrush() { CancelPasteMode(); ActiveTool = ToolType.Brush; }
[RelayCommand]
- private void SelectEraser() => ActiveTool = ToolType.Eraser;
+ private void SelectEraser() { CancelPasteMode(); ActiveTool = ToolType.Eraser; }
[RelayCommand]
- private void SelectFill() => ActiveTool = ToolType.Fill;
+ private void SelectFill() { CancelPasteMode(); ActiveTool = ToolType.Fill; }
+
+ [RelayCommand]
+ private void SelectSelectTool() { CancelPasteMode(); ActiveTool = ToolType.Select; }
#endregion
- #region Fragment copy (A4)
+ #region Selection + Copy/Paste (A4)
- ///
- /// Copies a rectangular fragment from active layer
- /// to active layer.
- ///
- public void CopyFragment(
- MinintDocument srcDoc, int srcLayerIndex,
- int srcX, int srcY, int regionW, int regionH,
- MinintDocument dstDoc, int dstLayerIndex,
- int dstX, int dstY)
+ /// Called by PixelCanvas when selection drag starts.
+ public void BeginSelection(int px, int py)
{
- if (Container is null) return;
- _fragmentService.CopyFragment(
- srcDoc, srcLayerIndex, srcX, srcY, regionW, regionH,
- dstDoc, dstLayerIndex, dstX, dstY,
- Container.Width, Container.Height);
+ SelectionRect = (px, py, 0, 0);
+ }
+
+ /// Called by PixelCanvas as the user drags.
+ public void UpdateSelection(int px, int py)
+ {
+ if (SelectionRect is null) return;
+ var s = SelectionRect.Value;
+ int x = Math.Min(s.X, px);
+ int y = Math.Min(s.Y, py);
+ int w = Math.Abs(px - s.X) + 1;
+ int h = Math.Abs(py - s.Y) + 1;
+ // Store normalized rect but keep original anchor in _selAnchor
+ _selectionRectNormalized = (x, y, w, h);
+ }
+
+ /// Called by PixelCanvas when mouse is released.
+ public void FinishSelection(int px, int py)
+ {
+ if (SelectionRect is null) return;
+ var s = SelectionRect.Value;
+ int x0 = Math.Min(s.X, px);
+ int y0 = Math.Min(s.Y, py);
+ int rw = Math.Abs(px - s.X) + 1;
+ int rh = Math.Abs(py - s.Y) + 1;
+ if (Container is not null)
+ {
+ x0 = Math.Max(0, x0);
+ y0 = Math.Max(0, y0);
+ rw = Math.Min(rw, Container.Width - x0);
+ rh = Math.Min(rh, Container.Height - y0);
+ }
+ if (rw <= 0 || rh <= 0)
+ {
+ SelectionRect = null;
+ _selectionRectNormalized = null;
+ return;
+ }
+ SelectionRect = (x0, y0, rw, rh);
+ _selectionRectNormalized = SelectionRect;
+ }
+
+ private (int X, int Y, int W, int H)? _selectionRectNormalized;
+
+ /// The normalized (positive W/H, clamped) selection rectangle for rendering.
+ public (int X, int Y, int W, int H)? SelectionRectNormalized => _selectionRectNormalized;
+
+ [RelayCommand]
+ private void CopySelection()
+ {
+ if (SelectionRect is null || ActiveDocument is null || ActiveLayer is null || Container is null)
+ return;
+
+ var (sx, sy, sw, sh) = SelectionRect.Value;
+ int cw = Container.Width;
+ var palette = ActiveDocument.Palette;
+ var srcPixels = ActiveLayer.Pixels;
+ var buf = new RgbaColor[sw * sh];
+
+ for (int dy = 0; dy < sh; dy++)
+ {
+ int srcRow = sy + dy;
+ for (int dx = 0; dx < sw; dx++)
+ {
+ int srcCol = sx + dx;
+ int idx = srcPixels[srcRow * cw + srcCol];
+ buf[dy * sw + dx] = idx < palette.Count ? palette[idx] : RgbaColor.Transparent;
+ }
+ }
+
+ Clipboard = new ClipboardFragment(sw, sh, buf);
+ }
+
+ [RelayCommand]
+ private void PasteClipboard()
+ {
+ if (Clipboard is null) return;
+ PastePosition = (0, 0);
+ }
+
+ public void MovePaste(int px, int py)
+ {
+ if (!IsPasting) return;
+ PastePosition = (px, py);
+ }
+
+ [RelayCommand]
+ public void CommitPaste()
+ {
+ if (!IsPasting || Clipboard is null || ActiveDocument is null || ActiveLayer is null || Container is null)
+ return;
+
+ var (px, py) = PastePosition!.Value;
+ int cw = Container.Width, ch = Container.Height;
+ var frag = Clipboard;
+ var dstPixels = ActiveLayer.Pixels;
+
+ for (int fy = 0; fy < frag.Height; fy++)
+ {
+ int dy = py + fy;
+ if (dy < 0 || dy >= ch) continue;
+ for (int fx = 0; fx < frag.Width; fx++)
+ {
+ int dx = px + fx;
+ if (dx < 0 || dx >= cw) continue;
+ var color = frag.Pixels[fy * frag.Width + fx];
+ if (color.A == 0) continue; // skip transparent
+ int colorIdx = ActiveDocument.EnsureColorCached(color);
+ dstPixels[dy * cw + dx] = colorIdx;
+ }
+ }
+
+ PastePosition = null;
+ SelectionRect = null;
+ _selectionRectNormalized = null;
RefreshCanvas();
}
+ [RelayCommand]
+ public void CancelPaste()
+ {
+ PastePosition = null;
+ }
+
+ public void ClearSelection()
+ {
+ SelectionRect = null;
+ _selectionRectNormalized = null;
+ }
+
+ private void CancelPasteMode()
+ {
+ PastePosition = null;
+ SelectionRect = null;
+ _selectionRectNormalized = null;
+ }
+
#endregion
#region Canvas rendering
diff --git a/Minint/ViewModels/MainWindowViewModel.cs b/Minint/ViewModels/MainWindowViewModel.cs
index 15fed79..2ce1dde 100644
--- a/Minint/ViewModels/MainWindowViewModel.cs
+++ b/Minint/ViewModels/MainWindowViewModel.cs
@@ -1,6 +1,5 @@
using System;
using System.IO;
-using System.Linq;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Platform.Storage;
@@ -153,49 +152,6 @@ public partial class MainWindowViewModel : ViewModelBase
#endregion
- #region Copy fragment (A4)
-
- [RelayCommand]
- private async Task CopyFragmentAsync()
- {
- if (Editor.Container is null || Owner is not Window window) return;
-
- var docs = Editor.Container.Documents;
- if (docs.Count == 0) return;
-
- var dialog = new CopyFragmentDialog(docs, Editor.Container.Width, Editor.Container.Height);
- var result = await dialog.ShowDialog(window);
- if (result != true) return;
-
- if (dialog.SourceDocument is null || dialog.DestDocument is null)
- {
- StatusText = "Copy failed: no document selected.";
- return;
- }
- if (dialog.SourceLayerIndex < 0 || dialog.DestLayerIndex < 0)
- {
- StatusText = "Copy failed: no layer selected.";
- return;
- }
-
- try
- {
- Editor.CopyFragment(
- dialog.SourceDocument, dialog.SourceLayerIndex,
- dialog.SourceX, dialog.SourceY,
- dialog.FragmentWidth, dialog.FragmentHeight,
- dialog.DestDocument, dialog.DestLayerIndex,
- dialog.DestX, dialog.DestY);
- StatusText = $"Copied {dialog.FragmentWidth}×{dialog.FragmentHeight} fragment.";
- }
- catch (Exception ex)
- {
- StatusText = $"Copy failed: {ex.Message}";
- }
- }
-
- #endregion
-
#region Pattern generation (Б4)
[RelayCommand]
diff --git a/Minint/ViewModels/ToolType.cs b/Minint/ViewModels/ToolType.cs
index a04b309..1c513e9 100644
--- a/Minint/ViewModels/ToolType.cs
+++ b/Minint/ViewModels/ToolType.cs
@@ -4,5 +4,6 @@ public enum ToolType
{
Brush,
Eraser,
- Fill
+ Fill,
+ Select
}
diff --git a/Minint/ViewModels/ToolTypeConverters.cs b/Minint/ViewModels/ToolTypeConverters.cs
index ec1d701..1b50d64 100644
--- a/Minint/ViewModels/ToolTypeConverters.cs
+++ b/Minint/ViewModels/ToolTypeConverters.cs
@@ -13,6 +13,7 @@ public static class ToolTypeConverters
public static readonly IValueConverter IsBrush = new ToolTypeConverter(ToolType.Brush);
public static readonly IValueConverter IsEraser = new ToolTypeConverter(ToolType.Eraser);
public static readonly IValueConverter IsFill = new ToolTypeConverter(ToolType.Fill);
+ public static readonly IValueConverter IsSelect = new ToolTypeConverter(ToolType.Select);
private sealed class ToolTypeConverter(ToolType target) : IValueConverter
{
diff --git a/Minint/Views/CopyFragmentDialog.axaml b/Minint/Views/CopyFragmentDialog.axaml
deleted file mode 100644
index 6168d92..0000000
--- a/Minint/Views/CopyFragmentDialog.axaml
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Minint/Views/CopyFragmentDialog.axaml.cs b/Minint/Views/CopyFragmentDialog.axaml.cs
deleted file mode 100644
index f9a1e6d..0000000
--- a/Minint/Views/CopyFragmentDialog.axaml.cs
+++ /dev/null
@@ -1,91 +0,0 @@
-using System;
-using System.Collections.Generic;
-using Avalonia.Controls;
-using Avalonia.Interactivity;
-using Minint.Core.Models;
-
-namespace Minint.Views;
-
-public partial class CopyFragmentDialog : Window
-{
- private readonly List _documents;
-
- public MinintDocument? SourceDocument => SrcDocCombo.SelectedItem as MinintDocument;
- public int SourceLayerIndex => SrcLayerCombo.SelectedIndex;
- public int SourceX => (int)(SrcX.Value ?? 0);
- public int SourceY => (int)(SrcY.Value ?? 0);
- public int FragmentWidth => (int)(RegionW.Value ?? 1);
- public int FragmentHeight => (int)(RegionH.Value ?? 1);
- public MinintDocument? DestDocument => DstDocCombo.SelectedItem as MinintDocument;
- public int DestLayerIndex => DstLayerCombo.SelectedIndex;
- public int DestX => (int)(DstX.Value ?? 0);
- public int DestY => (int)(DstY.Value ?? 0);
-
- public CopyFragmentDialog()
- {
- _documents = [];
- InitializeComponent();
- }
-
- public CopyFragmentDialog(List documents, int maxW, int maxH) : this()
- {
- _documents = documents;
-
- SrcDocCombo.ItemsSource = documents;
- SrcDocCombo.DisplayMemberBinding = new Avalonia.Data.Binding("Name");
- DstDocCombo.ItemsSource = documents;
- DstDocCombo.DisplayMemberBinding = new Avalonia.Data.Binding("Name");
-
- SrcX.Maximum = maxW - 1;
- SrcY.Maximum = maxH - 1;
- DstX.Maximum = maxW - 1;
- DstY.Maximum = maxH - 1;
- RegionW.Maximum = maxW;
- RegionH.Maximum = maxH;
-
- if (documents.Count > 0)
- {
- SrcDocCombo.SelectedIndex = 0;
- DstDocCombo.SelectedIndex = documents.Count > 1 ? 1 : 0;
- }
-
- SrcDocCombo.SelectionChanged += (_, _) => UpdateSrcLayers();
- DstDocCombo.SelectionChanged += (_, _) => UpdateDstLayers();
-
- UpdateSrcLayers();
- UpdateDstLayers();
-
- OkButton.Click += OnOkClick;
- CancelButton.Click += OnCancelClick;
- }
-
- private void UpdateSrcLayers()
- {
- if (SrcDocCombo.SelectedItem is MinintDocument doc)
- {
- SrcLayerCombo.ItemsSource = doc.Layers;
- SrcLayerCombo.DisplayMemberBinding = new Avalonia.Data.Binding("Name");
- if (doc.Layers.Count > 0) SrcLayerCombo.SelectedIndex = 0;
- }
- }
-
- private void UpdateDstLayers()
- {
- if (DstDocCombo.SelectedItem is MinintDocument doc)
- {
- DstLayerCombo.ItemsSource = doc.Layers;
- DstLayerCombo.DisplayMemberBinding = new Avalonia.Data.Binding("Name");
- if (doc.Layers.Count > 0) DstLayerCombo.SelectedIndex = 0;
- }
- }
-
- private void OnOkClick(object? sender, RoutedEventArgs e)
- {
- Close(true);
- }
-
- private void OnCancelClick(object? sender, RoutedEventArgs e)
- {
- Close(false);
- }
-}
diff --git a/Minint/Views/MainWindow.axaml b/Minint/Views/MainWindow.axaml
index 7db5281..383bd02 100644
--- a/Minint/Views/MainWindow.axaml
+++ b/Minint/Views/MainWindow.axaml
@@ -27,8 +27,12 @@
+
diff --git a/Minint/Views/MainWindow.axaml.cs b/Minint/Views/MainWindow.axaml.cs
index 915ea45..bd9ed73 100644
--- a/Minint/Views/MainWindow.axaml.cs
+++ b/Minint/Views/MainWindow.axaml.cs
@@ -37,9 +37,19 @@ public partial class MainWindow : Window
private static void WireCanvasEvents(PixelCanvas canvas, EditorViewModel editor)
{
+ canvas.Editor = editor;
+
canvas.ToolDown += (px, py) => editor.OnToolDown(px, py);
canvas.ToolDrag += (px, py) => editor.OnToolDrag(px, py);
canvas.CursorPixelChanged += pixel => editor.PreviewCenter = pixel;
canvas.GetPreviewMask = () => editor.GetPreviewMask();
+
+ canvas.SelectionStart += (px, py) => editor.BeginSelection(px, py);
+ canvas.SelectionUpdate += (px, py) => editor.UpdateSelection(px, py);
+ canvas.SelectionEnd += (px, py) => { editor.FinishSelection(px, py); canvas.InvalidateVisual(); };
+
+ canvas.PasteMoved += (px, py) => editor.MovePaste(px, py);
+ canvas.PasteCommitted += () => { editor.CommitPaste(); canvas.InvalidateVisual(); };
+ canvas.PasteCancelled += () => { editor.CancelPaste(); canvas.InvalidateVisual(); };
}
}