浏览代码

avalonia: Added InkCanvas and fixed colours for tab items and tab strip

Kenric Nugteren 2 周之前
父节点
当前提交
3423e07229

+ 13 - 0
InABox.Avalonia/Components/InkCanvas/InkCanvas.axaml

@@ -0,0 +1,13 @@
+<UserControl xmlns="https://github.com/avaloniaui"
+             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"
+             mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+             x:Class="InABox.Avalonia.Components.InkCanvas">
+	<Canvas Name="Canvas"
+			Background="Transparent"
+			PointerPressed="Canvas_PointerPressed"
+			PointerMoved="Canvas_PointerMoved"
+			PointerReleased="Canvas_PointerReleased"
+			ClipToBounds="True"/>
+</UserControl>

+ 119 - 0
InABox.Avalonia/Components/InkCanvas/InkCanvas.axaml.cs

@@ -0,0 +1,119 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.Shapes;
+using Avalonia.Input;
+using Avalonia.Markup.Xaml;
+using Avalonia.Media;
+using Avalonia.Media.Imaging;
+using System.Collections.ObjectModel;
+
+namespace InABox.Avalonia.Components;
+
+public partial class InkCanvas : UserControl
+{
+    public static readonly StyledProperty<double> LineThicknessProperty =
+        AvaloniaProperty.Register<InkCanvas, double>(nameof(LineThickness), 3.0);
+
+    public static readonly StyledProperty<Color> LineColourProperty =
+        AvaloniaProperty.Register<InkCanvas, Color>(nameof(LineColour), Colors.Black);
+
+    public double LineThickness
+    {
+        get => GetValue(LineThicknessProperty);
+        set => SetValue(LineThicknessProperty, value);
+    }
+
+    public Color LineColour
+    {
+        get => GetValue(LineColourProperty);
+        set => SetValue(LineColourProperty, value);
+    }
+
+    private ObservableCollection<Polyline> Lines = new();
+
+    private Polyline? CurrentLine;
+
+    public event EventHandler? Changed;
+
+    public InkCanvas()
+    {
+        InitializeComponent();
+
+        Lines.CollectionChanged += Lines_CollectionChanged;
+    }
+
+    public void Clear()
+    {
+        Canvas.Children.Clear();
+        Lines.Clear();
+        Changed?.Invoke(this, EventArgs.Empty);
+    }
+
+    public byte[] SaveImage()
+    {
+        var width = (int)Math.Floor(Canvas.Bounds.Width);
+        var height = (int)Math.Floor(Canvas.Bounds.Height);
+
+        var renderBitmap = new RenderTargetBitmap(new PixelSize(width, height));
+        using var context = renderBitmap.CreateDrawingContext();
+        foreach(var line in Lines)
+        {
+            line.Render(context);
+        }
+
+        using var stream = new MemoryStream();
+        renderBitmap.Save(stream);
+        return stream.ToArray();
+    }
+
+    private void Lines_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
+    {
+        Canvas.Children.Clear();
+        foreach(var line in Lines)
+        {
+            if (!Canvas.Children.Contains(line))
+            {
+                Canvas.Children.Add(line);
+            }
+        }
+    }
+
+    Point ConvertToImageCoordinates(Point canvasCoordinates)
+    {
+        return canvasCoordinates;
+    }
+
+    private void Canvas_PointerPressed(object? sender, PointerPressedEventArgs e)
+    {
+        var position = ConvertToImageCoordinates(e.GetPosition(Canvas));
+        CurrentLine = new Polyline
+        {
+            Points = new ObservableCollection<Point>(new Point[] { position }),
+            StrokeThickness = LineThickness,
+            Stroke = new SolidColorBrush(LineColour),
+            StrokeJoin = PenLineJoin.Round,
+            StrokeLineCap = PenLineCap.Round
+        };
+        Lines.Add(CurrentLine);
+    }
+
+    private void Canvas_PointerMoved(object? sender, PointerEventArgs e)
+    {
+        if(CurrentLine is not null)
+        {
+            var position = ConvertToImageCoordinates(e.GetPosition(Canvas));
+            CurrentLine.Points.Add(position);
+        }
+    }
+
+    private void Canvas_PointerReleased(object? sender, PointerReleasedEventArgs e)
+    {
+        if(CurrentLine is not null)
+        {
+            var position = ConvertToImageCoordinates(e.GetPosition(Canvas));
+            CurrentLine.Points.Add(position);
+            CurrentLine = null;
+            Changed?.Invoke(this, new());
+        }
+    }
+}

+ 7 - 3
InABox.Avalonia/Converters/ByteArrayToImageSourceConverter.cs

@@ -13,8 +13,8 @@ public class ByteArrayToImageSourceConverter : AbstractConverter<byte[]?, IImage
     public static ByteArrayToImageSourceConverter Instance { get; } = new();
     
     public bool Transparent { get; set; } = false;
-    
-    protected override IImage? Convert(byte[]? value, object? parameter = null)
+
+    public IImage? Convert(byte[]? value)
     {
         Bitmap? result = null;
         if (value?.Any() == true)
@@ -35,7 +35,11 @@ public class ByteArrayToImageSourceConverter : AbstractConverter<byte[]?, IImage
                 result = new Bitmap(stream);
             }
         }
-
         return result;
     }
+    
+    protected override IImage? Convert(byte[]? value, object? parameter = null)
+    {
+        return Convert(value);
+    }
 }

+ 4 - 4
InABox.Avalonia/DataModels/ShellColumns.cs

@@ -9,19 +9,19 @@ namespace InABox.Avalonia
     public class ShellColumns<TParent,TEntity> : IShellColumns<TEntity> where TEntity : Entity
     {
         
-        private static Dictionary<string, Tuple<int,Expression<Func<TEntity, object>>>> _columns = new Dictionary<string, Tuple<int,Expression<Func<TEntity, object>>>> ();
+        private static Dictionary<string, Tuple<int,Expression<Func<TEntity, object?>>>> _columns = new Dictionary<string, Tuple<int,Expression<Func<TEntity, object?>>>> ();
 
         public int IndexOf(string name) => _columns[name].Item1;
         
-        public Expression<Func<TEntity, object>> this[string name] => _columns[name].Item2;
+        public Expression<Func<TEntity, object?>> this[string name] => _columns[name].Item2;
 
-        public ShellColumns<TParent,TEntity> Map(string property, Expression<Func<TEntity, object>> expression)
+        public ShellColumns<TParent,TEntity> Map(string property, Expression<Func<TEntity, object?>> expression)
         {
             int iCol = _columns.TryGetValue(property, out var column)
                 ? column.Item1
                 : _columns.Keys.Count;
             
-            _columns[property] = new Tuple<int, Expression<Func<TEntity, object>>>(iCol, expression);
+            _columns[property] = new Tuple<int, Expression<Func<TEntity, object?>>>(iCol, expression);
             //_columns[property] = new Tuple<int, Expression<Func<TEntity, object>>>(_columns.Keys.Count, expression);
             return this;
         }

+ 1 - 0
InABox.Avalonia/Theme/Classes/TabItem.axaml

@@ -68,6 +68,7 @@
 	
 	<Style Selector="TabControl.Standard TabItem:selected">
 		<Setter Property="Background" Value="{DynamicResource PrsTileBackground}"/>
+		<Setter Property="TextElement.Foreground" Value="Black"/>
 	</Style>
 	
 	<Style Selector="TabControl.Standard TabItem:selected:pointerover /template/ Border#PART_LayoutRoot">

+ 2 - 1
InABox.Avalonia/Theme/Classes/TabStrip.axaml

@@ -33,6 +33,7 @@
 	
 	<Style Selector="TabStrip.Standard TabStripItem:selected">
 		<Setter Property="Background" Value="{DynamicResource PrsTileBackground}"/>
+		<Setter Property="TextElement.Foreground" Value="Black"/>
 	</Style>
 	<Style Selector="TabStrip.Standard TabStripItem:selected:pointerover /template/ Border#PART_LayoutRoot">
 		<Setter Property="Background" Value="{DynamicResource PrsTileBackground}"/>
@@ -61,7 +62,7 @@
 	
 	<Style Selector="TabStrip.ButtonsList TabStripItem:selected">
 		<Setter Property="Background" Value="{DynamicResource PrsTileBackground}"/>
-		<Setter Property="Foreground" Value="{DynamicResource PrsTileForeground}"/>
+		<Setter Property="TextElement.Foreground" Value="{DynamicResource PrsTileForeground}"/>
 	</Style>
 	<Style Selector="TabStrip.ButtonsList TabStripItem:selected:pointerover /template/ Border#PART_LayoutRoot">
 		<Setter Property="Background" Value="{DynamicResource PrsTileBackground}"/>