1 /*
2  * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_TAB_BAR_RENDER_TAB_CONTENT_H
17 #define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_TAB_BAR_RENDER_TAB_CONTENT_H
18 
19 #include <functional>
20 #include <unordered_map>
21 
22 #include "base/memory/ace_type.h"
23 #include "core/animation/animation.h"
24 #include "core/animation/animator.h"
25 #include "core/components/tab_bar/tab_content_component.h"
26 #include "core/gestures/drag_recognizer.h"
27 #include "core/gestures/raw_recognizer.h"
28 #include "core/pipeline/base/render_node.h"
29 
30 namespace OHOS::Ace {
31 
32 class RenderTabContent : public RenderNode {
33     DECLARE_ACE_TYPE(RenderTabContent, RenderNode)
34 
35 public:
36     RenderTabContent();
37     ~RenderTabContent() override = default;
38 
39     static RefPtr<RenderNode> Create();
40     void Update(const RefPtr<Component>& component) override;
41     void PerformLayout() override;
42     bool IsUseOnly() override;
43 
44     using UpdateIndexFunc = std::function<void(int32_t index)>;
RegisterCallback(const UpdateIndexFunc & callback)45     void RegisterCallback(const UpdateIndexFunc& callback)
46     {
47         callback_ = callback;
48     }
49 
RegisterRequireCallback(const UpdateIndexFunc & callback)50     void RegisterRequireCallback(const UpdateIndexFunc& callback)
51     {
52         requireCallback_ = callback;
53     }
54 
55     using UpdateIndicatorFunc = std::function<void(double percent, int32_t newIndex, bool needChange)>;
RegisterIndicatorCallback(const UpdateIndicatorFunc & callback)56     void RegisterIndicatorCallback(const UpdateIndicatorFunc& callback)
57     {
58         indicatorCallback_ = callback;
59     }
60 
61     void ChangeScroll(int32_t index, bool fromController = false);
62 
63     void UpdateDragPosition(int32_t index);
64 
AddChildContent(int32_t index,const RefPtr<RenderNode> & child)65     void AddChildContent(int32_t index, const RefPtr<RenderNode>& child)
66     {
67         auto iter = contentMap_.begin();
68         while (iter != contentMap_.end()) {
69             if (iter->second == child) {
70                 if (iter->first != index) {
71                     // index has changed, clear map.
72                     contentMap_.clear();
73                 }
74                 break;
75             }
76             iter++;
77         }
78         contentMap_[index] = child;
79         requestedIndex_ = index;
80         forceUpdate_ = true;
81     }
82 
RemoveChildContent(int32_t index)83     void RemoveChildContent(int32_t index)
84     {
85         if (contentMap_.find(index) != contentMap_.end()) {
86             contentMap_.clear();
87             forceUpdate_ = true;
88         }
89     }
90 
RemoveChildContent(const RefPtr<RenderNode> & child)91     void RemoveChildContent(const RefPtr<RenderNode>& child)
92     {
93         auto iter = contentMap_.begin();
94         while (iter != contentMap_.end()) {
95             if (iter->second == child) {
96                 iter->second->SetHidden(true);
97                 contentMap_.clear();
98                 forceUpdate_ = true;
99                 break;
100             }
101             iter++;
102         }
103     }
104 
RemoveChildContentUpdateMap(const RefPtr<RenderNode> & child)105     void RemoveChildContentUpdateMap(const RefPtr<RenderNode>& child)
106     {
107         int32_t idx = -1;
108 
109         for (const auto& item : contentMap_) {
110             if (item.second == child) {
111                 item.second->SetHidden(true);
112                 idx = item.first;
113                 break;
114             }
115         }
116 
117         if (idx < 0) {
118             return;
119         }
120 
121         std::unordered_map<int32_t, RefPtr<RenderNode>> newContentMap;
122         for (const auto& item : contentMap_) {
123             if (item.first < idx) {
124                 newContentMap.emplace(item);
125             } else if (item.first > idx) {
126                 newContentMap.emplace(std::pair<int32_t, RefPtr<RenderNode>>(item.first - 1, item.second));
127             }
128         }
129 
130         std::swap(contentMap_, newContentMap);
131     }
132 
UpdateContentCount(int32_t count)133     void UpdateContentCount(int32_t count)
134     {
135         contentCount_ = count;
136     }
137 
138     // Used by declarative element to flush index after performBuild.
139     void FlushIndex();
140 
GetCurrentIndex()141     int32_t GetCurrentIndex() const
142     {
143         return currentIndex_;
144     }
145 
GetChildContents()146     const std::unordered_map<int32_t, RefPtr<RenderNode>>& GetChildContents() const
147     {
148         return contentMap_;
149     }
150 
151     void FireDomChangeEvent(int32_t index) const;
152 
IsScrollable()153     bool IsScrollable() const
154     {
155         return scrollable_;
156     }
157 
GetScrollDuration()158     float GetScrollDuration() const
159     {
160         return scrollDuration_;
161     }
162 
163     std::string ProvideRestoreInfo() override;
164 
165 private:
166     // for handle scroll tabContent
167     void OnTouchTestHit(
168         const Offset& coordinateOffset, const TouchRestrict& touchRestrict, TouchTestResult& result) override;
169     void HandleDragStart();
170     void HandleDragUpdate(double offset);
171     void HandleDragEnd();
172 
173     void Initialize(const WeakPtr<PipelineContext>& context);
174     void FireContentChangeEvent() const;
175     void HandContentIndicatorEvent(int32_t newIndex, bool needChange) const;
176 
177     // used to scroll TabContent and update the position
178     void ScrollContents(int32_t newIndex, bool isLinkBar, bool fromController = false);
179     void UpdateScrollPosition(double dragDelta);
180     void UpdateChildPosition(double offset, int32_t currentIndex, int32_t newIndex, bool needChange);
181     void HandleStopListener(int32_t newIndex, bool needChange, bool FromController = false);
182     void HandleStartListener(int32_t newIndex, bool needChange, bool isLinkBar);
183     void SetHiddenChild();
184 
185     double GetOffset(double offset);
186 
187     static double GetFriction(double percentage);
188 
189     // if tab is vertical, offset changed on Y axis
190     // if tab is horizontal, offset changed on X axis
GetMainAxisOffset(double offset)191     Offset GetMainAxisOffset(double offset) const
192     {
193         return isVertical_ ? Offset(0.0, offset) : Offset(offset, 0.0);
194     }
195 
IsRightToLeft()196     bool IsRightToLeft() const
197     {
198         return GetTextDirection() == TextDirection::RTL && !isVertical_;
199     }
200     // used to get the previous index or the next index
201     int32_t GetPrevIndex() const;
202     int32_t GetNextIndex() const;
203 
204     void ApplyRestoreInfo();
205 
206     // dragRecognizer and the animation
207     RefPtr<DragRecognizer> dragDetector_;
208     RefPtr<Animation<double>> translate_;
209     RefPtr<Animator> animator_;
210 
211     // map for contents
212     std::unordered_map<int32_t, RefPtr<RenderNode>> contentMap_;
213 
214     // onChange event
215     std::function<void(const std::string&)> changeEvent_;
216     std::function<void(uint32_t)> domChangeEvent_;
217     std::function<void(const std::shared_ptr<BaseEventInfo>&)> onChangeEvent_;
218 
219     bool isAnimationAdded_ = false; // whether the animation is added
220     bool scrollable_ = true;        // the default value is true
221     bool isInAnimation_ = false;    // whether it is in animation
222     bool isDragging_ = false;       // whether it is dragging
223     bool isInitialized_ = false;
224     bool isVertical_ = false; // whether the tab is vertical
225     bool forceUpdate_ = false;
226 
227     int32_t contentCount_ = 0;    // the count of content
228     int32_t currentIndex_ = 0;    // the index of current tab
229     int32_t requestedIndex_ = -1; // the next requested index
230 
231     float scrollDuration_ = 0.0f; // the duration of the scroll animation
232     double contentWidth_ = 0.0;   // the width of tab content
233     double contentHeight_ = 0.0;  // the height of tab content
234     double prevOffset_ = 0.0;     // the offset of previous item
235     double nextOffset_ = 0.0;     // the offset of next item
236     double scrollOffset_ = 0.0;   // the offset when the content is scrolling
237 
238     // callbacks when updating the index
239     UpdateIndexFunc callback_;
240     UpdateIndexFunc requireCallback_;
241     UpdateIndicatorFunc indicatorCallback_;
242 
243     RefPtr<TabController> controller_;
244     bool useInitialIndex_ = true;
245 };
246 
247 } // namespace OHOS::Ace
248 
249 #endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_TAB_BAR_RENDER_TAB_CONTENT_H
250