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/badge/render_badge.h"
17 
18 #include "base/log/event_report.h"
19 #include "core/common/container.h"
20 #include "core/common/font_manager.h"
21 
22 namespace OHOS::Ace {
23 
RenderBadge()24 RenderBadge::RenderBadge()
25 {
26     clickRecognizer_ = AceType::MakeRefPtr<ClickRecognizer>();
27     clickRecognizer_->SetOnClick([wp = AceType::WeakClaim(this)](const ClickInfo&) {
28         auto badge = wp.Upgrade();
29         if (badge) {
30             badge->HandleClickEvent();
31         }
32     });
33 }
34 
~RenderBadge()35 RenderBadge::~RenderBadge()
36 {
37     auto context = context_.Upgrade();
38     if (context) {
39         context->RemoveFontNode(WeakClaim(this));
40         auto fontManager = context->GetFontManager();
41         if (fontManager) {
42             fontManager->RemoveVariationNode(WeakClaim(this));
43         }
44     }
45 }
46 
HandleClickEvent()47 void RenderBadge::HandleClickEvent()
48 {
49     if (onClick_) {
50         onClick_();
51     }
52 }
53 
OnTouchTestHit(const Offset & coordinateOffset,const TouchRestrict & touchRestrict,TouchTestResult & result)54 void RenderBadge::OnTouchTestHit(
55     const Offset& coordinateOffset, const TouchRestrict& touchRestrict, TouchTestResult& result)
56 {
57     if (!clickRecognizer_) {
58         return;
59     }
60     clickRecognizer_->SetCoordinateOffset(coordinateOffset);
61     result.emplace_back(clickRecognizer_);
62 }
63 
Update(const RefPtr<Component> & component)64 void RenderBadge::Update(const RefPtr<Component>& component)
65 {
66     badge_ = AceType::DynamicCast<BadgeComponent>(component);
67     if (!badge_) {
68         LOGE("Update error, badge component is null");
69         EventReport::SendRenderException(RenderExcepType::RENDER_COMPONENT_ERR);
70         return;
71     }
72     showMessage_ = badge_->IsShowMessage();
73     auto padding = badge_->GetPadding();
74     auto badgeLabel = badge_->GetBadgeLabel();
75     auto messageCount = badge_->GetMessageCount();
76     auto maxCount = badge_->GetMaxCount();
77     auto countLimit = maxCount > INT_MAX ? INT_MAX : maxCount;
78     badgeChildInitialOffset_ = Offset(NormalizeToPx(padding.Left()), NormalizeToPx(padding.Top()));
79     onClick_ = AceAsyncEvent<void()>::Create(badge_->GetClickEvent(), context_);
80     auto catchMode = true;
81     if (badge_->GetClickEvent().IsEmpty()) {
82         catchMode = false;
83     } else {
84         catchMode = badge_->GetClickEvent().GetCatchMode();
85     }
86     static const int32_t bubbleModeVersion = 6;
87     auto pipeline = context_.Upgrade();
88     if (!catchMode) {
89         if (pipeline && pipeline->GetMinPlatformVersion() >= bubbleModeVersion) {
90             catchMode = false;
91         } else {
92             catchMode = true;
93         }
94     }
95     clickRecognizer_->SetUseCatchMode(catchMode);
96     textData_.clear();
97     // double check for badge
98     if (showMessage_) {
99         showMessage_ = ParseBadgeStatus(badgeLabel, messageCount, countLimit);
100     }
101     badgeTextComponent_ = AceType::MakeRefPtr<TextComponent>(textData_);
102     if (!badgeRenderText_) {
103         InitialBadgeText();
104     }
105     UpdateBadgeText();
106 }
107 
PerformLayout()108 void RenderBadge::PerformLayout()
109 {
110     if (!badge_) {
111         return;
112     }
113 
114     if (!GetChildren().front()) {
115         SetLayoutSize(Size());
116         // For partial update code path we can get children added
117         // without executing of RenderBadge::Update
118         // In that case showMessage_ remains false and
119         // Badge never shown.
120         if (!Container::IsCurrentUsePartialUpdate()) {
121             showMessage_ = false;
122         }
123         return;
124     }
125 
126     auto context = context_.Upgrade();
127     if (context) {
128         dipScale_ = context->GetDipScale();
129     }
130 
131     // child layout
132     LayoutParam layoutParam = GetLayoutParam();
133     Size minSize = layoutParam.GetMinSize();
134     Size maxSize = layoutParam.GetMaxSize();
135     LayoutParam innerLayoutParam = layoutParam;
136     Size paddingSize = badge_->GetPadding().GetLayoutSizeInPx(dipScale_);
137     innerLayoutParam.SetMinSize(minSize - paddingSize);
138     innerLayoutParam.SetMaxSize(maxSize - paddingSize);
139     double maxWidth = minSize.Width();
140     double maxHeight = minSize.Height();
141     if (!GetChildren().empty()) {
142         auto child = GetChildren().front();
143         child->Layout(innerLayoutParam);
144         child->SetPosition(badgeChildInitialOffset_);
145         maxWidth = std::max(maxWidth, child->GetLayoutSize().Width() + paddingSize.Width());
146         maxHeight = std::max(maxHeight, child->GetLayoutSize().Height() + paddingSize.Height());
147     }
148 
149     // calculate self layout size
150     if (maxSize.IsInfinite()) {
151         // same with child size
152         badgeSize_ = Size(maxWidth, maxHeight);
153     } else {
154         badgeSize_ = maxSize;
155     }
156     if (!badgeSize_.IsValid()) {
157         badgeSize_ = Size();
158         showMessage_ = false;
159     }
160     SetLayoutSize(badgeSize_);
161     width_ = badgeSize_.Width();
162     height_ = badgeSize_.Height();
163 }
164 
InitialBadgeText()165 void RenderBadge::InitialBadgeText()
166 {
167     badgeRenderText_ = AceType::DynamicCast<RenderText>(badgeTextComponent_->CreateRenderNode());
168     LayoutParam innerLayout;
169     innerLayout.SetMaxSize(Size(Size::INFINITE_SIZE, Size::INFINITE_SIZE));
170     badgeRenderText_->Attach(GetContext());
171     badgeRenderText_->Update(badgeTextComponent_);
172     badgeRenderText_->SetLayoutParam(innerLayout);
173 }
174 
ParseBadgeStatus(const std::optional<std::string> & label,int64_t messageCount,int64_t countLimit)175 bool RenderBadge::ParseBadgeStatus(const std::optional<std::string>& label, int64_t messageCount, int64_t countLimit)
176 {
177     if (label.has_value()) {
178         textData_ = label.value();
179         return true;
180     }
181     if (messageCount < 0) {
182         return true;
183     }
184     if (messageCount > 0) {
185         if (messageCount > countLimit) {
186             textData_ = std::to_string(countLimit) + '+';
187         } else {
188             textData_ = std::to_string(messageCount);
189         }
190         return true;
191     }
192     return false;
193 }
194 
UpdateBadgeText()195 void RenderBadge::UpdateBadgeText()
196 {
197     auto context = context_.Upgrade();
198     if (context) {
199         auto fontManager = context->GetFontManager();
200         if (fontManager) {
201             fontManager->AddVariationNode(WeakClaim(this));
202         }
203     }
204     if (badge_) {
205         textStyle_.SetTextColor(badge_->GetBadgeTextColor());
206         textStyle_.SetFontSize(badge_->GetBadgeFontSize());
207         textStyle_.SetAllowScale(false);
208         badgeTextComponent_->SetData(textData_);
209         badgeTextComponent_->SetTextStyle(textStyle_);
210         badgeRenderText_->Update(badgeTextComponent_);
211     }
212 }
213 
214 } // namespace OHOS::Ace
215