1 /* 2 * Copyright (c) 2025 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_PATTERNS_RICH_EDITOR_UNDO_MANAGER_H 17 #define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_RICH_EDITOR_UNDO_MANAGER_H 18 19 #include "core/components_ng/pattern/rich_editor/rich_editor_pattern.h" 20 21 namespace OHOS::Ace::NG { 22 enum class SpanOptionsType { TEXT = 0, IMAGE, SYMBOL, BUILDER }; 23 24 struct UndoRedoRecord { 25 TextRange rangeBefore; 26 TextRange rangeAfter; 27 RefPtr<SpanString> styledStringBefore; 28 RefPtr<SpanString> styledStringAfter; 29 std::optional<OptionsList> optionsListBefore; 30 std::optional<OptionsList> optionsListAfter; 31 TextRange selectionBefore; 32 CaretAffinityPolicy caretAffinityBefore = CaretAffinityPolicy::DEFAULT; 33 bool isOnlyStyleChange = false; 34 bool restoreBuilderSpan = false; 35 std::unordered_set<SpanType> updateSpanTypes; 36 SetOperationBeforeUndoRedoRecord37 void SetOperationBefore(TextRange range, const RefPtr<SpanString>& styledString, TextRange selection, 38 CaretAffinityPolicy caretAffinity) 39 { 40 rangeBefore = range; 41 styledStringBefore = styledString; 42 selectionBefore = selection; 43 caretAffinityBefore = caretAffinity; 44 } 45 SetOperationBeforeUndoRedoRecord46 void SetOperationBefore(TextRange range, const OptionsList& optionsList, TextRange selection, 47 CaretAffinityPolicy caretAffinity) 48 { 49 rangeBefore = range; 50 optionsListBefore = optionsList; 51 selectionBefore = selection; 52 caretAffinityBefore = caretAffinity; 53 } 54 SetOperationAfterUndoRedoRecord55 void SetOperationAfter(TextRange range, const RefPtr<SpanString>& styledString) 56 { 57 rangeAfter = range; 58 styledStringAfter = styledString; 59 } 60 SetOperationAfterUndoRedoRecord61 void SetOperationAfter(TextRange range, const OptionsList& optionsList) 62 { 63 rangeAfter = range; 64 optionsListAfter = optionsList; 65 } 66 CopyOperationAfterUndoRedoRecord67 void CopyOperationAfter(const UndoRedoRecord& record) 68 { 69 rangeAfter = record.rangeAfter; 70 styledStringAfter = record.styledStringAfter; 71 optionsListAfter = record.optionsListAfter; 72 } 73 AddUpdateSpanTypeUndoRedoRecord74 void AddUpdateSpanType(SpanType type) 75 { 76 updateSpanTypes.insert(type); 77 } 78 ResetUndoRedoRecord79 void Reset() 80 { 81 rangeBefore.Reset(); 82 rangeAfter.Reset(); 83 styledStringBefore = nullptr; 84 styledStringAfter = nullptr; 85 optionsListBefore = std::nullopt; 86 optionsListAfter = std::nullopt; 87 selectionBefore.Reset(); 88 caretAffinityBefore = CaretAffinityPolicy::DEFAULT; 89 isOnlyStyleChange = false; 90 updateSpanTypes.clear(); 91 } 92 ReverseUndoRedoRecord93 void Reverse() 94 { 95 std::swap(rangeBefore, rangeAfter); 96 std::swap(styledStringBefore, styledStringAfter); 97 std::swap(optionsListBefore, optionsListAfter); 98 } 99 IsBeforeStateValidUndoRedoRecord100 bool IsBeforeStateValid() const 101 { 102 return rangeBefore.IsValid() && (styledStringBefore || optionsListBefore); 103 } 104 IsAfterStateValidUndoRedoRecord105 bool IsAfterStateValid() const 106 { 107 return rangeAfter.IsValid() && (styledStringAfter || optionsListAfter); 108 } 109 IsValidUndoRedoRecord110 bool IsValid() const 111 { 112 return IsBeforeStateValid() && IsAfterStateValid(); 113 } 114 IsEmptyUndoRedoRecord115 bool IsEmpty() const 116 { 117 return rangeBefore.GetLength() == 0 && rangeAfter.GetLength() == 0; 118 } 119 IsRestoreBuilderSpanUndoRedoRecord120 bool IsRestoreBuilderSpan() const 121 { 122 return isOnlyStyleChange || restoreBuilderSpan; 123 } 124 ToStringUndoRedoRecord125 std::string ToString() const 126 { 127 auto jsonValue = JsonUtil::Create(true); 128 JSON_STRING_PUT_STRINGABLE(jsonValue, rangeBefore); 129 JSON_STRING_PUT_STRINGABLE(jsonValue, rangeAfter); 130 JSON_STRING_PUT_STRINGABLE(jsonValue, selectionBefore); 131 JSON_STRING_PUT_INT(jsonValue, caretAffinityBefore); 132 JSON_STRING_PUT_BOOL(jsonValue, isOnlyStyleChange); 133 JSON_STRING_PUT_BOOL(jsonValue, restoreBuilderSpan); 134 return jsonValue->ToString(); 135 } 136 }; 137 138 class RichEditorUndoManager { 139 public: RichEditorUndoManager(const WeakPtr<RichEditorPattern> & pattern)140 RichEditorUndoManager(const WeakPtr<RichEditorPattern>& pattern): pattern_(pattern) {} 141 virtual ~RichEditorUndoManager() = default; 142 virtual bool IsStyledUndoRedoSupported() = 0; 143 virtual bool BeforeChangeByRecord(const UndoRedoRecord& record, bool isUndo = false) = 0; 144 virtual void AfterChangeByRecord(const UndoRedoRecord& record, bool isUndo = false) = 0; ApplyOperationToRecord(int32_t start,int32_t length,const std::u16string & string,UndoRedoRecord & record)145 virtual void ApplyOperationToRecord(int32_t start, int32_t length, const std::u16string& string, 146 UndoRedoRecord& record) {} ApplyOperationToRecord(int32_t start,int32_t length,const RefPtr<SpanString> & styledString,UndoRedoRecord & record)147 virtual void ApplyOperationToRecord(int32_t start, int32_t length, const RefPtr<SpanString>& styledString, 148 UndoRedoRecord& record) {} RecordAddSpanOperation(const RefPtr<SpanItem> & item,SpanOptionsType type)149 virtual void RecordAddSpanOperation(const RefPtr<SpanItem>& item, SpanOptionsType type) {} 150 virtual void SetOperationBefore(TextRange range, TextRange selection, CaretAffinityPolicy caretAffinity, 151 UndoRedoRecord& record) = 0; 152 virtual void UpdateRecordAfterChange(int32_t start, int32_t length, UndoRedoRecord& record) = 0; RemoveBuilderSpanOptions(const RefPtr<NG::UINode> customNode)153 virtual void RemoveBuilderSpanOptions(const RefPtr<NG::UINode> customNode) {} 154 155 static std::unique_ptr<RichEditorUndoManager> Create(bool isSpanStringMode, 156 const WeakPtr<RichEditorPattern>& pattern); 157 void UndoByRecords(); 158 void RedoByRecords(); 159 void RecordSelectionBefore(); 160 void RecordSelectionBefore(TextRange selection); 161 void UpdateRecordBeforeChange( 162 int32_t start, int32_t length, UndoRedoRecord& record, bool isOnlyStyleChange = false); 163 void RecordOperation(const UndoRedoRecord& record, bool isFromRedo = false); 164 void RecordPreviewInputtingStart(int32_t start, int32_t length); 165 bool RecordPreviewInputtingEnd(const UndoRedoRecord& record); 166 void RecordInsertOperation(const UndoRedoRecord& record); 167 void RecordOperationAfterChange(int32_t start, int32_t length, UndoRedoRecord& record); ClearSelectionBefore()168 void ClearSelectionBefore() 169 { 170 selectionBefore_.Reset(); 171 } 172 ClearPreviewInputRecord()173 void ClearPreviewInputRecord() 174 { 175 if (IsPreviewInputStartWithSelection()) { 176 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "ClearPreviewInputRecord"); 177 } 178 previewInputRecord_.Reset(); 179 } 180 ClearUndoRedoRecords()181 void ClearUndoRedoRecords() 182 { 183 undoRecords_.clear(); 184 redoRecords_.clear(); 185 ClearPreviewInputRecord(); 186 ClearSelectionBefore(); 187 } 188 189 protected: 190 void RecordOperation(const UndoRedoRecord& record, size_t index); 191 void RecordUndoOperation(const UndoRedoRecord& record); IsPreviewInputStartWithSelection()192 bool IsPreviewInputStartWithSelection() 193 { 194 return previewInputRecord_.IsBeforeStateValid(); 195 } 196 StartCountingRecord()197 void StartCountingRecord() 198 { 199 CHECK_NULL_VOID(!isCountingRecord_); 200 recordCount_ = 0; 201 isCountingRecord_ = true; 202 } 203 CountRecord()204 void CountRecord() 205 { 206 CHECK_NULL_VOID(isCountingRecord_); 207 recordCount_ ++; 208 } 209 EndCountingRecord()210 size_t EndCountingRecord() 211 { 212 CHECK_NULL_RETURN(isCountingRecord_, 0); 213 isCountingRecord_ = false; 214 return recordCount_; 215 } 216 217 WeakPtr<RichEditorPattern> pattern_; 218 std::deque<UndoRedoRecord> undoRecords_; 219 std::deque<UndoRedoRecord> redoRecords_; 220 UndoRedoRecord previewInputRecord_; 221 // used to record selection or caret position at the start of a multi-step operation 222 TextRange selectionBefore_; 223 size_t recordCount_ = 0; 224 bool isCountingRecord_ = false; 225 }; 226 227 class StyledStringUndoManager : public RichEditorUndoManager { 228 public: StyledStringUndoManager(const WeakPtr<RichEditorPattern> & pattern)229 StyledStringUndoManager(const WeakPtr<RichEditorPattern>& pattern): RichEditorUndoManager(pattern) {} 230 bool IsStyledUndoRedoSupported() override; 231 bool BeforeChangeByRecord(const UndoRedoRecord& record, bool isUndo = false) override; 232 void AfterChangeByRecord(const UndoRedoRecord& record, bool isUndo = false) override; 233 void ApplyOperationToRecord(int32_t start, int32_t length, const std::u16string& string, 234 UndoRedoRecord& record) override; 235 void ApplyOperationToRecord(int32_t start, int32_t length, const RefPtr<SpanString>& styledString, 236 UndoRedoRecord& record) override; 237 void SetOperationBefore(TextRange range, TextRange selection, CaretAffinityPolicy caretAffinity, 238 UndoRedoRecord& record) override; 239 void UpdateRecordAfterChange(int32_t start, int32_t length, UndoRedoRecord& record) override; 240 }; 241 242 class SpansUndoManager : public RichEditorUndoManager { 243 public: SpansUndoManager(const WeakPtr<RichEditorPattern> & pattern)244 SpansUndoManager(const WeakPtr<RichEditorPattern>& pattern): RichEditorUndoManager(pattern) {} 245 bool IsStyledUndoRedoSupported() override; 246 bool BeforeChangeByRecord(const UndoRedoRecord& record, bool isUndo = false) override; 247 void AfterChangeByRecord(const UndoRedoRecord& record, bool isUndo = false) override; 248 void RecordAddSpanOperation(const RefPtr<SpanItem>& item, SpanOptionsType type) override; 249 void SetOperationBefore(TextRange range, TextRange selection, CaretAffinityPolicy caretAffinity, 250 UndoRedoRecord& record) override; 251 void UpdateRecordAfterChange(int32_t start, int32_t length, UndoRedoRecord& record) override; 252 void RemoveBuilderSpanOptions(const RefPtr<NG::UINode> customNode) override; 253 private: 254 OptionsList CreateOptionsListByRange(TextRange range); 255 SpanOptions CreateSpanOptionsBySpanObject(const ResultObject& object); 256 TextSpanOptions CreateTextSpanOptions(const ResultObject& object); 257 TextSpanOptions CreateTextSpanOptions(const RefPtr<SpanItem>& item); 258 ImageSpanOptions CreateImageSpanOptions(const ResultObject& object); 259 ImageSpanOptions CreateImageSpanOptions(const RefPtr<ImageSpanItem>& item); 260 SymbolSpanOptions CreateSymbolSpanOptions(const ResultObject& object); 261 SymbolSpanOptions CreateSymbolSpanOptions(const RefPtr<SpanItem>& item); 262 BuilderSpanOptions CreateBuilderSpanOptions(const ResultObject& object); 263 BuilderSpanOptions CreateBuilderSpanOptions(const RefPtr<PlaceholderSpanItem>& item); 264 void RemoveBuilderSpanOptions(std::deque<UndoRedoRecord>& records, const RefPtr<NG::UINode> customNode); 265 void RemoveBuilderSpanOptions(OptionsList& optionsList, const RefPtr<NG::UINode> customNode); 266 }; 267 } 268 269 #endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_RICH_EDITOR_UNDO_MANAGER_H