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 #include "core/components/split_container/render_split_container.h"
17 
18 #include "core/components/display/render_display.h"
19 
20 namespace OHOS::Ace {
21 
22 namespace {
23 
24 constexpr int32_t DISABLE_HIDE = -1;
25 
26 } // namespace
27 
Update(const RefPtr<Component> & component)28 void RenderSplitContainer::Update(const RefPtr<Component>& component)
29 {
30     ResetComponentMember();
31     UpdateComponentAttr(component);
32     InitializeRecognizer();
33     MarkNeedLayout();
34 }
35 
ResetComponentMember()36 void RenderSplitContainer::ResetComponentMember()
37 {
38     dragDetector_.Reset();
39     resizable_ = false;
40     layoutWidth_ = 0.0;
41     layoutHeight_ = 0.0;
42 }
43 
UpdateComponentAttr(const RefPtr<Component> & component)44 void RenderSplitContainer::UpdateComponentAttr(const RefPtr<Component>& component)
45 {
46     auto splitComponent = AceType::DynamicCast<SplitContainerComponent>(component);
47     resizable_ = splitComponent->GetResizable();
48     splitType_ = splitComponent->GetSplitType();
49 }
50 
InitProperties()51 void RenderSplitContainer::InitProperties()
52 {
53     magicNodes_.clear();
54     displayNodes_.clear();
55     disableHideNodes_.clear();
56     for (const auto& child : GetChildren()) {
57         MagicLayoutNode node;
58         node.node = child;
59         auto displayIndexSetted = child->GetDisplayIndexSetted();
60         auto idx = displayIndexSetted ? child->GetDisplayIndex() : DISABLE_HIDE;
61         if (idx == DISABLE_HIDE) {
62             disableHideNodes_.insert(child);
63             continue;
64         }
65 
66         if (magicNodes_.find(idx) != magicNodes_.end()) {
67             magicNodes_[idx].emplace_back(node);
68         } else {
69             std::list<MagicLayoutNode> nodes;
70             nodes.emplace_back(node);
71             magicNodes_[idx] = nodes;
72         }
73     }
74 }
75 
GetMainSize(const RefPtr<RenderNode> & renderNode) const76 double RenderSplitContainer::GetMainSize(const RefPtr<RenderNode>& renderNode) const
77 {
78     auto layoutSize = renderNode->GetLayoutSize();
79     if (splitType_ == SplitType::ROW_SPLIT) {
80         return layoutSize.Width();
81     } else {
82         return layoutSize.Height();
83     }
84 }
85 
GetMainMinSize(const RefPtr<RenderNode> & renderNode) const86 double RenderSplitContainer::GetMainMinSize(const RefPtr<RenderNode>& renderNode) const
87 {
88     Size minSize;
89     auto flexItem = AceType::DynamicCast<RenderFlexItem>(renderNode);
90     if (flexItem) {
91         minSize = flexItem->GetNormalizedConstraints().GetMinSize();
92     }
93 
94     if (splitType_ == SplitType::ROW_SPLIT) {
95         return minSize.Width();
96     } else {
97         return minSize.Height();
98     }
99 }
100 
UpdateDisplayNode()101 void RenderSplitContainer::UpdateDisplayNode()
102 {
103     auto maxSize = GetLayoutParam().GetMaxSize();
104     double allocatedSize = 0.0;
105     double totalMainSize = 0.0;
106     for (const auto& node : disableHideNodes_) {
107         auto layoutSize = node->GetLayoutSize();
108         totalMainSize += splitType_ == SplitType::ROW_SPLIT ? layoutSize.Width() : layoutSize.Height();
109     }
110 
111     auto maxMainSize = splitType_ == SplitType::ROW_SPLIT ? maxSize.Width() : maxSize.Height();
112     maxMainSize -= totalMainSize;
113     for (auto iter = magicNodes_.rbegin(); iter != magicNodes_.rend();) {
114         auto nodeList = (*iter).second;
115         for (const auto& node : nodeList) {
116             auto child = node.node;
117             allocatedSize += GetMainSize(child);
118             allocatedSize += DEFAULT_SPLIT_HEIGHT;
119         }
120 
121         if ((allocatedSize - DEFAULT_SPLIT_HEIGHT) > maxMainSize) {
122             for (const auto& node : nodeList) {
123                 auto child = node.node;
124                 allocatedSize -= GetMainSize(child);
125                 allocatedSize -= DEFAULT_SPLIT_HEIGHT;
126             }
127             break;
128         }
129         for (const auto& node : nodeList) {
130             auto child = node.node;
131             if (child->GetLayoutSize().IsValid()) {
132                 displayNodes_.insert(child);
133             }
134         }
135         if (NearEqual(allocatedSize, maxMainSize)) {
136             break;
137         }
138         iter++;
139     }
140 }
141 
PerformLayout()142 void RenderSplitContainer::PerformLayout()
143 {
144     InitProperties();
145     layoutHeight_ = 0.0;
146     layoutWidth_ = 0.0;
147     for (const auto& item : GetChildren()) {
148         item->Layout(GetLayoutParam());
149     }
150 
151     UpdateDisplayNode();
152     Size maxSize = GetLayoutParam().GetMaxSize();
153     splitRects_.clear();
154     if (dragSplitOffset_.size() == 0) {
155         dragSplitOffset_ = std::vector<double>(GetChildren().size(), 0.0);
156     }
157     layoutHeight_ = splitType_ == SplitType::ROW_SPLIT ? maxSize.Height() : 0.0;
158     layoutWidth_ = splitType_ == SplitType::ROW_SPLIT ? 0.0 : maxSize.Width();
159     LayoutChildren();
160 
161     if (!displayNodes_.empty() || !disableHideNodes_.empty()) {
162         if (splitType_ == SplitType::ROW_SPLIT) {
163             layoutWidth_ -= DEFAULT_SPLIT_HEIGHT;
164         } else {
165             layoutHeight_ -= DEFAULT_SPLIT_HEIGHT;
166         }
167     }
168 
169     double maxHeight = GetLayoutParam().GetMaxSize().Height();
170     double maxWidth = GetLayoutParam().GetMaxSize().Width();
171     layoutHeight_ = LessNotEqual(layoutHeight_, maxHeight) ? layoutHeight_ : maxHeight;
172     layoutWidth_ = LessNotEqual(layoutWidth_, maxWidth) ? layoutWidth_ : maxWidth;
173     SetLayoutSize(Size { layoutWidth_, layoutHeight_ });
174 }
175 
LayoutChildren()176 void RenderSplitContainer::LayoutChildren()
177 {
178     Size maxSize = GetLayoutParam().GetMaxSize();
179     size_t index = 0;
180     double childOffsetMain = 0.0;
181     for (const auto& item : GetChildren()) {
182         auto renderDisplay = FindChildOfClass<RenderDisplay>(item);
183         if (displayNodes_.find(item) == displayNodes_.end() &&
184             disableHideNodes_.find(item) == disableHideNodes_.end()) {
185             if (renderDisplay) {
186                 renderDisplay->UpdateVisibleType(VisibleType::INVISIBLE);
187             }
188 
189             item->Layout(LayoutParam(Size(), Size()));
190             index++;
191             continue;
192         }
193 
194         if (renderDisplay) {
195             renderDisplay->UpdateVisibleType(VisibleType::VISIBLE);
196         }
197         Offset offset;
198         if (splitType_ == SplitType::ROW_SPLIT) {
199             offset = Offset(childOffsetMain, 0);
200             layoutWidth_ += GetMainSize(item);
201             layoutWidth_ += DEFAULT_SPLIT_HEIGHT;
202         } else {
203             offset = Offset(0, childOffsetMain);
204             layoutHeight_ += GetMainSize(item);
205             layoutHeight_ += DEFAULT_SPLIT_HEIGHT;
206         }
207         item->SetPosition(offset);
208         childOffsetMain += GetMainSize(item);
209         if (dragSplitOffset_[index] > 0.0) {
210             childOffsetMain += dragSplitOffset_[index];
211         }
212         double posMain = childOffsetMain;
213         if (splitType_ == SplitType::ROW_SPLIT) {
214             splitRects_.push_back(Rect(posMain, 0, DEFAULT_SPLIT_HEIGHT, maxSize.Height()));
215         } else {
216             splitRects_.push_back(Rect(0, posMain, maxSize.Width(), DEFAULT_SPLIT_HEIGHT));
217         }
218 
219         childOffsetMain += DEFAULT_SPLIT_HEIGHT;
220 
221         index++;
222     }
223     if (splitType_ == SplitType::ROW_SPLIT) {
224         layoutWidth_ = childOffsetMain > maxSize.Width() ? maxSize.Width() : childOffsetMain;
225     } else {
226         layoutHeight_ = childOffsetMain > maxSize.Height() ? maxSize.Height() : childOffsetMain;
227     }
228 }
229 
InitializeRecognizer()230 void RenderSplitContainer::InitializeRecognizer()
231 {
232     if (!resizable_) {
233         return;
234     }
235     if (!dragDetector_) {
236         dragDetector_ = AceType::MakeRefPtr<VerticalDragRecognizer>();
237         dragDetector_->SetOnDragStart([weak = WeakClaim(this)](const DragStartInfo& startInfo) {
238             auto splitContainer = weak.Upgrade();
239             if (splitContainer) {
240                 splitContainer->HandleDragStart(startInfo.GetLocalLocation());
241             }
242         });
243         dragDetector_->SetOnDragUpdate([weakDrag = AceType::WeakClaim(this)](const DragUpdateInfo& info) {
244             auto splitContainer = weakDrag.Upgrade();
245             if (splitContainer) {
246                 splitContainer->HandleDragUpdate(info.GetLocalLocation());
247             }
248         });
249         dragDetector_->SetOnDragEnd([weakDrag = AceType::WeakClaim(this)](const DragEndInfo& info) {
250             auto splitContainer = weakDrag.Upgrade();
251             if (splitContainer) {
252                 splitContainer->HandleDragEnd(info.GetLocalLocation(), info.GetMainVelocity());
253             }
254         });
255     }
256 }
257 
OnTouchTestHit(const Offset & coordinateOffset,const TouchRestrict & touchRestrict,TouchTestResult & result)258 void RenderSplitContainer::OnTouchTestHit(
259     const Offset& coordinateOffset, const TouchRestrict& touchRestrict, TouchTestResult& result)
260 {
261     if (dragDetector_) {
262         dragDetector_->SetCoordinateOffset(coordinateOffset);
263         result.emplace_back(dragDetector_);
264     }
265 }
266 
HandleMouseEvent(const MouseEvent & event)267 bool RenderSplitContainer::HandleMouseEvent(const MouseEvent& event)
268 {
269     if (isDragedMoving_) {
270         return false;
271     }
272     MouseInfo info;
273     info.SetButton(event.button);
274     info.SetAction(event.action);
275     info.SetGlobalLocation(event.GetOffset());
276     info.SetLocalLocation(event.GetOffset() - Offset(GetCoordinatePoint().GetX(), GetCoordinatePoint().GetY()));
277     info.SetScreenLocation(event.GetScreenOffset());
278     info.SetTimeStamp(event.time);
279     if (splitType_ == SplitType::ROW_SPLIT) {
280         for (int32_t i = 0; i < static_cast<int32_t>(splitRects_.size()) - 1; i++) {
281             auto lowBound = splitRects_[i].GetOffset().GetX();
282             auto upBound = splitRects_[i].GetOffset().GetX() + DEFAULT_SPLIT_HEIGHT;
283             if (info.GetLocalLocation().GetX() >= lowBound && info.GetLocalLocation().GetX() <= upBound) {
284                 isDraged_ = true;
285                 break;
286             } else {
287                 isDraged_ = false;
288             }
289         }
290         if (preIsDraged_ != isDraged_) {
291             preIsDraged_ = isDraged_;
292             auto mouseStyle = MouseStyle::CreateMouseStyle();
293             auto context = GetContext().Upgrade();
294             auto windowId = context->GetWindowId();
295             if (isDraged_) {
296                 MouseFormat leftRightStyle = MouseFormat::WEST_EAST;
297                 mouseStyle->SetPointerStyle(windowId, leftRightStyle);
298             } else {
299                 MouseFormat defaultStyle = MouseFormat::DEFAULT;
300                 mouseStyle->SetPointerStyle(windowId, defaultStyle);
301             }
302         }
303     } else {
304         for (int32_t i = 0; i < static_cast<int32_t>(splitRects_.size()) - 1; i++) {
305             auto lowBound = splitRects_[i].GetOffset().GetY();
306             auto upBound = splitRects_[i].GetOffset().GetY() + DEFAULT_SPLIT_HEIGHT;
307             if (info.GetLocalLocation().GetY() >= lowBound && info.GetLocalLocation().GetY() <= upBound) {
308                 isDraged_ = true;
309                 break;
310             } else {
311                 isDraged_ = false;
312             }
313         }
314         if (preIsDraged_ != isDraged_) {
315             preIsDraged_ = isDraged_;
316             auto mouseStyle = MouseStyle::CreateMouseStyle();
317             auto context = GetContext().Upgrade();
318             auto windowId = context->GetWindowId();
319             if (isDraged_) {
320                 MouseFormat upDownStyle = MouseFormat::NORTH_SOUTH;
321                 mouseStyle->SetPointerStyle(windowId, upDownStyle);
322             } else {
323                 MouseFormat defaultStyle = MouseFormat::DEFAULT;
324                 mouseStyle->SetPointerStyle(windowId, defaultStyle);
325             }
326         }
327     }
328     return true;
329 }
330 
HandleMouseHoverEvent(MouseState mouseState)331 void RenderSplitContainer::HandleMouseHoverEvent(MouseState mouseState) {}
332 
333 } // namespace OHOS::Ace