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