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