• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 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/text_base.h"
17 #include "core/components_ng/property/measure_utils.h"
18 #include "core/text/text_emoji_processor.h"
19 #include <cstdint>
20 
21 namespace OHOS::Ace::NG {
22 namespace {
23 const Dimension SELECTED_BLANK_LINE_WIDTH = 2.0_vp;
24 constexpr size_t UTF16_SURROGATE_PAIR_LENGTH = 2;
25 constexpr size_t UTF16_SINGLE_CHAR_LENGTH = 1;
26 }; // namespace
27 
SetSelectionNode(const SelectedByMouseInfo & info)28 void TextBase::SetSelectionNode(const SelectedByMouseInfo& info)
29 {
30     auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
31     CHECK_NULL_VOID(pipeline);
32     auto selectOverlayManager = pipeline->GetSelectOverlayManager();
33     selectOverlayManager->SetSelectedNodeByMouse(info);
34 }
35 
GetGraphemeClusterLength(const std::u16string & text,int32_t extend,bool checkPrev)36 int32_t TextBase::GetGraphemeClusterLength(
37     const std::u16string& text, int32_t extend, bool checkPrev)
38 {
39     char16_t aroundChar = 0;
40     if (checkPrev) {
41         if (static_cast<size_t>(extend) <= text.length()) {
42             aroundChar = text[std::max(0, extend - 1)];
43         }
44     } else {
45         if (static_cast<size_t>(extend) <= (text.length())) {
46             aroundChar = text[std::min(text.length() ? static_cast<int32_t>(text.length()) - 1 : 0, extend)];
47         }
48     }
49     return StringUtils::NotInUtf16Bmp(aroundChar) ? 2 : 1;
50 }
51 
CalculateSelectedRect(std::vector<RectF> & selectedRect,float longestLine,TextDirection direction)52 void TextBase::CalculateSelectedRect(std::vector<RectF>& selectedRect, float longestLine, TextDirection direction)
53 {
54     if (selectedRect.size() <= 1 || direction == TextDirection::RTL) {
55         return;
56     }
57     std::map<float, RectF> lineGroup;
58     for (const auto& localRect : selectedRect) {
59         if (NearZero(localRect.Width()) && NearZero(localRect.Height())) {
60             continue;
61         }
62         auto it = lineGroup.find(localRect.GetY());
63         if (it == lineGroup.end()) {
64             lineGroup.emplace(localRect.GetY(), localRect);
65         } else {
66             auto lineRect = it->second;
67             it->second = lineRect.CombineRectT(localRect);
68         }
69     }
70     selectedRect.clear();
71     auto firstRect = lineGroup.begin()->second;
72     float lastLineBottom = firstRect.Top();
73     auto end = *(lineGroup.rbegin());
74     for (const auto& line : lineGroup) {
75         auto width = (line == end) ? line.second.Width() : longestLine - line.second.Left();
76         auto rect = RectF(line.second.Left(), lastLineBottom, width, line.second.Bottom() - lastLineBottom);
77         selectedRect.emplace_back(rect);
78         lastLineBottom = line.second.Bottom();
79     }
80 }
81 
GetSelectedBlankLineWidth()82 float TextBase::GetSelectedBlankLineWidth()
83 {
84     auto pipeline = PipelineContext::GetCurrentContext();
85     CHECK_NULL_RETURN(pipeline, static_cast<float>(SELECTED_BLANK_LINE_WIDTH.ConvertToPx()));
86     auto blankWidth = pipeline->NormalizeToPx(SELECTED_BLANK_LINE_WIDTH);
87     return static_cast<float>(blankWidth);
88 }
89 
CalculateSelectedRectEx(std::vector<RectF> & selectedRects,float lastLineBottom,const std::optional<TextDirection> & direction)90 void TextBase::CalculateSelectedRectEx(std::vector<RectF>& selectedRects, float lastLineBottom,
91     const std::optional<TextDirection>& direction)
92 {
93     if (selectedRects.empty()) {
94         return;
95     }
96     std::map<float, std::pair<RectF, std::vector<RectF>>> lineGroup;
97     SelectedRectsToLineGroup(selectedRects, lineGroup);
98     selectedRects.clear();
99     if (lineGroup.empty()) {
100         return;
101     }
102     lastLineBottom = LessNotEqual(lastLineBottom, 0.0f) ? lineGroup.begin()->second.first.Top() : lastLineBottom;
103     for (const auto& line : lineGroup) {
104         const auto& lineRect = line.second.first;
105         const auto& lineRects = line.second.second;
106         for (const auto& lineItem : lineRects) {
107             RectF rect = RectF(lineItem.Left(), lastLineBottom, lineItem.Width(), lineRect.Bottom() - lastLineBottom);
108             selectedRects.emplace_back(rect);
109         }
110         lastLineBottom = line.second.first.Bottom();
111     }
112 }
113 
UpdateSelectedBlankLineRect(RectF & rect,float blankWidth,TextAlign textAlign,float longestLine)114 bool TextBase::UpdateSelectedBlankLineRect(RectF& rect, float blankWidth, TextAlign textAlign, float longestLine)
115 {
116     CHECK_EQUAL_RETURN(NearZero(rect.Width()), false, false);
117     switch (textAlign) {
118         case TextAlign::JUSTIFY:
119         case TextAlign::START: {
120             if (GreatNotEqual(longestLine, 0.0f) && GreatNotEqual(rect.Left() + blankWidth, longestLine)) {
121                 rect.SetLeft(longestLine - blankWidth);
122             }
123             break;
124         }
125         case TextAlign::CENTER:
126             rect.SetLeft(rect.Left() - (blankWidth / 2.0f));
127             break;
128         case TextAlign::END: {
129             auto left = rect.Left() - blankWidth;
130             if (GreatOrEqual(left, 0.0f)) {
131                 rect.SetLeft(left);
132             }
133             break;
134         }
135         default:
136             return false;
137     }
138     rect.SetWidth(blankWidth);
139     return true;
140 }
141 
SelectedRectsToLineGroup(const std::vector<RectF> & selectedRect,std::map<float,std::pair<RectF,std::vector<RectF>>> & lineGroup)142 void TextBase::SelectedRectsToLineGroup(const std::vector<RectF>& selectedRect,
143     std::map<float, std::pair<RectF, std::vector<RectF>>>& lineGroup)
144 {
145     for (const auto& localRect : selectedRect) {
146         if (NearZero(localRect.Width()) && NearZero(localRect.Height())) {
147             continue;
148         }
149         auto it = lineGroup.find(localRect.GetY());
150         if (it == lineGroup.end()) {
151             std::vector<RectF> rects = { localRect };
152             lineGroup.emplace(localRect.GetY(), std::make_pair(localRect, rects));
153             continue;
154         }
155         auto lineRect = it->second.first;
156         it->second.first = lineRect.CombineRectT(localRect);
157         if (it->second.second.empty()) {
158             it->second.second.emplace_back(localRect);
159             continue;
160         }
161         auto backRect = it->second.second.back();
162         if (NearEqual(backRect.Left(), localRect.Right()) || NearEqual(backRect.Right(), localRect.Left())) {
163             it->second.second.back() = backRect.CombineRectT(localRect);
164         } else {
165             it->second.second.emplace_back(localRect);
166         }
167     }
168 }
169 
CheckTextAlignByDirection(TextAlign textAlign,TextDirection direction)170 TextAlign TextBase::CheckTextAlignByDirection(TextAlign textAlign, TextDirection direction)
171 {
172     if (direction == TextDirection::RTL) {
173         if (textAlign == TextAlign::START) {
174             return TextAlign::END;
175         } else if (textAlign == TextAlign::END) {
176             return TextAlign::START;
177         }
178     }
179     return textAlign;
180 }
181 
RevertLocalPointWithTransform(const RefPtr<FrameNode> & targetNode,OffsetF & point)182 void TextBase::RevertLocalPointWithTransform(const RefPtr<FrameNode>& targetNode, OffsetF& point)
183 {
184     auto pattern = targetNode->GetPattern<Pattern>();
185     CHECK_NULL_VOID(pattern);
186     auto parent = pattern->GetHost();
187     CHECK_NULL_VOID(parent);
188     std::stack<RefPtr<FrameNode>> nodeStack;
189     while (parent) {
190         nodeStack.push(parent);
191         parent = parent->GetAncestorNodeOfFrame(true);
192     }
193     CHECK_NULL_VOID(!nodeStack.empty());
194     PointF localPoint(point.GetX(), point.GetY());
195     while (!nodeStack.empty()) {
196         parent = nodeStack.top();
197         CHECK_NULL_VOID(parent);
198         nodeStack.pop();
199         auto renderContext = parent->GetRenderContext();
200         CHECK_NULL_VOID(renderContext);
201         renderContext->GetPointWithRevert(localPoint);
202         auto rectOffset = renderContext->GetPaintRectWithoutTransform().GetOffset();
203         localPoint = localPoint - rectOffset;
204     }
205     point.SetX(localPoint.GetX());
206     point.SetY(localPoint.GetY());
207 }
208 
HasRenderTransform(const RefPtr<FrameNode> & targetNode)209 bool TextBase::HasRenderTransform(const RefPtr<FrameNode>& targetNode)
210 {
211     auto pattern = targetNode->GetPattern<Pattern>();
212     CHECK_NULL_RETURN(pattern, false);
213     auto host = pattern->GetHost();
214     CHECK_NULL_RETURN(host, false);
215     auto hasTransform = false;
216     while (host) {
217         auto renderContext = host->GetRenderContext();
218         CHECK_NULL_RETURN(renderContext, false);
219         if (host->GetTag() == V2::WINDOW_SCENE_ETS_TAG) {
220             break;
221         }
222         if (!hasTransform) {
223             auto noTransformRect = renderContext->GetPaintRectWithoutTransform();
224             auto transformRect = renderContext->GetPaintRectWithTransform();
225             hasTransform = noTransformRect != transformRect;
226         } else {
227             break;
228         }
229         host = host->GetAncestorNodeOfFrame(true);
230     }
231     return hasTransform;
232 }
233 
ConvertStr8toStr16(const std::string & value)234 std::u16string TextBase::ConvertStr8toStr16(const std::string& value)
235 {
236     auto content = value;
237     std::u16string result = StringUtils::Str8ToStr16(content);
238     if (result.length() == 0 && value.length() != 0) {
239         content = TextEmojiProcessor::ConvertU8stringUnpairedSurrogates(value);
240         result = StringUtils::Str8ToStr16(content);
241     }
242     return result;
243 }
244 
TruncateText(const std::u16string & text,const size_t & length) const245 std::u16string TextBase::TruncateText(const std::u16string& text, const size_t& length) const
246 {
247     const size_t maxLength = length;
248     size_t charCount = 0;
249     size_t byteIndex = 0;
250 
251     while (byteIndex < text.size() && charCount < maxLength) {
252         charCount++;
253         byteIndex += (text[byteIndex] >= 0xD800 && text[byteIndex] <= 0xDBFF) ?
254             UTF16_SURROGATE_PAIR_LENGTH : UTF16_SINGLE_CHAR_LENGTH;
255     }
256 
257     if (charCount >= maxLength) {
258         return text.substr(0, byteIndex);
259     }
260     return text;
261 }
262 
CountUtf16Chars(const std::u16string & s)263 size_t TextBase::CountUtf16Chars(const std::u16string& s)
264 {
265     size_t charCount = 0;
266     size_t i = 0;
267     while (i < s.size()) {
268         charCount++;
269         i += (s[i] >= 0xD800 && s[i] <= 0xDBFF) ? UTF16_SURROGATE_PAIR_LENGTH : UTF16_SINGLE_CHAR_LENGTH;
270     }
271     return charCount;
272 }
273 
GetLayoutCalPolicy(LayoutWrapper * layoutWrapper,bool isHorizontal)274 LayoutCalPolicy TextBase::GetLayoutCalPolicy(LayoutWrapper* layoutWrapper, bool isHorizontal)
275 {
276     CHECK_NULL_RETURN(layoutWrapper, LayoutCalPolicy::NO_MATCH);
277     auto layoutProperty = layoutWrapper->GetLayoutProperty();
278     CHECK_NULL_RETURN(layoutProperty, LayoutCalPolicy::NO_MATCH);
279     auto layoutPolicyProperty = layoutProperty->GetLayoutPolicyProperty();
280     CHECK_NULL_RETURN(layoutPolicyProperty, LayoutCalPolicy::NO_MATCH);
281     if (isHorizontal) {
282         CHECK_NULL_RETURN(layoutPolicyProperty->widthLayoutPolicy_, LayoutCalPolicy::NO_MATCH);
283         return layoutPolicyProperty->widthLayoutPolicy_.value();
284     }
285     CHECK_NULL_RETURN(layoutPolicyProperty->heightLayoutPolicy_, LayoutCalPolicy::NO_MATCH);
286     return layoutPolicyProperty->heightLayoutPolicy_.value();
287 }
288 
GetConstraintMaxLength(LayoutWrapper * layoutWrapper,const LayoutConstraintF & constraint,bool isHorizontal)289 float TextBase::GetConstraintMaxLength(
290     LayoutWrapper* layoutWrapper, const LayoutConstraintF& constraint, bool isHorizontal)
291 {
292     auto layoutCalPolicy = GetLayoutCalPolicy(layoutWrapper, isHorizontal);
293     if (layoutCalPolicy == LayoutCalPolicy::MATCH_PARENT) {
294         return isHorizontal ? constraint.parentIdealSize.Width().value_or(constraint.maxSize.Width())
295                             : constraint.parentIdealSize.Height().value_or(constraint.maxSize.Height());
296     }
297     return isHorizontal ? constraint.maxSize.Width() : constraint.maxSize.Height();
298 }
299 
GetCalcLayoutConstraintLength(LayoutWrapper * layoutWrapper,bool isMax,bool isWidth)300 std::optional<float> TextBase::GetCalcLayoutConstraintLength(LayoutWrapper* layoutWrapper, bool isMax, bool isWidth)
301 {
302     CHECK_NULL_RETURN(layoutWrapper, std::nullopt);
303     auto layoutProperty = layoutWrapper->GetLayoutProperty();
304     CHECK_NULL_RETURN(layoutProperty, std::nullopt);
305     const auto& layoutCalcConstraint = layoutProperty->GetCalcLayoutConstraint();
306     CHECK_NULL_RETURN(layoutCalcConstraint, std::nullopt);
307     auto layoutConstraint = layoutProperty->GetLayoutConstraint();
308     CHECK_NULL_RETURN(layoutConstraint, std::nullopt);
309     auto calcLayoutConstraintMaxMinSize = isMax ? layoutCalcConstraint->maxSize : layoutCalcConstraint->minSize;
310     CHECK_NULL_RETURN(calcLayoutConstraintMaxMinSize, std::nullopt);
311     auto optionalCalcLength =
312         isWidth ? calcLayoutConstraintMaxMinSize->Width() : calcLayoutConstraintMaxMinSize->Height();
313     auto percentLength =
314         isWidth ? layoutConstraint->percentReference.Width() : layoutConstraint->percentReference.Height();
315     CHECK_NULL_RETURN(optionalCalcLength, std::nullopt);
316     return ConvertToPx(optionalCalcLength, ScaleProperty::CreateScaleProperty(), percentLength);
317 }
318 
DoGestureSelection(const TouchEventInfo & info)319 void TextGestureSelector::DoGestureSelection(const TouchEventInfo& info)
320 {
321     if (!isStarted_ || info.GetChangedTouches().empty()) {
322         return;
323     }
324     auto locationInfo = info.GetChangedTouches().front();
325     auto touchType = locationInfo.GetTouchType();
326     switch (touchType) {
327         case TouchType::UP:
328             EndGestureSelection(locationInfo);
329             break;
330         case TouchType::MOVE:
331             DoTextSelectionTouchMove(info);
332             break;
333         case TouchType::CANCEL:
334             CancelGestureSelection();
335             break;
336         default:
337             break;
338     }
339 }
340 
DoTextSelectionTouchMove(const TouchEventInfo & info)341 void TextGestureSelector::DoTextSelectionTouchMove(const TouchEventInfo& info)
342 {
343     auto locationInfo = info.GetChangedTouches().front();
344     auto localOffset = locationInfo.GetLocalLocation();
345     if (!isSelecting_) {
346         if (LessOrEqual((localOffset - startOffset_).GetDistance(), minMoveDistance_.ConvertToPx())) {
347             return;
348         }
349         isSelecting_ = true;
350         selectingFingerId_ = locationInfo.GetFingerId();
351     }
352     auto index = GetTouchIndex({ localOffset.GetX(), localOffset.GetY() });
353     auto start = std::min(index, start_);
354     auto end = std::max(index, end_);
355     OnTextGestureSelectionUpdate(start, end, info);
356 }
357 
DetectTextDiff(const std::string & latestContent)358 std::pair<std::string, std::string> TextBase::DetectTextDiff(const std::string& latestContent)
359 {
360     const std::string& beforeText = textCache_;
361     std::string addedText;
362     std::string removedText;
363     size_t prefixLen = 0;
364     size_t minLength = std::min(beforeText.length(), latestContent.length());
365     while (prefixLen < minLength && beforeText[prefixLen] == latestContent[prefixLen]) {
366         prefixLen++;
367     }
368     while (prefixLen > 0 && ((beforeText[prefixLen] & 0xC0) == 0x80)) {
369         prefixLen--;
370     }
371     size_t suffixLen = 0;
372     size_t remainBefore = beforeText.length() - prefixLen;
373     size_t remainAfter = latestContent.length() - prefixLen;
374     size_t minSuffix = std::min(remainBefore, remainAfter);
375     while (suffixLen < minSuffix &&
376         beforeText[beforeText.length() - 1 - suffixLen] == latestContent[latestContent.length() - 1 - suffixLen]) {
377         suffixLen++;
378     }
379     while (suffixLen > 0 && ((beforeText[beforeText.size() - suffixLen] & 0xC0) == 0x80)) {
380         suffixLen--;
381     }
382     size_t removeStart = prefixLen;
383     size_t removeEnd = beforeText.length() - suffixLen;
384     if (removeEnd > removeStart) {
385         removedText = beforeText.substr(removeStart, removeEnd - removeStart);
386     }
387     size_t addStart = prefixLen;
388     size_t addEnd = latestContent.length() - suffixLen;
389     if (addEnd > addStart) {
390         addedText = latestContent.substr(addStart, addEnd - addStart);
391     }
392     return {std::move(addedText), std::move(removedText)};
393 }
394 }