[NUI] Add InterceptWheelEvent
authorjoogab.yun <joogab.yun@samsung.com>
Tue, 4 Jul 2023 07:06:47 +0000 (16:06 +0900)
committerBowon Ryu <wonrst22@naver.com>
Wed, 5 Jul 2023 07:42:04 +0000 (16:42 +0900)
The Wheel event calls the WheelEvent callback by going back from the last child actor to the parent via hitTest.
InterceptWheelEvent checks the wheel event in the parent first.
Returning false from interceptWheelEvent allows child actors to receive WheelEvents.
If it returns true, the actor will receive a WheelEvent.

for example

   View parent = new View();
   View child = new View();
   parent.Add(child);
   child.WheelEvent += childFunctor;
   parent.WheelEvent += parentFunctor;

The callbacks are called in the order childFunctor -> parentFunctor.

If you connect InterceptWheelEvent to parentActor.

   parent.InterceptWheelEvent += interceptFunctor;

When interceptFunctor returns false, it is called in the same order childFunctor -> parentFunctor.
If intereptFunctor returns true, it means that the WheelEvent was intercepted.
So the child actor will not be able to receive wheel events.
Only the parentFunctor is called.

refer :
https://review.tizen.org/gerrit/#/c/platform/core/uifw/dali-core/+/295232/
https://review.tizen.org/gerrit/#/c/platform/core/uifw/dali-csharp-binder/+/295233/

src/Tizen.NUI/src/internal/Interop/Interop.ActorSignal.cs
src/Tizen.NUI/src/public/BaseComponents/ViewEvent.cs
src/Tizen.NUI/src/public/BaseComponents/ViewInternal.cs
src/Tizen.NUI/src/public/Window/WindowEvent.cs
test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/ScrollableFocus/ScrollableFocusSample.cs

index 69192dc2d1d19049a89f1e8b26487ac7da6708ea..97e527c1bd914dca00bd3c91950e5b98bb52e258 100755 (executable)
@@ -52,6 +52,12 @@ namespace Tizen.NUI
             [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_Actor_WheelEventSignal_Disconnect")]
             public static extern void WheelEventDisconnect(HandleRef actor, HandleRef handler);
 
+            [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_Actor_InterceptWheelSignal_Connect")]
+            public static extern void InterceptWheelConnect(HandleRef actor, HandleRef handler);
+
+            [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_Actor_InterceptWheelSignal_Disconnect")]
+            public static extern void InterceptWheelDisconnect(HandleRef actor, HandleRef handler);
+
             [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_Actor_OnSceneSignal_Connect")]
             public static extern void OnSceneConnect(HandleRef actor, HandleRef handler);
 
index e41cb78f2c149a083c55794fc87f6782defb8d48..eab74a4372bf879112497b58a53b36409327e6cc 100755 (executable)
@@ -29,6 +29,8 @@ namespace Tizen.NUI.BaseComponents
     {
         private EventHandler offWindowEventHandler;
         private OffWindowEventCallbackType offWindowEventCallback;
+        private EventHandlerWithReturnType<object, WheelEventArgs, bool> interceptWheelHandler;
+        private WheelEventCallbackType interceptWheelCallback;
         private EventHandlerWithReturnType<object, WheelEventArgs, bool> wheelEventHandler;
         private WheelEventCallbackType wheelEventCallback;
         private EventHandlerWithReturnType<object, KeyEventArgs, bool> keyEventHandler;
@@ -325,6 +327,50 @@ namespace Tizen.NUI.BaseComponents
             }
         }
 
+        /// <summary>
+        /// An event for the wheel which can be used to subscribe or unsubscribe the event handler provided by the user.<br />
+        /// The wheel event is emitted when the wheel input is received.<br />
+        /// This can receive wheel events before child. <br />
+        /// If it returns false, the child can receive the wheel event. If it returns true, the wheel event is intercepted. So child cannot receive wheel event.<br />
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public event EventHandlerWithReturnType<object, WheelEventArgs, bool> InterceptWheelEvent
+        {
+            add
+            {
+                if (interceptWheelHandler == null)
+                {
+                    interceptWheelCallback = OnInterceptWheel;
+                    Interop.ActorSignal.InterceptWheelConnect(SwigCPtr, interceptWheelCallback.ToHandleRef(this));
+                    NDalicPINVOKE.ThrowExceptionIfExists();
+                }
+                interceptWheelHandler += value;
+            }
+
+            remove
+            {
+                interceptWheelHandler -= value;
+                if (interceptWheelHandler == null && interceptWheelCallback != null)
+                {
+                    Interop.ActorSignal.InterceptWheelDisconnect(SwigCPtr, interceptWheelCallback.ToHandleRef(this));
+                    NDalicPINVOKE.ThrowExceptionIfExists();
+                    interceptWheelCallback = null;
+                }
+            }
+        }
+
+        /// <summary>
+        /// If child view doesn't want the parent's view to intercept the wheel event, you can set it to true.
+        /// for example :
+        ///    parent.Add(child);
+        ///    parent.InterceptWheelEvent += OnInterceptWheelEvent;
+        ///    View view = child.GetParent() as View;
+        ///    view.DisallowInterceptWheelEvent = true;
+        ///  This prevents the parent from intercepting wheel event.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public bool DisallowInterceptWheelEvent { get; set; }
+
         /// <summary>
         /// An event for the WheelMoved signal which can be used to subscribe or unsubscribe the event handler provided by the user.<br />
         /// The WheelMoved signal is emitted when the wheel event is received.<br />
@@ -861,6 +907,35 @@ namespace Tizen.NUI.BaseComponents
             return consumed;
         }
 
+        // Callback for View InterceptWheel signal
+        private bool OnInterceptWheel(IntPtr view, IntPtr wheelEvent)
+        {
+            if (wheelEvent == global::System.IntPtr.Zero)
+            {
+                NUILog.Error("wheelEvent should not be null!");
+                return true;
+            }
+
+            // DisallowInterceptWheelEvent prevents the parent from intercepting wheel.
+            if (DisallowInterceptWheelEvent)
+            {
+                return false;
+            }
+
+            WheelEventArgs e = new WheelEventArgs();
+
+            e.Wheel = Tizen.NUI.Wheel.GetWheelFromPtr(wheelEvent);
+
+            bool consumed = false;
+
+            if (interceptWheelHandler != null)
+            {
+                consumed = interceptWheelHandler(this, e);
+            }
+
+            return consumed;
+        }
+
         // Callback for View Wheel signal
         private bool OnWheelEvent(IntPtr view, IntPtr wheelEvent)
         {
index f30bd8f18473ab8e54cd3d22488166f2bb4d6da1..dc168bb438d80c91f8d96db81e1d24d0a9f12924 100755 (executable)
@@ -545,9 +545,9 @@ namespace Tizen.NUI.BaseComponents
             {
                 internalTargetSize = new Vector3(0, 0, 0);
             }
-            
+
             Interop.ActorInternal.RetrieveTargetSize(SwigCPtr, internalTargetSize.SwigCPtr);
-            
+
             if (NDalicPINVOKE.SWIGPendingException.Pending)
             {
                 throw NDalicPINVOKE.SWIGPendingException.Retrieve();
@@ -562,7 +562,7 @@ namespace Tizen.NUI.BaseComponents
             {
                 internalCurrentSize = new Size2D(0, 0);
             }
-            
+
             Interop.ActorInternal.RetrieveCurrentPropertyVector2ActualVector3(SwigCPtr, Property.SIZE, internalCurrentSize.SwigCPtr);
 
             if (NDalicPINVOKE.SWIGPendingException.Pending)
@@ -627,7 +627,7 @@ namespace Tizen.NUI.BaseComponents
             {
                 internalCurrentPosition = new Position(0, 0, 0);
             }
-            
+
             Interop.ActorInternal.RetrieveCurrentPropertyVector3(SwigCPtr, Property.POSITION, internalCurrentPosition.SwigCPtr);
 
             if (NDalicPINVOKE.SWIGPendingException.Pending)
@@ -643,7 +643,7 @@ namespace Tizen.NUI.BaseComponents
             {
                 internalCurrentWorldPosition = new Vector3(0, 0, 0);
             }
-            
+
             Interop.ActorInternal.RetrieveCurrentPropertyVector3(SwigCPtr, View.Property.WorldPosition, internalCurrentWorldPosition.SwigCPtr);
 
             if (NDalicPINVOKE.SWIGPendingException.Pending)
@@ -772,7 +772,7 @@ namespace Tizen.NUI.BaseComponents
             {
                 internalCurrentScale = new Vector3(0, 0, 0);
             }
-            
+
             Interop.ActorInternal.RetrieveCurrentPropertyVector3(SwigCPtr, View.Property.SCALE, internalCurrentScale.SwigCPtr);
 
             if (NDalicPINVOKE.SWIGPendingException.Pending)
@@ -789,7 +789,7 @@ namespace Tizen.NUI.BaseComponents
             {
                 internalCurrentWorldScale = new Vector3(0, 0, 0);
             }
-            
+
             Interop.ActorInternal.RetrieveCurrentPropertyVector3(SwigCPtr, View.Property.WorldScale, internalCurrentWorldScale.SwigCPtr);
 
             if (NDalicPINVOKE.SWIGPendingException.Pending)
@@ -866,7 +866,7 @@ namespace Tizen.NUI.BaseComponents
             {
                 internalCurrentColor = new Vector4(0, 0, 0, 0);
             }
-            
+
             Interop.ActorInternal.RetrieveCurrentPropertyVector4(SwigCPtr, Interop.ActorProperty.ColorGet(), internalCurrentColor.SwigCPtr);
 
             if (NDalicPINVOKE.SWIGPendingException.Pending)
@@ -890,7 +890,7 @@ namespace Tizen.NUI.BaseComponents
             {
                 internalCurrentWorldColor = new Vector4(0, 0, 0, 0);
             }
-            
+
             Interop.ActorInternal.RetrieveCurrentPropertyVector4(SwigCPtr, Property.WorldColor, internalCurrentWorldColor.SwigCPtr);
 
             if (NDalicPINVOKE.SWIGPendingException.Pending)
@@ -1448,6 +1448,15 @@ namespace Tizen.NUI.BaseComponents
                 onWindowEventCallback = null;
             }
 
+            if (interceptWheelCallback != null)
+            {
+                NUILog.Debug($"[Dispose] interceptWheelCallback");
+
+                Interop.ActorSignal.InterceptWheelDisconnect(GetBaseHandleCPtrHandleRef, interceptWheelCallback.ToHandleRef(this));
+                NDalicPINVOKE.ThrowExceptionIfExistsDebug();
+                interceptWheelCallback = null;
+            }
+
             if (wheelEventCallback != null)
             {
                 NUILog.Debug($"[Dispose] wheelEventCallback");
index dceaf1942dd55957bd71f3840b2f0578967a4f53..4fe11003e2f0746e764643693e6f3114bd8c0e5f 100755 (executable)
@@ -34,6 +34,7 @@ namespace Tizen.NUI
         private RootLayerTouchDataCallbackType rootLayerTouchDataCallback;
         private RootLayerTouchDataCallbackType rootLayerInterceptTouchDataCallback;
         private WheelEventCallbackType wheelEventCallback;
+        private WheelEventCallbackType interceptWheelCallback;
         private EventCallbackDelegateType1 stageKeyCallbackDelegate;
         private InterceptKeyEventDelegateType stageInterceptKeyCallbackDelegate;
         private EventCallbackDelegateType0 stageEventProcessingFinishedEventCallbackDelegate;
@@ -217,6 +218,37 @@ namespace Tizen.NUI
             }
         }
 
+        /// <summary>
+        /// An event for the wheel event signal which can be used to subscribe or unsubscribe the event handler provided by the user.<br />
+        /// The wheel event signal is emitted when the wheel input is received.<br />
+        /// This can receive wheel events before child. <br />
+        /// If it returns false, the child can receive the wheel event. If it returns true, the wheel event is intercepted. So child cannot receive wheel event.<br />
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public event ReturnTypeEventHandler<object, WheelEventArgs, bool> InterceptWheelEvent
+        {
+            add
+            {
+                if (interceptWheelHandler == null)
+                {
+                    interceptWheelCallback = OnWindowInterceptWheel;
+                    Interop.ActorSignal.InterceptWheelConnect(Layer.getCPtr(GetRootLayer()), interceptWheelCallback.ToHandleRef(this));
+                    NDalicPINVOKE.ThrowExceptionIfExists();
+                }
+                interceptWheelHandler += value;
+            }
+            remove
+            {
+                interceptWheelHandler -= value;
+                if (interceptWheelHandler == null && interceptWheelCallback != null)
+                {
+                    Interop.ActorSignal.InterceptWheelDisconnect(Layer.getCPtr(GetRootLayer()), interceptWheelCallback.ToHandleRef(this));
+                    NDalicPINVOKE.ThrowExceptionIfExists();
+                    interceptWheelCallback = null;
+                }
+            }
+        }
+
         /// <summary>
         /// Emits the event when the key event is received.
         /// </summary>
@@ -543,6 +575,7 @@ namespace Tizen.NUI
         private event EventHandler<TouchEventArgs> rootLayerTouchDataEventHandler;
         private ReturnTypeEventHandler<object, TouchEventArgs, bool> rootLayerInterceptTouchDataEventHandler;
         private event EventHandler<WheelEventArgs> stageWheelHandler;
+        private ReturnTypeEventHandler<object, WheelEventArgs, bool> interceptWheelHandler;
         private event EventHandler<KeyEventArgs> stageKeyHandler;
         private ReturnTypeEventHandler<object, KeyEventArgs, bool> stageInterceptKeyHandler;
         private event EventHandler stageEventProcessingFinishedEventHandler;
@@ -708,6 +741,13 @@ namespace Tizen.NUI
                 DetentEventCallback = null;
             }
 
+            if (interceptWheelCallback != null)
+            {
+                Interop.ActorSignal.InterceptWheelDisconnect(Layer.getCPtr(GetRootLayer()), interceptWheelCallback.ToHandleRef(this));
+                NDalicPINVOKE.ThrowExceptionIfExists();
+                interceptWheelCallback = null;
+            }
+
             if (stageKeyCallbackDelegate != null)
             {
                 using KeyEventSignal signal = new KeyEventSignal(Interop.Window.KeyEventSignal(GetBaseHandleCPtrHandleRef), false);
@@ -896,6 +936,24 @@ namespace Tizen.NUI
             return true;
         }
 
+        private bool OnWindowInterceptWheel(IntPtr view, IntPtr wheelEvent)
+        {
+            if (wheelEvent == global::System.IntPtr.Zero)
+            {
+                NUILog.Error("wheelEvent should not be null!");
+                return true;
+            }
+
+            bool consumed = false;
+            if (interceptWheelHandler != null)
+            {
+                WheelEventArgs e = new WheelEventArgs();
+                e.Wheel = Tizen.NUI.Wheel.GetWheelFromPtr(wheelEvent);
+                consumed = interceptWheelHandler(this, e);
+            }
+            return consumed;
+        }
+
         // Callback for Stage KeyEventsignal
         private void OnStageKey(IntPtr data)
         {
index f0a9aedcdea0d38f2b9c315ff32d166f6e3109c5..5a85cdf4ad279d937021bec1d15f454fcccdede9 100755 (executable)
@@ -9,21 +9,18 @@ namespace Tizen.NUI.Samples
     public class ScrollableFocusSample : IExample
     {
         public View root;
-        public class CustomScrollableBase : ScrollableBase
-        {
-            public override bool OnWheel(Wheel wheel)
-            {
-                base.OnWheel(wheel);
-                Window window = NUIApplication.GetDefaultWindow();
-                window.LazyFeedHover();
-                return true;
-            }
-        }
 
         public void Activate()
         {
             Window window = NUIApplication.GetDefaultWindow();
 
+            // Use LazyFeedHover if you want to trigger the HoverEvent again when a Wheel event is received.
+            window.InterceptWheelEvent += (s, e) =>
+            {
+                window.LazyFeedHover();
+                return false;
+            };
+
             root = new View();
             root.Layout = new AbsoluteLayout();
             root.Size = new Size(500, 800);
@@ -48,7 +45,7 @@ namespace Tizen.NUI.Samples
             };
             root.Add(topbtn);
 
-            var scrollview = new CustomScrollableBase
+            var scrollview = new ScrollableBase
             {
                 ScrollingDirection = ScrollableBase.Direction.Vertical,
                 WidthSpecification = LayoutParamPolicies.MatchParent,
@@ -72,7 +69,7 @@ namespace Tizen.NUI.Samples
             };
             root.Add(middle);
 
-            var myscrollview = new CustomScrollableBase
+            var myscrollview = new ScrollableBase
             {
                 ScrollingDirection = ScrollableBase.Direction.Vertical,
                 WidthSpecification = LayoutParamPolicies.MatchParent,