1 /*
2 * Copyright (c) 2024 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 #include "core/components_ng/pattern/rich_editor/one_step_drag_controller.h"
17
18 namespace OHOS::Ace::NG {
19
20 /* begin OneStepDragParam */
OneStepDragParam(const Builder & builder,const SelectMenuParam & selectMenuParam,TextSpanType spanType,TagFilter tagFilter)21 OneStepDragParam::OneStepDragParam(const Builder& builder, const SelectMenuParam& selectMenuParam,
22 TextSpanType spanType, TagFilter tagFilter) : spanType_(spanType), tagFilter_(tagFilter)
23 {
24 menuBuilder = builder;
25 onAppear = [onAppearFunc = selectMenuParam.onAppear, onMenuShowFunc = selectMenuParam.onMenuShow](
26 int32_t start, int32_t end) {
27 onAppearFunc(start, end);
28 onMenuShowFunc(start, end);
29 };
30 onDisappear = [onDisappearFunc = selectMenuParam.onDisappear, onMenuHideFunc = selectMenuParam.onMenuHide](
31 int32_t start, int32_t end) {
32 onMenuHideFunc(start, end);
33 onDisappearFunc();
34 };
35 menuParam.previewMode = MenuPreviewMode::IMAGE;
36 menuParam.type = MenuType::CONTEXT_MENU;
37 menuParam.previewAnimationOptions.scaleFrom = 1.0f;
38 menuParam.previewBorderRadius = BorderRadiusProperty(Dimension(0));
39 menuParam.backgroundBlurStyle = static_cast<int>(BlurStyle::NO_MATERIAL);
40 }
41
EnableDrag(const RefPtr<FrameNode> & frameNode) const42 void OneStepDragParam::EnableDrag(const RefPtr<FrameNode>& frameNode) const
43 {
44 frameNode->SetDraggable(true);
45 auto gestureHub = frameNode->GetOrCreateGestureEventHub();
46 CHECK_NULL_VOID(gestureHub);
47 gestureHub->InitDragDropEvent();
48 }
49
BindContextMenu(const RefPtr<FrameNode> & frameNode)50 void OneStepDragParam::BindContextMenu(const RefPtr<FrameNode>& frameNode)
51 {
52 #ifndef ACE_UNITTEST
53 CHECK_NULL_VOID(frameNode);
54 auto resType = ResponseType::LONG_PRESS;
55 auto menuParam = GetMenuParam(frameNode);
56 ViewStackProcessor::GetInstance()->Push(frameNode);
57 ViewAbstractModel::GetInstance()->BindContextMenu(resType, menuBuilder, menuParam, previewBuilder);
58 ViewAbstractModel::GetInstance()->BindDragWithContextMenuParams(menuParam);
59 ViewStackProcessor::GetInstance()->Finish();
60 #endif
61 }
62
FillJsonValue(const std::unique_ptr<JsonValue> & jsonValue) const63 void OneStepDragParam::FillJsonValue(const std::unique_ptr<JsonValue>& jsonValue) const
64 {
65 CHECK_NULL_VOID(jsonValue);
66 auto jsonItem = JsonUtil::Create(true);
67 jsonItem->Put("spanType", static_cast<int32_t>(spanType_));
68 jsonItem->Put("responseType", static_cast<int32_t>(TextResponseType::LONG_PRESS));
69 jsonItem->Put("menuType", static_cast<int32_t>(SelectionMenuType::PREVIEW_MENU));
70 jsonValue->Put(jsonItem);
71 }
72
HandleDirtyNodes()73 void OneStepDragParam::HandleDirtyNodes()
74 {
75 while (!dirtyFrameNodes.empty()) {
76 auto weakNode = dirtyFrameNodes.front();
77 dirtyFrameNodes.pop();
78 auto frameNode = weakNode.Upgrade();
79 BindContextMenu(frameNode);
80 }
81 }
82
MarkDirtyNode(const WeakPtr<FrameNode> & dirtyFrameNode)83 inline void OneStepDragParam::MarkDirtyNode(const WeakPtr<FrameNode>& dirtyFrameNode)
84 {
85 dirtyFrameNodes.push(dirtyFrameNode);
86 }
87 /* end OneStepDragParam */
88
89 /* begin ImageOneStepDragParam */
GetMenuParam(const RefPtr<FrameNode> & frameNode) const90 MenuParam ImageOneStepDragParam::GetMenuParam(const RefPtr<FrameNode>& frameNode) const
91 {
92 auto imageNode = AceType::DynamicCast<ImageSpanNode>(frameNode);
93 CHECK_NULL_RETURN(imageNode, menuParam);
94 auto res = menuParam;
95 res.onAppear = [weak = AceType::WeakClaim(AceType::RawPtr(imageNode)), onAppear = this->onAppear]() {
96 CHECK_NULL_VOID(onAppear);
97 auto imageNode = weak.Upgrade();
98 CHECK_NULL_VOID(imageNode);
99 const auto& spanItem = imageNode->GetSpanItem();
100 onAppear(spanItem->rangeStart, spanItem->position);
101 };
102 res.onDisappear = [weak = AceType::WeakClaim(AceType::RawPtr(imageNode)), onDisappear = this->onDisappear]() {
103 CHECK_NULL_VOID(onDisappear);
104 auto imageNode = weak.Upgrade();
105 CHECK_NULL_VOID(imageNode);
106 const auto& spanItem = imageNode->GetSpanItem();
107 onDisappear(spanItem->rangeStart, spanItem->position);
108 };
109 res.previewAnimationOptions.scaleTo = CalcImageScale(imageNode);
110 return res;
111 }
112
EnableOneStepDrag(const RefPtr<FrameNode> & frameNode)113 void ImageOneStepDragParam::EnableOneStepDrag(const RefPtr<FrameNode>& frameNode)
114 {
115 // image need to update [scaleTo] when size change, bindContextMenu when HandleDirtyNodes
116 EnableDrag(frameNode);
117 }
118
CalcImageScale(const RefPtr<ImageSpanNode> & imageNode) const119 float ImageOneStepDragParam::CalcImageScale(const RefPtr<ImageSpanNode>& imageNode) const
120 {
121 float scale = 1.1f;
122 auto dispSize = imageNode->GetGeometryNode()->GetMarginFrameSize();
123 CHECK_NULL_RETURN(dispSize.IsPositive(), scale);
124 auto imageLayoutProperty = imageNode->GetLayoutProperty<ImageLayoutProperty>();
125 CHECK_NULL_RETURN(imageLayoutProperty, scale);
126 auto imageSourceInfo = imageLayoutProperty->GetImageSourceInfoValue();
127 CHECK_NULL_RETURN(imageSourceInfo.IsPixmap(), scale);
128 auto pixelMap = imageSourceInfo.GetPixmap();
129 CHECK_NULL_RETURN(pixelMap, scale);
130 auto realWidth = pixelMap->GetWidth();
131 auto realHeight = pixelMap->GetHeight();
132 scale = std::max((float) realWidth / dispSize.Width(), (float) realHeight / dispSize.Height());
133 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "realSize=[%{public}d,%{public}d], scale=%{public}.2f",
134 realWidth, realHeight, scale);
135 return std::max(scale, 1.1f);
136 }
137 /* end ImageOneStepDragParam */
138
139 /* begin PlaceholderOneStepDragParam */
GetMenuParam(const RefPtr<FrameNode> & frameNode) const140 MenuParam PlaceholderOneStepDragParam::GetMenuParam(const RefPtr<FrameNode>& frameNode) const
141 {
142 auto placeholderNode = AceType::DynamicCast<PlaceholderSpanNode>(frameNode);
143 CHECK_NULL_RETURN(placeholderNode, menuParam);
144 auto res = menuParam;
145 res.onAppear = [weak = AceType::WeakClaim(AceType::RawPtr(placeholderNode)), onAppear = this->onAppear]() {
146 CHECK_NULL_VOID(onAppear);
147 auto placeholderNode = weak.Upgrade();
148 CHECK_NULL_VOID(placeholderNode);
149 const auto& spanItem = placeholderNode->GetSpanItem();
150 onAppear(spanItem->rangeStart, spanItem->position);
151 };
152 res.onDisappear = [weak = AceType::WeakClaim(AceType::RawPtr(placeholderNode)), onDisappear = this->onDisappear]() {
153 CHECK_NULL_VOID(onDisappear);
154 auto placeholderNode = weak.Upgrade();
155 CHECK_NULL_VOID(placeholderNode);
156 const auto& spanItem = placeholderNode->GetSpanItem();
157 onDisappear(spanItem->rangeStart, spanItem->position);
158 };
159 return res;
160 }
161
EnableDrag(const RefPtr<FrameNode> & frameNode) const162 void PlaceholderOneStepDragParam::EnableDrag(const RefPtr<FrameNode>& frameNode) const
163 {
164 OneStepDragParam::EnableDrag(frameNode);
165 auto eventHub = frameNode->GetEventHub<EventHub>();
166 CHECK_NULL_VOID(eventHub);
167 auto dragStart = [](const RefPtr<OHOS::Ace::DragEvent>&, const std::string&) -> DragDropInfo {
168 return DragDropInfo();
169 };
170 eventHub->SetDefaultOnDragStart(std::move(dragStart));
171 }
172
EnableOneStepDrag(const RefPtr<FrameNode> & frameNode)173 void PlaceholderOneStepDragParam::EnableOneStepDrag(const RefPtr<FrameNode>& frameNode)
174 {
175 EnableDrag(frameNode);
176 BindContextMenu(frameNode);
177 }
178 /* end PlaceholderOneStepDragParam */
179
180 /* begin OneStepDragController */
181 // handle existing nodes, when bindSelectionMenu
SetMenuParam(TextSpanType spanType,const Builder & builder,const SelectMenuParam & menuParam)182 bool OneStepDragController::SetMenuParam(TextSpanType spanType, const Builder& builder,
183 const SelectMenuParam& menuParam)
184 {
185 const auto& dragParam = CreateDragParam(spanType, builder, menuParam);
186 CHECK_NULL_RETURN(dragParam, false);
187
188 auto pattern = pattern_.Upgrade();
189 CHECK_NULL_RETURN(pattern, false);
190 auto host = pattern->GetHost();
191 CHECK_NULL_RETURN(host, false);
192 const auto& tagFilter = dragParam->tagFilter_;
193 CHECK_NULL_RETURN(tagFilter, false);
194
195 for (const auto& uiNode : host->GetChildren()) {
196 const auto& tag = uiNode->GetTag();
197 CHECK_NULL_CONTINUE(tagFilter(tag));
198 auto frameNode = AceType::DynamicCast<FrameNode>(uiNode);
199 CHECK_NULL_CONTINUE(frameNode);
200 dragParam->EnableDrag(frameNode);
201 dragParam->BindContextMenu(frameNode);
202 }
203 return true;
204 }
205
206 // handle new added node
EnableOneStepDrag(TextSpanType spanType,const RefPtr<FrameNode> & frameNode)207 void OneStepDragController::EnableOneStepDrag(TextSpanType spanType, const RefPtr<FrameNode>& frameNode)
208 {
209 const auto& dragParam = GetDragParam(spanType);
210 CHECK_NULL_VOID(frameNode && dragParam);
211 dragParam->EnableOneStepDrag(frameNode);
212 CopyDragCallback(spanType, frameNode);
213 }
214
GetJsonRange(const TextSpanType spanType,const RefPtr<FrameNode> & frameNode)215 std::string OneStepDragController::GetJsonRange(const TextSpanType spanType, const RefPtr<FrameNode>& frameNode)
216 {
217 RefPtr<SpanItem> spanItem;
218 if (spanType == TextSpanType::IMAGE) {
219 auto imageNode = AceType::DynamicCast<ImageSpanNode>(frameNode);
220 IF_TRUE(imageNode, spanItem = imageNode->GetSpanItem());
221 } else if (spanType == TextSpanType::BUILDER) {
222 auto placeholderNode = AceType::DynamicCast<PlaceholderSpanNode>(frameNode);
223 IF_TRUE(placeholderNode, spanItem = placeholderNode->GetSpanItem());
224 }
225 CHECK_NULL_RETURN(spanItem, "");
226 auto jsonRange = JsonUtil::Create(true);
227 jsonRange->Put("rangeStart", spanItem->rangeStart);
228 jsonRange->Put("rangeEnd", spanItem->position);
229 return jsonRange->ToString();
230 }
231
CopyDragCallback(TextSpanType spanType,const RefPtr<FrameNode> & frameNode)232 void OneStepDragController::CopyDragCallback(TextSpanType spanType, const RefPtr<FrameNode>& frameNode)
233 {
234 auto pattern = pattern_.Upgrade();
235 CHECK_NULL_VOID(pattern);
236 auto host = pattern->GetHost();
237 CHECK_NULL_VOID(host);
238
239 auto hostEventHub = host->GetEventHub<EventHub>();
240 auto frameNodeEventHub = frameNode->GetEventHub<EventHub>();
241 CHECK_NULL_VOID(hostEventHub && frameNodeEventHub);
242
243 // start
244 auto start = hostEventHub->GetOnDragStart();
245 auto oneStepDragStart = [weakNode = AceType::WeakClaim(AceType::RawPtr(frameNode)), start, spanType](
246 const RefPtr<OHOS::Ace::DragEvent>& event, const std::string& extraParams) -> DragDropInfo {
247 auto frameNode = weakNode.Upgrade();
248 auto jsonStr = OneStepDragController::GetJsonRange(spanType, frameNode);
249 return start(event, jsonStr);
250 };
251 IF_TRUE(start, frameNodeEventHub->SetOnDragStart(std::move(oneStepDragStart)));
252
253 // end
254 auto end = hostEventHub->GetCustomerOnDragEndFunc();
255 auto oneStepDragEnd = [end, weakPattern = pattern_, scopeId = Container::CurrentId()]
256 (const RefPtr<OHOS::Ace::DragEvent>& event) {
257 ContainerScope scope(scopeId);
258 auto pattern = weakPattern.Upgrade();
259 CHECK_NULL_VOID(pattern);
260 IF_TRUE(end, end(event));
261 };
262 frameNodeEventHub->SetCustomerOnDragFunc(DragFuncType::DRAG_END, std::move(oneStepDragEnd));
263 }
264
SetEnableEventResponse(bool isEnable)265 void OneStepDragController::SetEnableEventResponse(bool isEnable)
266 {
267 CHECK_NULL_VOID(imageDragParam_ || placeholderDragParam_);
268 CHECK_NULL_VOID(isEnableEventResponse_ != isEnable);
269 auto pattern = pattern_.Upgrade();
270 CHECK_NULL_VOID(pattern);
271 auto host = pattern->GetHost();
272 CHECK_NULL_VOID(host);
273
274 const auto& imageTagFilter = imageDragParam_ ? imageDragParam_->tagFilter_ : DEFAULT_FILTER;
275 const auto& placeholderTagFilter = placeholderDragParam_ ? placeholderDragParam_->tagFilter_ : DEFAULT_FILTER;
276
277 for (const auto& uiNode : host->GetChildren()) {
278 auto& tag = uiNode->GetTag();
279 CHECK_NULL_CONTINUE(imageTagFilter(tag) || placeholderTagFilter(tag));
280 auto frameNode = AceType::DynamicCast<FrameNode>(uiNode);
281 CHECK_NULL_CONTINUE(frameNode);
282 auto hub = frameNode->GetOrCreateGestureEventHub();
283 CHECK_NULL_CONTINUE(hub);
284 hub->SetHitTestMode(isEnable ? HitTestMode::HTMDEFAULT : HitTestMode::HTMNONE);
285 }
286 isEnableEventResponse_ = isEnable;
287 }
288
SetEnableEventResponse(const TextSelector & selector,std::list<WeakPtr<ImageSpanNode>> & imageNodes,std::list<WeakPtr<PlaceholderSpanNode>> & builderNodes)289 void OneStepDragController::SetEnableEventResponse(const TextSelector& selector,
290 std::list<WeakPtr<ImageSpanNode>>& imageNodes, std::list<WeakPtr<PlaceholderSpanNode>>& builderNodes)
291 {
292 auto start = selector.GetTextStart();
293 auto end = selector.GetTextEnd();
294 IF_TRUE(imageDragParam_, SetEnableEventResponse(start, end, imageNodes));
295 IF_TRUE(placeholderDragParam_, SetEnableEventResponse(start, end, builderNodes));
296 }
297
298 template<typename T>
SetEnableEventResponse(int32_t start,int32_t end,std::list<WeakPtr<T>> & nodes)299 void OneStepDragController::SetEnableEventResponse(int32_t start, int32_t end, std::list<WeakPtr<T>>& nodes)
300 {
301 for (auto it = nodes.begin(); it != nodes.end();) {
302 auto node = it->Upgrade();
303 if (!node) {
304 it = nodes.erase(it);
305 continue;
306 }
307 ++it;
308 auto hub = node->GetOrCreateGestureEventHub();
309 CHECK_NULL_CONTINUE(hub);
310 auto spanItem = node->GetSpanItem();
311 bool enableResponse = start > spanItem->rangeStart || spanItem->position > end;
312 hub->SetHitTestMode(enableResponse ? HitTestMode::HTMDEFAULT : HitTestMode::HTMNONE);
313 }
314 }
315
FillJsonValue(const std::unique_ptr<JsonValue> & jsonValue)316 void OneStepDragController::FillJsonValue(const std::unique_ptr<JsonValue>& jsonValue)
317 {
318 IF_PRESENT(imageDragParam_, FillJsonValue(jsonValue));
319 IF_PRESENT(placeholderDragParam_, FillJsonValue(jsonValue));
320 }
321
MarkDirtyNode(const WeakPtr<ImageSpanNode> & dirtyFrameNode)322 void OneStepDragController::MarkDirtyNode(const WeakPtr<ImageSpanNode>& dirtyFrameNode)
323 {
324 IF_PRESENT(imageDragParam_, MarkDirtyNode(dirtyFrameNode));
325 }
326
HandleDirtyNodes()327 void OneStepDragController::HandleDirtyNodes()
328 {
329 IF_PRESENT(imageDragParam_, HandleDirtyNodes());
330 IF_PRESENT(placeholderDragParam_, HandleDirtyNodes());
331 }
332
GetDragParam(TextSpanType spanType) const333 const std::unique_ptr<OneStepDragParam>& OneStepDragController::GetDragParam(TextSpanType spanType) const
334 {
335 if (spanType == TextSpanType::IMAGE) {
336 return imageDragParam_;
337 }
338 if (spanType == TextSpanType::BUILDER) {
339 return placeholderDragParam_;
340 }
341 return invalidParam;
342 }
343
CreateDragParam(TextSpanType spanType,const Builder & builder,const SelectMenuParam & menuParam)344 const std::unique_ptr<OneStepDragParam>& OneStepDragController::CreateDragParam(TextSpanType spanType,
345 const Builder& builder, const SelectMenuParam& menuParam)
346 {
347 CHECK_NULL_RETURN(builder, invalidParam);
348 if (spanType == TextSpanType::IMAGE) {
349 return imageDragParam_ = std::make_unique<ImageOneStepDragParam>(builder, menuParam);
350 }
351 if (spanType == TextSpanType::BUILDER) {
352 return placeholderDragParam_ = std::make_unique<PlaceholderOneStepDragParam>(builder, menuParam);
353 }
354 return invalidParam;
355 }
356 /* end OneStepDragController */
357
358 } // namespace OHOS::Ace::NG