Add InterceptWheelEvent
[platform/core/uifw/dali-core.git] / dali / internal / event / events / wheel-event-processor.cpp
1 /*
2  * Copyright (c) 2023 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali/internal/event/events/wheel-event-processor.h>
20
21 // INTERNAL INCLUDES
22 #include <dali/public-api/events/wheel-event.h>
23
24 #include <dali/integration-api/debug.h>
25 #include <dali/integration-api/events/wheel-event-integ.h>
26 #include <dali/integration-api/trace.h>
27 #include <dali/internal/event/actors/actor-impl.h>
28 #include <dali/internal/event/common/scene-impl.h>
29 #include <dali/internal/event/events/hit-test-algorithm-impl.h>
30 #include <dali/internal/event/events/wheel-event-impl.h>
31 #include <dali/public-api/math/vector2.h>
32
33 namespace Dali
34 {
35 namespace Internal
36 {
37 namespace
38 {
39 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_PERFORMANCE_MARKER, false);
40 #if defined(DEBUG_ENABLED)
41 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_WHEEL_PROCESSOR");
42 #endif
43
44 Dali::Actor EmitInterceptWheelSignals(Dali::Actor actor, const Dali::WheelEvent& wheelEvent)
45 {
46   Dali::Actor interceptedActor;
47
48   if(actor)
49   {
50     Dali::Actor parent = actor.GetParent();
51     if(parent)
52     {
53       // Recursively deliver events to the actor and its parents for intercept wheel event.
54       interceptedActor = EmitInterceptWheelSignals(parent, wheelEvent);
55     }
56
57     if(!interceptedActor)
58     {
59       bool   intercepted = false;
60       Actor& actorImpl(GetImplementation(actor));
61       if(actorImpl.GetInterceptWheelRequired())
62       {
63         DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_INTERCEPT_WHEEL_EVENT_SIGNAL");
64         intercepted = actorImpl.EmitInterceptWheelEventSignal(wheelEvent);
65         if(intercepted)
66         {
67           interceptedActor = Dali::Actor(&actorImpl);
68         }
69       }
70     }
71   }
72
73   return interceptedActor;
74 }
75
76 /**
77  *  Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
78  */
79 Dali::Actor EmitWheelSignals(Dali::Actor actor, const Dali::WheelEvent& event)
80 {
81   Dali::Actor consumedActor;
82
83   if(actor)
84   {
85     Dali::Actor oldParent(actor.GetParent());
86
87     Actor& actorImpl(GetImplementation(actor));
88
89     bool consumed(false);
90
91     // Only do the conversion and emit the signal if the actor's wheel signal has connections.
92     if(actorImpl.GetWheelEventRequired())
93     {
94       // Emit the signal to the parent
95       DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_WHEEL_EVENT_SIGNAL");
96       consumed = actorImpl.EmitWheelEventSignal(event);
97     }
98
99     if(consumed)
100     {
101       // One of this actor's listeners has consumed the event so set this actor as the consumed actor.
102       consumedActor = Dali::Actor(&actorImpl);
103     }
104     else
105     {
106       // The actor may have been removed/reparented during the signal callbacks.
107       Dali::Actor parent = actor.GetParent();
108
109       if(parent &&
110          (parent == oldParent))
111       {
112         // One of the actor's parents may consumed the event and they should be set as the consumed actor.
113         consumedActor = EmitWheelSignals(parent, event);
114       }
115     }
116   }
117
118   return consumedActor;
119 }
120
121 /**
122  * The function to be used in the hit-test algorithm to check whether the actor is wheelable.
123  */
124 bool IsActorWheelableFunction(Dali::Actor actor, Dali::HitTestAlgorithm::TraverseType type)
125 {
126   bool hittable = false;
127
128   switch(type)
129   {
130     case Dali::HitTestAlgorithm::CHECK_ACTOR:
131     {
132       if((GetImplementation(actor).GetWheelEventRequired() || GetImplementation(actor).GetInterceptWheelRequired()) && // Does the Application or derived actor type require a wheel event?
133          GetImplementation(actor).IsHittable())
134       {
135         hittable = true;
136       }
137       break;
138     }
139     case Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE:
140     {
141       if(actor.GetProperty<bool>(Dali::Actor::Property::VISIBLE)) // Actor is visible, if not visible then none of its children are visible.
142       {
143         hittable = true;
144       }
145       break;
146     }
147     default:
148     {
149       break;
150     }
151   }
152
153   return hittable;
154 };
155
156 } // unnamed namespace
157
158 WheelEventProcessor::WheelEventProcessor(Scene& scene)
159 : mScene(scene)
160 {
161 }
162
163 WheelEventProcessor::~WheelEventProcessor() = default;
164
165 void WheelEventProcessor::ProcessWheelEvent(const Integration::WheelEvent& event)
166 {
167   WheelEventPtr    wheelEvent = WheelEvent::New(static_cast<Dali::WheelEvent::Type>(event.type), event.direction, event.modifiers, event.point, event.delta, event.timeStamp);
168   Dali::WheelEvent wheelEventHandle(wheelEvent.Get());
169
170   DALI_TRACE_SCOPE(gTraceFilter, "DALI_PROCESS_WHEEL_EVENT");
171
172   if(wheelEvent->GetType() == Dali::WheelEvent::MOUSE_WHEEL)
173   {
174     Dali::HitTestAlgorithm::Results hitTestResults;
175     HitTestAlgorithm::HitTest(mScene.GetSize(), mScene.GetRenderTaskList(), mScene.GetLayerList(), event.point, hitTestResults, IsActorWheelableFunction);
176
177     DALI_LOG_INFO(gLogFilter, Debug::General, "  Screen(%.0f, %.0f), HitActor(%p, %s), Local(%.2f, %.2f)\n", event.point.x, event.point.y, (hitTestResults.actor ? reinterpret_cast<void*>(&hitTestResults.actor.GetBaseObject()) : NULL), (hitTestResults.actor ? hitTestResults.actor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : ""), hitTestResults.actorCoordinates.x, hitTestResults.actorCoordinates.y);
178
179     // Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
180     Dali::Actor consumedActor;
181
182     // Emit the intercept wheel event signal
183     Dali::Actor interceptedActor = EmitInterceptWheelSignals(hitTestResults.actor, wheelEventHandle);
184     if(interceptedActor)
185     {
186       consumedActor = EmitWheelSignals(interceptedActor, wheelEventHandle);
187     }
188     else
189     {
190       consumedActor = EmitWheelSignals(hitTestResults.actor, wheelEventHandle);
191     }
192
193     DALI_LOG_INFO(gLogFilter, Debug::Concise, "HitActor:      (%p) %s\n", hitTestResults.actor ? reinterpret_cast<void*>(&hitTestResults.actor.GetBaseObject()) : NULL, hitTestResults.actor ? hitTestResults.actor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : "");
194     DALI_LOG_INFO(gLogFilter, Debug::Concise, "ConsumedActor: (%p) %s\n", consumedActor ? reinterpret_cast<void*>(&consumedActor.GetBaseObject()) : NULL, consumedActor ? consumedActor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : "");
195   }
196   else
197   {
198     // if CUSTOM_WHEEL, emit the wheel event signal from the scene.
199     bool consumed = mScene.EmitWheelEventGeneratedSignal(wheelEventHandle);
200     if(!consumed)
201     {
202       mScene.EmitWheelEventSignal(wheelEventHandle);
203     }
204   }
205 }
206
207 } // namespace Internal
208
209 } // namespace Dali