forked from dotnet/maui
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add GestureManager * Fix DragGestureHandler * Fix GestureManager * Check nullable enable
- Loading branch information
1 parent
43ea663
commit fa4d8eb
Showing
11 changed files
with
1,485 additions
and
1 deletion.
There are no files selected for viewing
121 changes: 120 additions & 1 deletion
121
src/Controls/src/Core/Platform/GestureManager/GestureManager.Tizen.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,137 @@ | ||
#nullable enable | ||
|
||
using System; | ||
using System.Collections.Specialized; | ||
using System.ComponentModel; | ||
using System.Linq; | ||
|
||
namespace Microsoft.Maui.Controls.Platform | ||
{ | ||
class GestureManager : IDisposable | ||
{ | ||
IViewHandler? _handler; | ||
GestureDetector? _gestureDetector; | ||
bool _disposed = false; | ||
|
||
protected virtual VisualElement? Element => _handler?.VirtualView as VisualElement; | ||
|
||
public GestureManager(IViewHandler handler) | ||
{ | ||
//TODO: Need to impl | ||
_handler = handler; | ||
_gestureDetector = null; | ||
SetupElement(null, Element); | ||
} | ||
|
||
void SetupElement(VisualElement? oldElement, VisualElement? newElement) | ||
{ | ||
if (oldElement != null) | ||
{ | ||
if (oldElement is View ov && | ||
ov.GestureRecognizers is INotifyCollectionChanged incc) | ||
{ | ||
incc.CollectionChanged -= OnGestureRecognizerCollectionChanged; | ||
_gestureDetector?.Clear(); | ||
} | ||
oldElement.PropertyChanged -= OnElementPropertyChanged; | ||
} | ||
|
||
if (newElement != null) | ||
{ | ||
if (newElement is View ov && | ||
ov.GestureRecognizers is INotifyCollectionChanged incc) | ||
{ | ||
incc.CollectionChanged += OnGestureRecognizerCollectionChanged; | ||
if (ov.GestureRecognizers.Count > 0) | ||
{ | ||
_gestureDetector = new GestureDetector(_handler); | ||
_gestureDetector.AddGestures(ov.GestureRecognizers); | ||
} | ||
} | ||
newElement.PropertyChanged += OnElementPropertyChanged; | ||
} | ||
|
||
UpdateInputTransparent(); | ||
UpdateIsEnabled(); | ||
} | ||
|
||
void OnElementPropertyChanged(object? sender, PropertyChangedEventArgs e) | ||
{ | ||
if (e.PropertyName == VisualElement.InputTransparentProperty.PropertyName) | ||
UpdateInputTransparent(); | ||
else if (e.PropertyName == VisualElement.IsEnabledProperty.PropertyName) | ||
UpdateIsEnabled(); | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
Dispose(true); | ||
GC.SuppressFinalize(this); | ||
} | ||
|
||
public void Dispose(bool disposing) | ||
{ | ||
if (_disposed) | ||
{ | ||
return; | ||
} | ||
|
||
_disposed = true; | ||
|
||
if (disposing) | ||
{ | ||
SetupElement(Element, null); | ||
if (_gestureDetector != null) | ||
{ | ||
_gestureDetector.Dispose(); | ||
_gestureDetector = null; | ||
} | ||
_handler = null; | ||
} | ||
} | ||
|
||
void UpdateInputTransparent() | ||
{ | ||
if (Element != null && _gestureDetector != null) | ||
{ | ||
_gestureDetector.InputTransparent = Element.InputTransparent; | ||
} | ||
} | ||
|
||
void UpdateIsEnabled() | ||
{ | ||
if (Element != null && _gestureDetector != null) | ||
{ | ||
_gestureDetector.IsEnabled = Element.IsEnabled; | ||
} | ||
} | ||
|
||
void OnGestureRecognizerCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) | ||
{ | ||
if (_gestureDetector == null) | ||
{ | ||
_gestureDetector = new GestureDetector(_handler); | ||
} | ||
|
||
// Gestures will be registered/unregistered according to changes in the GestureRecognizers list | ||
switch (e.Action) | ||
{ | ||
case NotifyCollectionChangedAction.Add: | ||
_gestureDetector.AddGestures(e.NewItems?.OfType<IGestureRecognizer>()); | ||
break; | ||
|
||
case NotifyCollectionChangedAction.Replace: | ||
_gestureDetector.RemoveGestures(e.OldItems?.OfType<IGestureRecognizer>()); | ||
_gestureDetector.AddGestures(e.NewItems?.OfType<IGestureRecognizer>()); | ||
break; | ||
|
||
case NotifyCollectionChangedAction.Remove: | ||
_gestureDetector.RemoveGestures(e.OldItems?.OfType<IGestureRecognizer>()); | ||
break; | ||
|
||
case NotifyCollectionChangedAction.Reset: | ||
_gestureDetector.Clear(); | ||
break; | ||
} | ||
} | ||
} | ||
} |
243 changes: 243 additions & 0 deletions
243
src/Controls/src/Core/Platform/Tizen/DragGestureHandler.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,243 @@ | ||
using System; | ||
using System.Threading.Tasks; | ||
using ElmSharp; | ||
using Microsoft.Maui.Controls.Internals; | ||
using Tizen.UIExtensions.ElmSharp; | ||
using EGestureType = ElmSharp.GestureLayer.GestureType; | ||
using TImage = Tizen.UIExtensions.ElmSharp.Image; | ||
using TLabel = Tizen.UIExtensions.ElmSharp.Label; | ||
|
||
namespace Microsoft.Maui.Controls.Platform | ||
{ | ||
public class DragGestureHandler : GestureHandler | ||
{ | ||
DragDropExtensions.Interop.DragIconCreateCallback _iconCallback; | ||
DragDropExtensions.Interop.DragStateCallback _dragDoneCallback; | ||
|
||
static bool s_isDragging; | ||
static CustomDragStateData s_currentDragStateData; | ||
|
||
protected virtual IView Element => Handler?.VirtualView as IView; | ||
|
||
public DragGestureHandler(IGestureRecognizer recognizer, IViewHandler handler) : base(recognizer) | ||
{ | ||
_iconCallback = OnIconCallback; | ||
_dragDoneCallback = OnDragDoneCallback; | ||
Handler = handler; | ||
} | ||
|
||
public override EGestureType Type => EGestureType.LongTap; | ||
|
||
public IViewHandler Handler { get; } | ||
|
||
public static CustomDragStateData CurrentStateData | ||
{ | ||
get | ||
{ | ||
return s_currentDragStateData; | ||
} | ||
} | ||
|
||
EvasObject NativeView | ||
{ | ||
get | ||
{ | ||
return Handler.NativeView as EvasObject; | ||
} | ||
} | ||
|
||
public void ResetCurrentStateData() | ||
{ | ||
s_currentDragStateData = null; | ||
} | ||
|
||
protected override void OnStarted(View sender, object data) | ||
{ | ||
} | ||
|
||
protected override void OnMoved(View sender, object data) | ||
{ | ||
//Workaround to prevent an error occuring by multiple StartDrag calling in Tizen 6.5 | ||
if (!s_isDragging) | ||
{ | ||
ResetCurrentStateData(); | ||
StartDrag(); | ||
} | ||
} | ||
|
||
protected override void OnCompleted(View sender, object data) | ||
{ | ||
} | ||
|
||
protected override void OnCanceled(View sender, object data) | ||
{ | ||
} | ||
|
||
void StartDrag() | ||
{ | ||
if (Recognizer is DragGestureRecognizer dragGestureRecognizer && dragGestureRecognizer.CanDrag) | ||
{ | ||
if (Handler == null) | ||
return; | ||
|
||
var arg = dragGestureRecognizer.SendDragStarting(Element); | ||
|
||
if (arg.Cancel) | ||
return; | ||
|
||
s_currentDragStateData = new CustomDragStateData(); | ||
s_currentDragStateData.DataPackage = arg.Data; | ||
|
||
var target = DragDropExtensions.DragDropContentType.Text; | ||
var strData = string.IsNullOrEmpty(arg.Data.Text) ? " " : arg.Data.Text; | ||
|
||
s_isDragging = true; | ||
|
||
DragDropExtensions.StartDrag(NativeView, | ||
target, | ||
strData, | ||
DragDropExtensions.DragDropActionType.Move, | ||
_iconCallback, | ||
null, | ||
null, | ||
_dragDoneCallback); | ||
} | ||
} | ||
|
||
IntPtr OnIconCallback(IntPtr data, IntPtr window, ref int xoff, ref int yoff) | ||
{ | ||
EvasObject icon = null; | ||
EvasObject parent = new CustomWindow(NativeView, window); | ||
|
||
if (s_currentDragStateData.DataPackage.Image != null) | ||
{ | ||
icon = GetImageIconAsync(parent).Result; | ||
} | ||
else if (NativeView is ShapeView) | ||
{ | ||
icon = GetShapeView(parent); | ||
} | ||
else | ||
{ | ||
icon = GetDefaultIcon(parent); | ||
} | ||
var bound = NativeView.Geometry; | ||
bound.X = 0; | ||
bound.Y = 0; | ||
icon.Geometry = bound; | ||
|
||
if (icon is TLabel) | ||
{ | ||
icon.Resized += (s, e) => | ||
{ | ||
var map = new EvasMap(4); | ||
map.PopulatePoints(icon.Geometry, 0); | ||
map.Zoom(0.5, 0.5, 0, 0); | ||
icon.IsMapEnabled = true; | ||
icon.EvasMap = map; | ||
}; | ||
} | ||
else | ||
{ | ||
var map = new EvasMap(4); | ||
map.PopulatePoints(icon.Geometry, 0); | ||
map.Zoom(0.5, 0.5, 0, 0); | ||
icon.IsMapEnabled = true; | ||
icon.EvasMap = map; | ||
} | ||
|
||
|
||
return icon; | ||
} | ||
|
||
EvasObject GetDefaultIcon(EvasObject parent) | ||
{ | ||
if (!string.IsNullOrEmpty(s_currentDragStateData.DataPackage.Text)) | ||
{ | ||
var label = new TLabel(parent); | ||
label.Text = s_currentDragStateData.DataPackage.Text; | ||
|
||
if (Element is IFontElement fe) | ||
label.FontSize = fe.FontSize; | ||
|
||
return label; | ||
} | ||
else | ||
{ | ||
var box = new ElmSharp.Rectangle(parent); | ||
box.Color = new ElmSharp.Color(128, 128, 128, 128); | ||
return box; | ||
} | ||
} | ||
|
||
async Task<EvasObject> GetImageIconAsync(EvasObject parent) | ||
{ | ||
var image = new TImage(parent); | ||
var mImage = s_currentDragStateData.DataPackage.Image; | ||
var services = Handler.MauiContext?.Services; | ||
var provider = services.GetService(typeof(IImageSourceServiceProvider)) as IImageSourceServiceProvider; | ||
var service = provider?.GetImageSourceService(mImage); | ||
var result = await service.LoadImageAsync(mImage, image); | ||
if (result == null) | ||
return null; | ||
return image; | ||
} | ||
|
||
EvasObject GetShapeView(EvasObject parent) | ||
{ | ||
var copiedImg = new EvasImage(parent) | ||
{ | ||
IsFilled = true | ||
}; | ||
|
||
if (NativeView is ShapeView shapeView) | ||
{ | ||
var canvas = shapeView.SKCanvasView; | ||
var realHandle = DragDropExtensions.Interop.elm_object_part_content_get(canvas, "elm.swallow.content"); | ||
|
||
DragDropExtensions.Interop.evas_object_image_size_get(realHandle, out int w, out int h); | ||
DragDropExtensions.Interop.evas_object_image_size_set(copiedImg, w, h); | ||
|
||
var imgData = DragDropExtensions.Interop.evas_object_image_data_get(realHandle, false); | ||
DragDropExtensions.Interop.evas_object_image_data_set(copiedImg, imgData); | ||
} | ||
|
||
return copiedImg; | ||
} | ||
|
||
void OnDragDoneCallback(IntPtr data, IntPtr obj) | ||
{ | ||
s_isDragging = false; | ||
if (Recognizer is DragGestureRecognizer dragGestureRecognizer && dragGestureRecognizer.CanDrag) | ||
{ | ||
dragGestureRecognizer.SendDropCompleted(new DropCompletedEventArgs()); | ||
} | ||
} | ||
|
||
public class CustomWindow : EvasObject | ||
{ | ||
IntPtr _handle; | ||
|
||
public CustomWindow(EvasObject parent, IntPtr handle) : base() | ||
{ | ||
_handle = handle; | ||
Realize(parent); | ||
} | ||
|
||
public CustomWindow(EvasObject handle) : base(handle) | ||
{ | ||
} | ||
|
||
protected override IntPtr CreateHandle(EvasObject parent) | ||
{ | ||
return _handle; | ||
} | ||
} | ||
|
||
public class CustomDragStateData | ||
{ | ||
public DataPackage DataPackage { get; set; } | ||
public DataPackageOperation AcceptedOperation { get; set; } = DataPackageOperation.Copy; | ||
} | ||
} | ||
} |
Oops, something went wrong.