• 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 
GetFirstBoxRect(const std::vector<RectF> & boxes,const RectF & contentRect,const float textStartY)35 const RectF GetFirstBoxRect(const std::vector<RectF>& boxes, const RectF& contentRect, const float textStartY)
36 {
37     for (const auto& box : boxes) {
38         if (box.Bottom() + textStartY > contentRect.Top() + BOX_EPSILON) {
39             return box;
40         }
41     }
42     return boxes.front();
43 } // Obtains the first line in the visible area of the text box, including the truncated part.
44 
GetLastBoxRect(const std::vector<RectF> & boxes,const RectF & contentRect,const float textStartY)45 const RectF GetLastBoxRect(const std::vector<RectF>& boxes, const RectF& contentRect, const float textStartY)
46 {
47     bool hasResult = false;
48     RectF result;
49     for (const auto& box : boxes) {
50         if (box.Bottom() + textStartY >= contentRect.Bottom() && !hasResult) {
51             result = box;
52             hasResult = true;
53             continue;
54         }
55         if (hasResult && box.Bottom() == result.Bottom()) {
56             result = box;
57         } else if (hasResult && box.Bottom() != result.Bottom()) {
58             return result;
59         }
60     }
61     return boxes.back();
62 } // Obtains the last line in the visible area of the text box, including the truncated part.
63 
CreateDragNode(const RefPtr<FrameNode> & hostNode)64 RefPtr<FrameNode> TextDragPattern::CreateDragNode(const RefPtr<FrameNode>& hostNode)
65 {
66     CHECK_NULL_RETURN(hostNode, nullptr);
67     auto hostPattern = hostNode->GetPattern<TextDragBase>();
68     const auto nodeId = ElementRegister::GetInstance()->MakeUniqueId();
69     auto dragNode = FrameNode::GetOrCreateFrameNode(
70         V2::TEXTDRAG_ETS_TAG, nodeId, []() { return AceType::MakeRefPtr<TextDragPattern>(); });
71     auto dragContext = dragNode->GetRenderContext();
72     auto hostContext = hostNode->GetRenderContext();
73     if (hostContext->HasForegroundColor()) {
74         dragContext->UpdateForegroundColor(hostContext->GetForegroundColor().value());
75     }
76     if (hostContext->HasForegroundColorStrategy()) {
77         dragContext->UpdateForegroundColorStrategy(hostContext->GetForegroundColorStrategy().value());
78     }
79     auto dragPattern = dragNode->GetPattern<TextDragPattern>();
80     auto data = CalculateTextDragData(hostPattern, dragNode);
81     dragPattern->Initialize(hostPattern->GetDragParagraph(), data);
82     dragPattern->SetLastLineHeight(data.lineHeight_);
83 
84     CalcSize size(NG::CalcLength(dragPattern->GetFrameWidth()), NG::CalcLength(dragPattern->GetFrameHeight()));
85     dragNode->GetLayoutProperty()->UpdateUserDefinedIdealSize(size);
86     return dragNode;
87 }
88 
CalculateTextDragData(RefPtr<TextDragBase> & hostPattern,RefPtr<FrameNode> & dragNode)89 TextDragData TextDragPattern::CalculateTextDragData(RefPtr<TextDragBase>& hostPattern, RefPtr<FrameNode>& dragNode)
90 {
91     auto dragContext = dragNode->GetRenderContext();
92     auto dragPattern = dragNode->GetPattern<TextDragPattern>();
93     float textStartX = hostPattern->GetTextRect().GetX();
94     float textStartY =
95         hostPattern->IsTextArea() ? hostPattern->GetTextRect().GetY() : hostPattern->GetTextContentRect().GetY();
96     auto contentRect = hostPattern->GetTextContentRect();
97     float lineHeight = hostPattern->GetLineHeight();
98     float minWidth = TEXT_DRAG_MIN_WIDTH.ConvertToPx();
99     float bothOffset = TEXT_DRAG_OFFSET.ConvertToPx() * 2; // 2 : double
100     auto boxes = hostPattern->GetTextBoxes();
101     CHECK_NULL_RETURN(!boxes.empty(), {});
102     auto boxFirst = GetFirstBoxRect(boxes, contentRect, textStartY);
103     auto boxLast = GetLastBoxRect(boxes, contentRect, textStartY);
104     float leftHandleX = boxFirst.Left() + textStartX;
105     float leftHandleY = boxFirst.Top() + textStartY;
106     float rightHandleX = boxLast.Right() + textStartX;
107     float rightHandleY = boxLast.Top() + textStartY;
108     float lastLineHeight = boxLast.Height();
109     bool oneLineSelected = (leftHandleY == rightHandleY);
110     if (oneLineSelected) {
111         if (leftHandleX < contentRect.Left()) {
112             leftHandleX = contentRect.Left();
113         }
114         if (rightHandleX > contentRect.Right()) {
115             rightHandleX = contentRect.Right();
116         }
117     }
118     auto hostGlobalOffset = hostPattern->GetParentGlobalOffset();
119     float width = rightHandleX - leftHandleX;
120     float height = rightHandleY - leftHandleY + lastLineHeight;
121     float globalX = leftHandleX + hostGlobalOffset.GetX() - TEXT_DRAG_OFFSET.ConvertToPx();
122     float globalY = leftHandleY + hostGlobalOffset.GetY() - TEXT_DRAG_OFFSET.ConvertToPx();
123     if (oneLineSelected) {
124         float delta = 0.0f;
125         if (rightHandleX - leftHandleX + bothOffset < minWidth) {
126             delta = minWidth - (rightHandleX - leftHandleX + bothOffset);
127             width += delta;
128             globalX -= delta / 2; // 2 : half
129         }
130 
131         dragPattern->SetContentOffset(
132             OffsetF(boxes.front().Left() - TEXT_DRAG_OFFSET.ConvertToPx() - delta / 2, // 2 : half
133                 boxes.front().Top() - TEXT_DRAG_OFFSET.ConvertToPx()));
134     } else {
135         globalX = contentRect.Left() + hostGlobalOffset.GetX() - TEXT_DRAG_OFFSET.ConvertToPx();
136         width = contentRect.Width();
137         dragPattern->SetContentOffset(
138             OffsetF(0 - TEXT_DRAG_OFFSET.ConvertToPx(), boxes.front().Top() - TEXT_DRAG_OFFSET.ConvertToPx()));
139     }
140     dragContext->UpdatePosition(OffsetT<Dimension>(Dimension(globalX), Dimension(globalY)));
141     RectF dragTextRect(
142         textStartX + hostGlobalOffset.GetX() - globalX, textStartY + hostGlobalOffset.GetY() - globalY, width, height);
143     SelectPositionInfo info(leftHandleX + hostGlobalOffset.GetX() - globalX,
144         leftHandleY + hostGlobalOffset.GetY() - globalY, rightHandleX + hostGlobalOffset.GetX() - globalX,
145         rightHandleY + hostGlobalOffset.GetY() - globalY);
146     TextDragData data(dragTextRect, width + bothOffset, height + bothOffset, lineHeight, info, oneLineSelected);
147     return data;
148 }
149 
GenerateClipPath()150 std::shared_ptr<RSPath> TextDragPattern::GenerateClipPath()
151 {
152     std::shared_ptr<RSPath> path = std::make_shared<RSPath>();
153     auto selectPosition = GetSelectPosition();
154     float startX = selectPosition.startX_;
155     float startY = selectPosition.startY_;
156     float endX = selectPosition.endX_;
157     float endY = selectPosition.endY_;
158     float textStart = GetTextRect().GetX();
159     float textEnd = textStart + GetTextRect().Width();
160     auto lineHeight = GetLineHeight();
161     if (OneLineSelected()) {
162         path->MoveTo(startX, endY);
163         path->LineTo(endX, endY);
164         path->LineTo(endX, endY + lineHeight);
165         path->LineTo(startX, endY + lineHeight);
166         path->LineTo(startX, endY);
167     } else {
168         path->MoveTo(startX, startY);
169         path->LineTo(textEnd, startY);
170         path->LineTo(textEnd, endY);
171         path->LineTo(endX, endY);
172         path->LineTo(endX, endY + lastLineHeight_);
173         path->LineTo(textStart, endY + lastLineHeight_);
174         path->LineTo(textStart, startY + lineHeight);
175         path->LineTo(startX, startY + lineHeight);
176         path->LineTo(startX, startY);
177     }
178     return path;
179 }
180 
GenerateBackgroundPath(float offset)181 std::shared_ptr<RSPath> TextDragPattern::GenerateBackgroundPath(float offset)
182 {
183     std::shared_ptr<RSPath> path = std::make_shared<RSPath>();
184     std::vector<TextPoint> points;
185     GenerateBackgroundPoints(points, offset);
186     CalculateLineAndArc(points, path);
187     return path;
188 }
189 
GenerateBackgroundPoints(std::vector<TextPoint> & points,float offset)190 void TextDragPattern::GenerateBackgroundPoints(std::vector<TextPoint>& points, float offset)
191 {
192     auto radius = TEXT_DRAG_RADIUS.ConvertToPx();
193     auto bothOffset = offset * 2; // 2 : double
194     auto minWidth = TEXT_DRAG_MIN_WIDTH.ConvertToPx();
195     auto selectPosition = GetSelectPosition();
196     float startX = selectPosition.startX_;
197     float startY = selectPosition.startY_;
198     float endX = selectPosition.endX_;
199     float endY = selectPosition.endY_;
200     float textStart = GetTextRect().GetX();
201     float textEnd = textStart + GetTextRect().Width();
202     auto lineHeight = GetLineHeight();
203     if (OneLineSelected()) {
204         if ((endX - startX) + bothOffset < minWidth) {
205             float delta = minWidth - ((endX - startX) + bothOffset);
206             startX -= delta / 2.0f; // 2 : half
207             endX += delta / 2.0f;   // 2 : half
208         }
209         points.push_back(TextPoint(startX - offset, startY - offset));
210         points.push_back(TextPoint(endX + offset, endY - offset));
211         points.push_back(TextPoint(endX + offset, endY + lineHeight + offset));
212         points.push_back(TextPoint(startX - offset, endY + lineHeight + offset));
213         points.push_back(TextPoint(startX - offset, endY - offset));
214         points.push_back(TextPoint(endX + offset, endY - offset));
215     } else {
216         points.push_back(TextPoint(startX - offset, startY - offset));
217         points.push_back(TextPoint(textEnd + offset, startY - offset));
218         if (textEnd - radius < endX + radius) {
219             points.push_back(TextPoint(textEnd + offset, endY + lastLineHeight_ + offset));
220         } else {
221             points.push_back(TextPoint(textEnd + offset, endY + offset));
222             points.push_back(TextPoint(endX + offset, endY + offset));
223             points.push_back(TextPoint(endX + offset, endY + lastLineHeight_ + offset));
224         }
225         points.push_back(TextPoint(textStart - offset, endY + lastLineHeight_ + offset));
226         if (startX - radius < textStart + radius) {
227             points[0] = TextPoint(textStart - offset, startY - offset);
228             points.push_back(TextPoint(textStart - offset, startY - offset));
229         } else {
230             points.push_back(TextPoint(textStart - offset, startY + lineHeight - offset));
231             points.push_back(TextPoint(startX - offset, startY + lineHeight - offset));
232             points.push_back(TextPoint(startX - offset, startY - offset));
233         }
234         points.push_back(TextPoint(textEnd + offset, startY - offset));
235     }
236 }
237 
CalculateLineAndArc(std::vector<TextPoint> & points,std::shared_ptr<RSPath> & path)238 void TextDragPattern::CalculateLineAndArc(std::vector<TextPoint>& points, std::shared_ptr<RSPath>& path)
239 {
240     auto radius = TEXT_DRAG_RADIUS.ConvertToPx();
241     path->MoveTo(points[0].x + radius, points[0].y);
242     size_t step = 2;
243     for (size_t i = 0; i + step < points.size(); i++) {
244         auto firstPoint = points[i];
245         auto crossPoint = points[i + 1];
246         auto secondPoint = points[i + step];
247 
248         if (crossPoint.y == firstPoint.y) {
249             int directionX = (crossPoint.x - firstPoint.x) > 0 ? 1 : -1;
250             int directionY = (secondPoint.y - crossPoint.y) > 0 ? 1 : -1;
251             auto direction =
252                 (directionX * directionY > 0) ? RSPathDirection::CW_DIRECTION : RSPathDirection::CCW_DIRECTION;
253             path->LineTo(crossPoint.x - radius * directionX, crossPoint.y);
254             path->ArcTo(radius, radius, 0.0f, direction, crossPoint.x, crossPoint.y + radius * directionY);
255         } else {
256             int directionX = (secondPoint.x - crossPoint.x) > 0 ? 1 : -1;
257             int directionY = (crossPoint.y - firstPoint.y) > 0 ? 1 : -1;
258             auto direction =
259                 (directionX * directionY < 0) ? RSPathDirection::CW_DIRECTION : RSPathDirection::CCW_DIRECTION;
260             path->LineTo(crossPoint.x, crossPoint.y - radius * directionY);
261             path->ArcTo(radius, radius, 0.0f, direction, crossPoint.x + radius * directionX, secondPoint.y);
262         }
263     }
264 }
265 } // namespace OHOS::Ace::NG
266