Skip to content

Commit

Permalink
fix(dragdrop): Fix support of transparent control in DragUI
Browse files Browse the repository at this point in the history
  • Loading branch information
dr1rrb committed Oct 15, 2021
1 parent 6ea93ed commit ef0b6fd
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 27 deletions.
2 changes: 2 additions & 0 deletions src/Uno.Foundation/Point.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ internal string ToDebugString()

internal Point WithY(double y) => new Point(X, y);

internal Point GetOpposite() => new Point(-X, -Y);

public static bool operator ==(Point left, Point right)
{
return left.Equals(right);
Expand Down
6 changes: 2 additions & 4 deletions src/Uno.UI/UI/Xaml/DragDrop/DragUI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,10 @@ public partial class DragUI
{
internal ImageSource? Content { get; set; }

internal Point? Anchor { get; private set; }
internal Point? Anchor { get; set; }

public void SetContentFromBitmapImage(BitmapImage bitmapImage)
{
Content = bitmapImage;
}
=> SetContentFromBitmapImage(bitmapImage, default);

public void SetContentFromBitmapImage(BitmapImage bitmapImage, Point anchorPoint)
{
Expand Down
26 changes: 23 additions & 3 deletions src/Uno.UI/UI/Xaml/DragDrop/DragView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,17 @@ public ImageSource? Content
}
#endregion

#region ContentAnchor
public static readonly DependencyProperty ContentAnchorProperty = DependencyProperty.Register(
"ContentAnchor", typeof(Point), typeof(DragView), new FrameworkPropertyMetadata(default(Point)));

public Point ContentAnchor
{
get => (Point)GetValue(ContentAnchorProperty);
private set => SetValue(ContentAnchorProperty, value);
}
#endregion

#region ContentVisibility
public static readonly DependencyProperty ContentVisibilityProperty = DependencyProperty.Register(
"ContentVisibility", typeof(Visibility), typeof(DragView), new FrameworkPropertyMetadata(default(Visibility)));
Expand Down Expand Up @@ -110,8 +121,8 @@ public void SetLocation(Point location)
// TODO: Make sure to not move the element out of the bounds of the window
_location = location;

_transform.X = location.X - (ActualWidth / 2);
_transform.Y = location.Y - 40; // The caption is above the pointer
_transform.X = location.X;
_transform.Y = location.Y;
}

public void Update(DataPackageOperation acceptedOperation, CoreDragUIOverride viewOverride)
Expand All @@ -132,7 +143,16 @@ public void Update(DataPackageOperation acceptedOperation, CoreDragUIOverride vi
GlyphVisibility = ToVisibility(viewOverride.IsGlyphVisible);
Caption = caption!;
CaptionVisibility = ToVisibility(viewOverride.IsCaptionVisible && !string.IsNullOrWhiteSpace(caption));
Content = viewOverride.Content as ImageSource ?? _ui?.Content;
if (viewOverride.Content is ImageSource overridenContent)
{
Content = overridenContent;
ContentAnchor = viewOverride.ContentAnchor;
}
else
{
Content = _ui?.Content;
ContentAnchor = _ui?.Anchor ?? default;
}
ContentVisibility = ToVisibility(viewOverride.IsContentVisible);
TooltipVisibility = ToVisibility(viewOverride.IsGlyphVisible || viewOverride.IsCaptionVisible);
Visibility = Visibility.Visible;
Expand Down
14 changes: 12 additions & 2 deletions src/Uno.UI/UI/Xaml/DragDrop/DragView.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
xmlns:uBehaviors="using:Uno.UI.Behaviors"
mc:Ignorable="d ios android wasm netstdref macos skia">

<!-- Default style for Windows.UI.Xaml.Controls.ScrollViewer -->
<!-- Default style for internal control Windows.UI.Xaml.DragView -->
<Style TargetType="DragView">
<Setter Property="IsTabStop" Value="False" />
<Setter Property="IsHitTestVisible" Value="False" />
Expand All @@ -26,14 +26,24 @@
<Setter.Value>
<ControlTemplate TargetType="DragView">
<Grid HorizontalAlignment="Left" VerticalAlignment="Top">
<Image Visibility="{TemplateBinding ContentVisibility}" Source="{TemplateBinding Content}" Opacity=".8" />
<Image Visibility="{TemplateBinding ContentVisibility}" Source="{TemplateBinding Content}" Opacity=".8">
<Image.RenderTransform>
<TranslateTransform X="{TemplateBinding ContentAnchor.X}" Y="{TemplateBinding ContentAnchor.Y}" />
</Image.RenderTransform>
</Image>
<StackPanel
HorizontalAlignment="Left"
VerticalAlignment="Top"
Orientation="Horizontal"
BorderThickness="1"
BorderBrush="{StaticResource SystemControlForegroundChromeHighBrush}"
Background="{StaticResource SystemControlBackgroundChromeMediumLowBrush}"
Padding="2,5"
Visibility="{TemplateBinding TooltipVisibility}">
<StackPanel.RenderTransform>
<!-- The caption is above the pointer -->
<TranslateTransform Y="-40" />
</StackPanel.RenderTransform>
<TextBlock Visibility="{TemplateBinding GlyphVisiblity}" Text="{TemplateBinding Glyph}" Margin="3,0" />
<TextBlock Visibility="{TemplateBinding CaptionVisiblity}" Text="{TemplateBinding Caption}" Margin="3,0" />
</StackPanel>
Expand Down
23 changes: 14 additions & 9 deletions src/Uno.UI/UI/Xaml/Media/Imaging/RenderTargetBitmap.Android.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
using System;
#nullable enable

using System;
using System.IO;
using System.Linq;
using System.Numerics;
using Android.Graphics;
using Uno.UI;
using Windows.Foundation;
Expand All @@ -13,29 +16,31 @@ partial class RenderTargetBitmap
private protected override bool IsSourceReady => _buffer != null;

/// <inheritdoc />
private protected override bool TryOpenSourceSync(int? targetWidth, int? targetHeight, out Bitmap image)
private protected override bool TryOpenSourceSync(int? targetWidth, int? targetHeight, out Bitmap? image)
{
image = BitmapFactory.DecodeByteArray(_buffer, 0, _buffer.Length);
return image != null;
}

private static byte[] RenderAsPng(UIElement element, Size? scaledSize = null)
{
var width = (int)ViewHelper.LogicalToPhysicalPixels(element.ActualSize.X);
var height = (int)ViewHelper.LogicalToPhysicalPixels(element.ActualSize.Y);
var bitmap = Bitmap.CreateBitmap(width, height, Bitmap.Config.Argb8888);
var logical = element.ActualSize.ToSize();
var physical = logical.LogicalToPhysicalPixels();
var bitmap = Bitmap.CreateBitmap((int)physical.Width, (int)physical.Height, Bitmap.Config.Argb8888!)
?? throw new InvalidOperationException("Failed to create target native bitmap.");
var canvas = new Canvas(bitmap);

// Make sure the element has been Layouted
element.Layout(0, 0, width, height);
element.Layout(0, 0, (int)physical.Width, (int)physical.Height);

// Render on the canvas
canvas.DrawColor(Colors.White);
canvas.DrawColor(Colors.Transparent);
element.Draw(canvas);

if (scaledSize.HasValue)
if (scaledSize is {} targetSize)
{
canvas.Scale((float)(scaledSize.Value.Width / (float)width), (float)(scaledSize.Value.Height / (float)height));
bitmap = Bitmap.CreateScaledBitmap(bitmap, (int)targetSize.Width, (int)targetSize.Height, false)
?? throw new InvalidOperationException("Failed to scaled native bitmap to the requested size.");
}

using var stream = new MemoryStream();
Expand Down
4 changes: 2 additions & 2 deletions src/Uno.UI/UI/Xaml/Media/Imaging/RenderTargetBitmap.iOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ private byte[] RenderAsPng(UIElement element, Size? scaledSize = null)
UIImage img;
try
{
UIGraphics.BeginImageContextWithOptions(new Size(element.ActualSize.X, element.ActualSize.Y), true, 1f);
UIGraphics.BeginImageContextWithOptions(new Size(element.ActualSize.X, element.ActualSize.Y), false, 1f);
var ctx = UIGraphics.GetCurrentContext();
ctx.SetFillColor(Colors.White);
ctx.SetFillColor(Colors.Transparent); // This is only for pixels not used, but the bitmap as the same size of the element. We keep it only for safety!
element.Layer.RenderInContext(ctx);
img = UIGraphics.GetImageFromCurrentImageContext();
}
Expand Down
23 changes: 16 additions & 7 deletions src/Uno.UI/UI/Xaml/UIElement.Pointers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ internal HitTestability GetHitTestVisibility()
#endif
}

#region GestureRecognizer wire-up
#region GestureRecognizer wire-up

#region Event to RoutedEvent handler adapters
// Note: For the manipulation and gesture event args, the original source has to be the element that raise the event
Expand Down Expand Up @@ -391,8 +391,8 @@ private async void HapticFeedbackWhenReadyToDrag(GestureRecognizer sender, Gestu
}
}
#endregion

#region Manipulations (recognizer settings / custom bubbling)
#region Manipulations (recognizer settings / custom bubbling)
partial void AddManipulationHandler(RoutedEvent routedEvent, int handlersCount, object handler, bool handledEventsToo)
{
if (handlersCount == 1)
Expand Down Expand Up @@ -449,7 +449,7 @@ partial void PrepareManagedManipulationEventBubbling(RoutedEvent routedEvent, re
}
#endregion

#region Gestures (recognizer settings / custom bubbling / early completion)
#region Gestures (recognizer settings / custom bubbling / early completion)
private bool _isGestureCompleted;

partial void AddGestureHandler(RoutedEvent routedEvent, int handlersCount, object handler, bool handledEventsToo)
Expand Down Expand Up @@ -522,7 +522,7 @@ private protected void CompleteGesture()
}
#endregion

#region Drag And Drop (recognizer settings / custom bubbling / drag starting event)
#region Drag And Drop (recognizer settings / custom bubbling / drag starting event)
private void UpdateDragAndDrop(bool isEnabled)
{
// Note: The drag and drop recognizer setting is only driven by the CanDrag,
Expand Down Expand Up @@ -640,6 +640,10 @@ private async Task<DataPackageOperation> StartDragAsyncCore(PointerPoint pointer
PrepareShare(routedArgs.Data); // Gives opportunity to the control to fulfill the data
SafeRaiseEvent(DragStartingEvent, routedArgs); // The event won't bubble, cf. PrepareManagedDragAndDropEventBubbling

// We capture the original position of the pointer before going async,
// so we have the closet location of the "down" possible.
var ptPosition = ptArgs.GetCurrentPoint(this).Position;

if (routedArgs.Deferral is { } deferral)
{
await deferral.Completed(ct);
Expand All @@ -666,9 +670,14 @@ private async Task<DataPackageOperation> StartDragAsyncCore(PointerPoint pointer

if (RenderTargetBitmap.IsImplemented && routedArgs.DragUI.Content is null)
{
// Note: Bitmap rendered by the RenderTargetBitmap is in physical pixels,
// so we provide the ActualSize to request the image to be scaled back in logical pixels.

var target = new RenderTargetBitmap();
await target.RenderAsync(this);
await target.RenderAsync(this, (int)ActualSize.X, (int)ActualSize.Y);

routedArgs.DragUI.Content = target;
routedArgs.DragUI.Anchor = ptPosition.GetOpposite();
}

var asyncResult = new TaskCompletionSource<DataPackageOperation>();
Expand Down Expand Up @@ -756,7 +765,7 @@ partial void PrepareManagedPointerEventBubbling(RoutedEvent routedEvent, ref Rou
}
}

#region Partial API to raise pointer events and gesture recognition (OnNative***)
#region Partial API to raise pointer events and gesture recognition (OnNative***)
private bool OnNativePointerEnter(PointerRoutedEventArgs args, BubblingContext ctx = default) => OnPointerEnter(args);

private bool OnPointerEnter(PointerRoutedEventArgs args, BubblingContext ctx = default)
Expand Down

0 comments on commit ef0b6fd

Please sign in to comment.