• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-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 #include "core/components_ng/pattern/text_field/text_field_manager.h"
17 
18 #include "base/geometry/dimension.h"
19 #include "base/memory/ace_type.h"
20 #include "base/utils/utils.h"
21 #include "core/common/ime/text_input_type.h"
22 #include "core/components_ng/event/focus_hub.h"
23 #include "core/components_ng/pattern/navigation/navigation_pattern.h"
24 #include "core/components_ng/pattern/scrollable/scrollable_pattern.h"
25 #include "core/components_ng/pattern/text/text_base.h"
26 #include "core/components_ng/pattern/text_field/text_field_pattern.h"
27 
28 namespace OHOS::Ace::NG {
29 namespace {
30 constexpr Dimension RESERVE_BOTTOM_HEIGHT = 24.0_vp;
31 } // namespace
32 
ClearOnFocusTextField()33 void TextFieldManagerNG::ClearOnFocusTextField()
34 {
35     onFocusTextField_ = nullptr;
36 }
37 
ClearOnFocusTextField(int32_t id)38 void TextFieldManagerNG::ClearOnFocusTextField(int32_t id)
39 {
40     if (onFocusTextFieldId == id) {
41         onFocusTextField_ = nullptr;
42         focusFieldIsInline = false;
43         optionalPosition_ = std::nullopt;
44         usingCustomKeyboardAvoid_ = false;
45     }
46 }
47 
OnBackPressed()48 bool TextFieldManagerNG::OnBackPressed()
49 {
50     auto pattern = onFocusTextField_.Upgrade();
51     CHECK_NULL_RETURN(pattern, false);
52     auto textBasePattern = AceType::DynamicCast<TextBase>(pattern);
53     CHECK_NULL_RETURN(textBasePattern, false);
54     return textBasePattern->OnBackPressed();
55 }
56 
SetClickPosition(const Offset & position)57 void TextFieldManagerNG::SetClickPosition(const Offset& position)
58 {
59     auto pipeline = PipelineContext::GetCurrentContextSafely();
60     CHECK_NULL_VOID(pipeline);
61     auto rootHeight = pipeline->GetRootHeight();
62     if (GreatOrEqual(position.GetY(), rootHeight)) {
63         auto pattern = onFocusTextField_.Upgrade();
64         CHECK_NULL_VOID(pattern);
65         auto host = pattern->GetHost();
66         CHECK_NULL_VOID(host);
67         auto parent = host->GetAncestorNodeOfFrame();
68         while (parent) {
69             if (parent->GetTag() == "Panel" || parent->GetTag() == "SheetPage") {
70                 return;
71             }
72             parent = parent->GetAncestorNodeOfFrame();
73         }
74     }
75     if (LessOrEqual(position.GetY(), 0.0f)) {
76         return;
77     }
78     auto rootWidth = pipeline->GetRootWidth();
79     if (GreatOrEqual(position.GetX(), rootWidth) || LessNotEqual(position.GetX(), 0.0f)) {
80         return;
81     }
82     position_ = position;
83     optionalPosition_ = position;
84 }
85 
FindScrollableOfFocusedTextField(const RefPtr<FrameNode> & textField)86 RefPtr<FrameNode> TextFieldManagerNG::FindScrollableOfFocusedTextField(const RefPtr<FrameNode>& textField)
87 {
88     CHECK_NULL_RETURN(textField, {});
89     auto parent = textField->GetAncestorNodeOfFrame();
90     while (parent) {
91         auto pattern = parent->GetPattern<ScrollablePattern>();
92         if (pattern) {
93             return parent;
94         }
95         parent = parent->GetAncestorNodeOfFrame();
96     }
97     return {};
98 }
99 
ScrollToSafeAreaHelper(const SafeAreaInsets::Inset & bottomInset,bool isShowKeyboard)100 bool TextFieldManagerNG::ScrollToSafeAreaHelper(
101     const SafeAreaInsets::Inset& bottomInset, bool isShowKeyboard)
102 {
103     auto node = onFocusTextField_.Upgrade();
104     CHECK_NULL_RETURN(node, false);
105     auto frameNode = node->GetHost();
106     CHECK_NULL_RETURN(frameNode, false);
107     auto textBase = DynamicCast<TextBase>(node);
108     CHECK_NULL_RETURN(textBase, false);
109     textBase->OnVirtualKeyboardAreaChanged();
110 
111     auto scrollableNode = FindScrollableOfFocusedTextField(frameNode);
112     CHECK_NULL_RETURN(scrollableNode, false);
113     auto scrollPattern = scrollableNode->GetPattern<ScrollablePattern>();
114     CHECK_NULL_RETURN(scrollPattern && scrollPattern->IsScrollToSafeAreaHelper(), false);
115     if (scrollPattern->GetAxis() == Axis::HORIZONTAL) {
116         return false;
117     }
118 
119     auto scrollableRect = scrollableNode->GetTransformRectRelativeToWindow();
120     if (isShowKeyboard) {
121         CHECK_NULL_RETURN(scrollableRect.Top() < bottomInset.start, false);
122     }
123 
124     auto caretRect = textBase->GetCaretRect() + frameNode->GetPositionToWindowWithTransform();
125     auto diffTop = caretRect.Top() - scrollableRect.Top();
126     // caret height larger scroll's content region
127     if (isShowKeyboard && diffTop <= 0 && LessNotEqual(bottomInset.start,
128         (caretRect.Bottom() + RESERVE_BOTTOM_HEIGHT.ConvertToPx()))) {
129         return false;
130     }
131 
132     // caret above scroll's content region
133     if (diffTop < 0) {
134         TAG_LOGI(ACE_KEYBOARD, "scrollRect:%{public}s caretRect:%{public}s totalOffset()=%{public}f diffTop=%{public}f",
135             scrollableRect.ToString().c_str(), caretRect.ToString().c_str(), scrollPattern->GetTotalOffset(), diffTop);
136         scrollPattern->ScrollTo(scrollPattern->GetTotalOffset() + diffTop);
137         return true;
138     }
139 
140     // caret inner scroll's content region
141     if (isShowKeyboard && LessNotEqual((caretRect.Bottom() + RESERVE_BOTTOM_HEIGHT.ConvertToPx()), bottomInset.start)) {
142         return false;
143     }
144 
145     // caret below safeArea
146     float diffBot = 0.0f;
147     if (isShowKeyboard) {
148         if (LessNotEqual(scrollableRect.Bottom(), bottomInset.start)) {
149             diffBot = scrollableRect.Bottom() - caretRect.Bottom() - RESERVE_BOTTOM_HEIGHT.ConvertToPx();
150         } else {
151             diffBot = bottomInset.start - caretRect.Bottom() - RESERVE_BOTTOM_HEIGHT.ConvertToPx();
152         }
153     } else {
154         diffBot = scrollableRect.Bottom() - caretRect.Bottom() - RESERVE_BOTTOM_HEIGHT.ConvertToPx();
155     }
156     CHECK_NULL_RETURN(diffBot < 0, false);
157     TAG_LOGI(ACE_KEYBOARD, "scrollRect:%{public}s caretRect:%{public}s totalOffset()=%{public}f diffBot=%{public}f",
158         scrollableRect.ToString().c_str(), caretRect.ToString().c_str(), scrollPattern->GetTotalOffset(), diffBot);
159     scrollPattern->ScrollTo(scrollPattern->GetTotalOffset() - diffBot);
160     return true;
161 }
162 
ScrollTextFieldToSafeArea()163 bool TextFieldManagerNG::ScrollTextFieldToSafeArea()
164 {
165     auto pipeline = PipelineContext::GetCurrentContext();
166     CHECK_NULL_RETURN(pipeline, false);
167     auto keyboardInset = pipeline->GetSafeAreaManager()->GetKeyboardInset();
168     bool isShowKeyboard = keyboardInset.IsValid();
169     if (isShowKeyboard) {
170         auto bottomInset = pipeline->GetSafeArea().bottom_.Combine(keyboardInset);
171         CHECK_NULL_RETURN(bottomInset.IsValid(), false);
172         return ScrollToSafeAreaHelper(bottomInset, isShowKeyboard);
173     } else if (pipeline->GetSafeAreaManager()->KeyboardSafeAreaEnabled()) {
174         // hide keyboard only scroll when keyboard avoid mode is resize
175         return ScrollToSafeAreaHelper({0, 0}, isShowKeyboard);
176     }
177     return false;
178 }
179 
SetHeight(float height)180 void TextFieldManagerNG::SetHeight(float height)
181 {
182     height_ = height + RESERVE_BOTTOM_HEIGHT.ConvertToPx();
183 }
184 
UpdateScrollableParentViewPort(const RefPtr<FrameNode> & node)185 void TextFieldManagerNG::UpdateScrollableParentViewPort(const RefPtr<FrameNode>& node)
186 {
187     CHECK_NULL_VOID(node);
188     auto scrollableNode = FindScrollableOfFocusedTextField(node);
189     CHECK_NULL_VOID(scrollableNode);
190     auto scrollPattern = scrollableNode->GetPattern<ScrollablePattern>();
191     CHECK_NULL_VOID(scrollPattern);
192     if (scrollPattern->GetAxis() == Axis::HORIZONTAL) {
193         return;
194     }
195     auto scrollableRect = scrollableNode->GetTransformRectRelativeToWindow();
196     scrollableNode->SetViewPort(scrollableRect);
197 }
198 
AvoidKeyBoardInNavigation()199 void TextFieldManagerNG::AvoidKeyBoardInNavigation()
200 {
201     auto node = onFocusTextField_.Upgrade();
202     auto pipeline = PipelineContext::GetCurrentContext();
203     CHECK_NULL_VOID(pipeline);
204     auto manager = pipeline->GetSafeAreaManager();
205     auto avoidKeyboardOffset =  manager ? manager->GetKeyboardOffset() : 0.0f;
206     if (!node) {
207         auto navNode = weakNavNode_.Upgrade();
208         CHECK_NULL_VOID(navNode);
209         SetNavContentAvoidKeyboardOffset(navNode, avoidKeyboardOffset);
210         return;
211     }
212     auto frameNode = node->GetHost();
213     CHECK_NULL_VOID(frameNode);
214     auto preNavNode = weakNavNode_.Upgrade();
215     if (preNavNode) {
216         SetNavContentAvoidKeyboardOffset(preNavNode, 0.0f);
217     }
218     auto navNode = FindNavNode(frameNode);
219     CHECK_NULL_VOID(navNode);
220     weakNavNode_ = navNode;
221     SetNavContentAvoidKeyboardOffset(navNode, avoidKeyboardOffset);
222 }
223 
AvoidKeyboardInSheet(const RefPtr<FrameNode> & textField)224 void TextFieldManagerNG::AvoidKeyboardInSheet(const RefPtr<FrameNode>& textField)
225 {
226     CHECK_NULL_VOID(textField);
227     auto parent = textField->GetAncestorNodeOfFrame();
228     bool findSheet = false;
229     while (parent) {
230         if (parent->GetHostTag() == V2::SHEET_PAGE_TAG) {
231             findSheet = true;
232             break;
233         }
234         parent = parent->GetAncestorNodeOfFrame();
235     }
236     CHECK_NULL_VOID(parent);
237     auto sheetNodePattern = parent->GetPattern<SheetPresentationPattern>();
238     CHECK_NULL_VOID(sheetNodePattern);
239     TAG_LOGI(ACE_KEYBOARD, "Force AvoidKeyboard in sheet");
240     sheetNodePattern->AvoidSafeArea(true);
241 }
242 
FindNavNode(const RefPtr<FrameNode> & textField)243 RefPtr<FrameNode> TextFieldManagerNG::FindNavNode(const RefPtr<FrameNode>& textField)
244 {
245     CHECK_NULL_RETURN(textField, nullptr);
246     auto parent = textField->GetAncestorNodeOfFrame();
247     RefPtr<FrameNode> ret = nullptr;
248     while (parent) {
249         // when the sheet showed in navdestination, sheet replaced navdestination to do avoid keyboard.
250         if (parent->GetHostTag() == V2::SHEET_WRAPPER_TAG) {
251             auto sheetNode = parent->GetChildAtIndex(0);
252             CHECK_NULL_RETURN(sheetNode, nullptr);
253             return AceType::DynamicCast<FrameNode>(sheetNode);
254         }
255         if (parent->GetHostTag() == V2::NAVDESTINATION_VIEW_ETS_TAG ||
256             parent->GetHostTag() == V2::NAVBAR_ETS_TAG) {
257                 ret = parent;
258                 break;
259             }
260         parent = parent->GetAncestorNodeOfFrame();
261     }
262     CHECK_NULL_RETURN(ret, nullptr);
263 
264     // return navdestination or navBar if the closest ancestor navigation can expandKeyboard
265     // if can't, recursively find the ancestor navigation can expandKeyboard.
266     auto navigationNode = ret->GetAncestorNodeOfFrame();
267     while (navigationNode) {
268         if (navigationNode->GetHostTag() == V2::NAVIGATION_VIEW_ETS_TAG) {
269             break;
270         }
271         navigationNode = navigationNode->GetAncestorNodeOfFrame();
272     }
273     CHECK_NULL_RETURN(navigationNode, nullptr);
274     auto layoutProperty = navigationNode->GetLayoutProperty<NavigationLayoutProperty>();
275     CHECK_NULL_RETURN(layoutProperty, nullptr);
276     auto& opts = layoutProperty->GetSafeAreaExpandOpts();
277 
278     // if the extended keyboard area is set for the navigation, top navdestination or navbar need to avoid keyboard,
279     // otherwise don't aovid, following parent navigation.
280     bool isExpandKeyboard = opts && (opts->type & SAFE_AREA_TYPE_KEYBOARD) && (opts->edges & SAFE_AREA_EDGE_BOTTOM);
281     if (isExpandKeyboard) {
282         return ret;
283     }
284     auto mayAvoidNavContentNode = FindNavNode(navigationNode);
285     if (mayAvoidNavContentNode) {
286         return mayAvoidNavContentNode;
287     }
288     SetNavContentAvoidKeyboardOffset(ret, 0.0f);
289     return nullptr;
290 }
291 
SetNavContentAvoidKeyboardOffset(RefPtr<FrameNode> navNode,float avoidKeyboardOffset)292 void TextFieldManagerNG::SetNavContentAvoidKeyboardOffset(RefPtr<FrameNode> navNode, float avoidKeyboardOffset)
293 {
294     auto navDestinationNode = AceType::DynamicCast<NavDestinationGroupNode>(navNode);
295     if (navDestinationNode) {
296         TAG_LOGI(ACE_KEYBOARD, "navNode id:%{public}d, avoidKeyboardOffset:%{public}f", navNode->GetId(),
297             avoidKeyboardOffset);
298         auto pattern = navDestinationNode->GetPattern<NavDestinationPattern>();
299         if (pattern) {
300             avoidKeyboardOffset = pattern->NeedIgnoreKeyboard() ? 0.0f : avoidKeyboardOffset;
301             pattern->SetAvoidKeyboardOffset(avoidKeyboardOffset);
302         }
303     }
304     auto navBarNode = AceType::DynamicCast<NavBarNode>(navNode);
305     if (navBarNode) {
306         auto pattern = navBarNode->GetPattern<NavBarPattern>();
307         if (pattern) {
308             pattern->SetAvoidKeyboardOffset(avoidKeyboardOffset);
309         }
310     }
311     navNode->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
312 }
313 
AddTextFieldInfo(const TextFieldInfo & textFieldInfo)314 void TextFieldManagerNG::AddTextFieldInfo(const TextFieldInfo& textFieldInfo)
315 {
316     if (textFieldInfo.nodeId == -1 || textFieldInfo.autoFillContainerNodeId == -1) {
317         return;
318     }
319 
320     auto containerNodeIter = textFieldInfoMap_.find(textFieldInfo.autoFillContainerNodeId);
321     if (containerNodeIter != textFieldInfoMap_.end()) {
322         auto& innerTextFieldMap = containerNodeIter->second;
323         innerTextFieldMap[textFieldInfo.nodeId] = textFieldInfo;
324     } else {
325         std::unordered_map<int32_t, TextFieldInfo> innerTextFieldInfoMap;
326         innerTextFieldInfoMap[textFieldInfo.nodeId] = textFieldInfo;
327         textFieldInfoMap_[textFieldInfo.autoFillContainerNodeId] = innerTextFieldInfoMap;
328     }
329 }
330 
RemoveTextFieldInfo(const int32_t & autoFillContainerNodeId,const int32_t & nodeId)331 void TextFieldManagerNG::RemoveTextFieldInfo(const int32_t& autoFillContainerNodeId, const int32_t& nodeId)
332 {
333     auto containerNodeIter = textFieldInfoMap_.find(autoFillContainerNodeId);
334     if (containerNodeIter != textFieldInfoMap_.end()) {
335         auto& innerTextFieldInfoMap = containerNodeIter->second;
336         auto textFieldNodeIter = innerTextFieldInfoMap.find(nodeId);
337         if (textFieldNodeIter != innerTextFieldInfoMap.end()) {
338             innerTextFieldInfoMap.erase(textFieldNodeIter);
339         }
340     }
341 }
342 
UpdateTextFieldInfo(const TextFieldInfo & textFieldInfo)343 void TextFieldManagerNG::UpdateTextFieldInfo(const TextFieldInfo& textFieldInfo)
344 {
345     if (textFieldInfo.nodeId == -1 || textFieldInfo.autoFillContainerNodeId == -1) {
346         return;
347     }
348     auto containerNodeIter = textFieldInfoMap_.find(textFieldInfo.autoFillContainerNodeId);
349     if (containerNodeIter != textFieldInfoMap_.end()) {
350         auto& innerTextFieldInfoMap = containerNodeIter->second;
351         auto textFieldNodeIter = innerTextFieldInfoMap.find(textFieldInfo.nodeId);
352         if (textFieldNodeIter != innerTextFieldInfoMap.end()) {
353             innerTextFieldInfoMap.erase(textFieldNodeIter);
354         }
355         innerTextFieldInfoMap[textFieldInfo.nodeId] = textFieldInfo;
356     } else {
357         AddTextFieldInfo(textFieldInfo);
358     }
359 }
360 
HasAutoFillPasswordNodeInContainer(const int32_t & autoFillContainerNodeId,const int32_t & nodeId)361 bool TextFieldManagerNG::HasAutoFillPasswordNodeInContainer(
362     const int32_t& autoFillContainerNodeId, const int32_t& nodeId)
363 {
364     auto containerNodeIter = textFieldInfoMap_.find(autoFillContainerNodeId);
365     if (containerNodeIter == textFieldInfoMap_.end()) {
366         return false;
367     }
368 
369     auto& innerTextFieldInfoMap = containerNodeIter->second;
370     auto textFieldNodeIter = innerTextFieldInfoMap.find(nodeId);
371     if (textFieldNodeIter == innerTextFieldInfoMap.end()) {
372         return false;
373     }
374 
375     for (const auto& textField : innerTextFieldInfoMap) {
376         auto textFieldId = textField.first;
377         auto textFieldInfo = textField.second;
378         if (textFieldId == nodeId) {
379             continue;
380         }
381 
382         auto isPasswordType = IsAutoFillPasswordType(textFieldInfo);
383         if (isPasswordType && textFieldInfo.enableAutoFill) {
384             return true;
385         }
386     }
387 
388     return false;
389 }
390 
IsAutoFillPasswordType(const TextFieldInfo & textFieldInfo)391 bool TextFieldManagerNG::IsAutoFillPasswordType(const TextFieldInfo& textFieldInfo)
392 {
393     return textFieldInfo.inputType == TextInputType::VISIBLE_PASSWORD ||
394            textFieldInfo.inputType == TextInputType::NEW_PASSWORD ||
395            textFieldInfo.inputType == TextInputType::NUMBER_PASSWORD ||
396            textFieldInfo.contentType == TextContentType::VISIBLE_PASSWORD ||
397            textFieldInfo.contentType == TextContentType::NEW_PASSWORD;
398 }
399 
~TextFieldManagerNG()400 TextFieldManagerNG::~TextFieldManagerNG()
401 {
402     textFieldInfoMap_.clear();
403 }
404 } // namespace OHOS::Ace::NG
405