1 /*
2  * Copyright (c) 2023-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 #ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_TEXT_FIELD_PATTERN_TEXT_SELECT_CONTROLLER_H
17 #define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_TEXT_FIELD_PATTERN_TEXT_SELECT_CONTROLLER_H
18 
19 #include <cstdint>
20 #include <functional>
21 
22 #include "base/geometry/ng/offset_t.h"
23 #include "base/geometry/ng/rect_t.h"
24 #include "base/memory/ace_type.h"
25 #include "base/memory/referenced.h"
26 #include "core/components_ng/pattern/pattern.h"
27 #include "core/components_ng/pattern/text_field/content_controller.h"
28 #include "core/components_ng/pattern/text_field/text_field_layout_property.h"
29 #include "core/components_ng/pattern/text_field/text_selector.h"
30 #include "core/components_ng/property/property.h"
31 #include "core/components_ng/render/paragraph.h"
32 
33 namespace OHOS::Ace::NG {
34 namespace {
35 using OnAccessibilityCallback = std::function<void()>;
36 } // namespace
37 
38 class TextSelectController : public Property {
39     DECLARE_ACE_TYPE(TextSelectController, AceType);
40 
41 public:
TextSelectController(const WeakPtr<Pattern> & pattern)42     explicit TextSelectController(const WeakPtr<Pattern>& pattern) : pattern_(pattern) {}
43     ~TextSelectController() override = default;
SetOnAccessibility(OnAccessibilityCallback && onAccessibilityCallback)44     void SetOnAccessibility(OnAccessibilityCallback&& onAccessibilityCallback)
45     {
46         if (onAccessibilityCallback) {
47             onAccessibilityCallback_ = std::move(onAccessibilityCallback);
48         }
49     }
50 
51     void FireSelectEvent();
52 
UpdateHandleIndex(int32_t handleIndex)53     void UpdateHandleIndex(int32_t handleIndex)
54     {
55         UpdateHandleIndex(handleIndex, handleIndex);
56     }
57 
GetStartIndex()58     inline int32_t GetStartIndex() const
59     {
60         return std::min(firstHandleInfo_.index, secondHandleInfo_.index);
61     }
62 
GetEndIndex()63     inline int32_t GetEndIndex() const
64     {
65         return std::max(firstHandleInfo_.index, secondHandleInfo_.index);
66     }
67 
GetCaretIndex()68     int32_t GetCaretIndex() const
69     {
70         return caretInfo_.index;
71     }
72 
GetFirstHandleIndex()73     int32_t GetFirstHandleIndex() const
74     {
75         return firstHandleInfo_.index;
76     }
77 
GetFirstHandleRect()78     RectF GetFirstHandleRect() const
79     {
80         return firstHandleInfo_.rect;
81     }
82 
GetSecondHandleIndex()83     int32_t GetSecondHandleIndex() const
84     {
85         return secondHandleInfo_.index;
86     }
87 
GetSecondHandleRect()88     RectF GetSecondHandleRect() const
89     {
90         return secondHandleInfo_.rect;
91     }
92 
GetFirstHandleOffset()93     OffsetF GetFirstHandleOffset() const
94     {
95         return firstHandleInfo_.rect.GetOffset();
96     }
97 
GetSecondHandleOffset()98     OffsetF GetSecondHandleOffset() const
99     {
100         return secondHandleInfo_.rect.GetOffset();
101     }
102 
UpdateCaretHeight(float height)103     void UpdateCaretHeight(float height)
104     {
105         caretInfo_.rect.SetHeight(height);
106         secondHandleInfo_.rect.SetHeight(height);
107     }
108 
GetCaretRect()109     RectF GetCaretRect() const
110     {
111         return caretInfo_.rect;
112     }
113 
GetSelectHeight()114     double GetSelectHeight() const
115     {
116         return std::max(firstHandleInfo_.rect.Height(), secondHandleInfo_.rect.Height());
117     }
118 
InitContentController(const RefPtr<ContentController> & controller)119     void InitContentController(const RefPtr<ContentController>& controller)
120     {
121         contentController_ = controller;
122     }
123 
IsSelected()124     inline bool IsSelected() const
125     {
126         return firstHandleInfo_.index >= 0 && secondHandleInfo_.index >= 0 &&
127                firstHandleInfo_.index != secondHandleInfo_.index;
128     }
129 
IsSelectedAll()130     inline bool IsSelectedAll() const
131     {
132         return firstHandleInfo_.index == 0 && secondHandleInfo_.index >= 0 &&
133                abs(firstHandleInfo_.index - secondHandleInfo_.index) ==
134                    static_cast<int32_t>(contentController_->GetWideText().length());
135     }
136 
IsHandleSamePosition()137     bool IsHandleSamePosition()
138     {
139         bool sameX = NearEqual(firstHandleInfo_.rect.GetX(), secondHandleInfo_.rect.GetX());
140         bool sameY = NearEqual(firstHandleInfo_.rect.GetY(), secondHandleInfo_.rect.GetY());
141         return (sameX && sameY);
142     }
143 
UpdateParagraph(const RefPtr<Paragraph> & paragraph)144     void UpdateParagraph(const RefPtr<Paragraph>& paragraph)
145     {
146         paragraph_ = paragraph;
147     }
148 
UpdateContentRect(const RectF & rect)149     void UpdateContentRect(const RectF& rect)
150     {
151         contentRect_ = rect;
152     }
153 
UpdateCaretWidth(float width)154     void UpdateCaretWidth(float width)
155     {
156         caretInfo_.rect.SetWidth(width);
157     }
158 
GetFirstHandleInfo()159     HandleInfoNG GetFirstHandleInfo() const
160     {
161         return firstHandleInfo_;
162     }
163 
GetSecondHandleInfo()164     HandleInfoNG GetSecondHandleInfo() const
165     {
166         return secondHandleInfo_;
167     }
168 
GetCaretInfo()169     HandleInfoNG GetCaretInfo() const
170     {
171         return caretInfo_;
172     }
173 
HasReverse()174     bool HasReverse()
175     {
176         return firstHandleInfo_.index > secondHandleInfo_.index;
177     }
178 
CaretAtLast()179     bool CaretAtLast() const
180     {
181         return caretInfo_.index == static_cast<int32_t>(contentController_->GetWideText().length());
182     }
183 
184     int32_t ConvertTouchOffsetToPosition(const Offset& localOffset, bool isSelectionPos = false);
185     void ResetHandles();
186     void UpdateHandleIndex(int32_t firstHandleIndex, int32_t secondHandleIndex);
187     void UpdateCaretIndex(int32_t index);
188     void UpdateCaretInfoByOffset(const Offset& localOffset);
189     OffsetF CalcCaretOffsetByOffset(const Offset& localOffset);
190     void UpdateSecondHandleInfoByMouseOffset(const Offset& localOffset);
191     void MoveSecondHandleByKeyBoard(int32_t index);
192     void UpdateSelectByOffset(const Offset& localOffset);
193     void UpdateSelectPragraphByOffset(const Offset& localOffset);
194     std::pair<int32_t, int32_t> GetSelectRangeByOffset(const Offset& localOffset);
195     std::pair<int32_t, int32_t> GetSelectParagraphByOffset(const Offset& localOffset);
196     void UpdateCaretOffset(TextAffinity textAffinity = TextAffinity::DOWNSTREAM, bool moveHandle = true);
197     void UpdateCaretOffset(const OffsetF& offset);
198     void UpdateFirstHandleOffset();
199     void UpdateSecondHandleOffset();
200     void MoveFirstHandleToContentRect(int32_t index, bool moveHandle = true);
201     void MoveSecondHandleToContentRect(int32_t index, bool moveHandle = true);
202     void MoveCaretToContentRect(
203         int32_t index, TextAffinity textAffinity = TextAffinity::UPSTREAM, bool isEditorValueChanged = true);
204     void MoveCaretAnywhere(const Offset& touchOffset);
205     void MoveHandleToContentRect(RectF& handleRect, float boundaryAdjustment = 0.0f) const;
206     void AdjustHandleAtEdge(RectF& handleRect) const;
207     void AdjustHandleOffset(RectF& handleRect) const;
208     static int32_t GetGraphemeClusterLength(const std::wstring& text, int32_t extend, bool checkPrev = false);
209     void CalculateHandleOffset();
210     std::vector<RectF> GetSelectedRects() const;
211     RectF CalculateEmptyValueCaretRect();
212     std::string ToString() const;
213     bool IsTouchAtLineEnd(const Offset& localOffset);
214     void GetSubParagraphByOffset(int32_t pos, int32_t &start, int32_t &end);
215     void UpdateSelectWithBlank(const Offset& localOffset);
216 
217 private:
218     constexpr static uint32_t SECONDS_TO_MILLISECONDS = 1000;
219 
220     void FitCaretMetricsToContentRect(CaretMetricsF& caretMetrics);
221     void FitCaretMetricsToTouchPoint(CaretMetricsF& caretMetrics, const Offset& touchOffset);
222     void CalcCaretMetricsByPosition(int32_t extent, CaretMetricsF& caretCaretMetric, TextAffinity textAffinity);
223     void CalcCaretMetricsByPositionNearTouchOffset(
224         int32_t extent, CaretMetricsF& caretMetrics, const OffsetF& touchOffset);
225     // The cursor needs to fit the line where the touch is located.
226     void UpdateCaretRectByPositionNearTouchOffset(int32_t position, const Offset& touchOffset);
227 
228     // ai text analysis or detect
229     bool NeedAIAnalysis(int32_t& index, const CaretUpdateType targetType, const Offset& touchOffset,
230         std::chrono::duration<float, std::ratio<1, SECONDS_TO_MILLISECONDS>> timeout);
231     void AdjustCursorPosition(int32_t& index, const Offset& touchOffset);
232     bool AdjustWordSelection(int32_t& index, int32_t& start, int32_t& end, const Offset& touchOffset);
233     bool IsClickAtBoundary(int32_t index, const Offset& touchOffset);
234     const TimeStamp& GetLastClickTime();
235 
236     ACE_DEFINE_PROPERTY_ITEM_WITHOUT_GROUP(FirstIndex, int32_t, PROPERTY_UPDATE_RENDER);
237     ACE_DEFINE_PROPERTY_ITEM_WITHOUT_GROUP(SecondIndex, int32_t, PROPERTY_UPDATE_RENDER);
238 
239     RectF contentRect_;
240     HandleInfoNG firstHandleInfo_;
241     HandleInfoNG secondHandleInfo_;
242     HandleInfoNG caretInfo_;
243     RefPtr<Paragraph> paragraph_;
244     RefPtr<ContentController> contentController_;
245     OnAccessibilityCallback onAccessibilityCallback_;
246     WeakPtr<Pattern> pattern_;
247     TimeStamp lastAiPosTimeStamp_;
248     TextAffinity textAffinity_ = TextAffinity::DOWNSTREAM;
249 };
250 } // namespace OHOS::Ace::NG
251 
252 #endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_TEXT_FIELD_PATTERN_TEXT_SELECT_CONTROLLER_H