1 /* 2 * Copyright (c) 2021-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_TEXT_SELECTOR_H 17 #define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_TEXT_FIELD_TEXT_SELECTOR_H 18 19 #include <cstdint> 20 #include <string> 21 #include <functional> 22 23 #include "base/geometry/ng/offset_t.h" 24 #include "base/geometry/ng/rect_t.h" 25 #include "frameworks/core/components/common/properties/decoration.h" 26 27 // avoid windows build error about macro defined in wincon.h 28 #ifdef DOUBLE_CLICK 29 #undef DOUBLE_CLICK 30 #endif 31 32 namespace OHOS::Ace::NG { 33 34 using OnAccessibilityCallback = std::function<void()>; 35 36 enum class CaretUpdateType { 37 PRESSED, 38 LONG_PRESSED, 39 ICON_PRESSED, 40 DEL, 41 EVENT, 42 HANDLE_MOVE, 43 HANDLE_MOVE_DONE, 44 INPUT, 45 NONE, 46 RIGHT_CLICK, 47 VISIBLE_PASSWORD_ICON, 48 DOUBLE_CLICK 49 }; 50 51 enum class AIResetSelectionReason { 52 INIT_SELECTION = 0, 53 SHOW_FOR_CANCEL = 1, 54 LONG_PRESS = 2, 55 CLICK = 3, 56 DRAG_START = 4, 57 DRAG_START_ON_CHILDREN = 5, 58 CLOSE_CONTEXT_MENU = 6 59 }; 60 /** 61 * Stands for selection indexes 62 * We use base/destination to indicate the start/end position because of uncertain direction. 63 */ 64 struct HandleInfo { 65 int32_t index = 0; 66 RectF rect; 67 AddRectYHandleInfo68 void AddRectY(float deltaY) 69 { 70 auto newOffset = rect.GetOffset(); 71 newOffset.AddY(deltaY); 72 rect.SetOffset(newOffset); 73 } 74 AddRectXHandleInfo75 void AddRectX(float deltaX) 76 { 77 auto newOffset = rect.GetOffset(); 78 newOffset.AddX(deltaX); 79 rect.SetOffset(newOffset); 80 } 81 }; 82 struct TextSelector { 83 TextSelector() = default; TextSelectorTextSelector84 TextSelector(int32_t base, int32_t destination) : baseOffset(base), destinationOffset(destination) {} 85 SetOnAccessibilityTextSelector86 void SetOnAccessibility(OnAccessibilityCallback&& onAccessibilityCallback) 87 { 88 if (onAccessibilityCallback) { 89 onAccessibilityCallback_ = std::move(onAccessibilityCallback); 90 } 91 } 92 FireAccessibilityCallbackTextSelector93 void FireAccessibilityCallback() const 94 { 95 if (onAccessibilityCallback_) { 96 onAccessibilityCallback_(); 97 } 98 } 99 UpdateTextSelector100 void Update(int32_t base, int32_t destination) 101 { 102 if (base == baseOffset && destination == destinationOffset) { 103 return; 104 } 105 bool isChanged = baseOffset != destinationOffset || base != destination; 106 baseOffset = base; 107 if (baseOffset >= 0) { 108 lastValidStart = baseOffset; 109 } 110 destinationOffset = destination; 111 if (isChanged) { 112 FireAccessibilityCallback(); 113 } 114 } 115 116 // Usually called when none is selected. UpdateTextSelector117 void Update(int32_t both) 118 { 119 if ((baseOffset != both) || (destinationOffset != both)) { 120 if (baseOffset != destinationOffset) { 121 FireAccessibilityCallback(); 122 } 123 } 124 baseOffset = both; 125 destinationOffset = both; 126 if (baseOffset >= 0) { 127 lastValidStart = baseOffset; 128 } 129 } 130 ReverseTextSelectorTextSelector131 void ReverseTextSelector() 132 { 133 if (baseOffset > destinationOffset) { 134 Update(destinationOffset, baseOffset); 135 } 136 } 137 138 bool operator==(const TextSelector& other) const 139 { 140 return baseOffset == other.baseOffset && destinationOffset == other.destinationOffset; 141 } 142 143 bool operator!=(const TextSelector& other) const 144 { 145 return !operator==(other); 146 } 147 GetTextStartTextSelector148 inline int32_t GetTextStart() const 149 { 150 return std::min(baseOffset, destinationOffset); 151 } 152 GetTextEndTextSelector153 inline int32_t GetTextEnd() const 154 { 155 return std::max(baseOffset, destinationOffset); 156 } 157 GetStartTextSelector158 inline int32_t GetStart() const 159 { 160 return baseOffset; 161 } 162 GetEndTextSelector163 inline int32_t GetEnd() const 164 { 165 return destinationOffset; 166 } 167 IsValidTextSelector168 inline bool IsValid() const 169 { 170 return baseOffset > -1 && destinationOffset > -1; 171 } 172 SelectNothingTextSelector173 inline bool SelectNothing() const 174 { 175 return !IsValid() || baseOffset == destinationOffset; 176 } 177 MoveSelectionLeftTextSelector178 bool MoveSelectionLeft() 179 { 180 destinationOffset = std::max(0, destinationOffset - 1); 181 return destinationOffset == baseOffset; 182 } 183 MoveSelectionRightTextSelector184 bool MoveSelectionRight() 185 { 186 destinationOffset = std::min(charCount, destinationOffset + 1); 187 return destinationOffset == baseOffset; 188 } 189 GetSelectHeightTextSelector190 double GetSelectHeight() const 191 { 192 return std::max(firstHandle.Height(), secondHandle.Height()); 193 } 194 StartEqualToDestTextSelector195 bool StartEqualToDest() const 196 { 197 return baseOffset == destinationOffset; 198 } 199 StartGreaterDestTextSelector200 bool StartGreaterDest() const 201 { 202 return baseOffset > destinationOffset; 203 } 204 ContainsRangeTextSelector205 bool ContainsRange(const std::pair<int32_t, int32_t>& range) const 206 { 207 return IsValid() && GetTextStart() <= range.first && range.second <= GetTextEnd(); 208 } 209 ResetAiSelectedTextSelector210 void ResetAiSelected() 211 { 212 aiStart = std::nullopt; 213 aiEnd = std::nullopt; 214 } 215 ToStringTextSelector216 std::string ToString() 217 { 218 std::string result; 219 result.append("baseOffset: "); 220 result.append(std::to_string(baseOffset)); 221 result.append(", selectionBaseOffset: "); 222 result.append(selectionBaseOffset.ToString()); 223 result.append(", destinationOffset: "); 224 result.append(std::to_string(destinationOffset)); 225 result.append(", selectionDestinationOffset: "); 226 result.append(selectionDestinationOffset.ToString()); 227 result.append(", firstHandle: "); 228 result.append(firstHandle.ToString()); 229 result.append(", secondHandle: "); 230 result.append(secondHandle.ToString()); 231 result.append(", firstHandleOffset_: "); 232 result.append(firstHandleOffset_.ToString()); 233 result.append(", secondHandleOffset_: "); 234 result.append(secondHandleOffset_.ToString()); 235 return result; 236 } 237 238 // May larger than, smaller than or equal to destinationOffset. 239 int32_t baseOffset = -1; 240 OffsetF selectionBaseOffset; 241 242 // When paints caret, this is where the caret position is. 243 int32_t destinationOffset = -1; 244 OffsetF selectionDestinationOffset; 245 246 std::optional<int32_t> aiStart; 247 std::optional<int32_t> aiEnd; 248 249 int32_t charCount = 0; 250 RectF firstHandle; 251 RectF secondHandle; 252 OffsetF firstHandleOffset_; 253 OffsetF secondHandleOffset_; 254 OnAccessibilityCallback onAccessibilityCallback_; 255 int32_t lastValidStart = 0; 256 }; 257 258 enum class TextSpanType : int32_t { 259 TEXT = 0, 260 IMAGE, 261 MIXED, 262 BUILDER, 263 NONE, 264 }; 265 266 enum class TextResponseType : int32_t { 267 RIGHT_CLICK = 0, 268 LONG_PRESS, 269 SELECTED_BY_MOUSE, 270 NONE, 271 }; 272 273 enum class SelectionMenuType : int32_t { 274 SELECTION_MENU = 0, 275 PREVIEW_MENU = 1, 276 }; 277 278 struct PreviewMenuOptions { 279 HapticFeedbackMode hapticFeedbackMode = HapticFeedbackMode::DISABLED; 280 }; 281 282 struct SelectMenuParam { 283 std::function<void(int32_t, int32_t)> onAppear; 284 std::function<void()> onDisappear; 285 std::function<void(int32_t, int32_t)> onMenuShow; 286 std::function<void(int32_t, int32_t)> onMenuHide; 287 bool isValid = true; 288 PreviewMenuOptions previewMenuOptions; 289 }; 290 291 struct SelectionMenuParams { 292 TextSpanType type; 293 std::function<void()> buildFunc; 294 std::function<void(int32_t, int32_t)> onAppear; 295 std::function<void()> onDisappear; 296 TextResponseType responseType; 297 std::function<void(int32_t, int32_t)> onMenuShow; 298 std::function<void(int32_t, int32_t)> onMenuHide; 299 bool isValid = true; 300 SelectionMenuParamsSelectionMenuParams301 SelectionMenuParams(TextSpanType _type, std::function<void()> _buildFunc, 302 std::function<void(int32_t, int32_t)> _onAppear, std::function<void()> _onDisappear, 303 TextResponseType _responseType) 304 : type(_type), buildFunc(_buildFunc), onAppear(_onAppear), onDisappear(_onDisappear), 305 responseType(_responseType) 306 {} 307 }; 308 309 struct TextSpanTypeMapper { GetTextSpanTypeFromJsTypeTextSpanTypeMapper310 static bool GetTextSpanTypeFromJsType(int32_t spanTypeId, NG::TextSpanType& spanType) 311 { 312 std::unordered_map<int32_t, NG::TextSpanType> spanTypeMap = { 313 { 0, NG::TextSpanType::TEXT }, 314 { 1, NG::TextSpanType::IMAGE }, 315 { 2, NG::TextSpanType::MIXED }, 316 { 3, NG::TextSpanType::NONE } 317 }; 318 if (spanTypeMap.find(spanTypeId) != spanTypeMap.end()) { 319 spanType = spanTypeMap[spanTypeId]; 320 return true; 321 } 322 spanType = static_cast<NG::TextSpanType>(spanTypeId); 323 return false; 324 } 325 GetJsSpanTypeTextSpanTypeMapper326 static int32_t GetJsSpanType(const NG::TextSpanType& spanType, bool isValid) 327 { 328 std::unordered_map<NG::TextSpanType, int32_t> spanTypeMap = { 329 { NG::TextSpanType::TEXT, 0 }, 330 { NG::TextSpanType::IMAGE, 1 }, 331 { NG::TextSpanType::MIXED, 2 }, 332 { NG::TextSpanType::NONE, 3 } 333 }; 334 if (isValid) { 335 return spanTypeMap[spanType]; 336 } 337 return static_cast<int32_t>(spanType); 338 } 339 }; 340 341 } // namespace OHOS::Ace::NG 342 343 #endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_TEXT_FIELD_TEXT_SELECTOR_H 344