diff --git a/src/Converters/DoubleConverters.cs b/src/Converters/DoubleConverters.cs new file mode 100644 index 00000000..5b7c0a03 --- /dev/null +++ b/src/Converters/DoubleConverters.cs @@ -0,0 +1,19 @@ +using Avalonia.Data.Converters; + +namespace SourceGit.Converters +{ + public static class DoubleConverters + { + public static readonly FuncValueConverter Increase = + new FuncValueConverter(v => v + 1.0); + + public static readonly FuncValueConverter Decrease = + new FuncValueConverter(v => v - 1.0); + + public static readonly FuncValueConverter ToPercentage = + new FuncValueConverter(v => (v * 100).ToString("F3") + "%"); + + public static readonly FuncValueConverter OneMinusToPercentage = + new FuncValueConverter(v => ((1.0 - v) * 100).ToString("F3") + "%"); + } +} diff --git a/src/Converters/FontSizeModifyConverters.cs b/src/Converters/FontSizeModifyConverters.cs deleted file mode 100644 index 4c885e38..00000000 --- a/src/Converters/FontSizeModifyConverters.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Avalonia.Data.Converters; - -namespace SourceGit.Converters -{ - public static class FontSizeModifyConverters - { - public static readonly FuncValueConverter Increase = - new FuncValueConverter(v => v + 1.0); - - public static readonly FuncValueConverter Decrease = - new FuncValueConverter(v => v - 1.0); - } -} diff --git a/src/Views/CommitBaseInfo.axaml b/src/Views/CommitBaseInfo.axaml index 9b683948..42fa2b48 100644 --- a/src/Views/CommitBaseInfo.axaml +++ b/src/Views/CommitBaseInfo.axaml @@ -25,7 +25,7 @@ @@ -41,7 +41,7 @@ diff --git a/src/Views/DiffView.axaml b/src/Views/DiffView.axaml index 4e978a43..ce96662c 100644 --- a/src/Views/DiffView.axaml +++ b/src/Views/DiffView.axaml @@ -184,60 +184,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0,0,0,4 - 0 - 0 - 8 - 16 - 16 - - - - + diff --git a/src/Views/DiffView.axaml.cs b/src/Views/DiffView.axaml.cs index d03f9f91..860627d3 100644 --- a/src/Views/DiffView.axaml.cs +++ b/src/Views/DiffView.axaml.cs @@ -1,164 +1,7 @@ -using System; - -using Avalonia; using Avalonia.Controls; -using Avalonia.Media; -using Avalonia.Media.Imaging; -using Avalonia.Styling; namespace SourceGit.Views { - public class ImageDiffView : Control - { - public static readonly StyledProperty AlphaProperty = - AvaloniaProperty.Register(nameof(Alpha), 0.5); - - public double Alpha - { - get => GetValue(AlphaProperty); - set => SetValue(AlphaProperty, value); - } - - public static readonly StyledProperty OldImageProperty = - AvaloniaProperty.Register(nameof(OldImage), null); - - public Bitmap OldImage - { - get => GetValue(OldImageProperty); - set => SetValue(OldImageProperty, value); - } - - public static readonly StyledProperty NewImageProperty = - AvaloniaProperty.Register(nameof(NewImage), null); - - public Bitmap NewImage - { - get => GetValue(NewImageProperty); - set => SetValue(NewImageProperty, value); - } - - static ImageDiffView() - { - AffectsMeasure(OldImageProperty, NewImageProperty); - AffectsRender(AlphaProperty); - } - - public override void Render(DrawingContext context) - { - if (_bgBrush == null) - { - var maskBrush = new SolidColorBrush(ActualThemeVariant == ThemeVariant.Dark ? 0xFF404040 : 0xFFBBBBBB); - var bg = new DrawingGroup() - { - Children = - { - new GeometryDrawing() { Brush = maskBrush, Geometry = new RectangleGeometry(new Rect(0, 0, 12, 12)) }, - new GeometryDrawing() { Brush = maskBrush, Geometry = new RectangleGeometry(new Rect(12, 12, 12, 12)) }, - } - }; - - _bgBrush = new DrawingBrush(bg) - { - AlignmentX = AlignmentX.Left, - AlignmentY = AlignmentY.Top, - DestinationRect = new RelativeRect(new Size(24, 24), RelativeUnit.Absolute), - Stretch = Stretch.None, - TileMode = TileMode.Tile, - }; - } - - context.FillRectangle(_bgBrush, new Rect(Bounds.Size)); - - var alpha = Alpha; - var w = Bounds.Width - 16; - var h = Bounds.Height - 16; - var x = w * alpha; - var left = OldImage; - if (left != null && alpha > 0) - { - var src = new Rect(0, 0, left.Size.Width * alpha, left.Size.Height); - var dst = new Rect(8, 8, x, h); - context.DrawImage(left, src, dst); - } - - var right = NewImage; - if (right != null && alpha < 1) - { - var src = new Rect(right.Size.Width * alpha, 0, right.Size.Width * (1 - alpha), right.Size.Height); - var dst = new Rect(x + 8, 8, w - x, h); - context.DrawImage(right, src, dst); - } - - context.DrawLine(new Pen(Brushes.DarkGreen, 2), new Point(x + 8, 0), new Point(x + 8, Bounds.Height)); - } - - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) - { - base.OnPropertyChanged(change); - - if (change.Property.Name == "ActualThemeVariant") - { - _bgBrush = null; - InvalidateVisual(); - } - } - - protected override Size MeasureOverride(Size availableSize) - { - var left = OldImage; - var right = NewImage; - - if (left != null) - { - var lSize = GetDesiredSize(left.Size, availableSize); - if (right != null) - { - var rSize = GetDesiredSize(right.Size, availableSize); - if (rSize.Width > lSize.Width) - return rSize; - return lSize; - } - else - { - return lSize; - } - } - else if (right != null) - { - return GetDesiredSize(right.Size, availableSize); - } - else - { - return availableSize; - } - } - - private Size GetDesiredSize(Size img, Size available) - { - var w = available.Width - 16; - var h = available.Height - 16; - - if (img.Width <= w) - { - if (img.Height <= h) - { - return new Size(img.Width + 16, img.Height + 16); - } - else - { - return new Size(h * img.Width / img.Height + 16, available.Height); - } - } - else - { - var s = Math.Max(img.Width / w, img.Height / h); - return new Size(img.Width / s + 16, img.Height / s + 16); - } - } - - private DrawingBrush _bgBrush = null; - } - public partial class DiffView : UserControl { public DiffView() diff --git a/src/Views/Hotkeys.axaml b/src/Views/Hotkeys.axaml index bf633bff..a4748880 100644 --- a/src/Views/Hotkeys.axaml +++ b/src/Views/Hotkeys.axaml @@ -55,7 +55,7 @@ @@ -81,7 +81,7 @@ @@ -113,7 +113,7 @@ diff --git a/src/Views/ImageDiffView.axaml b/src/Views/ImageDiffView.axaml new file mode 100644 index 00000000..9987a523 --- /dev/null +++ b/src/Views/ImageDiffView.axaml @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0,0,0,4 + 0 + 0 + 8 + 16 + 16 + + + + + + + + + + + + diff --git a/src/Views/ImageDiffView.axaml.cs b/src/Views/ImageDiffView.axaml.cs new file mode 100644 index 00000000..ddb6968c --- /dev/null +++ b/src/Views/ImageDiffView.axaml.cs @@ -0,0 +1,294 @@ +using System; + +using Avalonia; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Media; +using Avalonia.Media.Imaging; +using Avalonia.Styling; + +namespace SourceGit.Views +{ + public class ImageContainer : ContentControl + { + protected override Type StyleKeyOverride => typeof(ContentControl); + + public override void Render(DrawingContext context) + { + if (_bgBrush == null) + { + var maskBrush = new SolidColorBrush(ActualThemeVariant == ThemeVariant.Dark ? 0xFF404040 : 0xFFBBBBBB); + var bg = new DrawingGroup() + { + Children = + { + new GeometryDrawing() { Brush = maskBrush, Geometry = new RectangleGeometry(new Rect(0, 0, 12, 12)) }, + new GeometryDrawing() { Brush = maskBrush, Geometry = new RectangleGeometry(new Rect(12, 12, 12, 12)) }, + } + }; + + _bgBrush = new DrawingBrush(bg) + { + AlignmentX = AlignmentX.Left, + AlignmentY = AlignmentY.Top, + DestinationRect = new RelativeRect(new Size(24, 24), RelativeUnit.Absolute), + Stretch = Stretch.None, + TileMode = TileMode.Tile, + }; + } + + context.FillRectangle(_bgBrush, new Rect(Bounds.Size)); + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property.Name == "ActualThemeVariant") + { + _bgBrush = null; + InvalidateVisual(); + } + } + + private DrawingBrush _bgBrush = null; + } + + public class ImagesSwipeControl : Control + { + public static readonly StyledProperty AlphaProperty = + AvaloniaProperty.Register(nameof(Alpha), 0.5); + + public double Alpha + { + get => GetValue(AlphaProperty); + set => SetValue(AlphaProperty, value); + } + + public static readonly StyledProperty OldImageProperty = + AvaloniaProperty.Register(nameof(OldImage), null); + + public Bitmap OldImage + { + get => GetValue(OldImageProperty); + set => SetValue(OldImageProperty, value); + } + + public static readonly StyledProperty NewImageProperty = + AvaloniaProperty.Register(nameof(NewImage), null); + + public Bitmap NewImage + { + get => GetValue(NewImageProperty); + set => SetValue(NewImageProperty, value); + } + + static ImagesSwipeControl() + { + AffectsMeasure(OldImageProperty, NewImageProperty); + AffectsRender(AlphaProperty); + } + + public override void Render(DrawingContext context) + { + var alpha = Alpha; + var w = Bounds.Width; + var h = Bounds.Height; + var x = w * alpha; + var left = OldImage; + if (left != null && alpha > 0) + { + var src = new Rect(0, 0, left.Size.Width * alpha, left.Size.Height); + var dst = new Rect(0, 0, x, h); + context.DrawImage(left, src, dst); + } + + var right = NewImage; + if (right != null && alpha < 1) + { + var src = new Rect(right.Size.Width * alpha, 0, right.Size.Width * (1 - alpha), right.Size.Height); + var dst = new Rect(x, 0, w - x, h); + context.DrawImage(right, src, dst); + } + + context.DrawLine(new Pen(Brushes.DarkGreen, 2), new Point(x, 0), new Point(x, Bounds.Height)); + } + + protected override void OnPointerPressed(PointerPressedEventArgs e) + { + base.OnPointerPressed(e); + + var p = e.GetPosition(this); + var hitbox = new Rect(Math.Max(Bounds.Width * Alpha - 2, 0), 0, 4, Bounds.Height); + var pointer = e.GetCurrentPoint(this); + if (pointer.Properties.IsLeftButtonPressed && hitbox.Contains(p)) + { + _pressedOnSlider = true; + Cursor = new Cursor(StandardCursorType.SizeWestEast); + e.Pointer.Capture(this); + e.Handled = true; + } + } + + protected override void OnPointerReleased(PointerReleasedEventArgs e) + { + base.OnPointerReleased(e); + _pressedOnSlider = false; + } + + protected override void OnPointerMoved(PointerEventArgs e) + { + var w = Bounds.Width; + var p = e.GetPosition(this); + + if (_pressedOnSlider) + { + SetCurrentValue(AlphaProperty, Math.Clamp(p.X, 0, w) / w); + } + else + { + var hitbox = new Rect(Math.Max(w * Alpha - 2, 0), 0, 4, Bounds.Height); + if (hitbox.Contains(p)) + { + if (!_lastInSlider) + { + _lastInSlider = true; + Cursor = new Cursor(StandardCursorType.SizeWestEast); + } + } + else + { + if (_lastInSlider) + { + _lastInSlider = false; + Cursor = null; + } + } + } + } + + protected override Size MeasureOverride(Size availableSize) + { + var left = OldImage; + var right = NewImage; + + if (left == null) + return right == null ? availableSize : GetDesiredSize(right.Size, availableSize); + + if (right == null) + return GetDesiredSize(left.Size, availableSize); + + var ls = GetDesiredSize(left.Size, availableSize); + var rs = GetDesiredSize(right.Size, availableSize); + return ls.Width > rs.Width ? ls : rs; + } + + private Size GetDesiredSize(Size img, Size available) + { + var w = available.Width; + var h = available.Height; + + var sw = available.Width / img.Width; + var sh = available.Height / img.Height; + var scale = Math.Min(sw, sh); + + return new Size(scale * img.Width, scale * img.Height); + } + + private bool _pressedOnSlider = false; + private bool _lastInSlider = false; + } + + public class ImageBlendControl : Control + { + public static readonly StyledProperty AlphaProperty = + AvaloniaProperty.Register(nameof(Alpha), 1.0); + + public double Alpha + { + get => GetValue(AlphaProperty); + set => SetValue(AlphaProperty, value); + } + + public static readonly StyledProperty OldImageProperty = + AvaloniaProperty.Register(nameof(OldImage), null); + + public Bitmap OldImage + { + get => GetValue(OldImageProperty); + set => SetValue(OldImageProperty, value); + } + + public static readonly StyledProperty NewImageProperty = + AvaloniaProperty.Register(nameof(NewImage), null); + + public Bitmap NewImage + { + get => GetValue(NewImageProperty); + set => SetValue(NewImageProperty, value); + } + + static ImageBlendControl() + { + AffectsMeasure(OldImageProperty, NewImageProperty); + AffectsRender(AlphaProperty); + } + + public override void Render(DrawingContext context) + { + var rect = new Rect(0, 0, Bounds.Width, Bounds.Height); + var alpha = Alpha; + var left = OldImage; + if (left != null && alpha < 1) + { + var state = context.PushOpacity(1- alpha); + context.DrawImage(left, rect); + state.Dispose(); + } + + var right = NewImage; + if (right != null && alpha > 0) + { + var state = context.PushOpacity(alpha); + context.DrawImage(right, rect); + state.Dispose(); + } + } + + protected override Size MeasureOverride(Size availableSize) + { + var left = OldImage; + var right = NewImage; + + if (left == null) + return right == null ? availableSize : GetDesiredSize(right.Size, availableSize); + + if (right == null) + return GetDesiredSize(left.Size, availableSize); + + var ls = GetDesiredSize(left.Size, availableSize); + var rs = GetDesiredSize(right.Size, availableSize); + return ls.Width > rs.Width ? ls : rs; + } + + private Size GetDesiredSize(Size img, Size available) + { + var w = available.Width; + var h = available.Height; + + var sw = available.Width / img.Width; + var sh = available.Height / img.Height; + var scale = Math.Min(sw, sh); + + return new Size(scale * img.Width, scale * img.Height); + } + } + + public partial class ImageDiffView : UserControl + { + public ImageDiffView() + { + InitializeComponent(); + } + } +} diff --git a/src/Views/PopupRunningStatus.axaml b/src/Views/PopupRunningStatus.axaml index 5cc9a4bd..c1451a1e 100644 --- a/src/Views/PopupRunningStatus.axaml +++ b/src/Views/PopupRunningStatus.axaml @@ -18,7 +18,7 @@ diff --git a/src/Views/Welcome.axaml b/src/Views/Welcome.axaml index ceccc0ce..fb74205f 100644 --- a/src/Views/Welcome.axaml +++ b/src/Views/Welcome.axaml @@ -117,7 +117,7 @@ Classes="italic" Margin="0,0,8,0" HorizontalAlignment="Center" VerticalAlignment="Center" - FontSize="{Binding Source={x:Static vm:Preference.Instance}, Path=DefaultFontSize, Converter={x:Static c:FontSizeModifyConverters.Decrease}}" + FontSize="{Binding Source={x:Static vm:Preference.Instance}, Path=DefaultFontSize, Converter={x:Static c:DoubleConverters.Decrease}}" Text="{DynamicResource Text.Welcome.DragDropTip}" Foreground="{DynamicResource Brush.FG2}"/>