• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 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_drag/text_drag_pattern.h"
17 
18 #include "base/utils/utils.h"
19 #include "core/components_ng/pattern/text/text_pattern.h"
20 #include "core/components_ng/pattern/text_drag/text_drag_base.h"
21 #include "core/components_ng/render/drawing.h"
22 #include "core/components_v2/inspector/inspector_constants.h"
23 
24 namespace {
25 // uncertainty range when comparing selectedTextBox to contentRect
26 constexpr float BOX_EPSILON = 0.2f;
27 } // namespace
28 
29 namespace OHOS::Ace::NG {
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)30 bool TextDragPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
31 {
32     return true;
33 }
34 
CreateDragNode(const RefPtr<FrameNode> & hostNode)35 RefPtr<FrameNode> TextDragPattern::CreateDragNode(const RefPtr<FrameNode>& hostNode)
36 {
37     CHECK_NULL_RETURN(hostNode, nullptr);
38     auto hostPattern = hostNode->GetPattern<TextDragBase>();
39     const auto nodeId = ElementRegister::GetInstance()->MakeUniqueId();
40     auto dragNode = FrameNode::GetOrCreateFrameNode(
41         V2::TEXTDRAG_ETS_TAG, nodeId, []() { return AceType::MakeRefPtr<TextDragPattern>(); });
42     auto dragContext = dragNode->GetRenderContext();
43     auto hostContext = hostNode->GetRenderContext();
44     if (hostContext->HasForegroundColor()) {
45         dragContext->UpdateForegroundColor(hostContext->GetForegroundColor().value());
46     }
47     if (hostContext->HasForegroundColorStrategy()) {
48         dragContext->UpdateForegroundColorStrategy(hostContext->GetForegroundColorStrategy().value());
49     }
50     auto dragPattern = dragNode->GetPattern<TextDragPattern>();
51     auto data = CalculateTextDragData(hostPattern, dragNode);
52     dragPattern->Initialize(hostPattern->GetDragParagraph(), data);
53     dragPattern->SetLastLineHeight(data.lineHeight_);
54 
55     CalcSize size(NG::CalcLength(dragPattern->GetFrameWidth()), NG::CalcLength(dragPattern->GetFrameHeight()));
56     dragNode->GetLayoutProperty()->UpdateUserDefinedIdealSize(size);
57     return dragNode;
58 }
59 
CreateDragNode(const RefPtr<FrameNode> & hostNode,std::list<RefPtr<FrameNode>> & imageChildren)60 RefPtr<FrameNode> TextDragPattern::CreateDragNode(
61     const RefPtr<FrameNode>& hostNode, std::list<RefPtr<FrameNode>>& imageChildren)
62 {
63     auto hostPattern = hostNode->GetPattern<TextDragBase>();
64     auto dragNode = TextDragPattern::CreateDragNode(hostNode);
65     auto dragPattern = dragNode->GetPattern<TextDragPattern>();
66     auto textPattern = hostNode->GetPattern<TextPattern>();
67     auto placeHolderIndex = textPattern->GetPlaceHolderIndex();
68     auto rectsForPlaceholders = textPattern->GetRectsForPlaceholders();
69 
70     size_t index = 0;
71     std::vector<Rect> realRectsForPlaceholders;
72     std::list<RefPtr<FrameNode>> realImageChildren;
73     auto boxes = hostPattern->GetTextBoxes();
74     for (const auto& child : imageChildren) {
75         auto imageIndex = placeHolderIndex[index];
76         auto rect = rectsForPlaceholders.at(imageIndex);
77 
78         for (const auto& box : boxes) {
79             if (LessOrEqual(box.rect_.GetLeft(), rect.Left()) &&
80                 GreatOrEqual(box.rect_.GetRight(), rect.Right()) &&
81                 LessOrEqual(box.rect_.GetTop(), rect.Top()) &&
82                 GreatOrEqual(box.rect_.GetBottom(), rect.Bottom())) {
83                 realImageChildren.emplace_back(child);
84                 realRectsForPlaceholders.emplace_back(rect);
85             }
86         }
87         ++index;
88     }
89     dragPattern->SetLastLineHeight(boxes.back().rect_.GetHeight());
90     dragPattern->InitSpanImageLayout(realImageChildren, realRectsForPlaceholders);
91     return dragNode;
92 }
93 
CalculateTextDragData(RefPtr<TextDragBase> & hostPattern,RefPtr<FrameNode> & dragNode)94 TextDragData TextDragPattern::CalculateTextDragData(RefPtr<TextDragBase>& hostPattern, RefPtr<FrameNode>& dragNode)
95 {
96     auto dragContext = dragNode->GetRenderContext();
97     auto dragPattern = dragNode->GetPattern<TextDragPattern>();
98     float textStartX = hostPattern->GetTextRect().GetX();
99     float textStartY =
100         hostPattern->IsTextArea() ? hostPattern->GetTextRect().GetY() : hostPattern->GetTextContentRect().GetY();
101     auto contentRect = hostPattern->GetTextContentRect();
102     float lineHeight = hostPattern->GetLineHeight();
103     float minWidth = TEXT_DRAG_MIN_WIDTH.ConvertToPx();
104     float bothOffset = TEXT_DRAG_OFFSET.ConvertToPx() * 2; // 2 : double
105     auto boxes = hostPattern->GetTextBoxes();
106     CHECK_NULL_RETURN(!boxes.empty(), {});
107     auto boxFirst = boxes.front();
108     auto boxLast = boxes.back();
109     float leftHandleX = boxFirst.rect_.GetLeft() + textStartX;
110     float leftHandleY = boxFirst.rect_.GetTop() + textStartY;
111     float rightHandleX = boxLast.rect_.GetRight() + textStartX;
112     float rightHandleY = boxLast.rect_.GetTop() + textStartY;
113     bool oneLineSelected = (leftHandleY == rightHandleY);
114     if (oneLineSelected) {
115         if (leftHandleX < contentRect.Left()) {
116             leftHandleX = contentRect.Left();
117         }
118         if (rightHandleX > contentRect.Right()) {
119             rightHandleX = contentRect.Right();
120         }
121     } else {
122         if (leftHandleY < contentRect.Top() - BOX_EPSILON) {
123             leftHandleX = contentRect.Left();
124             leftHandleY = contentRect.Top();
125         }
126         if ((boxLast.rect_.GetBottom() + textStartY) > contentRect.Bottom()) {
127             rightHandleX = contentRect.Right();
128             rightHandleY = contentRect.Bottom() - lineHeight;
129         }
130     }
131     auto hostGlobalOffset = hostPattern->GetParentGlobalOffset();
132     float width = rightHandleX - leftHandleX;
133     float height = rightHandleY - leftHandleY + lineHeight;
134     float globalX = leftHandleX + hostGlobalOffset.GetX() - TEXT_DRAG_OFFSET.ConvertToPx();
135     float globalY = leftHandleY + hostGlobalOffset.GetY() - TEXT_DRAG_OFFSET.ConvertToPx();
136     if (oneLineSelected) {
137         if (rightHandleX - leftHandleX + bothOffset < minWidth) {
138             float delta = minWidth - (rightHandleX - leftHandleX + bothOffset);
139             width += delta;
140             globalX -= delta / 2; // 2 : half
141         }
142         dragPattern->SetContentOffset(OffsetF(boxes.front().rect_.GetLeft() - TEXT_DRAG_OFFSET.ConvertToPx(),
143             boxes.front().rect_.GetTop() - TEXT_DRAG_OFFSET.ConvertToPx()));
144     } else {
145         globalX = contentRect.Left() + hostGlobalOffset.GetX() - TEXT_DRAG_OFFSET.ConvertToPx();
146         width = contentRect.Width();
147         dragPattern->SetContentOffset(
148             OffsetF(0 - TEXT_DRAG_OFFSET.ConvertToPx(), boxes.front().rect_.GetTop() - TEXT_DRAG_OFFSET.ConvertToPx()));
149     }
150     dragContext->UpdatePosition(OffsetT<Dimension>(Dimension(globalX), Dimension(globalY)));
151     RectF dragTextRect(
152         textStartX + hostGlobalOffset.GetX() - globalX, textStartY + hostGlobalOffset.GetY() - globalY, width, height);
153     SelectPositionInfo info(leftHandleX + hostGlobalOffset.GetX() - globalX,
154         leftHandleY + hostGlobalOffset.GetY() - globalY, rightHandleX + hostGlobalOffset.GetX() - globalX,
155         rightHandleY + hostGlobalOffset.GetY() - globalY);
156     TextDragData data(dragTextRect, width + bothOffset, height + bothOffset, lineHeight, info, oneLineSelected);
157     return data;
158 }
159 
GenerateClipPath()160 std::shared_ptr<RSPath> TextDragPattern::GenerateClipPath()
161 {
162     std::shared_ptr<RSPath> path = std::make_shared<RSPath>();
163     auto selectPosition = GetSelectPosition();
164     float startX = selectPosition.startX_;
165     float startY = selectPosition.startY_;
166     float endX = selectPosition.endX_;
167     float endY = selectPosition.endY_;
168     float textStart = GetTextRect().GetX();
169     float textEnd = textStart + GetTextRect().Width();
170     auto lineHeight = GetLineHeight();
171     if (OneLineSelected()) {
172         path->MoveTo(startX, endY);
173         path->LineTo(endX, endY);
174         path->LineTo(endX, endY + lineHeight);
175         path->LineTo(startX, endY + lineHeight);
176         path->LineTo(startX, endY);
177     } else {
178         path->MoveTo(startX, startY);
179         path->LineTo(textEnd, startY);
180         path->LineTo(textEnd, endY);
181         path->LineTo(endX, endY);
182         path->LineTo(endX, endY + lastLineHeight_);
183         path->LineTo(textStart, endY + lastLineHeight_);
184         path->LineTo(textStart, startY + lineHeight);
185         path->LineTo(startX, startY + lineHeight);
186         path->LineTo(startX, startY);
187     }
188     return path;
189 }
190 
GenerateBackgroundPath(float offset)191 std::shared_ptr<RSPath> TextDragPattern::GenerateBackgroundPath(float offset)
192 {
193     std::shared_ptr<RSPath> path = std::make_shared<RSPath>();
194     std::vector<TextPoint> points;
195     GenerateBackgroundPoints(points, offset);
196     CalculateLineAndArc(points, path);
197     return path;
198 }
199 
GenerateBackgroundPoints(std::vector<TextPoint> & points,float offset)200 void TextDragPattern::GenerateBackgroundPoints(std::vector<TextPoint>& points, float offset)
201 {
202     auto radius = TEXT_DRAG_RADIUS.ConvertToPx();
203     auto bothOffset = offset * 2; // 2 : double
204     auto minWidth = TEXT_DRAG_MIN_WIDTH.ConvertToPx();
205     auto selectPosition = GetSelectPosition();
206     float startX = selectPosition.startX_;
207     float startY = selectPosition.startY_;
208     float endX = selectPosition.endX_;
209     float endY = selectPosition.endY_;
210     float textStart = GetTextRect().GetX();
211     float textEnd = textStart + GetTextRect().Width();
212     auto lineHeight = GetLineHeight();
213     if (OneLineSelected()) {
214         if ((endX - startX) + bothOffset < minWidth) {
215             float delta = minWidth - ((endX - startX) + bothOffset);
216             startX -= delta / 2.0f; // 2 : half
217             endX += delta / 2.0f;   // 2 : half
218         }
219         points.push_back(TextPoint(startX - offset, startY - offset));
220         points.push_back(TextPoint(endX + offset, endY - offset));
221         points.push_back(TextPoint(endX + offset, endY + lineHeight + offset));
222         points.push_back(TextPoint(startX - offset, endY + lineHeight + offset));
223         points.push_back(TextPoint(startX - offset, endY - offset));
224         points.push_back(TextPoint(endX + offset, endY - offset));
225     } else {
226         points.push_back(TextPoint(startX - offset, startY - offset));
227         points.push_back(TextPoint(textEnd + offset, startY - offset));
228         if (textEnd - radius < endX + radius) {
229             points.push_back(TextPoint(textEnd + offset, endY + lineHeight + offset));
230         } else {
231             points.push_back(TextPoint(textEnd + offset, endY + offset));
232             points.push_back(TextPoint(endX + offset, endY + offset));
233             points.push_back(TextPoint(endX + offset, endY + lineHeight + offset));
234         }
235         points.push_back(TextPoint(textStart - offset, endY + lineHeight + offset));
236         if (startX - radius < textStart + radius) {
237             points[0] = TextPoint(textStart - offset, startY - offset);
238             points.push_back(TextPoint(textStart - offset, startY - offset));
239         } else {
240             points.push_back(TextPoint(textStart - offset, startY + lineHeight - offset));
241             points.push_back(TextPoint(startX - offset, startY + lineHeight - offset));
242             points.push_back(TextPoint(startX - offset, startY - offset));
243         }
244         points.push_back(TextPoint(textEnd + offset, startY - offset));
245     }
246 }
247 
CalculateLineAndArc(std::vector<TextPoint> & points,std::shared_ptr<RSPath> & path)248 void TextDragPattern::CalculateLineAndArc(std::vector<TextPoint>& points, std::shared_ptr<RSPath>& path)
249 {
250     auto radius = TEXT_DRAG_RADIUS.ConvertToPx();
251     path->MoveTo(points[0].x + radius, points[0].y);
252     size_t step = 2;
253     for (size_t i = 0; i + step < points.size(); i++) {
254         auto firstPoint = points[i];
255         auto crossPoint = points[i + 1];
256         auto secondPoint = points[i + step];
257 
258         if (crossPoint.y == firstPoint.y) {
259             int directionX = (crossPoint.x - firstPoint.x) > 0 ? 1 : -1;
260             int directionY = (secondPoint.y - crossPoint.y) > 0 ? 1 : -1;
261             auto direction =
262                 (directionX * directionY > 0) ? RSPathDirection::CW_DIRECTION : RSPathDirection::CCW_DIRECTION;
263             path->LineTo(crossPoint.x - radius * directionX, crossPoint.y);
264             path->ArcTo(radius, radius, 0.0f, direction, crossPoint.x, crossPoint.y + radius * directionY);
265         } else {
266             int directionX = (secondPoint.x - crossPoint.x) > 0 ? 1 : -1;
267             int directionY = (crossPoint.y - firstPoint.y) > 0 ? 1 : -1;
268             auto direction =
269                 (directionX * directionY < 0) ? RSPathDirection::CW_DIRECTION : RSPathDirection::CCW_DIRECTION;
270             path->LineTo(crossPoint.x, crossPoint.y - radius * directionY);
271             path->ArcTo(radius, radius, 0.0f, direction, crossPoint.x + radius * directionX, secondPoint.y);
272         }
273     }
274 }
275 } // namespace OHOS::Ace::NG
276