[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 69192dc..97e527c 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 e41cb78..eab74a4 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;
@@ -326,6 +328,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 />
         /// </summary>
@@ -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 f30bd8f..dc168bb 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 dceaf19..4fe1100 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;
@@ -218,6 +219,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>
         /// <since_tizen> 3 </since_tizen>
@@ -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 f0a9aed..5a85cdf 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,