Skip to content

Commit ef0b6fd

Browse files
committed
fix(dragdrop): Fix support of transparent control in DragUI
1 parent 6ea93ed commit ef0b6fd

File tree

7 files changed

+71
-27
lines changed

7 files changed

+71
-27
lines changed

src/Uno.Foundation/Point.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ internal string ToDebugString()
3737

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

40+
internal Point GetOpposite() => new Point(-X, -Y);
41+
4042
public static bool operator ==(Point left, Point right)
4143
{
4244
return left.Equals(right);

src/Uno.UI/UI/Xaml/DragDrop/DragUI.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,10 @@ public partial class DragUI
1010
{
1111
internal ImageSource? Content { get; set; }
1212

13-
internal Point? Anchor { get; private set; }
13+
internal Point? Anchor { get; set; }
1414

1515
public void SetContentFromBitmapImage(BitmapImage bitmapImage)
16-
{
17-
Content = bitmapImage;
18-
}
16+
=> SetContentFromBitmapImage(bitmapImage, default);
1917

2018
public void SetContentFromBitmapImage(BitmapImage bitmapImage, Point anchorPoint)
2119
{

src/Uno.UI/UI/Xaml/DragDrop/DragView.cs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,17 @@ public ImageSource? Content
6969
}
7070
#endregion
7171

72+
#region ContentAnchor
73+
public static readonly DependencyProperty ContentAnchorProperty = DependencyProperty.Register(
74+
"ContentAnchor", typeof(Point), typeof(DragView), new FrameworkPropertyMetadata(default(Point)));
75+
76+
public Point ContentAnchor
77+
{
78+
get => (Point)GetValue(ContentAnchorProperty);
79+
private set => SetValue(ContentAnchorProperty, value);
80+
}
81+
#endregion
82+
7283
#region ContentVisibility
7384
public static readonly DependencyProperty ContentVisibilityProperty = DependencyProperty.Register(
7485
"ContentVisibility", typeof(Visibility), typeof(DragView), new FrameworkPropertyMetadata(default(Visibility)));
@@ -110,8 +121,8 @@ public void SetLocation(Point location)
110121
// TODO: Make sure to not move the element out of the bounds of the window
111122
_location = location;
112123

113-
_transform.X = location.X - (ActualWidth / 2);
114-
_transform.Y = location.Y - 40; // The caption is above the pointer
124+
_transform.X = location.X;
125+
_transform.Y = location.Y;
115126
}
116127

117128
public void Update(DataPackageOperation acceptedOperation, CoreDragUIOverride viewOverride)
@@ -132,7 +143,16 @@ public void Update(DataPackageOperation acceptedOperation, CoreDragUIOverride vi
132143
GlyphVisibility = ToVisibility(viewOverride.IsGlyphVisible);
133144
Caption = caption!;
134145
CaptionVisibility = ToVisibility(viewOverride.IsCaptionVisible && !string.IsNullOrWhiteSpace(caption));
135-
Content = viewOverride.Content as ImageSource ?? _ui?.Content;
146+
if (viewOverride.Content is ImageSource overridenContent)
147+
{
148+
Content = overridenContent;
149+
ContentAnchor = viewOverride.ContentAnchor;
150+
}
151+
else
152+
{
153+
Content = _ui?.Content;
154+
ContentAnchor = _ui?.Anchor ?? default;
155+
}
136156
ContentVisibility = ToVisibility(viewOverride.IsContentVisible);
137157
TooltipVisibility = ToVisibility(viewOverride.IsGlyphVisible || viewOverride.IsCaptionVisible);
138158
Visibility = Visibility.Visible;

src/Uno.UI/UI/Xaml/DragDrop/DragView.xaml

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
xmlns:uBehaviors="using:Uno.UI.Behaviors"
1515
mc:Ignorable="d ios android wasm netstdref macos skia">
1616

17-
<!-- Default style for Windows.UI.Xaml.Controls.ScrollViewer -->
17+
<!-- Default style for internal control Windows.UI.Xaml.DragView -->
1818
<Style TargetType="DragView">
1919
<Setter Property="IsTabStop" Value="False" />
2020
<Setter Property="IsHitTestVisible" Value="False" />
@@ -26,14 +26,24 @@
2626
<Setter.Value>
2727
<ControlTemplate TargetType="DragView">
2828
<Grid HorizontalAlignment="Left" VerticalAlignment="Top">
29-
<Image Visibility="{TemplateBinding ContentVisibility}" Source="{TemplateBinding Content}" Opacity=".8" />
29+
<Image Visibility="{TemplateBinding ContentVisibility}" Source="{TemplateBinding Content}" Opacity=".8">
30+
<Image.RenderTransform>
31+
<TranslateTransform X="{TemplateBinding ContentAnchor.X}" Y="{TemplateBinding ContentAnchor.Y}" />
32+
</Image.RenderTransform>
33+
</Image>
3034
<StackPanel
35+
HorizontalAlignment="Left"
36+
VerticalAlignment="Top"
3137
Orientation="Horizontal"
3238
BorderThickness="1"
3339
BorderBrush="{StaticResource SystemControlForegroundChromeHighBrush}"
3440
Background="{StaticResource SystemControlBackgroundChromeMediumLowBrush}"
3541
Padding="2,5"
3642
Visibility="{TemplateBinding TooltipVisibility}">
43+
<StackPanel.RenderTransform>
44+
<!-- The caption is above the pointer -->
45+
<TranslateTransform Y="-40" />
46+
</StackPanel.RenderTransform>
3747
<TextBlock Visibility="{TemplateBinding GlyphVisiblity}" Text="{TemplateBinding Glyph}" Margin="3,0" />
3848
<TextBlock Visibility="{TemplateBinding CaptionVisiblity}" Text="{TemplateBinding Caption}" Margin="3,0" />
3949
</StackPanel>

src/Uno.UI/UI/Xaml/Media/Imaging/RenderTargetBitmap.Android.cs

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
using System;
1+
#nullable enable
2+
3+
using System;
24
using System.IO;
35
using System.Linq;
6+
using System.Numerics;
47
using Android.Graphics;
58
using Uno.UI;
69
using Windows.Foundation;
@@ -13,29 +16,31 @@ partial class RenderTargetBitmap
1316
private protected override bool IsSourceReady => _buffer != null;
1417

1518
/// <inheritdoc />
16-
private protected override bool TryOpenSourceSync(int? targetWidth, int? targetHeight, out Bitmap image)
19+
private protected override bool TryOpenSourceSync(int? targetWidth, int? targetHeight, out Bitmap? image)
1720
{
1821
image = BitmapFactory.DecodeByteArray(_buffer, 0, _buffer.Length);
1922
return image != null;
2023
}
2124

2225
private static byte[] RenderAsPng(UIElement element, Size? scaledSize = null)
2326
{
24-
var width = (int)ViewHelper.LogicalToPhysicalPixels(element.ActualSize.X);
25-
var height = (int)ViewHelper.LogicalToPhysicalPixels(element.ActualSize.Y);
26-
var bitmap = Bitmap.CreateBitmap(width, height, Bitmap.Config.Argb8888);
27+
var logical = element.ActualSize.ToSize();
28+
var physical = logical.LogicalToPhysicalPixels();
29+
var bitmap = Bitmap.CreateBitmap((int)physical.Width, (int)physical.Height, Bitmap.Config.Argb8888!)
30+
?? throw new InvalidOperationException("Failed to create target native bitmap.");
2731
var canvas = new Canvas(bitmap);
2832

2933
// Make sure the element has been Layouted
30-
element.Layout(0, 0, width, height);
34+
element.Layout(0, 0, (int)physical.Width, (int)physical.Height);
3135

3236
// Render on the canvas
33-
canvas.DrawColor(Colors.White);
37+
canvas.DrawColor(Colors.Transparent);
3438
element.Draw(canvas);
3539

36-
if (scaledSize.HasValue)
40+
if (scaledSize is {} targetSize)
3741
{
38-
canvas.Scale((float)(scaledSize.Value.Width / (float)width), (float)(scaledSize.Value.Height / (float)height));
42+
bitmap = Bitmap.CreateScaledBitmap(bitmap, (int)targetSize.Width, (int)targetSize.Height, false)
43+
?? throw new InvalidOperationException("Failed to scaled native bitmap to the requested size.");
3944
}
4045

4146
using var stream = new MemoryStream();

src/Uno.UI/UI/Xaml/Media/Imaging/RenderTargetBitmap.iOS.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ private byte[] RenderAsPng(UIElement element, Size? scaledSize = null)
2525
UIImage img;
2626
try
2727
{
28-
UIGraphics.BeginImageContextWithOptions(new Size(element.ActualSize.X, element.ActualSize.Y), true, 1f);
28+
UIGraphics.BeginImageContextWithOptions(new Size(element.ActualSize.X, element.ActualSize.Y), false, 1f);
2929
var ctx = UIGraphics.GetCurrentContext();
30-
ctx.SetFillColor(Colors.White);
30+
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!
3131
element.Layer.RenderInContext(ctx);
3232
img = UIGraphics.GetImageFromCurrentImageContext();
3333
}

src/Uno.UI/UI/Xaml/UIElement.Pointers.cs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ internal HitTestability GetHitTestVisibility()
259259
#endif
260260
}
261261

262-
#region GestureRecognizer wire-up
262+
#region GestureRecognizer wire-up
263263

264264
#region Event to RoutedEvent handler adapters
265265
// Note: For the manipulation and gesture event args, the original source has to be the element that raise the event
@@ -391,8 +391,8 @@ private async void HapticFeedbackWhenReadyToDrag(GestureRecognizer sender, Gestu
391391
}
392392
}
393393
#endregion
394-
395-
#region Manipulations (recognizer settings / custom bubbling)
394+
395+
#region Manipulations (recognizer settings / custom bubbling)
396396
partial void AddManipulationHandler(RoutedEvent routedEvent, int handlersCount, object handler, bool handledEventsToo)
397397
{
398398
if (handlersCount == 1)
@@ -449,7 +449,7 @@ partial void PrepareManagedManipulationEventBubbling(RoutedEvent routedEvent, re
449449
}
450450
#endregion
451451

452-
#region Gestures (recognizer settings / custom bubbling / early completion)
452+
#region Gestures (recognizer settings / custom bubbling / early completion)
453453
private bool _isGestureCompleted;
454454

455455
partial void AddGestureHandler(RoutedEvent routedEvent, int handlersCount, object handler, bool handledEventsToo)
@@ -522,7 +522,7 @@ private protected void CompleteGesture()
522522
}
523523
#endregion
524524

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

643+
// We capture the original position of the pointer before going async,
644+
// so we have the closet location of the "down" possible.
645+
var ptPosition = ptArgs.GetCurrentPoint(this).Position;
646+
643647
if (routedArgs.Deferral is { } deferral)
644648
{
645649
await deferral.Completed(ct);
@@ -666,9 +670,14 @@ private async Task<DataPackageOperation> StartDragAsyncCore(PointerPoint pointer
666670

667671
if (RenderTargetBitmap.IsImplemented && routedArgs.DragUI.Content is null)
668672
{
673+
// Note: Bitmap rendered by the RenderTargetBitmap is in physical pixels,
674+
// so we provide the ActualSize to request the image to be scaled back in logical pixels.
675+
669676
var target = new RenderTargetBitmap();
670-
await target.RenderAsync(this);
677+
await target.RenderAsync(this, (int)ActualSize.X, (int)ActualSize.Y);
678+
671679
routedArgs.DragUI.Content = target;
680+
routedArgs.DragUI.Anchor = ptPosition.GetOpposite();
672681
}
673682

674683
var asyncResult = new TaskCompletionSource<DataPackageOperation>();
@@ -756,7 +765,7 @@ partial void PrepareManagedPointerEventBubbling(RoutedEvent routedEvent, ref Rou
756765
}
757766
}
758767

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

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

0 commit comments

Comments
 (0)