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