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 void UpdateRecordCaretIndex(int32_t index) const; 53 UpdateHandleIndex(int32_t handleIndex)54 void UpdateHandleIndex(int32_t handleIndex) 55 { 56 UpdateHandleIndex(handleIndex, handleIndex); 57 } 58 GetStartIndex()59 inline int32_t GetStartIndex() const 60 { 61 return std::min(firstHandleInfo_.index, secondHandleInfo_.index); 62 } 63 GetEndIndex()64 inline int32_t GetEndIndex() const 65 { 66 return std::max(firstHandleInfo_.index, secondHandleInfo_.index); 67 } 68 GetCaretIndex()69 int32_t GetCaretIndex() const 70 { 71 return caretInfo_.index; 72 } 73 GetFirstHandleIndex()74 int32_t GetFirstHandleIndex() const 75 { 76 return firstHandleInfo_.index; 77 } 78 GetFirstHandleRect()79 RectF GetFirstHandleRect() const 80 { 81 return firstHandleInfo_.rect; 82 } 83 GetSecondHandleIndex()84 int32_t GetSecondHandleIndex() const 85 { 86 return secondHandleInfo_.index; 87 } 88 GetSecondHandleRect()89 RectF GetSecondHandleRect() const 90 { 91 return secondHandleInfo_.rect; 92 } 93 GetFirstHandleOffset()94 OffsetF GetFirstHandleOffset() const 95 { 96 return firstHandleInfo_.rect.GetOffset(); 97 } 98 GetSecondHandleOffset()99 OffsetF GetSecondHandleOffset() const 100 { 101 return secondHandleInfo_.rect.GetOffset(); 102 } 103 UpdateCaretHeight(float height)104 void UpdateCaretHeight(float height) 105 { 106 caretInfo_.rect.SetHeight(height); 107 secondHandleInfo_.rect.SetHeight(height); 108 } 109 GetCaretRect()110 RectF GetCaretRect() const 111 { 112 return caretInfo_.rect; 113 } 114 GetSelectHeight()115 double GetSelectHeight() const 116 { 117 return std::max(firstHandleInfo_.rect.Height(), secondHandleInfo_.rect.Height()); 118 } 119 InitContentController(const RefPtr<ContentController> & controller)120 void InitContentController(const RefPtr<ContentController>& controller) 121 { 122 contentController_ = controller; 123 } 124 IsSelected()125 inline bool IsSelected() const 126 { 127 return firstHandleInfo_.index >= 0 && secondHandleInfo_.index >= 0 && 128 firstHandleInfo_.index != secondHandleInfo_.index; 129 } 130 IsSelectedAll()131 inline bool IsSelectedAll() const 132 { 133 return firstHandleInfo_.index == 0 && secondHandleInfo_.index >= 0 && 134 abs(firstHandleInfo_.index - secondHandleInfo_.index) == 135 static_cast<int32_t>(contentController_->GetWideText().length()); 136 } 137 IsHandleSamePosition()138 bool IsHandleSamePosition() 139 { 140 bool sameX = NearEqual(firstHandleInfo_.rect.GetX(), secondHandleInfo_.rect.GetX()); 141 bool sameY = NearEqual(firstHandleInfo_.rect.GetY(), secondHandleInfo_.rect.GetY()); 142 return (sameX && sameY); 143 } 144 UpdateParagraph(const RefPtr<Paragraph> & paragraph)145 void UpdateParagraph(const RefPtr<Paragraph>& paragraph) 146 { 147 paragraph_ = paragraph; 148 } 149 UpdateContentRect(const RectF & rect)150 void UpdateContentRect(const RectF& rect) 151 { 152 contentRect_ = rect; 153 } 154 UpdateCaretWidth(float width)155 void UpdateCaretWidth(float width) 156 { 157 caretInfo_.rect.SetWidth(width); 158 } 159 GetFirstHandleInfo()160 HandleInfoNG GetFirstHandleInfo() const 161 { 162 return firstHandleInfo_; 163 } 164 GetSecondHandleInfo()165 HandleInfoNG GetSecondHandleInfo() const 166 { 167 return secondHandleInfo_; 168 } 169 GetCaretInfo()170 HandleInfoNG GetCaretInfo() const 171 { 172 return caretInfo_; 173 } 174 HasReverse()175 bool HasReverse() 176 { 177 return firstHandleInfo_.index > secondHandleInfo_.index; 178 } 179 CaretAtLast()180 bool CaretAtLast() const 181 { 182 return caretInfo_.index == static_cast<int32_t>(contentController_->GetWideText().length()); 183 } 184 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 void UpdateSecondHandleInfoByMouseOffset(const Offset& localOffset); 190 void MoveSecondHandleByKeyBoard(int32_t index); 191 void UpdateSelectByOffset(const Offset& localOffset); 192 std::pair<int32_t, int32_t> GetSelectRangeByOffset(const Offset& localOffset); 193 void UpdateCaretOffset(TextAffinity textAffinity = TextAffinity::DOWNSTREAM); 194 void UpdateCaretOffset(const OffsetF& offset); 195 void UpdateFirstHandleOffset(); 196 void UpdateSecondHandleOffset(); 197 void MoveFirstHandleToContentRect(int32_t index); 198 void MoveSecondHandleToContentRect(int32_t index); 199 void MoveCaretToContentRect( 200 int32_t index, TextAffinity textAffinity = TextAffinity::UPSTREAM, bool isEditorValueChanged = true); 201 void MoveHandleToContentRect(RectF& handleRect, float boundaryAdjustment = 0.0f) const; 202 void AdjustHandleAtEdge(RectF& handleRect) const; 203 static int32_t GetGraphemeClusterLength(const std::wstring& text, int32_t extend, bool checkPrev = false); 204 void CalculateHandleOffset(); 205 std::vector<RectF> GetSelectedRects() const; 206 RectF CalculateEmptyValueCaretRect() const; 207 std::string ToString() const; 208 int32_t ConvertTouchOffsetToPosition(const Offset& localOffset, bool isSelectionPos = false); 209 210 private: 211 constexpr static uint32_t SECONDS_TO_MILLISECONDS = 1000; 212 213 void FitCaretMetricsToContentRect(CaretMetricsF& caretMetrics); 214 void CalcCaretMetricsByPosition(int32_t extent, CaretMetricsF& caretCaretMetric, TextAffinity textAffinity); 215 void CalcCaretMetricsByPositionNearTouchOffset( 216 int32_t extent, CaretMetricsF& caretMetrics, const OffsetF& touchOffset); 217 // The cursor needs to fit the line where the touch is located. 218 void UpdateCaretRectByPositionNearTouchOffset(int32_t position, const Offset& touchOffset); 219 // ai text analysis or detect 220 bool NeedAIAnalysis(int32_t& index, const CaretUpdateType targetType, const Offset& touchOffset, 221 std::chrono::duration<float, std::ratio<1, SECONDS_TO_MILLISECONDS>> timeout); 222 bool IsLineBreakOrEndOfParagraph(int32_t pos) const; 223 void AdjustCursorPosition(int32_t& index, const Offset& touchOffset); 224 bool AdjustWordSelection(int32_t& index, int32_t& start, int32_t& end, const Offset& touchOffset); 225 bool IsClickAtBoundary(int32_t index, const Offset& touchOffset); 226 const TimeStamp& GetLastClickTime(); 227 228 ACE_DEFINE_PROPERTY_ITEM_WITHOUT_GROUP(FirstIndex, int32_t, PROPERTY_UPDATE_RENDER); 229 ACE_DEFINE_PROPERTY_ITEM_WITHOUT_GROUP(SecondIndex, int32_t, PROPERTY_UPDATE_RENDER); 230 231 RectF contentRect_; 232 HandleInfoNG firstHandleInfo_; 233 HandleInfoNG secondHandleInfo_; 234 HandleInfoNG caretInfo_; 235 RefPtr<Paragraph> paragraph_; 236 RefPtr<ContentController> contentController_; 237 OnAccessibilityCallback onAccessibilityCallback_; 238 WeakPtr<Pattern> pattern_; 239 TimeStamp lastAiPosTimeStamp_; 240 TextAffinity textAffinity_ = TextAffinity::DOWNSTREAM; 241 }; 242 } // namespace OHOS::Ace::NG 243 244 #endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_TEXT_FIELD_PATTERN_TEXT_SELECT_CONTROLLER_H