• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2023 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_ng/pattern/text/span_node.h"
17 
18 #include <optional>
19 
20 #include "base/utils/utils.h"
21 #include "core/components/common/layout/constants.h"
22 #include "core/components/common/properties/text_style.h"
23 #include "core/components_ng/base/frame_node.h"
24 #include "core/components_ng/pattern/text/text_pattern.h"
25 #include "core/components_ng/pattern/text/text_styles.h"
26 #include "core/components_ng/property/property.h"
27 #include "core/components_ng/render/drawing_prop_convertor.h"
28 #include "core/components_ng/render/paragraph.h"
29 #include "core/pipeline/pipeline_context.h"
30 #include "core/common/font_manager.h"
31 
32 namespace OHOS::Ace::NG {
33 namespace {
GetDeclaration(const std::optional<Color> & color,const std::optional<TextDecoration> & textDecoration)34 std::string GetDeclaration(const std::optional<Color>& color, const std::optional<TextDecoration>& textDecoration)
35 {
36     auto jsonSpanDeclaration = JsonUtil::Create(true);
37     jsonSpanDeclaration->Put(
38         "type", V2::ConvertWrapTextDecorationToStirng(textDecoration.value_or(TextDecoration::NONE)).c_str());
39     jsonSpanDeclaration->Put("color", (color.value_or(Color::BLACK).ColorToString()).c_str());
40     return jsonSpanDeclaration->ToString();
41 }
42 } // namespace
43 
GetFont() const44 std::string SpanItem::GetFont() const
45 {
46     auto jsonValue = JsonUtil::Create(true);
47     jsonValue->Put("style", GetFontStyleInJson(fontStyle->GetItalicFontStyle()).c_str());
48     jsonValue->Put("size", GetFontSizeInJson(fontStyle->GetFontSize()).c_str());
49     jsonValue->Put("weight", GetFontWeightInJson(fontStyle->GetFontWeight()).c_str());
50     jsonValue->Put("family", GetFontFamilyInJson(fontStyle->GetFontFamily()).c_str());
51     return jsonValue->ToString();
52 }
53 
ToJsonValue(std::unique_ptr<JsonValue> & json) const54 void SpanItem::ToJsonValue(std::unique_ptr<JsonValue>& json) const
55 {
56     json->Put("content", content.c_str());
57     if (fontStyle) {
58         json->Put("font", GetFont().c_str());
59         json->Put("fontSize", GetFontSizeInJson(fontStyle->GetFontSize()).c_str());
60         json->Put(
61             "decoration", GetDeclaration(fontStyle->GetTextDecorationColor(), fontStyle->GetTextDecoration()).c_str());
62         json->Put("letterSpacing", fontStyle->GetLetterSpacing().value_or(Dimension()).ToString().c_str());
63         json->Put(
64             "textCase", V2::ConvertWrapTextCaseToStirng(fontStyle->GetTextCase().value_or(TextCase::NORMAL)).c_str());
65         json->Put("fontColor", fontStyle->GetForegroundColor()
66             .value_or(fontStyle->GetTextColor().value_or(Color::BLACK)).ColorToString().c_str());
67         json->Put("fontStyle", GetFontStyleInJson(fontStyle->GetItalicFontStyle()).c_str());
68         json->Put("fontWeight", GetFontWeightInJson(fontStyle->GetFontWeight()).c_str());
69         json->Put("fontFamily", GetFontFamilyInJson(fontStyle->GetFontFamily()).c_str());
70     }
71     if (textLineStyle) {
72         json->Put("lineHeight", textLineStyle->GetLineHeight().value_or(Dimension()).ToString().c_str());
73     }
74 }
75 
GetOrCreateSpanNode(int32_t nodeId)76 RefPtr<SpanNode> SpanNode::GetOrCreateSpanNode(int32_t nodeId)
77 {
78     auto spanNode = ElementRegister::GetInstance()->GetSpecificItemById<SpanNode>(nodeId);
79     if (spanNode) {
80         return spanNode;
81     }
82     spanNode = MakeRefPtr<SpanNode>(nodeId);
83     ElementRegister::GetInstance()->AddUINode(spanNode);
84     return spanNode;
85 }
86 
MountToParagraph()87 void SpanNode::MountToParagraph()
88 {
89     auto parent = GetParent();
90     while (parent) {
91         auto spanNode = DynamicCast<SpanNode>(parent);
92         if (spanNode) {
93             spanNode->AddChildSpanItem(Claim(this));
94             return;
95         }
96         auto textNode = DynamicCast<FrameNode>(parent);
97         if (textNode) {
98             auto textPattern = textNode->GetPattern<TextPattern>();
99             if (textPattern) {
100                 textPattern->AddChildSpanItem(Claim(this));
101                 return;
102             }
103         }
104         parent = parent->GetParent();
105     }
106     LOGE("fail to find Text or Parent Span");
107 }
108 
RequestTextFlushDirty()109 void SpanNode::RequestTextFlushDirty()
110 {
111     auto parent = GetParent();
112     while (parent) {
113         auto textNode = DynamicCast<FrameNode>(parent);
114         if (textNode) {
115             textNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
116             auto textPattern = textNode->GetPattern<TextPattern>();
117             if (textPattern) {
118                 textPattern->OnModifyDone();
119                 return;
120             }
121         }
122         parent = parent->GetParent();
123     }
124     LOGD("fail to find Text or Parent Span");
125 }
126 
UpdateParagraph(const RefPtr<FrameNode> & frameNode,const RefPtr<Paragraph> & builder,double,double,VerticalAlign)127 int32_t SpanItem::UpdateParagraph(const RefPtr<FrameNode>& frameNode,
128     const RefPtr<Paragraph>& builder, double /* width */, double /* height */, VerticalAlign /* verticalAlign */)
129 {
130     CHECK_NULL_RETURN(builder, -1);
131     std::optional<TextStyle> textStyle;
132     if (fontStyle || textLineStyle) {
133         auto pipelineContext = PipelineContext::GetCurrentContext();
134         CHECK_NULL_RETURN(pipelineContext, -1);
135         TextStyle themeTextStyle =
136             CreateTextStyleUsingTheme(fontStyle, textLineStyle, pipelineContext->GetTheme<TextTheme>());
137         if (frameNode) {
138             FontRegisterCallback(frameNode, themeTextStyle);
139         }
140         if (NearZero(themeTextStyle.GetFontSize().Value())) {
141             return -1;
142         }
143         textStyle = themeTextStyle;
144         builder->PushStyle(themeTextStyle);
145     }
146     UpdateTextStyle(builder, textStyle);
147     textStyle_ = textStyle;
148     for (const auto& child : children) {
149         if (child) {
150             child->UpdateParagraph(frameNode, builder);
151         }
152     }
153     if (fontStyle || textLineStyle) {
154         builder->PopStyle();
155     }
156     return -1;
157 }
158 
FontRegisterCallback(const RefPtr<FrameNode> & frameNode,const TextStyle & textStyle)159 void SpanItem::FontRegisterCallback(const RefPtr<FrameNode>& frameNode, const TextStyle& textStyle)
160 {
161     auto callback = [weakNode = WeakPtr<FrameNode>(frameNode)] {
162         auto frameNode = weakNode.Upgrade();
163         CHECK_NULL_VOID(frameNode);
164         frameNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
165         auto pattern = frameNode->GetPattern<TextPattern>();
166         CHECK_NULL_VOID(pattern);
167         auto modifier = DynamicCast<TextContentModifier>(pattern->GetContentModifier());
168         CHECK_NULL_VOID(modifier);
169         modifier->SetFontReady(true);
170     };
171     auto pipeline = frameNode->GetContext();
172     CHECK_NULL_VOID(pipeline);
173     auto fontManager = pipeline->GetFontManager();
174     if (fontManager) {
175         bool isCustomFont = false;
176         for (const auto& familyName : textStyle.GetFontFamilies()) {
177             bool customFont = fontManager->RegisterCallbackNG(frameNode, familyName, callback);
178             if (customFont) {
179                 isCustomFont = true;
180             }
181         }
182         if (isCustomFont) {
183             auto pattern = frameNode->GetPattern<TextPattern>();
184             CHECK_NULL_VOID(pattern);
185             pattern->SetIsCustomFont(true);
186             auto modifier = DynamicCast<TextContentModifier>(pattern->GetContentModifier());
187             CHECK_NULL_VOID(modifier);
188             modifier->SetIsCustomFont(true);
189         }
190     }
191 }
192 
UpdateTextStyle(const RefPtr<Paragraph> & builder,const std::optional<TextStyle> & textStyle)193 void SpanItem::UpdateTextStyle(const RefPtr<Paragraph>& builder, const std::optional<TextStyle>& textStyle)
194 {
195     auto textCase = fontStyle ? fontStyle->GetTextCase().value_or(TextCase::NORMAL) : TextCase::NORMAL;
196     auto updateTextAction = [builder, textCase](const std::string& content, const std::optional<TextStyle>& textStyle) {
197         if (content.empty()) {
198             return;
199         }
200         auto displayText = content;
201         StringUtils::TransformStrCase(displayText, static_cast<int32_t>(textCase));
202         if (textStyle.has_value()) {
203             builder->PushStyle(textStyle.value());
204         }
205         builder->AddText(StringUtils::Str8ToStr16(displayText));
206         if (textStyle.has_value()) {
207             builder->PopStyle();
208         }
209     };
210 #ifdef ENABLE_DRAG_FRAMEWORK
211     if (!IsDragging()) {
212 #endif // ENABLE_DRAG_FRAMEWORK
213         updateTextAction(content, textStyle);
214 #ifdef ENABLE_DRAG_FRAMEWORK
215     } else {
216         if (content.empty()) {
217             return;
218         }
219         auto displayContent = StringUtils::Str8ToStr16(content);
220         auto contentLength = static_cast<int32_t>(displayContent.length());
221         auto beforeSelectedText = displayContent.substr(0, selectedStart);
222         updateTextAction(StringUtils::Str16ToStr8(beforeSelectedText), textStyle);
223 
224         if (selectedStart < contentLength) {
225             auto pipelineContext = PipelineContext::GetCurrentContext();
226             TextStyle normalStyle =
227                 !pipelineContext ? TextStyle()
228                                  : CreateTextStyleUsingTheme(nullptr, nullptr, pipelineContext->GetTheme<TextTheme>());
229             TextStyle selectedTextStyle = textStyle.value_or(normalStyle);
230             Color color = selectedTextStyle.GetTextColor().ChangeAlpha(DRAGGED_TEXT_OPACITY);
231             selectedTextStyle.SetTextColor(color);
232             auto selectedText = displayContent.substr(selectedStart, selectedEnd - selectedStart);
233             updateTextAction(StringUtils::Str16ToStr8(selectedText), selectedTextStyle);
234         }
235 
236         if (selectedEnd < contentLength) {
237             auto afterSelectedText = displayContent.substr(selectedEnd);
238             updateTextAction(StringUtils::Str16ToStr8(afterSelectedText), textStyle);
239         }
240     }
241 #endif // ENABLE_DRAG_FRAMEWORK
242 }
243 
244 #ifdef ENABLE_DRAG_FRAMEWORK
StartDrag(int32_t start,int32_t end)245 void SpanItem::StartDrag(int32_t start, int32_t end)
246 {
247     selectedStart = std::max(0, start);
248     int contentLen = content.size();
249     selectedEnd = std::min(contentLen, end);
250 }
251 
EndDrag()252 void SpanItem::EndDrag()
253 {
254     selectedStart = -1;
255     selectedEnd = -1;
256 }
257 
IsDragging()258 bool SpanItem::IsDragging()
259 {
260     return selectedStart >= 0 && selectedEnd >= 0;
261 }
262 #endif // ENABLE_DRAG_FRAMEWORK
263 
UpdateParagraph(const RefPtr<FrameNode> &,const RefPtr<Paragraph> & builder,double width,double height,VerticalAlign verticalAlign)264 int32_t ImageSpanItem::UpdateParagraph(const RefPtr<FrameNode>& /* frameNode */,
265     const RefPtr<Paragraph>& builder, double width, double height, VerticalAlign verticalAlign)
266 {
267     LOGD("ImageSpanItem::UpdateParagraph imageWidth = %{public}f, imageHeight = %{public}f verticalAlign = "
268          "%{public}d",
269         width, height, verticalAlign);
270     CHECK_NULL_RETURN(builder, -1);
271     PlaceholderRun run;
272     run.width = width;
273     run.height = height;
274     switch (verticalAlign) {
275         case VerticalAlign::TOP:
276             run.alignment = PlaceholderAlignment::TOP;
277             break;
278         case VerticalAlign::CENTER:
279             run.alignment = PlaceholderAlignment::MIDDLE;
280             break;
281         case VerticalAlign::BOTTOM:
282         case VerticalAlign::NONE:
283             run.alignment = PlaceholderAlignment::BOTTOM;
284             break;
285         case VerticalAlign::BASELINE:
286             run.alignment = PlaceholderAlignment::ABOVEBASELINE;
287             break;
288         default:
289             run.alignment = PlaceholderAlignment::BOTTOM;
290     }
291     // ImageSpan should ignore decoration styles
292     textStyle.SetTextDecoration(TextDecoration::NONE);
293     builder->PushStyle(textStyle);
294     LOGD("ImageSpan fontsize = %{public}f", textStyle.GetFontSize().Value());
295     int32_t index = builder->AddPlaceholder(run);
296     builder->PopStyle();
297     return index;
298 }
299 } // namespace OHOS::Ace::NG
300