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/rich_text/render_rich_text.h"
17 
18 #include <iomanip>
19 #include <sstream>
20 
21 #include "base/log/log.h"
22 #include "core/event/ace_event_helper.h"
23 
24 namespace OHOS::Ace {
25 
RenderRichText()26 RenderRichText::RenderRichText() : RenderNode(true)
27 {
28     LOGI("[richtext] recognizer init");
29     touchRecognizer_ = AceType::MakeRefPtr<RawRecognizer>();
30     touchRecognizer_->SetOnTouchDown([wp = AceType::WeakClaim(this)](const TouchEventInfo& info) {
31         auto sp = wp.Upgrade();
32         if (sp) {
33             sp->prevPos_ = info.GetTouches().front().GetLocalLocation().GetY();
34         }
35     });
36     touchRecognizer_->SetOnTouchUp([wp = AceType::WeakClaim(this)](const TouchEventInfo& info) {
37         // do nothing
38     });
39     touchRecognizer_->SetOnTouchMove([wp = AceType::WeakClaim(this)](const TouchEventInfo& info) {
40         auto sp = wp.Upgrade();
41         if (!sp) {
42             LOGE("[richtext] process move, could not get render node");
43             return;
44         }
45 
46         sp->ProcessMove(info.GetTouches().front().GetLocalLocation().GetY());
47     });
48 
49     // register this listener for consuming the drag events.
50     dragRecognizer_ = AceType::MakeRefPtr<VerticalDragRecognizer>();
51     dragRecognizer_->SetOnDragStart([](const DragStartInfo& info) {});
52     dragRecognizer_->SetOnDragEnd([](const DragEndInfo& info) {});
53 }
54 
ProcessMove(double posY)55 void RenderRichText::ProcessMove(double posY)
56 {
57     auto diff = prevPos_ - posY;
58     double scale = 1.0f;
59     auto context = GetContext().Upgrade();
60     if (context) {
61         scale = context->GetViewScale();
62     }
63     prevPos_ = posY;
64 
65     // downside or upside
66     if (diff > 0) {
67         if (!startSelfScroll_ && currentScrollLength_ == couldScrollLength_) {
68             // do nothing
69         } else {
70             if (currentScrollLength_ > couldScrollLength_) {
71                 startSelfScroll_ = false;
72                 currentScrollLength_ = couldScrollLength_;
73             } else {
74                 startSelfScroll_ = true;
75                 currentScrollLength_ += std::round(scale * diff);
76             }
77         }
78     } else {
79         if (!startSelfScroll_ && currentScrollLength_ == 0) {
80             // do nothing
81         } else {
82             if (currentScrollLength_ < 0) {
83                 startSelfScroll_ = false;
84                 currentScrollLength_ = 0;
85             } else {
86                 startSelfScroll_ = true;
87                 currentScrollLength_ += std::round(scale * diff);
88             }
89         }
90     }
91 
92     // let window scroll
93     if (startSelfScroll_ && delegate_) {
94         delegate_->UpdateContentScroll(0, currentScrollLength_);
95     }
96 }
97 
OnTouchTestHit(const Offset & coordinateOffset,const TouchRestrict & touchRestrict,TouchTestResult & result)98 void RenderRichText::OnTouchTestHit(
99     const Offset& coordinateOffset, const TouchRestrict& touchRestrict, TouchTestResult& result)
100 {
101     if (canSelfScroll_) {
102         // control self scroll, true only self scroll, false outside or parent render node scroll
103         if (startSelfScroll_) {
104             if (dragRecognizer_) {
105                 dragRecognizer_->SetCoordinateOffset(coordinateOffset);
106                 result.emplace_back(dragRecognizer_);
107             }
108         }
109 
110         if (touchRecognizer_) {
111             touchRecognizer_->SetCoordinateOffset(coordinateOffset);
112             result.emplace_back(touchRecognizer_);
113         }
114     }
115 }
116 
Update(const RefPtr<Component> & component)117 void RenderRichText::Update(const RefPtr<Component>& component)
118 {
119     if (!component || !delegate_) {
120         return;
121     }
122 
123     const RefPtr<RichTextComponent> richText = AceType::DynamicCast<RichTextComponent>(component);
124     if (!richText) {
125         LOGE("RenderRichText update dynamicCast to nullptr error");
126         return;
127     }
128 
129     auto& declaration = richText->GetDeclaration();
130     if (declaration && declaration->HasDisplayStyle()) {
131         bool visible = true;
132         auto& style = static_cast<CommonVisibilityStyle&>(declaration->GetStyle(StyleTag::COMMON_VISIBILITY_STYLE));
133         if (style.IsValid()) {
134             visible = style.visibility == VisibilityType::VISIBLE ? true : false;
135         }
136 
137         if (isVisible_ != visible) {
138             isVisible_ = visible;
139             if (hasCreateWeb_) {
140                 delegate_->ChangeRichTextVisibility(isVisible_);
141             }
142         }
143     }
144     if (!hasCreateWeb_) {
145         CreateRealWeb(0, 0, isVisible_);
146     }
147 
148     drawSize_.SetWidth(webContentWidth_);
149     drawSize_.SetHeight(webContentHeight_);
150 
151     MarkNeedLayout(true, true);
152 }
153 
CreateRealWeb(int32_t top,int32_t left,bool visible,bool reCreate)154 void RenderRichText::CreateRealWeb(int32_t top, int32_t left, bool visible, bool reCreate)
155 {
156     if (reCreate) {
157         delegate_->ReleasePlatformResource();
158         delegate_->CreatePlatformResource(context_, top, left, visible);
159         return;
160     }
161 
162     hasCreateWeb_ = true;
163     delegate_->CreatePlatformResource(context_, top, left, visible);
164 }
165 
UpdateLayoutParams(const int32_t width,const int32_t height,const int32_t contentHeight)166 void RenderRichText::UpdateLayoutParams(const int32_t width, const int32_t height, const int32_t contentHeight)
167 {
168     float scale = 1.0f;
169     auto pipelineContext = context_.Upgrade();
170     if (pipelineContext) {
171         scale = pipelineContext->GetViewScale();
172     }
173     if (!NearZero(scale)) {
174         webContentWidth_ = width / scale;
175         webContentHeight_ = height / scale;
176     } else {
177         webContentWidth_ = width;
178         webContentHeight_ = height;
179     }
180     drawSize_.SetWidth(webContentWidth_);
181     drawSize_.SetHeight(webContentHeight_);
182 
183     auto diff = contentHeight - height;
184     LOGI("richtext update layout h:%{public}d c-h:%{public}d", height, contentHeight);
185     // content height should more than layout height 2 pixel.
186     if (diff > 2) {
187         canSelfScroll_ = true;
188         couldScrollLength_ = diff;
189     }
190 
191     MarkNeedLayout(false, true);
192 }
193 
OnGlobalPositionChanged()194 void RenderRichText::OnGlobalPositionChanged()
195 {
196     auto pipelineContext = context_.Upgrade();
197     if (!pipelineContext || !delegate_) {
198         LOGE("update rich text position, but context or delegate is null");
199         return;
200     }
201     Offset offset = GetGlobalOffset();
202     double top = offset.GetY() * pipelineContext->GetViewScale();
203     double left = offset.GetX() * pipelineContext->GetViewScale();
204 
205     if (!initPositionSet_) {
206         initPositionSet_ = true;
207         CreateRealWeb((int32_t)top, (int32_t)left, isVisible_, true);
208     } else {
209         delegate_->UpdateWebPostion((int32_t)top, (int32_t)left);
210     }
211 }
212 
PerformLayout()213 void RenderRichText::PerformLayout()
214 {
215     if (!NeedLayout()) {
216         LOGI("Render Rich Text PerformLayout No Need to Layout");
217         return;
218     }
219     drawSize_ = Size(webContentWidth_, webContentHeight_);
220     SetLayoutSize(drawSize_);
221     SetNeedLayout(false);
222     MarkNeedRender();
223 }
224 
MarkNeedRender(bool overlay)225 void RenderRichText::MarkNeedRender(bool overlay)
226 {
227     RenderNode::MarkNeedRender(overlay);
228 }
229 
230 } // namespace OHOS::Ace
231