Browse Source

Add image copy to clipboard flyout

pull/165/head
Ionite 1 year ago
parent
commit
5783dcfeef
No known key found for this signature in database
  1. 21
      StabilityMatrix.Avalonia/Controls/ImageGalleryCard.axaml
  2. 21
      StabilityMatrix.Avalonia/Helpers/Win32ClipboardFormat.cs
  3. 99
      StabilityMatrix.Avalonia/Helpers/WindowsClipboard.cs
  4. 18
      StabilityMatrix.Avalonia/ViewModels/Inference/ImageGalleryCardViewModel.cs

21
StabilityMatrix.Avalonia/Controls/ImageGalleryCard.axaml

@ -13,7 +13,8 @@
<controls:ImageGalleryCard DataContext="{x:Static mocks:DesignData.ImageGalleryCardViewModel}" />
</Grid>
</Design.PreviewWith>
<Style Selector="ListBox /template/ VirtualizingStackPanel">
<Setter Property="Orientation" Value="Horizontal" />
</Style>
@ -39,10 +40,11 @@
</Button>
<Panel Grid.Column="1">
<Carousel
Name="ImageCarousel"
x:Name="ImageCarousel"
IsVisible="{Binding !IsPreviewOverlayEnabled}"
SelectedItem="{Binding SelectedImage}"
ItemsSource="{Binding ImageSources}">
<Carousel.PageTransition>
<CompositePageTransition>
<PageSlide Orientation="Horizontal" Duration="0.167">
@ -56,15 +58,28 @@
<CrossFade Duration="0.1" />
</CompositePageTransition>
</Carousel.PageTransition>
<Carousel.ItemTemplate>
<DataTemplate DataType="{x:Type system:String}">
<controls:BetterAdvancedImage
x:Name="ImageView"
CornerRadius="4"
Source="{Binding}"
Stretch="Uniform"
StretchDirection="Both" />
StretchDirection="Both">
<controls:BetterAdvancedImage.ContextFlyout>
<ui:FAMenuFlyout>
<ui:MenuFlyoutItem
Command="{ReflectionBinding #ImageCarousel.DataContext.CopyImageCommand}"
CommandParameter="{Binding #ImageView.CurrentImage}"
Text="Copy"
IconSource="Copy" />
</ui:FAMenuFlyout>
</controls:BetterAdvancedImage.ContextFlyout>
</controls:BetterAdvancedImage>
</DataTemplate>
</Carousel.ItemTemplate>
</Carousel>
<Border
ClipToBounds="True"

21
StabilityMatrix.Avalonia/Helpers/Win32ClipboardFormat.cs

@ -0,0 +1,21 @@
using System.Diagnostics.CodeAnalysis;
namespace StabilityMatrix.Avalonia.Helpers;
[SuppressMessage("ReSharper", "InconsistentNaming")]
[SuppressMessage("ReSharper", "IdentifierTypo")]
internal enum Win32ClipboardFormat : uint
{
CF_TEXT = 1,
CF_BITMAP = 2,
CF_SYLK = 4,
CF_DIF = 5,
CF_TIFF = 6,
CF_OEMTEXT = 7,
CF_DIB = 8,
CF_PALETTE = 9,
CF_PENDATA = 10,
CF_RIFF = 11,
CF_WAVE = 12,
CF_UNICODETEXT = 13
}

99
StabilityMatrix.Avalonia/Helpers/WindowsClipboard.cs

@ -0,0 +1,99 @@
using System;
using System.ComponentModel;
using System.IO;
using System.Runtime.InteropServices;
using Avalonia.Media.Imaging;
namespace StabilityMatrix.Avalonia.Helpers;
public static class WindowsClipboard
{
public static void SetBitmap(Bitmap bitmap)
{
if (bitmap == null)
throw new ArgumentNullException(nameof(bitmap));
// Convert from Avalonia Bitmap to System Bitmap
var memoryStream = new MemoryStream();
bitmap.Save(memoryStream); // this returns a png from Skia (we could save/load it from the system bitmap to convert it to a bmp first, but this seems to work well already)
var systemBitmap = new System.Drawing.Bitmap(memoryStream);
var hBitmap = systemBitmap.GetHbitmap();
var screenDC = GetDC(IntPtr.Zero);
var sourceDC = CreateCompatibleDC(screenDC);
var sourceBitmapSelection = SelectObject(sourceDC, hBitmap);
var destDC = CreateCompatibleDC(screenDC);
var compatibleBitmap = CreateCompatibleBitmap(
screenDC,
systemBitmap.Width,
systemBitmap.Height
);
var destinationBitmapSelection = SelectObject(destDC, compatibleBitmap);
BitBlt(destDC, 0, 0, systemBitmap.Width, systemBitmap.Height, sourceDC, 0, 0, 0x00CC0020); // SRCCOPY
try
{
OpenClipboard(IntPtr.Zero);
EmptyClipboard();
var result = SetClipboardData(
(uint) Win32ClipboardFormat.CF_BITMAP,
compatibleBitmap
);
if (result == IntPtr.Zero)
{
var errno = Marshal.GetLastWin32Error();
throw new Win32Exception(errno, $"SetClipboardData failed");
}
}
finally
{
CloseClipboard();
}
}
[DllImport("user32.dll", SetLastError = true)]
static extern bool OpenClipboard(IntPtr hWndNewOwner);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool CloseClipboard();
[DllImport("user32.dll")]
static extern IntPtr SetClipboardData(uint uFormat, IntPtr hMem);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool EmptyClipboard();
[DllImport("user32.dll", ExactSpelling = true)]
public static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("gdi32.dll", ExactSpelling = true)]
public static extern IntPtr CreateCompatibleDC(IntPtr hDC);
[DllImport("gdi32.dll", ExactSpelling = true)]
public static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int cx, int cy);
[DllImport("gdi32.dll", SetLastError = true, ExactSpelling = true)]
public static extern IntPtr SelectObject(IntPtr hdc, IntPtr h);
[DllImport("gdi32.dll", SetLastError = true, ExactSpelling = true)]
public static extern bool BitBlt(
IntPtr hdc,
int x,
int y,
int cx,
int cy,
IntPtr hdcSrc,
int x1,
int y1,
uint rop
);
}

18
StabilityMatrix.Avalonia/ViewModels/Inference/ImageGalleryCardViewModel.cs

@ -1,7 +1,11 @@
using Avalonia.Collections;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using NLog;
using StabilityMatrix.Avalonia.Controls;
using StabilityMatrix.Avalonia.Helpers;
using StabilityMatrix.Core.Attributes;
namespace StabilityMatrix.Avalonia.ViewModels.Inference;
@ -9,10 +13,24 @@ namespace StabilityMatrix.Avalonia.ViewModels.Inference;
[View(typeof(ImageGalleryCard))]
public partial class ImageGalleryCardViewModel : ViewModelBase
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
[ObservableProperty] private bool isPreviewOverlayEnabled;
[ObservableProperty] private IImage? previewImage;
[ObservableProperty] private AvaloniaList<string> imageSources = new();
[ObservableProperty] private string? selectedImage;
[RelayCommand]
private void FlyoutCopy(IImage? image)
{
if (image is null)
{
Logger.Trace("FlyoutCopy: image is null");
return;
}
Logger.Trace($"FlyoutCopy is copying {image}");
WindowsClipboard.SetBitmap((Bitmap) image);
}
}

Loading…
Cancel
Save