diff --git a/demo/Ursa.Demo/Pages/ImageViewerDemo.axaml b/demo/Ursa.Demo/Pages/ImageViewerDemo.axaml index 67d03a8..9cfcb96 100644 --- a/demo/Ursa.Demo/Pages/ImageViewerDemo.axaml +++ b/demo/Ursa.Demo/Pages/ImageViewerDemo.axaml @@ -10,20 +10,72 @@ mc:Ignorable="d"> + + + + + + + + + + + + + + diff --git a/src/Ursa.Themes.Semi/Controls/ImageViewer.axaml b/src/Ursa.Themes.Semi/Controls/ImageViewer.axaml index 3a8b234..485606c 100644 --- a/src/Ursa.Themes.Semi/Controls/ImageViewer.axaml +++ b/src/Ursa.Themes.Semi/Controls/ImageViewer.axaml @@ -4,12 +4,15 @@ xmlns:u="https://irihi.tech/ursa"> - + - - + + @@ -22,5 +25,8 @@ + diff --git a/src/Ursa/Controls/ImageViewer/ImageViewer.cs b/src/Ursa/Controls/ImageViewer/ImageViewer.cs index ca77270..ac01c35 100644 --- a/src/Ursa/Controls/ImageViewer/ImageViewer.cs +++ b/src/Ursa/Controls/ImageViewer/ImageViewer.cs @@ -4,25 +4,28 @@ using Avalonia.Controls.Metadata; using Avalonia.Controls.Primitives; using Avalonia.Input; using Avalonia.Media; +using Avalonia.Media.Imaging; namespace Ursa.Controls; [TemplatePart(PART_Image, typeof(Image))] [TemplatePart(PART_Layer, typeof(VisualLayerManager))] +[PseudoClasses(PC_Moving)] public class ImageViewer: TemplatedControl { public const string PART_Image = "PART_Image"; public const string PART_Layer = "PART_Layer"; + public const string PC_Moving = ":moving"; - private Image _image = null!; + private Image? _image = null!; private VisualLayerManager? _layer; private Point? _lastClickPoint; - private Point? _lastReleasePoint; + private Point? _lastlocation; - public static readonly StyledProperty OverlayerProperty = AvaloniaProperty.Register( + public static readonly StyledProperty OverlayerProperty = AvaloniaProperty.Register( nameof(Overlayer)); - public object? Overlayer + public Control? Overlayer { get => GetValue(OverlayerProperty); set => SetValue(OverlayerProperty, value); @@ -38,34 +41,35 @@ public class ImageViewer: TemplatedControl private double _scale = 1; public static readonly DirectProperty ScaleProperty = AvaloniaProperty.RegisterDirect( - nameof(Scale), o => o.Scale, unsetValue: 1); + nameof(Scale), o => o.Scale, (o,v)=> o.Scale = v, unsetValue: 1); public double Scale { get => _scale; - private set => SetAndRaise(ScaleProperty, ref _scale, value); + set => SetAndRaise(ScaleProperty, ref _scale, value); } private double _translateX; public static readonly DirectProperty TranslateXProperty = AvaloniaProperty.RegisterDirect( - nameof(TranslateX), o => o.TranslateX, unsetValue: 0); + nameof(TranslateX), o => o.TranslateX, (o,v)=>o.TranslateX = v, unsetValue: 0); public double TranslateX { get => _translateX; - private set => SetAndRaise(TranslateXProperty, ref _translateX, value); + set => SetAndRaise(TranslateXProperty, ref _translateX, value); } private double _translateY; - public static readonly DirectProperty TranslateYProperty = AvaloniaProperty.RegisterDirect( - nameof(TranslateY), o => o.TranslateY); + public static readonly DirectProperty TranslateYProperty = + AvaloniaProperty.RegisterDirect( + nameof(TranslateY), o => o.TranslateY, (o, v) => o.TranslateY = v, unsetValue: 0); public double TranslateY { get => _translateY; - private set => SetAndRaise(TranslateYProperty, ref _translateY, value); + set => SetAndRaise(TranslateYProperty, ref _translateY, value); } @@ -73,12 +77,42 @@ public class ImageViewer: TemplatedControl { OverlayerProperty.Changed.AddClassHandler((o, e) => o.OnOverlayerChanged(e)); SourceProperty.Changed.AddClassHandler((o, e) => o.OnSourceChanged(e)); + TranslateXProperty.Changed.AddClassHandler((o,e)=>o.OnTranslateXChanged(e)); + TranslateYProperty.Changed.AddClassHandler((o, e) => o.OnTranslateYChanged(e)); } - + + private void OnTranslateYChanged(AvaloniaPropertyChangedEventArgs args) + { + if (PseudoClasses.Contains(PC_Moving)) return; + var newValue = args.GetNewValue(); + if (_lastlocation is not null) + { + _lastlocation = _lastlocation.Value.WithY(newValue); + } + else + { + _lastlocation = new Point(0, newValue); + } + } + + private void OnTranslateXChanged(AvaloniaPropertyChangedEventArgs args) + { + if (PseudoClasses.Contains(PC_Moving)) return; + var newValue = args.GetNewValue(); + if (_lastlocation is not null) + { + _lastlocation = _lastlocation.Value.WithX(newValue); + } + else + { + _lastlocation = new Point(newValue, 0); + } + } + private void OnOverlayerChanged(AvaloniaPropertyChangedEventArgs args) { - var control = args.GetNewValue(); - if (control is Control c) + var control = args.GetNewValue(); + if (control is { } c) { AdornerLayer.SetAdorner(this, c); } @@ -90,7 +124,12 @@ public class ImageViewer: TemplatedControl Size size = image.Size; double width = this.Width; double height = this.Height; - Scale = Math.Min(width/size.Width, height/size.Height); + if (_image is not null) + { + _image.Width = size.Width; + _image.Height = size.Height; + } + Scale = Math.Max(width/size.Width, height/size.Height); } protected override void OnApplyTemplate(TemplateAppliedEventArgs e) @@ -98,8 +137,16 @@ public class ImageViewer: TemplatedControl base.OnApplyTemplate(e); _image = e.NameScope.Get(PART_Image); _layer = e.NameScope.Get(PART_Layer); - Scale = 1; - if (Overlayer is Control c) + if (Source is { } i) + { + Size size = i.Size; + double width = this.Width; + double height = this.Height; + _image.Width = size.Width; + _image.Height = size.Height; + Scale = Math.Max(width/size.Width, height/size.Height); + } + if (Overlayer is { } c) { AdornerLayer.SetAdorner(this, c); } @@ -128,13 +175,13 @@ public class ImageViewer: TemplatedControl base.OnPointerMoved(e); if (e.Pointer.Captured == this && _lastClickPoint != null) { + PseudoClasses.Set(PC_Moving, true); Point p = e.GetPosition(this); double deltaX = p.X - _lastClickPoint.Value.X; double deltaY = p.Y - _lastClickPoint.Value.Y; - TranslateX = deltaX + (_lastReleasePoint?.X ?? 0); - TranslateY = deltaY + (_lastReleasePoint?.Y ?? 0); + TranslateX = deltaX + (_lastlocation?.X ?? 0); + TranslateY = deltaY + (_lastlocation?.Y ?? 0); } - } protected override void OnPointerPressed(PointerPressedEventArgs e) @@ -148,6 +195,7 @@ public class ImageViewer: TemplatedControl { base.OnPointerReleased(e); e.Pointer.Capture(null); - _lastReleasePoint = new Point(TranslateX, TranslateY); + _lastlocation = new Point(TranslateX, TranslateY); + PseudoClasses.Set(PC_Moving, false); } } \ No newline at end of file