• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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